--- /dev/null
+# List of valid osd flags
+OSD_FLAGS = [
+ 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill',
+ 'norecover', 'noscrub', 'nodeep-scrub',
+]
+
+# Implemented osd commands
+OSD_IMPLEMENTED_COMMANDS = [
+ 'scrub', 'deep_scrub', 'repair'
+]
+
+# Valid values for the 'var' argument to 'ceph osd pool set'
+POOL_PROPERTIES_1 = [
+ 'size', 'min_size', 'crash_replay_interval', 'pg_num',
+ 'crush_rule', 'hashpspool',
+]
+
+POOL_PROPERTIES_2 = [
+ 'pgp_num'
+]
+
+POOL_PROPERTIES = POOL_PROPERTIES_1 + POOL_PROPERTIES_2
+
+# Valid values for the 'ceph osd pool set-quota' command
+POOL_QUOTA_PROPERTIES = [
+ ('quota_max_bytes', 'max_bytes'),
+ ('quota_max_objects', 'max_objects'),
+]
+
+POOL_ARGS = POOL_PROPERTIES + map(
+ lambda x: x[0],
+ POOL_QUOTA_PROPERTIES
+)
+
+
+# Transform command to a human readable form
+def humanify_command(command):
+ out = [command['prefix']]
+
+ for arg, val in command.iteritems():
+ if arg != 'prefix':
+ out.append("%s=%s" % (str(arg), str(val)))
+
+ return " ".join(out)
+
+
+def invalid_pool_args(args):
+ invalid = []
+ for arg in args:
+ if arg not in POOL_ARGS:
+ invalid.append(arg)
+
+ return invalid
+
+
+def pool_update_commands(pool_name, args):
+ commands = [[], []]
+
+ # We should increase pgp_num when we are re-setting pg_num
+ if 'pg_num' in args and 'pgp_num' not in args:
+ args['pgp_num'] = args['pg_num']
+
+ # Run the first pool set and quota properties in parallel
+ for var in POOL_PROPERTIES_1:
+ if var in args:
+ commands[0].append({
+ 'prefix': 'osd pool set',
+ 'pool': pool_name,
+ 'var': var,
+ 'val': args[var],
+ })
+
+ for (var, field) in POOL_QUOTA_PROPERTIES:
+ if var in args:
+ commands[0].append({
+ 'prefix': 'osd pool set-quota',
+ 'pool': pool_name,
+ 'field': field,
+ 'val': str(args[var]),
+ })
+
+ # The second pool set properties need to be run after the first wave
+ for var in POOL_PROPERTIES_2:
+ if var in args:
+ commands[1].append({
+ 'prefix': 'osd pool set',
+ 'pool': pool_name,
+ 'var': var,
+ })
+
+ return commands
+
+
+def crush_rule_osds(nodes, rule):
+ nodes_by_id = dict((n['id'], n) for n in nodes)
+
+ def _gather_leaf_ids(node):
+ if node['id'] >= 0:
+ return set([node['id']])
+
+ result = set()
+ for child_id in node['children']:
+ if child_id >= 0:
+ result.add(child_id)
+ else:
+ result |= _gather_leaf_ids(nodes_by_id[child_id])
+
+ return result
+
+ def _gather_descendent_ids(node, typ):
+ result = set()
+ for child_id in node['children']:
+ child_node = nodes_by_id[child_id]
+ if child_node['type'] == typ:
+ result.add(child_node['id'])
+ elif 'children' in child_node:
+ result |= _gather_descendent_ids(child_node, typ)
+
+ return result
+
+ def _gather_osds(root, steps):
+ if root['id'] >= 0:
+ return set([root['id']])
+
+ osds = set()
+ step = steps[0]
+ if step['op'] == 'choose_firstn':
+ # Choose all descendents of the current node of type 'type'
+ d = _gather_descendent_ids(root, step['type'])
+ for desc_node in [nodes_by_id[i] for i in d]:
+ osds |= _gather_osds(desc_node, steps[1:])
+ elif step['op'] == 'chooseleaf_firstn':
+ # Choose all descendents of the current node of type 'type',
+ # and select all leaves beneath those
+ for desc_node in [nodes_by_id[i] for i in _gather_descendent_ids(root, step['type'])]:
+ # Short circuit another iteration to find the emit
+ # and assume anything we've done a chooseleaf on
+ # is going to be part of the selected set of osds
+ osds |= _gather_leaf_ids(desc_node)
+ elif step['op'] == 'emit':
+ if root['id'] >= 0:
+ osds |= root['id']
+
+ return osds
+
+ osds = set()
+ for i, step in enumerate(rule['steps']):
+ if step['op'] == 'take':
+ osds |= _gather_osds(nodes_by_id[step['item']], rule['steps'][i + 1:])
+ return osds