]> git.apps.os.sepia.ceph.com Git - teuthology.git/commitdiff
erasure-code: separate profile from pool creation 271/head
authorLoic Dachary <loic@dachary.org>
Tue, 10 Jun 2014 15:17:26 +0000 (17:17 +0200)
committerLoic Dachary <loic@dachary.org>
Mon, 30 Jun 2014 20:43:57 +0000 (22:43 +0200)
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 <loic@dachary.org>
teuthology/task/ceph_manager.py
teuthology/task/rados.py
teuthology/task/radosbench.py
teuthology/task_util/rados.py
teuthology/task_util/test/__init__.py [new file with mode: 0644]
teuthology/task_util/test/test_rados.py [new file with mode: 0644]

index c059ec46f5a46d05458c84e172edb9d5a6468d0a..581c598608ba96089f34b6a4c8964a4519a8dd65 100644 (file)
@@ -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
index d24797472a6cd01fa5c16308a8025be629596b36..28cccf259f77579d489e423c8c22a07010e04287 100644 (file)
@@ -29,6 +29,7 @@ def task(ctx, config):
           op_weights: <dictionary mapping operation type to integer weight>
           runs: <number of times to run> - 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()
index d2e75716e9194a6014e29155460fe7e02805ace1..53a54f6e088c6f7ab54ff2b718084a75f770950d 100644 (file)
@@ -21,7 +21,12 @@ def task(ctx, config):
         time: <seconds to run>
         pool: <pool to use>
         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=[
index f6a806c95db2cf74f74026b2984b6ebbb989a09b..8cf7177f1cdaca0ef0158afa9f5862a9842ac4c8 100644 (file)
@@ -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 (file)
index 0000000..e69de29
diff --git a/teuthology/task_util/test/test_rados.py b/teuthology/task_util/test/test_rados.py
new file mode 100644 (file)
index 0000000..ee1cfa6
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  The MIT License
+#
+# Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+#  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