From 98e061765177630f4814bf3ad0b511c70f7580cb Mon Sep 17 00:00:00 2001 From: Loic Dachary Date: Tue, 10 Jun 2014 17:17:26 +0200 Subject: [PATCH] erasure-code: separate profile from pool creation Instead of being hard coded, the erasure code profile is read from yaml as: erasure_code_profile: name: teuthologyprofile k: 2 m: 1 ruleset-failure-domain: osd Which translates into ceph osd erasure-code-profile set teuthologyprofile \ k=2 m=1 ruleset-failure-domain=osd The semantic of k/m etc. depends on the erasure code plugin, but it is common to use k as the dividing factor for each object and m as the number of coding chunks. Using a different set of parameters or a different plugin can be done without modifying the teuthology code: erasure_code_profile: name: LRCprofile plugin: LRC mapping: __DD__DD layers: [[ "_cDD_cDD", "" ], [ "cDDD____", "" ], [ "____cDDD", ""]] For backward compatibility, the default erasure code profile is set to k: 2 m: 1 ruleset-failure-domain: osd which matches the previously hardcoded default of the corresponding functions. Signed-off-by: Loic Dachary --- teuthology/task/ceph_manager.py | 27 +++++++++-------- teuthology/task/rados.py | 16 ++++++++-- teuthology/task/radosbench.py | 18 +++++++++-- teuthology/task_util/rados.py | 40 +++++++++++++++++++++---- teuthology/task_util/test/__init__.py | 0 teuthology/task_util/test/test_rados.py | 40 +++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 23 deletions(-) create mode 100644 teuthology/task_util/test/__init__.py create mode 100644 teuthology/task_util/test/test_rados.py diff --git a/teuthology/task/ceph_manager.py b/teuthology/task/ceph_manager.py index c059ec46f5..581c598608 100644 --- a/teuthology/task/ceph_manager.py +++ b/teuthology/task/ceph_manager.py @@ -10,6 +10,7 @@ import threading from teuthology import misc as teuthology from teuthology.task import ceph as ceph_task from teuthology.task.scrub import Scrubber +from teuthology.task_util.rados import cmd_erasure_code_profile class Thrasher: """ @@ -404,7 +405,6 @@ class CephManager: self.config = config self.controller = controller self.next_pool_id = 0 - self.created_erasure_pool = False if (logger): self.log = lambda x: logger.info(x) else: @@ -702,7 +702,16 @@ class CephManager: self.log(status) return status['pgmap']['num_pgs'] - def create_pool_with_unique_name(self, pg_num=16, ec_pool=False, ec_m=1, ec_k=2): + def create_erasure_code_profile(self, profile_name, profile): + """ + Create an erasure code profile name that can be used as a parameter + when creating an erasure coded pool. + """ + with self.lock: + args = cmd_erasure_code_profile(profile_name, profile) + self.raw_cluster_cmd(*args) + + def create_pool_with_unique_name(self, pg_num=16, erasure_code_profile_name=None): """ Create a pool named unique_pool_X where X is unique. """ @@ -713,12 +722,10 @@ class CephManager: self.create_pool( name, pg_num, - ec_pool=ec_pool, - ec_m=ec_m, - ec_k=ec_k) + erasure_code_profile=erasure_code_profile_name) return name - def create_pool(self, pool_name, pg_num=16, ec_pool=False, ec_m=1, ec_k=2): + def create_pool(self, pool_name, pg_num=16, erasure_code_profile_name=None): """ Create a pool named from the pool_name parameter. :param pool_name: name of the pool being created. @@ -729,12 +736,8 @@ class CephManager: assert isinstance(pg_num, int) assert pool_name not in self.pools self.log("creating pool_name %s"%(pool_name,)) - if ec_pool and not self.created_erasure_pool: - self.created_erasure_pool = True - self.raw_cluster_cmd('osd', 'erasure-code-profile', 'set', 'teuthologyprofile', 'ruleset-failure-domain=osd', 'm='+str(ec_m), 'k='+str(ec_k)) - - if ec_pool: - self.raw_cluster_cmd('osd', 'pool', 'create', pool_name, str(pg_num), str(pg_num), 'erasure', 'teuthologyprofile') + if erasure_code_profile_name: + self.raw_cluster_cmd('osd', 'pool', 'create', pool_name, str(pg_num), str(pg_num), 'erasure', erasure_code_profile_name) else: self.raw_cluster_cmd('osd', 'pool', 'create', pool_name, str(pg_num)) self.pools[pool_name] = pg_num diff --git a/teuthology/task/rados.py b/teuthology/task/rados.py index d24797472a..28cccf259f 100644 --- a/teuthology/task/rados.py +++ b/teuthology/task/rados.py @@ -29,6 +29,7 @@ def task(ctx, config): op_weights: runs: - the pool is remade between runs ec_pool: use an ec pool + erasure_code_profile: profile to use with the erasure coded pool pool_snaps: use pool snapshots instead of selfmanaged snapshots For example:: @@ -51,7 +52,12 @@ def task(ctx, config): snap_create: 3 rollback: 2 snap_remove: 0 - ec_pool: true + ec_pool: create an ec pool, defaults to False + erasure_code_profile: + name: teuthologyprofile + k: 2 + m: 1 + ruleset-failure-domain: osd pool_snaps: true runs: 10 - interactive: @@ -147,6 +153,12 @@ def task(ctx, config): clients = ['client.{id}'.format(id=id_) for id_ in teuthology.all_roles_of_type(ctx.cluster, 'client')] log.info('clients are %s' % clients) + if config.get('ec_pool', False): + profile = config.get('erasure_code_profile', {}) + profile_name = profile.get('name', 'teuthologyprofile') + ctx.manager.create_erasure_code_profile(profile_name, profile) + else: + profile_name = None for i in range(int(config.get('runs', '1'))): log.info("starting run %s out of %s", str(i), config.get('runs', '1')) tests = {} @@ -162,7 +174,7 @@ def task(ctx, config): if not pool and existing_pools: pool = existing_pools.pop() else: - pool = ctx.manager.create_pool_with_unique_name(ec_pool=config.get('ec_pool', False)) + pool = ctx.manager.create_pool_with_unique_name(erasure_code_profile_name=profile_name) created_pools.append(pool) (remote,) = ctx.cluster.only(role).remotes.iterkeys() diff --git a/teuthology/task/radosbench.py b/teuthology/task/radosbench.py index d2e75716e9..53a54f6e08 100644 --- a/teuthology/task/radosbench.py +++ b/teuthology/task/radosbench.py @@ -21,7 +21,12 @@ def task(ctx, config): time: pool: unique_pool: use a unique pool, defaults to False - ec_pool: create ec pool, defaults to False + ec_pool: create an ec pool, defaults to False + erasure_code_profile: + name: teuthologyprofile + k: 2 + m: 1 + ruleset-failure-domain: osd example: @@ -46,13 +51,20 @@ def task(ctx, config): id_ = role[len(PREFIX):] (remote,) = ctx.cluster.only(role).remotes.iterkeys() + if config.get('ec_pool', False): + profile = config.get('erasure_code_profile', {}) + profile_name = profile.get('name', 'teuthologyprofile') + ctx.manager.create_erasure_code_profile(profile_name, profile) + else: + profile_name = None + pool = 'data' if config.get('pool'): pool = config.get('pool') if pool is not 'data': - ctx.manager.create_pool(pool, ec_pool=config.get('ec_pool', False)) + ctx.manager.create_pool(pool, erasure_code_profile_name=profile_name) else: - pool = ctx.manager.create_pool_with_unique_name(ec_pool=config.get('ec_pool', False)) + pool = ctx.manager.create_pool_with_unique_name(erasure_code_profile_name=profile_name) proc = remote.run( args=[ diff --git a/teuthology/task_util/rados.py b/teuthology/task_util/rados.py index f6a806c95d..8cf7177f1c 100644 --- a/teuthology/task_util/rados.py +++ b/teuthology/task_util/rados.py @@ -24,12 +24,8 @@ def rados(ctx, remote, cmd, wait=True, check_status=False): else: return proc -def create_ec_pool(remote, name, profile_name, pgnum, m=1, k=2): - remote.run(args=[ - 'ceph', 'osd', 'erasure-code-profile', 'set', - profile_name, 'm=' + str(m), 'k=' + str(k), - 'ruleset-failure-domain=osd', - ]) +def create_ec_pool(remote, name, profile_name, pgnum, profile): + remote.run(args=cmd_erasure_code_profile(profile_name, profile)) remote.run(args=[ 'ceph', 'osd', 'pool', 'create', name, str(pgnum), str(pgnum), 'erasure', profile_name, @@ -48,3 +44,35 @@ def create_cache_pool(remote, base_name, cache_name, pgnum, size): 'ceph', 'osd', 'tier', 'add-cache', base_name, cache_name, str(size), ]) + +def cmd_erasure_code_profile(profile_name, profile): + """ + Return the shell command to run to create the erasure code profile + described by the profile parameter. + + :param profile_name: a string matching [A-Za-z0-9-_.]+ + :param profile: a map whose semantic depends on the erasure code plugin + :returns: a shell command as an array suitable for Remote.run + + If profile is {}, it is replaced with + + { 'k': '2', 'm': '1', 'ruleset-failure-domain': 'osd'} + + for backward compatibility. In previous versions of teuthology, + these values were hardcoded as function arguments and some yaml + files were designed with these implicit values. The teuthology + code should not know anything about the erasure code profile + content or semantic. The valid values and parameters are outside + its scope. + """ + + if profile == {}: + profile = { + 'k': '2', + 'm': '1', + 'ruleset-failure-domain': 'osd' + } + return [ + 'ceph', 'osd', 'erasure-code-profile', 'set', + profile_name + ] + [ key + '=' + value for key, value in profile.iteritems() ] diff --git a/teuthology/task_util/test/__init__.py b/teuthology/task_util/test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/teuthology/task_util/test/test_rados.py b/teuthology/task_util/test/test_rados.py new file mode 100644 index 0000000000..ee1cfa62ab --- /dev/null +++ b/teuthology/task_util/test/test_rados.py @@ -0,0 +1,40 @@ +# +# The MIT License +# +# Copyright (C) 2014 Cloudwatt +# +# Author: Loic Dachary +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +from .. import rados + +class TestRados(object): + + def test_cmd_erasure_code_profile(self): + name = 'NAME' + cmd = rados.cmd_erasure_code_profile(name, {}) + assert 'k=2' in cmd + assert name in cmd + cmd = rados.cmd_erasure_code_profile(name, { 'k': '88' }) + assert 'k=88' in cmd + assert name in cmd -- 2.39.5