master - config: cleanup default values for some configuration settings with array values
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=1545ebf938d3ea...
Commit: 1545ebf938d3eab5df5ab48b8258afc4f62b896b
Parent: 9465963faf485dc5ae58b1c5aafa02a615411572
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Tue Jun 23 13:18:53 2015 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Wed Jun 24 11:13:38 2015 +0200
config: cleanup default values for some configuration settings with array values
Do not use "#S" (blank string) as default value as that ends up with
'key = [ "" ]' to be generated which is not what we want in most cases.
Also, fix default values for global/{thin,cache}_{check,repair}_options
and avoid assigning blank values. For example, the thin_check_options
had this set as default value previously:
"#S" DEFAULT_THIN_CHECK_OPTION1 "#S" DEFAULT_THIN_CHECK_OPTION2
If any (or both) of DEFAULT_THIN_CHECK_OPTION* variables was set
to "", we ended up with clumsy default value generated like:
thin_check_options = [ "-q", "" ]
With this patch, we end up with correct:
thin_check_options = [ "-q" ]
or, if all options are undefined:
thin_check_options = [ ]
Which is the correct way to express this.
---
lib/config/config_settings.h | 20 ++++++++++----------
lib/config/defaults.h | 5 +++++
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index f8ea579..9335a7a 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -203,7 +203,7 @@ cfg(devices_external_device_info_source_CFG, "external_device_info_source", devi
"udev - Reuse existing udev database records. Applicable\n"
"only if LVM is compiled with udev support.\n")
-cfg_array(devices_preferred_names_CFG, "preferred_names", devices_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED , CFG_TYPE_STRING, "#S", vsn(1, 2, 19), NULL, 0, NULL,
+cfg_array(devices_preferred_names_CFG, "preferred_names", devices_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED , CFG_TYPE_STRING, NULL, vsn(1, 2, 19), NULL, 0, NULL,
"Select which path name to display for a block device.\n"
"If multiple path names exist for a block device,\n"
"and LVM needs to display a name for the device,\n"
@@ -250,7 +250,7 @@ cfg_array(devices_filter_CFG, "filter", devices_CFG_SECTION, CFG_DEFAULT_UNDEFIN
"filter = [ \"a|loop|\", \"r|.*|\" ]\n"
"Example:\n"
"Accept all loop devices and ide drives except hdc.\n"
- "filter =[ \"a|loop|\", \"r|/dev/hdc|\", \"a|/dev/ide|\", \"r|.*|\" ]\n"
+ "filter = [ \"a|loop|\", \"r|/dev/hdc|\", \"a|/dev/ide|\", \"r|.*|\" ]\n"
"Example:\n"
"Use anchors to be very specific.\n"
"filter = [ \"a|^/dev/hda8$|\", \"r|.*/|\" ]\n")
@@ -618,7 +618,7 @@ cfg(global_si_unit_consistency_CFG, "si_unit_consistency", global_CFG_SECTION, C
cfg(global_suffix_CFG, "suffix", global_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_BOOL, DEFAULT_SUFFIX, vsn(1, 0, 0), NULL, 0, NULL,
"Display unit suffix for sizes.\n"
"This setting has no effect if the units are in human-readable\n"
- "form (global/units=\"h\") in which case the suffix is always\n"
+ "form (global/units = \"h\") in which case the suffix is always\n"
"displayed.\n")
cfg(global_activation_CFG, "activation", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_ACTIVATION, vsn(1, 0, 0), NULL, 0, NULL,
@@ -855,7 +855,7 @@ cfg(global_thin_repair_executable_CFG, "thin_repair_executable", global_CFG_SECT
"Also see thin_repair_options.\n"
"(For thin tools, see thin_check_executable.)\n")
-cfg_array(global_thin_check_options_CFG, "thin_check_options", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, "#S" DEFAULT_THIN_CHECK_OPTION1 "#S" DEFAULT_THIN_CHECK_OPTION2, vsn(2, 2, 96), NULL, 0, NULL,
+cfg_array(global_thin_check_options_CFG, "thin_check_options", global_CFG_SECTION, DEFAULT_THIN_CHECK_OPTIONS_CONFIG ? CFG_DEFAULT_COMMENTED : CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, DEFAULT_THIN_CHECK_OPTIONS_CONFIG, vsn(2, 2, 96), NULL, 0, NULL,
"List of options passed to the thin_check command.\n"
"With thin_check version 2.1 or newer you can add\n"
"--ignore-non-fatal-errors to let it pass through\n"
@@ -863,10 +863,10 @@ cfg_array(global_thin_check_options_CFG, "thin_check_options", global_CFG_SECTIO
"With thin_check version 3.2 or newer you should add\n"
"--clear-needs-check-flag.\n")
-cfg_array(global_thin_repair_options_CFG, "thin_repair_options", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, "#S" DEFAULT_THIN_REPAIR_OPTIONS, vsn(2, 2, 100), NULL, 0, NULL,
+cfg_array(global_thin_repair_options_CFG, "thin_repair_options", global_CFG_SECTION, DEFAULT_THIN_REPAIR_OPTIONS_CONFIG ? CFG_DEFAULT_COMMENTED : CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, DEFAULT_THIN_REPAIR_OPTIONS_CONFIG, vsn(2, 2, 100), NULL, 0, NULL,
"List of options passed to the thin_repair command.\n")
-cfg_array(global_thin_disabled_features_CFG, "thin_disabled_features", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, "#S", vsn(2, 2, 99), NULL, 0, NULL,
+cfg_array(global_thin_disabled_features_CFG, "thin_disabled_features", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 99), NULL, 0, NULL,
"Features to not use in the thin driver.\n"
"This can be helpful for testing, or to avoid\n"
"using a feature that is causing problems.\n"
@@ -900,10 +900,10 @@ cfg(global_cache_repair_executable_CFG, "cache_repair_executable", global_CFG_SE
"Also see cache_repair_options.\n"
"(For cache tools, see cache_check_executable.)\n")
-cfg_array(global_cache_check_options_CFG, "cache_check_options", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, "#S" DEFAULT_CACHE_CHECK_OPTION1, vsn(2, 2, 108), NULL, 0, NULL,
+cfg_array(global_cache_check_options_CFG, "cache_check_options", global_CFG_SECTION, DEFAULT_CACHE_CHECK_OPTIONS_CONFIG ? CFG_DEFAULT_COMMENTED : CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, DEFAULT_CACHE_CHECK_OPTIONS_CONFIG, vsn(2, 2, 108), NULL, 0, NULL,
"List of options passed to the cache_check command.\n")
-cfg_array(global_cache_repair_options_CFG, "cache_repair_options", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, "#S" DEFAULT_CACHE_REPAIR_OPTIONS, vsn(2, 2, 108), NULL, 0, NULL,
+cfg_array(global_cache_repair_options_CFG, "cache_repair_options", global_CFG_SECTION, DEFAULT_CACHE_REPAIR_OPTIONS_CONFIG ? CFG_DEFAULT_COMMENTED : CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, DEFAULT_CACHE_REPAIR_OPTIONS_CONFIG, vsn(2, 2, 108), NULL, 0, NULL,
"List of options passed to the cache_repair command.\n")
cfg(global_system_id_source_CFG, "system_id_source", global_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_SYSTEM_ID_SOURCE, vsn(2, 2, 117), NULL, 0, NULL,
@@ -1206,7 +1206,7 @@ cfg_array(activation_mlock_filter_CFG, "mlock_filter", activation_CFG_SECTION, C
"locale-archive was found to make up over 80% of the memory\n"
"used by the process.\n"
"Example:\n"
- "mlock_filter=[ \"locale/locale-archive\", \"gconv/gconv-modules.cache\" ]\n")
+ "mlock_filter = [ \"locale/locale-archive\", \"gconv/gconv-modules.cache\" ]\n")
cfg(activation_use_mlockall_CFG, "use_mlockall", activation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_USE_MLOCKALL, vsn(2, 2, 62), NULL, 0, NULL,
"Use the old behavior of mlockall to pin all memory.\n"
@@ -1497,7 +1497,7 @@ cfg(local_system_id_CFG, "system_id", local_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_D
"Set the system_id to the string 'host1'.\n"
"system_id = \"host1\"\n")
-cfg_array(local_extra_system_ids_CFG, "extra_system_ids", local_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, "#S", vsn(2, 2, 117), NULL, 0, NULL,
+cfg_array(local_extra_system_ids_CFG, "extra_system_ids", local_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 117), NULL, 0, NULL,
"A list of extra VG system IDs the local host can access.\n"
"VGs with the system IDs listed here (in addition\n"
"to the host's own system ID) can be fully accessed\n"
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index 6793d01..d764ec9 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -78,12 +78,15 @@
#ifdef THIN_CHECK_NEEDS_CHECK
# define DEFAULT_THIN_CHECK_OPTION1 "-q"
# define DEFAULT_THIN_CHECK_OPTION2 "--clear-needs-check-flag"
+# define DEFAULT_THIN_CHECK_OPTIONS_CONFIG "#S" DEFAULT_THIN_CHECK_OPTION1 "#S" DEFAULT_THIN_CHECK_OPTION2
#else
# define DEFAULT_THIN_CHECK_OPTION1 "-q"
# define DEFAULT_THIN_CHECK_OPTION2 ""
+# define DEFAULT_THIN_CHECK_OPTIONS_CONFIG "#S" DEFAULT_THIN_CHECK_OPTION1
#endif
#define DEFAULT_THIN_REPAIR_OPTIONS ""
+#define DEFAULT_THIN_REPAIR_OPTIONS_CONFIG NULL
#define DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS 0
#define DEFAULT_THIN_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024) /* KB */
#define DEFAULT_THIN_POOL_MIN_METADATA_SIZE 2048 /* KB */
@@ -96,7 +99,9 @@
#define DEFAULT_POOL_METADATA_SPARE 1 /* thin + cache */
#define DEFAULT_CACHE_CHECK_OPTION1 "-q"
+#define DEFAULT_CACHE_CHECK_OPTIONS_CONFIG "#S" DEFAULT_CACHE_CHECK_OPTION1
#define DEFAULT_CACHE_REPAIR_OPTIONS ""
+#define DEFAULT_CACHE_REPAIR_OPTIONS_CONFIG NULL
#define DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS 0
#define DEFAULT_CACHE_POOL_CHUNK_SIZE 64 /* KB */
#define DEFAULT_CACHE_POOL_MIN_METADATA_SIZE 2048 /* KB */
8 years, 10 months
master - config: add support for config value formatting flags
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=9465963faf485d...
Commit: 9465963faf485dc5ae58b1c5aafa02a615411572
Parent: c5ba60827ea720679c70f51b6ee7e999750e44e3
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Tue Jun 23 13:02:45 2015 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Wed Jun 24 11:13:37 2015 +0200
config: add support for config value formatting flags
There are two basic groups of formatting flags (32 bits):
- common ones applicable for all config value types (lower 16 bits)
- type-related formatting flags (higher 16 bits)
With this patch, we initially support four new flags that
modify the the way the config value is displayed:
Common flags:
=============
DM_CONFIG_VALUE_FMT_COMMON_ARRAY - causes array config values
to be enclosed in "[ ]" even if there's only one item
(previously, there was no way to recognize an array with one
item and scalar value, hence array values with one member
were always displayed without "[ ]" which libdm accepted
when reading, but it may have been misleading for users)
DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACE - causes extra spaces to
be inserted in "key = value" (or key = [ value, value, ... ] in
case of arrays), compared to "key=value" seen on output before.
This makes the output more readable for users.
Type-related flags:
===================
DM_CONFIG_VALUE_FMT_INT_OCTAL - prints integers in octal form with
"0" as a prefix (libdm's config reading code can handle this via
strtol just fine so it's properly recognized as number in octal
form already if there's "0" used as prefix)
DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES - makes it possible to print
strings without enclosing " "
This patch also adds dm_config_value_set_format_flags and
dm_config_value_get_format_flags functions to set and get
these formatting flags.
---
lib/config/config.c | 32 ++++++++++++++++++-----
lib/config/config.h | 2 +
lib/config/config_settings.h | 1 +
libdm/.exported_symbols.DM_1_02_100 | 2 +
libdm/libdevmapper.h | 19 ++++++++++++++
libdm/libdm-config.c | 48 +++++++++++++++++++++++++++++------
6 files changed, 89 insertions(+), 15 deletions(-)
diff --git a/lib/config/config.c b/lib/config/config.c
index 32b3c66..6f9d413 100644
--- a/lib/config/config.c
+++ b/lib/config/config.c
@@ -667,7 +667,8 @@ static void _log_type_error(const char *path, cfg_def_type_t actual,
}
static struct dm_config_value *_get_def_array_values(struct dm_config_tree *cft,
- const cfg_def_item_t *def)
+ const cfg_def_item_t *def,
+ uint32_t format_flags)
{
char *enc_value, *token, *p, *r;
struct dm_config_value *array = NULL, *v = NULL, *oldv = NULL;
@@ -678,6 +679,7 @@ static struct dm_config_value *_get_def_array_values(struct dm_config_tree *cft,
return NULL;
}
array->type = DM_CFG_EMPTY_ARRAY;
+ dm_config_value_set_format_flags(array, format_flags);
return array;
}
@@ -710,6 +712,9 @@ static struct dm_config_value *_get_def_array_values(struct dm_config_tree *cft,
dm_free(enc_value);
return NULL;
}
+
+ dm_config_value_set_format_flags(v, format_flags);
+
if (oldv)
oldv->next = v;
if (!array)
@@ -839,7 +844,7 @@ static int _check_value_differs_from_default(struct cft_check_handle *handle,
}
if (!v_def && (def->type & CFG_TYPE_ARRAY)) {
- if (!(v_def_array = v_def_iter = _get_def_array_values(handle->cft, def)))
+ if (!(v_def_array = v_def_iter = _get_def_array_values(handle->cft, def, 0)))
return_0;
do {
/* iterate over each element of the array and check its value */
@@ -1739,15 +1744,19 @@ static struct dm_config_node *_add_def_node(struct dm_config_tree *cft,
{
struct dm_config_node *cn;
const char *str;
+ uint32_t format_flags = 0;
if (!(cn = dm_config_create_node(cft, def->name))) {
log_error("Failed to create default config setting node.");
return NULL;
}
- if (!(def->type & CFG_TYPE_SECTION) && (!(cn->v = dm_config_create_value(cft)))) {
- log_error("Failed to create default config setting node value.");
- return NULL;
+ if (!(def->type & CFG_TYPE_SECTION) && !(def->type & CFG_TYPE_ARRAY)) {
+ if (!(cn->v = dm_config_create_value(cft))) {
+ log_error("Failed to create default config setting node value.");
+ return NULL;
+ }
+ format_flags |= DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES;
}
cn->id = def->id;
@@ -1755,6 +1764,9 @@ static struct dm_config_node *_add_def_node(struct dm_config_tree *cft,
if (spec->unconfigured && def->default_unconfigured_value.v_UNCONFIGURED) {
cn->v->type = DM_CFG_STRING;
cn->v->v.str = cfg_def_get_default_unconfigured_value_hint(spec->cmd, def);
+ if (def->type != CFG_TYPE_STRING)
+ format_flags |= DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES;
+ dm_config_value_set_format_flags(cn->v, format_flags);
} else if (!(def->type & CFG_TYPE_ARRAY)) {
switch (def->type) {
case CFG_TYPE_SECTION:
@@ -1767,6 +1779,8 @@ static struct dm_config_node *_add_def_node(struct dm_config_tree *cft,
case CFG_TYPE_INT:
cn->v->type = DM_CFG_INT;
cn->v->v.i = cfg_def_get_default_value_hint(spec->cmd, def, CFG_TYPE_INT, NULL);
+ if (def->flags & CFG_FORMAT_INT_OCTAL)
+ format_flags |= DM_CONFIG_VALUE_FMT_INT_OCTAL;
break;
case CFG_TYPE_FLOAT:
cn->v->type = DM_CFG_FLOAT;
@@ -1783,8 +1797,12 @@ static struct dm_config_node *_add_def_node(struct dm_config_tree *cft,
return NULL;
break;
}
- } else
- cn->v = _get_def_array_values(cft, def);
+ dm_config_value_set_format_flags(cn->v, format_flags);
+ } else {
+ format_flags |= (DM_CONFIG_VALUE_FMT_COMMON_ARRAY |
+ DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES);
+ cn->v = _get_def_array_values(cft, def, format_flags);
+ }
cn->child = NULL;
if (parent) {
diff --git a/lib/config/config.h b/lib/config/config.h
index 0b7221c..b03ad6b 100644
--- a/lib/config/config.h
+++ b/lib/config/config.h
@@ -119,6 +119,8 @@ typedef union {
#define CFG_DEFAULT_RUN_TIME 0x100
/* whether the configuration setting is disabled (and hence defaults always used) */
#define CFG_DISABLED 0x200
+/* whether to print integers in octal form (prefixed by "0") */
+#define CFG_FORMAT_INT_OCTAL 0x400
/* configuration definition item structure */
typedef struct cfg_def_item {
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index 5c7b132..f8ea579 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -50,6 +50,7 @@
* CFG_DEFAULT_UNDEFINED - node's default value is undefined (depends on other system/kernel values outside of lvm)
* CFG_DEFAULT_COMMENTED - node's default value is commented out on output
* CFG_DISABLED - configuration is disabled (defaults always used)
+ * CFG_FORMAT_INT_OCTAL - print integer number in octal form (also prefixed by "0")
*
* type: Allowed type for the value of simple configuation setting, one of:
* CFG_TYPE_BOOL
diff --git a/libdm/.exported_symbols.DM_1_02_100 b/libdm/.exported_symbols.DM_1_02_100
new file mode 100644
index 0000000..00d7d5b
--- /dev/null
+++ b/libdm/.exported_symbols.DM_1_02_100
@@ -0,0 +1,2 @@
+dm_config_value_set_format_flags
+dm_config_value_get_format_flags
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index cbd4f3d..7476aef 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -1847,6 +1847,7 @@ struct dm_config_value {
} v;
struct dm_config_value *next; /* For arrays */
+ uint32_t format_flags;
};
struct dm_config_node {
@@ -1949,6 +1950,24 @@ struct dm_config_node *dm_config_create_node(struct dm_config_tree *cft, const c
struct dm_config_value *dm_config_create_value(struct dm_config_tree *cft);
struct dm_config_node *dm_config_clone_node(struct dm_config_tree *cft, const struct dm_config_node *cn, int siblings);
+/*
+ * Common formatting flags applicable to all config node types (lower 16 bits).
+ */
+#define DM_CONFIG_VALUE_FMT_COMMON_ARRAY 0x00000001 /* value is array */
+#define DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES 0x00000002 /* add spaces in "key = value" pairs in constrast to "key=value" for better readability */
+
+/*
+ * Type-related config node formatting flags (higher 16 bits).
+ */
+/* int-related formatting flags */
+#define DM_CONFIG_VALUE_FMT_INT_OCTAL 0x00010000 /* print number in octal form */
+
+/* string-related formatting flags */
+#define DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES 0x00010000 /* do not print quotes around string value */
+
+void dm_config_value_set_format_flags(struct dm_config_value *cv, uint32_t format_flags);
+uint32_t dm_config_value_get_format_flags(struct dm_config_value *cv);
+
struct dm_pool *dm_config_memory(struct dm_config_tree *cft);
/* Udev device directory. */
diff --git a/libdm/libdm-config.c b/libdm/libdm-config.c
index f7b07a5..7b28972 100644
--- a/libdm/libdm-config.c
+++ b/libdm/libdm-config.c
@@ -273,11 +273,13 @@ static int _line_end(const struct dm_config_node *cn, struct config_output *out)
static int _write_value(struct config_output *out, const struct dm_config_value *v)
{
char *buf;
+ const char *s;
switch (v->type) {
case DM_CFG_STRING:
buf = alloca(dm_escaped_len(v->v.str));
- line_append("\"%s\"", dm_escape_double_quotes(buf, v->v.str));
+ s = (v->format_flags & DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES) ? "" : "\"";
+ line_append("%s%s%s", s, dm_escape_double_quotes(buf, v->v.str), s);
break;
case DM_CFG_FLOAT:
@@ -285,11 +287,15 @@ static int _write_value(struct config_output *out, const struct dm_config_value
break;
case DM_CFG_INT:
- line_append("%" PRId64, v->v.i);
+ if (v->format_flags & DM_CONFIG_VALUE_FMT_INT_OCTAL)
+ line_append("0%" PRIo64, v->v.i);
+ else
+ line_append("%" PRId64, v->v.i);
break;
case DM_CFG_EMPTY_ARRAY:
- line_append("[]");
+ s = (v->format_flags & DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES) ? " " : "";
+ line_append("[%s]", s);
break;
default:
@@ -303,6 +309,8 @@ static int _write_value(struct config_output *out, const struct dm_config_value
static int _write_config(const struct dm_config_node *n, int only_one,
struct config_output *out, int level)
{
+ const char *extra_space;
+ int format_array;
char space[MAX_INDENT + 1];
int l = (level < MAX_INDENT) ? level : MAX_INDENT;
int i;
@@ -316,6 +324,9 @@ static int _write_config(const struct dm_config_node *n, int only_one,
space[i] = '\0';
do {
+ extra_space = (n->v && (n->v->format_flags & DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES)) ? " " : "";
+ format_array = (n->v && (n->v->format_flags & DM_CONFIG_VALUE_FMT_COMMON_ARRAY));
+
if (out->spec && out->spec->prefix_fn)
out->spec->prefix_fn(n, space, out->baton);
@@ -341,20 +352,25 @@ static int _write_config(const struct dm_config_node *n, int only_one,
} else {
/* it's a value */
const struct dm_config_value *v = n->v;
- line_append("=");
+ line_append("%s=%s", extra_space, extra_space);
if (v->next) {
- line_append("[");
+ line_append("[%s", extra_space);
while (v && v->type != DM_CFG_EMPTY_ARRAY) {
if (!_write_value(out, v))
return_0;
v = v->next;
if (v && v->type != DM_CFG_EMPTY_ARRAY)
- line_append(", ");
+ line_append(",%s", extra_space);
}
- line_append("]");
- } else
+ line_append("%s]", extra_space);
+ } else {
+ if (format_array && (v->type != DM_CFG_EMPTY_ARRAY))
+ line_append("[%s", extra_space);
if (!_write_value(out, v))
return_0;
+ if (format_array && (v->type != DM_CFG_EMPTY_ARRAY))
+ line_append("%s]", extra_space);
+ }
}
if (!_line_end(n, out))
return_0;
@@ -1328,6 +1344,22 @@ struct dm_config_value *dm_config_create_value(struct dm_config_tree *cft)
return _create_value(cft->mem);
}
+void dm_config_value_set_format_flags(struct dm_config_value *cv, uint32_t format_flags)
+{
+ if (!cv)
+ return;
+
+ cv->format_flags = format_flags;
+}
+
+uint32_t dm_config_value_get_format_flags(struct dm_config_value *cv)
+{
+ if (!cv)
+ return 0;
+
+ return cv->format_flags;
+}
+
struct dm_pool *dm_config_memory(struct dm_config_tree *cft)
{
return cft->mem;
8 years, 10 months
master - libdaemon: allow main processing function to be specified
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=c5ba60827ea720...
Commit: c5ba60827ea720679c70f51b6ee7e999750e44e3
Parent: ba2b701f2cc98eb8e4049866f3d1cc77b61b4abd
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Dec 2 14:08:58 2014 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Tue Jun 23 16:55:45 2015 -0500
libdaemon: allow main processing function to be specified
---
libdaemon/server/daemon-server.c | 8 +++++++-
libdaemon/server/daemon-server.h | 1 +
2 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/libdaemon/server/daemon-server.c b/libdaemon/server/daemon-server.c
index bedd3b2..ef0b964 100644
--- a/libdaemon/server/daemon-server.c
+++ b/libdaemon/server/daemon-server.c
@@ -604,6 +604,12 @@ void daemon_start(daemon_state s)
if (!s.foreground)
kill(getppid(), SIGTERM);
+ if (s.daemon_main) {
+ if (!s.daemon_main(&s))
+ failed = 1;
+ goto out;
+ }
+
if (s.daemon_init)
if (!s.daemon_init(&s))
failed = 1;
@@ -635,7 +641,7 @@ void daemon_start(daemon_state s)
INFO(&s, "%s waiting for client threads to finish", s.name);
reap(s, 1);
-
+out:
/* If activated by systemd, do not unlink the socket - systemd takes care of that! */
if (!_systemd_activation && s.socket_fd >= 0)
if (unlink(s.socket_path))
diff --git a/libdaemon/server/daemon-server.h b/libdaemon/server/daemon-server.h
index 820e5e0..b235edc 100644
--- a/libdaemon/server/daemon-server.h
+++ b/libdaemon/server/daemon-server.h
@@ -107,6 +107,7 @@ typedef struct daemon_state {
handle_request handler;
int (*daemon_init)(struct daemon_state *st);
int (*daemon_fini)(struct daemon_state *st);
+ int (*daemon_main)(struct daemon_state *st);
/* Global runtime info maintained by the framework. */
int socket_fd;
8 years, 10 months
master - doc: mention new invalid states in lvmetad_design
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=ba2b701f2cc98e...
Commit: ba2b701f2cc98eb8e4049866f3d1cc77b61b4abd
Parent: c23e7ff2a02c65619d05eb525adaaf9336de2692
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Jun 23 16:48:28 2015 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Tue Jun 23 16:48:28 2015 -0500
doc: mention new invalid states in lvmetad_design
---
doc/lvmetad_design.txt | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/doc/lvmetad_design.txt b/doc/lvmetad_design.txt
index 3b336ec..1961cfb 100644
--- a/doc/lvmetad_design.txt
+++ b/doc/lvmetad_design.txt
@@ -137,6 +137,17 @@ hosts. Overall, this is not hard, but the devil is in the details. I would
possibly disable lvmetad for clustered volume groups in the first phase and
only proceed when the local mode is robust and well tested.
+With lvmlockd, lvmetad state is kept up to date by flagging either an
+individual VG as "invalid", or the global state as "invalid". When either
+the VG or the global state are read, this invalid flag is returned along
+with the data. The client command can check for this invalid state and
+decide to read the information from disk rather than use the stale cached
+data. After the latest data is read from disk, the command may choose to
+send it to lvmetad to update the cache. lvmlockd uses version numbers
+embedded in its VG and global locks to detect when cached data becomes
+invalid, and it then tells lvmetad to set the related invalid flag.
+dct, 2015-06-23
+
Protocol & co.
--------------
8 years, 10 months
master - Reread global state the lvmetad copy is stale
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=c23e7ff2a02c65...
Commit: c23e7ff2a02c65619d05eb525adaaf9336de2692
Parent: 679b6b5b29b2257672087f387ef04bc4c8a4cca7
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Fri Nov 7 12:51:42 2014 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Tue Jun 23 16:36:40 2015 -0500
Reread global state the lvmetad copy is stale
This is the client side handling of the global_invalid state
added to lvmetad in commit c595b50cec8a6b95c6ac4988912d1412f3cc0237.
The function added here:
. checks if the global state in lvmetad is invalid
. if so, scans disks to update the state in lvmetad
. clears the global_invalid flag in lvmetad
. updates the local udev db to reflect any changes
---
lib/cache/lvmetad.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/cache/lvmetad.h | 2 +
2 files changed, 298 insertions(+), 0 deletions(-)
diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c
index 3c07cd1..01c3f08 100644
--- a/lib/cache/lvmetad.c
+++ b/lib/cache/lvmetad.c
@@ -1283,3 +1283,299 @@ int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg)
return result;
}
+
+/*
+ * Records the state of cached PVs in lvmetad so we can look for changes
+ * after rescanning.
+ */
+struct pv_cache_list {
+ struct dm_list list;
+ dev_t devt;
+ struct id pvid;
+ const char *vgid;
+ unsigned found : 1;
+ unsigned update_udev : 1;
+};
+
+/*
+ * Get the list of PVs known to lvmetad.
+ */
+static int _lvmetad_get_pv_cache_list(struct cmd_context *cmd, struct dm_list *pvc_list)
+{
+ daemon_reply reply;
+ struct dm_config_node *cn;
+ struct pv_cache_list *pvcl;
+ const char *pvid_txt;
+ const char *vgid;
+
+ if (!lvmetad_active())
+ return 1;
+
+ log_debug_lvmetad("Asking lvmetad for complete list of known PVs");
+ reply = _lvmetad_send("pv_list", NULL);
+ if (!_lvmetad_handle_reply(reply, "list PVs", "", NULL)) {
+ log_error("lvmetad message failed.");
+ daemon_reply_destroy(reply);
+ return_0;
+ }
+
+ if ((cn = dm_config_find_node(reply.cft->root, "physical_volumes"))) {
+ for (cn = cn->child; cn; cn = cn->sib) {
+ if (!(pvcl = dm_pool_zalloc(cmd->mem, sizeof(*pvcl)))) {
+ log_error("pv_cache_list allocation failed.");
+ return 0;
+ }
+
+ pvid_txt = cn->key;
+ if (!id_read_format(&pvcl->pvid, pvid_txt)) {
+ stack;
+ continue;
+ }
+
+ pvcl->devt = dm_config_find_int(cn->child, "device", 0);
+
+ if ((vgid = dm_config_find_str(cn->child, "vgid", NULL)))
+ pvcl->vgid = dm_pool_strdup(cmd->mem, vgid);
+
+ dm_list_add(pvc_list, &pvcl->list);
+ }
+ }
+
+ daemon_reply_destroy(reply);
+
+ return 1;
+}
+
+/*
+ * Opening the device RDWR should trigger a udev db update.
+ * FIXME: is there a better way to update the udev db than
+ * doing an open/close of the device?
+ */
+static void _update_pv_in_udev(struct cmd_context *cmd, dev_t devt)
+{
+ struct device *dev;
+
+ log_debug_devs("device %d:%d open to update udev",
+ (int)MAJOR(devt), (int)MINOR(devt));
+
+ if (!(dev = dev_cache_get_by_devt(devt, cmd->lvmetad_filter))) {
+ log_error("_update_pv_in_udev no dev found");
+ return;
+ }
+
+ if (!dev_open(dev))
+ return;
+ dev_close(dev);
+}
+
+/*
+ * Compare before and after PV lists from before/after rescanning,
+ * and update udev db for changes.
+ *
+ * For PVs that have changed pvid or vgid in lvmetad from rescanning,
+ * there may be information in the udev database to update, so open
+ * these devices to trigger a udev update.
+ *
+ * "before" refers to the list of pvs from lvmetad before rescanning
+ * "after" refers to the list of pvs from lvmetad after rescanning
+ *
+ * Comparing both lists, we can see which PVs changed (pvid or vgid),
+ * and trigger a udev db update for those.
+ */
+static void _update_changed_pvs_in_udev(struct cmd_context *cmd,
+ struct dm_list *pvc_before,
+ struct dm_list *pvc_after)
+{
+ struct pv_cache_list *before;
+ struct pv_cache_list *after;
+ char id_before[ID_LEN + 1] __attribute__((aligned(8)));
+ char id_after[ID_LEN + 1] __attribute__((aligned(8)));
+ int found;
+
+ dm_list_iterate_items(before, pvc_before) {
+ found = 0;
+
+ dm_list_iterate_items(after, pvc_after) {
+ if (after->found)
+ continue;
+
+ if (before->devt != after->devt)
+ continue;
+
+ if (!id_equal(&before->pvid, &after->pvid)) {
+ memset(id_before, 0, sizeof(id_before));
+ memset(id_after, 0, sizeof(id_after));
+ strncpy(&id_before[0], (char *) &before->pvid, sizeof(id_before) - 1);
+ strncpy(&id_after[0], (char *) &after->pvid, sizeof(id_after) - 1);
+
+ log_debug_devs("device %d:%d changed pvid from %s to %s",
+ (int)MAJOR(before->devt), (int)MINOR(before->devt),
+ id_before, id_after);
+
+ before->update_udev = 1;
+
+ } else if ((before->vgid && !after->vgid) ||
+ (after->vgid && !before->vgid) ||
+ (before->vgid && after->vgid && strcmp(before->vgid, after->vgid))) {
+
+ log_debug_devs("device %d:%d changed vg from %s to %s",
+ (int)MAJOR(before->devt), (int)MINOR(before->devt),
+ before->vgid ?: "none", after->vgid ?: "none");
+
+ before->update_udev = 1;
+ }
+
+ after->found = 1;
+ before->found = 1;
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ memset(id_before, 0, sizeof(id_before));
+ strncpy(&id_before[0], (char *) &before->pvid, sizeof(id_before) - 1);
+
+ log_debug_devs("device %d:%d pvid %s vg %s is gone",
+ (int)MAJOR(before->devt), (int)MINOR(before->devt),
+ id_before, before->vgid ? before->vgid : "none");
+
+ before->update_udev = 1;
+ }
+ }
+
+ dm_list_iterate_items(before, pvc_before) {
+ if (before->update_udev)
+ _update_pv_in_udev(cmd, before->devt);
+ }
+
+ dm_list_iterate_items(after, pvc_after) {
+ if (after->update_udev)
+ _update_pv_in_udev(cmd, after->devt);
+ }
+}
+
+/*
+ * Before this command was run, some external entity may have
+ * invalidated lvmetad's cache of global information, e.g. lvmlockd.
+ *
+ * The global information includes things like a new VG, a
+ * VG that was removed, the assignment of a PV to a VG;
+ * any change that is not isolated within a single VG.
+ *
+ * The external entity, like a lock manager, would invalidate
+ * the lvmetad global cache if it detected that the global
+ * information had been changed on disk by something other
+ * than a local lvm command, e.g. an lvm command on another
+ * host with access to the same devices. (How it detects
+ * the change is specific to lock manager or other entity.)
+ *
+ * The effect is that metadata on disk is newer than the metadata
+ * in the local lvmetad daemon, and the local lvmetad's cache
+ * should be updated from disk before this command uses it.
+ *
+ * So, using this function, a command checks if lvmetad's global
+ * cache is valid. If so, it does nothing. If not, it rescans
+ * devices to update the lvmetad cache, then it notifies lvmetad
+ * that it's cache is valid again (consistent with what's on disk.)
+ * This command can then go ahead and use the newly refreshed metadata.
+ *
+ * 1. Check if the lvmetad global cache is invalid.
+ * 2. If so, reread metadata from all devices and update the lvmetad cache.
+ * 3. Tell lvmetad that the global cache is now valid.
+ */
+
+void lvmetad_validate_global_cache(struct cmd_context *cmd, int force)
+{
+ struct dm_list pvc_before; /* pv_cache_list */
+ struct dm_list pvc_after; /* pv_cache_list */
+ daemon_reply reply;
+ int global_invalid;
+
+ dm_list_init(&pvc_before);
+ dm_list_init(&pvc_after);
+
+ if (!lvmetad_used())
+ return;
+
+ if (force)
+ goto do_scan;
+
+ reply = daemon_send_simple(_lvmetad, "get_global_info",
+ "token = %s", "skip",
+ NULL);
+
+ if (reply.error) {
+ log_error("lvmetad_validate_global_cache get_global_info error %d", reply.error);
+ goto do_scan;
+ }
+
+ if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) {
+ log_error("lvmetad_validate_global_cache get_global_info not ok");
+ goto do_scan;
+ }
+
+ global_invalid = daemon_reply_int(reply, "global_invalid", -1);
+
+ daemon_reply_destroy(reply);
+
+ if (!global_invalid) {
+ /* cache is valid */
+ return;
+ }
+
+ do_scan:
+ /*
+ * Save the current state of pvs from lvmetad so after devices are
+ * scanned, we can compare to the new state to see if pvs changed.
+ */
+ _lvmetad_get_pv_cache_list(cmd, &pvc_before);
+
+ /*
+ * Update the local lvmetad cache so it correctly reflects any
+ * changes made on remote hosts.
+ */
+ lvmetad_pvscan_all_devs(cmd, NULL);
+
+ /*
+ * Clear the global_invalid flag in lvmetad.
+ * Subsequent local commands that read global state
+ * from lvmetad will not see global_invalid until
+ * another host makes another global change.
+ */
+ reply = daemon_send_simple(_lvmetad, "set_global_info",
+ "token = %s", "skip",
+ "global_invalid = %d", 0,
+ NULL);
+ if (reply.error)
+ log_error("lvmetad_validate_global_cache set_global_info error %d", reply.error);
+
+ if (strcmp(daemon_reply_str(reply, "response", ""), "OK"))
+ log_error("lvmetad_validate_global_cache set_global_info not ok");
+
+ daemon_reply_destroy(reply);
+
+ /*
+ * Populate this command's lvmcache structures from lvmetad.
+ */
+ lvmcache_seed_infos_from_lvmetad(cmd);
+
+ /*
+ * Update the local udev database to reflect PV changes from
+ * other hosts.
+ *
+ * Compare the before and after PV lists, and if a PV's
+ * pvid or vgid has changed, then open that device to trigger
+ * a uevent to update the udev db.
+ *
+ * This has no direct benefit to lvm, but is just a best effort
+ * attempt to keep the udev db updated and reflecting current
+ * lvm information.
+ *
+ * FIXME: lvmcache_seed_infos_from_lvmetad() and _lvmetad_get_pv_cache_list()
+ * each get pv_list from lvmetad, and they could share a single pv_list reply.
+ */
+ if (!dm_list_empty(&pvc_before)) {
+ _lvmetad_get_pv_cache_list(cmd, &pvc_after);
+ _update_changed_pvs_in_udev(cmd, &pvc_before, &pvc_after);
+ }
+}
diff --git a/lib/cache/lvmetad.h b/lib/cache/lvmetad.h
index 8224675..af0d562 100644
--- a/lib/cache/lvmetad.h
+++ b/lib/cache/lvmetad.h
@@ -167,6 +167,7 @@ int lvmetad_pvscan_all_devs(struct cmd_context *cmd, activation_handler handler)
int lvmetad_pvscan_foreign_vgs(struct cmd_context *cmd, activation_handler handler);
int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg);
+void lvmetad_validate_global_cache(struct cmd_context *cmd, int force);
# else /* LVMETAD_SUPPORT */
@@ -195,6 +196,7 @@ int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg);
# define lvmetad_pvscan_all_devs(cmd, handler) (0)
# define lvmetad_pvscan_foreign_vgs(cmd, handler) (0)
# define lvmetad_vg_clear_outdated_pvs(vg) (1)
+# define lvmetad_validate_global_cache(cmd, force) do { } while (0)
# endif /* LVMETAD_SUPPORT */
8 years, 10 months
master - Reread a VG if the lvmetad copy is stale
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=679b6b5b29b225...
Commit: 679b6b5b29b2257672087f387ef04bc4c8a4cca7
Parent: bf77f717119c0e567a36d7d48e06d81bf5aa2fb9
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Nov 6 14:01:12 2014 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Tue Jun 23 16:36:40 2015 -0500
Reread a VG if the lvmetad copy is stale
and update the lvmetad copy after it is reread from disk.
This is the client side handling of the vg_invalid state
added to lvmetad in commit c595b50cec8a6b95c6ac4988912d1412f3cc0237.
---
lib/cache/lvmetad.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 106 insertions(+), 0 deletions(-)
diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c
index 92998bc..3c07cd1 100644
--- a/lib/cache/lvmetad.c
+++ b/lib/cache/lvmetad.c
@@ -34,6 +34,8 @@ static char *_lvmetad_token = NULL;
static const char *_lvmetad_socket = NULL;
static struct cmd_context *_lvmetad_cmd = NULL;
+static struct volume_group *lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg);
+
void lvmetad_disconnect(void)
{
if (_lvmetad_connected)
@@ -417,6 +419,7 @@ static int _pv_update_struct_pv(struct physical_volume *pv, struct format_instan
struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgname, const char *vgid)
{
struct volume_group *vg = NULL;
+ struct volume_group *vg2 = NULL;
daemon_reply reply;
int found;
char uuid[64];
@@ -486,6 +489,18 @@ struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgna
if (!(vg = import_vg_from_lvmetad_config_tree(reply.cft, fid)))
goto_out;
+ /*
+ * locking may have detected a newer vg version and
+ * invalidated the cached vg.
+ */
+ if (dm_config_find_node(reply.cft->root, "vg_invalid")) {
+ log_debug_lvmetad("Update invalid lvmetad cache for VG %s", vgname);
+ vg2 = lvmetad_pvscan_vg(cmd, vg);
+ release_vg(vg);
+ vg = vg2;
+ fid = vg->fid;
+ }
+
dm_list_iterate_items(pvl, &vg->pvs) {
if (!_pv_update_struct_pv(pvl->pv, fid)) {
vg = NULL;
@@ -1012,6 +1027,97 @@ static int _lvmetad_pvscan_single(struct metadata_area *mda, void *baton)
return 1;
}
+/*
+ * The lock manager may detect that the vg cached in lvmetad is out of date,
+ * due to something like an lvcreate from another host.
+ * This is limited to changes that only affect the vg (not global state like
+ * orphan PVs), so we only need to reread mdas on the vg's existing pvs.
+ */
+
+static struct volume_group *lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg)
+{
+ struct volume_group *vg_ret = NULL;
+ struct dm_config_tree *vgmeta_ret = NULL;
+ struct dm_config_tree *vgmeta;
+ struct pv_list *pvl;
+ struct lvmcache_info *info;
+ struct format_instance *fid;
+ struct format_instance_ctx fic = { .type = 0 };
+ struct _lvmetad_pvscan_baton baton;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ /* missing pv */
+ if (!pvl->pv->dev)
+ continue;
+
+ info = lvmcache_info_from_pvid((const char *)&pvl->pv->id, 0);
+
+ baton.vg = NULL;
+ baton.fid = lvmcache_fmt(info)->ops->create_instance(lvmcache_fmt(info), &fic);
+
+ if (!baton.fid)
+ return NULL;
+
+ if (baton.fid->fmt->features & FMT_OBSOLETE) {
+ log_error("WARNING: Ignoring obsolete format of metadata (%s) on device %s when using lvmetad",
+ baton.fid->fmt->name, dev_name(pvl->pv->dev));
+ lvmcache_fmt(info)->ops->destroy_instance(baton.fid);
+ return NULL;
+ }
+
+ lvmcache_foreach_mda(info, _lvmetad_pvscan_single, &baton);
+
+ if (!baton.vg) {
+ lvmcache_fmt(info)->ops->destroy_instance(baton.fid);
+ return NULL;
+ }
+
+ if (!(vgmeta = export_vg_to_config_tree(baton.vg))) {
+ log_error("VG export to config tree failed");
+ release_vg(baton.vg);
+ return NULL;
+ }
+
+ if (!vgmeta_ret) {
+ vgmeta_ret = vgmeta;
+ } else {
+ if (!compare_config(vgmeta_ret->root, vgmeta->root)) {
+ log_error("VG metadata comparison failed");
+ dm_config_destroy(vgmeta);
+ dm_config_destroy(vgmeta_ret);
+ release_vg(baton.vg);
+ return NULL;
+ }
+ dm_config_destroy(vgmeta);
+ }
+
+ release_vg(baton.vg);
+ }
+
+ if (vgmeta_ret) {
+ fid = lvmcache_fmt(info)->ops->create_instance(lvmcache_fmt(info), &fic);
+ if (!(vg_ret = import_vg_from_config_tree(vgmeta_ret, fid))) {
+ log_error("VG import from config tree failed");
+ lvmcache_fmt(info)->ops->destroy_instance(fid);
+ goto out;
+ }
+
+ /*
+ * Update lvmetad with the newly read version of the VG.
+ * The "precommitted" name is a misnomer in this case,
+ * but that is the field which lvmetad_vg_update() uses
+ * to send the metadata cft to lvmetad.
+ */
+ vg_ret->cft_precommitted = vgmeta_ret;
+ if (!lvmetad_vg_update(vg_ret))
+ log_error("Failed to update lvmetad with new VG meta");
+ vg_ret->cft_precommitted = NULL;
+ dm_config_destroy(vgmeta_ret);
+ }
+out:
+ return vg_ret;
+}
+
int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
activation_handler handler, int ignore_obsolete)
{
8 years, 10 months
master - lvmetad: add invalidation method
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=bf77f717119c0e...
Commit: bf77f717119c0e567a36d7d48e06d81bf5aa2fb9
Parent: 4c6b3f5ec37bf4e0d361d241a0b6f9d0902a4359
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Oct 21 09:40:13 2014 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Tue Jun 23 16:36:33 2015 -0500
lvmetad: add invalidation method
Add the ability to invalidate global or individual VG metadata.
The invalid state is returned to lvm commands along with the metadata.
This allows lvm commands to detect stale metadata from the cache and
reread the latest metadata from disk (in a subsequent patch.)
These changes do not change the protocol or compatibility between
lvm commands and lvmetad.
Global information
------------------
Global information refers to metadata that is not isolated
to a single VG , e.g. the list of vg names, or the list of pvs.
When an external system, e.g. a locking system, detects that global
information has been changed from another host (e.g. a new vg has been
created) it sends lvmetad the message: set_global_info: global_invalid=1.
lvmetad sets the global invalid flag to indicate that its cached data is
stale.
When lvm commands request information from lvmetad, lvmetad returns the
cached information, along with an additional top-level config node called
"global_invalid". This new info tells the lvm command that the cached
information is stale.
When an lvm command sees global_invalid from lvmated, it knows it should
rescan devices and update lvmetad with the latest information. When this
is complete, it sends lvmetad the message: set_global_info:
global_invalid=0, and lvmetad clears the global invalid flag. Further lvm
commands will use the lvmetad cache until it is invalidated again.
The most common commands that cause global invalidation are vgcreate and
vgextend. These are uncommon compared to commands that report global
information, e.g. vgs. So, the percentage of lvmetad replies containing
global_invalid should be very small.
VG information
--------------
VG information refers to metadata that is isolated to a single VG,
e.g. an LV or the size of an LV.
When an external system determines that VG information has been changed
from another host (e.g. an lvcreate or lvresize), it sends lvmetad the
message: set_vg_info: uuid=X version=N. X is the VG uuid, and N is the
latest VG seqno that was written. lvmetad checks the seqno of its cached
VG, and if the version from the message is newer, it sets an invalid flag
for the cached VG. The invalid flag, along with the newer seqno are saved
in a new vg_info struct.
When lvm commands request VG metadata from lvmetad, lvmetad includes the
invalid flag along with the VG metadata. The lvm command checks for this
flag, and rereads the VG from disk if set. The VG read from disk is sent
to lvmetad. lvmetad sees that the seqno in the new version matches the
seqno from the last set_vg_info message, and clears the vg invalid flag.
Further lvm commands will use the VG metadata from lvmetad until it is
next invalidated.
---
daemons/lvmetad/lvmetad-core.c | 289 +++++++++++++++++++++++++++++++++++++++-
1 files changed, 288 insertions(+), 1 deletions(-)
diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c
index dc0c92a..7f2405c 100644
--- a/daemons/lvmetad/lvmetad-core.c
+++ b/daemons/lvmetad/lvmetad-core.c
@@ -28,6 +28,100 @@
#define LVMETAD_SOCKET DEFAULT_RUN_DIR "/lvmetad.socket"
+/*
+ * valid/invalid state of cached metadata
+ *
+ * Normally when using lvmetad, the state is kept up-to-date through a
+ * combination of notifications from clients and updates triggered by uevents.
+ * When using lvmlockd, the lvmetad state is expected to become out of
+ * date (invalid/stale) when other hosts make changes to the metadata on disk.
+ *
+ * To deal with this, the metadata cached in lvmetad can be flagged as invalid.
+ * This invalid flag is returned along with the metadata when read by a
+ * command. The command can check for the invalid flag and decide that it
+ * should either use the stale metadata (uncommon), or read the latest metadata
+ * from disk rather than using the invalid metadata that was returned. If the
+ * command reads the latest metadata from disk, it can choose to send it to
+ * lvmetad to update the cached copy and clear the invalid flag in lvmetad.
+ * Otherwise, the next command to read the metadata from lvmetad will also
+ * receive the invalid metadata with the invalid flag (and like the previous
+ * command, it too may choose to read the latest metadata from disk and can
+ * then also choose to update the lvmetad copy.)
+ *
+ * For purposes of tracking the invalid state, LVM metadata is considered
+ * to be either VG-specific or global. VG-specific metadata is metadata
+ * that is isolated to a VG, such as the LVs it contains. Global
+ * metadata is metadata that is not isolated to a single VG. Global
+ * metdata includes:
+ * . the VG namespace (which VG names are used)
+ * . the set of orphan PVs (which PVs are in VGs and which are not)
+ * . properties of orphan PVs (the size of an orphan PV)
+ *
+ * If the metadata for a single VG becomes invalid, the VGFL_INVALID
+ * flag can be set in the vg_info struct for that VG. If the global
+ * metdata becomes invalid, the GLFL_INVALID flag can be set in the
+ * lvmetad daemon state.
+ *
+ * If a command reads VG metadata and VGFL_INVALID is set, an
+ * extra config node called "vg_invalid" is added to the config
+ * data returned to the command.
+ *
+ * If a command reads global metdata and GLFL_INVALID is set, an
+ * extra config node called "global_invalid" is added to the
+ * config data returned to the command.
+ *
+ * If a command sees vg_invalid, and wants the latest VG metadata,
+ * it only needs to scan disks of the PVs in that VG.
+ * It can then use vg_update to send the latest metadata to lvmetad
+ * which clears the VGFL_INVALID flag.
+ *
+ * If a command sees global_invalid, and wants the latest metadata,
+ * it should scan all devices to update lvmetad, and then send
+ * lvmetad the "set_global_info global_invalid=0" message to clear
+ * GLFL_INVALID.
+ *
+ * (When rescanning devices to update lvmetad, the command must use
+ * the global filter cmd->lvmetad_filter so that it processes the same
+ * devices that are seen by lvmetad.)
+ *
+ * The lvmetad INVALID flags can be set by sending lvmetad the messages:
+ *
+ * . set_vg_info with the latest VG seqno. If the VG seqno is larger
+ * than the cached VG seqno, VGFL_INVALID is set for the VG.
+ *
+ * . set_global_info with global_invalid=1 sets GLFL_INVALID.
+ *
+ * Different entities could use these functions to invalidate metadata
+ * if/when they detected that the cache is stale. How they detect that
+ * the cache is stale depends on the details of the specific entity.
+ *
+ * In the case of lvmlockd, it embeds values into its locks to keep track
+ * of when other nodes have changed metadata on disk related to those locks.
+ * When acquring locks it can look at these values and detect that
+ * the metadata associated with the lock has been changed.
+ * When the values change, it uses set_vg_info/set_global_info to
+ * invalidate the lvmetad cache.
+ *
+ * The values that lvmlockd distributes through its locks are the
+ * latest VG seqno in VG locks and a global counter in the global lock.
+ * When a host acquires a VG lock and sees that the embedded seqno is
+ * larger than it was previously, it knows that it should invalidate the
+ * lvmetad cache for the VG. If the host acquires the global lock
+ * and sees that the counter is larger than previously, it knows that
+ * it should invalidate the global info in lvmetad. This invalidation
+ * is done before the lock is returned to the command. This way the
+ * invalid flag will be set on the metadata before the command reads
+ * it from lvmetad.
+ */
+
+struct vg_info {
+ int64_t external_version;
+ uint32_t flags; /* VGFL_ */
+};
+
+#define GLFL_INVALID 0x00000001
+#define VGFL_INVALID 0x00000001
+
typedef struct {
log_state *log; /* convenience */
const char *log_config;
@@ -38,6 +132,7 @@ typedef struct {
struct dm_hash_table *vgid_to_metadata;
struct dm_hash_table *vgid_to_vgname;
struct dm_hash_table *vgid_to_outdated_pvs;
+ struct dm_hash_table *vgid_to_info;
struct dm_hash_table *vgname_to_vgid;
struct dm_hash_table *pvid_to_vgid;
struct {
@@ -48,6 +143,7 @@ typedef struct {
pthread_mutex_t pvid_to_vgid;
} lock;
char token[128];
+ uint32_t flags; /* GLFL_ */
pthread_mutex_t token_lock;
} lvmetad_state;
@@ -68,6 +164,7 @@ static void destroy_metadata_hashes(lvmetad_state *s)
dm_hash_destroy(s->vgid_to_metadata);
dm_hash_destroy(s->vgid_to_vgname);
dm_hash_destroy(s->vgid_to_outdated_pvs);
+ dm_hash_destroy(s->vgid_to_info);
dm_hash_destroy(s->vgname_to_vgid);
dm_hash_destroy(s->device_to_pvid);
@@ -81,6 +178,7 @@ static void create_metadata_hashes(lvmetad_state *s)
s->vgid_to_metadata = dm_hash_create(32);
s->vgid_to_vgname = dm_hash_create(32);
s->vgid_to_outdated_pvs = dm_hash_create(32);
+ s->vgid_to_info = dm_hash_create(32);
s->pvid_to_vgid = dm_hash_create(32);
s->vgname_to_vgid = dm_hash_create(32);
}
@@ -244,6 +342,30 @@ static int update_pv_status(lvmetad_state *s,
return complete;
}
+static struct dm_config_node *add_last_node(struct dm_config_tree *cft, const char *node_name)
+{
+ struct dm_config_node *cn, *last;
+
+ cn = cft->root;
+ last = cn;
+
+ while (cn->sib) {
+ last = cn->sib;
+ cn = last;
+ }
+
+ cn = dm_config_create_node(cft, node_name);
+ if (!cn)
+ return NULL;
+
+ cn->v = NULL;
+ cn->sib = NULL;
+ cn->parent = cft->root;
+ last->sib = cn;
+
+ return cn;
+}
+
static struct dm_config_node *make_pv_node(lvmetad_state *s, const char *pvid,
struct dm_config_tree *cft,
struct dm_config_node *parent,
@@ -307,6 +429,9 @@ static response pv_list(lvmetad_state *s, request r)
cn = make_pv_node(s, id, res.cft, cn_pvs, cn);
}
+ if (s->flags & GLFL_INVALID)
+ add_last_node(res.cft, "global_invalid");
+
unlock_pvid_to_pvmeta(s);
return res;
@@ -351,6 +476,9 @@ static response pv_lookup(lvmetad_state *s, request r)
pv->key = "physical_volume";
unlock_pvid_to_pvmeta(s);
+ if (s->flags & GLFL_INVALID)
+ add_last_node(res.cft, "global_invalid");
+
return res;
}
@@ -419,6 +547,9 @@ static response vg_list(lvmetad_state *s, request r)
}
unlock_vgid_to_metadata(s);
+
+ if (s->flags & GLFL_INVALID)
+ add_last_node(res.cft, "global_invalid");
bad:
return res;
}
@@ -496,6 +627,7 @@ static response vg_lookup(lvmetad_state *s, request r)
{
struct dm_config_tree *cft;
struct dm_config_node *metadata, *n;
+ struct vg_info *info;
response res = { 0 };
const char *uuid = daemon_request_str(r, "uuid", NULL);
@@ -560,6 +692,16 @@ static response vg_lookup(lvmetad_state *s, request r)
update_pv_status(s, res.cft, n, 1); /* FIXME report errors */
chain_outdated_pvs(s, uuid, res.cft, n);
+ if (s->flags & GLFL_INVALID)
+ add_last_node(res.cft, "global_invalid");
+
+ info = dm_hash_lookup(s->vgid_to_info, uuid);
+ if (info && (info->flags & VGFL_INVALID)) {
+ n = add_last_node(res.cft, "vg_invalid");
+ if (!n)
+ goto bad;
+ }
+
return res;
bad:
unlock_vg(s, uuid);
@@ -1089,6 +1231,24 @@ static response vg_clear_outdated_pvs(lvmetad_state *s, request r)
return daemon_reply_simple("OK", NULL);
}
+static void vg_info_update(lvmetad_state *s, const char *uuid,
+ struct dm_config_node *metadata)
+{
+ struct vg_info *info;
+ int64_t cache_version;
+
+ cache_version = dm_config_find_int64(metadata, "metadata/seqno", -1);
+ if (cache_version == -1)
+ return;
+
+ info = (struct vg_info *) dm_hash_lookup(s->vgid_to_info, uuid);
+ if (!info)
+ return;
+
+ if (cache_version >= info->external_version)
+ info->flags &= ~VGFL_INVALID;
+}
+
static response vg_update(lvmetad_state *s, request r)
{
struct dm_config_node *metadata = dm_config_find_node(r.cft->root, "metadata");
@@ -1106,6 +1266,8 @@ static response vg_update(lvmetad_state *s, request r)
* call; if client does not commit, die */
if (!update_metadata(s, vgname, vgid, metadata, NULL, NULL))
return reply_fail("metadata update failed");
+
+ vg_info_update(s, vgid, metadata);
}
return daemon_reply_simple("OK", NULL);
}
@@ -1126,6 +1288,70 @@ static response vg_remove(lvmetad_state *s, request r)
return daemon_reply_simple("OK", NULL);
}
+static response set_global_info(lvmetad_state *s, request r)
+{
+ const int global_invalid = daemon_request_int(r, "global_invalid", -1);
+
+ if (global_invalid == 1)
+ s->flags |= GLFL_INVALID;
+
+ else if (global_invalid == 0)
+ s->flags &= ~GLFL_INVALID;
+
+ return daemon_reply_simple("OK", NULL);
+}
+
+static response get_global_info(lvmetad_state *s, request r)
+{
+ return daemon_reply_simple("OK", "global_invalid = %d",
+ (s->flags & GLFL_INVALID) ? 1 : 0,
+ NULL);
+}
+
+static response set_vg_info(lvmetad_state *s, request r)
+{
+ struct dm_config_tree *vg;
+ struct vg_info *info;
+ const char *uuid = daemon_request_str(r, "uuid", NULL);
+ const int64_t new_version = daemon_request_int(r, "version", -1);
+ int64_t cache_version;
+
+ if (!uuid)
+ goto out;
+
+ if (new_version == -1)
+ goto out;
+
+ vg = dm_hash_lookup(s->vgid_to_metadata, uuid);
+ if (!vg)
+ goto out;
+
+ if (!new_version)
+ goto inval;
+
+ cache_version = dm_config_find_int64(vg->root, "metadata/seqno", -1);
+
+ if (cache_version != -1 && new_version != -1 && cache_version >= new_version)
+ goto out;
+inval:
+ info = dm_hash_lookup(s->vgid_to_info, uuid);
+ if (!info) {
+ info = malloc(sizeof(struct vg_info));
+ if (!info)
+ goto bad;
+ memset(info, 0, sizeof(struct vg_info));
+ dm_hash_insert(s->vgid_to_info, uuid, (void*)info);
+ }
+
+ info->external_version = new_version;
+ info->flags |= VGFL_INVALID;
+
+out:
+ return daemon_reply_simple("OK", NULL);
+bad:
+ return reply_fail("out of memory");
+}
+
static void _dump_cft(struct buffer *buf, struct dm_hash_table *ht, const char *key_addr)
{
struct dm_hash_node *n;
@@ -1163,6 +1389,52 @@ static void _dump_pairs(struct buffer *buf, struct dm_hash_table *ht, const char
buffer_append(buf, "}\n");
}
+static void _dump_info_version(struct buffer *buf, struct dm_hash_table *ht, const char *name, int int_key)
+{
+ char *append;
+ struct dm_hash_node *n = dm_hash_get_first(ht);
+ struct vg_info *info;
+
+ buffer_append(buf, name);
+ buffer_append(buf, " {\n");
+
+ while (n) {
+ const char *key = dm_hash_get_key(ht, n);
+ info = dm_hash_get_data(ht, n);
+ buffer_append(buf, " ");
+ (void) dm_asprintf(&append, "%s = %lld", key, (long long)info->external_version);
+ if (append)
+ buffer_append(buf, append);
+ buffer_append(buf, "\n");
+ dm_free(append);
+ n = dm_hash_get_next(ht, n);
+ }
+ buffer_append(buf, "}\n");
+}
+
+static void _dump_info_flags(struct buffer *buf, struct dm_hash_table *ht, const char *name, int int_key)
+{
+ char *append;
+ struct dm_hash_node *n = dm_hash_get_first(ht);
+ struct vg_info *info;
+
+ buffer_append(buf, name);
+ buffer_append(buf, " {\n");
+
+ while (n) {
+ const char *key = dm_hash_get_key(ht, n);
+ info = dm_hash_get_data(ht, n);
+ buffer_append(buf, " ");
+ (void) dm_asprintf(&append, "%s = %llx", key, (long long)info->flags);
+ if (append)
+ buffer_append(buf, append);
+ buffer_append(buf, "\n");
+ dm_free(append);
+ n = dm_hash_get_next(ht, n);
+ }
+ buffer_append(buf, "}\n");
+}
+
static response dump(lvmetad_state *s)
{
response res = { 0 };
@@ -1197,6 +1469,12 @@ static response dump(lvmetad_state *s)
buffer_append(b, "\n# DEVICE to PVID mapping\n\n");
_dump_pairs(b, s->device_to_pvid, "device_to_pvid", 1);
+ buffer_append(b, "\n# VGID to INFO version mapping\n\n");
+ _dump_info_version(b, s->vgid_to_info, "vgid_to_info", 0);
+
+ buffer_append(b, "\n# VGID to INFO flags mapping\n\n");
+ _dump_info_flags(b, s->vgid_to_info, "vgid_to_info", 0);
+
unlock_pvid_to_vgid(s);
unlock_pvid_to_pvmeta(s);
unlock_vgid_to_metadata(s);
@@ -1218,7 +1496,7 @@ static response handler(daemon_state s, client_handle h, request r)
return daemon_reply_simple("OK", NULL);
}
- if (strcmp(token, state->token) && strcmp(rq, "dump")) {
+ if (strcmp(token, state->token) && strcmp(rq, "dump") && strcmp(token, "skip")) {
pthread_mutex_unlock(&state->token_lock);
return daemon_reply_simple("token_mismatch",
"expected = %s", state->token,
@@ -1262,6 +1540,15 @@ static response handler(daemon_state s, client_handle h, request r)
if (!strcmp(rq, "vg_list"))
return vg_list(state, r);
+ if (!strcmp(rq, "set_global_info"))
+ return set_global_info(state, r);
+
+ if (!strcmp(rq, "get_global_info"))
+ return get_global_info(state, r);
+
+ if (!strcmp(rq, "set_vg_info"))
+ return set_vg_info(state, r);
+
if (!strcmp(rq, "dump"))
return dump(state);
8 years, 10 months
master - tests: use vgscan after enable_dev
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=4c6b3f5ec37bf4...
Commit: 4c6b3f5ec37bf4e0d361d241a0b6f9d0902a4359
Parent: ae76e8f0d0659482e8c0440a93d63a9ce5786b10
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Tue Jun 23 13:39:57 2015 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Tue Jun 23 13:39:57 2015 +0200
tests: use vgscan after enable_dev
Since our test environment runs also in non-real-udev world,
it's using /etc/.cache file with scanned files.
So in this case it is mandatory the user runs 'vgscan'
after a device reappears in the system.
This 'first' lvm2 command then fixes metadata (just like vgs did).
---
test/shell/unlost-pv.sh | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/test/shell/unlost-pv.sh b/test/shell/unlost-pv.sh
index c56d488..76bf8a1 100644
--- a/test/shell/unlost-pv.sh
+++ b/test/shell/unlost-pv.sh
@@ -48,7 +48,11 @@ check_ not
aux disable_dev "$dev1"
vgreduce --removemissing --force $vg
aux enable_dev "$dev1"
-vgs 2>&1 | grep 'Removing PV'
-vgs 2>&1 | not grep 'Removing PV'
+
+vgscan 2>&1 | tee out
+grep 'Removing PV' out
+
+vgs 2>&1 | tee out
+not grep 'Removing PV' out
vgremove -ff $vg
8 years, 10 months
master - tests: skip when snapshot does not work
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=ae76e8f0d06594...
Commit: ae76e8f0d0659482e8c0440a93d63a9ce5786b10
Parent: 7ee3ccd826e609fa62a57205f0e8480686f906af
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Tue Jun 23 13:25:09 2015 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Tue Jun 23 13:25:09 2015 +0200
tests: skip when snapshot does not work
Some older kernel (i.e. 3.11.10 on fc20) do not work properly.
Skip the test if snapshot does not meet 50%.
---
test/api/percent.sh | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/test/api/percent.sh b/test/api/percent.sh
index 0031a67..a9b7b90 100644
--- a/test/api/percent.sh
+++ b/test/api/percent.sh
@@ -22,6 +22,8 @@ lvcreate -aey -l 5 -n foo $vg
lvcreate -s -n snap $vg/foo -l 3 -c 4k
lvcreate -s -n snap2 $vg/foo -l 6 -c 4k
dd if=/dev/zero of="$DM_DEV_DIR/$vg/snap2" count=1 bs=1024 oflag=direct
+# skip test with broken kernel
+check lv_field $vg/snap2 data_percent "50.00" || skip
lvcreate -aey --type mirror -m 1 -n mirr $vg -l 1 --mirrorlog core
lvs -a $vg
aux apitest percent $vg
8 years, 10 months
master - tests: newer version needed for ext-orig
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=7ee3ccd826e609...
Commit: 7ee3ccd826e609fa62a57205f0e8480686f906af
Parent: 9c86d33e68ceb7a5a5e6754f4551545d915e403d
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Tue Jun 23 11:56:42 2015 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Tue Jun 23 11:56:42 2015 +0200
tests: newer version needed for ext-orig
---
test/shell/lvcreate-thin-external-size.sh | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/test/shell/lvcreate-thin-external-size.sh b/test/shell/lvcreate-thin-external-size.sh
index 73e36ea..d8e98f9 100644
--- a/test/shell/lvcreate-thin-external-size.sh
+++ b/test/shell/lvcreate-thin-external-size.sh
@@ -23,7 +23,9 @@ which cmp || skip
#
# Main
#
-aux have_thin 1 3 0 || skip
+
+# Test needs thin-pool target with unaligned ext-orig size support
+aux have_thin 1 13 0 || skip
aux prepare_pvs 2 640
8 years, 10 months