HONEYCOMB-61: Fix outstanding issues 21/1221/12
authorMaros Marsalek <mmarsale@cisco.com>
Mon, 23 May 2016 07:26:27 +0000 (09:26 +0200)
committerMaros Marsalek <mmarsale@cisco.com>
Tue, 24 May 2016 10:53:23 +0000 (12:53 +0200)
Change-Id: I2dec6bbd8db656663029ad0f59da1b583c890565
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
14 files changed:
v3po/api/src/main/yang/naming-context.yang
v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java
v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java
v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java
v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java
v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java
v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/InMemoryDataTreeModule.java
v3po/data-impl/src/main/yang/data-impl.yang
v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/JsonUtils.java
v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/initializers/InterfacesInitializer.java
v3po/vpp-cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/RestoringInitializer.java
v3po/vpp-cfg-init/src/main/yang/vpp-cfg-init.yang
v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java

index aa4ced2..fa44bdd 100644 (file)
@@ -1,7 +1,7 @@
 module naming-context {
     yang-version 1;
     namespace "urn:honeycomb:params:xml:ns:yang:naming:context";
-    prefix "vpp-u";
+    prefix "nc";
 
     description
         "This module contains data definition for naming mapping context";
@@ -25,6 +25,7 @@ module naming-context {
                 list mapping {
 
                     key "name";
+                    unique "index";
 
                     leaf name {
                         type string;
index 2b90107..75ffb70 100644 (file)
@@ -125,14 +125,14 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
                 // Blocking on context data update
                 contextUpdateResult.checkedGet();
 
-            } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) {
+            } catch (WriterRegistry.BulkUpdateException e) {
                 LOG.warn("Failed to apply all changes", e);
                 LOG.info("Trying to revert successful changes for current transaction");
 
                 try {
                     e.revertChanges();
                     LOG.info("Changes successfully reverted");
-                } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException) {
+                } catch (WriterRegistry.Reverter.RevertFailedException revertFailedException) {
                     // fail with failed revert
                     LOG.error("Failed to revert successful changes", revertFailedException);
                     throw revertFailedException;
@@ -140,7 +140,7 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
 
                 throw e; // fail with success revert
             } catch (TransactionCommitFailedException e) {
-                // FIXME revert should probably occur when context is not written successfully, but can that even happen ?
+                // FIXME revert should probably occur when context is not written successfully
                 final String msg = "Error while updating mapping context data";
                 LOG.error(msg, e);
                 throw new TranslationException(msg, e);
index df5bbee..9b71dfd 100644 (file)
@@ -17,6 +17,7 @@
 package io.fd.honeycomb.v3po.data.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.base.Optional;
 import io.fd.honeycomb.v3po.translate.util.JsonUtils;
@@ -41,32 +42,32 @@ import org.slf4j.LoggerFactory;
  * Adapter for a DataTree that stores current state of data in backing DataTree on each successful commit.
  * Uses JSON format.
  */
-public class PersistingDataTreeAdapter implements org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree, AutoCloseable {
+public class PersistingDataTreeAdapter implements DataTree, AutoCloseable {
 
     private static final Logger LOG = LoggerFactory.getLogger(PersistingDataTreeAdapter.class);
 
     private final DataTree delegateDependency;
-    private SchemaService schemaServiceDependency;
+    private final SchemaService schemaServiceDependency;
     private final Path path;
 
     /**
      * Create new Persisting DataTree adapter
      *
-     * @param delegateDependency backing data tree that actually handles all the operations
+     * @param delegate backing data tree that actually handles all the operations
      * @param persistPath path to a file (existing or not) to be used as storage for persistence. Full control over
      *                    a file at peristPath is expected
-     * @param schemaServiceDependency schemaContext provier
+     * @param schemaService schemaContext provier
      */
-    public PersistingDataTreeAdapter(@Nonnull final DataTree delegateDependency,
-                                     @Nonnull final SchemaService schemaServiceDependency,
+    public PersistingDataTreeAdapter(@Nonnull final DataTree delegate,
+                                     @Nonnull final SchemaService schemaService,
                                      @Nonnull final Path persistPath) {
-        this.path = testPersistPath(persistPath);
-        this.delegateDependency = delegateDependency;
-        this.schemaServiceDependency = schemaServiceDependency;
+        this.path = testPersistPath(checkNotNull(persistPath, "persistPath is null"));
+        this.delegateDependency = checkNotNull(delegate, "delegate is null");
+        this.schemaServiceDependency = checkNotNull(schemaService, "schemaService is null");
     }
 
     /**
-     * Test whether file at persistPath is a file and can be created/deleted
+     * Test whether file at persistPath exists and is readable or create it along with its parent structure
      */
     private Path testPersistPath(final Path persistPath) {
         try {
@@ -115,12 +116,8 @@ public class PersistingDataTreeAdapter implements org.opendaylight.yangtools.yan
         if(currentRoot.isPresent()) {
             try {
                 LOG.trace("Persisting current data: {} into: {}", currentRoot.get(), path);
-                // Make sure the file gets overwritten
-                if(Files.exists(path)) {
-                    Files.delete(path);
-                }
                 JsonUtils.writeJsonRoot(currentRoot.get(), schemaServiceDependency.getGlobalContext(),
-                    Files.newOutputStream(path, StandardOpenOption.CREATE));
+                    Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
                 LOG.trace("Data persisted successfully in {}", path);
             } catch (IOException e) {
                 throw new IllegalStateException("Unable to persist current data", e);
index c38fa27..2850a0d 100644 (file)
@@ -41,11 +41,11 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction {
     private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTransaction.class);
 
     @Nullable
-    private volatile ReadableDataManager operationalData;
+    private ReadableDataManager operationalData;
     @Nullable
-    private volatile ReadableDataManager configSnapshot;
+    private ReadableDataManager configSnapshot;
 
-    private volatile boolean closed = false;
+    private boolean closed = false;
 
     /**
      * @param configData config data tree manager. Null if config reads are not to be supported
@@ -58,14 +58,14 @@ final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction {
     }
 
     @Override
-    public void close() {
+    public synchronized void close() {
         closed = true;
         configSnapshot = null;
         operationalData = null;
     }
 
     @Override
-    public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
+    public synchronized CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
             final LogicalDatastoreType store,
             final YangInstanceIdentifier path) {
         LOG.debug("ReadOnlyTransaction.read(), store={}, path={}", store, path);
index 46903a1..8dc7f8f 100644 (file)
@@ -114,7 +114,7 @@ public final class ReadableDataTreeDelegator implements ReadableDataManager {
                 new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(
                     "Failed to read VPP data", e));
         } catch (TransactionCommitFailedException e) {
-            // FIXME revert should probably occur when context is not written successfully, but can that even happen ?
+            // FIXME revert should probably occur when context is not written successfully
             final String msg = "Error while updating mapping context data";
             LOG.error(msg, e);
             return Futures.immediateFailedCheckedFuture(
index ac4ba90..c8f9bd3 100644 (file)
@@ -30,6 +30,7 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import io.fd.honeycomb.v3po.data.DataModification;
 import io.fd.honeycomb.v3po.translate.TranslationException;
+import java.util.function.Consumer;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.NotThreadSafe;
@@ -74,7 +75,7 @@ final class WriteTransaction implements DOMDataWriteTransaction {
     }
 
     private void handleOperation(final LogicalDatastoreType store,
-                                 final java.util.function.Consumer<DataModification> r) {
+                                 final Consumer<DataModification> r) {
         switch (store) {
             case CONFIGURATION:
                 checkArgument(configModification != null, "Modification of %s is not supported", store);
index 956aa90..99e5b39 100644 (file)
@@ -31,11 +31,10 @@ public class InMemoryDataTreeModule extends org.opendaylight.yang.gen.v1.urn.hon
         return new CloseableConfigDataTree(getSchemaServiceDependency().getGlobalContext(), getType());
     }
 
-    private static class CloseableConfigDataTree implements AutoCloseable,
-        org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree {
-        private final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree dataTree;
+    private static class CloseableConfigDataTree implements AutoCloseable, DataTree {
+        private final DataTree dataTree;
 
-        public CloseableConfigDataTree(final SchemaContext schemaContext, final DatatreeType type) {
+        CloseableConfigDataTree(final SchemaContext schemaContext, final DatatreeType type) {
             this.dataTree = InMemoryDataTreeFactory.getInstance().create(
                 type == DatatreeType.Config ? TreeType.CONFIGURATION : TreeType.OPERATIONAL
             );
index ebca239..0ea76cf 100644 (file)
@@ -39,6 +39,7 @@ module data-impl {
 
             leaf type {
                 type dapi:datatree-type;
+                mandatory true;
             }
         }
     }
@@ -73,6 +74,8 @@ module data-impl {
 
             leaf persist-file-path {
                 type string;
+                mandatory true;
+                description "Path to a file to be used as data storage";
             }
         }
     }
index be6ffa2..d9798d0 100644 (file)
@@ -41,7 +41,7 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContaine
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
-public class JsonUtils {
+public final class JsonUtils {
 
     private JsonUtils() {}
 
@@ -80,7 +80,7 @@ public class JsonUtils {
         final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(builder);
 
         final JsonParserStream jsonParser = JsonParserStream.create(writer, schemaContext);
-        final JsonReader reader = new JsonReader(new InputStreamReader(stream));
+        final JsonReader reader = new JsonReader(new InputStreamReader(stream, Charsets.UTF_8));
         jsonParser.parse(reader);
 
         return builder.build();
index 20bbf19..47498f5 100644 (file)
@@ -43,7 +43,7 @@ public final class TransactionWriteContext implements WriteContext {
     private final DOMDataReadOnlyTransaction afterTx;
     private final ModificationCache ctx;
     private final BindingNormalizedNodeSerializer serializer;
-    private MappingContext mappingContext;
+    private final MappingContext mappingContext;
 
     public TransactionWriteContext(final BindingNormalizedNodeSerializer serializer,
                                    final DOMDataReadOnlyTransaction beforeTx,
index faaf063..bd10e5f 100644 (file)
@@ -74,7 +74,7 @@ public class InterfacesInitializer extends AbstractDataTreeConverter<InterfacesS
         return interfacesBuilder.build();
     }
 
-    // FIXME this kind of initialization/transformation is bad
+    // FIXME https://jira.fd.io/browse/HONEYCOMB-73 this kind of initialization/transformation is bad
     // There is no relation to readers, it cannot be extended (readers can) and its hard to keep in sync with readers
 
     // TODO add IP v4/ v6 initializer
index eed2231..ff3dc10 100644 (file)
@@ -33,9 +33,13 @@ import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.in
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class RestoringInitializer implements DataTreeInitializer {
 
+    private static final Logger LOG = LoggerFactory.getLogger(InitializerRegistryImpl.class);
+
     private final SchemaService schemaService;
     private final Path path;
     private final DOMDataBroker dataTree;
@@ -55,15 +59,9 @@ public class RestoringInitializer implements DataTreeInitializer {
     }
 
     private Path checkStorage(final Path path) {
-        try {
-            if(Files.exists(path)) {
-                checkArgument(!Files.isDirectory(path), "File %s is a directory", path);
-                checkArgument(Files.isReadable(path), "File %s is not readable", path);
-            } else {
-                return checkStorage(Files.createFile(path));
-            }
-        } catch (IOException e) {
-            throw new IllegalArgumentException("Cannot use " + path + " for restoring data", e);
+        if (Files.exists(path)) {
+            checkArgument(!Files.isDirectory(path), "File %s is a directory", path);
+            checkArgument(Files.isReadable(path), "File %s is not readable", path);
         }
 
         return path;
@@ -71,7 +69,9 @@ public class RestoringInitializer implements DataTreeInitializer {
 
     @Override
     public void initialize() throws InitializeException {
+        LOG.debug("Starting restoration of {} from {} using {}", dataTree, path, restorationType);
         if(!Files.exists(path)) {
+            LOG.debug("Persist file {} does not exist. Skipping restoration", path);
             return;
         }
 
@@ -83,6 +83,8 @@ public class RestoringInitializer implements DataTreeInitializer {
             for (DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild : containerNode
                 .getValue()) {
                 final YangInstanceIdentifier iid = YangInstanceIdentifier.create(dataContainerChild.getIdentifier());
+                LOG.trace("Restoring {} from {}", iid, path);
+
                 switch (restorationType) {
                     case Merge:
                         domDataWriteTransaction.merge(datastoreType, iid, dataContainerChild);
@@ -98,6 +100,7 @@ public class RestoringInitializer implements DataTreeInitializer {
 
             // Block here to prevent subsequent initializers processing before context is fully restored
             domDataWriteTransaction.submit().checkedGet();
+            LOG.debug("Data from {} restored successfully", path);
 
         } catch (IOException | TransactionCommitFailedException e) {
             throw new InitializeException("Unable to restore data from " + path, e);
index 8d30568..52750d9 100644 (file)
@@ -98,6 +98,7 @@ module vpp-cfg-init {
 
             leaf persist-file-path {
                 type string;
+                mandatory true;
             }
 
             leaf restoration-type {
@@ -108,6 +109,7 @@ module vpp-cfg-init {
 
             leaf datastore-type {
                 type dapi:datatree-type;
+                mandatory true;
             }
         }
     }
index d32f272..34c8104 100644 (file)
@@ -36,14 +36,15 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Utility adapter on top of {@link MappingContext}
+ * Utility adapter on top of {@link MappingContext} storing integer to string mappings according to naming-context yang
+ * model
  */
 public final class NamingContext implements AutoCloseable {
 
     private static final Logger LOG = LoggerFactory.getLogger(NamingContext.class);
 
     private final String artificialNamePrefix;
-    private KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext, NamingContextKey>
+    private final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext, NamingContextKey>
         namingContextIid;
 
     /**
@@ -58,15 +59,30 @@ public final class NamingContext implements AutoCloseable {
             return list.get(0);
         });
 
-    public NamingContext(final String artificialNamePrefix, final String instanceName) {
+    /**
+     * Create new naming context
+     *
+     * @param artificialNamePrefix artificial name to be used for items without a name in VPP (or not provided)
+     * @param instanceName name of this context instance. Will be used as list item identifier within context data tree
+     */
+    public NamingContext(@Nonnull final String artificialNamePrefix, @Nonnull final String instanceName) {
         this.artificialNamePrefix = artificialNamePrefix;
         namingContextIid = InstanceIdentifier.create(Contexts.class).child(
             org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext.class,
             new NamingContextKey(instanceName));
     }
 
+    /**
+     * Retrieve name for mapping stored provided mappingContext instance. If not present, artificial name will be
+     * generated.
+     *
+     * @param index index of a mapped item
+     * @param mappingContext mapping context providing context data for current transaction
+     *
+     * @return name mapped to provided index
+     */
     @Nonnull
-    public synchronized String getName(final int index, final MappingContext mappingContext) {
+    public synchronized String getName(final int index, @Nonnull final MappingContext mappingContext) {
         if (!containsName(index, mappingContext)) {
             final String artificialName = getArtificialName(index);
             LOG.info("Assigning artificial name: {} for index: {}", artificialName, index);
@@ -81,13 +97,29 @@ public final class NamingContext implements AutoCloseable {
             .collect(SINGLE_ITEM_COLLECTOR).getName();
     }
 
-    public synchronized boolean containsName(final int index, final MappingContext mappingContext) {
+    /**
+     * Check whether mapping is present for index.
+     *
+     * @param index index of a mapped item
+     * @param mappingContext mapping context providing context data for current transaction
+     *
+     * @return true if present, false otherwise
+     */
+    public synchronized boolean containsName(final int index, @Nonnull final MappingContext mappingContext) {
         final Optional<Mappings> read = mappingContext.read(namingContextIid.child(Mappings.class));
         return read.isPresent()
             ? read.get().getMapping().stream().anyMatch(mapping -> mapping.getIndex().equals(index))
             : false;
     }
 
+
+    /**
+     * Add mapping to current context
+     *
+     * @param index index of a mapped item
+     * @param name name of a mapped item
+     * @param mappingContext mapping context providing context data for current transaction
+     */
     public synchronized void addName(final int index, final String name, final MappingContext mappingContext) {
         final KeyedInstanceIdentifier<Mapping, MappingKey> mappingIid = getMappingIid(name);
         mappingContext.put(mappingIid, new MappingBuilder().setIndex(index).setName(name).build());
@@ -97,6 +129,12 @@ public final class NamingContext implements AutoCloseable {
         return namingContextIid.child(Mappings.class).child(Mapping.class, new MappingKey(name));
     }
 
+    /**
+     * Remove mapping from current context
+     *
+     * @param name name of a mapped item
+     * @param mappingContext mapping context providing context data for current transaction
+     */
     public synchronized void removeName(final String name, final MappingContext mappingContext) {
         mappingContext.delete(getMappingIid(name));
     }
@@ -105,6 +143,8 @@ public final class NamingContext implements AutoCloseable {
      * Returns index value associated with the given name.
      *
      * @param name the name whose associated index value is to be returned
+     * @param mappingContext mapping context providing context data for current transaction
+     *
      * @return integer index value matching supplied name
      * @throws IllegalArgumentException if name was not found
      */
@@ -115,6 +155,14 @@ public final class NamingContext implements AutoCloseable {
 
     }
 
+    /**
+     * Check whether mapping is present for name.
+     *
+     * @param name name of a mapped item
+     * @param mappingContext mapping context providing context data for current transaction
+     *
+     * @return true if present, false otherwise
+     */
     public synchronized boolean containsIndex(final String name, final MappingContext mappingContext) {
         return mappingContext.read(getMappingIid(name)).isPresent();
     }