1 # -*- coding: utf-8 -*-
2 from __future__
import absolute_import
7 from . import ApiController
, RESTController
, Endpoint
, ReadPermission
, Task
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
= [pool
for pool
in pools
if pool
['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 RbdConfiguration(pool_name
).set_configuration(configuration
)
87 self
._wait
_for
_pgs
(pool_name
)
89 @pool_task('create', {'pool_name': '{pool}'})
90 @handle_send_command_error('pool')
91 def create(self
, pool
, pg_num
, pool_type
, erasure_code_profile
=None, flags
=None,
92 application_metadata
=None, rule_name
=None, configuration
=None, **kwargs
):
93 ecp
= erasure_code_profile
if erasure_code_profile
else None
94 CephService
.send_command('mon', 'osd pool create', pool
=pool
, pg_num
=int(pg_num
),
95 pgp_num
=int(pg_num
), pool_type
=pool_type
, erasure_code_profile
=ecp
,
97 self
._set
_pool
_values
(pool
, application_metadata
, flags
, False, kwargs
)
98 RbdConfiguration(pool
).set_configuration(configuration
)
99 self
._wait
_for
_pgs
(pool
)
101 def _set_pool_values(self
, pool
, application_metadata
, flags
, update_existing
, kwargs
):
103 current_pool
= self
._get
(pool
)
104 if update_existing
and kwargs
.get('compression_mode') == 'unset':
105 self
._prepare
_compression
_removal
(current_pool
.get('options'), kwargs
)
106 if flags
and 'ec_overwrites' in flags
:
107 CephService
.send_command('mon', 'osd pool set', pool
=pool
, var
='allow_ec_overwrites',
109 if application_metadata
is not None:
110 def set_app(what
, app
):
111 CephService
.send_command('mon', 'osd pool application ' + what
, pool
=pool
, app
=app
,
112 yes_i_really_mean_it
=True)
114 original_app_metadata
= set(
115 current_pool
.get('application_metadata'))
117 original_app_metadata
= set()
119 for app
in original_app_metadata
- set(application_metadata
):
120 set_app('disable', app
)
121 for app
in set(application_metadata
) - original_app_metadata
:
122 set_app('enable', app
)
124 def set_key(key
, value
):
125 CephService
.send_command('mon', 'osd pool set', pool
=pool
, var
=key
, val
=str(value
))
127 for key
, value
in kwargs
.items():
134 set_key('pgp_num', value
)
136 CephService
.send_command('mon', 'osd pool rename', srcpool
=pool
, destpool
=destpool
)
138 def _prepare_compression_removal(self
, options
, kwargs
):
140 Presets payload with values to remove compression attributes in case they are not
143 In case compression is not needed the dashboard will send 'compression_mode' with the
146 :param options: All set options for the current pool.
147 :param kwargs: Payload of the PUT / POST call
149 if options
is not None:
150 def reset_arg(arg
, value
):
153 for arg
in ['compression_min_blob_size', 'compression_max_blob_size',
154 'compression_required_ratio']:
156 reset_arg('compression_algorithm', 'unset')
159 def _wait_for_pgs(cls
, pool_name
):
161 Keep the task waiting for until all pg changes are complete
162 :param pool_name: The name of the pool.
163 :type pool_name: string
165 current_pool
= cls
._get
(pool_name
)
166 initial_pgs
= int(current_pool
['pg_placement_num']) + int(current_pool
['pg_num'])
167 cls
._pg
_wait
_loop
(current_pool
, initial_pgs
)
170 def _pg_wait_loop(cls
, pool
, initial_pgs
):
172 Compares if all pg changes are completed, if not it will call itself
173 until all changes are completed.
174 :param pool: The dict that represents a pool.
176 :param initial_pgs: The pg and pg_num count before any change happened.
177 :type initial_pgs: int
179 if 'pg_num_target' in pool
:
180 target
= int(pool
['pg_num_target']) + int(pool
['pg_placement_num_target'])
181 current
= int(pool
['pg_placement_num']) + int(pool
['pg_num'])
182 if current
!= target
:
183 max_diff
= abs(target
- initial_pgs
)
184 diff
= max_diff
- abs(target
- current
)
185 percentage
= int(round(diff
/ float(max_diff
) * 100))
186 TaskManager
.current_task().set_progress(percentage
)
188 cls
._pg
_wait
_loop
(cls
._get
(pool
['pool_name']), initial_pgs
)
190 @RESTController.Resource()
192 def configuration(self
, pool_name
):
193 return RbdConfiguration(pool_name
).list()
197 def _info(self
, pool_name
=''):
198 # type: (str) -> dict
199 """Used by the create-pool dialog"""
201 def rules(pool_type
):
203 for r
in mgr
.get('osd_map_crush')['rules']
204 if r
['type'] == pool_type
]
207 return all(o
['osd_objectstore'] == 'bluestore'
208 for o
in mgr
.get('osd_metadata').values())
210 def compression_enum(conf_name
):
211 return [[v
for v
in o
['enum_values'] if len(v
) > 0]
212 for o
in mgr
.get('config_options')['options']
213 if o
['name'] == conf_name
][0]
216 "pool_names": [p
['pool_name'] for p
in self
._pool
_list
()],
217 "crush_rules_replicated": rules(1),
218 "crush_rules_erasure": rules(3),
219 "is_all_bluestore": all_bluestore(),
220 "osd_count": len(mgr
.get('osd_map')['osds']),
221 "bluestore_compression_algorithm": mgr
.get('config')['bluestore_compression_algorithm'],
222 "compression_algorithms": compression_enum('bluestore_compression_algorithm'),
223 "compression_modes": compression_enum('bluestore_compression_mode'),
227 result
['pool_options'] = RbdConfiguration(pool_name
).list()