Neutron Model update
[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 com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.CheckedFuture;
14 import java.lang.reflect.InvocationTargetException;
15 import java.lang.reflect.Method;
16 import java.lang.reflect.ParameterizedType;
17 import java.lang.reflect.Type;
18 import java.util.ArrayList;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Set;
22 import java.util.concurrent.ExecutionException;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
25 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
26 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
27 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
30 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
32 import org.opendaylight.neutron.spi.INeutronAdminAttributes;
33 import org.opendaylight.neutron.spi.INeutronBaseAttributes;
34 import org.opendaylight.neutron.spi.INeutronCRUD;
35 import org.opendaylight.neutron.spi.INeutronObject;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.AdminAttributes;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.BaseAttributes;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
40 import org.opendaylight.yangtools.concepts.Builder;
41 import org.opendaylight.yangtools.yang.binding.Augmentable;
42 import org.opendaylight.yangtools.yang.binding.ChildOf;
43 import org.opendaylight.yangtools.yang.binding.DataObject;
44 import org.opendaylight.yangtools.yang.binding.Identifiable;
45 import org.opendaylight.yangtools.yang.binding.Identifier;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 public abstract class AbstractNeutronInterface<T extends DataObject & Identifiable<K> & ChildOf<? super U>,
51         U extends ChildOf<? super Neutron> & Augmentable<U>, K extends Identifier<T>, S extends INeutronObject<S>>
52         implements AutoCloseable, INeutronCRUD<S> {
53     // T extends DataObject & Identifiable<K> & ChildOf<? super U> as 0th type argument
54     private static final int MD_LIST_CLASS_TYPE_INDEX = 0;
55     // U extends ChildOf<? super Neutron> & Augmentable<U> as 1st type argument
56     private static final int MD_CONTAINER_CLASS_TYPE_INDEX = 1;
57     // S extends INeutronObject<S> as 3rd type argument
58     private static final int NEUTRON_OBJECT_TYPE_INDEX = 3;
59
60     private static final Logger LOGGER = LoggerFactory.getLogger(AbstractNeutronInterface.class);
61     private static final int DEDASHED_UUID_LENGTH = 32;
62     private static final int DEDASHED_UUID_START = 0;
63     private static final int DEDASHED_UUID_DIV1 = 8;
64     private static final int DEDASHED_UUID_DIV2 = 12;
65     private static final int DEDASHED_UUID_DIV3 = 16;
66     private static final int DEDASHED_UUID_DIV4 = 20;
67
68     private static final int RETRY_MAX = 2;
69
70     private final DataBroker db;
71
72     private final Class<U> mdContainerClass;
73     private final Class<T> mdListClass;
74
75     // Unfortunately odl yangtools doesn't model yang model "uses" as
76     // class/interface hierarchy. So we need to resort to use reflection
77     // to call setter method.
78     private final Class<? extends Builder<T>> builderClass;
79     private final Method setUuid;
80     private final Method setTenantId;
81     private final Method setProjectId;
82     private final Method setName;
83     private final Method setAdminStateUp;
84     private final Method setStatus;
85     private final Method setRevisionNumber;
86
87     AbstractNeutronInterface(Class<? extends Builder<T>> builderClass, DataBroker db) {
88         this.db = Preconditions.checkNotNull(db);
89         this.builderClass = builderClass;
90
91         ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
92         Type[] types = parameterizedType.getActualTypeArguments();
93         @SuppressWarnings("unchecked")
94         Class<T> localMdListClass = (Class<T>) types[MD_LIST_CLASS_TYPE_INDEX];
95         mdListClass = localMdListClass;
96         @SuppressWarnings("unchecked")
97         Class<U> localMdContainerClass = (Class<U>) types[MD_CONTAINER_CLASS_TYPE_INDEX];
98         mdContainerClass = localMdContainerClass;
99         @SuppressWarnings("unchecked")
100         Class<S> neutronObjectClass = (Class<S>) types[NEUTRON_OBJECT_TYPE_INDEX];
101         try {
102             setUuid = builderClass.getDeclaredMethod("setUuid", Uuid.class);
103             setTenantId = builderClass.getDeclaredMethod("setTenantId", Uuid.class);
104             if (INeutronBaseAttributes.class.isAssignableFrom(neutronObjectClass)) {
105                 setName = builderClass.getDeclaredMethod("setName", String.class);
106                 setProjectId = builderClass.getDeclaredMethod("setProjectId", String.class);
107                 setRevisionNumber = builderClass.getDeclaredMethod("setRevisionNumber", Long.class);
108             } else {
109                 setName = null;
110                 setProjectId = null;
111                 setRevisionNumber = null;
112             }
113
114             if (INeutronAdminAttributes.class.isAssignableFrom(neutronObjectClass)) {
115                 setAdminStateUp = builderClass.getDeclaredMethod("setAdminStateUp", Boolean.class);
116                 setStatus = builderClass.getDeclaredMethod("setStatus", String.class);
117             } else {
118                 setAdminStateUp = null;
119                 setStatus = null;
120             }
121         } catch (NoSuchMethodException e) {
122             throw new IllegalArgumentException(e);
123         }
124     }
125
126     public DataBroker getDataBroker() {
127         Preconditions.checkNotNull(db);
128         return db;
129     }
130
131     private InstanceIdentifier<T> createInstanceIdentifier(T item) {
132         return InstanceIdentifier.create(Neutron.class).child(mdContainerClass).child(mdListClass, item.getKey());
133     }
134
135     private InstanceIdentifier<U> createInstanceIdentifier() {
136         return InstanceIdentifier.create(Neutron.class).child(mdContainerClass);
137     }
138
139     protected <S1 extends INeutronBaseAttributes<S1>, M extends BaseAttributes, B extends Builder<M>>
140         void toMdIds(INeutronObject<S1> neutronObject, B builder) {
141         try {
142             if (neutronObject.getID() != null) {
143                 setUuid.invoke(builder, toUuid(neutronObject.getID()));
144             } else {
145                 LOGGER.warn("Attempting to write neutron object {} without UUID", builderClass.getSimpleName());
146             }
147             if (neutronObject.getTenantID() != null && !neutronObject.getTenantID().isEmpty()) {
148                 setTenantId.invoke(builder, toUuid(neutronObject.getTenantID()));
149             }
150             if (neutronObject.getProjectID() != null) {
151                 setProjectId.invoke(builder, neutronObject.getTenantID());
152             }
153         } catch (IllegalAccessException | InvocationTargetException e) {
154             throw new IllegalArgumentException(e);
155         }
156     }
157
158     protected <S1 extends INeutronBaseAttributes<S1>>
159         void fromMdIds(BaseAttributes baseAttributes, INeutronObject<S1> answer) {
160         if (baseAttributes.getUuid() != null) {
161             answer.setID(baseAttributes.getUuid().getValue());
162         }
163         if (baseAttributes.getTenantId() != null) {
164             answer.setTenantID(baseAttributes.getTenantId());
165         }
166         if (baseAttributes.getProjectId() != null) {
167             answer.setProjectID(baseAttributes.getProjectId());
168         }
169     }
170
171     protected <S1 extends INeutronBaseAttributes<S1>, M extends BaseAttributes, B extends Builder<M>>
172         void toMdBaseAttributes(S1 neutronObject, B builder) {
173         toMdIds(neutronObject, builder);
174         try {
175             if (neutronObject.getName() != null) {
176                 setName.invoke(builder, neutronObject.getName());
177             }
178             if (neutronObject.getRevisionNumber() != null) {
179                 setRevisionNumber.invoke(builder, neutronObject.getRevisionNumber());
180             }
181         } catch (IllegalAccessException | InvocationTargetException e) {
182             throw new IllegalArgumentException(e);
183         }
184     }
185
186     protected <S1 extends INeutronBaseAttributes<S1>>
187         void fromMdBaseAttributes(BaseAttributes baseAttributes, S1 answer) {
188         fromMdIds(baseAttributes, answer);
189         if (baseAttributes.getName() != null) {
190             answer.setName(baseAttributes.getName());
191         }
192         if (baseAttributes.getRevisionNumber() != null) {
193             answer.setRevisionNumber(baseAttributes.getRevisionNumber());
194         }
195     }
196
197     protected <S1 extends INeutronAdminAttributes<S1>, M extends BaseAttributes & AdminAttributes, B extends Builder<M>>
198         void toMdAdminAttributes(S1 neutronObject, B builder) {
199         toMdBaseAttributes(neutronObject, builder);
200         try {
201             if (neutronObject.getAdminStateUp() != null) {
202                 setAdminStateUp.invoke(builder, neutronObject.getAdminStateUp());
203             }
204             if (neutronObject.getStatus() != null) {
205                 setStatus.invoke(builder, neutronObject.getStatus());
206             }
207         } catch (IllegalAccessException | InvocationTargetException e) {
208             throw new IllegalArgumentException(e);
209         }
210     }
211
212     protected <M extends BaseAttributes & AdminAttributes, S1 extends INeutronAdminAttributes<S1>>
213         void fromMdAdminAttributes(M attr, S1 answer) {
214         fromMdBaseAttributes(attr, answer);
215         if (attr.isAdminStateUp() != null) {
216             answer.setAdminStateUp(attr.isAdminStateUp());
217         }
218         if (attr.getStatus() != null) {
219             answer.setStatus(attr.getStatus());
220         }
221     }
222
223     protected abstract T toMd(S neutronObject);
224
225     protected T toMd(String uuid) {
226         Builder<T> builder;
227         try {
228             builder = builderClass.newInstance();
229             setUuid.invoke(builder, toUuid(uuid));
230         } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
231             // should not happen.
232             throw new IllegalArgumentException(e);
233         }
234         return builder.build();
235     }
236
237     protected abstract S fromMd(T dataObject);
238
239     private <T extends DataObject> T readMd(InstanceIdentifier<T> path, ReadTransaction tx) {
240         Preconditions.checkNotNull(tx);
241         T result = null;
242         final CheckedFuture<Optional<T>,
243                 ReadFailedException> future = tx.read(LogicalDatastoreType.CONFIGURATION, path);
244         if (future != null) {
245             Optional<T> optional;
246             try {
247                 optional = future.checkedGet();
248                 if (optional.isPresent()) {
249                     result = optional.get();
250                 }
251             } catch (final ReadFailedException e) {
252                 LOGGER.warn("Failed to read {}", path, e);
253             }
254         }
255         return result;
256     }
257
258     protected <T extends DataObject> T readMd(InstanceIdentifier<T> path) {
259         try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
260             return readMd(path, tx);
261         }
262     }
263
264     private void addMd(S neutronObject, WriteTransaction tx) throws InterruptedException, ExecutionException {
265         // TODO think about adding existence logic
266         updateMd(neutronObject, tx);
267     }
268
269     protected boolean addMd(S neutronObject) {
270         try {
271             final WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
272             addMd(neutronObject, tx);
273             return true;
274         } catch (InterruptedException | ExecutionException e) {
275             LOGGER.warn("Transaction failed", e);
276         }
277         return false;
278     }
279
280     private void updateMd(S neutronObject, WriteTransaction tx) throws InterruptedException, ExecutionException {
281         Preconditions.checkNotNull(tx);
282
283         final T item = toMd(neutronObject);
284         final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
285         tx.put(LogicalDatastoreType.CONFIGURATION, iid, item, true);
286         final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
287         // Check if it's successfuly committed, otherwise exception will be thrown.
288         future.get();
289     }
290
291     protected boolean updateMd(S neutronObject) {
292         int retries = RETRY_MAX;
293         while (retries-- >= 0) {
294             try {
295                 final WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
296                 updateMd(neutronObject, tx);
297                 return true;
298             } catch (InterruptedException | ExecutionException e) {
299                 if (e.getCause() instanceof OptimisticLockFailedException) {
300                     LOGGER.warn("Got OptimisticLockFailedException - {} {}", neutronObject, retries);
301                     continue;
302                 }
303                 // TODO: rethrow exception. don't mask exception
304                 LOGGER.error("Transaction failed", e);
305             }
306             break;
307         }
308         return false;
309     }
310
311     private void removeMd(T item, WriteTransaction tx) throws InterruptedException, ExecutionException {
312         Preconditions.checkNotNull(tx);
313         final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
314         tx.delete(LogicalDatastoreType.CONFIGURATION, iid);
315         final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
316         // Check if it's successfuly committed, otherwise exception will be thrown.
317         future.get();
318     }
319
320     protected boolean removeMd(T item) {
321         final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
322         try {
323             removeMd(item, tx);
324             return true;
325         } catch (InterruptedException | ExecutionException e) {
326             LOGGER.warn("Transaction failed", e);
327         }
328         return false;
329     }
330
331     protected Uuid toUuid(String uuid) {
332         Preconditions.checkNotNull(uuid);
333         Uuid result;
334         try {
335             result = new Uuid(uuid);
336         } catch (final IllegalArgumentException e) {
337             // OK... someone didn't follow RFC 4122... lets try this the hard way
338             final String dedashed = uuid.replace("-", "");
339             if (dedashed.length() == DEDASHED_UUID_LENGTH) {
340                 final String redashed = dedashed.substring(DEDASHED_UUID_START, DEDASHED_UUID_DIV1) + "-"
341                         + dedashed.substring(DEDASHED_UUID_DIV1, DEDASHED_UUID_DIV2) + "-"
342                         + dedashed.substring(DEDASHED_UUID_DIV2, DEDASHED_UUID_DIV3) + "-"
343                         + dedashed.substring(DEDASHED_UUID_DIV3, DEDASHED_UUID_DIV4) + "-"
344                         + dedashed.substring(DEDASHED_UUID_DIV4, DEDASHED_UUID_LENGTH);
345                 result = new Uuid(redashed);
346             } else {
347                 throw e;
348             }
349         }
350         return result;
351     }
352
353     @Override
354     public void close() throws Exception {
355         // TODO Auto-generated method stub
356
357     }
358
359     private boolean exists(String uuid, ReadTransaction tx) {
360         Preconditions.checkNotNull(tx);
361         final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
362         return dataObject != null;
363     }
364
365     @Override
366     public boolean exists(String uuid) {
367         try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
368             return exists(uuid, tx);
369         }
370     }
371
372     private S get(String uuid, ReadTransaction tx) {
373         Preconditions.checkNotNull(tx);
374         final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
375         if (dataObject == null) {
376             return null;
377         }
378         return fromMd(dataObject);
379     }
380
381     @Override
382     public S get(String uuid) {
383         try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
384             return get(uuid, tx);
385         }
386     }
387
388     protected abstract List<T> getDataObjectList(U dataObjects);
389
390     private List<S> getAll(ReadTransaction tx) {
391         Preconditions.checkNotNull(tx);
392         final Set<S> allNeutronObjects = new HashSet<S>();
393         final U dataObjects = readMd(createInstanceIdentifier(), tx);
394         if (dataObjects != null) {
395             for (final T dataObject : getDataObjectList(dataObjects)) {
396                 allNeutronObjects.add(fromMd(dataObject));
397             }
398         }
399         LOGGER.debug("Exiting _getAll, Found {} OpenStackFirewall", allNeutronObjects.size());
400         final List<S> ans = new ArrayList<S>();
401         ans.addAll(allNeutronObjects);
402         return ans;
403     }
404
405     @Override
406     public List<S> getAll() {
407         try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
408             return getAll(tx);
409         }
410     }
411
412     private boolean add(S input, ReadWriteTransaction tx) throws InterruptedException, ExecutionException {
413         Preconditions.checkNotNull(tx);
414         if (exists(input.getID(), tx)) {
415             tx.cancel();
416             return false;
417         }
418         addMd(input, tx);
419         return true;
420     }
421
422     @Override
423     public boolean add(S input) {
424         int retries = RETRY_MAX;
425         while (retries-- >= 0) {
426             final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
427             try {
428                 return add(input, tx);
429             } catch (InterruptedException | ExecutionException e) {
430                 if (e.getCause() instanceof OptimisticLockFailedException) {
431                     LOGGER.warn("Got OptimisticLockFailedException - {} {}", input, retries);
432                     continue;
433                 }
434                 // TODO: rethrow exception. don't mask exception
435                 LOGGER.error("Transaction failed", e);
436             }
437             break;
438         }
439         return false;
440     }
441
442     private boolean remove(String uuid, ReadWriteTransaction tx) throws InterruptedException, ExecutionException {
443         Preconditions.checkNotNull(tx);
444         if (!exists(uuid, tx)) {
445             tx.cancel();
446             return false;
447         }
448         removeMd(toMd(uuid), tx);
449         return true;
450     }
451
452     @Override
453     public boolean remove(String uuid) {
454         int retries = RETRY_MAX;
455         while (retries-- >= 0) {
456             final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
457             try {
458                 return remove(uuid, tx);
459             } catch (InterruptedException | ExecutionException e) {
460                 if (e.getCause() instanceof OptimisticLockFailedException) {
461                     LOGGER.warn("Got OptimisticLockFailedException - {} {}", uuid, retries);
462                     continue;
463                 }
464                 // TODO: rethrow exception. don't mask exception
465                 LOGGER.error("Transaction failed", e);
466             }
467             break;
468         }
469         return false;
470     }
471
472     private boolean update(String uuid, S delta, ReadWriteTransaction tx)
473             throws InterruptedException, ExecutionException {
474         Preconditions.checkNotNull(tx);
475         if (!exists(uuid, tx)) {
476             tx.cancel();
477             return false;
478         }
479         updateMd(delta, tx);
480         return true;
481     }
482
483     @Override
484     public boolean update(String uuid, S delta) {
485         int retries = RETRY_MAX;
486         while (retries-- >= 0) {
487             final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
488             try {
489                 return update(uuid, delta, tx);
490             } catch (InterruptedException | ExecutionException e) {
491                 if (e.getCause() instanceof OptimisticLockFailedException) {
492                     LOGGER.warn("Got OptimisticLockFailedException - {} {} {}", uuid, delta, retries);
493                     continue;
494                 }
495                 // TODO: rethrow exception. don't mask exception
496                 LOGGER.error("Transaction failed", e);
497             }
498             break;
499         }
500         return false;
501     }
502 }