Jonathan Hart
Committed by Gerrit Code Review

Allow list-type config to be POSTed to subjectkey/subject/configkey endpoint.

Also add validation that the given JSON node is appropriate for the config
type (list vs object).

Change-Id: Ib1c12b538860a6f18b8311c5f5a786608c04beb8
......@@ -61,7 +61,11 @@ import java.util.Objects;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onosproject.net.config.NetworkConfigEvent.Type.*;
import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REGISTERED;
import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REMOVED;
import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UNREGISTERED;
import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
/**
* Implementation of a distributed network configuration store.
......@@ -77,6 +81,10 @@ public class DistributedNetworkConfigStore
private static final int MAX_BACKOFF = 10;
private static final String INVALID_CONFIG_JSON =
"JSON node does not contain valid configuration";
private static final String INVALID_JSON_LIST =
"JSON node is not a list for list type config";
private static final String INVALID_JSON_OBJECT =
"JSON node is not an object for object type config";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
......@@ -262,47 +270,67 @@ public class DistributedNetworkConfigStore
* @return config object or null of no factory found or if the specified
* JSON is null
*/
@SuppressWarnings("unchecked")
private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
JsonNode json) {
if (json != null) {
ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
if (factory != null) {
C config = factory.createConfig();
config.init(subject, factory.configKey(), json, mapper, applyDelegate);
return config;
}
}
return null;
return createConfig(subject, configClass, json, false);
}
/**
* Produces a detached config from the specified subject, config class and
* raw JSON.
* Produces a config from the specified subject, config class and raw JSON.
*
* A detached config can no longer be applied. This should be used only for
* passing the config object in the NetworkConfigEvent.
* The config can optionally be detached, which means it does not contain a
* reference to an apply delegate. This means a detached config can not be
* applied. This should be used only for passing the config object in the
* NetworkConfigEvent.
*
* @param subject config subject
* @param configClass config class
* @param json raw JSON data
* @param detached whether the config should be detached, that is, should
* be created without setting an apply delegate.
* @return config object or null of no factory found or if the specified
* JSON is null
*/
@SuppressWarnings("unchecked")
private Config createDetachedConfig(Object subject,
Class configClass, JsonNode json) {
private <S, C extends Config<S>> C createConfig(S subject, Class<C> configClass,
JsonNode json, boolean detached) {
if (json != null) {
ConfigFactory factory = factoriesByConfig.get(configClass.getName());
ConfigFactory<S, C> factory = factoriesByConfig.get(configClass.getName());
if (factory != null) {
Config config = factory.createConfig();
config.init(subject, factory.configKey(), json, mapper, null);
validateJsonType(json, factory);
C config = factory.createConfig();
config.init(subject, factory.configKey(), json, mapper,
detached ? null : applyDelegate);
return config;
}
}
return null;
}
/**
* Validates that the type of the JSON node is appropriate for the type of
* configuration. A list type configuration must be created with an
* ArrayNode, and an object type configuration must be created with an
* ObjectNode.
*
* @param json JSON node to check
* @param factory config factory of configuration
* @param <S> subject
* @param <C> configuration
* @return true if the JSON node type is appropriate for the configuration
*/
private <S, C extends Config<S>> boolean validateJsonType(JsonNode json,
ConfigFactory<S, C> factory) {
if (factory.isList() && !(json instanceof ArrayNode)) {
throw new IllegalArgumentException(INVALID_JSON_LIST);
}
if (!factory.isList() && !(json instanceof ObjectNode)) {
throw new IllegalArgumentException(INVALID_JSON_OBJECT);
}
return true;
}
// Auxiliary delegate to receive notifications about changes applied to
// the network configuration - by the apps.
......@@ -380,11 +408,11 @@ public class DistributedNetworkConfigStore
Versioned<JsonNode> oldValue = event.oldValue();
Config config = (newValue != null) ?
createDetachedConfig(subject, configClass, newValue.value()) :
null;
createConfig(subject, configClass, newValue.value(), true) :
null;
Config prevConfig = (oldValue != null) ?
createDetachedConfig(subject, configClass, oldValue.value()) :
null;
createConfig(subject, configClass, oldValue.value(), true) :
null;
NetworkConfigEvent.Type type;
switch (event.type()) {
......
......@@ -15,9 +15,12 @@
*/
package org.onosproject.rest.resources;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.net.config.Config;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.config.SubjectFactory;
import org.onosproject.rest.AbstractWebResource;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
......@@ -28,13 +31,9 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.onosproject.net.config.Config;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.config.SubjectFactory;
import org.onosproject.rest.AbstractWebResource;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import static org.onlab.util.Tools.emptyIsNotFound;
import static org.onlab.util.Tools.nullIsNotFound;
......@@ -263,7 +262,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
@PathParam("configKey") String configKey,
InputStream request) throws IOException {
NetworkConfigService service = get(NetworkConfigService.class);
ObjectNode root = (ObjectNode) mapper().readTree(request);
JsonNode root = mapper().readTree(request);
service.applyConfig(subjectClassKey,
service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
configKey, root);
......