CDS: Add stress test RPC to the cars model
[controller.git] / opendaylight / netconf / config-netconf-connector / src / main / java / org / opendaylight / controller / netconf / confignetconfconnector / transactions / TransactionProvider.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
9 package org.opendaylight.controller.netconf.confignetconfconnector.transactions;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Set;
16 import javax.management.InstanceNotFoundException;
17 import javax.management.ObjectName;
18 import org.opendaylight.controller.config.api.ConflictingVersionException;
19 import org.opendaylight.controller.config.api.ValidationException;
20 import org.opendaylight.controller.config.api.jmx.CommitStatus;
21 import org.opendaylight.controller.config.util.ConfigRegistryClient;
22 import org.opendaylight.controller.config.util.ConfigTransactionClient;
23 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
24 import org.opendaylight.controller.netconf.confignetconfconnector.exception.NoTransactionFoundException;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 public class TransactionProvider implements AutoCloseable {
29     private static final Logger LOG = LoggerFactory.getLogger(TransactionProvider.class);
30
31     private final ConfigRegistryClient configRegistryClient;
32
33     private final String netconfSessionIdForReporting;
34     private ObjectName candidateTx;
35     private ObjectName readTx;
36     private final List<ObjectName> allOpenedTransactions = new ArrayList<>();
37     private static final String  NO_TRANSACTION_FOUND_FOR_SESSION = "No transaction found for session ";
38
39     public TransactionProvider(ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) {
40         this.configRegistryClient = configRegistryClient;
41         this.netconfSessionIdForReporting = netconfSessionIdForReporting;
42     }
43
44     @Override
45     public synchronized void close() {
46         for (ObjectName tx : allOpenedTransactions) {
47             try {
48                 if (isStillOpenTransaction(tx)) {
49                     configRegistryClient.getConfigTransactionClient(tx).abortConfig();
50                 }
51             } catch (Exception e) {
52                 LOG.debug("Ignoring exception while closing transaction {}", tx, e);
53             }
54         }
55         allOpenedTransactions.clear();
56     }
57
58     public synchronized Optional<ObjectName> getTransaction() {
59
60         if (candidateTx == null){
61             return Optional.absent();
62         }
63
64         // Transaction was already closed somehow
65         if (!isStillOpenTransaction(candidateTx)) {
66             LOG.warn("Fixing illegal state: transaction {} was closed in {}", candidateTx,
67                     netconfSessionIdForReporting);
68             candidateTx = null;
69             return Optional.absent();
70         }
71         return Optional.of(candidateTx);
72     }
73
74     public synchronized Optional<ObjectName> getReadTransaction() {
75
76         if (readTx == null){
77             return Optional.absent();
78         }
79
80         // Transaction was already closed somehow
81         if (!isStillOpenTransaction(readTx)) {
82             LOG.warn("Fixing illegal state: transaction {} was closed in {}", readTx,
83                     netconfSessionIdForReporting);
84             readTx = null;
85             return Optional.absent();
86         }
87         return Optional.of(readTx);
88     }
89
90     private boolean isStillOpenTransaction(ObjectName transaction) {
91         return configRegistryClient.getOpenConfigs().contains(transaction);
92     }
93
94     public synchronized ObjectName getOrCreateTransaction() {
95         Optional<ObjectName> ta = getTransaction();
96
97         if (ta.isPresent()) {
98             return ta.get();
99         }
100         candidateTx = configRegistryClient.beginConfig();
101         allOpenedTransactions.add(candidateTx);
102         return candidateTx;
103     }
104
105     public synchronized ObjectName getOrCreateReadTransaction() {
106         Optional<ObjectName> ta = getReadTransaction();
107
108         if (ta.isPresent()) {
109             return ta.get();
110         }
111         readTx = configRegistryClient.beginConfig();
112         allOpenedTransactions.add(readTx);
113         return readTx;
114     }
115
116     /**
117      * Used for editConfig test option
118      */
119     public synchronized ObjectName getTestTransaction() {
120         ObjectName testTx = configRegistryClient.beginConfig();
121         allOpenedTransactions.add(testTx);
122         return testTx;
123     }
124
125     /**
126      * Commit and notification send must be atomic
127      */
128     public synchronized CommitStatus commitTransaction() throws ValidationException, ConflictingVersionException, NoTransactionFoundException {
129         if (!getTransaction().isPresent()){
130             throw new NoTransactionFoundException("No transaction found for session " + netconfSessionIdForReporting,
131                     NetconfDocumentedException.ErrorType.application,
132                     NetconfDocumentedException.ErrorTag.operation_failed,
133                     NetconfDocumentedException.ErrorSeverity.error);
134         }
135         final Optional<ObjectName> maybeTaON = getTransaction();
136         ObjectName taON = maybeTaON.get();
137         try {
138             CommitStatus status = configRegistryClient.commitConfig(taON);
139             // clean up
140             allOpenedTransactions.remove(candidateTx);
141             candidateTx = null;
142             return status;
143         } catch (ValidationException validationException) {
144             // no clean up: user can reconfigure and recover this transaction
145             LOG.warn("Transaction {} failed on {}", taON, validationException.toString());
146             throw validationException;
147         } catch (ConflictingVersionException e) {
148             LOG.error("Exception while commit of {}, aborting transaction", taON, e);
149             // clean up
150             abortTransaction();
151             throw e;
152         }
153     }
154
155     public synchronized void abortTransaction() {
156         LOG.debug("Aborting current transaction");
157         Optional<ObjectName> taON = getTransaction();
158         Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting);
159
160         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
161         transactionClient.abortConfig();
162         allOpenedTransactions.remove(candidateTx);
163         candidateTx = null;
164     }
165
166     public synchronized void closeReadTransaction() {
167         LOG.debug("Closing read transaction");
168         Optional<ObjectName> taON = getReadTransaction();
169         Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting);
170
171         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
172         transactionClient.abortConfig();
173         allOpenedTransactions.remove(readTx);
174         readTx = null;
175     }
176
177     public synchronized void abortTestTransaction(ObjectName testTx) {
178         LOG.debug("Aborting transaction {}", testTx);
179         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(testTx);
180         allOpenedTransactions.remove(testTx);
181         transactionClient.abortConfig();
182     }
183
184     public void validateTransaction() throws ValidationException {
185         Optional<ObjectName> taON = getTransaction();
186         Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting);
187
188         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
189         transactionClient.validateConfig();
190     }
191
192     public void validateTestTransaction(ObjectName taON) throws ValidationException {
193         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
194         transactionClient.validateConfig();
195     }
196
197     public void wipeTestTransaction(ObjectName taON) {
198         wipeInternal(taON, true);
199     }
200
201     /**
202      * Wiping means removing all module instances keeping the transaction open + service references.
203      */
204     synchronized void wipeInternal(ObjectName taON, boolean isTest) {
205         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
206
207         Set<ObjectName> lookupConfigBeans = transactionClient.lookupConfigBeans();
208         int i = lookupConfigBeans.size();
209         for (ObjectName instance : lookupConfigBeans) {
210             try {
211                 transactionClient.destroyModule(instance);
212             } catch (InstanceNotFoundException e) {
213                 if (isTest){
214                     LOG.debug("Unable to clean configuration in transactiom {}", taON, e);
215                 } else {
216                     LOG.warn("Unable to clean configuration in transactiom {}", taON, e);
217                 }
218
219                 throw new IllegalStateException("Unable to clean configuration in transactiom " + taON, e);
220             }
221         }
222         LOG.debug("Transaction {} wiped clean of {} config beans", taON, i);
223
224         transactionClient.removeAllServiceReferences();
225         LOG.debug("Transaction {} wiped clean of all service references", taON);
226     }
227
228     public void wipeTransaction() {
229         Optional<ObjectName> taON = getTransaction();
230         Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting);
231         wipeInternal(taON.get(), false);
232     }
233
234 }