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 javax.annotation.PreDestroy;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
28 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
29 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
32 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
34 import org.opendaylight.infrautils.utils.function.CheckedFunction;
35 import org.opendaylight.neutron.spi.INeutronAdminAttributes;
36 import org.opendaylight.neutron.spi.INeutronBaseAttributes;
37 import org.opendaylight.neutron.spi.INeutronCRUD;
38 import org.opendaylight.neutron.spi.INeutronObject;
39 import org.opendaylight.neutron.spi.NeutronObject;
40 import org.opendaylight.neutron.spi.ReadFailedRuntimeException;
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.opendaylight.yangtools.yang.common.OperationFailedException;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 * Base class of Transcriber converts data from/to neutron spi to/from md-sal yang model.
65 * T(MD-SAL model) <-> S (neutron spi): Neutron northbound
80 * @param <T> Target yang model
81 * @param <U> parent of T
82 * @param <K> key type to indentify T
83 * @param <S> Neutron-spi class
84 * @param <V> parent of U
86 public abstract class AbstractTranscriberInterface<
87 T extends DataObject & Identifiable<K> & ChildOf<? super U>,
88 U extends ChildOf<? super V> & Augmentable<U>,
89 K extends Identifier<T>, S extends INeutronObject<S>,
91 implements AutoCloseable, INeutronCRUD<S> {
93 private static final Logger LOG = LoggerFactory.getLogger(AbstractTranscriberInterface.class);
95 // T extends DataObject & Identifiable<K> & ChildOf<? super U> as 0th type argument
96 private static final int MD_LIST_CLASS_TYPE_INDEX = 0;
97 // U extends ChildOf<? super Neutron> & Augmentable<U> as 1st type argument
98 private static final int MD_CONTAINER_CLASS_TYPE_INDEX = 1;
99 // V extends DataObject as 4th type argument
100 private static final int MD_PARENT_CLASS_TYPE_INDEX = 4;
101 // S extends INeutronObject<S> as 3rd type argument
102 private static final int NEUTRON_OBJECT_TYPE_INDEX = 3;
104 private static final int DEDASHED_UUID_LENGTH = 32;
105 private static final int DEDASHED_UUID_START = 0;
106 private static final int DEDASHED_UUID_DIV1 = 8;
107 private static final int DEDASHED_UUID_DIV2 = 12;
108 private static final int DEDASHED_UUID_DIV3 = 16;
109 private static final int DEDASHED_UUID_DIV4 = 20;
111 private static final int RETRY_MAX = 2;
113 private final DataBroker db;
115 private final Class<V> mdParentClass;
116 private final Class<U> mdContainerClass;
117 private final Class<T> mdListClass;
119 // Unfortunately odl yangtools doesn't model yang model "uses" as
120 // class/interface hierarchy. So we need to resort to use reflection
121 // to call setter method.
122 private final Class<? extends Builder<T>> builderClass;
123 private final Method setUuid;
124 private final Method setTenantId;
125 private final Method setProjectId;
126 private final Method setName;
127 private final Method setAdminStateUp;
128 private final Method setStatus;
129 private final Method setRevisionNumber;
131 protected Class<V> getMdParentClass(final Type[] types) {
132 @SuppressWarnings("unchecked")
133 Class<V> localMdParentClass = (Class<V>) types[MD_PARENT_CLASS_TYPE_INDEX];
134 return localMdParentClass;
137 protected AbstractTranscriberInterface(Class<? extends Builder<T>> builderClass, DataBroker db) {
138 this.db = Preconditions.checkNotNull(db);
139 this.builderClass = builderClass;
141 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
142 Type[] types = parameterizedType.getActualTypeArguments();
143 @SuppressWarnings("unchecked")
144 Class<T> localMdListClass = (Class<T>) types[MD_LIST_CLASS_TYPE_INDEX];
145 mdListClass = localMdListClass;
146 @SuppressWarnings("unchecked")
147 Class<U> localMdContainerClass = (Class<U>) types[MD_CONTAINER_CLASS_TYPE_INDEX];
148 mdContainerClass = localMdContainerClass;
149 mdParentClass = getMdParentClass(types);
150 @SuppressWarnings("unchecked")
151 Class<S> neutronObjectClass = (Class<S>) types[NEUTRON_OBJECT_TYPE_INDEX];
153 setUuid = builderClass.getDeclaredMethod("setUuid", Uuid.class);
154 setTenantId = builderClass.getDeclaredMethod("setTenantId", Uuid.class);
155 setProjectId = builderClass.getDeclaredMethod("setProjectId", String.class);
156 setRevisionNumber = builderClass.getDeclaredMethod("setRevisionNumber", Long.class);
157 if (INeutronBaseAttributes.class.isAssignableFrom(neutronObjectClass)) {
158 setName = builderClass.getDeclaredMethod("setName", String.class);
163 if (INeutronAdminAttributes.class.isAssignableFrom(neutronObjectClass)) {
164 setAdminStateUp = builderClass.getDeclaredMethod("setAdminStateUp", Boolean.class);
165 setStatus = builderClass.getDeclaredMethod("setStatus", String.class);
167 setAdminStateUp = null;
170 } catch (NoSuchMethodException e) {
171 throw new IllegalArgumentException(e);
175 public DataBroker getDataBroker() {
176 Preconditions.checkNotNull(db);
180 private InstanceIdentifier<T> createInstanceIdentifier(T item) {
181 return InstanceIdentifier.create(mdParentClass).child(mdContainerClass).child(mdListClass, item.key());
184 private InstanceIdentifier<U> createInstanceIdentifier() {
185 return InstanceIdentifier.create(mdParentClass).child(mdContainerClass);
188 protected static <S1 extends INeutronObject<S1>, M extends IdAttributes, B extends Builder<M>>
189 B toMdIds(INeutronObject<S1> neutronObject, Class<B> builderClass) {
192 builder = builderClass.newInstance();
194 if (neutronObject.getID() != null) {
195 final Method setUuid = builderClass.getMethod("setUuid", Uuid.class);
196 setUuid.invoke(builder, toUuid(neutronObject.getID()));
198 if (neutronObject.getTenantID() != null && !neutronObject.getTenantID().isEmpty()) {
199 final Method setTenantId = builderClass.getMethod("setTenantId", Uuid.class);
200 setTenantId.invoke(builder, toUuid(neutronObject.getTenantID()));
202 if (neutronObject.getProjectID() != null) {
203 final Method setProjectId = builderClass.getMethod("setProjectId", String.class);
204 setProjectId.invoke(builder, neutronObject.getTenantID());
206 if (neutronObject.getRevisionNumber() != null) {
207 final Method setRevisionNumber = builderClass.getMethod("setRevisionNumber", Long.class);
208 setRevisionNumber.invoke(builder, neutronObject.getRevisionNumber());
210 } catch (IllegalAccessException | InstantiationException | InvocationTargetException
211 | NoSuchMethodException e) {
212 throw new IllegalArgumentException(e);
217 protected <S1 extends INeutronObject<S1>, M extends IdAttributes, B extends Builder<M>>
218 void toMdIds(INeutronObject<S1> neutronObject, B builder) {
220 if (neutronObject.getID() != null) {
221 setUuid.invoke(builder, toUuid(neutronObject.getID()));
223 LOG.warn("Attempting to write neutron object {} without UUID", builderClass.getSimpleName());
225 if (neutronObject.getTenantID() != null && !neutronObject.getTenantID().isEmpty()) {
226 setTenantId.invoke(builder, toUuid(neutronObject.getTenantID()));
228 if (neutronObject.getProjectID() != null) {
229 setProjectId.invoke(builder, neutronObject.getTenantID());
231 if (neutronObject.getRevisionNumber() != null) {
232 setRevisionNumber.invoke(builder, neutronObject.getRevisionNumber());
234 } catch (IllegalAccessException | InvocationTargetException e) {
235 throw new IllegalArgumentException(e);
239 protected <S1 extends INeutronObject<S1>>
240 void fromMdIds(IdAttributes idAttributes, INeutronObject<S1> answer) {
241 if (idAttributes.getUuid() != null) {
242 answer.setID(idAttributes.getUuid().getValue());
244 if (idAttributes.getTenantId() != null) {
245 answer.setTenantID(idAttributes.getTenantId());
247 if (idAttributes.getProjectId() != null) {
248 answer.setProjectID(idAttributes.getProjectId());
250 if (idAttributes.getRevisionNumber() != null) {
251 answer.setRevisionNumber(idAttributes.getRevisionNumber());
255 protected <S1 extends INeutronBaseAttributes<S1>, M extends BaseAttributes, B extends Builder<M>>
256 void toMdBaseAttributes(S1 neutronObject, B builder) {
257 toMdIds(neutronObject, builder);
259 if (neutronObject.getName() != null) {
260 setName.invoke(builder, neutronObject.getName());
262 } catch (IllegalAccessException | InvocationTargetException e) {
263 throw new IllegalArgumentException(e);
267 protected <S1 extends INeutronBaseAttributes<S1>>
268 void fromMdBaseAttributes(BaseAttributes baseAttributes, S1 answer) {
269 fromMdIds(baseAttributes, answer);
270 if (baseAttributes.getName() != null) {
271 answer.setName(baseAttributes.getName());
275 protected <S1 extends INeutronAdminAttributes<S1>, M extends BaseAttributes & AdminAttributes, B extends Builder<M>>
276 void toMdAdminAttributes(S1 neutronObject, B builder) {
277 toMdBaseAttributes(neutronObject, builder);
279 if (neutronObject.getAdminStateUp() != null) {
280 setAdminStateUp.invoke(builder, neutronObject.getAdminStateUp());
282 if (neutronObject.getStatus() != null) {
283 setStatus.invoke(builder, neutronObject.getStatus());
285 } catch (IllegalAccessException | InvocationTargetException e) {
286 throw new IllegalArgumentException(e);
290 protected <M extends BaseAttributes & AdminAttributes, S1 extends INeutronAdminAttributes<S1>>
291 void fromMdAdminAttributes(M attr, S1 answer) {
292 fromMdBaseAttributes(attr, answer);
293 if (attr.isAdminStateUp() != null) {
294 answer.setAdminStateUp(attr.isAdminStateUp());
296 if (attr.getStatus() != null) {
297 answer.setStatus(attr.getStatus());
301 protected abstract T toMd(S neutronObject);
303 protected T toMd(String uuid) {
306 builder = builderClass.newInstance();
307 setUuid.invoke(builder, toUuid(uuid));
308 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
309 // should not happen.
310 throw new IllegalArgumentException(e);
312 return builder.build();
315 protected abstract S fromMd(T dataObject);
317 private <W extends DataObject> W readMd(InstanceIdentifier<W> path, ReadTransaction tx) throws ReadFailedException {
318 Preconditions.checkNotNull(tx);
320 final CheckedFuture<Optional<W>,
321 ReadFailedException> future = tx.read(LogicalDatastoreType.CONFIGURATION, path);
322 if (future != null) {
323 Optional<W> optional = future.checkedGet();
324 if (optional.isPresent()) {
325 result = optional.get();
331 protected <W extends DataObject> W readMd(InstanceIdentifier<W> path) throws ReadFailedException {
332 try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
333 return readMd(path, tx);
337 private void addMd(S neutronObject, WriteTransaction tx) throws TransactionCommitFailedException {
338 // TODO think about adding existence logic
339 updateMd(neutronObject, tx);
342 private void updateMd(S neutronObject, WriteTransaction tx) throws TransactionCommitFailedException {
343 Preconditions.checkNotNull(tx);
345 final T item = toMd(neutronObject);
346 final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
347 tx.put(LogicalDatastoreType.CONFIGURATION, iid, item, true);
348 final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
349 // Check if it's successfully committed, otherwise exception will be thrown.
353 private void removeMd(T item, WriteTransaction tx) throws TransactionCommitFailedException {
354 Preconditions.checkNotNull(tx);
355 final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
356 tx.delete(LogicalDatastoreType.CONFIGURATION, iid);
357 final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
358 // Check if it's successfully committed, otherwise exception will be thrown.
362 protected static Uuid toUuid(String uuid) {
363 Preconditions.checkNotNull(uuid, "uuid");
366 result = new Uuid(uuid);
367 } catch (final IllegalArgumentException e) {
368 // OK... someone didn't follow RFC 4122... lets try this the hard way
369 final String dedashed = uuid.replace("-", "");
370 if (dedashed.length() == DEDASHED_UUID_LENGTH) {
371 final String redashed = dedashed.substring(DEDASHED_UUID_START, DEDASHED_UUID_DIV1) + "-"
372 + dedashed.substring(DEDASHED_UUID_DIV1, DEDASHED_UUID_DIV2) + "-"
373 + dedashed.substring(DEDASHED_UUID_DIV2, DEDASHED_UUID_DIV3) + "-"
374 + dedashed.substring(DEDASHED_UUID_DIV3, DEDASHED_UUID_DIV4) + "-"
375 + dedashed.substring(DEDASHED_UUID_DIV4, DEDASHED_UUID_LENGTH);
376 result = new Uuid(redashed);
386 public void close() throws Exception {
390 public boolean exists(String uuid, ReadTransaction tx) throws ReadFailedException {
391 Preconditions.checkNotNull(tx);
392 final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
393 return dataObject != null;
396 private S get(String uuid, ReadTransaction tx) throws ReadFailedException {
397 Preconditions.checkNotNull(tx);
398 final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
399 if (dataObject == null) {
402 return fromMd(dataObject);
406 public S get(String uuid) throws ReadFailedException {
407 try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
408 return get(uuid, tx);
412 protected abstract List<T> getDataObjectList(U dataObjects);
414 private List<S> getAll(ReadTransaction tx) throws ReadFailedException {
415 Preconditions.checkNotNull(tx);
416 final Set<S> allNeutronObjects = new HashSet<>();
417 final U dataObjects = readMd(createInstanceIdentifier(), tx);
418 if (dataObjects != null) {
419 for (final T dataObject : getDataObjectList(dataObjects)) {
420 allNeutronObjects.add(fromMd(dataObject));
423 LOG.debug("Exiting _getAll, Found {} OpenStackFirewall", allNeutronObjects.size());
424 final List<S> ans = new ArrayList<>();
425 ans.addAll(allNeutronObjects);
430 public List<S> getAll() throws ReadFailedRuntimeException {
431 try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
434 } catch (ReadFailedException e) {
435 throw new ReadFailedRuntimeException(e);
440 private Result add(S input, ReadWriteTransaction tx) throws OperationFailedException {
441 Preconditions.checkNotNull(tx);
442 if (exists(input.getID(), tx)) {
444 return Result.AlreadyExists;
447 return Result.Success;
451 public Result add(S input) throws OperationFailedException {
452 int retries = RETRY_MAX;
453 OptimisticLockFailedException lastOptimisticLockFailedException = null;
454 while (retries-- >= 0) {
455 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
457 if (areAllDependenciesAvailable(tx, input)) {
458 return add(input, tx);
460 return Result.DependencyMissing;
462 } catch (TransactionCommitFailedException e) {
463 // TODO replace all this with org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner
464 if (e instanceof OptimisticLockFailedException) {
465 LOG.debug("Got OptimisticLockFailedException - {} {}", input, retries);
466 lastOptimisticLockFailedException = (OptimisticLockFailedException) e;
473 throw lastOptimisticLockFailedException;
476 private boolean remove(String uuid, ReadWriteTransaction tx) throws OperationFailedException {
477 Preconditions.checkNotNull(tx);
478 if (!exists(uuid, tx)) {
482 removeMd(toMd(uuid), tx);
487 public boolean remove(String uuid) throws OperationFailedException {
488 int retries = RETRY_MAX;
489 OptimisticLockFailedException lastOptimisticLockFailedException = null;
490 while (retries-- >= 0) {
491 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
493 return remove(uuid, tx);
494 } catch (TransactionCommitFailedException e) {
495 // TODO replace all this with org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner
496 if (e instanceof OptimisticLockFailedException) {
497 LOG.debug("Got OptimisticLockFailedException - {} {}", uuid, retries);
498 lastOptimisticLockFailedException = (OptimisticLockFailedException) e;
505 throw lastOptimisticLockFailedException;
508 private Result update(String uuid, S delta, ReadWriteTransaction tx) throws OperationFailedException {
509 Preconditions.checkNotNull(tx);
510 if (!exists(uuid, tx)) {
512 return Result.DoesNotExist;
515 return Result.Success;
519 public Result update(String uuid, S delta) throws OperationFailedException {
520 int retries = RETRY_MAX;
521 OptimisticLockFailedException lastOptimisticLockFailedException = null;
522 while (retries-- >= 0) {
523 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
525 if (areAllDependenciesAvailable(tx, delta)) {
526 return update(uuid, delta, tx);
528 return Result.DependencyMissing;
530 } catch (TransactionCommitFailedException e) {
531 // TODO replace all this with org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner
532 if (e instanceof OptimisticLockFailedException) {
533 LOG.debug("Got OptimisticLockFailedException - {} {} {}", uuid, delta, retries);
534 lastOptimisticLockFailedException = (OptimisticLockFailedException) e;
541 throw lastOptimisticLockFailedException;
545 * Check if this particular (subclass) transcriber's dependencies are met.
546 * Default implementation just returns true. Some but not all transcribers will customize this.
548 * <p>Implementations *MUST* use the passed in transaction. They will typically call the
549 * {@link #exists(String, ReadTransaction)} method on ANOTHER transcriber with it.
551 * <p>Implementations should chain {@link #ifNonNull(Object, CheckedFunction)}, or perform null safe comparisons
552 * otherwise, for both optional non-mandatory {@link NeutronObject} as well as mandatory properties which may well
553 * be null. Both must mandatory and non-mandatory must be guarded, because modify (update) operation are allowed to
554 * contain partial neutron objects with missing fields.
556 * @param tx the transaction within which to perform reads to check for dependencies
557 * @param neutronObject the incoming main neutron object in which there may be references to dependencies
559 * @return true if all dependencies are available and the
560 * {@link #add(INeutronObject)} (or {@link #update(String, INeutronObject)} operation can proceed; false if
561 * there are unmet dependencies, which will cause the add to abort, and a respective
562 * error code returned to the caller.
564 * @throws ReadFailedException in case of a data store problem
566 protected boolean areAllDependenciesAvailable(ReadTransaction tx, S neutronObject) throws ReadFailedException {
571 * Utility to perform well readable code of null-safe chains of e.g.
572 * {@link #exists(String, ReadTransaction)} method calls.
574 * @throws ReadFailedException in case of a data store problem
576 protected static final <X> boolean ifNonNull(@Nullable X property,
577 CheckedFunction<@NonNull X, @NonNull Boolean, ReadFailedException> function) throws ReadFailedException {
578 if (property != null) {
579 Boolean result = function.apply(property);
580 Preconditions.checkNotNull(result, "result");
583 // We return true, in line with the default implementation
584 // in org.opendaylight.neutron.transcriber.AbstractTranscriberInterface.
585 // areAllDependenciesAvailable(ReadTransaction, S)