]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/rest/app/types.py
bump version to 12.0.3-pve3
[ceph.git] / ceph / src / pybind / mgr / rest / app / types.py
CommitLineData
7c673cae
FG
1from collections import namedtuple
2from rest.app.util import memoize
3
4from rest.logger import logger
5log = logger()
6
7
8CRUSH_RULE_TYPE_REPLICATED = 1
9CRUSH_RULE_TYPE_ERASURE = 3
10
11
12ServiceId = namedtuple('ServiceId', ['fsid', 'service_type', 'service_id'])
13
14
15MON = 'mon'
16OSD = 'osd'
17MDS = 'mds'
18POOL = 'pool'
19OSD_MAP = 'osd_map'
20CRUSH_RULE = 'crush_rule'
21CLUSTER = 'cluster'
22SERVER = 'server'
23
24
25class SyncObject(object):
26 """
27 An object from a Ceph cluster that we are maintaining
28 a copy of on the Calamari server.
29
30 We wrap these JSON-serializable objects in a python object to:
31
32 - Decorate them with things like id-to-entry dicts
33 - Have a generic way of seeing the version of an object
34
35 """
36 def __init__(self, version, data):
37 self.version = version
38 self.data = data
39
40 @classmethod
41 def cmp(cls, a, b):
42 """
43 Slight bastardization of cmp. Takes two versions,
44 and returns a cmp-like value, except that if versions
45 are not sortable we only return 0 or 1.
46 """
47 # Version is something unique per version (like a hash)
48 return 1 if a != b else 0
49
50
51class VersionedSyncObject(SyncObject):
52 @classmethod
53 def cmp(cls, a, b):
54 # Version is something numeric like an epoch
55 return cmp(a, b)
56
57
58class OsdMap(VersionedSyncObject):
59 str = OSD_MAP
60
61 def __init__(self, version, data):
62 super(OsdMap, self).__init__(version, data)
63 if data is not None:
64 self.osds_by_id = dict([(o['osd'], o) for o in data['osds']])
65 self.pools_by_id = dict([(p['pool'], p) for p in data['pools']])
66 self.osd_tree_node_by_id = dict([(o['id'], o) for o in data['tree']['nodes'] if o['id'] >= 0])
67
68 # Special case Yuck
69 flags = data.get('flags', '').replace('pauserd,pausewr', 'pause')
70 tokenized_flags = flags.split(',')
71
72 self.flags = dict([(x, x in tokenized_flags) for x in OSD_FLAGS])
73 else:
74 self.osds_by_id = {}
75 self.pools_by_id = {}
76 self.osd_tree_node_by_id = {}
77 self.flags = dict([(x, False) for x in OSD_FLAGS])
78
79 @property
80 def osd_metadata(self):
81 return self.data['osd_metadata']
82
83 @memoize
84 def get_tree_nodes_by_id(self):
85 return dict((n["id"], n) for n in self.data['tree']["nodes"])
86
87 def _get_crush_rule_osds(self, rule):
88 nodes_by_id = self.get_tree_nodes_by_id()
89
90 def _gather_leaf_ids(node):
91 if node['id'] >= 0:
92 return set([node['id']])
93
94 result = set()
95 for child_id in node['children']:
96 if child_id >= 0:
97 result.add(child_id)
98 else:
99 result |= _gather_leaf_ids(nodes_by_id[child_id])
100
101 return result
102
103 def _gather_descendent_ids(node, typ):
104 result = set()
105 for child_id in node['children']:
106 child_node = nodes_by_id[child_id]
107 if child_node['type'] == typ:
108 result.add(child_node['id'])
109 elif 'children' in child_node:
110 result |= _gather_descendent_ids(child_node, typ)
111
112 return result
113
114 def _gather_osds(root, steps):
115 if root['id'] >= 0:
116 return set([root['id']])
117
118 osds = set()
119 step = steps[0]
120 if step['op'] == 'choose_firstn':
121 # Choose all descendents of the current node of type 'type'
122 d = _gather_descendent_ids(root, step['type'])
123 for desc_node in [nodes_by_id[i] for i in d]:
124 osds |= _gather_osds(desc_node, steps[1:])
125 elif step['op'] == 'chooseleaf_firstn':
126 # Choose all descendents of the current node of type 'type',
127 # and select all leaves beneath those
128 for desc_node in [nodes_by_id[i] for i in _gather_descendent_ids(root, step['type'])]:
129 # Short circuit another iteration to find the emit
130 # and assume anything we've done a chooseleaf on
131 # is going to be part of the selected set of osds
132 osds |= _gather_leaf_ids(desc_node)
133 elif step['op'] == 'emit':
134 if root['id'] >= 0:
135 osds |= root['id']
136
137 return osds
138
139 osds = set()
140 for i, step in enumerate(rule['steps']):
141 if step['op'] == 'take':
142 osds |= _gather_osds(nodes_by_id[step['item']], rule['steps'][i + 1:])
143 return osds
144
145 @property
146 @memoize
147 def osds_by_rule_id(self):
148 result = {}
149 for rule in self.data['crush']['rules']:
150 result[rule['rule_id']] = list(self._get_crush_rule_osds(rule))
151
152 return result
153
154 @property
155 @memoize
156 def osds_by_pool(self):
157 """
158 Get the OSDS which may be used in this pool
159
160 :return dict of pool ID to OSD IDs in the pool
161 """
162
163 result = {}
164 for pool_id, pool in self.pools_by_id.items():
165 osds = None
166 for rule in [r for r in self.data['crush']['rules'] if r['ruleset'] == pool['crush_ruleset']]:
167 if rule['min_size'] <= pool['size'] <= rule['max_size']:
168 osds = self.osds_by_rule_id[rule['rule_id']]
169
170 if osds is None:
171 # Fallthrough, the pool size didn't fall within any of the rules in its ruleset, Calamari
172 # doesn't understand. Just report all OSDs instead of failing horribly.
173 log.error("Cannot determine OSDS for pool %s" % pool_id)
174 osds = self.osds_by_id.keys()
175
176 result[pool_id] = osds
177
178 return result
179
180 @property
181 @memoize
182 def osd_pools(self):
183 """
184 A dict of OSD ID to list of pool IDs
185 """
186 osds = dict([(osd_id, []) for osd_id in self.osds_by_id.keys()])
187 for pool_id in self.pools_by_id.keys():
188 for in_pool_id in self.osds_by_pool[pool_id]:
189 osds[in_pool_id].append(pool_id)
190
191 return osds
192
193
194class FsMap(VersionedSyncObject):
195 str = 'fs_map'
196
197
198class MonMap(VersionedSyncObject):
199 str = 'mon_map'
200
201
202class MonStatus(VersionedSyncObject):
203 str = 'mon_status'
204
205 def __init__(self, version, data):
206 super(MonStatus, self).__init__(version, data)
207 if data is not None:
208 self.mons_by_rank = dict([(m['rank'], m) for m in data['monmap']['mons']])
209 else:
210 self.mons_by_rank = {}
211
212
213class PgSummary(SyncObject):
214 """
215 A summary of the state of PGs in the cluster, reported by pool and by OSD.
216 """
217 str = 'pg_summary'
218
219
220class Health(SyncObject):
221 str = 'health'
222
223
224class Config(SyncObject):
225 str = 'config'
226
227
228class NotFound(Exception):
229 def __init__(self, object_type, object_id):
230 self.object_type = object_type
231 self.object_id = object_id
232
233 def __str__(self):
234 return "Object of type %s with id %s not found" % (self.object_type, self.object_id)
235
236
237# The objects that ClusterMonitor keeps copies of from the mon
238SYNC_OBJECT_TYPES = [FsMap, OsdMap, MonMap, MonStatus, PgSummary, Health, Config]
239SYNC_OBJECT_STR_TYPE = dict((t.str, t) for t in SYNC_OBJECT_TYPES)
240
241USER_REQUEST_COMPLETE = 'complete'
242USER_REQUEST_SUBMITTED = 'submitted'
243
244# List of allowable things to send as ceph commands to OSDs
245OSD_IMPLEMENTED_COMMANDS = ('scrub', 'deep_scrub', 'repair')
246OSD_FLAGS = ('pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norecover', 'noscrub', 'nodeep-scrub')
247
248# Severity codes for Calamari events
249CRITICAL = 1
250ERROR = 2
251WARNING = 3
252RECOVERY = 4
253INFO = 5
254
255SEVERITIES = {
256 CRITICAL: "CRITICAL",
257 ERROR: "ERROR",
258 WARNING: "WARNING",
259 RECOVERY: "RECOVERY",
260 INFO: "INFO"
261}
262
263STR_TO_SEVERITY = dict([(b, a) for (a, b) in SEVERITIES.items()])
264
265
266def severity_str(severity):
267 return SEVERITIES[severity]
268
269
270def severity_from_str(severitry_str):
271 return STR_TO_SEVERITY[severitry_str]