master - lvmcmdline: profile: early profile load for lvm shell
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=e805ef2d66bc8a...
Commit: e805ef2d66bc8a06481de648497ff1369965d3c3
Parent: f21afddeb71c052617afb41fed9a2bd68ad532cf
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Thu Aug 4 16:45:27 2016 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Aug 9 18:49:11 2016 +0200
lvmcmdline: profile: early profile load for lvm shell
Make it possible to also load profile for lvm shell by honouring
LVM_COMMAND_PROFILE environment variable.
---
tools/lvmcmdline.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 81118c1..cb41534 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -1395,6 +1395,7 @@ static int _prepare_profiles(struct cmd_context *cmd)
cmd->profile_params->global_metadata_profile = profile;
}
+ remove_config_tree_by_source(cmd, source);
if (!override_config_tree_from_profile(cmd, profile)) {
log_error(_failed_to_apply_profile_msg, source_name, name);
return 0;
@@ -1420,6 +1421,8 @@ static int _prepare_profiles(struct cmd_context *cmd)
log_error(_failed_to_add_profile_msg, source_name, name);
return 0;
}
+
+ remove_config_tree_by_source(cmd, CONFIG_PROFILE_COMMAND);
if (!override_config_tree_from_profile(cmd, profile)) {
log_error(_failed_to_apply_profile_msg, source_name, name);
return 0;
@@ -1438,6 +1441,7 @@ static int _prepare_profiles(struct cmd_context *cmd)
log_error(_failed_to_add_profile_msg, source_name, name);
return 0;
}
+ remove_config_tree_by_source(cmd, CONFIG_PROFILE_METADATA);
if (!override_config_tree_from_profile(cmd, profile)) {
log_error(_failed_to_apply_profile_msg, source_name, name);
return 0;
@@ -2217,6 +2221,10 @@ int lvm2_main(int argc, char **argv)
#ifdef READLINE_SUPPORT
if (!alias && argc == 1) {
_nonroot_warning();
+ if (!_prepare_profiles(cmd)) {
+ ret = ECMD_FAILED;
+ goto out;
+ }
ret = lvm_shell(cmd, &_cmdline);
goto out;
}
7 years, 9 months
master - lvm: shell: extend log report to cover whole lvm shell's main loop
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=f21afddeb71c05...
Commit: f21afddeb71c052617afb41fed9a2bd68ad532cf
Parent: 7d1125e5b785014d3bae34acd158c528a4222871
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Thu Aug 4 13:42:57 2016 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Aug 9 18:49:10 2016 +0200
lvm: shell: extend log report to cover whole lvm shell's main loop
When lvm commands are executed in lvm shell, we cover the whole lvm
command execution within this shell now. That means, all messages logged
and status caught during each command execution is now recorded in the
log report, including overall command's return code.
---
lib/commands/toolcontext.c | 5 ---
tools/lvm.c | 64 +++++++++++++++++++++++++++++++++++--------
tools/reporter.c | 25 +++++++----------
tools/toollib.c | 14 ++++++---
4 files changed, 71 insertions(+), 37 deletions(-)
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index 57d3bdc..a7f76ba 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -2218,11 +2218,6 @@ void destroy_toolcontext(struct cmd_context *cmd)
if (cmd->cft_def_hash)
dm_hash_destroy(cmd->cft_def_hash);
- if (cmd->cmd_report.log_rh) {
- dm_report_free(cmd->cmd_report.log_rh);
- cmd->cmd_report.log_rh = NULL;
- }
-
if (cmd->libmem)
dm_pool_destroy(cmd->libmem);
diff --git a/tools/lvm.c b/tools/lvm.c
index c2d5096..2b95c41 100644
--- a/tools/lvm.c
+++ b/tools/lvm.c
@@ -200,6 +200,12 @@ static int _log_shell_command_status(struct cmd_context *cmd, int ret_code)
stored_errno(), ret_code);
}
+static void _discard_log_report_content(struct cmd_context *cmd)
+{
+ if (cmd->cmd_report.log_rh)
+ dm_report_destroy_rows(cmd->cmd_report.log_rh);
+}
+
int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
{
log_report_t saved_log_report_state = log_get_report_state();
@@ -210,65 +216,76 @@ int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
rl_attempted_completion_function = (rl_completion_func_t *) _completion;
_read_history(cmd);
-
_cmdline = cmdline;
cmd->is_interactive = 1;
+
+ if (!report_format_init(cmd))
+ return_ECMD_FAILED;
+
log_set_report_context(LOG_REPORT_CONTEXT_SHELL);
log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_CMD);
while (1) {
+ log_set_report(cmd->cmd_report.log_rh);
log_set_report_object_name_and_id(NULL, NULL);
+
free(input);
input = readline("lvm> ");
/* EOF */
if (!input) {
+ _discard_log_report_content(cmd);
/* readline sends prompt to stdout */
printf("\n");
break;
}
/* empty line */
- if (!*input)
+ if (!*input) {
+ _discard_log_report_content(cmd);
continue;
+ }
add_history(input);
argv = args;
if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
+ _discard_log_report_content(cmd);
log_error("Too many arguments, sorry.");
continue;
}
- if (!argc)
+ if (!argc) {
+ _discard_log_report_content(cmd);
continue;
+ }
if (!strcmp(argv[0], "lvm")) {
argv++;
argc--;
}
- if (!argc)
+ if (!argc) {
+ _discard_log_report_content(cmd);
continue;
+ }
log_set_report_object_name_and_id(argv[0], NULL);
+ is_lastlog_cmd = !strcmp(argv[0], "lastlog");
+
+ if (!is_lastlog_cmd)
+ _discard_log_report_content(cmd);
+
if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
+ _discard_log_report_content(cmd);
remove_history(history_length - 1);
log_error("Exiting.");
break;
}
- is_lastlog_cmd = !strcmp(argv[0], "lastlog");
-
- if (cmd->cmd_report.log_rh && !is_lastlog_cmd) {
- /* drop old log report */
- dm_report_free(cmd->cmd_report.log_rh);
- cmd->cmd_report.log_rh = NULL;
- }
-
ret = lvm_run_command(cmd, argc, argv);
if (ret == ENO_SUCH_CMD)
log_error("No such command '%s'. Try 'help'.",
@@ -282,12 +299,35 @@ int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
if (!is_lastlog_cmd)
_log_shell_command_status(cmd, ret);
+
+ log_set_report(NULL);
+ dm_report_group_output_and_pop_all(cmd->cmd_report.report_group);
+
+ if (cmd->cmd_report.log_rh &&
+ !(dm_report_group_push(cmd->cmd_report.report_group,
+ cmd->cmd_report.log_rh,
+ (void *) cmd->cmd_report.log_name))) {
+ log_set_report(NULL);
+ log_error("Failed to add log report.");
+ break;
+ }
}
log_restore_report_state(saved_log_report_state);
cmd->is_interactive = 0;
free(input);
+
+ if (cmd->cmd_report.report_group) {
+ dm_report_group_destroy(cmd->cmd_report.report_group);
+ cmd->cmd_report.report_group = NULL;
+ }
+
+ if (cmd->cmd_report.log_rh) {
+ dm_report_free(cmd->cmd_report.log_rh);
+ cmd->cmd_report.report_group = NULL;
+ }
+
return 0;
}
diff --git a/tools/reporter.c b/tools/reporter.c
index d386efc..4a622ed 100644
--- a/tools/reporter.c
+++ b/tools/reporter.c
@@ -1387,6 +1387,11 @@ static int _report(struct cmd_context *cmd, int argc, char **argv, report_type_t
} else
r = _do_report(cmd, handle, &args, single_args);
+ if (!args.log_only && !dm_report_group_pop(cmd->cmd_report.report_group)) {
+ log_error("Failed to finalize main report section in report group.");
+ r = ECMD_FAILED;
+ }
+
destroy_processing_handle(cmd, handle);
return r;
}
@@ -1508,32 +1513,22 @@ bad:
return 0;
}
-int lastlog(struct cmd_context *cmd, int argc, char **argv)
+int lastlog(struct cmd_context *cmd, int argc __attribute((unused)), char **argv __attribute__((unused)))
{
- struct dm_report_group *report_group = NULL;
const char *selection;
- int r = ECMD_FAILED;
if (!cmd->cmd_report.log_rh) {
log_error("No log report stored.");
- goto out;
+ return ECMD_FAILED;
}
if (!_do_report_get_selection(cmd, CMDLOG, 1, NULL, &selection))
- goto_out;
+ return_ECMD_FAILED;
if (!dm_report_set_selection(cmd->cmd_report.log_rh, selection)) {
log_error("Failed to set selection for log report.");
- goto out;
+ return ECMD_FAILED;
}
- if (!dm_report_output(cmd->cmd_report.log_rh) ||
- !dm_report_group_pop(report_group))
- goto_out;
-
- r = ECMD_PROCESSED;
-out:
- if (!dm_report_group_destroy(report_group))
- stack;
- return r;
+ return ECMD_PROCESSED;
}
diff --git a/tools/toollib.c b/tools/toollib.c
index bcf94ea..e24946f 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1798,11 +1798,15 @@ void destroy_processing_handle(struct cmd_context *cmd, struct processing_handle
log_restore_report_state(cmd->cmd_report.saved_log_report_state);
- if (!dm_report_group_destroy(cmd->cmd_report.report_group))
- stack;
- if (!cmd->is_interactive && cmd->cmd_report.log_rh) {
- dm_report_free(cmd->cmd_report.log_rh);
- cmd->cmd_report.log_rh = NULL;
+ if (!cmd->is_interactive) {
+ if (!dm_report_group_destroy(cmd->cmd_report.report_group))
+ stack;
+ cmd->cmd_report.report_group = NULL;
+
+ if (cmd->cmd_report.log_rh) {
+ dm_report_free(cmd->cmd_report.log_rh);
+ cmd->cmd_report.log_rh = NULL;
+ }
}
/*
* TODO: think about better alternatives:
7 years, 9 months
master - libdm: report: add dm_report_group_output_and_pop_all
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=7d1125e5b78501...
Commit: 7d1125e5b785014d3bae34acd158c528a4222871
Parent: d86caf952ee208ef2b3b56937af670c76db75ba0
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Thu Aug 4 13:50:45 2016 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Aug 9 18:24:45 2016 +0200
libdm: report: add dm_report_group_output_and_pop_all
The dm_report_group_output_and_pop_all calls dm_report_output and
dm_report_group_pop for all the items that are currently in report
group. This is just a shortcut that makes it easier to output and
pop group's content so the group handle can be reused again without
a need to initialize and configure it again.
The functionality of dm_report_group_output_and_pop_all is the
same as dm_report_destroy but without destroying the report group
handle.
---
libdm/.exported_symbols.DM_1_02_133 | 1 +
libdm/libdevmapper.h | 1 +
libdm/libdm-report.c | 59 ++++++++++++----------------------
3 files changed, 23 insertions(+), 38 deletions(-)
diff --git a/libdm/.exported_symbols.DM_1_02_133 b/libdm/.exported_symbols.DM_1_02_133
index 4318f80..f104808 100644
--- a/libdm/.exported_symbols.DM_1_02_133
+++ b/libdm/.exported_symbols.DM_1_02_133
@@ -1 +1,2 @@
dm_report_destroy_rows
+dm_report_group_output_and_pop_all
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index 844a455..5246b15 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -2974,6 +2974,7 @@ typedef enum {
struct dm_report_group *dm_report_group_create(dm_report_group_type_t type, void *data);
int dm_report_group_push(struct dm_report_group *group, struct dm_report *report, void *data);
int dm_report_group_pop(struct dm_report_group *group);
+int dm_report_group_output_and_pop_all(struct dm_report_group *group);
int dm_report_group_destroy(struct dm_report_group *group);
/*
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index d1f2dd8..fed79d4 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -5028,57 +5028,40 @@ int dm_report_group_pop(struct dm_report_group *group)
return 1;
}
-static int _report_group_destroy_single(void)
+int dm_report_group_output_and_pop_all(struct dm_report_group *group)
{
- return 1;
-}
+ struct report_group_item *item, *tmp_item;
-static int _report_group_destroy_basic(void)
-{
- return 1;
-}
+ dm_list_iterate_items_safe(item, tmp_item, &group->items) {
+ if (!item->parent) {
+ item->store.finished_count = 0;
+ continue;
+ }
+ if (item->report && !dm_report_output(item->report))
+ return_0;
+ if (!dm_report_group_pop(group))
+ return_0;
+ }
+
+ if (group->type == DM_REPORT_GROUP_JSON) {
+ _json_output_start(group);
+ log_print(JSON_OBJECT_END);
+ group->indent -= JSON_INDENT_UNIT;
+ }
-static int _report_group_destroy_json(struct dm_report_group *group)
-{
- _json_output_start(group);
- log_print(JSON_OBJECT_END);
return 1;
}
int dm_report_group_destroy(struct dm_report_group *group)
{
- struct report_group_item *item, *tmp_item;
- int r = 0;
+ int r = 1;
if (!group)
return 1;
- dm_list_iterate_items_safe(item, tmp_item, &group->items) {
- if (item->report && !dm_report_output(item->report))
- goto_out;
- if (!dm_report_group_pop(group))
- goto_out;
- }
-
- switch (group->type) {
- case DM_REPORT_GROUP_SINGLE:
- if (!_report_group_destroy_single())
- goto_out;
- break;
- case DM_REPORT_GROUP_BASIC:
- if (!_report_group_destroy_basic())
- goto_out;
- break;
- case DM_REPORT_GROUP_JSON:
- if (!_report_group_destroy_json(group))
- goto_out;
- break;
- default:
- goto_out;
- }
+ if (!dm_report_group_output_and_pop_all(group))
+ r = 0;
- r = 1;
-out:
dm_pool_destroy(group->mem);
return r;
}
7 years, 9 months
master - libdm: report: postpone printing of JSON starting '{' character till it's needed
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=d86caf952ee208...
Commit: d86caf952ee208ef2b3b56937af670c76db75ba0
Parent: 9c2113928430e25a4cc7b68a94aca7d8388b3b60
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Thu Aug 4 11:05:20 2016 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Aug 9 18:24:45 2016 +0200
libdm: report: postpone printing of JSON starting '{' character till it's needed
This patch moves printing of starting '{' character for JSON output up
untili it's known there's any further output following - either the
content or ending '}' character.
Also, remove unnecessary switch for different report group types and
calling individual functions to handle dm_report_group_create as that
code is shared for all existing types at the moment.
---
libdm/libdm-report.c | 50 ++++++++++++++------------------------------------
1 files changed, 14 insertions(+), 36 deletions(-)
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index 886d8a2..d1f2dd8 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -4674,6 +4674,14 @@ static struct report_group_item *_get_topmost_report_group_item(struct dm_report
return item;
}
+static void _json_output_start(struct dm_report_group *group)
+{
+ if (!group->indent) {
+ log_print(JSON_OBJECT_START);
+ group->indent += JSON_INDENT_UNIT;
+ }
+}
+
static int _json_output_array_start(struct dm_pool *mem, struct report_group_item *item)
{
const char *name = (const char *) item->data;
@@ -4713,6 +4721,8 @@ bad:
static int _prepare_json_report_output(struct dm_report *rh)
{
+ _json_output_start(rh->group_item->group);
+
if (rh->group_item->output_done && dm_list_empty(&rh->rows))
return 1;
@@ -4795,23 +4805,6 @@ void dm_report_destroy_rows(struct dm_report *rh)
_destroy_rows(rh);
}
-static int _report_group_create_single(struct dm_report_group *group)
-{
- return 1;
-}
-
-static int _report_group_create_basic(struct dm_report_group *group)
-{
- return 1;
-}
-
-static int _report_group_create_json(struct dm_report_group *group)
-{
- log_print(JSON_OBJECT_START);
- group->indent += JSON_INDENT_UNIT;
- return 1;
-}
-
struct dm_report_group *dm_report_group_create(dm_report_group_type_t type, void *data)
{
struct dm_report_group *group;
@@ -4839,23 +4832,6 @@ struct dm_report_group *dm_report_group_create(dm_report_group_type_t type, void
dm_list_add_h(&group->items, &item->list);
- switch (type) {
- case DM_REPORT_GROUP_SINGLE:
- if (!_report_group_create_single(group))
- goto_bad;
- break;
- case DM_REPORT_GROUP_BASIC:
- if (!_report_group_create_basic(group))
- goto_bad;
- break;
- case DM_REPORT_GROUP_JSON:
- if (!_report_group_create_json(group))
- goto_bad;
- break;
- default:
- goto_bad;
- }
-
return group;
bad:
dm_pool_destroy(mem);
@@ -4907,6 +4883,7 @@ static int _report_group_push_json(struct report_group_item *item, const char *n
DM_REPORT_OUTPUT_COLUMNS_AS_ROWS);
item->report->flags |= DM_REPORT_OUTPUT_BUFFERED;
} else {
+ _json_output_start(item->group);
if (name) {
if (!_json_output_array_start(item->group->mem, item))
return_0;
@@ -5061,8 +5038,9 @@ static int _report_group_destroy_basic(void)
return 1;
}
-static int _report_group_destroy_json(void)
+static int _report_group_destroy_json(struct dm_report_group *group)
{
+ _json_output_start(group);
log_print(JSON_OBJECT_END);
return 1;
}
@@ -5092,7 +5070,7 @@ int dm_report_group_destroy(struct dm_report_group *group)
goto_out;
break;
case DM_REPORT_GROUP_JSON:
- if (!_report_group_destroy_json())
+ if (!_report_group_destroy_json(group))
goto_out;
break;
default:
7 years, 9 months
master - libdm: report: add dm_report_destroy_rows
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=9c2113928430e2...
Commit: 9c2113928430e25a4cc7b68a94aca7d8388b3b60
Parent: 5649834f7d62b9cba3b06c1b5d0dd425978f3ad2
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Thu Aug 4 09:42:33 2016 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Aug 9 18:24:45 2016 +0200
libdm: report: add dm_report_destroy_rows
Calling dm_report_destroy_rows makes it possible to destroy any report
content we have but at the same time it doesn't destroy the report
handle itself, thus it's possible to reuse that handle again for new
report content.
Functionally, this is the same as calling dm_report_output with the
report handle but omitting the output iself. This functionality may
be useful if we, for whatever reason, need to discard the report
content and start a fresh new one but with the same report configuration
and initialization and thus we can just reuse the existing handle.
---
libdm/.exported_symbols.DM_1_02_133 | 1 +
libdm/libdevmapper.h | 5 +++++
libdm/libdm-report.c | 5 +++++
3 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/libdm/.exported_symbols.DM_1_02_133 b/libdm/.exported_symbols.DM_1_02_133
new file mode 100644
index 0000000..4318f80
--- /dev/null
+++ b/libdm/.exported_symbols.DM_1_02_133
@@ -0,0 +1 @@
+dm_report_destroy_rows
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index 2270016..844a455 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -2907,6 +2907,11 @@ int dm_report_compact_given_fields(struct dm_report *rh, const char *fields);
*/
int dm_report_is_empty(struct dm_report *rh);
+/*
+ * Destroy report content without doing output.
+ */
+void dm_report_destroy_rows(struct dm_report *rh);
+
int dm_report_output(struct dm_report *rh);
/*
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index 02fead5..886d8a2 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -4790,6 +4790,11 @@ out:
return r;
}
+void dm_report_destroy_rows(struct dm_report *rh)
+{
+ _destroy_rows(rh);
+}
+
static int _report_group_create_single(struct dm_report_group *group)
{
return 1;
7 years, 9 months
master - lvmcmdline: return 0/NULL if cmd->arg_values not set and arg_count/grouped_arg_count/arg_value called
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=5649834f7d62b9...
Commit: 5649834f7d62b9cba3b06c1b5d0dd425978f3ad2
Parent: 1fde4bf4d08cbf579dbbe5eb08661eac67eb68f7
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Thu Aug 4 09:32:05 2016 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Aug 9 18:24:45 2016 +0200
lvmcmdline: return 0/NULL if cmd->arg_values not set and arg_count/grouped_arg_count/arg_value called
We may call arg_count/grouped_arg_count/arg_value soon enough that
cmd->arg_values is not set yet.
Normally, when running a command, we execute lvm_run_command which in
turn calls _process_command_line to allocate and parse the command line
values and stores them in cmd->arg_values.
However, if we run lvm shell, this one doesn't accept any command line
options and we parse the command line for each command that is executed
within the lvm shell then. If we used any code that tries to access
cmd->arg_values through any of the the arg handling functions too
early, we could end up with a segfault due to uninitialized (NULL)
cmd->arg_values.
This patch just saves extra checks in all the code where arg handling
may be called too early so that the cmd->arg_values is not set up yet.
This does not apply to any of existing code, but subsequent patches
will need that.
---
tools/lvmcmdline.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 629fb14..81118c1 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -63,12 +63,12 @@ static struct cmdline_context _cmdline;
/* Command line args */
unsigned arg_count(const struct cmd_context *cmd, int a)
{
- return cmd->arg_values[a].count;
+ return cmd->arg_values ? cmd->arg_values[a].count : 0;
}
unsigned grouped_arg_count(const struct arg_values *av, int a)
{
- return av[a].count;
+ return av ? av[a].count : 0;
}
unsigned arg_is_set(const struct cmd_context *cmd, int a)
@@ -182,7 +182,7 @@ const char *arg_long_option_name(int a)
const char *arg_value(const struct cmd_context *cmd, int a)
{
- return cmd->arg_values[a].value;
+ return cmd->arg_values ? cmd->arg_values[a].value : NULL;
}
const char *arg_str_value(const struct cmd_context *cmd, int a, const char *def)
7 years, 9 months
master - refactor: move report grouping and log reporting handles from processing_handle to cmd_context
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=1fde4bf4d08cbf...
Commit: 1fde4bf4d08cbf579dbbe5eb08661eac67eb68f7
Parent: ef69934746e7c31aef224129b19ac0cfb8101b5b
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Wed Aug 3 15:37:14 2016 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Aug 9 18:24:45 2016 +0200
refactor: move report grouping and log reporting handles from processing_handle to cmd_context
With patches that will follow, this will make it possible to widen log
report coverage when commands are executed from lvm shell so the amount
of messages that may end up in stderr/stdout instead of log report are
minimized.
---
lib/commands/toolcontext.c | 6 ++-
lib/commands/toolcontext.h | 13 ++++++-
lib/report/report.h | 4 +--
tools/lvm.c | 10 +++---
tools/reporter.c | 78 ++++++++++++++++++--------------------------
tools/toollib.c | 24 ++++---------
tools/toollib.h | 5 ---
7 files changed, 61 insertions(+), 79 deletions(-)
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index 82e990e..57d3bdc 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -2218,8 +2218,10 @@ void destroy_toolcontext(struct cmd_context *cmd)
if (cmd->cft_def_hash)
dm_hash_destroy(cmd->cft_def_hash);
- if (cmd->log_rh)
- dm_report_free(cmd->log_rh);
+ if (cmd->cmd_report.log_rh) {
+ dm_report_free(cmd->cmd_report.log_rh);
+ cmd->cmd_report.log_rh = NULL;
+ }
if (cmd->libmem)
dm_pool_destroy(cmd->libmem);
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index ee849b5..c6d938d 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -66,6 +66,15 @@ struct cmd_context_initialized_parts {
unsigned connections:1;
};
+struct cmd_report {
+ int log_only;
+ dm_report_group_type_t report_group_type;
+ struct dm_report_group *report_group;
+ struct dm_report *log_rh;
+ const char *log_name;
+ log_report_t saved_log_report_state;
+};
+
/* FIXME Split into tool & library contexts */
/* command-instance-related variables needed by library */
struct cmd_context {
@@ -186,9 +195,9 @@ struct cmd_context {
char proc_dir[PATH_MAX];
/*
- * Command log reporting.
+ * Reporting.
*/
- struct dm_report *log_rh; /* keep log report of last cmd for further queries if cmd line is interactive (e.g. lvm shell) */
+ struct cmd_report cmd_report;
/*
* Buffers.
diff --git a/lib/report/report.h b/lib/report/report.h
index ba3a8fa..cf4145e 100644
--- a/lib/report/report.h
+++ b/lib/report/report.h
@@ -81,9 +81,7 @@ struct processing_handle;
typedef int (*field_report_fn) (struct report_handle * dh, struct field * field,
const void *data);
-int report_format_init(struct cmd_context *cmd, dm_report_group_type_t *report_group_type,
- struct dm_report_group **report_group, struct dm_report **log_rh,
- int *log_only, log_report_t *saved_log_report_state);
+int report_format_init(struct cmd_context *cmd);
void *report_init(struct cmd_context *cmd, const char *format, const char *keys,
report_type_t *report_type, const char *separator,
diff --git a/tools/lvm.c b/tools/lvm.c
index ec8a352..c2d5096 100644
--- a/tools/lvm.c
+++ b/tools/lvm.c
@@ -185,12 +185,12 @@ static int _log_shell_command_status(struct cmd_context *cmd, int ret_code)
{
log_report_t log_state;
- if (!cmd->log_rh)
+ if (!cmd->cmd_report.log_rh)
return 1;
log_state = log_get_report_state();
- return report_cmdlog(cmd->log_rh, REPORT_OBJECT_CMDLOG_NAME,
+ return report_cmdlog(cmd->cmd_report.log_rh, REPORT_OBJECT_CMDLOG_NAME,
log_get_report_context_name(log_state.context),
log_get_report_object_type_name(log_state.object_type),
log_state.object_name, log_state.object_id,
@@ -263,10 +263,10 @@ int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
is_lastlog_cmd = !strcmp(argv[0], "lastlog");
- if (cmd->log_rh && !is_lastlog_cmd) {
+ if (cmd->cmd_report.log_rh && !is_lastlog_cmd) {
/* drop old log report */
- dm_report_free(cmd->log_rh);
- cmd->log_rh = NULL;
+ dm_report_free(cmd->cmd_report.log_rh);
+ cmd->cmd_report.log_rh = NULL;
}
ret = lvm_run_command(cmd, argc, argv);
diff --git a/tools/reporter.c b/tools/reporter.c
index ad0a356..d386efc 100644
--- a/tools/reporter.c
+++ b/tools/reporter.c
@@ -1055,7 +1055,7 @@ static int _do_report(struct cmd_context *cmd, struct processing_handle *handle,
goto_out;
if (!(args->log_only && (single_args->report_type != CMDLOG))) {
- if (!dm_report_group_push(handle->report_group, report_handle, (void *) single_args->report_name))
+ if (!dm_report_group_push(cmd->cmd_report.report_group, report_handle, (void *) single_args->report_name))
goto_out;
report_in_group = 1;
}
@@ -1174,7 +1174,7 @@ static int _do_report(struct cmd_context *cmd, struct processing_handle *handle,
unlock_vg(cmd, NULL, VG_GLOBAL);
out:
if (report_handle) {
- if (report_in_group && !dm_report_group_pop(handle->report_group))
+ if (report_in_group && !dm_report_group_pop(cmd->cmd_report.report_group))
stack;
dm_report_free(report_handle);
}
@@ -1197,7 +1197,7 @@ static int _full_report_single(struct cmd_context *cmd,
args->full_report_vg = vg;
- if (!args->log_only && !dm_report_group_push(handle->report_group, NULL, NULL))
+ if (!args->log_only && !dm_report_group_push(cmd->cmd_report.report_group, NULL, NULL))
goto out;
if (orphan) {
@@ -1213,7 +1213,7 @@ static int _full_report_single(struct cmd_context *cmd,
stack;
}
- if (!args->log_only && !dm_report_group_pop(handle->report_group))
+ if (!args->log_only && !dm_report_group_pop(cmd->cmd_report.report_group))
goto_out;
out:
args->full_report_vg = NULL;
@@ -1367,15 +1367,15 @@ static int _report(struct cmd_context *cmd, int argc, char **argv, report_type_t
handle->internal_report_for_select = 0;
handle->include_historical_lvs = cmd->include_historical_lvs;
- args.report_group_type = handle->report_group_type;
- args.log_only = handle->log_only;
+ args.report_group_type = cmd->cmd_report.report_group_type;
+ args.log_only = cmd->cmd_report.log_only;
if (!_config_report(cmd, &args, single_args)) {
destroy_processing_handle(cmd, handle);
return_ECMD_FAILED;
}
- if (!args.log_only && !dm_report_group_push(handle->report_group, NULL, report_name)) {
+ if (!args.log_only && !dm_report_group_push(cmd->cmd_report.report_group, NULL, report_name)) {
log_error("Failed to add main report section to report group.");
destroy_processing_handle(cmd, handle);
return ECMD_FAILED;
@@ -1433,9 +1433,7 @@ int devtypes(struct cmd_context *cmd, int argc, char **argv)
#define REPORT_FORMAT_NAME_BASIC "basic"
#define REPORT_FORMAT_NAME_JSON "json"
-int report_format_init(struct cmd_context *cmd, dm_report_group_type_t *report_group_type,
- struct dm_report_group **report_group, struct dm_report **log_rh,
- int *log_only, log_report_t *saved_log_report_state)
+int report_format_init(struct cmd_context *cmd)
{
int config_set = find_config_tree_node(cmd, report_output_format_CFG, NULL) != NULL;
const char *config_format_str = find_config_tree_str(cmd, report_output_format_CFG, NULL);
@@ -1446,7 +1444,7 @@ int report_format_init(struct cmd_context *cmd, dm_report_group_type_t *report_g
struct dm_report_group *new_report_group;
struct dm_report *tmp_log_rh = NULL;
- args.log_only = arg_is_set(cmd, logonly_ARG) || *log_rh;
+ args.log_only = arg_is_set(cmd, logonly_ARG);
report_command_log = args.log_only || find_config_tree_bool(cmd, log_report_command_log_CFG, NULL);
if (!format_str || !strcmp(format_str, REPORT_FORMAT_NAME_BASIC)) {
@@ -1462,10 +1460,8 @@ int report_format_init(struct cmd_context *cmd, dm_report_group_type_t *report_g
return 0;
}
- if (report_group_type)
- *report_group_type = args.report_group_type;
- if (log_only)
- *log_only = args.log_only;
+ cmd->cmd_report.report_group_type = args.report_group_type;
+ cmd->cmd_report.log_only = args.log_only;
if (!(new_report_group = dm_report_group_create(args.report_group_type, NULL))) {
log_error("Failed to create report group.");
@@ -1476,40 +1472,33 @@ int report_format_init(struct cmd_context *cmd, dm_report_group_type_t *report_g
single_args = &args.single_args[REPORT_IDX_LOG];
single_args->report_type = CMDLOG;
- if (!*log_rh) {
- if (!_config_report(cmd, &args, single_args))
- goto_bad;
+ if (!_config_report(cmd, &args, single_args))
+ goto_bad;
- if (!(tmp_log_rh = report_init(NULL, single_args->options, single_args->keys, &single_args->report_type,
- args.separator, args.aligned, args.buffered, args.headings,
- args.field_prefixes, args.quoted, args.columns_as_rows,
- single_args->selection, 1))) {
- log_error("Failed to create log report.");
- goto bad;
- }
- } else {
- /*
- * We reusing existing log report handle.
- * Just get report's name and prefix now.
- */
- if (!_set_report_prefix_and_name(&args, single_args))
- goto_bad;
+ if (!(tmp_log_rh = report_init(NULL, single_args->options, single_args->keys, &single_args->report_type,
+ args.separator, args.aligned, args.buffered, args.headings,
+ args.field_prefixes, args.quoted, args.columns_as_rows,
+ single_args->selection, 1))) {
+ log_error("Failed to create log report.");
+ goto bad;
}
- if (!(dm_report_group_push(new_report_group, tmp_log_rh ? : *log_rh, (void *) single_args->report_name))) {
+ if (!(dm_report_group_push(new_report_group, tmp_log_rh, (void *) single_args->report_name))) {
log_error("Failed to add log report to report group.");
goto bad;
}
+
+ cmd->cmd_report.log_rh = tmp_log_rh;
+ if (!(cmd->cmd_report.log_name = dm_pool_strdup(cmd->libmem, single_args->report_name))) {
+ log_error("Failed to set log report name for command context.");
+ goto bad;
+ }
}
- *report_group = new_report_group;
- if (tmp_log_rh)
- *log_rh = tmp_log_rh;
+ cmd->cmd_report.report_group = new_report_group;
+ cmd->cmd_report.saved_log_report_state = log_get_report_state();
+ log_set_report(cmd->cmd_report.log_rh);
- if (saved_log_report_state) {
- *saved_log_report_state = log_get_report_state();
- log_set_report(*log_rh);
- }
return 1;
bad:
if (!dm_report_group_destroy(new_report_group))
@@ -1525,23 +1514,20 @@ int lastlog(struct cmd_context *cmd, int argc, char **argv)
const char *selection;
int r = ECMD_FAILED;
- if (!cmd->log_rh) {
+ if (!cmd->cmd_report.log_rh) {
log_error("No log report stored.");
goto out;
}
- if (!report_format_init(cmd, NULL, &report_group, &cmd->log_rh, NULL, NULL))
- goto_out;
-
if (!_do_report_get_selection(cmd, CMDLOG, 1, NULL, &selection))
goto_out;
- if (!dm_report_set_selection(cmd->log_rh, selection)) {
+ if (!dm_report_set_selection(cmd->cmd_report.log_rh, selection)) {
log_error("Failed to set selection for log report.");
goto out;
}
- if (!dm_report_output(cmd->log_rh) ||
+ if (!dm_report_output(cmd->cmd_report.log_rh) ||
!dm_report_group_pop(report_group))
goto_out;
diff --git a/tools/toollib.c b/tools/toollib.c
index 4cd9020..bcf94ea 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1754,15 +1754,13 @@ struct processing_handle *init_processing_handle(struct cmd_context *cmd, struct
handle->internal_report_for_select = arg_is_set(cmd, select_ARG);
handle->include_historical_lvs = cmd->include_historical_lvs;
- if (!parent_handle) {
- if (!report_format_init(cmd, &handle->report_group_type, &handle->report_group,
- &handle->log_rh, &handle->log_only,
- &handle->saved_log_report_state)) {
+ if (!parent_handle && !cmd->cmd_report.report_group) {
+ if (!report_format_init(cmd)) {
dm_pool_free(cmd->mem, handle);
return NULL;
}
} else
- handle->saved_log_report_state = log_get_report_state();
+ cmd->cmd_report.saved_log_report_state = log_get_report_state();
log_set_report_context(LOG_REPORT_CONTEXT_PROCESSING);
return handle;
@@ -1798,19 +1796,13 @@ void destroy_processing_handle(struct cmd_context *cmd, struct processing_handle
if (handle->selection_handle && handle->selection_handle->selection_rh)
dm_report_free(handle->selection_handle->selection_rh);
- log_restore_report_state(handle->saved_log_report_state);
+ log_restore_report_state(cmd->cmd_report.saved_log_report_state);
- if (!dm_report_group_destroy(handle->report_group))
+ if (!dm_report_group_destroy(cmd->cmd_report.report_group))
stack;
- if (handle->log_rh) {
- if (cmd->is_interactive) {
- /*
- * Keep log report if we're interactive so
- * we can do further queries on this report.
- */
- cmd->log_rh = handle->log_rh;
- } else
- dm_report_free(handle->log_rh);
+ if (!cmd->is_interactive && cmd->cmd_report.log_rh) {
+ dm_report_free(cmd->cmd_report.log_rh);
+ cmd->cmd_report.log_rh = NULL;
}
/*
* TODO: think about better alternatives:
diff --git a/tools/toollib.h b/tools/toollib.h
index ed5ec76..d5de733 100644
--- a/tools/toollib.h
+++ b/tools/toollib.h
@@ -73,11 +73,6 @@ struct processing_handle {
int internal_report_for_select;
int include_historical_lvs;
struct selection_handle *selection_handle;
- dm_report_group_type_t report_group_type;
- struct dm_report_group *report_group;
- struct dm_report *log_rh;
- int log_only;
- log_report_t saved_log_report_state;
void *custom_handle;
};
7 years, 9 months
master - shell: also collect last command's return code for subsequent 'lastlog' invocation
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=ef69934746e7c3...
Commit: ef69934746e7c31aef224129b19ac0cfb8101b5b
Parent: 06ce9b4e4238da58d6c2acda7878dbc51245a8fc
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Mon Jul 25 12:20:22 2016 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Aug 9 18:24:45 2016 +0200
shell: also collect last command's return code for subsequent 'lastlog' invocation
Add new log_context=shell and with log_object_type=cmd and
log_object_name=<command_name> for command log report to collect
overall return code from last command (this is reported under
log_type=status).
---
lib/log/log.c | 2 ++
lib/log/lvm-logging.h | 6 ++++++
tools/lvm.c | 37 +++++++++++++++++++++++++++++++++++--
3 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/lib/log/log.c b/lib/log/log.c
index d021ffe..c933154 100644
--- a/lib/log/log.c
+++ b/lib/log/log.c
@@ -449,6 +449,7 @@ static const char *_get_log_level_name(int use_stderr, int level)
const char *log_get_report_context_name(log_report_context_t context)
{
static const char *log_context_names[LOG_REPORT_CONTEXT_COUNT] = {[LOG_REPORT_CONTEXT_NULL] = "",
+ [LOG_REPORT_CONTEXT_SHELL] = "shell",
[LOG_REPORT_CONTEXT_PROCESSING] = "processing"};
return log_context_names[context];
}
@@ -457,6 +458,7 @@ const char *log_get_report_context_name(log_report_context_t context)
const char *log_get_report_object_type_name(log_report_object_type_t object_type)
{
static const char *log_object_type_names[LOG_REPORT_OBJECT_TYPE_COUNT] = {[LOG_REPORT_OBJECT_TYPE_NULL] = "",
+ [LOG_REPORT_OBJECT_TYPE_CMD] = "cmd",
[LOG_REPORT_OBJECT_TYPE_ORPHAN] = "orphan",
[LOG_REPORT_OBJECT_TYPE_PV] = "pv",
[LOG_REPORT_OBJECT_TYPE_LABEL] = "label",
diff --git a/lib/log/lvm-logging.h b/lib/log/lvm-logging.h
index 7ddbf2c..64c1928 100644
--- a/lib/log/lvm-logging.h
+++ b/lib/log/lvm-logging.h
@@ -77,12 +77,14 @@ void syslog_suppress(int suppress);
/* Hooks to handle logging through report. */
typedef enum {
LOG_REPORT_CONTEXT_NULL,
+ LOG_REPORT_CONTEXT_SHELL,
LOG_REPORT_CONTEXT_PROCESSING,
LOG_REPORT_CONTEXT_COUNT
} log_report_context_t;
typedef enum {
LOG_REPORT_OBJECT_TYPE_NULL,
+ LOG_REPORT_OBJECT_TYPE_CMD,
LOG_REPORT_OBJECT_TYPE_ORPHAN,
LOG_REPORT_OBJECT_TYPE_PV,
LOG_REPORT_OBJECT_TYPE_LABEL,
@@ -101,6 +103,10 @@ typedef struct log_report {
const char *object_group_id;
} log_report_t;
+#define LOG_STATUS_NAME "status"
+#define LOG_STATUS_SUCCESS "success"
+#define LOG_STATUS_FAILURE "failure"
+
log_report_t log_get_report_state(void);
void log_restore_report_state(log_report_t log_report);
diff --git a/tools/lvm.c b/tools/lvm.c
index 62cc141..ec8a352 100644
--- a/tools/lvm.c
+++ b/tools/lvm.c
@@ -181,9 +181,29 @@ static void _write_history(void)
log_very_verbose("Couldn't write history to %s.", hist_file);
}
+static int _log_shell_command_status(struct cmd_context *cmd, int ret_code)
+{
+ log_report_t log_state;
+
+ if (!cmd->log_rh)
+ return 1;
+
+ log_state = log_get_report_state();
+
+ return report_cmdlog(cmd->log_rh, REPORT_OBJECT_CMDLOG_NAME,
+ log_get_report_context_name(log_state.context),
+ log_get_report_object_type_name(log_state.object_type),
+ log_state.object_name, log_state.object_id,
+ log_state.object_group, log_state.object_group_id,
+ ret_code == ECMD_PROCESSED ? REPORT_OBJECT_CMDLOG_SUCCESS
+ : REPORT_OBJECT_CMDLOG_FAILURE,
+ stored_errno(), ret_code);
+}
+
int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
{
- int argc, ret;
+ log_report_t saved_log_report_state = log_get_report_state();
+ int is_lastlog_cmd, argc, ret;
char *input = NULL, *args[MAX_ARGS], **argv;
rl_readline_name = "lvm";
@@ -194,7 +214,11 @@ int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
_cmdline = cmdline;
cmd->is_interactive = 1;
+ log_set_report_context(LOG_REPORT_CONTEXT_SHELL);
+ log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_CMD);
+
while (1) {
+ log_set_report_object_name_and_id(NULL, NULL);
free(input);
input = readline("lvm> ");
@@ -229,13 +253,17 @@ int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
if (!argc)
continue;
+ log_set_report_object_name_and_id(argv[0], NULL);
+
if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
remove_history(history_length - 1);
log_error("Exiting.");
break;
}
- if (cmd->log_rh && strcmp(argv[0], "lastlog")) {
+ is_lastlog_cmd = !strcmp(argv[0], "lastlog");
+
+ if (cmd->log_rh && !is_lastlog_cmd) {
/* drop old log report */
dm_report_free(cmd->log_rh);
cmd->log_rh = NULL;
@@ -251,7 +279,12 @@ int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
log_error("Command failed with status code %d.", ret);
}
_write_history();
+
+ if (!is_lastlog_cmd)
+ _log_shell_command_status(cmd, ret);
}
+
+ log_restore_report_state(saved_log_report_state);
cmd->is_interactive = 0;
free(input);
7 years, 9 months
master - log: separate output and make it possible to use given FDs
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=06ce9b4e4238da...
Commit: 06ce9b4e4238da58d6c2acda7878dbc51245a8fc
Parent: bb9789f2b3404893dcb92cfc2d1eeec002c90140
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Fri Jul 8 16:47:51 2016 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Tue Aug 9 18:24:45 2016 +0200
log: separate output and make it possible to use given FDs
Currently, the output is separated in 3 parts and each part can go into
a separate and user-defined file descriptor:
- common output (stdout by default, customizable by LVM_OUT_FD environment variable)
- error output (stderr by default, customizable by LVM_ERR_FD environment variable)
- report output (stdout by default, customizable by LVM_REPORT_FD environment variable)
For example, each type of output goes to different output file:
[0] fedora/~ # export LVM_REPORT_FD=3
[0] fedora/~ # lvs fedora vg/abc 1>out 2>err 3>report
[0] fedora/~ # cat out
[0] fedora/~ # cat err
Volume group "vg" not found
Cannot process volume group vg
[0] fedora/~ # cat report
LV VG Attr LSize Layout Role CTime
root fedora -wi-ao---- 19.00g linear public Wed May 27 2015 08:09:21
swap fedora -wi-ao---- 500.00m linear public Wed May 27 2015 08:09:21
Another example in LVM shell where the report goes to "report" file:
[0] fedora/~ # export LVM_REPORT_FD=3
[0] fedora/~ # lvm 3>report
(in lvm shell)
lvm> vgs
(content of "report" file)
[1] fedora/~ # cat report
VG #PV #LV #SN Attr VSize VFree
fedora 1 2 0 wz--n- 19.49g 0
(in lvm shell)
lvm> lvs
(content of "report" file)
[1] fedora/~ # cat report
VG #PV #LV #SN Attr VSize VFree
fedora 1 2 0 wz--n- 19.49g 0
LV VG Attr LSize Layout Role CTime
root fedora -wi-ao---- 19.00g linear public Wed May 27 2015 08:09:21
swap fedora -wi-ao---- 500.00m linear public Wed May 27 2015 08:09:21
---
lib/commands/toolcontext.c | 50 ++-----------
lib/log/log.c | 172 +++++++++++++++++++++++++++++++++++++++++---
lib/log/lvm-logging.h | 5 ++
lib/misc/lvm-file.h | 6 ++
man/lvm.8.in | 9 +++
tools/lvmcmdline.c | 62 ++++++++++++++--
6 files changed, 245 insertions(+), 59 deletions(-)
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index d3bd5bc..82e990e 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -1650,37 +1650,6 @@ static void _init_globals(struct cmd_context *cmd)
}
/*
- * Close and reopen stream on file descriptor fd.
- */
-static int _reopen_stream(FILE *stream, int fd, const char *mode, const char *name, FILE **new_stream)
-{
- int fd_copy, new_fd;
-
- if ((fd_copy = dup(fd)) < 0) {
- log_sys_error("dup", name);
- return 0;
- }
-
- if (fclose(stream))
- log_sys_error("fclose", name);
-
- if ((new_fd = dup2(fd_copy, fd)) < 0)
- log_sys_error("dup2", name);
- else if (new_fd != fd)
- log_error("dup2(%d, %d) returned %d", fd_copy, fd, new_fd);
-
- if (close(fd_copy) < 0)
- log_sys_error("close", name);
-
- if (!(*new_stream = fdopen(fd, mode))) {
- log_sys_error("fdopen", name);
- return 0;
- }
-
- return 1;
-}
-
-/*
* init_connections();
* _init_lvmetad();
* lvmetad_disconnect(); (close previous connection)
@@ -1835,7 +1804,6 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
unsigned set_filters)
{
struct cmd_context *cmd;
- FILE *new_stream;
int flags;
#ifdef M_MMAP_MAX
@@ -1885,9 +1853,8 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
if (is_valid_fd(STDIN_FILENO) &&
((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_WRONLY) {
- if (!_reopen_stream(stdin, STDIN_FILENO, "r", "stdin", &new_stream))
+ if (!reopen_standard_stream(&stdin, "r"))
goto_out;
- stdin = new_stream;
if (setvbuf(stdin, cmd->linebuffer, _IOLBF, linebuffer_size)) {
log_sys_error("setvbuf", "");
goto out;
@@ -1897,9 +1864,8 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
if (is_valid_fd(STDOUT_FILENO) &&
((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_RDONLY) {
- if (!_reopen_stream(stdout, STDOUT_FILENO, "w", "stdout", &new_stream))
+ if (!reopen_standard_stream(&stdout, "w"))
goto_out;
- stdout = new_stream;
if (setvbuf(stdout, cmd->linebuffer + linebuffer_size,
_IOLBF, linebuffer_size)) {
log_sys_error("setvbuf", "");
@@ -1911,7 +1877,6 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
/* Without buffering, must not use stdin/stdout */
init_silent(1);
#endif
-
/*
* Environment variable LVM_SYSTEM_DIR overrides this below.
*/
@@ -2227,7 +2192,6 @@ int refresh_toolcontext(struct cmd_context *cmd)
void destroy_toolcontext(struct cmd_context *cmd)
{
struct dm_config_tree *cft_cmdline;
- FILE *new_stream;
int flags;
if (cmd->dump_filter && cmd->filter && cmd->filter->dump &&
@@ -2266,20 +2230,18 @@ void destroy_toolcontext(struct cmd_context *cmd)
if (is_valid_fd(STDIN_FILENO) &&
((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_WRONLY) {
- if (_reopen_stream(stdin, STDIN_FILENO, "r", "stdin", &new_stream)) {
- stdin = new_stream;
+ if (reopen_standard_stream(&stdin, "r"))
setlinebuf(stdin);
- } else
+ else
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
}
if (is_valid_fd(STDOUT_FILENO) &&
((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) &&
(flags & O_ACCMODE) != O_RDONLY) {
- if (_reopen_stream(stdout, STDOUT_FILENO, "w", "stdout", &new_stream)) {
- stdout = new_stream;
+ if (reopen_standard_stream(&stdout, "w"))
setlinebuf(stdout);
- } else
+ else
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
}
diff --git a/lib/log/log.c b/lib/log/log.c
index 6d5fc59..d021ffe 100644
--- a/lib/log/log.c
+++ b/lib/log/log.c
@@ -18,6 +18,7 @@
#include "memlock.h"
#include "defaults.h"
#include "report.h"
+#include "lvm-file.h"
#include <stdio.h>
#include <stdarg.h>
@@ -59,6 +60,154 @@ static log_report_t _log_report = {
.object_group = NULL
};
+#define LOG_STREAM_BUFFER_SIZE 4096
+
+struct log_stream_item {
+ FILE *stream;
+ char *buffer;
+};
+
+struct log_stream {
+ struct log_stream_item out;
+ struct log_stream_item err;
+ struct log_stream_item report;
+} _log_stream = {{NULL, NULL},
+ {NULL, NULL},
+ {NULL, NULL}};
+
+#define out_stream (_log_stream.out.stream ? : stdout)
+#define err_stream (_log_stream.err.stream ? : stderr)
+#define report_stream (_log_stream.report.stream ? : stdout)
+
+static int _set_custom_log_stream(struct log_stream_item *stream_item, int custom_fd)
+{
+ FILE *final_stream = NULL;
+ int flags;
+ int r = 1;
+
+ if (custom_fd < 0)
+ goto out;
+
+ if (is_valid_fd(custom_fd)) {
+ if ((flags = fcntl(custom_fd, F_GETFL)) > 0) {
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ log_error("File descriptor %d already open in read-only "
+ "mode, expected write-only or read-write mode.",
+ (int) custom_fd);
+ r = 0;
+ goto out;
+ }
+ }
+
+ if (custom_fd == STDIN_FILENO) {
+ log_error("Can't set standard input for log output.");
+ r = 0;
+ goto out;
+ }
+
+ if (custom_fd == STDOUT_FILENO) {
+ final_stream = stdout;
+ goto out;
+ }
+
+ if (custom_fd == STDERR_FILENO) {
+ final_stream = stderr;
+ goto out;
+ }
+ }
+
+ if (!(final_stream = fdopen(custom_fd, "w"))) {
+ log_error("Failed to open stream for file descriptor %d.",
+ (int) custom_fd);
+ r = 0;
+ goto out;
+ }
+
+ if (!(stream_item->buffer = dm_malloc(LOG_STREAM_BUFFER_SIZE))) {
+ log_error("Failed to allocate buffer for stream on file "
+ "descriptor %d.", (int) custom_fd);
+ } else {
+ if (setvbuf(final_stream, stream_item->buffer, _IOLBF, LOG_STREAM_BUFFER_SIZE)) {
+ log_sys_error("setvbuf", "");
+ dm_free(stream_item->buffer);
+ stream_item->buffer = NULL;
+ }
+ }
+out:
+ stream_item->stream = final_stream;
+ return r;
+}
+
+int init_custom_log_streams(struct custom_fds *custom_fds)
+{
+ return _set_custom_log_stream(&_log_stream.out, custom_fds->out) &&
+ _set_custom_log_stream(&_log_stream.err, custom_fds->err) &&
+ _set_custom_log_stream(&_log_stream.report, custom_fds->report);
+}
+
+static void _check_and_replace_standard_log_streams(FILE *old_stream, FILE *new_stream)
+{
+ if (_log_stream.out.stream == old_stream)
+ _log_stream.out.stream = new_stream;
+
+ if (_log_stream.err.stream == old_stream)
+ _log_stream.err.stream = new_stream;
+
+ if (_log_stream.report.stream == old_stream)
+ _log_stream.report.stream = new_stream;
+}
+
+/*
+ * Close and reopen standard stream on file descriptor fd.
+ */
+int reopen_standard_stream(FILE **stream, const char *mode)
+{
+ int fd, fd_copy, new_fd;
+ const char *name;
+ FILE *old_stream = *stream;
+ FILE *new_stream;
+
+ if (old_stream == stdin) {
+ fd = STDIN_FILENO;
+ name = "stdin";
+ } else if (old_stream == stdout) {
+ fd = STDOUT_FILENO;
+ name = "stdout";
+ } else if (old_stream == stderr) {
+ fd = STDERR_FILENO;
+ name = "stderr";
+ } else {
+ log_error(INTERNAL_ERROR "reopen_standard_stream called on non-standard stream");
+ return 0;
+ }
+
+ if ((fd_copy = dup(fd)) < 0) {
+ log_sys_error("dup", name);
+ return 0;
+ }
+
+ if (fclose(old_stream))
+ log_sys_error("fclose", name);
+
+ if ((new_fd = dup2(fd_copy, fd)) < 0)
+ log_sys_error("dup2", name);
+ else if (new_fd != fd)
+ log_error("dup2(%d, %d) returned %d", fd_copy, fd, new_fd);
+
+ if (close(fd_copy) < 0)
+ log_sys_error("close", name);
+
+ if (!(new_stream = fdopen(fd, mode))) {
+ log_sys_error("fdopen", name);
+ return 0;
+ }
+
+ _check_and_replace_standard_log_streams(old_stream, new_stream);
+
+ *stream = new_stream;
+ return 1;
+}
+
void init_log_fn(lvm2_log_fn_t log_fn)
{
_lvm2_log_fn = log_fn;
@@ -207,10 +356,10 @@ void fin_log(void)
if (_log_to_file) {
if (dm_fclose(_log_file)) {
if (errno)
- fprintf(stderr, "failed to write log file: %s\n",
+ fprintf(err_stream, "failed to write log file: %s\n",
strerror(errno));
else
- fprintf(stderr, "failed to write log file\n");
+ fprintf(err_stream, "failed to write log file\n");
}
_log_to_file = 0;
@@ -378,8 +527,8 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
/* When newer glibc returns >= sizeof(locn), we will just log what
* has fit into buffer, it's '\0' terminated string */
if (n < 0) {
- fprintf(stderr, _("vsnprintf failed: skipping external "
- "logging function"));
+ fprintf(err_stream, _("vsnprintf failed: skipping external "
+ "logging function"));
goto log_it;
}
}
@@ -426,7 +575,7 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
_log_report.object_name, _log_report.object_id,
_log_report.object_group, _log_report.object_group_id,
message, _lvm_errno, 0))
- fprintf(stderr, _("failed to report cmdstatus"));
+ fprintf(err_stream, _("failed to report cmdstatus"));
else
logged_via_report = 1;
@@ -468,10 +617,10 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
break;
/* fall through */
default:
- /* Typically only log_warn goes to stdout */
- stream = (use_stderr || (level != _LOG_WARN)) ? stderr : stdout;
- if (stream == stderr)
- fflush(stdout);
+ /* Typically only log_warn goes to out_stream */
+ stream = (use_stderr || (level != _LOG_WARN)) ? err_stream : out_stream;
+ if (stream == err_stream)
+ fflush(out_stream);
fprintf(stream, "%s%s%s%s", buf, log_command_name(),
_msg_prefix, indent_spaces);
vfprintf(stream, trformat, ap);
@@ -556,6 +705,7 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
void print_log_libdm(int level, const char *file, int line, int dm_errno_or_class,
const char *format, ...)
{
+ FILE *orig_out_stream = out_stream;
va_list ap;
/*
@@ -567,9 +717,13 @@ void print_log_libdm(int level, const char *file, int line, int dm_errno_or_clas
((level & ~(_LOG_STDERR|_LOG_ONCE|_LOG_BYPASS_REPORT)) == _LOG_WARN))
level |= _LOG_BYPASS_REPORT;
+ _log_stream.out.stream = report_stream;
+
va_start(ap, format);
_vprint_log(level, file, line, dm_errno_or_class, format, ap);
va_end(ap);
+
+ _log_stream.out.stream = orig_out_stream;
}
log_report_t log_get_report_state(void)
diff --git a/lib/log/lvm-logging.h b/lib/log/lvm-logging.h
index 4101bc8..7ddbf2c 100644
--- a/lib/log/lvm-logging.h
+++ b/lib/log/lvm-logging.h
@@ -16,6 +16,8 @@
#ifndef _LVM_LOGGING_H
#define _LVM_LOGGING_H
+#include "lvm-file.h"
+
__attribute__ ((format(printf, 5, 6)))
void print_log(int level, const char *file, int line, int dm_errno_or_class,
const char *format, ...);
@@ -35,6 +37,9 @@ void print_log_libdm(int level, const char *file, int line, int dm_errno_or_clas
#include "log.h"
+int init_custom_log_streams(struct custom_fds *custom_fds);
+int reopen_standard_stream(FILE **stream, const char *mode);
+
typedef void (*lvm2_log_fn_t) (int level, const char *file, int line,
int dm_errno_or_class, const char *message);
diff --git a/lib/misc/lvm-file.h b/lib/misc/lvm-file.h
index c92f783..6128abd 100644
--- a/lib/misc/lvm-file.h
+++ b/lib/misc/lvm-file.h
@@ -16,6 +16,12 @@
#ifndef _LVM_FILE_H
#define _LVM_FILE_H
+struct custom_fds {
+ int out;
+ int err;
+ int report;
+};
+
/*
* Create a temporary filename, and opens a descriptor to the file.
*/
diff --git a/man/lvm.8.in b/man/lvm.8.in
index 0215783..76d05fd 100644
--- a/man/lvm.8.in
+++ b/man/lvm.8.in
@@ -748,6 +748,15 @@ All tools return a status code of zero on success or non-zero on failure.
Directory containing \fI.lvm_history\fP if the internal readline
shell is invoked.
.TP
+.B LVM_OUT_FD
+File descriptor to use for common output from LVM commands.
+.TP
+.B LVM_ERR_FD
+File descriptor to use for error output from LVM commands.
+.TP
+.B LVM_REPORT_FD
+File descriptor to use for report output from LVM commands.
+.TP
.B LVM_COMMAND_PROFILE
Name of default command profile to use for LVM commands. This profile
is overriden by direct use of \fB\-\-commandprofile\fP command line option.
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 3c3498a..629fb14 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -1838,6 +1838,39 @@ static int _check_standard_fds(void)
return 1;
}
+#define LVM_OUT_FD_ENV_VAR_NAME "LVM_OUT_FD"
+#define LVM_ERR_FD_ENV_VAR_NAME "LVM_ERR_FD"
+#define LVM_REPORT_FD_ENV_VAR_NAME "LVM_REPORT_FD"
+
+static int _do_get_custom_fd(const char *env_var_name, int *fd)
+{
+ const char *str;
+ char *endptr;
+ int tmp_fd;
+
+ *fd = -1;
+
+ if (!(str = getenv(env_var_name)))
+ return 1;
+
+ errno = 0;
+ tmp_fd = strtol(str, &endptr, 10);
+ if (errno || *endptr || (tmp_fd < 0) || (tmp_fd > INT_MAX)) {
+ log_error("%s: invalid file descriptor.", env_var_name);
+ return 0;
+ }
+
+ *fd = tmp_fd;
+ return 1;
+}
+
+static int _get_custom_fds(struct custom_fds *custom_fds)
+{
+ return _do_get_custom_fd(LVM_OUT_FD_ENV_VAR_NAME, &custom_fds->out) &&
+ _do_get_custom_fd(LVM_ERR_FD_ENV_VAR_NAME, &custom_fds->err) &&
+ _do_get_custom_fd(LVM_REPORT_FD_ENV_VAR_NAME, &custom_fds->report);
+}
+
static const char *_get_cmdline(pid_t pid)
{
static char _proc_cmdline[32];
@@ -1905,7 +1938,7 @@ static void _close_descriptor(int fd, unsigned suppress_warnings,
fprintf(stderr, " Parent PID %" PRIpid_t ": %s\n", ppid, parent_cmdline);
}
-static int _close_stray_fds(const char *command)
+static int _close_stray_fds(const char *command, struct custom_fds *custom_fds)
{
#ifndef VALGRIND_POOL
struct rlimit rlim;
@@ -1939,17 +1972,27 @@ static int _close_stray_fds(const char *command)
return 1;
}
- for (fd = 3; fd < (int)rlim.rlim_cur; fd++)
- _close_descriptor(fd, suppress_warnings, command, ppid,
- parent_cmdline);
+ for (fd = 3; fd < (int)rlim.rlim_cur; fd++) {
+ if ((fd != custom_fds->out) &&
+ (fd != custom_fds->err) &&
+ (fd != custom_fds->report)) {
+ _close_descriptor(fd, suppress_warnings, command, ppid,
+ parent_cmdline);
+ }
+ }
return 1;
}
while ((dirent = readdir(d))) {
fd = atoi(dirent->d_name);
- if (fd > 2 && fd != dirfd(d))
+ if ((fd > 2) &&
+ (fd != dirfd(d)) &&
+ (fd != custom_fds->out) &&
+ (fd != custom_fds->err) &&
+ (fd != custom_fds->report)) {
_close_descriptor(fd, suppress_warnings,
command, ppid, parent_cmdline);
+ }
}
if (closedir(d))
@@ -2111,6 +2154,7 @@ int lvm2_main(int argc, char **argv)
{
const char *base;
int ret, alias = 0;
+ struct custom_fds custom_fds;
struct cmd_context *cmd;
if (!argv)
@@ -2124,7 +2168,13 @@ int lvm2_main(int argc, char **argv)
if (!_check_standard_fds())
return -1;
- if (!_close_stray_fds(base))
+ if (!_get_custom_fds(&custom_fds))
+ return -1;
+
+ if (!_close_stray_fds(base, &custom_fds))
+ return -1;
+
+ if (!init_custom_log_streams(&custom_fds))
return -1;
if (is_static() && strcmp(base, "lvm.static") &&
7 years, 9 months
master - test: add lvconvert-raid-takeover.sh
by Heinz Mauelshagen
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=3d3f62e10acee6...
Commit: 3d3f62e10acee60bd61a401b76f009a611d22f9e
Parent: e5a5fd3669914337f85fc5c12e867680eb3f58a9
Author: Heinz Mauelshagen <heinzm(a)redhat.com>
AuthorDate: Tue Aug 9 14:17:26 2016 +0200
Committer: Heinz Mauelshagen <heinzm(a)redhat.com>
CommitterDate: Tue Aug 9 14:17:26 2016 +0200
test: add lvconvert-raid-takeover.sh
Commit 76ef2d15d839106adac4d82005d517be05a657eb introduced
raid0 <-> raid4 takeover and full mirror <-> raid1 support.
Add tests for these conversions.
Tests exposed a kernel semantics change freezing resynchronization
on conversions from raid0[_meta] -> raid4 or adding raid1 legs
because kernel kept the RAID mapped device in 'frozen' state unless
an 'idle' message was sent or the table was reloaded (kernel patch pending).
---
test/shell/lvconvert-raid-takeover.sh | 126 +++++++++++++++++++++++++++++++++
1 files changed, 126 insertions(+), 0 deletions(-)
diff --git a/test/shell/lvconvert-raid-takeover.sh b/test/shell/lvconvert-raid-takeover.sh
new file mode 100644
index 0000000..2ead582
--- /dev/null
+++ b/test/shell/lvconvert-raid-takeover.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+# Copyright (C) 2016 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+SKIP_WITH_LVMLOCKD=1
+SKIP_WITH_LVMPOLLD=1
+
+. lib/inittest
+
+aux have_raid 1 9 0 || skip
+
+aux prepare_vg 9 288
+
+# Delay 1st leg so that rebuilding status characters
+# can be read before resync finished too quick.
+# aux delay_dev "$dev1" 0 1
+
+# Create 3-way mirror
+lvcreate --yes --type mirror -m 2 -L 256M -n $lv1 $vg
+check lv_field $vg/$lv1 segtype "mirror"
+check lv_field $vg/$lv1 stripes 3
+echo y | mkfs -t ext4 /dev/mapper/$vg-$lv1
+aux wait_for_sync $vg $lv1
+fsck -fn /dev/mapper/$vg-$lv1
+
+# Convert 3-way to 4-way mirror
+lvconvert -m 3 $vg/$lv1
+check lv_field $vg/$lv1 segtype "mirror"
+check lv_field $vg/$lv1 stripes 4
+fsck -fn /dev/mapper/$vg-$lv1
+aux wait_for_sync $vg $lv1
+fsck -fn /dev/mapper/$vg-$lv1
+
+# Takeover 4-way mirror to raid1
+lvconvert --yes --type raid1 $vg/$lv1
+check lv_field $vg/$lv1 segtype "raid1"
+check lv_field $vg/$lv1 stripes 4
+fsck -fn /dev/mapper/$vg-$lv1
+
+## Convert 4-way raid1 to 5-way
+lvconvert -m 4 $vg/$lv1
+check lv_field $vg/$lv1 segtype "raid1"
+check lv_field $vg/$lv1 stripes 5
+fsck -fn /dev/mapper/$vg-$lv1
+aux wait_for_sync $vg $lv1
+fsck -fn /dev/mapper/$vg-$lv1
+
+# FIXME: enable once lvconvert rejects early
+## Try converting 4-way raid1 to 9-way
+#not lvconvert --yes -m 8 $vg/$lv1
+#check lv_field $vg/$lv1 segtype "raid1"
+#check lv_field $vg/$lv1 stripes 4
+
+# Convert 5-way raid1 to 2-way
+lvconvert --yes -m 1 $vg/$lv1
+lvs $vg/$lv1
+dmsetup status $vg-$lv1
+dmsetup table $vg-$lv1
+check lv_field $vg/$lv1 segtype "raid1"
+check lv_field $vg/$lv1 stripes 2
+fsck -fn /dev/mapper/$vg-$lv1
+
+# Convert 2-way raid1 to mirror
+lvconvert --yes --type mirror $vg/$lv1
+check lv_field $vg/$lv1 segtype "mirror"
+check lv_field $vg/$lv1 stripes 2
+aux wait_for_sync $vg $lv1
+fsck -fn /dev/mapper/$vg-$lv1
+aux wait_for_sync $vg $lv1
+
+# Clean up
+lvremove --yes $vg/$lv1
+
+# Create 3-way raid0
+lvcreate -y --type raid0 -i 3 -L 256M -n $lv1 $vg
+check lv_field $vg/$lv1 segtype "raid0"
+check lv_field $vg/$lv1 stripes 3
+echo y | mkfs -t ext4 /dev/mapper/$vg-$lv1
+fsck -fn /dev/mapper/$vg-$lv1
+
+# Convert raid0 -> raid4
+lvconvert -y --ty raid4 $vg/$lv1
+lvchange --refresh $vg/$lv1
+check lv_field $vg/$lv1 segtype "raid4"
+check lv_field $vg/$lv1 stripes 4
+fsck -fn /dev/mapper/$vg-$lv1
+aux wait_for_sync $vg $lv1
+fsck -fn /dev/mapper/$vg-$lv1
+
+# Convert raid4 -> raid0_meta
+lvconvert -y --ty raid0_meta $vg/$lv1
+check lv_field $vg/$lv1 segtype "raid0_meta"
+check lv_field $vg/$lv1 stripes 3
+fsck -fn /dev/mapper/$vg-$lv1
+
+# Convert raid0_meta -> raid4
+lvconvert -y --ty raid4 $vg/$lv1
+fsck -fn /dev/mapper/$vg-$lv1
+check lv_field $vg/$lv1 segtype "raid4"
+check lv_field $vg/$lv1 stripes 4
+aux wait_for_sync $vg $lv1
+fsck -fn /dev/mapper/$vg-$lv1
+
+# Convert raid4 -> raid0
+lvconvert -y --ty raid0 $vg/$lv1
+check lv_field $vg/$lv1 segtype "raid0"
+check lv_field $vg/$lv1 stripes 3
+fsck -fn /dev/mapper/$vg-$lv1
+
+# Convert raid0 -> raid4
+lvconvert -y --ty raid4 $vg/$lv1
+lvchange --refresh $vg/$lv1
+check lv_field $vg/$lv1 segtype "raid4"
+check lv_field $vg/$lv1 stripes 4
+fsck -fn /dev/mapper/$vg-$lv1
+aux wait_for_sync $vg $lv1
+fsck -fn /dev/mapper/$vg-$lv1
+
+vgremove -ff $vg
7 years, 9 months