Jonathan Hart
Committed by Gerrit Code Review

Improve network config validation errors to show which fields are invalid.

Previously, uploading invalid config results in a generic error message
which makes it difficult to figure out what is wrong with the config

Change-Id: I307d2fc0669679b067389c722556eef3aae098b9
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.net.config;
18 +
19 +/**
20 + * Indicates an invalid configuration was supplied by the user.
21 + */
22 +public class InvalidConfigException extends RuntimeException {
23 +
24 + private final String subjectKey;
25 + private final String subject;
26 + private final String configKey;
27 +
28 + /**
29 + * Creates a new invalid config exception about a specific config.
30 + *
31 + * @param subjectKey config's subject key
32 + * @param subject config's subject
33 + * @param configKey config's config key
34 + */
35 + public InvalidConfigException(String subjectKey, String subject, String configKey) {
36 + this(subjectKey, subject, configKey, null);
37 + }
38 +
39 + /**
40 + * Creates a new invalid config exception about a specific config with an
41 + * exception regarding the cause of the invalidity.
42 + *
43 + * @param subjectKey config's subject key
44 + * @param subject config's subject
45 + * @param configKey config's config key
46 + * @param cause cause of the invalidity
47 + */
48 + public InvalidConfigException(String subjectKey, String subject, String configKey, Throwable cause) {
49 + super(message(subjectKey, subject, configKey, cause), cause);
50 + this.subjectKey = subjectKey;
51 + this.subject = subject;
52 + this.configKey = configKey;
53 + }
54 +
55 + /**
56 + * Returns the subject key of the config.
57 + *
58 + * @return subject key
59 + */
60 + public String subjectKey() {
61 + return subjectKey;
62 + }
63 +
64 + /**
65 + * Returns the string representation of the subject of the config.
66 + *
67 + * @return subject
68 + */
69 + public String subject() {
70 + return subject;
71 + }
72 +
73 + /**
74 + * Returns the config key of the config.
75 + *
76 + * @return config key
77 + */
78 + public String configKey() {
79 + return configKey;
80 + }
81 +
82 + private static String message(String subjectKey, String subject, String configKey, Throwable cause) {
83 + String error = "Error parsing config " + subjectKey + "/" + subject + "/" + configKey;
84 + if (cause != null) {
85 + error = error + ": " + cause.getMessage();
86 + }
87 + return error;
88 + }
89 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.net.config;
18 +
19 +/**
20 + * Indicates a field of a configuration was invalid.
21 + */
22 +public class InvalidFieldException extends RuntimeException {
23 +
24 + private final String field;
25 + private final String reason;
26 +
27 + /**
28 + * Creates a new invalid field exception about a given field.
29 + *
30 + * @param field field name
31 + * @param reason reason the field is invalid
32 + */
33 + public InvalidFieldException(String field, String reason) {
34 + super(message(field, reason));
35 + this.field = field;
36 + this.reason = reason;
37 + }
38 +
39 + /**
40 + * Creates a new invalid field exception about a given field.
41 + *
42 + * @param field field name
43 + * @param cause throwable that occurred while trying to validate field
44 + */
45 + public InvalidFieldException(String field, Throwable cause) {
46 + super(message(field, cause.getMessage()));
47 + this.field = field;
48 + this.reason = cause.getMessage();
49 + }
50 +
51 + /**
52 + * Returns the field name.
53 + *
54 + * @return field name
55 + */
56 + public String field() {
57 + return field;
58 + }
59 +
60 + /**
61 + * Returns the reason the field failed to validate.
62 + *
63 + * @return reason
64 + */
65 + public String reason() {
66 + return reason;
67 + }
68 +
69 + private static String message(String field, String reason) {
70 + return "Field \"" + field + "\" is invalid: " + reason;
71 + }
72 +
73 +}
...@@ -40,6 +40,7 @@ import org.onlab.util.Tools; ...@@ -40,6 +40,7 @@ import org.onlab.util.Tools;
40 import org.onosproject.net.config.Config; 40 import org.onosproject.net.config.Config;
41 import org.onosproject.net.config.ConfigApplyDelegate; 41 import org.onosproject.net.config.ConfigApplyDelegate;
42 import org.onosproject.net.config.ConfigFactory; 42 import org.onosproject.net.config.ConfigFactory;
43 +import org.onosproject.net.config.InvalidConfigException;
43 import org.onosproject.net.config.NetworkConfigEvent; 44 import org.onosproject.net.config.NetworkConfigEvent;
44 import org.onosproject.net.config.NetworkConfigStore; 45 import org.onosproject.net.config.NetworkConfigStore;
45 import org.onosproject.net.config.NetworkConfigStoreDelegate; 46 import org.onosproject.net.config.NetworkConfigStoreDelegate;
...@@ -236,7 +237,17 @@ public class DistributedNetworkConfigStore ...@@ -236,7 +237,17 @@ public class DistributedNetworkConfigStore
236 public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) { 237 public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
237 // Create the configuration and validate it. 238 // Create the configuration and validate it.
238 C config = createConfig(subject, configClass, json); 239 C config = createConfig(subject, configClass, json);
239 - checkArgument(config.isValid(), INVALID_CONFIG_JSON); 240 +
241 + try {
242 + checkArgument(config.isValid(), INVALID_CONFIG_JSON);
243 + } catch (RuntimeException e) {
244 + ConfigFactory<S, C> configFactory = getConfigFactory(configClass);
245 + String subjectKey = configFactory.subjectFactory().subjectClassKey();
246 + String subjectString = configFactory.subjectFactory().subjectKey(config.subject());
247 + String configKey = config.key();
248 +
249 + throw new InvalidConfigException(subjectKey, subjectString, configKey, e);
250 + }
240 251
241 // Insert the validated configuration and get it back. 252 // Insert the validated configuration and get it back.
242 Versioned<JsonNode> versioned = configs.putAndGet(key(subject, configClass), json); 253 Versioned<JsonNode> versioned = configs.putAndGet(key(subject, configClass), json);
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.rest.exceptions;
18 +
19 +import com.fasterxml.jackson.databind.ObjectMapper;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
21 +import org.onlab.rest.exceptions.AbstractMapper;
22 +import org.onosproject.net.config.InvalidConfigException;
23 +import org.onosproject.net.config.InvalidFieldException;
24 +
25 +import javax.ws.rs.core.Response;
26 +
27 +/**
28 + * Maps InvalidConfigException to JSON output.
29 + */
30 +public class InvalidConfigExceptionMapper extends AbstractMapper<InvalidConfigException> {
31 +
32 + @Override
33 + protected Response.Status responseStatus() {
34 + return Response.Status.BAD_REQUEST;
35 + }
36 +
37 + @Override
38 + protected Response.ResponseBuilder response(Response.Status status, Throwable exception) {
39 + error = exception;
40 +
41 + InvalidConfigException ex = (InvalidConfigException) exception;
42 +
43 + ObjectMapper mapper = new ObjectMapper();
44 + String message = messageFrom(exception);
45 + ObjectNode result = mapper.createObjectNode()
46 + .put("code", status.getStatusCode())
47 + .put("message", message)
48 + .put("subjectKey", ex.subjectKey())
49 + .put("subject", ex.subject())
50 + .put("configKey", ex.configKey());
51 +
52 + if (ex.getCause() instanceof InvalidFieldException) {
53 + InvalidFieldException fieldException = (InvalidFieldException) ex.getCause();
54 + result.put("field", fieldException.field())
55 + .put("reason", fieldException.reason());
56 + }
57 +
58 + return Response.status(status).entity(result.toString());
59 + }
60 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * REST exceptions.
19 + */
20 +package org.onosproject.rest.exceptions;
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
17 package org.onosproject.rest.resources; 17 package org.onosproject.rest.resources;
18 18
19 import org.onlab.rest.AbstractWebApplication; 19 import org.onlab.rest.AbstractWebApplication;
20 +import org.onosproject.rest.exceptions.InvalidConfigExceptionMapper;
20 21
21 import java.util.Set; 22 import java.util.Set;
22 23
...@@ -49,7 +50,8 @@ public class CoreWebApplication extends AbstractWebApplication { ...@@ -49,7 +50,8 @@ public class CoreWebApplication extends AbstractWebApplication {
49 RegionsWebResource.class, 50 RegionsWebResource.class,
50 TenantWebResource.class, 51 TenantWebResource.class,
51 VirtualNetworkWebResource.class, 52 VirtualNetworkWebResource.class,
52 - MastershipWebResource.class 53 + MastershipWebResource.class,
54 + InvalidConfigExceptionMapper.class
53 ); 55 );
54 } 56 }
55 } 57 }
......