]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: add config commands
authorMykola Golub <mgolub@suse.com>
Sun, 26 Aug 2018 08:27:47 +0000 (11:27 +0300)
committerMykola Golub <mgolub@suse.com>
Thu, 20 Sep 2018 07:35:18 +0000 (10:35 +0300)
Signed-off-by: Mykola Golub <mgolub@suse.com>
doc/man/8/rbd.rst
qa/workunits/rbd/cli_generic.sh
src/test/cli/rbd/help.t
src/tools/rbd/CMakeLists.txt
src/tools/rbd/action/Config.cc [new file with mode: 0644]

index 3268c922b2be6f9c5416baba11ad36e9d25e2614..1895ff99cd79c6666fc502ffcbe2a24bba7af967 100644 (file)
@@ -197,6 +197,30 @@ Commands
   The parent snapshot must be protected (see `rbd snap protect`).
   This requires image format 2.
 
+:command:`config image get` *image-spec* *key*
+  Get an image-level configuration override.
+
+:command:`config image list` [--format plain | json | xml] [--pretty-format] *image-spec* *key*
+  List image-level configuration overrides.
+
+:command:`config image set` *image-spec* *key* *value*
+  Set an image-level configuration override.
+
+:command:`config image remove` *image-spec* *key*
+  Remove an image-level configuration override.
+
+:command:`config pool get` *pool-name* *key*
+  Get a pool-level configuration override.
+
+:command:`config pool list` [--format plain | json | xml] [--pretty-format] *pool-name*
+  List pool-level configuration overrides.
+
+:command:`config pool set` *pool-name* *key* *value*
+  Set a pool-level configuration override.
+
+:command:`config pool remove` *pool-name* *key*
+  Remove a pool-level configuration override.
+
 :command:`cp` (*src-image-spec* | *src-snap-spec*) *dest-image-spec*
   Copy the content of a src-image into the newly created dest-image.
   dest-image will have the same size, object size, and image format as src-image.
index 24de58cbf3bb58d94c411b87113eb64cc1f22056..7b1f41cec2005d4f0e2b81abc7a0900c9a23e197 100755 (executable)
@@ -772,11 +772,37 @@ test_migration() {
     ceph osd pool rm rbd2 rbd2 --yes-i-really-really-mean-it
 }
 
+test_config() {
+    echo "testing config..."
+    remove_images
+
+    rbd config pool set rbd rbd_cache true
+    rbd config pool list rbd | grep '^rbd_cache * true * pool *$'
+    rbd config pool get rbd rbd_cache | grep '^true$'
+
+    rbd create $RBD_CREATE_ARGS -s 1 test1
+
+    rbd config image list rbd/test1 | grep '^rbd_cache * true * pool *$'
+    rbd config image set rbd/test1 rbd_cache false
+    rbd config image list rbd/test1 | grep '^rbd_cache * false * image *$'
+    rbd config image get rbd/test1 rbd_cache | grep '^false$'
+    rbd config image remove rbd/test1 rbd_cache
+    expect_fail rbd config image get rbd/test1 rbd_cache
+    rbd config image list rbd/test1 | grep '^rbd_cache * true * pool *$'
+
+    rbd config pool remove rbd rbd_cache
+    expect_fail rbd config pool get rbd rbd_cache
+    rbd config pool list rbd | grep '^rbd_cache * true * config *$'
+
+    rbd rm test1
+}
+
 test_pool_image_args
 test_rename
 test_ls
 test_remove
 test_migration
+test_config
 RBD_CREATE_ARGS=""
 test_others
 test_locking
index 846a323d9e4727a1ad44350ebc91923d36863872..ce3f66a762016938efcc8b97ce6f06e2b2abf242 100644 (file)
   
   Positional arguments:
     <command>
-      bench                               Simple benchmark.
-      children                            Display children of snapshot.
-      clone                               Clone a snapshot into a CoW child image.
-      copy (cp)                           Copy src image to dest.
-      create                              Create an empty image.
-      deep copy (deep cp)                 Deep copy src image to dest.
-      device list (showmapped)            List mapped rbd images.
-      device map (map)                    Map an image to a block device.
-      device unmap (unmap)                Unmap a rbd device.
-      diff                                Print extents that differ since a
-                                          previous snap, or image creation.
-      disk-usage (du)                     Show disk usage stats for pool, image
-                                          or snapshot.
-      export                              Export image to file.
-      export-diff                         Export incremental diff to file.
-      feature disable                     Disable the specified image feature.
-      feature enable                      Enable the specified image feature.
-      flatten                             Fill clone with parent data (make it
-                                          independent).
-      group create                        Create a group.
-      group image add                     Add an image to a group.
-      group image list (group image ls)   List images in a group.
-      group image remove (group image rm) Remove an image from a group.
-      group list (group ls)               List rbd groups.
-      group remove (group rm)             Delete a group.
-      group rename                        Rename a group within pool.
-      group snap create                   Make a snapshot of a group.
-      group snap list (group snap ls)     List snapshots of a group.
-      group snap remove (group snap rm)   Remove a snapshot from a group.
-      group snap rename                   Rename group's snapshot.
-      group snap rollback                 Rollback group to snapshot.
-      image-meta get                      Image metadata get the value associated
-                                          with the key.
-      image-meta list (image-meta ls)     Image metadata list keys with values.
-      image-meta remove (image-meta rm)   Image metadata remove the key and value
-                                          associated.
-      image-meta set                      Image metadata set key with value.
-      import                              Import image from file.
-      import-diff                         Import an incremental diff.
-      info                                Show information about image size,
-                                          striping, etc.
-      journal client disconnect           Flag image journal client as
-                                          disconnected.
-      journal export                      Export image journal.
-      journal import                      Import image journal.
-      journal info                        Show information about image journal.
-      journal inspect                     Inspect image journal for structural
-                                          errors.
-      journal reset                       Reset image journal.
-      journal status                      Show status of image journal.
-      list (ls)                           List rbd images.
-      lock add                            Take a lock on an image.
-      lock list (lock ls)                 Show locks held on an image.
-      lock remove (lock rm)               Release a lock on an image.
-      merge-diff                          Merge two diff exports together.
-      migration abort                     Cancel interrupted image migration.
-      migration commit                    Commit image migration.
-      migration execute                   Execute image migration.
-      migration prepare                   Prepare image migration.
-      mirror image demote                 Demote an image to non-primary for RBD
-                                          mirroring.
-      mirror image disable                Disable RBD mirroring for an image.
-      mirror image enable                 Enable RBD mirroring for an image.
-      mirror image promote                Promote an image to primary for RBD
-                                          mirroring.
-      mirror image resync                 Force resync to primary image for RBD
-                                          mirroring.
-      mirror image status                 Show RBD mirroring status for an image.
-      mirror pool demote                  Demote all primary images in the pool.
-      mirror pool disable                 Disable RBD mirroring by default within
-                                          a pool.
-      mirror pool enable                  Enable RBD mirroring by default within
-                                          a pool.
-      mirror pool info                    Show information about the pool
-                                          mirroring configuration.
-      mirror pool peer add                Add a mirroring peer to a pool.
-      mirror pool peer remove             Remove a mirroring peer from a pool.
-      mirror pool peer set                Update mirroring peer settings.
-      mirror pool promote                 Promote all non-primary images in the
-                                          pool.
-      mirror pool status                  Show status for all mirrored images in
-                                          the pool.
-      namespace create                    Create an RBD image namespace.
-      namespace list (namespace ls)       List RBD image namespaces.
-      namespace remove (namespace rm)     Remove an RBD image namespace.
-      object-map check                    Verify the object map is correct.
-      object-map rebuild                  Rebuild an invalid object map.
-      pool init                           Initialize pool for use by RBD.
-      remove (rm)                         Delete an image.
-      rename (mv)                         Rename image within pool.
-      resize                              Resize (expand or shrink) image.
-      snap create (snap add)              Create a snapshot.
-      snap limit clear                    Remove snapshot limit.
-      snap limit set                      Limit the number of snapshots.
-      snap list (snap ls)                 Dump list of image snapshots.
-      snap protect                        Prevent a snapshot from being deleted.
-      snap purge                          Delete all unprotected snapshots.
-      snap remove (snap rm)               Delete a snapshot.
-      snap rename                         Rename a snapshot.
-      snap rollback (snap revert)         Rollback image to snapshot.
-      snap unprotect                      Allow a snapshot to be deleted.
-      status                              Show the status of this image.
-      trash list (trash ls)               List trash images.
-      trash move (trash mv)               Move an image to the trash.
-      trash purge                         Remove all expired images from trash.
-      trash remove (trash rm)             Remove an image from trash.
-      trash restore                       Restore an image from trash.
-      watch                               Watch events on image.
+      bench                                Simple benchmark.
+      children                             Display children of snapshot.
+      clone                                Clone a snapshot into a CoW child
+                                           image.
+      config image get                     Get an image-level configuration
+                                           override.
+      config image list (config image ls)  List image-level configuration
+                                           overrides.
+      config image remove (config image rm)
+                                           Remove an image-level configuration
+                                           override.
+      config image set                     Set an image-level configuration
+                                           override.
+      config pool get                      Get a pool-level configuration
+                                           override.
+      config pool list (config pool ls)    List pool-level configuration
+                                           overrides.
+      config pool remove (config pool rm)  Remove a pool-level configuration
+                                           override.
+      config pool set                      Set a pool-level configuration
+                                           override.
+      copy (cp)                            Copy src image to dest.
+      create                               Create an empty image.
+      deep copy (deep cp)                  Deep copy src image to dest.
+      device list (showmapped)             List mapped rbd images.
+      device map (map)                     Map an image to a block device.
+      device unmap (unmap)                 Unmap a rbd device.
+      diff                                 Print extents that differ since a
+                                           previous snap, or image creation.
+      disk-usage (du)                      Show disk usage stats for pool, image
+                                           or snapshot.
+      export                               Export image to file.
+      export-diff                          Export incremental diff to file.
+      feature disable                      Disable the specified image feature.
+      feature enable                       Enable the specified image feature.
+      flatten                              Fill clone with parent data (make it
+                                           independent).
+      group create                         Create a group.
+      group image add                      Add an image to a group.
+      group image list (group image ls)    List images in a group.
+      group image remove (group image rm)  Remove an image from a group.
+      group list (group ls)                List rbd groups.
+      group remove (group rm)              Delete a group.
+      group rename                         Rename a group within pool.
+      group snap create                    Make a snapshot of a group.
+      group snap list (group snap ls)      List snapshots of a group.
+      group snap remove (group snap rm)    Remove a snapshot from a group.
+      group snap rename                    Rename group's snapshot.
+      group snap rollback                  Rollback group to snapshot.
+      image-meta get                       Image metadata get the value
+                                           associated with the key.
+      image-meta list (image-meta ls)      Image metadata list keys with values.
+      image-meta remove (image-meta rm)    Image metadata remove the key and
+                                           value associated.
+      image-meta set                       Image metadata set key with value.
+      import                               Import image from file.
+      import-diff                          Import an incremental diff.
+      info                                 Show information about image size,
+                                           striping, etc.
+      journal client disconnect            Flag image journal client as
+                                           disconnected.
+      journal export                       Export image journal.
+      journal import                       Import image journal.
+      journal info                         Show information about image journal.
+      journal inspect                      Inspect image journal for structural
+                                           errors.
+      journal reset                        Reset image journal.
+      journal status                       Show status of image journal.
+      list (ls)                            List rbd images.
+      lock add                             Take a lock on an image.
+      lock list (lock ls)                  Show locks held on an image.
+      lock remove (lock rm)                Release a lock on an image.
+      merge-diff                           Merge two diff exports together.
+      migration abort                      Cancel interrupted image migration.
+      migration commit                     Commit image migration.
+      migration execute                    Execute image migration.
+      migration prepare                    Prepare image migration.
+      mirror image demote                  Demote an image to non-primary for RBD
+                                           mirroring.
+      mirror image disable                 Disable RBD mirroring for an image.
+      mirror image enable                  Enable RBD mirroring for an image.
+      mirror image promote                 Promote an image to primary for RBD
+                                           mirroring.
+      mirror image resync                  Force resync to primary image for RBD
+                                           mirroring.
+      mirror image status                  Show RBD mirroring status for an image.
+      mirror pool demote                   Demote all primary images in the pool.
+      mirror pool disable                  Disable RBD mirroring by default
+                                           within a pool.
+      mirror pool enable                   Enable RBD mirroring by default within
+                                           a pool.
+      mirror pool info                     Show information about the pool
+                                           mirroring configuration.
+      mirror pool peer add                 Add a mirroring peer to a pool.
+      mirror pool peer remove              Remove a mirroring peer from a pool.
+      mirror pool peer set                 Update mirroring peer settings.
+      mirror pool promote                  Promote all non-primary images in the
+                                           pool.
+      mirror pool status                   Show status for all mirrored images in
+                                           the pool.
+      namespace create                     Create an RBD image namespace.
+      namespace list (namespace ls)        List RBD image namespaces.
+      namespace remove (namespace rm)      Remove an RBD image namespace.
+      object-map check                     Verify the object map is correct.
+      object-map rebuild                   Rebuild an invalid object map.
+      pool init                            Initialize pool for use by RBD.
+      remove (rm)                          Delete an image.
+      rename (mv)                          Rename image within pool.
+      resize                               Resize (expand or shrink) image.
+      snap create (snap add)               Create a snapshot.
+      snap limit clear                     Remove snapshot limit.
+      snap limit set                       Limit the number of snapshots.
+      snap list (snap ls)                  Dump list of image snapshots.
+      snap protect                         Prevent a snapshot from being deleted.
+      snap purge                           Delete all unprotected snapshots.
+      snap remove (snap rm)                Delete a snapshot.
+      snap rename                          Rename a snapshot.
+      snap rollback (snap revert)          Rollback image to snapshot.
+      snap unprotect                       Allow a snapshot to be deleted.
+      status                               Show the status of this image.
+      trash list (trash ls)                List trash images.
+      trash move (trash mv)                Move an image to the trash.
+      trash purge                          Remove all expired images from trash.
+      trash remove (trash rm)              Remove an image from trash.
+      trash restore                        Restore an image from trash.
+      watch                                Watch events on image.
   
   Optional arguments:
     -c [ --conf ] arg     path to cluster configuration
     (-) supports disabling-only on existing images
     (+) enabled by default for new images if features not specified
   
+  rbd help config image get
+  usage: rbd config image get [--pool <pool>] [--namespace <namespace>] 
+                              [--image <image>] 
+                              <image-spec> <key> 
+  
+  Get an image-level configuration override.
+  
+  Positional arguments
+    <image-spec>         image specification
+                         (example: [<pool-name>/[<namespace-name>/]]<image-name>)
+    <key>                config key
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --image arg          image name
+  
+  rbd help config image list
+  usage: rbd config image list [--pool <pool>] [--namespace <namespace>] 
+                               [--image <image>] [--format <format>] 
+                               [--pretty-format] 
+                               <image-spec> 
+  
+  List image-level configuration overrides.
+  
+  Positional arguments
+    <image-spec>         image specification
+                         (example: [<pool-name>/[<namespace-name>/]]<image-name>)
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --image arg          image name
+    --format arg         output format (plain, json, or xml) [default: plain]
+    --pretty-format      pretty formatting (json and xml)
+  
+  rbd help config image remove
+  usage: rbd config image remove [--pool <pool>] [--namespace <namespace>] 
+                                 [--image <image>] 
+                                 <image-spec> <key> 
+  
+  Remove an image-level configuration override.
+  
+  Positional arguments
+    <image-spec>         image specification
+                         (example: [<pool-name>/[<namespace-name>/]]<image-name>)
+    <key>                config key
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --image arg          image name
+  
+  rbd help config image set
+  usage: rbd config image set [--pool <pool>] [--namespace <namespace>] 
+                              [--image <image>] 
+                              <image-spec> <key> <value> 
+  
+  Set an image-level configuration override.
+  
+  Positional arguments
+    <image-spec>         image specification
+                         (example: [<pool-name>/[<namespace-name>/]]<image-name>)
+    <key>                config key
+    <value>              config value
+  
+  Optional arguments
+    -p [ --pool ] arg    pool name
+    --namespace arg      namespace name
+    --image arg          image name
+  
+  rbd help config pool get
+  usage: rbd config pool get 
+                             <pool-name> <key> 
+  
+  Get a pool-level configuration override.
+  
+  Positional arguments
+    <pool-name>          pool name
+    <key>                config key
+  
+  rbd help config pool list
+  usage: rbd config pool list [--format <format>] [--pretty-format] 
+                              <pool-name> 
+  
+  List pool-level configuration overrides.
+  
+  Positional arguments
+    <pool-name>          pool name
+  
+  Optional arguments
+    --format arg         output format (plain, json, or xml) [default: plain]
+    --pretty-format      pretty formatting (json and xml)
+  
+  rbd help config pool remove
+  usage: rbd config pool remove 
+                                <pool-name> <key> 
+  
+  Remove a pool-level configuration override.
+  
+  Positional arguments
+    <pool-name>          pool name
+    <key>                config key
+  
+  rbd help config pool set
+  usage: rbd config pool set 
+                             <pool-name> <key> <value> 
+  
+  Set a pool-level configuration override.
+  
+  Positional arguments
+    <pool-name>          pool name
+    <key>                config key
+    <value>              config value
+  
   rbd help copy
   usage: rbd copy [--pool <pool>] [--namespace <namespace>] [--image <image>] 
                   [--snap <snap>] [--dest-pool <dest-pool>] 
index 55bc116d9c99a4df9e2e7c306329f972e6bb86c9..76cc57904fd5dc1398dc8ff2742993b75bb21659 100644 (file)
@@ -8,6 +8,7 @@ set(rbd_srcs
   action/Bench.cc
   action/Children.cc
   action/Clone.cc
+  action/Config.cc
   action/Copy.cc
   action/Create.cc
   action/Device.cc
diff --git a/src/tools/rbd/action/Config.cc b/src/tools/rbd/action/Config.cc
new file mode 100644 (file)
index 0000000..ae1593a
--- /dev/null
@@ -0,0 +1,544 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/Formatter.h"
+#include "common/TextTable.h"
+#include "common/config_proxy.h"
+#include "common/errno.h"
+
+#include "tools/rbd/ArgumentTypes.h"
+#include "tools/rbd/Shell.h"
+#include "tools/rbd/Utils.h"
+
+#include <iostream>
+#include <boost/program_options.hpp>
+
+namespace rbd {
+namespace action {
+namespace config {
+
+namespace at = argument_types;
+namespace po = boost::program_options;
+
+namespace {
+
+const std::string METADATA_CONF_PREFIX = "conf_";
+const uint32_t MAX_KEYS = 64;
+
+void add_pool_option(boost::program_options::options_description *positional) {
+  positional->add_options()
+    ("pool-name", "pool name");
+}
+
+void add_key_option(po::options_description *positional) {
+  positional->add_options()
+    ("key", "config key");
+}
+
+int get_pool(const po::variables_map &vm, std::string *pool_name) {
+  *pool_name = utils::get_positional_argument(vm, 0);
+  if (pool_name->empty()) {
+    std::cerr << "rbd: pool name was not specified" << std::endl;
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+int get_key(const po::variables_map &vm, std::string *key) {
+  *key = utils::get_positional_argument(vm, 1);
+  if (key->empty()) {
+    std::cerr << "rbd: config key was not specified" << std::endl;
+    return -EINVAL;
+  }
+  return 0;
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const librbd::config_source_t& source) {
+  switch (source) {
+  case RBD_CONFIG_SOURCE_CONFIG:
+    os << "config";
+    break;
+  case RBD_CONFIG_SOURCE_POOL:
+    os << "pool";
+    break;
+  case RBD_CONFIG_SOURCE_IMAGE:
+    os << "image";
+    break;
+  default:
+    os << "unknown (" << static_cast<uint32_t>(source) << ")";
+    break;
+  }
+  return os;
+}
+
+} // anonymous namespace
+
+void get_pool_get_arguments(po::options_description *positional,
+                            po::options_description *options) {
+  add_pool_option(positional);
+  add_key_option(positional);
+}
+
+int execute_pool_get(const po::variables_map &vm,
+                     const std::vector<std::string> &ceph_global_init_args) {
+  std::string pool_name;
+  int r = get_pool(vm, &pool_name);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key;
+  r = get_key(vm, &key);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, "", &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+  std::string value;
+
+  r = rbd.pool_metadata_get(io_ctx, METADATA_CONF_PREFIX + key, &value);
+  if (r < 0) {
+    if (r == -ENOENT) {
+      std::cerr << "rbd: " << key << " is not set" << std::endl;
+    } else {
+      std::cerr << "rbd: failed to get " << key << ": " << cpp_strerror(r)
+                << std::endl;
+    }
+    return r;
+  }
+
+  std::cout << value << std::endl;
+  return 0;
+}
+
+void get_pool_set_arguments(po::options_description *positional,
+                            po::options_description *options) {
+  add_pool_option(positional);
+  add_key_option(positional);
+  positional->add_options()
+    ("value", "config value");
+}
+
+int execute_pool_set(const po::variables_map &vm,
+                     const std::vector<std::string> &ceph_global_init_args) {
+  std::string pool_name;
+  int r = get_pool(vm, &pool_name);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key;
+  r = get_key(vm, &key);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string value = utils::get_positional_argument(vm, 2);
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, "", &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+  r = rbd.pool_metadata_set(io_ctx, METADATA_CONF_PREFIX + key, value);
+  if (r < 0) {
+    std::cerr << "rbd: failed to set " << key << ": " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
+
+  return 0;
+}
+
+void get_pool_remove_arguments(po::options_description *positional,
+                               po::options_description *options) {
+  add_pool_option(positional);
+  add_key_option(positional);
+}
+
+int execute_pool_remove(const po::variables_map &vm,
+                        const std::vector<std::string> &ceph_global_init_args) {
+  std::string pool_name;
+  int r = get_pool(vm, &pool_name);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key;
+  r = get_key(vm, &key);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, "", &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  librbd::RBD rbd;
+  r = rbd.pool_metadata_remove(io_ctx, METADATA_CONF_PREFIX + key);
+  if (r < 0) {
+    std::cerr << "rbd: failed to remove " << key << ": " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
+
+  return 0;
+}
+
+void get_pool_list_arguments(po::options_description *positional,
+                             po::options_description *options) {
+  add_pool_option(positional);
+  at::add_format_options(options);
+}
+
+int execute_pool_list(const po::variables_map &vm,
+                      const std::vector<std::string> &ceph_global_init_args) {
+  std::string pool_name;
+  int r = get_pool(vm, &pool_name);
+  if (r < 0) {
+    return r;
+  }
+
+  at::Format::Formatter f;
+  r = utils::get_formatter(vm, &f);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, "", &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  TextTable tbl;
+  librbd::RBD rbd;
+  std::vector<librbd::config_option_t> options;
+
+  r = rbd.config_list(io_ctx, &options);
+  if (r < 0) {
+    std::cerr << "rbd: failed to list config: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
+
+  if (f) {
+    f->open_array_section("config");
+  } else {
+    tbl.define_column("Name", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("Value", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("Source", TextTable::LEFT, TextTable::LEFT);
+  }
+
+  for (auto &option : options) {
+    if (f) {
+      f->open_object_section("option");
+      f->dump_string("name", option.name);
+      f->dump_string("value", option.value);
+      f->dump_stream("source") << option.source;
+      f->close_section();
+    } else {
+      std::ostringstream source;
+      source << option.source;
+      tbl << option.name << option.value << source.str() << TextTable::endrow;
+    }
+  }
+
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  } else {
+    std::cout << std::endl << tbl;
+  }
+
+  return 0;
+}
+
+void get_image_get_arguments(po::options_description *positional,
+                             po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  add_key_option(positional);
+}
+
+int execute_image_get(const po::variables_map &vm,
+                      const std::vector<std::string> &ceph_global_init_args) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
+    &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
+    utils::SPEC_VALIDATION_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key;
+  r = get_key(vm, &key);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
+                                 false, &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string value;
+
+  r = image.metadata_get(METADATA_CONF_PREFIX + key, &value);
+  if (r < 0) {
+    if (r == -ENOENT) {
+      std::cerr << "rbd: " << key << " is not set" << std::endl;
+    } else {
+      std::cerr << "rbd: failed to get " << key << ": " << cpp_strerror(r)
+                << std::endl;
+    }
+    return r;
+  }
+
+  std::cout << value << std::endl;
+  return 0;
+}
+
+void get_image_set_arguments(po::options_description *positional,
+                             po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  add_key_option(positional);
+  positional->add_options()
+    ("value", "config value");
+}
+
+int execute_image_set(const po::variables_map &vm,
+                      const std::vector<std::string> &ceph_global_init_args) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
+    &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
+    utils::SPEC_VALIDATION_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key;
+  r = get_key(vm, &key);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string value = utils::get_positional_argument(vm, 2);
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
+                                 false, &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  r = image.metadata_set(METADATA_CONF_PREFIX + key, value);
+  if (r < 0) {
+    std::cerr << "rbd: failed to set " << key << ": " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
+
+  return 0;
+}
+
+void get_image_remove_arguments(po::options_description *positional,
+                                po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  add_key_option(positional);
+}
+
+int execute_image_remove(
+    const po::variables_map &vm,
+    const std::vector<std::string> &ceph_global_init_args) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
+    &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
+    utils::SPEC_VALIDATION_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key;
+  r = get_key(vm, &key);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
+                                 false, &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  r = image.metadata_remove(METADATA_CONF_PREFIX + key);
+  if (r < 0) {
+    std::cerr << "rbd: failed to remove " << key << ": " << cpp_strerror(r)
+              << std::endl;
+    return r;
+  }
+
+  return 0;
+}
+
+void get_image_list_arguments(po::options_description *positional,
+                              po::options_description *options) {
+  at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_format_options(options);
+}
+
+int execute_image_list(const po::variables_map &vm,
+                       const std::vector<std::string> &ceph_global_init_args) {
+  size_t arg_index = 0;
+  std::string pool_name;
+  std::string namespace_name;
+  std::string image_name;
+  std::string snap_name;
+  int r = utils::get_pool_image_snapshot_names(
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
+    &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
+    utils::SPEC_VALIDATION_NONE);
+  if (r < 0) {
+    return r;
+  }
+
+  at::Format::Formatter f;
+  r = utils::get_formatter(vm, &f);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  librbd::Image image;
+  r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
+                                 false, &rados, &io_ctx, &image);
+  if (r < 0) {
+    return r;
+  }
+
+  TextTable tbl;
+  std::vector<librbd::config_option_t> options;
+
+  r = image.config_list(&options);
+  if (r < 0) {
+    std::cerr << "rbd: failed to list config: " << cpp_strerror(r) << std::endl;
+    return r;
+  }
+
+  if (options.empty()) {
+    if (f == nullptr) {
+      std::cout << "There are no values" << std::endl;
+    }
+    return 0;
+  }
+
+  if (f) {
+    f->open_array_section("config");
+  } else {
+    tbl.define_column("Name", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("Value", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("Source", TextTable::LEFT, TextTable::LEFT);
+  }
+
+  for (auto &option : options) {
+    if (f) {
+      f->open_object_section("option");
+      f->dump_string("name", option.name);
+      f->dump_string("value", option.value);
+      f->dump_stream("source") << option.source;
+      f->close_section();
+    } else {
+      std::ostringstream source;
+      source << option.source;
+      tbl << option.name << option.value << source.str() << TextTable::endrow;
+    }
+  }
+
+  if (f == nullptr) {
+    bool single = (options.size() == 1);
+    std::cout << "There " << (single ? "is" : "are") << " " << options.size()
+              << " " << (single ? "value" : "values") << ":" << std::endl;
+  }
+
+  if (f) {
+    f->close_section();
+    f->flush(std::cout);
+  } else {
+    std::cout << std::endl << tbl;
+  }
+
+  return 0;
+}
+
+Shell::Action action_pool_get(
+  {"config", "pool", "get"}, {}, "Get a pool-level configuration override.", "",
+   &get_pool_get_arguments, &execute_pool_get);
+Shell::Action action_pool_set(
+  {"config", "pool", "set"}, {}, "Set a pool-level configuration override.", "",
+   &get_pool_set_arguments, &execute_pool_set);
+Shell::Action action_pool_remove(
+  {"config", "pool", "remove"}, {"config", "pool", "rm"},
+   "Remove a pool-level configuration override.", "",
+   &get_pool_remove_arguments, &execute_pool_remove);
+Shell::Action action_pool_list(
+  {"config", "pool", "list"}, {"config", "pool", "ls"},
+   "List pool-level configuration overrides.", "",
+   &get_pool_list_arguments, &execute_pool_list);
+
+Shell::Action action_image_get(
+  {"config", "image", "get"}, {}, "Get an image-level configuration override.",
+   "", &get_image_get_arguments, &execute_image_get);
+Shell::Action action_image_set(
+  {"config", "image", "set"}, {}, "Set an image-level configuration override.",
+   "", &get_image_set_arguments, &execute_image_set);
+Shell::Action action_image_remove(
+  {"config", "image", "remove"}, {"config", "image", "rm"},
+   "Remove an image-level configuration override.", "",
+   &get_image_remove_arguments, &execute_image_remove);
+Shell::Action action_image_list(
+  {"config", "image", "list"}, {"config", "image", "ls"},
+   "List image-level configuration overrides.", "",
+   &get_image_list_arguments, &execute_image_list);
+
+} // namespace config
+} // namespace action
+} // namespace rbd