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
7 from rest
.module
import global_instance
as rest_plugin
9 from rest
.logger
import logger
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"]
16 # In Ceph versions before mon_osd_max_split_count, assume it is set to this
17 LEGACY_MON_OSD_MAX_SPLIT_COUNT
= "32"
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
]
25 def _pool_attribute_commands(self
, pool_name
, attributes
):
27 for var
in POOL_PROPERTIES
:
31 # Special case for hashpspool, accepts 'true' from firefly
32 # onwards but requires 0 or 1 for dumpling, so just use the
34 if isinstance(val
, bool):
37 commands
.append(('osd pool set', {
43 # Quota setting ('osd pool set-quota') is separate to the main 'set'
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', {
51 # set-quota wants a string in case it has units in
52 'val': attributes
[attr_name
].__str
__()
55 # Renames come last (the preceeding commands reference the pool by its
57 if 'name' in attributes
:
58 commands
.append(('osd pool rename', {
60 "destpool": attributes
['name']
65 def delete(self
, pool_id
):
66 # Resolve pool ID to name
67 pool_name
= self
._resolve
_pool
(pool_id
)['pool_name']
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.
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
),
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']
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']
93 if 'pgp_num' in attributes
:
94 pgp_num
= attributes
['pgp_num']
95 del attributes
['pgp_num']
97 pgp_num
= attributes
['pg_num']
98 del attributes
['pg_num']
100 pre_create_commands
= self
._pool
_attribute
_commands
(pool_name
,
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
116 return PgCreatingRequest(
117 "Growing pool '{name}' to {size} PGs".format(
118 name
=pool_name
, size
=final_pg_count
),
120 pool_id
, pool_name
, pgp_num
,
121 initial_pg_count
, final_pg_count
, block_size
)
123 commands
= self
._pool
_attribute
_commands
(pool_name
, attributes
)
125 raise NotImplementedError(attributes
)
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?
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
:
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())
144 def create(self
, attributes
):
145 commands
= [('osd pool create', {'pool': attributes
['name'],
146 'pg_num': attributes
['pg_num']})]
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']
155 commands
.extend(self
._pool
_attribute
_commands
(
160 log
.debug("Post-create attributes: %s" % post_create_attrs
)
161 log
.debug("Commands: %s" % post_create_attrs
)
163 return PoolCreatingRequest(
164 "Creating pool '{name}'".format(name
=attributes
['name']),
165 attributes
['name'], commands
)