1 # -*- coding: utf-8 -*-
2 from __future__
import absolute_import
7 from . import ApiController
, RESTController
, Endpoint
, ReadPermission
, Task
, UiApiController
9 from ..security
import Scope
10 from ..services
.ceph_service
import CephService
11 from ..services
.rbd
import RbdConfiguration
12 from ..services
.exception
import handle_send_command_error
13 from ..tools
import str_to_bool
, TaskManager
16 def pool_task(name
, metadata
, wait_for
=2.0):
17 return Task("pool/{}".format(name
), metadata
, wait_for
)
20 @ApiController('/pool', Scope
.POOL
)
21 class Pool(RESTController
):
24 def _serialize_pool(pool
, attrs
):
25 if not attrs
or not isinstance(attrs
, list):
28 crush_rules
= {r
['rule_id']: r
["rule_name"] for r
in mgr
.get('osd_map_crush')['rules']}
35 res
[attr
] = {1: 'replicated', 3: 'erasure'}[pool
[attr
]]
36 elif attr
== 'crush_rule':
37 res
[attr
] = crush_rules
[pool
[attr
]]
38 elif attr
== 'application_metadata':
39 res
[attr
] = list(pool
[attr
].keys())
41 res
[attr
] = pool
[attr
]
43 # pool_name is mandatory
44 res
['pool_name'] = pool
['pool_name']
48 def _pool_list(cls
, attrs
=None, stats
=False):
50 attrs
= attrs
.split(',')
52 if str_to_bool(stats
):
53 pools
= CephService
.get_pool_list_with_stats()
55 pools
= CephService
.get_pool_list()
57 return [cls
._serialize
_pool
(pool
, attrs
) for pool
in pools
]
59 def list(self
, attrs
=None, stats
=False):
60 return self
._pool
_list
(attrs
, stats
)
63 def _get(cls
, pool_name
, attrs
=None, stats
=False):
64 # type: (str, str, bool) -> dict
65 pools
= cls
._pool
_list
(attrs
, stats
)
66 pool
= [p
for p
in pools
if p
['pool_name'] == pool_name
]
68 raise cherrypy
.NotFound('No such pool')
71 def get(self
, pool_name
, attrs
=None, stats
=False):
72 # type: (str, str, bool) -> dict
73 pool
= self
._get
(pool_name
, attrs
, stats
)
74 pool
['configuration'] = RbdConfiguration(pool_name
).list()
77 @pool_task('delete', ['{pool_name}'])
78 @handle_send_command_error('pool')
79 def delete(self
, pool_name
):
80 return CephService
.send_command('mon', 'osd pool delete', pool
=pool_name
, pool2
=pool_name
,
81 yes_i_really_really_mean_it
=True)
83 @pool_task('edit', ['{pool_name}'])
84 def set(self
, pool_name
, flags
=None, application_metadata
=None, configuration
=None, **kwargs
):
85 self
._set
_pool
_values
(pool_name
, application_metadata
, flags
, True, kwargs
)
86 if kwargs
.get('pool'):
87 pool_name
= kwargs
['pool']
88 RbdConfiguration(pool_name
).set_configuration(configuration
)
89 self
._wait
_for
_pgs
(pool_name
)
91 @pool_task('create', {'pool_name': '{pool}'})
92 @handle_send_command_error('pool')
93 def create(self
, pool
, pg_num
, pool_type
, erasure_code_profile
=None, flags
=None,
94 application_metadata
=None, rule_name
=None, configuration
=None, **kwargs
):
95 ecp
= erasure_code_profile
if erasure_code_profile
else None
96 CephService
.send_command('mon', 'osd pool create', pool
=pool
, pg_num
=int(pg_num
),
97 pgp_num
=int(pg_num
), pool_type
=pool_type
, erasure_code_profile
=ecp
,
99 self
._set
_pool
_values
(pool
, application_metadata
, flags
, False, kwargs
)
100 RbdConfiguration(pool
).set_configuration(configuration
)
101 self
._wait
_for
_pgs
(pool
)
103 def _set_pool_values(self
, pool
, application_metadata
, flags
, update_existing
, kwargs
):
105 current_pool
= self
._get
(pool
)
106 if update_existing
and kwargs
.get('compression_mode') == 'unset':
107 self
._prepare
_compression
_removal
(current_pool
.get('options'), kwargs
)
108 if flags
and 'ec_overwrites' in flags
:
109 CephService
.send_command('mon', 'osd pool set', pool
=pool
, var
='allow_ec_overwrites',
111 if application_metadata
is not None:
112 def set_app(what
, app
):
113 CephService
.send_command('mon', 'osd pool application ' + what
, pool
=pool
, app
=app
,
114 yes_i_really_mean_it
=True)
116 original_app_metadata
= set(
117 current_pool
.get('application_metadata'))
119 original_app_metadata
= set()
121 for app
in original_app_metadata
- set(application_metadata
):
122 set_app('disable', app
)
123 for app
in set(application_metadata
) - original_app_metadata
:
124 set_app('enable', app
)
126 def set_key(key
, value
):
127 CephService
.send_command('mon', 'osd pool set', pool
=pool
, var
=key
, val
=str(value
))
130 quotas
['max_objects'] = kwargs
.pop('quota_max_objects', None)
131 quotas
['max_bytes'] = kwargs
.pop('quota_max_bytes', None)
132 self
._set
_quotas
(pool
, quotas
)
134 for key
, value
in kwargs
.items():
141 set_key('pgp_num', value
)
143 CephService
.send_command('mon', 'osd pool rename', srcpool
=pool
, destpool
=destpool
)
145 def _set_quotas(self
, pool
, quotas
):
146 for field
, value
in quotas
.items():
147 if value
is not None:
148 CephService
.send_command('mon', 'osd pool set-quota',
149 pool
=pool
, field
=field
, val
=str(value
))
151 def _prepare_compression_removal(self
, options
, kwargs
):
153 Presets payload with values to remove compression attributes in case they are not
156 In case compression is not needed the dashboard will send 'compression_mode' with the
159 :param options: All set options for the current pool.
160 :param kwargs: Payload of the PUT / POST call
162 if options
is not None:
163 def reset_arg(arg
, value
):
166 for arg
in ['compression_min_blob_size', 'compression_max_blob_size',
167 'compression_required_ratio']:
169 reset_arg('compression_algorithm', 'unset')
172 def _wait_for_pgs(cls
, pool_name
):
174 Keep the task waiting for until all pg changes are complete
175 :param pool_name: The name of the pool.
176 :type pool_name: string
178 current_pool
= cls
._get
(pool_name
)
179 initial_pgs
= int(current_pool
['pg_placement_num']) + int(current_pool
['pg_num'])
180 cls
._pg
_wait
_loop
(current_pool
, initial_pgs
)
183 def _pg_wait_loop(cls
, pool
, initial_pgs
):
185 Compares if all pg changes are completed, if not it will call itself
186 until all changes are completed.
187 :param pool: The dict that represents a pool.
189 :param initial_pgs: The pg and pg_num count before any change happened.
190 :type initial_pgs: int
192 if 'pg_num_target' in pool
:
193 target
= int(pool
['pg_num_target']) + int(pool
['pg_placement_num_target'])
194 current
= int(pool
['pg_placement_num']) + int(pool
['pg_num'])
195 if current
!= target
:
196 max_diff
= abs(target
- initial_pgs
)
197 diff
= max_diff
- abs(target
- current
)
198 percentage
= int(round(diff
/ float(max_diff
) * 100))
199 TaskManager
.current_task().set_progress(percentage
)
201 cls
._pg
_wait
_loop
(cls
._get
(pool
['pool_name']), initial_pgs
)
203 @RESTController.Resource()
205 def configuration(self
, pool_name
):
206 return RbdConfiguration(pool_name
).list()
209 @UiApiController('/pool', Scope
.POOL
)
214 """Used by the create-pool dialog"""
215 osd_map_crush
= mgr
.get('osd_map_crush')
216 options
= mgr
.get('config_options')['options']
218 def rules(pool_type
):
220 for r
in osd_map_crush
['rules']
221 if r
['type'] == pool_type
]
224 return all(o
['osd_objectstore'] == 'bluestore'
225 for o
in mgr
.get('osd_metadata').values())
227 def get_config_option_enum(conf_name
):
228 return [[v
for v
in o
['enum_values'] if len(v
) > 0]
230 if o
['name'] == conf_name
][0]
232 profiles
= CephService
.get_erasure_code_profiles()
236 for p
in self
._pool
_list
():
237 name
= p
['pool_name']
238 pool_names
.append(name
)
239 rule
= p
['crush_rule']
240 if rule
in used_rules
:
241 used_rules
[rule
].append(name
)
243 used_rules
[rule
] = [name
]
244 profile
= p
['erasure_code_profile']
245 if profile
in used_profiles
:
246 used_profiles
[profile
].append(name
)
248 used_profiles
[profile
] = [name
]
250 mgr_config
= mgr
.get('config')
252 "pool_names": pool_names
,
253 "crush_rules_replicated": rules(1),
254 "crush_rules_erasure": rules(3),
255 "is_all_bluestore": all_bluestore(),
256 "osd_count": len(mgr
.get('osd_map')['osds']),
257 "bluestore_compression_algorithm": mgr_config
['bluestore_compression_algorithm'],
258 "compression_algorithms": get_config_option_enum('bluestore_compression_algorithm'),
259 "compression_modes": get_config_option_enum('bluestore_compression_mode'),
260 "pg_autoscale_default_mode": mgr_config
['osd_pool_default_pg_autoscale_mode'],
261 "pg_autoscale_modes": get_config_option_enum('osd_pool_default_pg_autoscale_mode'),
262 "erasure_code_profiles": profiles
,
263 "used_rules": used_rules
,
264 "used_profiles": used_profiles
,