]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | |
2 | from distutils.version import StrictVersion | |
3 | ||
4 | import rest_framework | |
5 | from rest_framework import serializers | |
6 | import rest.app.serializers.fields as fields | |
7 | from rest.app.types import CRUSH_RULE_TYPE_REPLICATED, \ | |
8 | CRUSH_RULE_TYPE_ERASURE, USER_REQUEST_COMPLETE, \ | |
9 | USER_REQUEST_SUBMITTED, OSD_FLAGS | |
10 | ||
11 | ||
12 | class ValidatingSerializer(serializers.Serializer): | |
13 | @property | |
14 | def init_data(self): | |
15 | """ | |
16 | Compatibility alias for django rest framework 2 vs. 3 | |
17 | """ | |
18 | return self.initial_data | |
19 | ||
20 | def is_valid(self, http_method): | |
21 | if StrictVersion(rest_framework.__version__) < StrictVersion("3.0.0"): | |
22 | self._errors = super(ValidatingSerializer, self).errors or {} | |
23 | else: | |
24 | # django rest framework >= 3 has different is_Valid prototype | |
25 | # than <= 2 | |
26 | super(ValidatingSerializer, self).is_valid(False) | |
27 | ||
28 | if self.init_data is not None: | |
29 | if http_method == 'POST': | |
30 | self._errors.update( | |
31 | self.construct_errors(self.Meta.create_allowed, | |
32 | self.Meta.create_required, | |
33 | self.init_data.keys(), | |
34 | http_method)) | |
35 | ||
36 | elif http_method in ('PATCH', 'PUT'): | |
37 | self._errors.update( | |
38 | self.construct_errors(self.Meta.modify_allowed, | |
39 | self.Meta.modify_required, | |
40 | self.init_data.keys(), | |
41 | http_method)) | |
42 | else: | |
43 | self._errors.update([[http_method, 'Not a valid method']]) | |
44 | ||
45 | return not self._errors | |
46 | ||
47 | def construct_errors(self, allowed, required, init_data, action): | |
48 | errors = {} | |
49 | ||
50 | not_allowed = set(init_data) - set(allowed) | |
51 | errors.update( | |
52 | dict([x, 'Not allowed during %s' % action] for x in not_allowed)) | |
53 | ||
54 | required = set(required) - set(init_data) | |
55 | errors.update( | |
56 | dict([x, 'Required during %s' % action] for x in required)) | |
57 | ||
58 | return errors | |
59 | ||
60 | def get_data(self): | |
61 | # like http://www.django-rest-framework.org/api-guide/serializers#dynamically-modifying-fields | |
62 | filtered_data = {} | |
63 | for field, value in self.init_data.iteritems(): | |
64 | filtered_data[field] = self.data[field] | |
65 | ||
66 | return filtered_data | |
67 | ||
68 | ||
69 | class PoolSerializer(ValidatingSerializer): | |
70 | class Meta: | |
71 | fields = ('name', 'id', 'size', 'pg_num', 'crush_ruleset', 'min_size', | |
72 | 'crash_replay_interval', 'crush_ruleset', | |
73 | 'pgp_num', 'hashpspool', 'full', 'quota_max_objects', | |
74 | 'quota_max_bytes') | |
75 | create_allowed = ('name', 'pg_num', 'pgp_num', 'size', 'min_size', | |
76 | 'crash_replay_interval', 'crush_ruleset', | |
77 | 'quota_max_objects', 'quota_max_bytes', 'hashpspool') | |
78 | create_required = ('name', 'pg_num') | |
79 | modify_allowed = ('name', 'pg_num', 'pgp_num', 'size', 'min_size', | |
80 | 'crash_replay_interval', 'crush_ruleset', | |
81 | 'quota_max_objects', 'quota_max_bytes', 'hashpspool') | |
82 | modify_required = () | |
83 | ||
84 | # Required in creation | |
85 | name = serializers.CharField(required=False, source='pool_name', | |
86 | help_text="Human readable name of the pool, may" | |
87 | "change over the pools lifetime at user request.") | |
88 | pg_num = serializers.IntegerField(required=False, | |
89 | help_text="Number of placement groups in this pool") | |
90 | ||
91 | # Not required in creation, immutable | |
92 | id = serializers.CharField(source='pool', required=False, | |
93 | help_text="Unique numeric ID") | |
94 | ||
95 | # May be set in creation or updates | |
96 | size = serializers.IntegerField(required=False, | |
97 | help_text="Replication factor") | |
98 | min_size = serializers.IntegerField(required=False, | |
99 | help_text="Minimum number of replicas required for I/O") | |
100 | crash_replay_interval = serializers.IntegerField(required=False, | |
101 | help_text="Number of seconds to allow clients to " | |
102 | "replay acknowledged, but uncommitted requests") | |
103 | crush_ruleset = serializers.IntegerField(required=False, | |
104 | help_text="CRUSH ruleset in use") | |
105 | # In 'ceph osd pool set' it's called pgp_num, but in 'ceph osd dump' it's called | |
106 | # pg_placement_num :-/ | |
107 | pgp_num = serializers.IntegerField(source='pg_placement_num', | |
108 | required=False, | |
109 | help_text="Effective number of placement groups to use when calculating " | |
110 | "data placement") | |
111 | ||
112 | # This is settable by 'ceph osd pool set' but in 'ceph osd dump' it only appears | |
113 | # within the 'flags' integer. We synthesize a boolean from the flags. | |
114 | hashpspool = serializers.BooleanField(required=False, | |
115 | help_text="Enable HASHPSPOOL flag") | |
116 | ||
117 | # This is synthesized from ceph's 'flags' attribute, read only. | |
118 | full = serializers.BooleanField(required=False, | |
119 | help_text="True if the pool is full") | |
120 | ||
121 | quota_max_objects = serializers.IntegerField(required=False, | |
122 | help_text="Quota limit on object count (0 is unlimited)") | |
123 | quota_max_bytes = serializers.IntegerField(required=False, | |
124 | help_text="Quota limit on usage in bytes (0 is unlimited)") | |
125 | ||
126 | ||
127 | class OsdSerializer(ValidatingSerializer): | |
128 | class Meta: | |
129 | fields = ('uuid', 'up', 'in', 'id', 'reweight', 'server', 'pools', | |
130 | 'valid_commands', 'public_addr', 'cluster_addr') | |
131 | create_allowed = () | |
132 | create_required = () | |
133 | modify_allowed = ('up', 'in', 'reweight') | |
134 | modify_required = () | |
135 | ||
136 | id = serializers.IntegerField(read_only=True, source='osd', | |
137 | help_text="ID of this OSD within this cluster") | |
138 | uuid = fields.UuidField(read_only=True, | |
139 | help_text="Globally unique ID for this OSD") | |
140 | up = fields.BooleanField(required=False, | |
141 | help_text="Whether the OSD is running from the point of view of the rest of the cluster") | |
142 | _in = fields.BooleanField(required=False, | |
143 | help_text="Whether the OSD is 'in' the set of OSDs which will be used to store data") | |
144 | reweight = serializers.FloatField(required=False, | |
145 | help_text="CRUSH weight factor") | |
146 | server = serializers.CharField(read_only=True, | |
147 | help_text="FQDN of server this OSD was last running on") | |
148 | pools = serializers.ListField( | |
149 | help_text="List of pool IDs which use this OSD for storage", | |
150 | required=False) | |
151 | valid_commands = serializers.CharField(read_only=True, | |
152 | help_text="List of commands that can be applied to this OSD") | |
153 | ||
154 | public_addr = serializers.CharField(read_only=True, | |
155 | help_text="Public/frontend IP address") | |
156 | cluster_addr = serializers.CharField(read_only=True, | |
157 | help_text="Cluster/backend IP address") | |
158 | ||
159 | ||
160 | class OsdConfigSerializer(ValidatingSerializer): | |
161 | class Meta: | |
162 | fields = OSD_FLAGS | |
163 | create_allowed = () | |
164 | create_required = () | |
165 | modify_allowed = OSD_FLAGS | |
166 | modify_required = () | |
167 | ||
168 | pause = serializers.BooleanField( | |
169 | help_text="Disable IO requests to all OSDs in cluster", required=False) | |
170 | noup = serializers.BooleanField( | |
171 | help_text="Prevent OSDs from automatically getting marked as Up by the monitors. This setting is useful for troubleshooting", | |
172 | required=False) | |
173 | nodown = serializers.BooleanField( | |
174 | help_text="Prevent OSDs from automatically getting marked as Down by the monitors. This setting is useful for troubleshooting", | |
175 | required=False) | |
176 | noout = serializers.BooleanField( | |
177 | help_text="Prevent Down OSDs from being marked as out", required=False) | |
178 | noin = serializers.BooleanField( | |
179 | help_text="Prevent OSDs from booting OSDs from being marked as IN. Will cause cluster health to be set to WARNING", | |
180 | required=False) | |
181 | nobackfill = serializers.BooleanField( | |
182 | help_text="Disable backfill operations on cluster", required=False) | |
183 | norecover = serializers.BooleanField( | |
184 | help_text="Disable replication of Placement Groups", required=False) | |
185 | noscrub = serializers.BooleanField( | |
186 | help_text="Disables automatic periodic scrub operations on OSDs. May still be initiated on demand", | |
187 | required=False) | |
188 | nodeepscrub = serializers.BooleanField( | |
189 | help_text="Disables automatic periodic deep scrub operations on OSDs. May still be initiated on demand", | |
190 | required=False) | |
191 | ||
192 | ||
193 | class CrushRuleSerializer(serializers.Serializer): | |
194 | class Meta: | |
195 | fields = ( | |
196 | 'id', 'name', 'ruleset', 'type', 'min_size', 'max_size', 'steps', | |
197 | 'osd_count') | |
198 | ||
199 | id = serializers.IntegerField(source='rule_id') | |
200 | name = serializers.CharField(source='rule_name', | |
201 | help_text="Human readable name") | |
202 | ruleset = serializers.IntegerField( | |
203 | help_text="ID of the CRUSH ruleset of which this rule is a member") | |
204 | type = fields.EnumField({CRUSH_RULE_TYPE_REPLICATED: 'replicated', | |
205 | CRUSH_RULE_TYPE_ERASURE: 'erasure'}, | |
206 | help_text="Data redundancy type") | |
207 | min_size = serializers.IntegerField( | |
208 | help_text="If a pool makes more replicas than this number, CRUSH will NOT select this rule") | |
209 | max_size = serializers.IntegerField( | |
210 | help_text="If a pool makes fewer replicas than this number, CRUSH will NOT select this rule") | |
211 | steps = serializers.ListField( | |
212 | help_text="List of operations used to select OSDs") | |
213 | osd_count = serializers.IntegerField( | |
214 | help_text="Number of OSDs which are used for data placement") | |
215 | ||
216 | ||
217 | class CrushRuleSetSerializer(serializers.Serializer): | |
218 | class Meta: | |
219 | fields = ('id', 'rules') | |
220 | ||
221 | id = serializers.IntegerField() | |
222 | rules = CrushRuleSerializer(many=True) | |
223 | ||
224 | ||
225 | class RequestSerializer(serializers.Serializer): | |
226 | class Meta: | |
227 | fields = ( | |
228 | 'id', 'state', 'error', 'error_message', 'headline', 'status', | |
229 | 'requested_at', 'completed_at') | |
230 | ||
231 | id = serializers.CharField( | |
232 | help_text="A globally unique ID for this request") | |
233 | state = serializers.CharField( | |
234 | help_text="One of '{complete}', '{submitted}'".format( | |
235 | complete=USER_REQUEST_COMPLETE, submitted=USER_REQUEST_SUBMITTED)) | |
236 | error = serializers.BooleanField( | |
237 | help_text="True if the request completed unsuccessfully") | |
238 | error_message = serializers.CharField( | |
239 | help_text="Human readable string describing failure if ``error`` is True") | |
240 | headline = serializers.CharField( | |
241 | help_text="Single sentence human readable description of the request") | |
242 | status = serializers.CharField( | |
243 | help_text="Single sentence human readable description of the request's current " | |
244 | "activity, if it has more than one stage. May be null.") | |
245 | requested_at = serializers.DateTimeField( | |
246 | help_text="Time at which the request was received by calamari server") | |
247 | completed_at = serializers.DateTimeField( | |
248 | help_text="Time at which the request completed, may be null.") | |
249 | ||
250 | ||
251 | class ServiceSerializer(serializers.Serializer): | |
252 | class Meta: | |
253 | fields = ('type', 'id') | |
254 | ||
255 | type = serializers.CharField() | |
256 | id = serializers.CharField() | |
257 | ||
258 | ||
259 | class ServerSerializer(serializers.Serializer): | |
260 | class Meta: | |
261 | fields = ('hostname', 'services', 'ceph_version') | |
262 | ||
263 | # Identifying information | |
264 | hostname = serializers.CharField(help_text="Domain name") | |
265 | ||
266 | ceph_version = serializers.CharField( | |
267 | help_text="The version of Ceph installed." | |
268 | ) | |
269 | services = ServiceSerializer(many=True, | |
270 | help_text="List of Ceph services seen" | |
271 | "on this server") | |
272 | ||
273 | ||
274 | class ConfigSettingSerializer(serializers.Serializer): | |
275 | class Meta: | |
276 | fields = ('key', 'value') | |
277 | ||
278 | # This is very simple for now, but later we may add more things like | |
279 | # schema information, allowed values, defaults. | |
280 | ||
281 | key = serializers.CharField(help_text="Name of the configuration setting") | |
282 | value = serializers.CharField( | |
283 | help_text="Current value of the setting, as a string") | |
284 | ||
285 | ||
286 | class MonSerializer(serializers.Serializer): | |
287 | class Meta: | |
288 | fields = ('name', 'rank', 'in_quorum', 'server', 'addr') | |
289 | ||
290 | name = serializers.CharField(help_text="Human readable name") | |
291 | rank = serializers.IntegerField( | |
292 | help_text="Unique of the mon within the cluster") | |
293 | in_quorum = serializers.BooleanField( | |
294 | help_text="True if the mon is a member of current quorum") | |
295 | server = serializers.CharField( | |
296 | help_text="Hostname of server running the OSD") | |
297 | addr = serializers.CharField(help_text="IP address of monitor service") | |
298 | leader = serializers.BooleanField( | |
299 | help_text="True if this monitor is the leader of the quorum. False otherwise") | |
300 | ||
301 | ||
302 | class CliSerializer(serializers.Serializer): | |
303 | class Meta: | |
304 | fields = ('out', 'err', 'status') | |
305 | ||
306 | out = serializers.CharField(help_text="Standard out") | |
307 | err = serializers.CharField(help_text="Standard error") | |
308 | status = serializers.IntegerField(help_text="Exit code") | |
309 | ||
310 | ||
311 | # Declarative metaclass definitions are great until you want | |
312 | # to use a reserved word | |
313 | if StrictVersion(rest_framework.__version__) < StrictVersion("3.0.0"): | |
314 | # In django-rest-framework 2.3.x (Calamari used this) | |
315 | OsdSerializer.base_fields['in'] = OsdSerializer.base_fields['_in'] | |
316 | OsdConfigSerializer.base_fields['nodeep-scrub'] = \ | |
317 | OsdConfigSerializer.base_fields['nodeepscrub'] | |
318 | else: | |
319 | OsdSerializer._declared_fields['in'] = OsdSerializer._declared_fields[ | |
320 | '_in'] | |
321 | del OsdSerializer._declared_fields['_in'] | |
322 | OsdSerializer._declared_fields['in'].source = "in" | |
323 | OsdConfigSerializer._declared_fields['nodeep-scrub'] = \ | |
324 | OsdConfigSerializer._declared_fields['nodeepscrub'] | |
325 | ||
326 |