BGPCEP-685: Rework deployers
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / RIBImpl.java
1 /*
2  * Copyright (c) 2013 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 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.MoreObjects;
13 import com.google.common.base.MoreObjects.ToStringHelper;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.util.concurrent.CheckedFuture;
17 import com.google.common.util.concurrent.Futures;
18 import com.google.common.util.concurrent.ListenableFuture;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Set;
25 import javax.annotation.Nonnull;
26 import javax.annotation.concurrent.GuardedBy;
27 import javax.annotation.concurrent.ThreadSafe;
28 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
37 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
38 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
39 import org.opendaylight.mdsal.binding.generator.impl.GeneratedClassLoadingStrategy;
40 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
41 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
42 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
43 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
44 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
45 import org.opendaylight.protocol.bgp.mode.impl.base.BasePathSelectionModeFactory;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
47 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
48 import org.opendaylight.protocol.bgp.rib.impl.spi.ImportPolicyPeerTracker;
49 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
50 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContext;
51 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
52 import org.opendaylight.protocol.bgp.rib.impl.state.BGPRIBStateImpl;
53 import org.opendaylight.protocol.bgp.rib.spi.ExportPolicyPeerTracker;
54 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
55 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
56 import org.opendaylight.protocol.bgp.rib.spi.util.ClusterSingletonServiceRegistrationHelper;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.BgpTableType;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.BgpRib;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.RibId;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.Rib;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.RibKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.LocRib;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpId;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
69 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
70 import org.opendaylight.yangtools.yang.common.QName;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
75 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
78 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
79 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
80 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
81 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
82 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
85
86 @ThreadSafe
87 public final class RIBImpl extends BGPRIBStateImpl implements ClusterSingletonService, RIB, TransactionChainListener,
88         SchemaContextListener, AutoCloseable {
89     private static final Logger LOG = LoggerFactory.getLogger(RIBImpl.class);
90     private static final QName RIB_ID_QNAME = QName.create(Rib.QNAME, "id").intern();
91     private static final ContainerNode EMPTY_TABLE_ATTRIBUTES = ImmutableNodes.containerNode(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Attributes.QNAME);
92
93     private final BGPDispatcher dispatcher;
94     private final AsNumber localAs;
95     private final BgpId bgpIdentifier;
96     private final Set<BgpTableType> localTables;
97     private final Set<TablesKey> localTablesKeys;
98     private final DOMDataBroker domDataBroker;
99     private final RIBExtensionConsumerContext extensions;
100     private final YangInstanceIdentifier yangRibId;
101     private final RIBSupportContextRegistryImpl ribContextRegistry;
102     private final CodecsRegistryImpl codecsRegistry;
103     private final ServiceGroupIdentifier serviceGroupIdentifier;
104     private final ClusterSingletonServiceProvider provider;
105     private final DOMDataBrokerExtension service;
106     private final Map<TransactionChain<?, ?>, LocRibWriter> txChainToLocRibWriter = new HashMap<>();
107     private final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies;
108     private final ImportPolicyPeerTracker importPolicyPeerTracker;
109     private final RibId ribId;
110     private final Map<TablesKey, ExportPolicyPeerTracker> exportPolicyPeerTrackerMap;
111     private ClusterSingletonServiceRegistration registration;
112     private DOMTransactionChain domChain;
113     @GuardedBy("this")
114     private boolean isServiceInstantiated;
115
116     public RIBImpl(final ClusterSingletonServiceProvider provider, final RibId ribId, final AsNumber localAs, final BgpId localBgpId,
117             final ClusterIdentifier clusterId, final RIBExtensionConsumerContext extensions, final BGPDispatcher dispatcher,
118             final BindingCodecTreeFactory codecFactory, final DOMDataBroker domDataBroker, final List<BgpTableType> localTables,
119             @Nonnull final Map<TablesKey, PathSelectionMode> bestPathSelectionStrategies,
120             final GeneratedClassLoadingStrategy classStrategy) {
121         super(InstanceIdentifier.create(BgpRib.class).child(Rib.class, new RibKey(requireNonNull(ribId))),
122                 localBgpId, localAs);
123         this.localAs = requireNonNull(localAs);
124         this.bgpIdentifier = requireNonNull(localBgpId);
125         this.dispatcher = requireNonNull(dispatcher);
126         this.localTables = ImmutableSet.copyOf(localTables);
127         this.localTablesKeys = new HashSet<>();
128         this.domDataBroker = requireNonNull(domDataBroker);
129         this.service = this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
130         this.extensions = requireNonNull(extensions);
131         this.codecsRegistry = CodecsRegistryImpl.create(codecFactory, classStrategy);
132         this.ribContextRegistry = RIBSupportContextRegistryImpl.create(extensions, this.codecsRegistry);
133         final InstanceIdentifierBuilder yangRibIdBuilder = YangInstanceIdentifier.builder().node(BgpRib.QNAME).node(Rib.QNAME);
134         this.yangRibId = yangRibIdBuilder.nodeWithKey(Rib.QNAME, RIB_ID_QNAME, ribId.getValue()).build();
135         this.bestPathSelectionStrategies = requireNonNull(bestPathSelectionStrategies);
136         final ClusterIdentifier cId = clusterId == null ? new ClusterIdentifier(localBgpId) : clusterId;
137         this.ribId = ribId;
138         final PolicyDatabase policyDatabase = new PolicyDatabase(this.localAs.getValue(), localBgpId, cId);
139         this.importPolicyPeerTracker = new ImportPolicyPeerTrackerImpl(policyDatabase);
140         this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.ribId.getValue() + "-service-group");
141         requireNonNull(provider, "ClusterSingletonServiceProvider is null");
142         this.provider = provider;
143
144         final ImmutableMap.Builder<TablesKey, ExportPolicyPeerTracker> exportPolicies = new ImmutableMap.Builder<>();
145         for (final BgpTableType t : this.localTables) {
146             final TablesKey key = new TablesKey(t.getAfi(), t.getSafi());
147             this.localTablesKeys.add(key);
148             exportPolicies.put(key, new ExportPolicyPeerTrackerImpl(policyDatabase, key));
149         }
150         this.exportPolicyPeerTrackerMap = exportPolicies.build();
151         LOG.info("RIB Singleton Service {} registered, RIB {}", getIdentifier().getValue(), this.ribId.getValue());
152         //this need to be always the last step
153         this.registration = registerClusterSingletonService(this);
154     }
155
156     private void startLocRib(final TablesKey key) {
157         LOG.debug("Creating LocRib table for {}", key);
158         // create locRibWriter for each table
159         final DOMDataWriteTransaction tx = this.domChain.newWriteOnlyTransaction();
160
161         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> table = ImmutableNodes.mapEntryBuilder();
162         table.withNodeIdentifier(RibSupportUtils.toYangTablesKey(key));
163         table.withChild(EMPTY_TABLE_ATTRIBUTES);
164
165         final NodeIdentifierWithPredicates tableKey = RibSupportUtils.toYangTablesKey(key);
166         final InstanceIdentifierBuilder tableId = YangInstanceIdentifier.builder(this.yangRibId.node(LocRib.QNAME).node(Tables.QNAME));
167         tableId.nodeWithKey(tableKey.getNodeType(), tableKey.getKeyValues());
168         for (final Entry<QName, Object> e : tableKey.getKeyValues().entrySet()) {
169             table.withChild(ImmutableNodes.leafNode(e.getKey(), e.getValue()));
170         }
171
172         final RIBSupportContext supportContext = this.ribContextRegistry.getRIBSupportContext(key);
173         if (supportContext != null) {
174             final ChoiceNode routes = supportContext.getRibSupport().emptyRoutes();
175             table.withChild(routes);
176
177             tx.put(LogicalDatastoreType.OPERATIONAL, tableId.build(), table.build());
178             try {
179                 tx.submit().checkedGet();
180             } catch (final TransactionCommitFailedException e1) {
181                 LOG.error("Failed to initiate LocRIB for key {}", key, e1);
182             }
183             createLocRibWriter(key);
184         } else {
185             LOG.warn("There's no registered RIB Context for {}", key.getAfi());
186         }
187     }
188
189     private synchronized void createLocRibWriter(final TablesKey key) {
190         LOG.debug("Creating LocRIB writer for key {}", key);
191         final DOMTransactionChain txChain = createPeerChain(this);
192         PathSelectionMode pathSelectionStrategy = this.bestPathSelectionStrategies.get(key);
193         if (pathSelectionStrategy == null) {
194             pathSelectionStrategy = BasePathSelectionModeFactory.createBestPathSelectionStrategy();
195         }
196
197         final LocRibWriter locRibWriter = LocRibWriter.create(this.ribContextRegistry, key, txChain,
198                 getYangRibId(), this.localAs, getService(), this.exportPolicyPeerTrackerMap.get(key), pathSelectionStrategy);
199         registerTotalPathCounter(key, locRibWriter);
200         registerTotalPrefixesCounter(key, locRibWriter);
201         this.txChainToLocRibWriter.put(txChain, locRibWriter);
202     }
203
204     @Override
205     public String toString() {
206         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
207     }
208
209     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
210         return toStringHelper;
211     }
212
213     @Override
214     public synchronized void close() throws Exception {
215         if (this.registration != null) {
216             this.registration.close();
217             this.registration = null;
218         }
219     }
220
221     @Override
222     public AsNumber getLocalAs() {
223         return this.localAs;
224     }
225
226     @Override
227     public BgpId getBgpIdentifier() {
228         return this.bgpIdentifier;
229     }
230
231     @Nonnull
232     @Override
233     public Set<? extends BgpTableType> getLocalTables() {
234         return this.localTables;
235     }
236
237     @Override
238     public BGPDispatcher getDispatcher() {
239         return this.dispatcher;
240     }
241
242     @Override
243     public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
244         LOG.error("Broken chain in RIB {} transaction {}", getInstanceIdentifier(), transaction != null ? transaction.getIdentifier() : null, cause);
245         if (this.txChainToLocRibWriter.containsKey(chain)) {
246             final LocRibWriter locRibWriter = this.txChainToLocRibWriter.remove(chain);
247             final DOMTransactionChain newChain = createPeerChain(this);
248             locRibWriter.restart(newChain);
249             this.txChainToLocRibWriter.put(newChain, locRibWriter);
250         }
251     }
252
253     @Override
254     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
255         LOG.info("RIB {} closed successfully", getInstanceIdentifier());
256     }
257
258     @Override
259     public Set<TablesKey> getLocalTablesKeys() {
260         return this.localTablesKeys;
261     }
262
263     @Override
264     public DOMDataTreeChangeService getService() {
265         return (DOMDataTreeChangeService) this.service;
266     }
267
268     @Override
269     public YangInstanceIdentifier getYangRibId() {
270         return this.yangRibId;
271     }
272
273     @Override
274     public DOMTransactionChain createPeerChain(final TransactionChainListener listener) {
275         return this.domDataBroker.createTransactionChain(listener);
276     }
277
278     @Override
279     public RIBExtensionConsumerContext getRibExtensions() {
280         return this.extensions;
281     }
282
283     @Override
284     public RIBSupportContextRegistry getRibSupportContext() {
285         return this.ribContextRegistry;
286     }
287
288     @Override
289     public void onGlobalContextUpdated(final SchemaContext context) {
290         this.codecsRegistry.onSchemaContextUpdated(context);
291     }
292
293     @Override
294     public CodecsRegistry getCodecsRegistry() {
295         return this.codecsRegistry;
296     }
297
298     @Override
299     public ImportPolicyPeerTracker getImportPolicyPeerTracker() {
300         return this.importPolicyPeerTracker;
301     }
302
303     @Override
304     public ExportPolicyPeerTracker getExportPolicyPeerTracker(final TablesKey tablesKey) {
305         return this.exportPolicyPeerTrackerMap.get(tablesKey);
306     }
307
308     @Override
309     public synchronized void instantiateServiceInstance() {
310         this.isServiceInstantiated = true;
311         setActive(true);
312         this.domChain = this.domDataBroker.createTransactionChain(this);
313         LOG.info("RIB Singleton Service {} instantiated, RIB {}", getIdentifier().getValue(), this.ribId.getValue());
314         LOG.debug("Instantiating RIB table {} at {}", this.ribId, this.yangRibId);
315
316         final ContainerNode bgpRib = Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(BgpRib.QNAME))
317                 .addChild(ImmutableNodes.mapNodeBuilder(Rib.QNAME).build()).build();
318
319         final MapEntryNode ribInstance = Builders.mapEntryBuilder().withNodeIdentifier(
320                 new NodeIdentifierWithPredicates(Rib.QNAME, RIB_ID_QNAME, this.ribId.getValue()))
321                 .addChild(ImmutableNodes.leafNode(RIB_ID_QNAME, this.ribId.getValue()))
322                 .addChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build())
323                 .addChild(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(LocRib.QNAME))
324                         .addChild(ImmutableNodes.mapNodeBuilder(Tables.QNAME).build())
325                         .build()).build();
326
327
328         final DOMDataWriteTransaction trans = this.domChain.newWriteOnlyTransaction();
329
330         // merge empty BgpRib + Rib, to make sure the top-level parent structure is present
331         trans.merge(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.builder().node(BgpRib.QNAME).build(), bgpRib);
332         trans.put(LogicalDatastoreType.OPERATIONAL, this.yangRibId, ribInstance);
333
334         try {
335             trans.submit().checkedGet();
336         } catch (final TransactionCommitFailedException e) {
337             LOG.error("Failed to initiate RIB {}", this.yangRibId, e);
338         }
339
340         LOG.debug("Effective RIB created.");
341
342         this.localTablesKeys.forEach(this::startLocRib);
343     }
344
345     @Override
346     public synchronized ListenableFuture<Void> closeServiceInstance() {
347         if (!this.isServiceInstantiated) {
348             LOG.trace("RIB Singleton Service {} already closed, RIB {}", getIdentifier().getValue(),
349                     this.ribId.getValue());
350             return Futures.immediateFuture(null);
351         }
352         LOG.info("Close RIB Singleton Service {}, RIB {}", getIdentifier().getValue(), this.ribId.getValue());
353         this.isServiceInstantiated = false;
354         setActive(false);
355
356         this.txChainToLocRibWriter.values().forEach(LocRibWriter::close);
357         this.txChainToLocRibWriter.clear();
358
359         final DOMDataWriteTransaction t = this.domChain.newWriteOnlyTransaction();
360         t.delete(LogicalDatastoreType.OPERATIONAL, getYangRibId());
361         final CheckedFuture<Void, TransactionCommitFailedException> cleanFuture = t.submit();
362
363         this.domChain.close();
364         return cleanFuture;
365     }
366
367     @Override
368     public ServiceGroupIdentifier getIdentifier() {
369         return this.serviceGroupIdentifier;
370     }
371
372     @Override
373     public ClusterSingletonServiceRegistration registerClusterSingletonService(
374             final ClusterSingletonService clusterSingletonService) {
375         return ClusterSingletonServiceRegistrationHelper
376                 .registerSingletonService(this.provider, clusterSingletonService);
377     }
378
379     @Override
380     public ServiceGroupIdentifier getRibIServiceGroupIdentifier() {
381         return getIdentifier();
382     }
383 }