Bug 8228 - metadata service fix made cleaner
[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.HashMap;
14 import java.util.Map;
15 import java.util.concurrent.ExecutionException;
16 import java.util.stream.Collectors;
17
18 import javax.annotation.Nonnull;
19
20 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
23 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
24 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
25 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.groupbasedpolicy.renderer.vpp.manager.VppNodeManager;
28 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.CloseOnFailTransactionChain;
29 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory;
30 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
31 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
32 import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
33 import org.opendaylight.groupbasedpolicy.util.EndpointUtils;
34 import org.opendaylight.groupbasedpolicy.util.IidFactory;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.address.endpoints.AddressEndpointKey;
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.has.absolute.location.AbsoluteLocation;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocationBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCaseBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.relative.location.RelativeLocations;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.relative.location.RelativeLocationsBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.relative.location.relative.locations.ExternalLocationBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.parent.child.endpoints.parent.endpoint.choice.parent.endpoint._case.ParentEndpoint;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.parent.child.endpoints.parent.endpoint.choice.parent.endpoint._case.ParentEndpointKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.ProviderName;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProvider;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProviderBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocation;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocationBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocationKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocationKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
60 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 import com.google.common.annotations.VisibleForTesting;
65 import com.google.common.base.Function;
66 import com.google.common.util.concurrent.FutureCallback;
67 import com.google.common.util.concurrent.Futures;
68 import com.google.common.util.concurrent.ListenableFuture;
69
70 public class VppEndpointLocationProvider extends DataTreeChangeHandler<AddressEndpoint> {
71
72     private static final Logger LOG = LoggerFactory.getLogger(VppEndpointLocationProvider.class);
73     public static final ProviderName VPP_ENDPOINT_LOCATION_PROVIDER =
74             new ProviderName("VPP endpoint location provider");
75     public static final long PROVIDER_PRIORITY = 10L;
76     private final BindingTransactionChain txChain;
77     private final Map<VppEndpointKey, VppEndpoint> vppEndpoints = new HashMap<>();
78     private final Map<VppEndpointKey, DataObjectModification<AddressEndpoint>> cachedVppEndpoints = new HashMap<>();
79
80     public VppEndpointLocationProvider(DataBroker dataProvider) {
81         super(dataProvider);
82         LocationProvider locationProvider = new LocationProviderBuilder().setProvider(VPP_ENDPOINT_LOCATION_PROVIDER)
83             .setPriority(PROVIDER_PRIORITY)
84             .build();
85         txChain = checkNotNull(dataProvider)
86             .createTransactionChain(new CloseOnFailTransactionChain(VppEndpointLocationProvider.class.getSimpleName()));
87         WriteTransaction wTx = txChain.newWriteOnlyTransaction();
88         wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER),
89                 locationProvider, true);
90
91         Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
92
93             @Override
94             public void onSuccess(Void result) {
95                 LOG.debug("{} was created", VPP_ENDPOINT_LOCATION_PROVIDER.getValue());
96             }
97
98             @Override
99             public void onFailure(Throwable t) {
100                 LOG.error("{} was NOT created", VPP_ENDPOINT_LOCATION_PROVIDER.getValue());
101             }
102         });
103         registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
104                 InstanceIdentifier.builder(Endpoints.class).child(AddressEndpoints.class).child(AddressEndpoint.class).build()));
105     }
106
107     @Override
108     protected void onWrite(DataObjectModification<AddressEndpoint> rootNode,
109             InstanceIdentifier<AddressEndpoint> rootIdentifier) {
110         LOG.debug("onWrite triggered by {}", rootNode.getDataAfter());
111         try {
112             if (EndpointUtils.isExternalEndpoint(dataProvider, rootNode.getDataAfter())) {
113                 writeLocation(createRelativeAddressEndpointLocation(rootNode.getDataAfter().getKey(),
114                         VppNodeManager.resolvePublicInterfaces(dataProvider))).get();
115             } else {
116                 createAbsoluteAddressEndpointLocation(null, rootNode).get();
117             }
118         } catch (InterruptedException | ExecutionException e) {
119             LOG.error("Failed to write location for endpoint {}. {}", rootNode.getDataAfter().getKey(), e.getMessage());
120         }
121     }
122
123     @Override
124     protected void onDelete(DataObjectModification<AddressEndpoint> rootNode,
125             InstanceIdentifier<AddressEndpoint> rootIdentifier) {
126         LOG.debug("onDelete triggered by {}", rootNode.getDataBefore());
127         try {
128             if (EndpointUtils.isExternalEndpoint(dataProvider, rootNode.getDataBefore())) {
129                 deleteLocation(createProviderAddressEndpointLocationKey(rootNode.getDataBefore().getKey())).get();
130             } else {
131                 createAbsoluteAddressEndpointLocation(null, rootNode).get();
132             }
133         } catch (InterruptedException | ExecutionException e) {
134             LOG.error("Failed to delete location for endpoint {}. {}", rootNode.getDataBefore().getKey(),
135                     e.getMessage());
136         }
137     }
138
139     @Override
140     protected void onSubtreeModified(DataObjectModification<AddressEndpoint> rootNode,
141             InstanceIdentifier<AddressEndpoint> rootIdentifier) {
142         LOG.debug("onSubtreeModified triggered by change: before={} after={}", rootNode.getDataBefore(),
143                 rootNode.getDataAfter());
144         if (rootNode.getDataBefore() != null) {
145             onDelete(rootNode, rootIdentifier);
146         }
147         if (rootNode.getDataAfter() != null) {
148             onWrite(rootNode, rootIdentifier);
149         }
150     }
151
152     public ListenableFuture<Void> createLocationForVppEndpoint(VppEndpoint vppEndpoint) {
153         return createAbsoluteAddressEndpointLocation(vppEndpoint, null);
154     }
155
156     public ListenableFuture<Void> deleteLocationForVppEndpoint(VppEndpoint vppEndpoint) {
157         // removing VPP EP from cache out of since block, it's not needed for the other thread.
158         vppEndpoints.remove(vppEndpoint.getKey());
159         return deleteLocation(createProviderAddressEndpointLocationKey(vppEndpoint));
160     }
161
162     /**
163      * There are two inputs from which we need to resolve location - {@link AddressEndpoint} and {@link VppEndpoint}
164      * These data are delivered by different threads which meet here.
165      */
166     @VisibleForTesting
167     synchronized ListenableFuture<Void> createAbsoluteAddressEndpointLocation(VppEndpoint vppEndpoint,
168             DataObjectModification<AddressEndpoint> rootNode) {
169         if (vppEndpoint != null) {
170             vppEndpoints.put(vppEndpoint.getKey(), vppEndpoint);
171             if (cachedVppEndpoints.get(vppEndpoint.getKey()) != null) {
172                 return processAddrEp(cachedVppEndpoints.get(vppEndpoint.getKey()));
173             }
174         } else if (rootNode != null) {
175             return processAddrEp(rootNode);
176         }
177         return Futures.immediateFuture(null);
178     }
179
180     private ListenableFuture<Void> processAddrEp(DataObjectModification<AddressEndpoint> rootNode) {
181         if (rootNode != null) {
182             AddressEndpointChange aec = new AddressEndpointChange(rootNode, dataProvider);
183             switch (rootNode.getModificationType()) {
184                 case WRITE:
185                 case SUBTREE_MODIFIED: {
186                     VppEndpoint vpp = vppEndpoints.get(vppEndpointKeyFrom(rootNode.getDataAfter().getKey()));
187                     if (vpp == null) {
188                         VppEndpointKey key = vppEndpointKeyFrom(rootNode.getDataAfter().getKey());
189                         cachedVppEndpoints.put(key, rootNode);
190                         return Futures.immediateFuture(null);
191                     }
192                     if (aec.hasMoreParents()) {
193                         return aec.syncMultiparents();
194                     }
195                         return aec.write();
196                 }
197                 case DELETE: {
198                     if (aec.hasMoreParents()) {
199                         return aec.syncMultiparents();
200                     } else {
201                         return aec.delete();
202                     }
203                 }
204             }
205         }
206         return Futures.immediateFuture(null);
207     }
208
209     private ProviderAddressEndpointLocation createAbsoluteLocationFromVppEndpoint(VppEndpoint vppEndpoint) {
210         InstanceIdentifier<Node> vppNodeIid = VppIidFactory.getNetconfNodeIid(vppEndpoint.getVppNodeId());
211         String restIfacePath = VppPathMapper.interfaceToRestPath(vppEndpoint.getVppInterfaceName());
212         AbsoluteLocation absoluteLocation =
213                 new AbsoluteLocationBuilder().setLocationType(new ExternalLocationCaseBuilder()
214                     .setExternalNodeMountPoint(vppNodeIid).setExternalNodeConnector(restIfacePath).build()).build();
215         return new ProviderAddressEndpointLocationBuilder()
216             .setKey(createProviderAddressEndpointLocationKey(vppEndpoint))
217             .setAbsoluteLocation(absoluteLocation)
218             .build();
219     }
220
221     public ProviderAddressEndpointLocation createRelativeAddressEndpointLocation(@Nonnull AddressEndpointKey addrEp,
222             @Nonnull Map<NodeId, String> publicIntfNamesByNodes) {
223         RelativeLocations relLocations =
224                 new RelativeLocationsBuilder()
225                     .setExternalLocation(publicIntfNamesByNodes.keySet()
226                         .stream()
227                         .filter(nodeId -> publicIntfNamesByNodes.get(nodeId) != null)
228                         .map(nodeId -> new ExternalLocationBuilder()
229                             .setExternalNodeMountPoint(VppIidFactory.getNetconfNodeIid(nodeId))
230                             .setExternalNodeConnector(
231                                     VppPathMapper.interfaceToRestPath(publicIntfNamesByNodes.get(nodeId)))
232                             .build())
233                         .collect(Collectors.toList()))
234                     .build();
235         return new ProviderAddressEndpointLocationBuilder().setKey(createProviderAddressEndpointLocationKey(addrEp))
236             .setRelativeLocations(relLocations)
237             .build();
238     }
239
240     public ListenableFuture<Void> writeLocation(ProviderAddressEndpointLocation location) {
241         WriteTransaction wTx = txChain.newWriteOnlyTransaction();
242         wTx.put(LogicalDatastoreType.CONFIGURATION,
243                 IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER, location.getKey()),
244                 location, true);
245         return Futures.transform(wTx.submit(), new Function<Void, Void>() {
246
247             @Override
248             public Void apply(Void input) {
249                 LOG.debug("{} provided location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(), location);
250                 return null;
251             }
252         });
253     }
254
255     public ListenableFuture<Void> deleteLocation(ProviderAddressEndpointLocationKey key) {
256         ReadWriteTransaction rwTx = txChain.newReadWriteTransaction();
257         LOG.debug("Deleting location for {}", key);
258         DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION,
259                 IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER, key), rwTx);
260         return Futures.transform(rwTx.submit(), new Function<Void, Void>() {
261
262             @Override
263             public Void apply(Void input) {
264                 LOG.debug("{} removed location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(), key);
265                 return null;
266             }
267         });
268     }
269
270     public ListenableFuture<Void> replaceLocationForEndpoint(@Nonnull ExternalLocationCase location, @Nonnull AddressEndpointWithLocationKey addrEpWithLocKey) {
271         ProviderAddressEndpointLocationKey provAddrEpLocKey =
272                 KeyFactory.providerAddressEndpointLocationKey(addrEpWithLocKey);
273         AbsoluteLocation absoluteLocation =
274                 new AbsoluteLocationBuilder().setLocationType(location).build();
275         ProviderAddressEndpointLocation providerAddressEndpointLocation = new ProviderAddressEndpointLocationBuilder()
276             .setKey(provAddrEpLocKey).setAbsoluteLocation(absoluteLocation).build();
277         WriteTransaction wTx = txChain.newWriteOnlyTransaction();
278         wTx.put(LogicalDatastoreType.CONFIGURATION,
279                 IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
280                         providerAddressEndpointLocation.getKey()),
281                 providerAddressEndpointLocation);
282         LOG.debug("Updating location for {}", provAddrEpLocKey);
283         return Futures.transform(wTx.submit(), new Function<Void, Void>() {
284
285             @Override
286             public Void apply(Void input) {
287                 LOG.debug("{} replaced location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(),
288                         providerAddressEndpointLocation);
289                 return null;
290             }
291         });
292     }
293
294     static ProviderAddressEndpointLocationKey createProviderAddressEndpointLocationKey(VppEndpoint vpp) {
295         return new ProviderAddressEndpointLocationKey(vpp.getAddress(), vpp.getAddressType(), vpp.getContextId(),
296                 vpp.getContextType());
297     }
298
299     static ProviderAddressEndpointLocationKey createProviderAddressEndpointLocationKey(AddressEndpointKey key) {
300         return new ProviderAddressEndpointLocationKey(key.getAddress(), key.getAddressType(), key.getContextId(),
301                 key.getContextType());
302     }
303
304     private static ProviderAddressEndpointLocationKey createProviderAddressEndpointLocationKey(ParentEndpointKey key) {
305         return new ProviderAddressEndpointLocationKey(key.getAddress(), key.getAddressType(), key.getContextId(),
306                 key.getContextType());
307     }
308
309     private VppEndpointKey vppEndpointKeyFrom(AddressEndpointKey key) {
310         return new VppEndpointKey(key.getAddress(), key.getAddressType(), key.getContextId(), key.getContextType());
311     }
312
313     private VppEndpointKey vppEndpointKeyFrom(ParentEndpointKey key) {
314         return new VppEndpointKey(key.getAddress(), key.getAddressType(), key.getContextId(), key.getContextType());
315     }
316
317     @Override
318     public void close() {
319         super.closeRegisteredListener();
320         WriteTransaction wTx = txChain.newWriteOnlyTransaction();
321         wTx.delete(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER));
322         wTx.submit();
323     }
324
325     private class AddressEndpointChange {
326
327         private final AddressEndpoint before;
328         private final AddressEndpoint after;
329         private final DataBroker dataBroker;
330
331         public AddressEndpointChange(DataObjectModification<AddressEndpoint> addrEp, @Nonnull DataBroker dataBroker) {
332             this.before = addrEp.getDataBefore();
333             this.after = addrEp.getDataAfter();
334             this.dataBroker = dataBroker;
335         }
336
337         boolean hasMoreParents() {
338             return (before != null && EndpointUtils.getParentEndpoints(before.getParentEndpointChoice()).size() > 1)
339                     || (after != null && EndpointUtils.getParentEndpoints(after.getParentEndpointChoice()).size() > 1);
340         }
341
342         ListenableFuture<Void> syncMultiparents() {
343             ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();
344             if (before != null) {
345                 for (ParentEndpoint pe : EndpointUtils.getParentEndpoints(before.getParentEndpointChoice())) {
346                     InstanceIdentifier<ProviderAddressEndpointLocation> iid =
347                             IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
348                                     createProviderAddressEndpointLocationKey(pe.getKey()));
349                     DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, iid, rwTx);
350                 }
351             }
352             if (after != null) {
353                 for (ParentEndpoint pe : EndpointUtils.getParentEndpoints(after.getParentEndpointChoice())) {
354                     VppEndpoint vppEndpoint = vppEndpoints.get(vppEndpointKeyFrom(after.getKey()));
355                     InstanceIdentifier<ProviderAddressEndpointLocation> iid =
356                             IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER,
357                                     createProviderAddressEndpointLocationKey(pe.getKey()));
358                     ProviderAddressEndpointLocation location = createAbsoluteLocationFromVppEndpoint(
359                             new VppEndpointBuilder(vppEndpoint).setKey(vppEndpointKeyFrom(pe.getKey())).build());
360                     rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true);
361                 }
362             }
363             return rwTx.submit();
364         }
365
366         ListenableFuture<Void> write() {
367             VppEndpoint vpp = vppEndpoints.get(vppEndpointKeyFrom(after.getKey()));
368             WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
369             ProviderAddressEndpointLocation location =
370                     createAbsoluteLocationFromVppEndpoint(vpp);
371             InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.providerAddressEndpointLocationIid(
372                     VPP_ENDPOINT_LOCATION_PROVIDER, createProviderAddressEndpointLocationKey(vpp));
373             wTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true);
374             return wTx.submit();
375         }
376
377         ListenableFuture<Void> delete() {
378             ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();
379             InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.providerAddressEndpointLocationIid(
380                     VPP_ENDPOINT_LOCATION_PROVIDER, createProviderAddressEndpointLocationKey(before.getKey()));
381             DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, iid, rwTx);
382             return rwTx.submit();
383         }
384     }
385 }