Skip to content

Commit

Permalink
mgr/dashboard: Add support for managing RBD QOS
Browse files Browse the repository at this point in the history
Signed-off-by: Patrick Nawracay <pnawracay@suse.com>
  • Loading branch information
p-se committed Dec 5, 2018
1 parent d8105e1 commit ce24065
Show file tree
Hide file tree
Showing 55 changed files with 1,597 additions and 132 deletions.
95 changes: 89 additions & 6 deletions src/pybind/mgr/dashboard/controllers/pool.py
Expand Up @@ -3,6 +3,7 @@


import cherrypy
import rbd

from . import ApiController, RESTController, Endpoint, ReadPermission, Task
from .. import mgr
Expand All @@ -11,11 +12,62 @@
from ..services.exception import handle_send_command_error
from ..tools import str_to_bool

from .. import logger


def pool_task(name, metadata, wait_for=2.0):
return Task("pool/{}".format(name), metadata, wait_for)


class PoolConfiguration(object):
_rbd = rbd.RBD()

@staticmethod
def _ensure_prefix(option):
# type: (str) -> str
return option if option.startswith('conf_') else 'conf_' + option

@classmethod
def list(cls, pool_name, filter_prefix=''):
# type: (str, str) -> [dict]
with mgr.rados.open_ioctx(pool_name) as ioctx:
pool_options = list(cls._rbd.config_list(ioctx))
return pool_options if not filter_prefix else \
[e for e in pool_options if e['name'].startswith(filter_prefix)]

@classmethod
def get(cls, pool_name, option_name):
# type: (str, str) -> str
option_name = cls._ensure_prefix(option_name)
with mgr.rados.open_ioctx(pool_name) as ioctx:
logger.debug('PCO: Getting %s for %s', option_name, pool_name)
return cls._rbd.pool_metadata_get(ioctx, option_name)

@classmethod
def set(cls, pool_name, option_name, option_value):
# type: (str, str, str) -> None
option_name = cls._ensure_prefix(option_name)
with mgr.rados.open_ioctx(pool_name) as ioctx:
logger.debug('PCO: Setting %s to %s for pool %s',
option_name, option_value, pool_name)
cls._rbd.pool_metadata_set(ioctx, option_name, option_value)

@classmethod
def remove(cls, pool_name, option_name):
"""
Removes an option by name. Will not raise an error, if the option hasn't been found.
type: (str, str) -> None
"""
option_name = cls._ensure_prefix(option_name)
with mgr.rados.open_ioctx(pool_name) as ioctx:
logger.debug('PCO: Removing %s from %s', option_name, pool_name)
try:
cls._rbd.pool_metadata_remove(ioctx, option_name)
except KeyError:
pass


@ApiController('/pool', Scope.POOL)
class Pool(RESTController):

Expand Down Expand Up @@ -67,7 +119,9 @@ def _get(self, pool_name, attrs=None, stats=False):

def get(self, pool_name, attrs=None, stats=False):
# type: (str, str, bool) -> dict
return self._get(pool_name, attrs, stats)
pool = self._get(pool_name, attrs, stats)
pool['configuration'] = PoolConfiguration.list(pool_name, 'rbd_qos')
return pool

@pool_task('delete', ['{pool_name}'])
@handle_send_command_error('pool')
Expand All @@ -76,19 +130,36 @@ def delete(self, pool_name):
yes_i_really_really_mean_it=True)

@pool_task('edit', ['{pool_name}'])
def set(self, pool_name, flags=None, application_metadata=None, **kwargs):
def set(self, pool_name, flags=None, application_metadata=None, configuration=None, **kwargs):
self._set_pool_values(pool_name, application_metadata, flags, True, kwargs)
self._update_pool_config_options(pool_name, configuration)

@pool_task('create', {'pool_name': '{pool}'})
@handle_send_command_error('pool')
def create(self, pool, pg_num, pool_type, erasure_code_profile=None, flags=None,
application_metadata=None, rule_name=None, **kwargs):
application_metadata=None, rule_name=None, configuration=None, **kwargs):
ecp = erasure_code_profile if erasure_code_profile else None
CephService.send_command('mon', 'osd pool create', pool=pool, pg_num=int(pg_num),
pgp_num=int(pg_num), pool_type=pool_type, erasure_code_profile=ecp,
rule=rule_name)

self._set_pool_values(pool, application_metadata, flags, False, kwargs)
self._update_pool_config_options(pool, configuration)

@staticmethod
def _update_pool_config_options(pool, pool_config_options):
"""
Set or delete configuration options. If the value is None, the configuration option will be
removed.
type: (str, [dict]) -> None
"""
if pool_config_options:
map(lambda e: e if e.startswith('conf_') else 'conf_' + e, pool_config_options)
for name, value in pool_config_options.items():
if value is None:
PoolConfiguration.remove(pool, name)
else:
value = str(value)
PoolConfiguration.set(pool, name, value)

def _set_pool_values(self, pool, application_metadata, flags, update_existing, kwargs):
update_name = False
Expand Down Expand Up @@ -137,10 +208,17 @@ def reset_arg(arg, value):
reset_arg(arg, '0')
reset_arg('compression_algorithm', 'unset')

@RESTController.Resource()
@ReadPermission
def configuration(self, pool_name):
return PoolConfiguration.list(pool_name, 'rbd_qos')

@Endpoint()
@ReadPermission
def _info(self):
def _info(self, pool_name=''):
# type: (str) -> dict
"""Used by the create-pool dialog"""

def rules(pool_type):
return [r
for r in mgr.get('osd_map_crush')['rules']
Expand All @@ -155,7 +233,7 @@ def compression_enum(conf_name):
for o in mgr.get('config_options')['options']
if o['name'] == conf_name][0]

return {
result = {
"pool_names": [p['pool_name'] for p in self._pool_list()],
"crush_rules_replicated": rules(1),
"crush_rules_erasure": rules(3),
Expand All @@ -165,3 +243,8 @@ def compression_enum(conf_name):
"compression_algorithms": compression_enum('bluestore_compression_algorithm'),
"compression_modes": compression_enum('bluestore_compression_mode'),
}

if pool_name:
result['pool_options'] = PoolConfiguration.list(pool_name, 'rbd_qos')

return result

0 comments on commit ce24065

Please sign in to comment.