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