]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/rest/app/manager/pool_request_factory.py
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / pybind / mgr / rest / app / manager / pool_request_factory.py
1
2 from rest.app.manager.request_factory import RequestFactory
3 from rest.app.types import OsdMap, Config
4 from rest.app.manager.user_request import OsdMapModifyingRequest, \
5 PgCreatingRequest, PoolCreatingRequest
6
7 from rest.module import global_instance as rest_plugin
8
9 from rest.logger import logger
10 log = logger()
11
12 # Valid values for the 'var' argument to 'ceph osd pool set'
13 POOL_PROPERTIES = ["size", "min_size", "crash_replay_interval", "pg_num",
14 "pgp_num", "crush_ruleset", "hashpspool"]
15
16 # In Ceph versions before mon_osd_max_split_count, assume it is set to this
17 LEGACY_MON_OSD_MAX_SPLIT_COUNT = "32"
18
19
20 class PoolRequestFactory(RequestFactory):
21 def _resolve_pool(self, pool_id):
22 osd_map = rest_plugin().get_sync_object(OsdMap)
23 return osd_map.pools_by_id[pool_id]
24
25 def _pool_attribute_commands(self, pool_name, attributes):
26 commands = []
27 for var in POOL_PROPERTIES:
28 if var in attributes:
29 val = attributes[var]
30
31 # Special case for hashpspool, accepts 'true' from firefly
32 # onwards but requires 0 or 1 for dumpling, so just use the
33 # old style.
34 if isinstance(val, bool):
35 val = 1 if val else 0
36
37 commands.append(('osd pool set', {
38 'pool': pool_name,
39 'var': var,
40 'val': val
41 }))
42
43 # Quota setting ('osd pool set-quota') is separate to the main 'set'
44 # operation
45 for attr_name, set_name in [('quota_max_bytes', 'max_bytes'),
46 ('quota_max_objects', 'max_objects')]:
47 if attr_name in attributes:
48 commands.append(('osd pool set-quota', {
49 'pool': pool_name,
50 'field': set_name,
51 # set-quota wants a string in case it has units in
52 'val': attributes[attr_name].__str__()
53 }))
54
55 # Renames come last (the preceeding commands reference the pool by its
56 # old name)
57 if 'name' in attributes:
58 commands.append(('osd pool rename', {
59 "srcpool": pool_name,
60 "destpool": attributes['name']
61 }))
62
63 return commands
64
65 def delete(self, pool_id):
66 # Resolve pool ID to name
67 pool_name = self._resolve_pool(pool_id)['pool_name']
68
69 # TODO: perhaps the REST API should have something in the body to
70 # make it slightly harder to accidentally delete a pool, to respect
71 # the severity of this operation since we're hiding the
72 # --yes-i-really-really-want-to stuff here
73 # TODO: handle errors in a way that caller can show to a user, e.g.
74 # if the name is wrong we should be sending a structured errors dict
75 # that they can use to associate the complaint with the 'name' field.
76 commands = [
77 ('osd pool delete', {'pool': pool_name, 'pool2': pool_name,
78 'sure': '--yes-i-really-really-mean-it'})]
79 return OsdMapModifyingRequest(
80 "Deleting pool '{name}'".format(name=pool_name),
81 commands)
82
83 def update(self, pool_id, attributes):
84 osd_map = rest_plugin().get_sync_object(OsdMap)
85 pool = self._resolve_pool(pool_id)
86 pool_name = pool['pool_name']
87
88 if 'pg_num' in attributes:
89 # Special case when setting pg_num: have to do some extra work
90 # to wait for PG creation between setting these two fields.
91 final_pg_count = attributes['pg_num']
92
93 if 'pgp_num' in attributes:
94 pgp_num = attributes['pgp_num']
95 del attributes['pgp_num']
96 else:
97 pgp_num = attributes['pg_num']
98 del attributes['pg_num']
99
100 pre_create_commands = self._pool_attribute_commands(pool_name,
101 attributes)
102
103 # This setting is new in Ceph Firefly, where it defaults to 32.
104 # For older revisions, we simply pretend that the setting exists
105 # with a default setting.
106 mon_osd_max_split_count = int(rest_plugin().get_sync_object(Config).data.get(
107 'mon_osd_max_split_count', LEGACY_MON_OSD_MAX_SPLIT_COUNT))
108 initial_pg_count = pool['pg_num']
109 n_osds = min(initial_pg_count, len(osd_map.osds_by_id))
110 # The rules about creating PGs:
111 # where N_osds = min(old_pg_count, osd_count)
112 # the number of new PGs divided by N_osds may not be greater
113 # than mon_osd_max_split_count
114 block_size = mon_osd_max_split_count * n_osds
115
116 return PgCreatingRequest(
117 "Growing pool '{name}' to {size} PGs".format(
118 name=pool_name, size=final_pg_count),
119 pre_create_commands,
120 pool_id, pool_name, pgp_num,
121 initial_pg_count, final_pg_count, block_size)
122 else:
123 commands = self._pool_attribute_commands(pool_name, attributes)
124 if not commands:
125 raise NotImplementedError(attributes)
126
127 # TODO: provide some machine-readable indication of which objects
128 # are affected by a particular request.
129 # Perhaps subclass Request for each type of object, and have that
130 # subclass provide both the patches->commands mapping and the
131 # human readable and machine readable descriptions of it?
132
133 # Objects may be decorated with 'id' from use in a bulk PATCH, but
134 # we don't want anything
135 # from this point onwards to see that.
136 if 'id' in attributes:
137 del attributes['id']
138 return OsdMapModifyingRequest(
139 "Modifying pool '{name}' ({attrs})".format(
140 name=pool_name, attrs=", ".join(
141 "%s=%s" % (k, v) for k, v in attributes.items())
142 ), commands)
143
144 def create(self, attributes):
145 commands = [('osd pool create', {'pool': attributes['name'],
146 'pg_num': attributes['pg_num']})]
147
148 # Which attributes must we set after the initial create?
149 post_create_attrs = attributes.copy()
150 del post_create_attrs['name']
151 del post_create_attrs['pg_num']
152 if 'pgp_num' in post_create_attrs:
153 del post_create_attrs['pgp_num']
154
155 commands.extend(self._pool_attribute_commands(
156 attributes['name'],
157 post_create_attrs
158 ))
159
160 log.debug("Post-create attributes: %s" % post_create_attrs)
161 log.debug("Commands: %s" % post_create_attrs)
162
163 return PoolCreatingRequest(
164 "Creating pool '{name}'".format(name=attributes['name']),
165 attributes['name'], commands)