Coverage for nova/api/validation/extra_specs/base.py: 91%
58 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +0000
1# Copyright 2020 Red Hat, Inc. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
15import dataclasses
16import re
17import typing as ty
19from oslo_utils import strutils
21from nova import exception
24@dataclasses.dataclass
25class ExtraSpecValidator:
26 name: str
27 description: str
28 value: ty.Dict[str, ty.Any]
29 deprecated: bool = False
30 parameters: ty.List[ty.Dict[str, ty.Any]] = dataclasses.field(
31 default_factory=list
32 )
34 name_regex: str = None
35 value_regex: str = None
37 def __post_init__(self):
38 # generate a regex for the name
40 name_regex = self.name
41 # replace the human-readable patterns with named regex groups; this
42 # will transform e.g. 'hw:numa_cpus.{id}' to 'hw:numa_cpus.(?P<id>\d+)'
43 for param in self.parameters:
44 pattern = f'(?P<{param["name"]}>{param["pattern"]})'
45 name_regex = name_regex.replace(f'{ {param["name"]}} ', pattern)
47 self.name_regex = name_regex
49 # ...and do the same for the value, but only if we're using strings
51 if self.value['type'] not in (int, str, bool): 51 ↛ 52line 51 didn't jump to line 52 because the condition on line 51 was never true
52 raise ValueError(
53 f"Unsupported parameter type '{self.value['type']}'"
54 )
56 value_regex = None
57 if self.value['type'] == str and self.value.get('pattern'):
58 value_regex = self.value['pattern']
60 self.value_regex = value_regex
62 def _validate_str(self, value):
63 if 'pattern' in self.value:
64 value_match = re.fullmatch(self.value_regex, value)
65 if not value_match:
66 raise exception.ValidationError(
67 f"Validation failed; '{value}' is not of the format "
68 f"'{self.value_regex}'."
69 )
70 elif 'enum' in self.value: 70 ↛ exitline 70 didn't return from function '_validate_str' because the condition on line 70 was always true
71 if value not in self.value['enum']:
72 values = ', '.join(str(x) for x in self.value['enum'])
73 raise exception.ValidationError(
74 f"Validation failed; '{value}' is not one of: {values}."
75 )
77 def _validate_int(self, value):
78 try:
79 value = int(value)
80 except ValueError:
81 raise exception.ValidationError(
82 f"Validation failed; '{value}' is not a valid integer value."
83 )
85 if 'max' in self.value and self.value['max'] < value: 85 ↛ 86line 85 didn't jump to line 86 because the condition on line 85 was never true
86 raise exception.ValidationError(
87 f"Validation failed; '{value}' is greater than the max value "
88 f"of '{self.value['max']}'."
89 )
91 if 'min' in self.value and self.value['min'] > value:
92 raise exception.ValidationError(
93 f"Validation failed; '{value}' is less than the min value "
94 f"of '{self.value['min']}'."
95 )
97 def _validate_bool(self, value):
98 try:
99 strutils.bool_from_string(value, strict=True)
100 except ValueError:
101 raise exception.ValidationError(
102 f"Validation failed; '{value}' is not a valid boolean-like "
103 f"value."
104 )
106 def validate(self, name, value):
107 name_match = re.fullmatch(self.name_regex, name)
108 if not name_match: 108 ↛ 110line 108 didn't jump to line 110 because the condition on line 108 was never true
109 # NOTE(stephenfin): This is mainly here for testing purposes
110 raise exception.ValidationError(
111 f"Validation failed; expected a name of format '{self.name}' "
112 f"but got '{name}'."
113 )
115 if self.value['type'] == int:
116 self._validate_int(value)
117 elif self.value['type'] == bool:
118 self._validate_bool(value)
119 else: # str
120 self._validate_str(value)