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";
list mapping {
key "name";
+ unique "index";
leaf name {
type string;
// 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;
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);
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;
* 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 {
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);
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
}
@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);
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(
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;
}
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);
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
);
leaf type {
type dapi:datatree-type;
+ mandatory true;
}
}
}
leaf persist-file-path {
type string;
+ mandatory true;
+ description "Path to a file to be used as data storage";
}
}
}
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() {}
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();
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,
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
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;
}
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;
@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;
}
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);
// 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);
leaf persist-file-path {
type string;
+ mandatory true;
}
leaf restoration-type {
leaf datastore-type {
type dapi:datatree-type;
+ mandatory true;
}
}
}
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;
/**
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);
.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());
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));
}
* 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
*/
}
+ /**
+ * 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();
}