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