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 javax.annotation.PreDestroy;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
26 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
27 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
31 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
33 import org.opendaylight.neutron.spi.INeutronAdminAttributes;
34 import org.opendaylight.neutron.spi.INeutronBaseAttributes;
35 import org.opendaylight.neutron.spi.INeutronCRUD;
36 import org.opendaylight.neutron.spi.INeutronObject;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.AdminAttributes;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.BaseAttributes;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.attrs.rev150712.IdAttributes;
41 import org.opendaylight.yangtools.concepts.Builder;
42 import org.opendaylight.yangtools.yang.binding.Augmentable;
43 import org.opendaylight.yangtools.yang.binding.ChildOf;
44 import org.opendaylight.yangtools.yang.binding.DataObject;
45 import org.opendaylight.yangtools.yang.binding.Identifiable;
46 import org.opendaylight.yangtools.yang.binding.Identifier;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
52 * Base class of Transcriber converts data from/to neutron spi to/from md-sal yang model.
60 * T(MD-SAL model) <-> S (neutron spi): Neutron northbound
75 * @param <T> Target yang model
76 * @param <U> parent of T
77 * @param <K> key type to indentify T
78 * @param <S> Neutron-spi class
79 * @param <V> parent of U
81 public abstract class AbstractTranscriberInterface<
82 T extends DataObject & Identifiable<K> & ChildOf<? super U>,
83 U extends ChildOf<? super V> & Augmentable<U>,
84 K extends Identifier<T>, S extends INeutronObject<S>,
86 implements AutoCloseable, INeutronCRUD<S> {
88 private static final Logger LOG = LoggerFactory.getLogger(AbstractTranscriberInterface.class);
90 // T extends DataObject & Identifiable<K> & ChildOf<? super U> as 0th type argument
91 private static final int MD_LIST_CLASS_TYPE_INDEX = 0;
92 // U extends ChildOf<? super Neutron> & Augmentable<U> as 1st type argument
93 private static final int MD_CONTAINER_CLASS_TYPE_INDEX = 1;
94 // V extends DataObject as 4th type argument
95 private static final int MD_PARENT_CLASS_TYPE_INDEX = 4;
96 // S extends INeutronObject<S> as 3rd type argument
97 private static final int NEUTRON_OBJECT_TYPE_INDEX = 3;
99 private static final int DEDASHED_UUID_LENGTH = 32;
100 private static final int DEDASHED_UUID_START = 0;
101 private static final int DEDASHED_UUID_DIV1 = 8;
102 private static final int DEDASHED_UUID_DIV2 = 12;
103 private static final int DEDASHED_UUID_DIV3 = 16;
104 private static final int DEDASHED_UUID_DIV4 = 20;
106 private static final int RETRY_MAX = 2;
108 private final DataBroker db;
110 private final Class<V> mdParentClass;
111 private final Class<U> mdContainerClass;
112 private final Class<T> mdListClass;
114 // Unfortunately odl yangtools doesn't model yang model "uses" as
115 // class/interface hierarchy. So we need to resort to use reflection
116 // to call setter method.
117 private final Class<? extends Builder<T>> builderClass;
118 private final Method setUuid;
119 private final Method setTenantId;
120 private final Method setProjectId;
121 private final Method setName;
122 private final Method setAdminStateUp;
123 private final Method setStatus;
124 private final Method setRevisionNumber;
126 protected Class<V> getMdParentClass(final Type[] types) {
127 @SuppressWarnings("unchecked")
128 Class<V> localMdParentClass = (Class<V>) types[MD_PARENT_CLASS_TYPE_INDEX];
129 return localMdParentClass;
132 protected AbstractTranscriberInterface(Class<? extends Builder<T>> builderClass, DataBroker db) {
133 this.db = Preconditions.checkNotNull(db);
134 this.builderClass = builderClass;
136 ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
137 Type[] types = parameterizedType.getActualTypeArguments();
138 @SuppressWarnings("unchecked")
139 Class<T> localMdListClass = (Class<T>) types[MD_LIST_CLASS_TYPE_INDEX];
140 mdListClass = localMdListClass;
141 @SuppressWarnings("unchecked")
142 Class<U> localMdContainerClass = (Class<U>) types[MD_CONTAINER_CLASS_TYPE_INDEX];
143 mdContainerClass = localMdContainerClass;
144 mdParentClass = getMdParentClass(types);
145 @SuppressWarnings("unchecked")
146 Class<S> neutronObjectClass = (Class<S>) types[NEUTRON_OBJECT_TYPE_INDEX];
148 setUuid = builderClass.getDeclaredMethod("setUuid", Uuid.class);
149 setTenantId = builderClass.getDeclaredMethod("setTenantId", Uuid.class);
150 setProjectId = builderClass.getDeclaredMethod("setProjectId", String.class);
151 setRevisionNumber = builderClass.getDeclaredMethod("setRevisionNumber", Long.class);
152 if (INeutronBaseAttributes.class.isAssignableFrom(neutronObjectClass)) {
153 setName = builderClass.getDeclaredMethod("setName", String.class);
158 if (INeutronAdminAttributes.class.isAssignableFrom(neutronObjectClass)) {
159 setAdminStateUp = builderClass.getDeclaredMethod("setAdminStateUp", Boolean.class);
160 setStatus = builderClass.getDeclaredMethod("setStatus", String.class);
162 setAdminStateUp = null;
165 } catch (NoSuchMethodException e) {
166 throw new IllegalArgumentException(e);
170 public DataBroker getDataBroker() {
171 Preconditions.checkNotNull(db);
175 private InstanceIdentifier<T> createInstanceIdentifier(T item) {
176 return InstanceIdentifier.create(mdParentClass).child(mdContainerClass).child(mdListClass, item.key());
179 private InstanceIdentifier<U> createInstanceIdentifier() {
180 return InstanceIdentifier.create(mdParentClass).child(mdContainerClass);
183 protected static <S1 extends INeutronObject<S1>, M extends IdAttributes, B extends Builder<M>>
184 B toMdIds(INeutronObject<S1> neutronObject, Class<B> builderClass) {
187 builder = builderClass.newInstance();
189 if (neutronObject.getID() != null) {
190 final Method setUuid = builderClass.getMethod("setUuid", Uuid.class);
191 setUuid.invoke(builder, toUuid(neutronObject.getID()));
193 if (neutronObject.getTenantID() != null && !neutronObject.getTenantID().isEmpty()) {
194 final Method setTenantId = builderClass.getMethod("setTenantId", Uuid.class);
195 setTenantId.invoke(builder, toUuid(neutronObject.getTenantID()));
197 if (neutronObject.getProjectID() != null) {
198 final Method setProjectId = builderClass.getMethod("setProjectId", String.class);
199 setProjectId.invoke(builder, neutronObject.getTenantID());
201 if (neutronObject.getRevisionNumber() != null) {
202 final Method setRevisionNumber = builderClass.getMethod("setRevisionNumber", Long.class);
203 setRevisionNumber.invoke(builder, neutronObject.getRevisionNumber());
205 } catch (IllegalAccessException | InstantiationException | InvocationTargetException
206 | NoSuchMethodException e) {
207 throw new IllegalArgumentException(e);
212 protected <S1 extends INeutronObject<S1>, M extends IdAttributes, B extends Builder<M>>
213 void toMdIds(INeutronObject<S1> neutronObject, B builder) {
215 if (neutronObject.getID() != null) {
216 setUuid.invoke(builder, toUuid(neutronObject.getID()));
218 LOG.warn("Attempting to write neutron object {} without UUID", builderClass.getSimpleName());
220 if (neutronObject.getTenantID() != null && !neutronObject.getTenantID().isEmpty()) {
221 setTenantId.invoke(builder, toUuid(neutronObject.getTenantID()));
223 if (neutronObject.getProjectID() != null) {
224 setProjectId.invoke(builder, neutronObject.getTenantID());
226 if (neutronObject.getRevisionNumber() != null) {
227 setRevisionNumber.invoke(builder, neutronObject.getRevisionNumber());
229 } catch (IllegalAccessException | InvocationTargetException e) {
230 throw new IllegalArgumentException(e);
234 protected <S1 extends INeutronObject<S1>>
235 void fromMdIds(IdAttributes idAttributes, INeutronObject<S1> answer) {
236 if (idAttributes.getUuid() != null) {
237 answer.setID(idAttributes.getUuid().getValue());
239 if (idAttributes.getTenantId() != null) {
240 answer.setTenantID(idAttributes.getTenantId());
242 if (idAttributes.getProjectId() != null) {
243 answer.setProjectID(idAttributes.getProjectId());
245 if (idAttributes.getRevisionNumber() != null) {
246 answer.setRevisionNumber(idAttributes.getRevisionNumber());
250 protected <S1 extends INeutronBaseAttributes<S1>, M extends BaseAttributes, B extends Builder<M>>
251 void toMdBaseAttributes(S1 neutronObject, B builder) {
252 toMdIds(neutronObject, builder);
254 if (neutronObject.getName() != null) {
255 setName.invoke(builder, neutronObject.getName());
257 } catch (IllegalAccessException | InvocationTargetException e) {
258 throw new IllegalArgumentException(e);
262 protected <S1 extends INeutronBaseAttributes<S1>>
263 void fromMdBaseAttributes(BaseAttributes baseAttributes, S1 answer) {
264 fromMdIds(baseAttributes, answer);
265 if (baseAttributes.getName() != null) {
266 answer.setName(baseAttributes.getName());
270 protected <S1 extends INeutronAdminAttributes<S1>, M extends BaseAttributes & AdminAttributes, B extends Builder<M>>
271 void toMdAdminAttributes(S1 neutronObject, B builder) {
272 toMdBaseAttributes(neutronObject, builder);
274 if (neutronObject.getAdminStateUp() != null) {
275 setAdminStateUp.invoke(builder, neutronObject.getAdminStateUp());
277 if (neutronObject.getStatus() != null) {
278 setStatus.invoke(builder, neutronObject.getStatus());
280 } catch (IllegalAccessException | InvocationTargetException e) {
281 throw new IllegalArgumentException(e);
285 protected <M extends BaseAttributes & AdminAttributes, S1 extends INeutronAdminAttributes<S1>>
286 void fromMdAdminAttributes(M attr, S1 answer) {
287 fromMdBaseAttributes(attr, answer);
288 if (attr.isAdminStateUp() != null) {
289 answer.setAdminStateUp(attr.isAdminStateUp());
291 if (attr.getStatus() != null) {
292 answer.setStatus(attr.getStatus());
296 protected abstract T toMd(S neutronObject);
298 protected T toMd(String uuid) {
301 builder = builderClass.newInstance();
302 setUuid.invoke(builder, toUuid(uuid));
303 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
304 // should not happen.
305 throw new IllegalArgumentException(e);
307 return builder.build();
310 protected abstract S fromMd(T dataObject);
312 private <W extends DataObject> W readMd(InstanceIdentifier<W> path, ReadTransaction tx) {
313 Preconditions.checkNotNull(tx);
315 final CheckedFuture<Optional<W>,
316 ReadFailedException> future = tx.read(LogicalDatastoreType.CONFIGURATION, path);
317 if (future != null) {
318 Optional<W> optional;
320 optional = future.checkedGet();
321 if (optional.isPresent()) {
322 result = optional.get();
324 } catch (final ReadFailedException e) {
325 LOG.warn("Failed to read {}", path, e);
331 protected <W extends DataObject> W readMd(InstanceIdentifier<W> path) {
332 try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
333 return readMd(path, tx);
337 private void addMd(S neutronObject, WriteTransaction tx) throws InterruptedException, ExecutionException {
338 // TODO think about adding existence logic
339 updateMd(neutronObject, tx);
342 protected boolean addMd(S neutronObject) {
344 final WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
345 addMd(neutronObject, tx);
347 } catch (InterruptedException | ExecutionException e) {
348 LOG.warn("Transaction failed", e);
353 private void updateMd(S neutronObject, WriteTransaction tx) throws InterruptedException, ExecutionException {
354 Preconditions.checkNotNull(tx);
356 final T item = toMd(neutronObject);
357 final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
358 tx.put(LogicalDatastoreType.CONFIGURATION, iid, item, true);
359 final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
360 // Check if it's successfully committed, otherwise exception will be thrown.
364 protected boolean updateMd(S neutronObject) {
365 int retries = RETRY_MAX;
366 while (retries-- >= 0) {
368 final WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
369 updateMd(neutronObject, tx);
371 } catch (InterruptedException | ExecutionException e) {
372 if (e.getCause() instanceof OptimisticLockFailedException) {
373 LOG.warn("Got OptimisticLockFailedException - {} {}", neutronObject, retries);
376 // TODO: rethrow exception. don't mask exception
377 LOG.error("Transaction failed", e);
384 private void removeMd(T item, WriteTransaction tx) throws InterruptedException, ExecutionException {
385 Preconditions.checkNotNull(tx);
386 final InstanceIdentifier<T> iid = createInstanceIdentifier(item);
387 tx.delete(LogicalDatastoreType.CONFIGURATION, iid);
388 final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
389 // Check if it's successfully committed, otherwise exception will be thrown.
393 protected boolean removeMd(T item) {
394 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
398 } catch (InterruptedException | ExecutionException e) {
399 LOG.warn("Transaction failed", e);
404 protected static Uuid toUuid(String uuid) {
405 Preconditions.checkNotNull(uuid);
408 result = new Uuid(uuid);
409 } catch (final IllegalArgumentException e) {
410 // OK... someone didn't follow RFC 4122... lets try this the hard way
411 final String dedashed = uuid.replace("-", "");
412 if (dedashed.length() == DEDASHED_UUID_LENGTH) {
413 final String redashed = dedashed.substring(DEDASHED_UUID_START, DEDASHED_UUID_DIV1) + "-"
414 + dedashed.substring(DEDASHED_UUID_DIV1, DEDASHED_UUID_DIV2) + "-"
415 + dedashed.substring(DEDASHED_UUID_DIV2, DEDASHED_UUID_DIV3) + "-"
416 + dedashed.substring(DEDASHED_UUID_DIV3, DEDASHED_UUID_DIV4) + "-"
417 + dedashed.substring(DEDASHED_UUID_DIV4, DEDASHED_UUID_LENGTH);
418 result = new Uuid(redashed);
428 public void close() throws Exception {
432 public boolean exists(String uuid, ReadTransaction tx) {
433 Preconditions.checkNotNull(tx);
434 final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
435 return dataObject != null;
438 private S get(String uuid, ReadTransaction tx) {
439 Preconditions.checkNotNull(tx);
440 final T dataObject = readMd(createInstanceIdentifier(toMd(uuid)), tx);
441 if (dataObject == null) {
444 return fromMd(dataObject);
448 public S get(String uuid) {
449 try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
450 return get(uuid, tx);
454 protected abstract List<T> getDataObjectList(U dataObjects);
456 private List<S> getAll(ReadTransaction tx) {
457 Preconditions.checkNotNull(tx);
458 final Set<S> allNeutronObjects = new HashSet<>();
459 final U dataObjects = readMd(createInstanceIdentifier(), tx);
460 if (dataObjects != null) {
461 for (final T dataObject : getDataObjectList(dataObjects)) {
462 allNeutronObjects.add(fromMd(dataObject));
465 LOG.debug("Exiting _getAll, Found {} OpenStackFirewall", allNeutronObjects.size());
466 final List<S> ans = new ArrayList<>();
467 ans.addAll(allNeutronObjects);
472 public List<S> getAll() {
473 try (ReadOnlyTransaction tx = getDataBroker().newReadOnlyTransaction()) {
478 private Result add(S input, ReadWriteTransaction tx) throws InterruptedException, ExecutionException {
479 Preconditions.checkNotNull(tx);
480 if (exists(input.getID(), tx)) {
482 return Result.AlreadyExists;
485 return Result.Success;
489 public Result add(S input) {
490 int retries = RETRY_MAX;
491 while (retries-- >= 0) {
492 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
494 return add(input, tx);
495 } catch (InterruptedException | ExecutionException e) {
496 // TODO replace all this with org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner
497 if (e.getCause() instanceof OptimisticLockFailedException) {
498 LOG.warn("Got OptimisticLockFailedException - {} {}", input, retries);
501 // TODO: rethrow exception. don't mask exception
502 LOG.error("Transaction failed", e);
506 // TODO remove when re-throwing, and remove Result.Exception completely
507 return Result.Exception;
510 private boolean remove(String uuid, ReadWriteTransaction tx) throws InterruptedException, ExecutionException {
511 Preconditions.checkNotNull(tx);
512 if (!exists(uuid, tx)) {
516 removeMd(toMd(uuid), tx);
521 public boolean remove(String uuid) {
522 int retries = RETRY_MAX;
523 while (retries-- >= 0) {
524 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
526 return remove(uuid, tx);
527 } catch (InterruptedException | ExecutionException e) {
528 if (e.getCause() instanceof OptimisticLockFailedException) {
529 LOG.warn("Got OptimisticLockFailedException - {} {}", uuid, retries);
532 // TODO: rethrow exception. don't mask exception
533 LOG.error("Transaction failed", e);
540 private boolean update(String uuid, S delta, ReadWriteTransaction tx)
541 throws InterruptedException, ExecutionException {
542 Preconditions.checkNotNull(tx);
543 if (!exists(uuid, tx)) {
552 public boolean update(String uuid, S delta) {
553 int retries = RETRY_MAX;
554 while (retries-- >= 0) {
555 final ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
557 return update(uuid, delta, tx);
558 } catch (InterruptedException | ExecutionException e) {
559 if (e.getCause() instanceof OptimisticLockFailedException) {
560 LOG.warn("Got OptimisticLockFailedException - {} {} {}", uuid, delta, retries);
563 // TODO: rethrow exception. don't mask exception
564 LOG.error("Transaction failed", e);