2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.neutron.transcriber;
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;
22 import java.util.concurrent.ExecutionException;
23 import java.util.function.Function;
24 import javax.annotation.PreDestroy;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
29 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
30 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
31 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
34 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
35 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
36 import org.opendaylight.neutron.spi.INeutronAdminAttributes;
37 import org.opendaylight.neutron.spi.INeutronBaseAttributes;
38 import org.opendaylight.neutron.spi.INeutronCRUD;
39 import org.opendaylight.neutron.spi.INeutronObject;
40 import org.opendaylight.neutron.spi.NeutronObject;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.AdminAttributes;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.BaseAttributes;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.IdAttributes;
45 import org.opendaylight.yangtools.concepts.Builder;
46 import org.opendaylight.yangtools.yang.binding.Augmentable;
47 import org.opendaylight.yangtools.yang.binding.ChildOf;
48 import org.opendaylight.yangtools.yang.binding.DataObject;
49 import org.opendaylight.yangtools.yang.binding.Identifiable;
50 import org.opendaylight.yangtools.yang.binding.Identifier;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 * Base class of Transcriber converts data from/to neutron spi to/from md-sal yang model.
64 * T(MD-SAL model) <-> S (neutron spi): Neutron northbound
79 * @param <T> Target yang model
80 * @param <U> parent of T
81 * @param <K> key type to indentify T
82 * @param <S> Neutron-spi class
83 * @param <V> parent of U
85 public abstract class AbstractTranscriberInterface<
86 T extends DataObject & Identifiable<K> & ChildOf<? super U>,
87 U extends ChildOf<? super V> & Augmentable<U>,
88 K extends Identifier<T>, S extends INeutronObject<S>,
90 implements AutoCloseable, INeutronCRUD<S> {
92 private static final Logger LOG = LoggerFactory.getLogger(AbstractTranscriberInterface.class);
94 // T extends DataObject & Identifiable<K> & ChildOf<? super U> as 0th type argument
95 private static final int MD_LIST_CLASS_TYPE_INDEX = 0;
96 // U extends ChildOf<? super Neutron> & Augmentable<U> as 1st type argument
97 private static final int MD_CONTAINER_CLASS_TYPE_INDEX = 1;
98 // V extends DataObject as 4th type argument
99 private static final int MD_PARENT_CLASS_TYPE_INDEX = 4;
100 // S extends INeutronObject<S> as 3rd type argument
101 private static final int NEUTRON_OBJECT_TYPE_INDEX = 3;
103 private static final int DEDASHED_UUID_LENGTH = 32;
104 private static final int DEDASHED_UUID_START = 0;
105 private static final int DEDASHED_UUID_DIV1 = 8;
106 private static final int DEDASHED_UUID_DIV2 = 12;
107 private static final int DEDASHED_UUID_DIV3 = 16;
108 private static final int DEDASHED_UUID_DIV4 = 20;
110 private static final int RETRY_MAX = 2;
112 private final DataBroker db;
114 private final Class<V> mdParentClass;
115 private final Class<U> mdContainerClass;
116 private final Class<T> mdListClass;
118 // Unfortunately odl yangtools doesn't model yang model "uses" as
119 // class/interface hierarchy. So we need to resort to use reflection
120 // to call setter method.
121 private final Class<? extends Builder<T>> builderClass;
122 private final Method setUuid;
123 private final Method setTenantId;
124 private final Method setProjectId;
125 private final Method setName;
126 private final Method setAdminStateUp;
127 private final Method setStatus;
128 private final Method setRevisionNumber;
130 protected Class<V> getMdParentClass(final Type[] types) {
131 @SuppressWarnings("unchecked")
132 Class<V> localMdParentClass = (Class<V>) types[MD_PARENT_CLASS_TYPE_INDEX];
133 return localMdParentClass;
136 protected AbstractTranscriberInterface(Class<? extends Builder<T>> builderClass, DataBroker db) {
137 this.db = Preconditions.checkNotNull(db);
138 this.builderClass = builderClass;
140 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
141 Type[] types = parameterizedType.getActualTypeArguments();
142 @SuppressWarnings("unchecked")
143 Class<T> localMdListClass = (Class<T>) types[MD_LIST_CLASS_TYPE_INDEX];
144 mdListClass = localMdListClass;
145 @SuppressWarnings("unchecked")
146 Class<U> localMdContainerClass = (Class<U>) types[MD_CONTAINER_CLASS_TYPE_INDEX];
147 mdContainerClass = localMdContainerClass;
148 mdParentClass = getMdParentClass(types);
149 @SuppressWarnings("unchecked")
150 Class<S> neutronObjectClass = (Class<S>) types[NEUTRON_OBJECT_TYPE_INDEX];
152 setUuid = builderClass.getDeclaredMethod("setUuid", Uuid.class);
153 setTenantId = builderClass.getDeclaredMethod("setTenantId", Uuid.class);
154 setProjectId = builderClass.getDeclaredMethod("setProjectId", String.class);
155 setRevisionNumber = builderClass.getDeclaredMethod("setRevisionNumber", Long.class);
156 if (INeutronBaseAttributes.class.isAssignableFrom(neutronObjectClass)) {
157 setName = builderClass.getDeclaredMethod("setName", String.class);
162 if (INeutronAdminAttributes.class.isAssignableFrom(neutronObjectClass)) {
163 setAdminStateUp = builderClass.getDeclaredMethod("setAdminStateUp", Boolean.class);
164 setStatus = builderClass.getDeclaredMethod("setStatus", String.class);
166 setAdminStateUp = null;
169 } catch (NoSuchMethodException e) {
170 throw new IllegalArgumentException(e);
174 public DataBroker getDataBroker() {
175 Preconditions.checkNotNull(db);
179 private InstanceIdentifier<T> createInstanceIdentifier(T item) {
180 return InstanceIdentifier.create(mdParentClass).child(mdContainerClass).child(mdListClass, item.key());
183 private InstanceIdentifier<U> createInstanceIdentifier() {
184 return InstanceIdentifier.create(mdParentClass).child(mdContainerClass);
187 protected static <S1 extends INeutronObject<S1>, M extends IdAttributes, B extends Builder<M>>
188 B toMdIds(INeutronObject<S1> neutronObject, Class<B> builderClass) {
191 builder = builderClass.newInstance();
193 if (neutronObject.getID() != null) {
194 final Method setUuid = builderClass.getMethod("setUuid", Uuid.class);
195 setUuid.invoke(builder, toUuid(neutronObject.getID()));
197 if (neutronObject.getTenantID() != null && !neutronObject.getTenantID().isEmpty()) {
198 final Method setTenantId = builderClass.getMethod("setTenantId", Uuid.class);
199 setTenantId.invoke(builder, toUuid(neutronObject.getTenantID()));
201 if (neutronObject.getProjectID() != null) {
202 final Method setProjectId = builderClass.getMethod("setProjectId", String.class);
203 setProjectId.invoke(builder, neutronObject.getTenantID());
205 if (neutronObject.getRevisionNumber() != null) {
206 final Method setRevisionNumber = builderClass.getMethod("setRevisionNumber", Long.class);
207 setRevisionNumber.invoke(builder, neutronObject.getRevisionNumber());
209 } catch (IllegalAccessException | InstantiationException | InvocationTargetException
210 | NoSuchMethodException e) {
211 throw new IllegalArgumentException(e);
216 protected <S1 extends INeutronObject<S1>, M extends IdAttributes, B extends Builder<M>>
217 void toMdIds(INeutronObject<S1> neutronObject, B builder) {
219 if (neutronObject.getID() != null) {
220 setUuid.invoke(builder, toUuid(neutronObject.getID()));
222 LOG.warn("Attempting to write neutron object {} without UUID", builderClass.getSimpleName());
224 if (neutronObject.getTenantID() != null && !neutronObject.getTenantID().isEmpty()) {
225 setTenantId.invoke(builder, toUuid(neutronObject.getTenantID()));
227 if (neutronObject.getProjectID() != null) {
228 setProjectId.invoke(builder, neutronObject.getTenantID());
230 if (neutronObject.getRevisionNumber() != null) {
231 setRevisionNumber.invoke(builder, neutronObject.getRevisionNumber());
233 } catch (IllegalAccessException | InvocationTargetException e) {
234 throw new IllegalArgumentException(e);
238 protected <S1 extends INeutronObject<S1>>
239 void fromMdIds(IdAttributes idAttributes, INeutronObject<S1> answer) {
240 if (idAttributes.getUuid() != null) {
241 answer.setID(idAttributes.getUuid().getValue());
243 if (idAttributes.getTenantId() != null) {
244 answer.setTenantID(idAttributes.getTenantId());
246 if (idAttributes.getProjectId() != null) {
247 answer.setProjectID(idAttributes.getProjectId());
249 if (idAttributes.getRevisionNumber() != null) {
250 answer.setRevisionNumber(idAttributes.getRevisionNumber());
254 protected <S1 extends INeutronBaseAttributes<S1>, M extends BaseAttributes, B extends Builder<M>>
255 void toMdBaseAttributes(S1 neutronObject, B builder) {
256 toMdIds(neutronObject, builder);
258 if (neutronObject.getName() != null) {
259 setName.invoke(builder, neutronObject.getName());
261 } catch (IllegalAccessException | InvocationTargetException e) {
262 throw new IllegalArgumentException(e);
266 protected <S1 extends INeutronBaseAttributes<S1>>
267 void fromMdBaseAttributes(BaseAttributes baseAttributes, S1 answer) {
268 fromMdIds(baseAttributes, answer);
269 if (baseAttributes.getName() != null) {
270 answer.setName(baseAttributes.getName());
274 protected <S1 extends INeutronAdminAttributes<S1>, M extends BaseAttributes & AdminAttributes, B extends Builder<M>>
275 void toMdAdminAttributes(S1 neutronObject, B builder) {
276 toMdBaseAttributes(neutronObject, builder);
278 if (neutronObject.getAdminStateUp() != null) {
279 setAdminStateUp.invoke(builder, neutronObject.getAdminStateUp());
281 if (neutronObject.getStatus() != null) {
282 setStatus.invoke(builder, neutronObject.getStatus());
284 } catch (IllegalAccessException | InvocationTargetException e) {
285 throw new IllegalArgumentException(e);
289 protected <M extends BaseAttributes & AdminAttributes, S1 extends INeutronAdminAttributes<S1>>
290 void fromMdAdminAttributes(M attr, S1 answer) {
291 fromMdBaseAttributes(attr, answer);
292 if (attr.isAdminStateUp() != null) {
293 answer.setAdminStateUp(attr.isAdminStateUp());
295 if (attr.getStatus() != null) {
296 answer.setStatus(attr.getStatus());
300 protected abstract T toMd(S neutronObject);
302 protected T toMd(String uuid) {
305 builder = builderClass.newInstance();
306 setUuid.invoke(builder, toUuid(uuid));
307 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
308 // should not happen.
309 throw new IllegalArgumentException(e);
311 return builder.build();
314 protected abstract S fromMd(T dataObject);
316 private <W extends DataObject> W readMd(InstanceIdentifier<W> path, ReadTransaction tx) {
317 Preconditions.checkNotNull(tx);
319 final CheckedFuture<Optional<W>,
320 ReadFailedException> future = tx.read(LogicalDatastoreType.CONFIGURATION, path);
321 if (future != null) {
322 Optional<W> optional;
324 optional = future.checkedGet();
325 if (optional.isPresent()) {
326 result = optional.get();
328 } catch (final ReadFailedException e) {
329 LOG.warn("Failed to read {}", path, e);
335 protected <W extends DataObject> W readMd(InstanceIdentifier<W> path) {
336 try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
337 return readMd(path, tx);
341 private void addMd(S neutronObject, WriteTransaction tx) throws InterruptedException, ExecutionException {
342 // TODO think about adding existence logic
343 updateMd(neutronObject, tx);
346 protected boolean addMd(S neutronObject) {
348 final WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
349 addMd(neutronObject, tx);
351 } catch (InterruptedException | ExecutionException e) {
352 LOG.warn("Transaction failed", e);
357 private void updateMd(S neutronObject, WriteTransaction tx) throws InterruptedException, ExecutionException {
358 Preconditions.checkNotNull(tx);
360 final T item = toMd(neutronObject);
361 final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
362 tx.put(LogicalDatastoreType.CONFIGURATION, iid, item, true);
363 final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
364 // Check if it's successfully committed, otherwise exception will be thrown.
368 protected boolean updateMd(S neutronObject) {
369 int retries = RETRY_MAX;
370 while (retries-- >= 0) {
372 final WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
373 updateMd(neutronObject, tx);
375 } catch (InterruptedException | ExecutionException e) {
376 if (e.getCause() instanceof OptimisticLockFailedException) {
377 LOG.warn("Got OptimisticLockFailedException - {} {}", neutronObject, retries);
380 // TODO: rethrow exception. don't mask exception
381 LOG.error("Transaction failed", e);
388 private void removeMd(T item, WriteTransaction tx) throws InterruptedException, ExecutionException {
389 Preconditions.checkNotNull(tx);
390 final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
391 tx.delete(LogicalDatastoreType.CONFIGURATION, iid);
392 final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
393 // Check if it's successfully committed, otherwise exception will be thrown.
397 protected boolean removeMd(T item) {
398 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
402 } catch (InterruptedException | ExecutionException e) {
403 LOG.warn("Transaction failed", e);
408 protected static Uuid toUuid(String uuid) {
409 Preconditions.checkNotNull(uuid, "uuid");
412 result = new Uuid(uuid);
413 } catch (final IllegalArgumentException e) {
414 // OK... someone didn't follow RFC 4122... lets try this the hard way
415 final String dedashed = uuid.replace("-", "");
416 if (dedashed.length() == DEDASHED_UUID_LENGTH) {
417 final String redashed = dedashed.substring(DEDASHED_UUID_START, DEDASHED_UUID_DIV1) + "-"
418 + dedashed.substring(DEDASHED_UUID_DIV1, DEDASHED_UUID_DIV2) + "-"
419 + dedashed.substring(DEDASHED_UUID_DIV2, DEDASHED_UUID_DIV3) + "-"
420 + dedashed.substring(DEDASHED_UUID_DIV3, DEDASHED_UUID_DIV4) + "-"
421 + dedashed.substring(DEDASHED_UUID_DIV4, DEDASHED_UUID_LENGTH);
422 result = new Uuid(redashed);
432 public void close() throws Exception {
436 public boolean exists(String uuid, ReadTransaction tx) {
437 Preconditions.checkNotNull(tx);
438 final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
439 return dataObject != null;
442 private S get(String uuid, ReadTransaction tx) {
443 Preconditions.checkNotNull(tx);
444 final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
445 if (dataObject == null) {
448 return fromMd(dataObject);
452 public S get(String uuid) {
453 try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
454 return get(uuid, tx);
458 protected abstract List<T> getDataObjectList(U dataObjects);
460 private List<S> getAll(ReadTransaction tx) {
461 Preconditions.checkNotNull(tx);
462 final Set<S> allNeutronObjects = new HashSet<>();
463 final U dataObjects = readMd(createInstanceIdentifier(), tx);
464 if (dataObjects != null) {
465 for (final T dataObject : getDataObjectList(dataObjects)) {
466 allNeutronObjects.add(fromMd(dataObject));
469 LOG.debug("Exiting _getAll, Found {} OpenStackFirewall", allNeutronObjects.size());
470 final List<S> ans = new ArrayList<>();
471 ans.addAll(allNeutronObjects);
476 public List<S> getAll() {
477 try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
482 private Result add(S input, ReadWriteTransaction tx) throws InterruptedException, ExecutionException {
483 Preconditions.checkNotNull(tx);
484 if (exists(input.getID(), tx)) {
486 return Result.AlreadyExists;
489 return Result.Success;
493 public Result add(S input) {
494 int retries = RETRY_MAX;
495 while (retries-- >= 0) {
496 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
498 if (areAllDependenciesAvailable(tx, input)) {
499 return add(input, tx);
501 return Result.DependencyMissing;
503 } catch (InterruptedException | ExecutionException e) {
504 // TODO replace all this with org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner
505 if (e.getCause() instanceof OptimisticLockFailedException) {
506 LOG.warn("Got OptimisticLockFailedException - {} {}", input, retries);
509 // TODO: rethrow exception. don't mask exception
510 LOG.error("Transaction failed", e);
514 // TODO remove when re-throwing, and remove Result.Exception completely
515 return Result.Exception;
518 private boolean remove(String uuid, ReadWriteTransaction tx) throws InterruptedException, ExecutionException {
519 Preconditions.checkNotNull(tx);
520 if (!exists(uuid, tx)) {
524 removeMd(toMd(uuid), tx);
529 public boolean remove(String uuid) {
530 int retries = RETRY_MAX;
531 while (retries-- >= 0) {
532 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
534 return remove(uuid, tx);
535 } catch (InterruptedException | ExecutionException e) {
536 if (e.getCause() instanceof OptimisticLockFailedException) {
537 LOG.warn("Got OptimisticLockFailedException - {} {}", uuid, retries);
540 // TODO: rethrow exception. don't mask exception
541 LOG.error("Transaction failed", e);
548 private Result update(String uuid, S delta, ReadWriteTransaction tx)
549 throws InterruptedException, ExecutionException {
550 Preconditions.checkNotNull(tx);
551 if (!exists(uuid, tx)) {
553 return Result.DoesNotExist;
556 return Result.Success;
560 public Result update(String uuid, S delta) {
561 int retries = RETRY_MAX;
562 while (retries-- >= 0) {
563 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
565 return update(uuid, delta, tx);
566 } catch (InterruptedException | ExecutionException e) {
567 if (e.getCause() instanceof OptimisticLockFailedException) {
568 LOG.warn("Got OptimisticLockFailedException - {} {} {}", uuid, delta, retries);
571 // TODO: rethrow exception. don't mask exception
572 LOG.error("Transaction failed", e);
576 // TODO remove when re-throwing, and remove Result.Exception completely
577 return Result.Exception;
581 * Check if this particular (subclass) transcriber's dependencies are met.
582 * Default implementation just returns true. Some but not all transcribers will customize this.
584 * <p>Implementations *MUST* use the passed in transaction. They will typically call the
585 * {@link #exists(String, ReadTransaction)} method on ANOTHER transcriber with it.
587 * <p>Implementations should chain {@link #ifNonNull(Object, Function)}, or perform null safe comparisons otherwise,
588 * for any optional non-mandatory {@link NeutronObject} properties which may well be null.
590 * @param tx the transaction within which to perform reads to check for dependencies
591 * @param neutronObject the incoming main neutron object in which there may be references to dependencies
593 * @return true if all dependencies are available and the
594 * {@link #add(INeutronObject)} (or {@link #update(String, INeutronObject)} operation can proceed; false if
595 * there are unmet dependencies, which will cause the add to abort, and a respective
596 * error code returned to the caller.
598 protected boolean areAllDependenciesAvailable(ReadTransaction tx, S neutronObject) {
603 * Utility to perform well readable code of null-safe chains of e.g.
604 * {@link #exists(String, ReadTransaction)} method calls.
606 protected static final <X> boolean ifNonNull(
607 @Nullable X property, Function<@NonNull X, @NonNull Boolean> function) {
608 if (property != null) {
609 Boolean result = function.apply(property);
610 Preconditions.checkNotNull(result, "result");
613 // We return true, in line with the default implementation
614 // in org.opendaylight.neutron.transcriber.AbstractTranscriberInterface.
615 // areAllDependenciesAvailable(ReadTransaction, S)