Skip to content

Commit

Permalink
Merge pull request #20014 from liewegas/wip-rbd-validate
Browse files Browse the repository at this point in the history
common/options,librbd/Utils: refactor RBD feature validation

Reviewed-by: Jason Dillaman <dillaman@redhat.com>
  • Loading branch information
Jason Dillaman committed Jan 23, 2018
2 parents cd70aa2 + fa2faee commit 80f6c5c
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 67 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ set(libcommon_files
common/dns_resolve.cc
common/hostname.cc
common/util.cc
librbd/Features.cc
arch/probe.cc
${auth_files}
${mds_files})
Expand Down
79 changes: 14 additions & 65 deletions src/common/options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
// Definitions for enums
#include "common/perf_counters.h"

// rbd feature validation
#include "librbd/Features.h"


void Option::dump_value(const char *field_name,
const Option::value_t &v, Formatter *f) const
Expand Down Expand Up @@ -5817,71 +5820,17 @@ static std::vector<Option> get_rbd_options() {
"+4 -> exclusive-lock, +8 -> object-map, +16 -> fast-diff, "
"+32 -> deep-flatten, +64 -> journaling, +128 -> data-pool")
.set_safe()
.set_validator([](std::string *value, std::string *error_message){
static const std::map<std::string, uint64_t> FEATURE_MAP = {
{RBD_FEATURE_NAME_LAYERING, RBD_FEATURE_LAYERING},
{RBD_FEATURE_NAME_STRIPINGV2, RBD_FEATURE_STRIPINGV2},
{RBD_FEATURE_NAME_EXCLUSIVE_LOCK, RBD_FEATURE_EXCLUSIVE_LOCK},
{RBD_FEATURE_NAME_OBJECT_MAP, RBD_FEATURE_OBJECT_MAP},
{RBD_FEATURE_NAME_FAST_DIFF, RBD_FEATURE_FAST_DIFF},
{RBD_FEATURE_NAME_DEEP_FLATTEN, RBD_FEATURE_DEEP_FLATTEN},
{RBD_FEATURE_NAME_JOURNALING, RBD_FEATURE_JOURNALING},
{RBD_FEATURE_NAME_DATA_POOL, RBD_FEATURE_DATA_POOL},
};
static_assert((RBD_FEATURE_OPERATIONS << 1) > RBD_FEATURES_ALL,
"new RBD feature added");

// convert user-friendly comma delimited feature name list to a bitmask
// that is used by the librbd API
uint64_t features = 0;
error_message->clear();

try {
features = boost::lexical_cast<decltype(features)>(*value);

uint64_t unsupported_features = (features & ~RBD_FEATURES_ALL);
if (unsupported_features != 0ull) {
features &= RBD_FEATURES_ALL;

std::stringstream ss;
ss << "ignoring unknown feature mask 0x"
<< std::hex << unsupported_features;
*error_message = ss.str();
}
uint64_t internal_features = (features & RBD_FEATURES_INTERNAL);
if (internal_features != 0ULL) {
features &= ~RBD_FEATURES_INTERNAL;

std::stringstream ss;
ss << "ignoring internal feature mask 0x"
<< std::hex << internal_features;
*error_message = ss.str();
}
} catch (const boost::bad_lexical_cast& ) {
int r = 0;
std::vector<std::string> feature_names;
boost::split(feature_names, *value, boost::is_any_of(","));
for (auto feature_name: feature_names) {
boost::trim(feature_name);
auto feature_it = FEATURE_MAP.find(feature_name);
if (feature_it != FEATURE_MAP.end()) {
features += feature_it->second;
} else {
if (!error_message->empty()) {
*error_message += ", ";
}
*error_message += "ignoring unknown feature " + feature_name;
r = -EINVAL;
}
}

if (features == 0 && r == -EINVAL) {
features = RBD_FEATURES_DEFAULT;
}
}
*value = stringify(features);
return 0;
}),
.set_validator([](std::string *value, std::string *error_message) {
ostringstream ss;
uint64_t features = librbd::rbd_features_from_string(*value, &ss);
// Leave this in integer form to avoid breaking Cinder. Someday
// we would like to present this in string form instead...
*value = stringify(features);
if (ss.str().size()) {
return -EINVAL;
}
return 0;
}),

Option("rbd_op_threads", Option::TYPE_INT, Option::LEVEL_ADVANCED)
.set_default(1)
Expand Down
105 changes: 105 additions & 0 deletions src/librbd/Features.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>

#include "librbd/Features.h"
#include "include/rbd/features.h"

#include <map>
#include <vector>

static const std::map<std::string, uint64_t> RBD_FEATURE_MAP = {
{RBD_FEATURE_NAME_LAYERING, RBD_FEATURE_LAYERING},
{RBD_FEATURE_NAME_STRIPINGV2, RBD_FEATURE_STRIPINGV2},
{RBD_FEATURE_NAME_EXCLUSIVE_LOCK, RBD_FEATURE_EXCLUSIVE_LOCK},
{RBD_FEATURE_NAME_OBJECT_MAP, RBD_FEATURE_OBJECT_MAP},
{RBD_FEATURE_NAME_FAST_DIFF, RBD_FEATURE_FAST_DIFF},
{RBD_FEATURE_NAME_DEEP_FLATTEN, RBD_FEATURE_DEEP_FLATTEN},
{RBD_FEATURE_NAME_JOURNALING, RBD_FEATURE_JOURNALING},
{RBD_FEATURE_NAME_DATA_POOL, RBD_FEATURE_DATA_POOL},
};
static_assert((RBD_FEATURE_OPERATIONS << 1) > RBD_FEATURES_ALL,
"new RBD feature added");


namespace librbd {

std::string rbd_features_to_string(uint64_t features,
std::ostream *err)
{
std::string r;
for (auto& i : RBD_FEATURE_MAP) {
if (features & i.second) {
if (r.empty()) {
r += ",";
}
r += i.first;
features &= ~i.second;
}
}
if (err && features) {
*err << "ignoring unknown feature mask 0x"
<< std::hex << features << std::dec;
}
return r;
}

uint64_t rbd_features_from_string(const std::string& orig_value,
std::ostream *err)
{
uint64_t features = 0;
std::string value = orig_value;
boost::trim(value);

// empty string means default features
if (!value.size()) {
return RBD_FEATURES_DEFAULT;
}

try {
// numeric?
features = boost::lexical_cast<uint64_t>(value);

// drop unrecognized bits
uint64_t unsupported_features = (features & ~RBD_FEATURES_ALL);
if (unsupported_features != 0ull) {
features &= RBD_FEATURES_ALL;
if (err) {
*err << "ignoring unknown feature mask 0x"
<< std::hex << unsupported_features << std::dec;
}
}
uint64_t internal_features = (features & RBD_FEATURES_INTERNAL);
if (internal_features != 0ULL) {
features &= ~RBD_FEATURES_INTERNAL;
if (err) {
*err << "ignoring internal feature mask 0x"
<< std::hex << internal_features;
}
}
} catch (boost::bad_lexical_cast&) {
// feature name list?
bool errors = false;
std::vector<std::string> feature_names;
boost::split(feature_names, value, boost::is_any_of(","));
for (auto feature_name: feature_names) {
boost::trim(feature_name);
auto feature_it = RBD_FEATURE_MAP.find(feature_name);
if (feature_it != RBD_FEATURE_MAP.end()) {
features += feature_it->second;
} else if (err) {
if (errors) {
*err << ", ";
} else {
errors = true;
}
*err << "ignoring unknown feature " << feature_name;
}
}
}
return features;
}

} // namespace librbd
16 changes: 16 additions & 0 deletions src/librbd/Features.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab

#pragma once

#include <string>
#include <ostream>

namespace librbd {

std::string rbd_features_to_string(uint64_t features,
std::ostream *err);
uint64_t rbd_features_from_string(const std::string& value,
std::ostream *err);

} // librbd
6 changes: 4 additions & 2 deletions src/librbd/Utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "include/rbd/features.h"
#include "common/dout.h"
#include "librbd/ImageCtx.h"
#include "librbd/Features.h"

#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
Expand Down Expand Up @@ -66,10 +67,11 @@ std::string generate_image_id(librados::IoCtx &ioctx) {

uint64_t get_rbd_default_features(CephContext* cct)
{
auto str_val = cct->_conf->get_val<std::string>("rbd_default_features");
return boost::lexical_cast<uint64_t>(str_val);
auto value = cct->_conf->get_val<std::string>("rbd_default_features");
return librbd::rbd_features_from_string(value, nullptr);
}


bool calc_sparse_extent(const bufferptr &bp,
size_t sparse_size,
uint64_t length,
Expand Down

0 comments on commit 80f6c5c

Please sign in to comment.