1 from collections
import namedtuple
4 CRUSH_RULE_TYPE_REPLICATED
= 1
5 CRUSH_RULE_TYPE_ERASURE
= 3
8 ServiceId
= namedtuple('ServiceId', ['fsid', 'service_type', 'service_id'])
16 CRUSH_RULE
= 'crush_rule'
21 def memoize(function
):
24 if not hasattr(self
, "_memo"):
27 if args
in self
._memo
:
28 return self
._memo
[args
]
36 OSD_FLAGS
= ('pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill',
37 'norecover', 'noscrub', 'nodeep-scrub')
39 class DataWrapper(object):
40 def __init__(self
, data
):
44 class OsdMap(DataWrapper
):
47 def __init__(self
, data
):
48 super(OsdMap
, self
).__init
__(data
)
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])
55 flags
= data
.get('flags', '').replace('pauserd,pausewr', 'pause')
56 tokenized_flags
= flags
.split(',')
58 self
.flags
= dict([(x
, x
in tokenized_flags
) for x
in OSD_FLAGS
])
62 self
.osd_tree_node_by_id
= {}
63 self
.flags
= dict([(x
, False) for x
in OSD_FLAGS
])
66 def osd_metadata(self
):
67 return self
.data
['osd_metadata']
70 def get_tree_nodes_by_id(self
):
71 return dict((n
["id"], n
) for n
in self
.data
['tree']["nodes"])
73 def _get_crush_rule_osds(self
, rule
):
74 nodes_by_id
= self
.get_tree_nodes_by_id()
76 def _gather_leaf_ids(node
):
78 return set([node
['id']])
81 for child_id
in node
['children']:
85 result |
= _gather_leaf_ids(nodes_by_id
[child_id
])
89 def _gather_descendent_ids(node
, typ
):
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
)
100 def _gather_osds(root
, steps
):
102 return set([root
['id']])
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':
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:])
133 def osds_by_rule_id(self
):
135 for rule
in self
.data
['crush']['rules']:
136 result
[rule
['rule_id']] = list(self
._get
_crush
_rule
_osds
(rule
))
142 def osds_by_pool(self
):
144 Get the OSDS which may be used in this pool
146 :return dict of pool ID to OSD IDs in the pool
150 for pool_id
, pool
in self
.pools_by_id
.items():
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']]
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()
161 result
[pool_id
] = osds
169 A dict of OSD ID to list of pool IDs
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
)
179 class FsMap(DataWrapper
):
182 def get_filesystem(self
, fscid
):
183 for fs
in self
.data
['filesystems']:
184 if fs
['id'] == fscid
:
187 raise NotFound("filesystem", fscid
)
190 class MonMap(DataWrapper
):
194 class MonStatus(DataWrapper
):
197 def __init__(self
, data
):
198 super(MonStatus
, self
).__init
__(data
)
200 self
.mons_by_rank
= dict([(m
['rank'], m
) for m
in data
['monmap']['mons']])
202 self
.mons_by_rank
= {}
205 class PgSummary(DataWrapper
):
207 A summary of the state of PGs in the cluster, reported by pool and by OSD.
212 class Health(DataWrapper
):
216 class Config(DataWrapper
):
220 class NotFound(Exception):
221 def __init__(self
, object_type
, object_id
):
222 self
.object_type
= object_type
223 self
.object_id
= object_id
226 return "Object of type %s with id %s not found" % (self
.object_type
, self
.object_id
)