transcriber: consolidate getAll() logic
[neutron.git] / transcriber / src / main / java / org / opendaylight / neutron / transcriber / AbstractNeutronInterface.java
1 /*
2  * Copyright (c) 2015 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.neutron.transcriber;
10
11 import java.lang.reflect.Method;
12 import java.util.ArrayList;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Set;
16
17 import java.util.concurrent.ExecutionException;
18
19 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
22 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
26 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
27 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
28 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
29 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
30 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
32 import org.opendaylight.yangtools.yang.binding.Augmentable;
33 import org.opendaylight.yangtools.yang.binding.ChildOf;
34 import org.opendaylight.yangtools.yang.binding.DataObject;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import com.google.common.base.Optional;
40 import com.google.common.base.Preconditions;
41 import com.google.common.util.concurrent.CheckedFuture;
42
43 import org.opendaylight.neutron.spi.INeutronCRUD;
44 import org.opendaylight.neutron.spi.INeutronObject;
45
46 public abstract class AbstractNeutronInterface<T extends DataObject, U extends ChildOf<? extends DataObject> & Augmentable<U>, S extends INeutronObject> implements AutoCloseable, INeutronCRUD<S>, TransactionChainListener {
47     private static final Logger LOGGER = LoggerFactory.getLogger(AbstractNeutronInterface.class);
48     private static final int DEDASHED_UUID_LENGTH = 32;
49     private static final int DEDASHED_UUID_START = 0;
50     private static final int DEDASHED_UUID_DIV1 = 8;
51     private static final int DEDASHED_UUID_DIV2 = 12;
52     private static final int DEDASHED_UUID_DIV3 = 16;
53     private static final int DEDASHED_UUID_DIV4 = 20;
54
55     private DataBroker db;
56
57     AbstractNeutronInterface(ProviderContext providerContext) {
58         this.db = providerContext.getSALService(DataBroker.class);
59     }
60
61     public DataBroker getDataBroker() {
62         return db;
63     }
64
65     public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause) {
66         LOGGER.error("Broken chain {} in TxchainDomWrite, transaction {}, cause {}",
67                      chain, transaction.getIdentifier(), cause);
68     }
69
70     public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
71         LOGGER.debug("Chain {} closed successfully", chain);
72     }
73
74     public BindingTransactionChain createTransactionChain() {
75         return getDataBroker().createTransactionChain(this);
76     }
77
78     protected interface Action0<U> {
79         public U action(BindingTransactionChain chain);
80     }
81
82     protected <U> U chainWrapper0(BindingTransactionChain chain,
83                                   Action0<U> action) {
84         if (chain != null) {
85             return action.action(chain);
86         }
87         try (BindingTransactionChain newChain = this.createTransactionChain()) {
88             return action.action(newChain);
89         }
90     }
91
92     protected interface Action1<U, V> {
93         public U action(V input, BindingTransactionChain chain);
94     }
95
96     protected <U, V> U chainWrapper1(V input, BindingTransactionChain chain,
97                                      Action1<U, V> action) {
98         if (chain != null) {
99             return action.action(input, chain);
100         }
101         try (BindingTransactionChain newChain = this.createTransactionChain()) {
102             return action.action(input, newChain);
103         }
104     }
105
106     protected interface Action2<U, V, W> {
107         public U action(V input0, W input1, BindingTransactionChain chain);
108     }
109
110     protected <U, V, W> U chainWrapper2(V input0, W input1,
111                                         BindingTransactionChain chain,
112                                         Action2<U, V, W> action) {
113         if (chain != null) {
114             return action.action(input0, input1, chain);
115         }
116         try (BindingTransactionChain newChain = this.createTransactionChain()) {
117             return action.action(input0, input1, newChain);
118         }
119     }
120
121     protected abstract InstanceIdentifier<T> createInstanceIdentifier(T item);
122
123     protected abstract InstanceIdentifier<U> createInstanceIdentifier();
124
125     protected abstract T toMd(S neutronObject);
126
127     protected abstract T toMd(String uuid);
128
129     protected abstract S fromMd(T dataObject);
130
131     private <T extends DataObject> T _readMd(InstanceIdentifier<T> path, BindingTransactionChain chain) {
132         Preconditions.checkNotNull(chain);
133         T result = null;
134         final ReadOnlyTransaction transaction = chain.newReadOnlyTransaction();
135         CheckedFuture<Optional<T>, ReadFailedException> future = transaction.read(LogicalDatastoreType.CONFIGURATION, path);
136         if (future != null) {
137             Optional<T> optional;
138             try {
139                 optional = future.checkedGet();
140                 if (optional.isPresent()) {
141                     result = optional.get();
142                 }
143             } catch (ReadFailedException e) {
144                 LOGGER.warn("Failed to read {}", path, e);
145             }
146         }
147         transaction.close();
148         return result;
149     }
150
151     protected <T extends DataObject> T readMd(InstanceIdentifier<T> path, BindingTransactionChain chain) {
152         return chainWrapper1(path, chain,
153                              new Action1<T, InstanceIdentifier<T>>() {
154                                  @Override
155                                  public T action(InstanceIdentifier<T> path, BindingTransactionChain chain) {
156                                      return _readMd(path, chain);
157                                  }
158                              });
159     }
160
161     protected <T extends DataObject> T readMd(InstanceIdentifier<T> path) {
162         return readMd(path, null);
163     }
164
165     protected boolean addMd(S neutronObject, BindingTransactionChain chain) {
166         // TODO think about adding existence logic
167         return updateMd(neutronObject, chain);
168     }
169
170     protected boolean addMd(S neutronObject) {
171         return addMd(neutronObject, null);
172     }
173
174    protected boolean _updateMd(S neutronObject, BindingTransactionChain chain) {
175         Preconditions.checkNotNull(chain);
176
177         /*
178          * retry for transaction conflict.
179          * see the comment
180          * org.opendaylight.controller.sal.restconf.impl.RestconfImpl#updateConfigurationData
181          */
182         int retries = 2;
183         while (true) {
184             WriteTransaction transaction = chain.newWriteOnlyTransaction();
185             T item = toMd(neutronObject);
186             InstanceIdentifier<T> iid = createInstanceIdentifier(item);
187             transaction.put(LogicalDatastoreType.CONFIGURATION, iid, item, true);
188             CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit();
189             try {
190                 future.get();
191             } catch (InterruptedException | ExecutionException e) {
192                 if (e.getCause() instanceof OptimisticLockFailedException) {
193                     if(--retries >= 0) {
194                         LOGGER.debug("Got OptimisticLockFailedException - trying again {}", neutronObject);
195                         continue;
196                     }
197                     LOGGER.warn("Got OptimisticLockFailedException on last try - failing {}", neutronObject);
198                 }
199                 LOGGER.warn("Transation failed ", e);
200                 return false;
201             }
202             break;
203         }
204         return true;
205     }
206
207     protected boolean updateMd(S neutronObject, BindingTransactionChain chain) {
208         return chainWrapper1(neutronObject, chain,
209                              new Action1<Boolean, S>() {
210                                  @Override
211                                  public Boolean action(S neutronObject, BindingTransactionChain chain) {
212                                      return _updateMd(neutronObject, chain);
213                                  }
214                              }).booleanValue();
215     }
216
217     protected boolean updateMd(S neutronObject) {
218         return updateMd(neutronObject, null);
219     }
220
221     private boolean _removeMd(T item, BindingTransactionChain chain) {
222         Preconditions.checkNotNull(chain);
223         WriteTransaction transaction = chain.newWriteOnlyTransaction();
224         InstanceIdentifier<T> iid = createInstanceIdentifier(item);
225         transaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
226         CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit();
227         try {
228             future.get();
229         } catch (InterruptedException | ExecutionException e) {
230             LOGGER.warn("Transation failed ",e);
231             return false;
232         }
233         return true;
234     }
235
236     protected boolean removeMd(T item, BindingTransactionChain chain) {
237         return chainWrapper1(item, chain,
238                              new Action1<Boolean, T>() {
239                                  @Override
240                                  public Boolean action(T item, BindingTransactionChain chain) {
241                                      return _removeMd(item, chain);
242                                  }
243                              }).booleanValue();
244     }
245
246     protected boolean removeMd(T item) {
247         return removeMd(item, null);
248     }
249
250     protected Uuid toUuid(String uuid) {
251         Preconditions.checkNotNull(uuid);
252         Uuid result;
253         try {
254             result = new Uuid(uuid);
255         } catch(IllegalArgumentException e) {
256             // OK... someone didn't follow RFC 4122... lets try this the hard way
257             String dedashed = uuid.replace("-", "");
258             if(dedashed.length() == DEDASHED_UUID_LENGTH) {
259                 String redashed = dedashed.substring(DEDASHED_UUID_START, DEDASHED_UUID_DIV1)
260                         + "-"
261                         + dedashed.substring(DEDASHED_UUID_DIV1, DEDASHED_UUID_DIV2)
262                         + "-"
263                         + dedashed.substring(DEDASHED_UUID_DIV2, DEDASHED_UUID_DIV3)
264                         + "-"
265                         + dedashed.substring(DEDASHED_UUID_DIV3, DEDASHED_UUID_DIV4)
266                         + "-"
267                         + dedashed.substring(DEDASHED_UUID_DIV4, DEDASHED_UUID_LENGTH);
268                 result = new Uuid(redashed);
269             } else {
270                 throw e;
271             }
272         }
273         return result;
274     }
275
276     // this method uses reflection to update an object from it's delta.
277
278     protected boolean overwrite(Object target, Object delta) {
279         Method[] methods = target.getClass().getMethods();
280
281         for(Method toMethod: methods){
282             if(toMethod.getDeclaringClass().equals(target.getClass())
283                     && toMethod.getName().startsWith("set")){
284
285                 String toName = toMethod.getName();
286                 String fromName = toName.replace("set", "get");
287
288                 try {
289                     Method fromMethod = delta.getClass().getMethod(fromName);
290                     Object value = fromMethod.invoke(delta, (Object[])null);
291                     if(value != null){
292                         toMethod.invoke(target, value);
293                     }
294                 } catch (Exception e) {
295                     LOGGER.error("Error in overwrite", e);
296                     return false;
297                 }
298             }
299         }
300         return true;
301     }
302
303     @Override
304     public void close() throws Exception {
305         // TODO Auto-generated method stub
306
307     }
308
309     protected boolean _exists(String uuid, BindingTransactionChain chain) {
310         Preconditions.checkNotNull(chain);
311         T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), chain);
312         return dataObject != null;
313     }
314
315     public boolean exists(String uuid, BindingTransactionChain chain) {
316         return chainWrapper1(uuid, chain,
317                              new Action1<Boolean, String>() {
318                                  @Override
319                                  public Boolean action(String uuid, BindingTransactionChain chain) {
320                                      return _exists(uuid, chain);
321                                  }
322                              }).booleanValue();
323     }
324
325     public boolean exists(String uuid) {
326         return exists(uuid, null);
327     }
328
329     public S get(String uuid) {
330         T dataObject = readMd(createInstanceIdentifier(toMd(uuid)));
331         if (dataObject == null) {
332             return null;
333         }
334         return fromMd(dataObject);
335     }
336
337     protected abstract List<T> getDataObjectList(U dataObjects);
338
339     public List<S> getAll() {
340         Set<S> allNeutronObjects = new HashSet<S>();
341         U dataObjects = readMd(createInstanceIdentifier());
342         if (dataObjects != null) {
343             for (T dataObject: getDataObjectList(dataObjects)) {
344                 allNeutronObjects.add(fromMd(dataObject));
345             }
346         }
347         LOGGER.debug("Exiting _getAll, Found {} OpenStackFirewall", allNeutronObjects.size());
348         List<S> ans = new ArrayList<S>();
349         ans.addAll(allNeutronObjects);
350         return ans;
351     }
352
353     public boolean add(S input) {
354         if (exists(input.getID())) {
355             return false;
356         }
357         addMd(input);
358         return true;
359     }
360
361     public boolean remove(String uuid) {
362         if (!exists(uuid)) {
363             return false;
364         }
365         return removeMd(toMd(uuid));
366     }
367
368     public boolean update(String uuid, S delta) {
369         if (!exists(uuid)) {
370             return false;
371         }
372         updateMd(delta);
373         return true;
374     }
375
376     public boolean inUse(String uuid) {
377         return false;
378     }
379 }