82496: Pre-create FRM Nodes and Tables
[genius.git] / interfacemanager / interfacemanager-impl / src / main / java / org / opendaylight / genius / interfacemanager / FrmNodeAndTablesBuilder.java
1 /*
2  * Copyright (c) 2020 Ericsson India Global Services Pvt Ltd. 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.genius.interfacemanager;
10
11 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
12
13 import com.google.common.base.Preconditions;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import java.util.concurrent.ExecutionException;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.opendaylight.mdsal.binding.api.DataBroker;
20 import org.opendaylight.mdsal.binding.util.Datastore.Configuration;
21 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
22 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
23 import org.opendaylight.mdsal.binding.util.TypedReadWriteTransaction;
24 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
25 import org.opendaylight.openflowplugin.applications.reconciliation.NotificationRegistration;
26 import org.opendaylight.openflowplugin.applications.reconciliation.ReconciliationManager;
27 import org.opendaylight.openflowplugin.applications.reconciliation.ReconciliationNotificationListener;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.rf.state.rev170713.ResultState;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
39 import org.opendaylight.yangtools.yang.common.Uint64;
40 import org.opendaylight.yangtools.yang.common.Uint8;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * Pre-creates the nodes and tables in the FRM config/inventory datastore
46  * whenever a switch is connected. This class implements the callback provided
47  * by openflowplugin to do the node and tables pre-creation.
48  */
49 @Singleton
50 public class FrmNodeAndTablesBuilder implements ReconciliationNotificationListener {
51
52     private static final Logger LOGGER = LoggerFactory.getLogger(FrmNodeAndTablesBuilder.class);
53
54     private static final String SERVICE_NAME = "FrmNodeAndTablesBuilder";
55     private static final short CANARY_TABLE_ID = 255;
56     private static final int SLEEP_BETWEEN_RETRIES = 1000;
57     private static final int MAX_RETRIES = 3;
58     private static final int TASK_PRIORITY = 2;
59
60     private final DataBroker dataBroker;
61     private NotificationRegistration registration;
62     private final ManagedNewTransactionRunner txRunner;
63
64     @Inject
65     public FrmNodeAndTablesBuilder(DataBroker dataBroker,
66                                    ReconciliationManager reconciliationManager) {
67         this.dataBroker = dataBroker;
68         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
69         reconciliationManager = Preconditions.checkNotNull(reconciliationManager,
70                 "ReconciliationManager cannot be null!");
71         reconciliationManager.registerService(this);
72         LOGGER.info("FrmNodeAndTablesBuilder has started successfully.");
73     }
74
75     @Override
76     public void close() throws Exception {
77         if (registration != null) {
78             registration.close();
79             registration = null;
80         }
81     }
82
83     @Override
84     public ListenableFuture<Boolean> startReconciliation(DeviceInfo connectedNode) {
85         return Futures.immediateFuture(preCreateInventoryNodeAndTables(connectedNode, MAX_RETRIES,
86                 SLEEP_BETWEEN_RETRIES));
87     }
88
89     @Override
90     public ListenableFuture<Boolean> endReconciliation(DeviceInfo node) {
91         return Futures.immediateFuture(true);
92     }
93
94     @Override
95     public String getName() {
96         return SERVICE_NAME;
97     }
98
99     @Override
100     public ResultState getResultState() {
101         return ResultState.DONOTHING;
102     }
103
104     @Override
105     public int getPriority() {
106         return TASK_PRIORITY;
107     }
108
109     private boolean preCreateInventoryNodeAndTables(DeviceInfo connectedNode, int maxRetries,
110                                                     int sleepBetweenRetries) {
111         Uint64 dpnId = getDpnIdFromNodeIdent(connectedNode);
112         Node nodeDpn = buildDpnNode(dpnId);
113         LOGGER.info("Pre-creating FRM node and tables for {}", dpnId);
114         while (true) {
115             ListenableFuture<?> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
116                 if (!isTableAlreadyPrecreated(tx, nodeDpn)) {
117                     LOGGER.info("{} getting connected for first time,"
118                             + "proceed with FRM node and table pre-create", dpnId);
119                     for (short tableId = 0; tableId <= 255; tableId++) {
120                         InstanceIdentifier<Table> tableIId = InstanceIdentifier.builder(Nodes.class)
121                                 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
122                                 .child(Table.class, new TableKey(Uint8.valueOf(tableId))).build();
123                         tx.mergeParentStructureMerge(tableIId,
124                                 new TableBuilder().withKey(new TableKey(Uint8.valueOf(tableId))).build());
125                     }
126                     LOGGER.info("Pre-creating FRM node and tables for {} is finished", dpnId);
127                 } else {
128                     LOGGER.info("FRM node and tables already present for {}", dpnId);
129                 }
130             });
131             try {
132                 future.get();
133                 return true;
134             } catch (InterruptedException | ExecutionException ex) {
135                 LOGGER.error("Pre-creating FRM node and tables for {} failed, retrying.. {}", dpnId, maxRetries);
136                 maxRetries--;
137                 if (maxRetries > 0) {
138                     try {
139                         Thread.sleep(sleepBetweenRetries);
140                     } catch (InterruptedException e) {
141                         continue;
142                     }
143                 } else {
144                     return false;
145                 }
146             }
147         }
148     }
149
150     private boolean isTableAlreadyPrecreated(TypedReadWriteTransaction<Configuration> tx, Node nodeDpn)
151             throws ExecutionException, InterruptedException {
152         InstanceIdentifier<Table> tableIId = InstanceIdentifier.builder(Nodes.class)
153                 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
154                 .child(Table.class, new TableKey(CANARY_TABLE_ID)).build();
155         return tx.exists(tableIId).get();
156     }
157
158     private Uint64 getDpnIdFromNodeIdent(final DeviceInfo nodeIdentity) {
159         Uint64 nodeName = nodeIdentity.getDatapathId();
160         return nodeName;
161     }
162
163     protected Node buildDpnNode(Uint64 dpnId) {
164         NodeId nodeId = new NodeId("openflow:" + dpnId);
165         Node nodeDpn = new NodeBuilder().setId(nodeId).withKey(new NodeKey(nodeId)).build();
166         return nodeDpn;
167     }
168
169 }