2 from distutils
.version
import StrictVersion
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
12 class ValidatingSerializer(serializers
.Serializer
):
16 Compatibility alias for django rest framework 2 vs. 3
18 return self
.initial_data
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 {}
24 # django rest framework >= 3 has different is_Valid prototype
26 super(ValidatingSerializer
, self
).is_valid(False)
28 if self
.init_data
is not None:
29 if http_method
== 'POST':
31 self
.construct_errors(self
.Meta
.create_allowed
,
32 self
.Meta
.create_required
,
33 self
.init_data
.keys(),
36 elif http_method
in ('PATCH', 'PUT'):
38 self
.construct_errors(self
.Meta
.modify_allowed
,
39 self
.Meta
.modify_required
,
40 self
.init_data
.keys(),
43 self
._errors
.update([[http_method
, 'Not a valid method']])
45 return not self
._errors
47 def construct_errors(self
, allowed
, required
, init_data
, action
):
50 not_allowed
= set(init_data
) - set(allowed
)
52 dict([x
, 'Not allowed during %s' % action
] for x
in not_allowed
))
54 required
= set(required
) - set(init_data
)
56 dict([x
, 'Required during %s' % action
] for x
in required
))
61 # like http://www.django-rest-framework.org/api-guide/serializers#dynamically-modifying-fields
63 for field
, value
in self
.init_data
.iteritems():
64 filtered_data
[field
] = self
.data
[field
]
69 class PoolSerializer(ValidatingSerializer
):
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',
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')
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")
91 # Not required in creation, immutable
92 id = serializers
.CharField(source
='pool', required
=False,
93 help_text
="Unique numeric ID")
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',
109 help_text
="Effective number of placement groups to use when calculating "
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")
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")
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)")
127 class OsdSerializer(ValidatingSerializer
):
129 fields
= ('uuid', 'up', 'in', 'id', 'reweight', 'server', 'pools',
130 'valid_commands', 'public_addr', 'cluster_addr')
133 modify_allowed
= ('up', 'in', 'reweight')
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",
151 valid_commands
= serializers
.CharField(read_only
=True,
152 help_text
="List of commands that can be applied to this OSD")
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")
160 class OsdConfigSerializer(ValidatingSerializer
):
165 modify_allowed
= OSD_FLAGS
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",
173 nodown
= serializers
.BooleanField(
174 help_text
="Prevent OSDs from automatically getting marked as Down by the monitors. This setting is useful for troubleshooting",
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",
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",
188 nodeepscrub
= serializers
.BooleanField(
189 help_text
="Disables automatic periodic deep scrub operations on OSDs. May still be initiated on demand",
193 class CrushRuleSerializer(serializers
.Serializer
):
196 'id', 'name', 'ruleset', 'type', 'min_size', 'max_size', 'steps',
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")
217 class CrushRuleSetSerializer(serializers
.Serializer
):
219 fields
= ('id', 'rules')
221 id = serializers
.IntegerField()
222 rules
= CrushRuleSerializer(many
=True)
225 class RequestSerializer(serializers
.Serializer
):
228 'id', 'state', 'error', 'error_message', 'headline', 'status',
229 'requested_at', 'completed_at')
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.")
251 class ServiceSerializer(serializers
.Serializer
):
253 fields
= ('type', 'id')
255 type = serializers
.CharField()
256 id = serializers
.CharField()
259 class ServerSerializer(serializers
.Serializer
):
261 fields
= ('hostname', 'services', 'ceph_version')
263 # Identifying information
264 hostname
= serializers
.CharField(help_text
="Domain name")
266 ceph_version
= serializers
.CharField(
267 help_text
="The version of Ceph installed."
269 services
= ServiceSerializer(many
=True,
270 help_text
="List of Ceph services seen"
274 class ConfigSettingSerializer(serializers
.Serializer
):
276 fields
= ('key', 'value')
278 # This is very simple for now, but later we may add more things like
279 # schema information, allowed values, defaults.
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")
286 class MonSerializer(serializers
.Serializer
):
288 fields
= ('name', 'rank', 'in_quorum', 'server', 'addr')
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")
302 class CliSerializer(serializers
.Serializer
):
304 fields
= ('out', 'err', 'status')
306 out
= serializers
.CharField(help_text
="Standard out")
307 err
= serializers
.CharField(help_text
="Standard error")
308 status
= serializers
.IntegerField(help_text
="Exit code")
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']
319 OsdSerializer
._declared
_fields
['in'] = OsdSerializer
._declared
_fields
[
321 del OsdSerializer
._declared
_fields
['_in']
322 OsdSerializer
._declared
_fields
['in'].source
= "in"
323 OsdConfigSerializer
._declared
_fields
['nodeep-scrub'] = \
324 OsdConfigSerializer
._declared
_fields
['nodeepscrub']