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
Showing
8 changed files
with
257 additions
and
2 deletions
This diff is collapsed. Click to expand it.
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 | +} |
This diff is collapsed. Click to expand it.
... | @@ -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 | } | ... | ... |
-
Please register or login to post a comment