Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / pybind / mgr / dashboard / types.py
1 from collections import namedtuple
2
3
4 CRUSH_RULE_TYPE_REPLICATED = 1
5 CRUSH_RULE_TYPE_ERASURE = 3
6
7
8 ServiceId = namedtuple('ServiceId', ['fsid', 'service_type', 'service_id'])
9
10
11 MON = 'mon'
12 OSD = 'osd'
13 MDS = 'mds'
14 POOL = 'pool'
15 OSD_MAP = 'osd_map'
16 CRUSH_RULE = 'crush_rule'
17 CLUSTER = 'cluster'
18 SERVER = 'server'
19
20
21 def memoize(function):
22     def wrapper(*args):
23         self = args[0]
24         if not hasattr(self, "_memo"):
25             self._memo = {}
26
27         if args in self._memo:
28             return self._memo[args]
29         else:
30             rv = function(*args)
31             self._memo[args] = rv
32             return rv
33     return wrapper
34
35
36 OSD_FLAGS = ('pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill',
37              'norecover', 'noscrub', 'nodeep-scrub')
38
39 class DataWrapper(object):
40     def __init__(self, data):
41         self.data = data
42
43
44 class OsdMap(DataWrapper):
45     str = OSD_MAP
46
47     def __init__(self, data):
48         super(OsdMap, self).__init__(data)
49         if data is not None:
50             self.osds_by_id = dict([(o['osd'], o) for o in data['osds']])
51             self.pools_by_id = dict([(p['pool'], p) for p in data['pools']])
52             self.osd_tree_node_by_id = dict([(o['id'], o) for o in data['tree']['nodes'] if o['id'] >= 0])
53
54             # Special case Yuck
55             flags = data.get('flags', '').replace('pauserd,pausewr', 'pause')
56             tokenized_flags = flags.split(',')
57
58             self.flags = dict([(x, x in tokenized_flags) for x in OSD_FLAGS])
59         else:
60             self.osds_by_id = {}
61             self.pools_by_id = {}
62             self.osd_tree_node_by_id = {}
63             self.flags = dict([(x, False) for x in OSD_FLAGS])
64
65     @property
66     def osd_metadata(self):
67         return self.data['osd_metadata']
68
69     @memoize
70     def get_tree_nodes_by_id(self):
71         return dict((n["id"], n) for n in self.data['tree']["nodes"])
72
73     def _get_crush_rule_osds(self, rule):
74         nodes_by_id = self.get_tree_nodes_by_id()
75
76         def _gather_leaf_ids(node):
77             if node['id'] >= 0:
78                 return set([node['id']])
79
80             result = set()
81             for child_id in node['children']:
82                 if child_id >= 0:
83                     result.add(child_id)
84                 else:
85                     result |= _gather_leaf_ids(nodes_by_id[child_id])
86
87             return result
88
89         def _gather_descendent_ids(node, typ):
90             result = set()
91             for child_id in node['children']:
92                 child_node = nodes_by_id[child_id]
93                 if child_node['type'] == typ:
94                     result.add(child_node['id'])
95                 elif 'children' in child_node:
96                     result |= _gather_descendent_ids(child_node, typ)
97
98             return result
99
100         def _gather_osds(root, steps):
101             if root['id'] >= 0:
102                 return set([root['id']])
103
104             osds = set()
105             step = steps[0]
106             if step['op'] == 'choose_firstn':
107                 # Choose all descendents of the current node of type 'type'
108                 d = _gather_descendent_ids(root, step['type'])
109                 for desc_node in [nodes_by_id[i] for i in d]:
110                     osds |= _gather_osds(desc_node, steps[1:])
111             elif step['op'] == 'chooseleaf_firstn':
112                 # Choose all descendents of the current node of type 'type',
113                 # and select all leaves beneath those
114                 for desc_node in [nodes_by_id[i] for i in _gather_descendent_ids(root, step['type'])]:
115                     # Short circuit another iteration to find the emit
116                     # and assume anything we've done a chooseleaf on
117                     # is going to be part of the selected set of osds
118                     osds |= _gather_leaf_ids(desc_node)
119             elif step['op'] == 'emit':
120                 if root['id'] >= 0:
121                     osds |= root['id']
122
123             return osds
124
125         osds = set()
126         for i, step in enumerate(rule['steps']):
127             if step['op'] == 'take':
128                 osds |= _gather_osds(nodes_by_id[step['item']], rule['steps'][i + 1:])
129         return osds
130
131     @property
132     @memoize
133     def osds_by_rule_id(self):
134         result = {}
135         for rule in self.data['crush']['rules']:
136             result[rule['rule_id']] = list(self._get_crush_rule_osds(rule))
137
138         return result
139
140     @property
141     @memoize
142     def osds_by_pool(self):
143         """
144         Get the OSDS which may be used in this pool
145
146         :return dict of pool ID to OSD IDs in the pool
147         """
148
149         result = {}
150         for pool_id, pool in self.pools_by_id.items():
151             osds = None
152             for rule in [r for r in self.data['crush']['rules'] if r['ruleset'] == pool['crush_ruleset']]:
153                 if rule['min_size'] <= pool['size'] <= rule['max_size']:
154                     osds = self.osds_by_rule_id[rule['rule_id']]
155
156             if osds is None:
157                 # Fallthrough, the pool size didn't fall within any of the rules in its ruleset, Calamari
158                 # doesn't understand.  Just report all OSDs instead of failing horribly.
159                 osds = self.osds_by_id.keys()
160
161             result[pool_id] = osds
162
163         return result
164
165     @property
166     @memoize
167     def osd_pools(self):
168         """
169         A dict of OSD ID to list of pool IDs
170         """
171         osds = dict([(osd_id, []) for osd_id in self.osds_by_id.keys()])
172         for pool_id in self.pools_by_id.keys():
173             for in_pool_id in self.osds_by_pool[pool_id]:
174                 osds[in_pool_id].append(pool_id)
175
176         return osds
177
178
179 class FsMap(DataWrapper):
180     str = 'fs_map'
181
182     def get_filesystem(self, fscid):
183         for fs in self.data['filesystems']:
184             if fs['id'] == fscid:
185                 return fs
186
187         raise NotFound("filesystem", fscid)
188
189
190 class MonMap(DataWrapper):
191     str = 'mon_map'
192
193
194 class MonStatus(DataWrapper):
195     str = 'mon_status'
196
197     def __init__(self, data):
198         super(MonStatus, self).__init__(data)
199         if data is not None:
200             self.mons_by_rank = dict([(m['rank'], m) for m in data['monmap']['mons']])
201         else:
202             self.mons_by_rank = {}
203
204
205 class PgSummary(DataWrapper):
206     """
207     A summary of the state of PGs in the cluster, reported by pool and by OSD.
208     """
209     str = 'pg_summary'
210
211
212 class Health(DataWrapper):
213     str = 'health'
214
215
216 class Config(DataWrapper):
217     str = 'config'
218
219
220 class NotFound(Exception):
221     def __init__(self, object_type, object_id):
222         self.object_type = object_type
223         self.object_id = object_id
224
225     def __str__(self):
226         return "Object of type %s with id %s not found" % (self.object_type, self.object_id)
227