Merge "Bug 4354 - neutron tenant_id doens't contain '-'(dash)"
[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     protected 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(Action0<U> action) {
83         try (BindingTransactionChain chain = this.createTransactionChain()) {
84             return action.action(chain);
85         }
86     }
87
88     protected interface Action1<U, V> {
89         public U action(V input, BindingTransactionChain chain);
90     }
91
92     protected <U, V> U chainWrapper1(V input, Action1<U, V> action) {
93         try (BindingTransactionChain chain = this.createTransactionChain()) {
94             return action.action(input, chain);
95         }
96     }
97
98     protected interface Action2<U, V, W> {
99         public U action(V input0, W input1, BindingTransactionChain chain);
100     }
101
102     protected <U, V, W> U chainWrapper2(V input0, W input1,
103                                         Action2<U, V, W> action) {
104         try (BindingTransactionChain chain = this.createTransactionChain()) {
105             return action.action(input0, input1, chain);
106         }
107     }
108
109     protected abstract InstanceIdentifier<T> createInstanceIdentifier(T item);
110
111     protected abstract InstanceIdentifier<U> createInstanceIdentifier();
112
113     protected abstract T toMd(S neutronObject);
114
115     protected abstract T toMd(String uuid);
116
117     protected abstract S fromMd(T dataObject);
118
119     protected <T extends DataObject> T readMd(InstanceIdentifier<T> path, BindingTransactionChain chain) {
120         Preconditions.checkNotNull(chain);
121         T result = null;
122         final ReadOnlyTransaction transaction = chain.newReadOnlyTransaction();
123         CheckedFuture<Optional<T>, ReadFailedException> future = transaction.read(LogicalDatastoreType.CONFIGURATION, path);
124         if (future != null) {
125             Optional<T> optional;
126             try {
127                 optional = future.checkedGet();
128                 if (optional.isPresent()) {
129                     result = optional.get();
130                 }
131             } catch (ReadFailedException e) {
132                 LOGGER.warn("Failed to read {}", path, e);
133             }
134         }
135         transaction.close();
136         return result;
137     }
138
139     protected <T extends DataObject> T readMd(InstanceIdentifier<T> path) {
140         return chainWrapper1(path,
141                              new Action1<T, InstanceIdentifier<T>>() {
142                                  @Override
143                                  public T action(InstanceIdentifier<T> path, BindingTransactionChain chain) {
144                                      return readMd(path, chain);
145                                  }
146                              });
147     }
148
149     protected boolean addMd(S neutronObject, BindingTransactionChain chain) {
150         // TODO think about adding existence logic
151         return updateMd(neutronObject, chain);
152     }
153
154     protected boolean addMd(S neutronObject) {
155         return chainWrapper1(neutronObject,
156                              new Action1<Boolean, S>() {
157                                  @Override
158                                  public Boolean action(S path, BindingTransactionChain chain) {
159                                      return addMd(path, chain);
160                                  }
161                              }).booleanValue();
162     }
163
164     protected boolean updateMd(S neutronObject, BindingTransactionChain chain) {
165         Preconditions.checkNotNull(chain);
166
167         /*
168          * retry for transaction conflict.
169          * see the comment
170          * org.opendaylight.controller.sal.restconf.impl.RestconfImpl#updateConfigurationData
171          */
172         int retries = 2;
173         while (true) {
174             WriteTransaction transaction = chain.newWriteOnlyTransaction();
175             T item = toMd(neutronObject);
176             InstanceIdentifier<T> iid = createInstanceIdentifier(item);
177             transaction.put(LogicalDatastoreType.CONFIGURATION, iid, item, true);
178             CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit();
179             try {
180                 future.get();
181             } catch (InterruptedException | ExecutionException e) {
182                 if (e.getCause() instanceof OptimisticLockFailedException) {
183                     if(--retries >= 0) {
184                         LOGGER.debug("Got OptimisticLockFailedException - trying again {}", neutronObject);
185                         continue;
186                     }
187                     LOGGER.warn("Got OptimisticLockFailedException on last try - failing {}", neutronObject);
188                 }
189                 LOGGER.warn("Transation failed ", e);
190                 return false;
191             }
192             break;
193         }
194         return true;
195     }
196
197     protected boolean updateMd(S neutronObject) {
198         return chainWrapper1(neutronObject,
199                              new Action1<Boolean, S>() {
200                                  @Override
201                                  public Boolean action(S neutronObject, BindingTransactionChain chain) {
202                                      return updateMd(neutronObject, chain);
203                                  }
204                              }).booleanValue();
205     }
206
207     private boolean removeMd(T item, BindingTransactionChain chain) {
208         Preconditions.checkNotNull(chain);
209         WriteTransaction transaction = chain.newWriteOnlyTransaction();
210         InstanceIdentifier<T> iid = createInstanceIdentifier(item);
211         transaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
212         CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit();
213         try {
214             future.get();
215         } catch (InterruptedException | ExecutionException e) {
216             LOGGER.warn("Transation failed ",e);
217             return false;
218         }
219         return true;
220     }
221
222     protected boolean removeMd(T item) {
223         return chainWrapper1(item,
224                              new Action1<Boolean, T>() {
225                                  @Override
226                                  public Boolean action(T item, BindingTransactionChain chain) {
227                                      return removeMd(item, chain);
228                                  }
229                              }).booleanValue();
230     }
231
232     protected Uuid toUuid(String uuid) {
233         Preconditions.checkNotNull(uuid);
234         Uuid result;
235         try {
236             result = new Uuid(uuid);
237         } catch(IllegalArgumentException e) {
238             // OK... someone didn't follow RFC 4122... lets try this the hard way
239             String dedashed = uuid.replace("-", "");
240             if(dedashed.length() == DEDASHED_UUID_LENGTH) {
241                 String redashed = dedashed.substring(DEDASHED_UUID_START, DEDASHED_UUID_DIV1)
242                         + "-"
243                         + dedashed.substring(DEDASHED_UUID_DIV1, DEDASHED_UUID_DIV2)
244                         + "-"
245                         + dedashed.substring(DEDASHED_UUID_DIV2, DEDASHED_UUID_DIV3)
246                         + "-"
247                         + dedashed.substring(DEDASHED_UUID_DIV3, DEDASHED_UUID_DIV4)
248                         + "-"
249                         + dedashed.substring(DEDASHED_UUID_DIV4, DEDASHED_UUID_LENGTH);
250                 result = new Uuid(redashed);
251             } else {
252                 throw e;
253             }
254         }
255         return result;
256     }
257
258     // this method uses reflection to update an object from it's delta.
259
260     protected boolean overwrite(Object target, Object delta) {
261         Method[] methods = target.getClass().getMethods();
262
263         for(Method toMethod: methods){
264             if(toMethod.getDeclaringClass().equals(target.getClass())
265                     && toMethod.getName().startsWith("set")){
266
267                 String toName = toMethod.getName();
268                 String fromName = toName.replace("set", "get");
269
270                 try {
271                     Method fromMethod = delta.getClass().getMethod(fromName);
272                     Object value = fromMethod.invoke(delta, (Object[])null);
273                     if(value != null){
274                         toMethod.invoke(target, value);
275                     }
276                 } catch (Exception e) {
277                     LOGGER.error("Error in overwrite", e);
278                     return false;
279                 }
280             }
281         }
282         return true;
283     }
284
285     @Override
286     public void close() throws Exception {
287         // TODO Auto-generated method stub
288
289     }
290
291     protected boolean exists(String uuid, BindingTransactionChain chain) {
292         Preconditions.checkNotNull(chain);
293         T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), chain);
294         return dataObject != null;
295     }
296
297     @Override
298     public boolean exists(String uuid) {
299         return chainWrapper1(uuid,
300                              new Action1<Boolean, String>() {
301                                  @Override
302                                  public Boolean action(String uuid, BindingTransactionChain chain) {
303                                      return exists(uuid, chain);
304                                  }
305                              }).booleanValue();
306     }
307
308     protected S get(String uuid, BindingTransactionChain chain) {
309         Preconditions.checkNotNull(chain);
310         T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), chain);
311         if (dataObject == null) {
312             return null;
313         }
314         return fromMd(dataObject);
315     }
316
317     @Override
318     public S get(String uuid) {
319         return chainWrapper1(uuid,
320                              new Action1<S, String>() {
321                                  @Override
322                                  public S action(String uuid, BindingTransactionChain chain) {
323                                      return get(uuid, chain);
324                                  }
325                              });
326     }
327
328     protected abstract List<T> getDataObjectList(U dataObjects);
329
330     protected List<S> getAll(BindingTransactionChain chain) {
331         Preconditions.checkNotNull(chain);
332         Set<S> allNeutronObjects = new HashSet<S>();
333         U dataObjects = readMd(createInstanceIdentifier(), chain);
334         if (dataObjects != null) {
335             for (T dataObject: getDataObjectList(dataObjects)) {
336                 allNeutronObjects.add(fromMd(dataObject));
337             }
338         }
339         LOGGER.debug("Exiting _getAll, Found {} OpenStackFirewall", allNeutronObjects.size());
340         List<S> ans = new ArrayList<S>();
341         ans.addAll(allNeutronObjects);
342         return ans;
343     }
344
345     @Override
346     public List<S> getAll() {
347         return chainWrapper0(new Action0<List<S>>() {
348                                  @Override
349                                  public List<S> action(BindingTransactionChain chain) {
350                                      return getAll(chain);
351                                  }
352                              });
353     }
354
355     protected boolean add(S input, BindingTransactionChain chain) {
356         Preconditions.checkNotNull(chain);
357         if (exists(input.getID(), chain)) {
358             return false;
359         }
360         addMd(input, chain);
361         return true;
362     }
363
364     @Override
365     public boolean add(S input) {
366         return chainWrapper1(input,
367                              new Action1<Boolean, S>() {
368                                  @Override
369                                  public Boolean action(S input, BindingTransactionChain chain) {
370                                      return add(input, chain);
371                                  }
372                              }).booleanValue();
373     }
374
375     protected boolean remove(String uuid, BindingTransactionChain chain) {
376         Preconditions.checkNotNull(chain);
377         if (!exists(uuid, chain)) {
378             return false;
379         }
380         return removeMd(toMd(uuid), chain);
381     }
382
383     @Override
384     public boolean remove(String uuid) {
385         return chainWrapper1(uuid,
386                              new Action1<Boolean, String>() {
387                                  @Override
388                                  public Boolean action(String uuid, BindingTransactionChain chain) {
389                                      return remove(uuid, chain);
390                                  }
391                              }).booleanValue();
392      }
393
394     protected boolean update(String uuid, S delta, BindingTransactionChain chain) {
395         Preconditions.checkNotNull(chain);
396         if (!exists(uuid, chain)) {
397             return false;
398         }
399         updateMd(delta, chain);
400         return true;
401     }
402
403     @Override
404     public boolean update(String uuid, S delta) {
405         return chainWrapper2(uuid, delta,
406                              new Action2<Boolean, String, S>() {
407                                  @Override
408                                  public Boolean action(String uuid, S delta, BindingTransactionChain chain) {
409                                      return update(uuid, delta, chain);
410                                  }
411                              }).booleanValue();
412     }
413
414     @Override
415     public boolean inUse(String uuid) {
416         return false;
417     }
418 }