5c93e268c6747535954e8a9825b8e2b572fef915
[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.Collections;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.locks.ReentrantLock;
20 import java.util.stream.Collectors;
21
22 import javax.annotation.Nonnull;
23
24 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
25 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
28 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
29 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
30 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
31 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
32 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
33 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
34 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
35 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
36 import org.opendaylight.groupbasedpolicy.renderer.util.AddressEndpointUtils;
37 import org.opendaylight.groupbasedpolicy.renderer.vpp.manager.VppNodeManager;
38 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.CloseOnFailTransactionChain;
39 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory;
40 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
41 import org.opendaylight.groupbasedpolicy.util.EndpointUtils;
42 import org.opendaylight.groupbasedpolicy.util.IidFactory;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.Endpoints;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.AddressEndpoints;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.address.endpoints.AddressEndpoint;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.address.endpoints.AddressEndpointKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocation;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocationBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.relative.location.RelativeLocations;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.relative.location.RelativeLocationsBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.relative.location.relative.locations.ExternalLocation;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.relative.location.relative.locations.ExternalLocationBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.parent.child.endpoints.parent.endpoint.choice.parent.endpoint._case.ParentEndpoint;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.ProviderName;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProvider;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProviderBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocation;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocationBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocationKey;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocationKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
65 import org.opendaylight.yangtools.concepts.ListenerRegistration;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 import com.google.common.annotations.VisibleForTesting;
71 import com.google.common.base.Optional;
72 import com.google.common.util.concurrent.CheckedFuture;
73 import com.google.common.util.concurrent.Futures;
74 import com.google.common.util.concurrent.ListenableFuture;
75
76 public class VppEndpointLocationProvider
77         implements ClusteredDataTreeChangeListener<AddressEndpoint>, VPPLocationProvider, AutoCloseable {
78
79     private static final Logger LOG = LoggerFactory.getLogger(VppEndpointLocationProvider.class);
80     public static final ProviderName VPP_ENDPOINT_LOCATION_PROVIDER =
81             new ProviderName("VPP endpoint location provider");
82     public static final long PROVIDER_PRIORITY = 10L;
83     private final SyncedWriter syncedWriter;
84     private final Map<VppEndpointKey, VppEndpoint> vppEndpoints = new HashMap<>();
85     private final Map<VppEndpointKey,AddressEndpoint> pendingAddrEndpoints = new HashMap<>();
86     private ListenerRegistration<VppEndpointLocationProvider> registeredListener;
87
88     public VppEndpointLocationProvider(DataBroker dataProvider) {
89         LocationProvider locationProvider = new LocationProviderBuilder().setProvider(VPP_ENDPOINT_LOCATION_PROVIDER)
90             .setPriority(PROVIDER_PRIORITY)
91             .build();
92         syncedWriter = new SyncedWriter(checkNotNull(dataProvider)
93             .createTransactionChain(new CloseOnFailTransactionChain(VppEndpointLocationProvider.class.getSimpleName())));
94         WriteTransaction wTx = syncedWriter.newWriteOnlyTransaction();
95         wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER),
96                 locationProvider, true);
97         syncedWriter.submitNow(wTx);
98         registeredListener = dataProvider.registerDataTreeChangeListener(
99                                checkNotNull(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
100                                         InstanceIdentifier.builder(Endpoints.class)
101                                             .child(AddressEndpoints.class)
102                                             .child(AddressEndpoint.class)
103                                             .build())), this);
104     }
105
106     @Override
107     public void onDataTreeChanged(Collection<DataTreeModification<AddressEndpoint>> change) {
108         change.forEach(dtm -> {
109             ReadWriteTransaction rwTx = syncedWriter.newReadWriteTransaction();
110             DataObjectModification<AddressEndpoint> rootNode = dtm.getRootNode();
111             if (rootNode.getDataBefore() != null) {
112                 locationFromParentWriter.clear(rwTx, rootNode.getDataBefore());
113                 regularLocationWriter.clear(rwTx, rootNode.getDataBefore());
114             }
115             if (rootNode.getDataAfter() == null || !canCreateLocation(dtm.getRootNode())) {
116                 syncedWriter.submitNow(rwTx);
117                 return;
118             }
119             if (VppLocationUtils.hasMultihomeParent(rwTx, rootNode.getDataAfter().getParentEndpointChoice())
120                     || VppLocationUtils.hasMultipleParents(rootNode.getDataAfter())) {
121                 locationFromParentWriter.sync(rwTx, rootNode.getDataAfter());
122             } else {
123                 regularLocationWriter.sync(rwTx, rootNode.getDataAfter());
124             }
125             syncedWriter.submitNow(rwTx);
126         });
127     }
128
129     @Override
130     public ListenableFuture<Void> createLocationForVppEndpoint(VppEndpoint vppEndpoint) {
131         vppEndpoints.put(vppEndpoint.getKey(), vppEndpoint);
132         ReadWriteTransaction rwTx = syncedWriter.newReadWriteTransaction();
133         Optional<AddressEndpoint> cachedAddrEp =
134                 Optional.fromNullable(pendingAddrEndpoints.get(vppEndpoint.getKey()));
135         if (cachedAddrEp.isPresent()) {
136             AddressEndpoint after = cachedAddrEp.get();
137             if (after != null && (VppLocationUtils.hasMultihomeParent(rwTx, after.getParentEndpointChoice())
138                     || VppLocationUtils.hasMultipleParents(after))) {
139                 locationFromParentWriter.sync(rwTx, after);
140             } else {
141                 regularLocationWriter.sync(rwTx, after);
142             }
143         }
144         syncedWriter.submitNow(rwTx);
145         return Futures.immediateFuture(null);
146     }
147
148     @Override
149     public ListenableFuture<Void> deleteLocationForVppEndpoint(VppEndpoint vppEndpoint) {
150         // onDelete location from DS
151         vppEndpoints.remove(vppEndpoint.getKey());
152         return Futures.immediateFuture(null);
153     }
154
155     @Override
156     public ListenableFuture<Void> replaceLocationForEndpoint(@Nonnull ExternalLocationCase location,
157             @Nonnull AddressEndpointWithLocationKey addrEpWithLocKey) {
158         InstanceIdentifier<ProviderAddressEndpointLocation> iid =
159                 IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
160                         VppLocationUtils.createProviderAddressEndpointLocationKey(addrEpWithLocKey));
161         ReadOnlyTransaction rTx = syncedWriter.newReadOnlyTransaction();
162         Optional<ProviderAddressEndpointLocation> optLoc =
163                 DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, iid, rTx);
164         syncedWriter.close(rTx);
165         ProviderAddressEndpointLocationKey provAddrEpLocKey =
166                 KeyFactory.providerAddressEndpointLocationKey(addrEpWithLocKey);
167         ProviderAddressEndpointLocationBuilder builder =
168                 new ProviderAddressEndpointLocationBuilder().setKey(provAddrEpLocKey);
169         if (optLoc.isPresent() && optLoc.get().getAbsoluteLocation() != null) {
170             AbsoluteLocation absoluteLocation = optLoc.get().getAbsoluteLocation();
171             builder
172                 .setAbsoluteLocation(new AbsoluteLocationBuilder(absoluteLocation).setLocationType(location).build());
173         } else if (optLoc.isPresent() && optLoc.get().getRelativeLocations() != null) {
174             ExternalLocation extLoc = new ExternalLocationBuilder().setExternalNode(location.getExternalNode())
175                 .setExternalNodeConnector(location.getExternalNodeConnector())
176                 .setExternalNodeMountPoint(location.getExternalNodeMountPoint())
177                 .build();
178             List<ExternalLocation> externalLocation = optLoc.get().getRelativeLocations().getExternalLocation();
179             externalLocation.add(extLoc);
180             RelativeLocations relativeLocation = new RelativeLocationsBuilder(optLoc.get().getRelativeLocations())
181                 .setExternalLocation(externalLocation).build();
182             builder.setRelativeLocations(relativeLocation);
183         } else {
184             LOG.warn("Cannot replace location for endpoint {}", addrEpWithLocKey);
185             return Futures.immediateFuture(null);
186         }
187         ProviderAddressEndpointLocation providerLocation = builder.build();
188         WriteTransaction wTx = syncedWriter.newWriteOnlyTransaction();
189         wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.providerAddressEndpointLocationIid(
190                 VPP_ENDPOINT_LOCATION_PROVIDER, providerLocation.getKey()), providerLocation);
191         LOG.debug("Updating location for {}", builder.build().getKey());
192         syncedWriter.submitNow(wTx);
193         return Futures.immediateFuture(null);
194     }
195
196     @VisibleForTesting
197     synchronized boolean canCreateLocation(@Nonnull DataObjectModification<AddressEndpoint> rootNode) {
198         VppEndpoint vpp = vppEndpoints.get(VppLocationUtils.vppEndpointKeyFrom(rootNode.getDataAfter().getKey()));
199         if (vpp == null) {
200             VppEndpointKey key = VppLocationUtils.vppEndpointKeyFrom(rootNode.getDataAfter().getKey());
201             LOG.debug("Caching VPP endpoint {}" + key);
202             pendingAddrEndpoints.put(key, rootNode.getDataAfter());
203             return false;
204         }
205         return true;
206     }
207
208     @Override
209     public void close() {
210         registeredListener.close();
211         WriteTransaction wTx = syncedWriter.newWriteOnlyTransaction();
212         wTx.delete(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER));
213         syncedWriter.submitNow(wTx);
214     }
215
216     private class SyncedWriter {
217
218         private final BindingTransactionChain txChain;
219         private final ReentrantLock SYNC_LOCK = new ReentrantLock();
220
221         SyncedWriter(BindingTransactionChain txChain) {
222             this.txChain = txChain;
223         }
224
225         ReadOnlyTransaction newReadOnlyTransaction() {
226             SYNC_LOCK.lock();
227             return txChain.newReadOnlyTransaction();
228         }
229
230         WriteTransaction newWriteOnlyTransaction() {
231             SYNC_LOCK.lock();
232             return txChain.newWriteOnlyTransaction();
233         }
234
235         ReadWriteTransaction newReadWriteTransaction() {
236             SYNC_LOCK.lock();
237             return txChain.newReadWriteTransaction();
238         }
239
240         void close(ReadOnlyTransaction rTx) {
241             rTx.close();
242             SYNC_LOCK.unlock();
243         }
244
245         void submitNow(WriteTransaction wTx) {
246             CheckedFuture<Void, TransactionCommitFailedException> submit = wTx.submit();
247             SYNC_LOCK.unlock();
248             try {
249                 submit.get();
250             } catch (InterruptedException | ExecutionException e) {
251                 LOG.error("Failed to submit transaction {}", e);
252             }
253             LOG.info("Submit done.");
254         }
255     }
256
257     private abstract class LocationWriter {
258
259         abstract void clear(ReadWriteTransaction rwTx, AddressEndpoint before);
260
261         abstract void sync(ReadWriteTransaction rwTx, AddressEndpoint after);
262     }
263
264     LocationWriter regularLocationWriter = new LocationWriter() {
265
266         @Override
267         void sync(ReadWriteTransaction rwTx, AddressEndpoint after) {
268             if (after == null) {
269                 return;
270             }
271             if (EndpointUtils.isExternalEndpoint(rwTx, after)) {
272                 ProviderAddressEndpointLocation location = VppLocationUtils.createRelativeAddressEndpointLocation(
273                         after.getKey(), VppNodeManager.resolvePublicInterfaces(rwTx));
274                 InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.providerAddressEndpointLocationIid(
275                         VppEndpointLocationProvider.VPP_ENDPOINT_LOCATION_PROVIDER, location.getKey());
276                 rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true);
277             } else {
278                 VppEndpoint vpp = vppEndpoints.get(VppLocationUtils.vppEndpointKeyFrom(after.getKey()));
279                 ProviderAddressEndpointLocation location = VppLocationUtils.createAbsoluteLocationFromVppEndpoint(vpp);
280                 InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.providerAddressEndpointLocationIid(
281                         VppEndpointLocationProvider.VPP_ENDPOINT_LOCATION_PROVIDER, location.getKey());
282                 rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true);
283             }
284         }
285
286         @Override
287         void clear(ReadWriteTransaction rwTx, AddressEndpoint before) {
288             if (before != null) {
289                 InstanceIdentifier<ProviderAddressEndpointLocation> iid =
290                         IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
291                                 VppLocationUtils.createProviderAddressEndpointLocationKey(before.getKey()));
292                 DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, iid, rwTx);
293             }
294         }
295     };
296
297     LocationWriter locationFromParentWriter = new LocationWriter() {
298
299         @Override
300         void clear(ReadWriteTransaction rwTx, AddressEndpoint before) {
301             if (before != null) {
302                 InstanceIdentifier<ProviderAddressEndpointLocation> iid =
303                         IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
304                                 VppLocationUtils.createProviderAddressEndpointLocationKey(before.getKey()));
305                 DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, iid, rwTx);
306                 for (ParentEndpoint l3Endpoint : EndpointUtils.getParentEndpoints(before.getParentEndpointChoice())) {
307                     iid = IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
308                             VppLocationUtils.createProviderAddressEndpointLocationKey(l3Endpoint.getKey()));
309                     DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, iid, rwTx);
310                 }
311             }
312         }
313
314         @Override
315         void sync(ReadWriteTransaction rwTx, AddressEndpoint after) {
316             if (after == null) {
317                 return;
318             }
319             for (ParentEndpoint l3Endpoint : EndpointUtils.getParentEndpoints(after.getParentEndpointChoice())) {
320                 InstanceIdentifier<ProviderAddressEndpointLocation> iid =
321                         IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
322                                 VppLocationUtils.createProviderAddressEndpointLocationKey(l3Endpoint.getKey()));
323                 List<VppEndpoint> l2childs = getL2Childs(rwTx, l3Endpoint);
324                 if (!l2childs.isEmpty() && l2childs.size() > 1) {
325                     // multihome interface
326                     ProviderAddressEndpointLocation location = VppLocationUtils.createRelativeAddressEndpointLocation(
327                             VppLocationUtils.createProviderAddressEndpointLocationKey(l3Endpoint.getKey()), l2childs);
328                     rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true);
329                 } else {
330                     VppEndpoint vppEndpoint = vppEndpoints.get(VppLocationUtils.vppEndpointKeyFrom(after.getKey()));
331                     ProviderAddressEndpointLocation location =
332                             VppLocationUtils.createAbsoluteLocationFromVppEndpoint(new VppEndpointBuilder(vppEndpoint)
333                                 .setKey(VppLocationUtils.vppEndpointKeyFrom(l3Endpoint.getKey())).build());
334                     rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true);
335                 }
336             }
337         }
338
339         private List<VppEndpoint> getL2Childs(ReadTransaction rTx, ParentEndpoint parentEndpoint) {
340             AddressEndpointKey addrEpKey =
341                     new AddressEndpointKey(AddressEndpointUtils.fromParentEndpointKey(parentEndpoint.getKey()));
342             Optional<AddressEndpoint> optParent = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
343                     IidFactory.addressEndpointIid(addrEpKey), rTx);
344             if (!optParent.isPresent() || optParent.get().getChildEndpoint() == null) {
345                 return Collections.emptyList();
346             }
347             return optParent.get()
348                 .getChildEndpoint()
349                 .stream()
350                 .filter(l2Ep -> vppEndpoints.get(VppLocationUtils.vppEndpointKeyFrom(l2Ep.getKey())) != null)
351                 .map(l2Ep -> vppEndpoints.get(VppLocationUtils.vppEndpointKeyFrom(l2Ep.getKey())))
352                 .collect(Collectors.toList());
353         }
354     };
355 }