import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.Future;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang3.StringUtils;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.sal.core.api.mount.MountInstance;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
import org.opendaylight.controller.sal.rest.api.Draft02;
import org.opendaylight.controller.sal.rest.api.RestconfService;
import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
import org.opendaylight.yangtools.concepts.Codec;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer.CnSnFromNormalizedNodeSerializerFactory;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.model.util.EmptyType;
import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class RestconfImpl implements RestconfService {
private enum UriParameters {
private String uriParameterName;
- UriParameters(String uriParameterName) {
+ UriParameters(final String uriParameterName) {
this.uriParameterName = uriParameterName;
}
private final static RestconfImpl INSTANCE = new RestconfImpl();
+ private static final int NOTIFICATION_PORT = 8181;
+
private static final int CHAR_NOT_FOUND = -1;
private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
private ControllerContext controllerContext;
+ private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class);
+
+ private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
+
+ private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION;
+
+ private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
+
+ private static final Date EVENT_SUBSCRIPTION_AUGMENT_REVISION;
+
+ private static final String DATASTORE_PARAM_NAME = "datastore";
+
+ private static final String SCOPE_PARAM_NAME = "scope";
+
+ static {
+ try {
+ EVENT_SUBSCRIPTION_AUGMENT_REVISION = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
+ } catch (ParseException e) {
+ throw new RestconfDocumentedException(
+ "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION,
+ ErrorTag.OPERATION_FAILED);
+ }
+ }
+
public void setBroker(final BrokerFacade broker) {
this.broker = broker;
}
@Override
public StructuredData getModules(final String identifier, final UriInfo uriInfo) {
Set<Module> modules = null;
- MountInstance mountPoint = null;
+ DOMMountPoint mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
public StructuredData getModule(final String identifier, final UriInfo uriInfo) {
final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier);
Module module = null;
- MountInstance mountPoint = null;
+ DOMMountPoint mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
@Override
public StructuredData getOperations(final String identifier, final UriInfo uriInfo) {
Set<Module> modules = null;
- MountInstance mountPoint = null;
+ DOMMountPoint mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
}
private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
- final MountInstance mountPoint, boolean prettyPrint) {
+ final DOMMountPoint mountPoint, final boolean prettyPrint) {
final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
Module restconfModule = this.getRestconfModule();
final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
return callRpc(rpc, payload, parsePrettyPrintParameter(uriInfo));
}
- private void validateInput(final DataSchemaNode inputSchema, final CompositeNode payload) {
+ private void validateInput(final DataSchemaNode inputSchema, final Node<?> payload) {
if (inputSchema != null && payload == null) {
// expected a non null payload
throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
// else
// {
// TODO: Validate "mandatory" and "config" values here??? Or should those be
+ // those be
// validate in a more central location inside MD-SAL core.
// }
}
rpc.getQName(), "path"));
final Object pathValue = pathNode == null ? null : pathNode.getValue();
- if (!(pathValue instanceof InstanceIdentifier)) {
+ if (!(pathValue instanceof YangInstanceIdentifier)) {
throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
}
- final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
+ final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
String streamName = null;
if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
- streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
+
+ LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
+ DATASTORE_PARAM_NAME);
+ datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
+
+ DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
+ scope = scope == null ? DEFAULT_SCOPE : scope;
+
+ streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
+ + "/scope=" + scope);
}
if (Strings.isNullOrEmpty(streamName)) {
final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(rpc.getOutput().getQName(),
null, output, null, null);
- if (!Notificator.existListenerFor(pathIdentifier)) {
+ if (!Notificator.existListenerFor(streamName)) {
Notificator.createListener(pathIdentifier, streamName);
}
private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
String identifierEncoded = null;
- MountInstance mountPoint = null;
+ DOMMountPoint mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
// mounted RPC call - look up mount instance.
InstanceIdWithSchemaNode mountPointId = controllerContext.toMountPointIdentifier(identifier);
}
final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
- RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded);
+
+ RpcDefinition rpc = null;
+ if (mountPoint == null) {
+ rpc = controllerContext.getRpcDefinition(identifierDecoded);
+ } else {
+ rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
+ }
if (rpc == null) {
throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
}
- private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, boolean prettyPrint) {
+ private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
+ final String[] splittedIdentifier = identifierDecoded.split(":");
+ if (splittedIdentifier.length != 2) {
+ throw new RestconfDocumentedException(identifierDecoded
+ + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
+ ErrorTag.INVALID_VALUE);
+ }
+ for (Module module : schemaContext.getModules()) {
+ if (module.getName().equals(splittedIdentifier[0])) {
+ for (RpcDefinition rpcDefinition : module.getRpcs()) {
+ if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
+ return rpcDefinition;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, final boolean prettyPrint) {
if (rpcExecutor == null) {
throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
}
@Override
public StructuredData readConfigurationData(final String identifier, final UriInfo uriInfo) {
- final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
- CompositeNode data = null;
- MountInstance mountPoint = iiWithData.getMountPoint();
+ final InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier(identifier);
+ DOMMountPoint mountPoint = iiWithData.getMountPoint();
+ NormalizedNode<?, ?> data = null;
+ YangInstanceIdentifier normalizedII;
if (mountPoint != null) {
- data = broker.readConfigurationDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
+ normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier());
+ data = broker.readConfigurationData(mountPoint, normalizedII);
} else {
- data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
+ normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
+ data = broker.readConfigurationData(normalizedII);
}
- data = pruneDataAtDepth(data, parseDepthParameter(uriInfo));
- boolean prettyPrintMode = parsePrettyPrintParameter(uriInfo);
- return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint(), prettyPrintMode);
+ final CompositeNode compositeNode = datastoreNormalizedNodeToCompositeNode(data, iiWithData.getSchemaNode());
+ final CompositeNode prunedCompositeNode = pruneDataAtDepth(compositeNode, parseDepthParameter(uriInfo));
+
+ final boolean prettyPrintMode = parsePrettyPrintParameter(uriInfo);
+ return new StructuredData(prunedCompositeNode, iiWithData.getSchemaNode(), mountPoint, prettyPrintMode);
}
@SuppressWarnings("unchecked")
@Override
public StructuredData readOperationalData(final String identifier, final UriInfo info) {
- final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
- CompositeNode data = null;
- MountInstance mountPoint = iiWithData.getMountPoint();
+ final InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier(identifier);
+ DOMMountPoint mountPoint = iiWithData.getMountPoint();
+ NormalizedNode<?, ?> data = null;
+ YangInstanceIdentifier normalizedII;
if (mountPoint != null) {
- data = broker.readOperationalDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
+ normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier());
+ data = broker.readOperationalData(mountPoint, normalizedII);
} else {
- data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
+ normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
+ data = broker.readOperationalData(normalizedII);
}
- data = pruneDataAtDepth(data, parseDepthParameter(info));
- boolean prettyPrintMode = parsePrettyPrintParameter(info);
- return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint, prettyPrintMode);
+ final CompositeNode compositeNode = datastoreNormalizedNodeToCompositeNode(data, iiWithData.getSchemaNode());
+ final CompositeNode prunedCompositeNode = pruneDataAtDepth(compositeNode, parseDepthParameter(info));
+
+ final boolean prettyPrintMode = parsePrettyPrintParameter(info);
+ return new StructuredData(prunedCompositeNode, iiWithData.getSchemaNode(), mountPoint, prettyPrintMode);
}
- private boolean parsePrettyPrintParameter(UriInfo info) {
+ private boolean parsePrettyPrintParameter(final UriInfo info) {
String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
return Boolean.parseBoolean(param);
}
@Override
- public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
+ public Response updateConfigurationData(final String identifier, final Node<?> payload) {
final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
validateInput(iiWithData.getSchemaNode(), payload);
- MountInstance mountPoint = iiWithData.getMountPoint();
+ DOMMountPoint mountPoint = iiWithData.getMountPoint();
final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
- validateListKeysEqualityInPayloadAndUri(iiWithData, payload);
- RpcResult<TransactionStatus> status = null;
+ validateListKeysEqualityInPayloadAndUri(iiWithData, value);
+ final NormalizedNode<?, ?> datastoreNormalizedNode = compositeNodeToDatastoreNormalizedNode(value,
+ iiWithData.getSchemaNode());
+
+ YangInstanceIdentifier normalizedII;
try {
if (mountPoint != null) {
- status = broker.commitConfigurationDataPutBehindMountPoint(mountPoint,
- iiWithData.getInstanceIdentifier(), value).get();
+ normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier());
+ broker.commitConfigurationDataPut(mountPoint, normalizedII, datastoreNormalizedNode).get();
} else {
- status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get();
+ normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
+ broker.commitConfigurationDataPut(normalizedII, datastoreNormalizedNode).get();
}
} catch (Exception e) {
throw new RestconfDocumentedException("Error updating data", e);
}
- if (status.getResult() == TransactionStatus.COMMITED) {
- return Response.status(Status.OK).build();
- }
-
- return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ return Response.status(Status.OK).build();
}
/**
}
@Override
- public Response createConfigurationData(final String identifier, final CompositeNode payload) {
+ public Response createConfigurationData(final String identifier, final Node<?> payload) {
if (payload == null) {
throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
final InstanceIdWithSchemaNode incompleteInstIdWithData = this.controllerContext
.toInstanceIdentifier(identifier);
final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode();
- MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
- final Module module = this.findModule(mountPoint, payload);
+ DOMMountPoint mountPoint = incompleteInstIdWithData.getMountPoint();
+ final Module module = findModule(mountPoint, payload);
if (module == null) {
throw new RestconfDocumentedException("Module was not found for \"" + payloadNS + "\"",
ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
parentSchema, payloadName, module.getNamespace());
value = this.normalizeNode(payload, schemaNode, mountPoint);
- iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode);
+ iiWithData = addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode);
}
- RpcResult<TransactionStatus> status = null;
- MountInstance mountPoint = iiWithData.getMountPoint();
+ final NormalizedNode<?, ?> datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value,
+ iiWithData.getSchemaNode());
+ DOMMountPoint mountPoint = iiWithData.getMountPoint();
+ YangInstanceIdentifier normalizedII;
+
try {
if (mountPoint != null) {
- Future<RpcResult<TransactionStatus>> future = broker.commitConfigurationDataPostBehindMountPoint(
- mountPoint, iiWithData.getInstanceIdentifier(), value);
- status = future == null ? null : future.get();
+ normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier());
+ broker.commitConfigurationDataPost(mountPoint, normalizedII, datastoreNormalizedData);
} else {
- Future<RpcResult<TransactionStatus>> future = broker.commitConfigurationDataPost(
- iiWithData.getInstanceIdentifier(), value);
- status = future == null ? null : future.get();
+ normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
+ broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData);
}
} catch (Exception e) {
throw new RestconfDocumentedException("Error creating data", e);
}
- if (status == null) {
- return Response.status(Status.ACCEPTED).build();
- }
-
- if (status.getResult() == TransactionStatus.COMMITED) {
- return Response.status(Status.NO_CONTENT).build();
- }
-
- return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ return Response.status(Status.NO_CONTENT).build();
}
@Override
- public Response createConfigurationData(final CompositeNode payload) {
+ public Response createConfigurationData(final Node<?> payload) {
if (payload == null) {
throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
payloadName, module.getNamespace());
final CompositeNode value = this.normalizeNode(payload, schemaNode, null);
final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode);
- RpcResult<TransactionStatus> status = null;
- MountInstance mountPoint = iiWithData.getMountPoint();
+ final NormalizedNode<?, ?> datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, schemaNode);
+ DOMMountPoint mountPoint = iiWithData.getMountPoint();
+ YangInstanceIdentifier normalizedII;
try {
if (mountPoint != null) {
- Future<RpcResult<TransactionStatus>> future = broker.commitConfigurationDataPostBehindMountPoint(
- mountPoint, iiWithData.getInstanceIdentifier(), value);
- status = future == null ? null : future.get();
+ normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier());
+ broker.commitConfigurationDataPost(mountPoint, normalizedII, datastoreNormalizedData);
+
} else {
- Future<RpcResult<TransactionStatus>> future = broker.commitConfigurationDataPost(
- iiWithData.getInstanceIdentifier(), value);
- status = future == null ? null : future.get();
+ normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
+ broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData);
}
} catch (Exception e) {
throw new RestconfDocumentedException("Error creating data", e);
}
- if (status == null) {
- return Response.status(Status.ACCEPTED).build();
- }
-
- if (status.getResult() == TransactionStatus.COMMITED) {
- return Response.status(Status.NO_CONTENT).build();
- }
-
- return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ return Response.status(Status.NO_CONTENT).build();
}
@Override
public Response deleteConfigurationData(final String identifier) {
- final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
- RpcResult<TransactionStatus> status = null;
- MountInstance mountPoint = iiWithData.getMountPoint();
+ final InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier(identifier);
+ DOMMountPoint mountPoint = iiWithData.getMountPoint();
+ YangInstanceIdentifier normalizedII;
try {
if (mountPoint != null) {
- status = broker.commitConfigurationDataDeleteBehindMountPoint(mountPoint,
- iiWithData.getInstanceIdentifier()).get();
+ normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier());
+ broker.commitConfigurationDataDelete(mountPoint, normalizedII);
} else {
- status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get();
+ normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
+ broker.commitConfigurationDataDelete(normalizedII).get();
}
} catch (Exception e) {
throw new RestconfDocumentedException("Error creating data", e);
}
- if (status.getResult() == TransactionStatus.COMMITED) {
- return Response.status(Status.OK).build();
- }
-
- return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ return Response.status(Status.OK).build();
}
+ /**
+ * Subscribes to some path in schema context (stream) to listen on changes on this stream.
+ *
+ * Additional parameters for subscribing to stream are loaded via rpc input parameters:
+ * <ul>
+ * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
+ * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
+ * </ul>
+ */
@Override
public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
final String streamName = Notificator.createStreamNameFromUri(identifier);
throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
}
- broker.registerToListenDataChanges(listener);
+ Map<String, String> paramToValues = resolveValuesFromUri(identifier);
+ LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
+ paramToValues.get(DATASTORE_PARAM_NAME));
+ if (datastore == null) {
+ throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
+ ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+ }
+ DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
+ if (scope == null) {
+ throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
+ ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+ }
+
+ broker.registerToListenDataChanges(datastore, scope, listener);
final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
- UriBuilder port = uriBuilder.port(WebSocketServer.getInstance().getPort());
+ int notificationPort = NOTIFICATION_PORT;
+ try {
+ WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
+ notificationPort = webSocketServerInstance.getPort();
+ } catch (NullPointerException e) {
+ WebSocketServer.createInstance(NOTIFICATION_PORT);
+ }
+ UriBuilder port = uriBuilder.port(notificationPort);
final URI uriToWebsocketServer = port.replacePath(streamName).build();
return Response.status(Status.OK).location(uriToWebsocketServer).build();
}
- private Module findModule(final MountInstance mountPoint, final CompositeNode data) {
- if (data instanceof CompositeNodeWrapper) {
- return findModule(mountPoint, (CompositeNodeWrapper) data);
+ /**
+ * Load parameter for subscribing to stream from input composite node
+ *
+ * @param compNode
+ * contains value
+ * @return enum object if its string value is equal to {@code paramName}. In other cases null.
+ */
+ private <T> T parseEnumTypeParameter(final CompositeNode compNode, final Class<T> classDescriptor,
+ final String paramName) {
+ QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
+ EVENT_SUBSCRIPTION_AUGMENT_REVISION);
+ SimpleNode<?> simpleNode = compNode.getFirstSimpleByName(QName.create(salRemoteAugment, paramName));
+ if (simpleNode == null) {
+ return null;
+ }
+ Object rawValue = simpleNode.getValue();
+ if (!(rawValue instanceof String)) {
+ return null;
+ }
+
+ return resolveAsEnum(classDescriptor, (String) rawValue);
+ }
+
+ /**
+ * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
+ *
+ * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
+ * null.
+ */
+ private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
+ if (Strings.isNullOrEmpty(value)) {
+ return null;
+ }
+ return resolveAsEnum(classDescriptor, value);
+ }
+
+ private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
+ T[] enumConstants = classDescriptor.getEnumConstants();
+ if (enumConstants != null) {
+ for (T enm : classDescriptor.getEnumConstants()) {
+ if (((Enum<?>) enm).name().equals(value)) {
+ return enm;
+ }
+ }
+ }
+ return null;
+ }
+
+ private Map<String, String> resolveValuesFromUri(final String uri) {
+ Map<String, String> result = new HashMap<>();
+ String[] tokens = uri.split("/");
+ for (int i = 1; i < tokens.length; i++) {
+ String[] parameterTokens = tokens[i].split("=");
+ if (parameterTokens.length == 2) {
+ result.put(parameterTokens[0], parameterTokens[1]);
+ }
+ }
+ return result;
+ }
+
+ private Module findModule(final DOMMountPoint mountPoint, final Node<?> data) {
+ if (data instanceof NodeWrapper) {
+ return findModule(mountPoint, (NodeWrapper<?>) data);
} else if (data != null) {
URI namespace = data.getNodeType().getNamespace();
if (mountPoint != null) {
}
}
- private Module findModule(final MountInstance mountPoint, final CompositeNodeWrapper data) {
+ private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper<?> data) {
URI namespace = data.getNamespace();
Preconditions.<URI> checkNotNull(namespace);
private InstanceIdWithSchemaNode addLastIdentifierFromData(final InstanceIdWithSchemaNode identifierWithSchemaNode,
final CompositeNode data, final DataSchemaNode schemaOfData) {
- InstanceIdentifier instanceIdentifier = null;
+ YangInstanceIdentifier instanceIdentifier = null;
if (identifierWithSchemaNode != null) {
instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
}
- final InstanceIdentifier iiOriginal = instanceIdentifier;
+ final YangInstanceIdentifier iiOriginal = instanceIdentifier;
InstanceIdentifierBuilder iiBuilder = null;
if (iiOriginal == null) {
- iiBuilder = InstanceIdentifier.builder();
+ iiBuilder = YangInstanceIdentifier.builder();
} else {
- iiBuilder = InstanceIdentifier.builder(iiOriginal);
+ iiBuilder = YangInstanceIdentifier.builder(iiOriginal);
}
- if ((schemaOfData instanceof ListSchemaNode)) {
- HashMap<QName, Object> keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
- iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
- } else {
- iiBuilder.node(schemaOfData.getQName());
- }
+ iiBuilder.node(schemaOfData.getQName());
- InstanceIdentifier instance = iiBuilder.toInstance();
- MountInstance mountPoint = null;
+ YangInstanceIdentifier instance = iiBuilder.toInstance();
+ DOMMountPoint mountPoint = null;
if (identifierWithSchemaNode != null) {
mountPoint = identifierWithSchemaNode.getMountPoint();
}
return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint);
}
- private HashMap<QName, Object> resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) {
- final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
- List<QName> _keyDefinition = listNode.getKeyDefinition();
- for (final QName key : _keyDefinition) {
- SimpleNode<? extends Object> head = null;
- String localName = key.getLocalName();
- List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
- if (simpleNodesByName != null) {
- head = Iterables.getFirst(simpleNodesByName, null);
- }
-
- Object dataNodeKeyValueObject = null;
- if (head != null) {
- dataNodeKeyValueObject = head.getValue();
- }
-
- if (dataNodeKeyValueObject == null) {
- throw new RestconfDocumentedException("Data contains list \"" + dataNode.getNodeType().getLocalName()
- + "\" which does not contain key: \"" + key.getLocalName() + "\"", ErrorType.PROTOCOL,
- ErrorTag.INVALID_VALUE);
- }
-
- keyValues.put(key, dataNodeKeyValueObject);
- }
-
- return keyValues;
- }
-
private boolean endsWithMountPoint(final String identifier) {
return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/");
}
- private boolean representsMountPointRootData(final CompositeNode data) {
+ private boolean representsMountPointRootData(final Node<?> data) {
URI namespace = this.namespace(data);
return (SchemaContext.NAME.getNamespace().equals(namespace) /*
* || MOUNT_POINT_MODULE_NAME .equals( namespace .
return identifier + "/" + ControllerContext.MOUNT;
}
- private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema,
- final MountInstance mountPoint) {
+ private CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
if (schema == null) {
QName nodeType = node == null ? null : node.getNodeType();
String localName = nodeType == null ? null : nodeType.getLocalName();
ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
}
- if ((node instanceof CompositeNodeWrapper)) {
- boolean isChangeAllowed = ((CompositeNodeWrapper) node).isChangeAllowed();
+ if ((node instanceof NodeWrapper<?>)) {
+ NodeWrapper<?> nodeWrap = (NodeWrapper<?>) node;
+ boolean isChangeAllowed = ((NodeWrapper<?>) node).isChangeAllowed();
if (isChangeAllowed) {
+ nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper<?>) node, schema);
try {
- this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint);
+ this.normalizeNode(nodeWrap, schema, null, mountPoint);
} catch (IllegalArgumentException e) {
throw new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
}
+ if (nodeWrap instanceof CompositeNodeWrapper) {
+ return ((CompositeNodeWrapper) nodeWrap).unwrap();
+ }
}
+ }
- return ((CompositeNodeWrapper) node).unwrap();
+ if (node instanceof CompositeNode) {
+ return (CompositeNode) node;
}
- return node;
+ throw new RestconfDocumentedException("Top level element is not interpreted as composite node.",
+ ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
}
private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
- final QName previousAugment, final MountInstance mountPoint) {
+ final QName previousAugment, final DOMMountPoint mountPoint) {
if (schema == null) {
throw new RestconfDocumentedException("Data has bad format.\n\"" + nodeBuilder.getLocalName()
+ "\" does not exist in yang schema.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
}
private void normalizeSimpleNode(final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
- final MountInstance mountPoint) {
+ final DOMMountPoint mountPoint) {
final Object value = simpleNode.getValue();
Object inputValue = value;
TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
}
private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder,
- final DataNodeContainer schema, final MountInstance mountPoint, final QName currentAugment) {
+ final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) {
final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
checkNodeMultiplicityAccordingToSchema(schema, children);
for (final NodeWrapper<? extends Object> child : children) {
}
private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
- final QName previousAugment, final MountInstance mountPoint) {
+ final QName previousAugment, final DOMMountPoint mountPoint) {
QName validQName = schema.getQName();
QName currentAugment = previousAugment;
if (schema.isAugmenting()) {
return currentAugment;
}
- private URI namespace(final CompositeNode data) {
- if (data instanceof CompositeNodeWrapper) {
- return ((CompositeNodeWrapper) data).getNamespace();
+ private URI namespace(final Node<?> data) {
+ if (data instanceof NodeWrapper) {
+ return ((NodeWrapper<?>) data).getNamespace();
} else if (data != null) {
return data.getNodeType().getNamespace();
} else {
}
}
- private String localName(final CompositeNode data) {
- if (data instanceof CompositeNodeWrapper) {
- return ((CompositeNodeWrapper) data).getLocalName();
+ private String localName(final Node<?> data) {
+ if (data instanceof NodeWrapper) {
+ return ((NodeWrapper<?>) data).getLocalName();
} else if (data != null) {
return data.getNodeType().getLocalName();
} else {
}
}
- private String getName(final CompositeNode data) {
- if (data instanceof CompositeNodeWrapper) {
- return ((CompositeNodeWrapper) data).getLocalName();
+ private String getName(final Node<?> data) {
+ if (data instanceof NodeWrapper) {
+ return ((NodeWrapper<?>) data).getLocalName();
} else if (data != null) {
return data.getNodeType().getLocalName();
} else {
throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
}
}
+
+ private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode<?, ?> dataNode, final DataSchemaNode schema) {
+ Iterable<Node<?>> nodes = null;
+ if (dataNode == null) {
+ throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING,
+ "No data was found."));
+ }
+ if (schema instanceof ContainerSchemaNode && dataNode instanceof ContainerNode) {
+ nodes = CnSnFromNormalizedNodeSerializerFactory.getInstance().getContainerNodeSerializer()
+ .serialize((ContainerSchemaNode) schema, (ContainerNode) dataNode);
+ } else if (schema instanceof ListSchemaNode && dataNode instanceof MapNode) {
+ nodes = CnSnFromNormalizedNodeSerializerFactory.getInstance().getMapNodeSerializer()
+ .serialize((ListSchemaNode) schema, (MapNode) dataNode);
+ } else if (schema instanceof ListSchemaNode && dataNode instanceof MapEntryNode) {
+ nodes = CnSnFromNormalizedNodeSerializerFactory.getInstance().getMapEntryNodeSerializer()
+ .serialize((ListSchemaNode) schema, (MapEntryNode) dataNode);
+ }
+ if (nodes != null) {
+ if (nodes.iterator().hasNext()) {
+ Node<?> nodeOldStruct = nodes.iterator().next();
+ return (CompositeNode) nodeOldStruct;
+ } else {
+ LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode.");
+ }
+ } else {
+ LOG.error("Top level node isn't of type Container or List schema node but "
+ + schema.getClass().getSimpleName());
+ }
+
+ throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+ "It wasn't possible to correctly interpret data."));
+ }
+
+ private NormalizedNode<?, ?> compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode, final DataSchemaNode schema) {
+ List<Node<?>> lst = new ArrayList<Node<?>>();
+ lst.add(compNode);
+ if (schema instanceof ContainerSchemaNode) {
+ return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
+ .parse(lst, (ContainerSchemaNode) schema);
+ } else if (schema instanceof ListSchemaNode) {
+ return CnSnToNormalizedNodeParserFactory.getInstance().getMapEntryNodeParser()
+ .parse(lst, (ListSchemaNode) schema);
+ }
+
+ LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName());
+
+ throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+ "It wasn't possible to translate specified data to datastore readable form."));
+ }
+
+ private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode(final InstanceIdWithSchemaNode iiWithSchemaNode) {
+ return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false);
+ }
+
+ private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode(
+ final InstanceIdWithSchemaNode iiWithSchemaNode, final boolean unwrapLastListNode) {
+ return new InstanceIdWithSchemaNode(instanceIdentifierToReadableFormForNormalizeNode(
+ iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(),
+ iiWithSchemaNode.getMountPoint());
+ }
+
+ private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(final YangInstanceIdentifier instIdentifier,
+ final boolean unwrapLastListNode) {
+ Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null");
+ final List<PathArgument> result = new ArrayList<PathArgument>();
+ final Iterator<PathArgument> iter = instIdentifier.getPathArguments().iterator();
+ while (iter.hasNext()) {
+ final PathArgument pathArgument = iter.next();
+ if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) {
+ result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType()));
+ }
+ result.add(pathArgument);
+ }
+ return YangInstanceIdentifier.create(result);
+ }
+
+ private CompositeNodeWrapper topLevelElementAsCompositeNodeWrapper(final NodeWrapper<?> node,
+ final DataSchemaNode schemaNode) {
+ if (node instanceof CompositeNodeWrapper) {
+ return (CompositeNodeWrapper) node;
+ } else if (node instanceof SimpleNodeWrapper && isDataContainerNode(schemaNode)) {
+ final SimpleNodeWrapper simpleNodeWrapper = (SimpleNodeWrapper) node;
+ return new CompositeNodeWrapper(namespace(simpleNodeWrapper), localName(simpleNodeWrapper));
+ }
+
+ throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+ "Top level element has to be composite node or has to represent data container node."));
+ }
+
+ private boolean isDataContainerNode(final DataSchemaNode schemaNode) {
+ if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) {
+ return true;
+ }
+ return false;
+ }
}