summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2015-12-15 12:55:48 -0600
committerDavid Teigland <teigland@redhat.com>2015-12-17 16:44:40 -0600
commita7dcf1e8c4ec60eeb05a7fa8d9ddad9d9a0d9b01 (patch)
tree6c474be4e12383977d006e27a8043c7c7d591d4e
parent796461a9125a8324a63be154fc998245617e5990 (diff)
downloadlvm2-dev-dct-pvcreate-3.tar.gz
pvcreate: restructuring to use toollibdev-dct-pvcreate-3
- Pull out the hidden equivalent of process_each_pv into an actual top level process_each_pv. - Pull the prompts to the top level, and out from under the VG locks. Previously, pvcreate_vol() was the shared function for creating a PV for pvcreate, vgcreate, vgextend. Now, it will be toollib function pvcreate_each_device(). pvcreate_vol() was called effectively as a helper, from within vgcreate and vgextend code paths. pvcreate_each_device() will be called at the same level as other process_each functions. One of the main problems with pvcreate_vol() is that it included a hidden equivalent of process_each_pv for each device being created: pvcreate_vol() -> _pvcreate_check() -> find_pv_by_name() -> get_pvs() -> get_pvs_internal() -> _get_pvs() -> get_vgids() -> /* equivalent to process_each_pv */ dm_list_iterate_items(vgids) vg = vg_read_internal() dm_list_iterate_items(&vg->pvs) pvcreate_each_device() reorganizes the code so that each-VG-each-PV loop is done once, and uses the standard process_each_pv function at the top level of the function. FIXME: the 1/0 change in _vg_read_orphan_pv was done because reading the orphan vg in process_each_pv was causing a full rereading of all labels (lvmcache_label_scan). The 1/0 change avoids that. It should be fine to use cached info at that point (it's the checking phase). The values will be verified under the write lock before being changed.
-rw-r--r--lib/metadata/metadata-exported.h50
-rw-r--r--lib/metadata/metadata.c3
-rw-r--r--lib/metadata/pv_manip.c20
-rw-r--r--liblvm/lvm_pv.c12
-rw-r--r--tools/pvchange.c2
-rw-r--r--tools/pvcreate.c262
-rw-r--r--tools/pvdisplay.c5
-rw-r--r--tools/pvresize.c2
-rw-r--r--tools/pvscan.c2
-rw-r--r--tools/reporter.c6
-rw-r--r--tools/toollib.c522
-rw-r--r--tools/toollib.h6
-rw-r--r--tools/tools.h2
-rw-r--r--tools/vgreduce.c2
14 files changed, 807 insertions, 89 deletions
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 8242db17d..e84b95a97 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -535,6 +535,56 @@ struct pvcreate_params {
struct pvcreate_restorable_params rp;
};
+/*
+ * FIXME: rename this pvcreate_params once the old pvcreate_params is unused.
+ * This can probably be put in toollib.h.
+ */
+struct pvcreate_each_params {
+ /*
+ * From argc and argv.
+ */
+ char **pv_names;
+ uint32_t pv_count;
+
+ /*
+ * From command line args.
+ */
+ int zero;
+ uint64_t size;
+ uint64_t data_alignment;
+ uint64_t data_alignment_offset;
+ int pvmetadatacopies;
+ uint64_t pvmetadatasize;
+ int64_t labelsector;
+ force_t force;
+ unsigned yes;
+ unsigned metadataignore;
+
+ /*
+ * From recovery-specific command line args.
+ */
+ const char *restorefile; /* NULL if no --restorefile option */
+ const char *uuid_str; /* id in printable format, NULL if no id */
+ struct id id;
+
+ /*
+ * From reading VG backup file.
+ */
+ uint64_t ba_start;
+ uint64_t ba_size;
+ uint64_t pe_start;
+ uint32_t extent_count;
+ uint32_t extent_size;
+
+ /*
+ * Used for command processing.
+ */
+ struct dm_list prompts; /* pvcreate_prompt */
+ struct dm_list arg_devices; /* pvcreate_device, one for each pv_name */
+ struct dm_list arg_create; /* pvcreate_device, processing from arg_devices */
+ struct dm_list arg_fail; /* pvcreate_device, cannot process */
+};
+
struct lvresize_params {
const char *vg_name; /* only-used when VG is not yet opened (in /tools) */
const char *lv_name;
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index ae40906e0..4ae6f52ba 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -3271,8 +3271,9 @@ static int _vg_read_orphan_pv(struct lvmcache_info *info, void *baton)
struct physical_volume *pv = NULL;
struct pv_list *pvl;
+ /* FIXME: 0 was changed to 1 here. Pass arg to do that. */
if (!(pv = _pv_read(b->vg->cmd, b->vg->vgmem, dev_name(lvmcache_device(info)),
- b->vg->fid, b->warn_flags, 0))) {
+ b->vg->fid, b->warn_flags, 1))) {
stack;
return 1;
}
diff --git a/lib/metadata/pv_manip.c b/lib/metadata/pv_manip.c
index e48fe42a1..09000ecde 100644
--- a/lib/metadata/pv_manip.c
+++ b/lib/metadata/pv_manip.c
@@ -852,23 +852,3 @@ out:
return ret;
}
-int pvcreate_single(struct cmd_context *cmd, const char *pv_name,
- struct pvcreate_params *pp)
-{
- int r = 0;
-
- if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) {
- log_error("Can't get lock for orphan PVs");
- return 0;
- }
-
- if (!(pvcreate_vol(cmd, pv_name, pp, 1)))
- goto_out;
-
- r = 1;
-
-out:
- unlock_vg(cmd, VG_ORPHANS);
-
- return r;
-}
diff --git a/liblvm/lvm_pv.c b/liblvm/lvm_pv.c
index fee1e8d15..ca9844c67 100644
--- a/liblvm/lvm_pv.c
+++ b/liblvm/lvm_pv.c
@@ -419,6 +419,7 @@ int lvm_pv_params_set_property(pv_create_params_t params, const char *name,
static int _pv_create(pv_create_params_t params)
{
struct cmd_context *cmd = (struct cmd_context *)params->libh;
+ int rc = 0;
if (params->pv_p.size) {
if (params->pv_p.size % SECTOR_SIZE) {
@@ -428,9 +429,16 @@ static int _pv_create(pv_create_params_t params)
params->pv_p.size = params->pv_p.size >> SECTOR_SHIFT;
}
- if (!pvcreate_single(cmd, params->pv_name, &params->pv_p))
+ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) {
+ log_errno(EINVAL, "Can't get lock for orphan PVs");
return -1;
- return 0;
+ }
+
+ if (!(pvcreate_vol(cmd, params->pv_name, &params->pv_p, 1)))
+ rc = -1;
+
+ unlock_vg(cmd, VG_ORPHANS);
+ return rc;
}
int lvm_pv_create(lvm_t libh, const char *pv_name, uint64_t size)
diff --git a/tools/pvchange.c b/tools/pvchange.c
index 91e93c1e7..da5a4a86a 100644
--- a/tools/pvchange.c
+++ b/tools/pvchange.c
@@ -213,7 +213,7 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv)
}
}
- ret = process_each_pv(cmd, argc, argv, NULL, READ_FOR_UPDATE, handle, _pvchange_single);
+ ret = process_each_pv(cmd, argc, argv, NULL, 0, READ_FOR_UPDATE, handle, _pvchange_single);
if (!argc)
unlock_vg(cmd, VG_GLOBAL);
diff --git a/tools/pvcreate.c b/tools/pvcreate.c
index 1f45ad91d..0035a6ad5 100644
--- a/tools/pvcreate.c
+++ b/tools/pvcreate.c
@@ -15,6 +15,114 @@
#include "tools.h"
+static int pvcreate_each_params_from_args(struct cmd_context *cmd, struct pvcreate_each_params *pp)
+{
+ pp->yes = arg_count(cmd, yes_ARG);
+ pp->force = (force_t) arg_count(cmd, force_ARG);
+
+ if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) {
+ log_error("labelsector must be less than %lu.",
+ LABEL_SCAN_SECTORS);
+ return 0;
+ } else {
+ pp->labelsector = arg_int64_value(cmd, labelsector_ARG,
+ DEFAULT_LABELSECTOR);
+ }
+
+ if (!(cmd->fmt->features & FMT_MDAS) &&
+ (arg_count(cmd, pvmetadatacopies_ARG) ||
+ arg_count(cmd, metadatasize_ARG) ||
+ arg_count(cmd, dataalignment_ARG) ||
+ arg_count(cmd, dataalignmentoffset_ARG))) {
+ log_error("Metadata and data alignment parameters only "
+ "apply to text format.");
+ return 0;
+ }
+
+ if (!(cmd->fmt->features & FMT_BAS) &&
+ arg_count(cmd, bootloaderareasize_ARG)) {
+ log_error("Bootloader area parameters only "
+ "apply to text format.");
+ return 0;
+ }
+
+ if (arg_count(cmd, metadataignore_ARG))
+ pp->metadataignore = arg_int_value(cmd, metadataignore_ARG,
+ DEFAULT_PVMETADATAIGNORE);
+ else
+ pp->metadataignore = find_config_tree_bool(cmd, metadata_pvmetadataignore_CFG, NULL);
+
+ if (arg_count(cmd, pvmetadatacopies_ARG) &&
+ !arg_int_value(cmd, pvmetadatacopies_ARG, -1) &&
+ pp->metadataignore) {
+ log_error("metadataignore only applies to metadatacopies > 0");
+ return 0;
+ }
+
+ pp->zero = arg_int_value(cmd, zero_ARG, 1);
+
+ if (arg_sign_value(cmd, dataalignment_ARG, SIGN_NONE) == SIGN_MINUS) {
+ log_error("Physical volume data alignment may not be negative.");
+ return 0;
+ }
+ pp->data_alignment = arg_uint64_value(cmd, dataalignment_ARG, UINT64_C(0));
+
+ if (pp->data_alignment > UINT32_MAX) {
+ log_error("Physical volume data alignment is too big.");
+ return 0;
+ }
+
+ if (arg_sign_value(cmd, dataalignmentoffset_ARG, SIGN_NONE) == SIGN_MINUS) {
+ log_error("Physical volume data alignment offset may not be negative");
+ return 0;
+ }
+ pp->data_alignment_offset = arg_uint64_value(cmd, dataalignmentoffset_ARG, UINT64_C(0));
+
+ if (pp->data_alignment_offset > UINT32_MAX) {
+ log_error("Physical volume data alignment offset is too big.");
+ return 0;
+ }
+
+ if ((pp->data_alignment + pp->data_alignment_offset) &&
+ (pp->pe_start != PV_PE_START_CALC)) {
+ if ((pp->data_alignment ? pp->pe_start % pp->data_alignment : pp->pe_start) != pp->data_alignment_offset) {
+ log_warn("WARNING: Ignoring data alignment %s"
+ " incompatible with restored pe_start value %s)",
+ display_size(cmd, pp->data_alignment + pp->data_alignment_offset),
+ display_size(cmd, pp->pe_start));
+ pp->data_alignment = 0;
+ pp->data_alignment_offset = 0;
+ }
+ }
+
+ if (arg_sign_value(cmd, metadatasize_ARG, SIGN_NONE) == SIGN_MINUS) {
+ log_error("Metadata size may not be negative.");
+ return 0;
+ }
+
+ if (arg_sign_value(cmd, bootloaderareasize_ARG, SIGN_NONE) == SIGN_MINUS) {
+ log_error("Bootloader area size may not be negative.");
+ return 0;
+ }
+
+ pp->pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, UINT64_C(0));
+ if (!pp->pvmetadatasize)
+ pp->pvmetadatasize = find_config_tree_int(cmd, metadata_pvmetadatasize_CFG, NULL);
+
+ pp->pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1);
+ if (pp->pvmetadatacopies < 0)
+ pp->pvmetadatacopies = find_config_tree_int(cmd, metadata_pvmetadatacopies_CFG, NULL);
+
+ if (pp->pvmetadatacopies > 2) {
+ log_error("Metadatacopies may only be 0, 1 or 2");
+ return 0;
+ }
+
+ pp->ba_size = arg_uint64_value(cmd, bootloaderareasize_ARG, pp->ba_size);
+
+ return 1;
+}
+
/*
* Intial sanity checking of recovery-related command-line arguments.
* These args are: --restorefile, --uuid, and --physicalvolumesize
@@ -22,13 +130,12 @@
* Output arguments:
* pp: structure allocated by caller, fields written / validated here
*/
-static int pvcreate_restore_params_validate(struct cmd_context *cmd,
- int argc, char **argv,
- struct pvcreate_params *pp)
+static int pvcreate_each_restore_params_from_args(struct cmd_context *cmd, int argc,
+ struct pvcreate_each_params *pp)
{
const char *uuid = NULL;
- struct volume_group *vg;
- struct pv_list *existing_pvl;
+
+ pp->restorefile = arg_str_value(cmd, restorefile_ARG, NULL);
if (arg_count(cmd, restorefile_ARG) && !arg_count(cmd, uuidstr_ARG)) {
log_error("--uuid is required with --restorefile");
@@ -48,35 +155,10 @@ static int pvcreate_restore_params_validate(struct cmd_context *cmd,
return 0;
}
- if (arg_count(cmd, uuidstr_ARG)) {
- uuid = arg_str_value(cmd, uuidstr_ARG, "");
- if (!id_read_format(&pp->rp.id, uuid))
- return 0;
- pp->rp.idp = &pp->rp.id;
- lvmcache_seed_infos_from_lvmetad(cmd); /* need to check for UUID dups */
- }
-
- if (arg_count(cmd, restorefile_ARG)) {
- pp->rp.restorefile = arg_str_value(cmd, restorefile_ARG, "");
- /* The uuid won't already exist */
- if (!(vg = backup_read_vg(cmd, NULL, pp->rp.restorefile))) {
- log_error("Unable to read volume group from %s",
- pp->rp.restorefile);
+ if (arg_count(cmd, uuidstr_ARG)) {
+ pp->uuid_str = arg_str_value(cmd, uuidstr_ARG, "");
+ if (!id_read_format(&pp->id, uuid))
return 0;
- }
- if (!(existing_pvl = find_pv_in_vg_by_uuid(vg, pp->rp.idp))) {
- release_vg(vg);
- log_error("Can't find uuid %s in backup file %s",
- uuid, pp->rp.restorefile);
- return 0;
- }
- pp->rp.ba_start = pv_ba_start(existing_pvl->pv);
- pp->rp.ba_size = pv_ba_size(existing_pvl->pv);
- pp->rp.pe_start = pv_pe_start(existing_pvl->pv);
- pp->rp.extent_size = pv_pe_size(existing_pvl->pv);
- pp->rp.extent_count = pv_pe_count(existing_pvl->pv);
-
- release_vg(vg);
}
if (arg_sign_value(cmd, physicalvolumesize_ARG, SIGN_NONE) == SIGN_MINUS) {
@@ -90,34 +172,112 @@ static int pvcreate_restore_params_validate(struct cmd_context *cmd,
return 1;
}
-int pvcreate(struct cmd_context *cmd, int argc, char **argv)
+static int pvcreate_each_restore_params_from_backup(struct cmd_context *cmd,
+ struct pvcreate_each_params *pp)
{
- int i;
- int ret = ECMD_PROCESSED;
- struct pvcreate_params pp;
+ struct volume_group *vg;
+ struct pv_list *existing_pvl;
+ const char *uuid;
- /* Needed to change the set of orphan PVs. */
- if (!lockd_gl(cmd, "ex", 0))
- return_ECMD_FAILED;
+ /*
+ * When restoring a PV, params need to be read from a backup file.
+ */
+ if (!pp->restorefile)
+ return 1;
- pvcreate_params_set_defaults(&pp);
+ uuid = arg_str_value(cmd, uuidstr_ARG, "");
- if (!pvcreate_restore_params_validate(cmd, argc, argv, &pp)) {
- return EINVALID_CMD_LINE;
+ if (!(vg = backup_read_vg(cmd, NULL, pp->restorefile))) {
+ log_error("Unable to read volume group from %s", pp->restorefile);
+ return 0;
}
- if (!pvcreate_params_validate(cmd, argc, &pp)) {
- return EINVALID_CMD_LINE;
+
+ if (!(existing_pvl = find_pv_in_vg_by_uuid(vg, &pp->id))) {
+ release_vg(vg);
+ log_error("Can't find uuid %s in backup file %s",
+ uuid, pp->restorefile);
+ return 0;
}
- for (i = 0; i < argc; i++) {
- if (sigint_caught())
- return_ECMD_FAILED;
+ pp->ba_start = pv_ba_start(existing_pvl->pv);
+ pp->ba_size = pv_ba_size(existing_pvl->pv);
+ pp->pe_start = pv_pe_start(existing_pvl->pv);
+ pp->extent_size = pv_pe_size(existing_pvl->pv);
+ pp->extent_count = pv_pe_count(existing_pvl->pv);
- dm_unescape_colons_and_at_signs(argv[i], NULL, NULL);
+ release_vg(vg);
+ return 1;
+}
+
+static void pvcreate_each_params_set_defaults(struct pvcreate_each_params *pp)
+{
+ pp->zero = 1;
+ pp->size = 0;
+ pp->data_alignment = UINT64_C(0);
+ pp->data_alignment_offset = UINT64_C(0);
+ pp->pvmetadatacopies = DEFAULT_PVMETADATACOPIES;
+ pp->pvmetadatasize = DEFAULT_PVMETADATASIZE;
+ pp->labelsector = DEFAULT_LABELSECTOR;
+ pp->force = PROMPT;
+ pp->yes = 0;
+ pp->metadataignore = DEFAULT_PVMETADATAIGNORE;
+ pp->restorefile = NULL;
+ pp->uuid_str = NULL;
+ pp->ba_start = 0;
+ pp->ba_size = 0;
+ pp->pe_start = PV_PE_START_CALC;
+ pp->extent_count = 0;
+ pp->extent_size = 0;
- if (!pvcreate_single(cmd, argv[i], &pp))
- ret = ECMD_FAILED;
+ dm_list_init(&pp->prompts);
+ dm_list_init(&pp->arg_devices);
+ dm_list_init(&pp->arg_create);
+ dm_list_init(&pp->arg_fail);
+}
+
+int pvcreate(struct cmd_context *cmd, int argc, char **argv)
+{
+ struct pvcreate_each_params pp = { 0 };
+ int ret;
+
+ if (!argc) {
+ log_error("Please enter a physical volume path.");
+ return 0;
}
+ /*
+ * Five kinds of pvcreate param values:
+ * 1. defaults
+ * 2. normal command line args
+ * 3. recovery-related command line args
+ * 4. recovery-related args from backup file
+ * 5. argc/argv free args specifying devices
+ */
+
+ pvcreate_each_params_set_defaults(&pp);
+
+ if (!pvcreate_each_params_from_args(cmd, &pp))
+ return EINVALID_CMD_LINE;
+
+ if (!pvcreate_each_restore_params_from_args(cmd, argc, &pp))
+ return EINVALID_CMD_LINE;
+
+ if (!pvcreate_each_restore_params_from_backup(cmd, &pp))
+ return EINVALID_CMD_LINE;
+
+ pp.pv_count = argc;
+ pp.pv_names = argv;
+
+ /*
+ * Needed to change the set of orphan PVs.
+ * (disable afterward to prevent process_each_pv from doing
+ * a shared global lock since it's already acquired it ex.)
+ */
+ if (!lockd_gl(cmd, "ex", 0))
+ return_ECMD_FAILED;
+ cmd->lockd_gl_disable = 1;
+
+ ret = pvcreate_each_device(cmd, &pp);
+
return ret;
}
diff --git a/tools/pvdisplay.c b/tools/pvdisplay.c
index 2763889a9..918aba8ea 100644
--- a/tools/pvdisplay.c
+++ b/tools/pvdisplay.c
@@ -107,8 +107,9 @@ int pvdisplay(struct cmd_context *cmd, int argc, char **argv)
}
}
- ret = process_each_pv(cmd, argc, argv, NULL, 0, NULL,
- _pvdisplay_single);
+ ret = process_each_pv(cmd, argc, argv, NULL,
+ arg_is_set(cmd, all_ARG), 0,
+ NULL, _pvdisplay_single);
if (lock_global)
unlock_vg(cmd, VG_GLOBAL);
diff --git a/tools/pvresize.c b/tools/pvresize.c
index 0b055e6ef..3ee08b954 100644
--- a/tools/pvresize.c
+++ b/tools/pvresize.c
@@ -84,7 +84,7 @@ int pvresize(struct cmd_context *cmd, int argc, char **argv)
handle->custom_handle = &params;
- ret = process_each_pv(cmd, argc, argv, NULL, READ_FOR_UPDATE, handle,
+ ret = process_each_pv(cmd, argc, argv, NULL, 0, READ_FOR_UPDATE, handle,
_pvresize_single);
log_print_unless_silent("%d physical volume(s) resized / %d physical volume(s) "
diff --git a/tools/pvscan.c b/tools/pvscan.c
index 68a074e02..b8e8a75d7 100644
--- a/tools/pvscan.c
+++ b/tools/pvscan.c
@@ -413,7 +413,7 @@ int pvscan(struct cmd_context *cmd, int argc, char **argv)
handle->custom_handle = &params;
- ret = process_each_pv(cmd, argc, argv, NULL, 0, handle, _pvscan_single);
+ ret = process_each_pv(cmd, argc, argv, NULL, 0, 0, handle, _pvscan_single);
if (!params.pvs_found)
log_print_unless_silent("No matching physical volumes found");
diff --git a/tools/reporter.c b/tools/reporter.c
index 416038c85..856cb82ec 100644
--- a/tools/reporter.c
+++ b/tools/reporter.c
@@ -893,7 +893,8 @@ static int _report(struct cmd_context *cmd, int argc, char **argv,
break;
case PVS:
if (args_are_pvs)
- r = process_each_pv(cmd, argc, argv, NULL, 0,
+ r = process_each_pv(cmd, argc, argv, NULL,
+ arg_is_set(cmd, all_ARG), 0,
&handle, &_pvs_single);
else
r = process_each_vg(cmd, argc, argv, NULL, 0,
@@ -910,7 +911,8 @@ static int _report(struct cmd_context *cmd, int argc, char **argv,
break;
case PVSEGS:
if (args_are_pvs)
- r = process_each_pv(cmd, argc, argv, NULL, 0,
+ r = process_each_pv(cmd, argc, argv, NULL,
+ arg_is_set(cmd, all_ARG), 0,
&handle,
lv_info_needed && !lv_segment_status_needed ? &_pvsegs_with_lv_info_single :
!lv_info_needed && lv_segment_status_needed ? &_pvsegs_with_lv_status_single :
diff --git a/tools/toollib.c b/tools/toollib.c
index 728e61d4d..cc8f01190 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -2864,6 +2864,8 @@ static int _get_all_devices(struct cmd_context *cmd, struct dm_list *all_devices
struct device_id_list *dil;
int r = ECMD_FAILED;
+ log_very_verbose("Getting list of all devices");
+
lvmcache_seed_infos_from_lvmetad(cmd);
if (!(iter = dev_iter_create(cmd->full_filter, 1))) {
@@ -3286,9 +3288,8 @@ endvg:
}
int process_each_pv(struct cmd_context *cmd,
- int argc, char **argv,
- const char *only_this_vgname,
- uint32_t read_flags,
+ int argc, char **argv, const char *only_this_vgname,
+ int all_is_set, uint32_t read_flags,
struct processing_handle *handle,
process_single_pv_fn_t process_single_pv)
{
@@ -3342,8 +3343,7 @@ int process_each_pv(struct cmd_context *cmd,
process_all_pvs = dm_list_empty(&arg_pvnames) && dm_list_empty(&arg_tags);
- process_all_devices = process_all_pvs && (cmd->command->flags & ENABLE_ALL_DEVS) &&
- arg_count(cmd, all_ARG);
+ process_all_devices = process_all_pvs && (cmd->command->flags & ENABLE_ALL_DEVS) && all_is_set;
/* Needed for a current listing of the global VG namespace. */
if (!only_this_vgname && !lockd_gl(cmd, "sh", 0))
@@ -3355,6 +3355,7 @@ int process_each_pv(struct cmd_context *cmd,
* so that get_vgnameids() will look at any new devices.
*/
if (!trust_cache()) {
+ log_verbose("Scanning for available devices");
dev_cache_full_scan(cmd->full_filter);
lvmcache_destroy(cmd, 1, 0);
}
@@ -3498,3 +3499,514 @@ int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv,
return ECMD_PROCESSED;
}
+
+enum {
+ PROMPT_PVCREATE_PV_IN_VG = 1,
+};
+
+/*
+ * When a prompt entry is created, save any strings or info
+ * in this struct that are needed for the prompt messages.
+ * The VG/PV structs are not be available when the prompt
+ * is run.
+ */
+struct pvcreate_prompt {
+ struct dm_list list;
+ uint32_t type;
+ const char *pv_name;
+ const char *vg_name;
+ struct device *dev;
+ unsigned answer : 1;
+ unsigned abort_command : 1;
+};
+
+struct pvcreate_device {
+ struct dm_list list;
+ const char *name;
+ struct device *dev;
+ struct id id;
+ int wiped;
+};
+
+static void _run_pvcreate_prompt(struct cmd_context *cmd,
+ struct pvcreate_each_params *pp,
+ struct pvcreate_prompt *prompt)
+{
+ if (prompt->type == PROMPT_PVCREATE_PV_IN_VG) {
+ if (pp->force != DONT_PROMPT_OVERRIDE) {
+ prompt->answer = 0;
+ log_error("Can't initialize physical volume \"%s\" of volume group \"%s\" without -ff",
+ prompt->pv_name, prompt->vg_name);
+ } else if (pp->yes) {
+ prompt->answer = 1;
+ } else {
+ if (yes_no_prompt("Really INITIALIZE physical volume \"%s\" of volume group \"%s\" [y/n]? ",
+ prompt->pv_name, prompt->vg_name) == 'n') {
+ prompt->answer = 0;
+ log_error("%s: physical volume not initialized", prompt->pv_name);
+ } else {
+ prompt->answer = 1;
+ }
+ }
+ }
+}
+
+
+static struct pvcreate_device *_pvcreate_list_find_dev(struct dm_list *devices, struct device *dev)
+{
+ struct pvcreate_device *pd;
+
+ dm_list_iterate_items(pd, devices) {
+ if (pd->dev == dev)
+ return pd;
+ }
+
+ return NULL;
+}
+
+/*
+ * If this function returns ECMD_FAILED, it will
+ * cause the entire command to fail.
+ *
+ * If this function decides that a arg_devices entry cannot be
+ * used, but the command might be able to continue without it,
+ * then it moves that entry from arg_devices to arg_fail
+ * and returns ECMD_SUCCESS.
+ *
+ * If this function decides that an arg_devices entry could be used
+ * (possibly requiring a prompt), then it moves the entry from
+ * arg_devices to arg_create and returns ECMD_SUCCESS.
+ *
+ * Any arg_devices entries that are not moved to arg_fail or
+ * arg_create were not found. The caller will decide if the
+ * command can continue if any arg_devices entries were not found,
+ * or if any were moved to arg_fail.
+ */
+
+static int _pvcreate_check_single(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct physical_volume *pv,
+ struct processing_handle *handle)
+{
+ struct pvcreate_each_params *pp = (struct pvcreate_each_params *) handle->custom_handle;
+ struct pvcreate_device *pd;
+ struct pvcreate_prompt *prompt;
+ struct device *dev;
+ int found = 0;
+
+ /*
+ * Check if pv uuid matches a uuid specified by the command.
+ */
+ if (pp->uuid_str && id_equal(&pv->id, &pp->id)) {
+ log_error("uuid %s already in use on \"%s\"", pp->uuid_str, pv_dev_name(pv));
+ return ECMD_FAILED;
+ }
+
+ /*
+ * Check if one of the command args in arg_devices
+ * matches this device.
+ *
+ * (Possible optimization: the first time this _single
+ * function is called, we could iterate through all
+ * arg_devices entries, do the name to dev lookup
+ * with dev_cache_get() and set the pd->dev fields.
+ * Subsequent _single calls would just compare devs
+ * and not do any dev_cache_get(). This would avoid
+ * repeating dev_cache_get() for arg_devices entries.)
+ */
+ dm_list_iterate_items(pd, &pp->arg_devices) {
+ dev = dev_cache_get(pd->name, cmd->full_filter);
+ if (dev != pv->dev)
+ continue;
+
+ pd->dev = pv->dev;
+ found = 1;
+ break;
+ }
+
+ /*
+ * The command is not interested in this device.
+ */
+ if (!found)
+ return ECMD_PROCESSED;
+
+ log_verbose("Checking device %s for pvcreate %s",
+ pv_dev_name(pv), pv->dev->pvid[0] ? pv->dev->pvid : "");
+
+ /*
+ * This test will fail if the device belongs to an MD array.
+ */
+ if (!dev_test_excl(pv->dev)) {
+ /* FIXME Detect whether device-mapper itself is still using it */
+ log_error("Can't open %s exclusively. Mounted filesystem?",
+ pv_dev_name(pv));
+ dm_list_move(&pp->arg_fail, &pd->list);
+ return ECMD_PROCESSED;
+ }
+
+ /*
+ * pvcreate is being run on this device, and it's not a PV,
+ * or is an orphan PV. Neither case requires a prompt.
+ */
+ if (!vg || is_orphan(pv)) {
+ pd->dev = pv->dev;
+ dm_list_move(&pp->arg_create, &pd->list);
+ return ECMD_PROCESSED;
+ }
+
+ /*
+ * pvcreate is being run on this device, but the device is already
+ * a PV in a VG. A prompt or force option is required to use it.
+ */
+
+ if (!(prompt = dm_pool_zalloc(cmd->mem, sizeof(*prompt)))) {
+ log_error("prompt alloc failed");
+ return ECMD_FAILED;
+ }
+
+ prompt->dev = pd->dev;
+ prompt->pv_name = dm_pool_strdup(cmd->mem, pd->name);
+ prompt->vg_name = dm_pool_strdup(cmd->mem, vg->name);
+ prompt->type = PROMPT_PVCREATE_PV_IN_VG;
+ dm_list_add(&pp->prompts, &prompt->list);
+
+ dm_list_move(&pp->arg_create, &pd->list);
+ return ECMD_PROCESSED;
+}
+
+/*
+ * This can be used by pvcreate, vgcreate and vgextend to create PVs. The
+ * callers need to set up the pvcreate_each_params structure based on command
+ * line args. This includes the pv_names field which specifies the devices to
+ * create PVs on.
+ *
+ * This uses process_each_pv() and should be called from a high level in the
+ * command -- the same level at which other instances of process_each are
+ * called.
+ *
+ * This function returns ECMD_FAILED if the caller requires all specified
+ * devices to be created, and any of those devices are not found, or any of
+ * them cannot be created.
+ *
+ * This function returns ECMD_PROCESSED if the caller requires all specified
+ * devices to be created, and all are created, or if the caller does not
+ * require all specified devices to be created and one or more were created.
+ */
+
+int pvcreate_each_device(struct cmd_context *cmd, struct pvcreate_each_params *pp)
+{
+ struct processing_handle *handle;
+ struct pvcreate_restorable_params rp;
+ struct pvcreate_device *pd, *pd2;
+ struct pvcreate_prompt *prompt, *prompt2;
+ struct physical_volume *pv;
+ struct device *dev;
+ const char *pv_name;
+ int must_use_all = (cmd->command->flags & MUST_USE_ALL_ARGS);
+ int filter_refresh_needed = 0;
+ int scan_needed = 0;
+ int wiped = 0;
+ int ret;
+ int i;
+
+ /*
+ * Find the device for each name arg.
+ */
+ for (i = 0; i < pp->pv_count; i++) {
+ dm_unescape_colons_and_at_signs(pp->pv_names[i], NULL, NULL);
+
+ pv_name = pp->pv_names[i];
+
+ if (!(pd = dm_pool_zalloc(cmd->mem, sizeof(*pd)))) {
+ log_error("alloc failed");
+ return ECMD_FAILED;
+ }
+
+ if (!(pd->name = dm_pool_strdup(cmd->mem, pv_name))) {
+ log_error("strdup failed");
+ return ECMD_FAILED;
+ }
+
+ dm_list_add(&pp->arg_devices, &pd->list);
+ }
+
+ /*
+ * process all existing PVs and devices.
+ *
+ * This is a slightly different way to use process_each_pv, because the
+ * command args (arg_devices) are not being processed directly by
+ * process_each_pv (argc and argv are not passed). Instead,
+ * process_each_pv is processing all existing PVs and devices, and the
+ * single function is matching each of those against the command args
+ * (arg_devices).
+ *
+ * If an arg_devices entry is found during process_each, it's moved to
+ * arg_create if it can be used, or arg_fail if it cannot be used. If
+ * it's added to arg_create but needs a prompt or force option, then a
+ * corresponding prompt entry is added to pp->prompts.
+ */
+
+ if (!(handle = init_processing_handle(cmd))) {
+ log_error("Failed to initialize processing handle.");
+ return ECMD_FAILED;
+ }
+
+ handle->custom_handle = pp;
+
+ ret = process_each_pv(cmd, 0, NULL, NULL, 1, 0, handle, _pvcreate_check_single);
+ if (ret == ECMD_FAILED)
+ return ECMD_FAILED;
+
+ destroy_processing_handle(cmd, handle);
+
+ log_debug("Checking devices for pvcreate found %u fail %d not found %d",
+ dm_list_size(&pp->arg_create), dm_list_size(&pp->arg_fail),
+ dm_list_size(&pp->arg_devices));
+
+ /*
+ * Check if all arg_devices were found by process_each_pv.
+ */
+ dm_list_iterate_items(pd, &pp->arg_devices)
+ log_error("Device %s not found (or ignored by filtering).", pd->name);
+
+ /*
+ * Can the command continue if some specified devices were not found?
+ */
+ if (!dm_list_empty(&pp->arg_devices) && must_use_all)
+ return ECMD_FAILED;
+
+ /*
+ * Can the command continue if some specified devices cannot be used?
+ */
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ return ECMD_FAILED;
+
+ /*
+ * The command cannot continue if there are no devices to create.
+ */
+ if (dm_list_empty(&pp->arg_create)) {
+ log_error("No devices found.");
+ return ECMD_FAILED;
+ }
+
+ /*
+ * Process any prompts that were gathered during the process_each_pv
+ * checks. Options can override/force/skip prompts.
+ */
+ dm_list_iterate_items_safe(prompt, prompt2, &pp->prompts) {
+ _run_pvcreate_prompt(cmd, pp, prompt);
+
+ if (!prompt->answer) {
+ /* Remove the corresponding entry from arg_create. */
+
+ if ((pd = _pvcreate_list_find_dev(&pp->arg_create, prompt->dev))) {
+ dm_list_move(&pp->arg_fail, &pd->list);
+ } else {
+ /* should never happen */
+ return_ECMD_FAILED;
+ }
+ }
+
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ return_ECMD_FAILED;
+
+ if (sigint_caught())
+ return_ECMD_FAILED;
+
+ if (prompt->abort_command)
+ return_ECMD_FAILED;
+ }
+
+ /*
+ * Reacquire the orphans lock to write the new PVs.
+ * It was locked for reading for the process_each_pv check.
+ */
+ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE, NULL)) {
+ log_error("Can't get lock for orphan PVs");
+ goto_out;
+ }
+
+ /*
+ * FIXME: The orphans lock was acquired and released during the
+ * process_each_pv read/check, and is reacquired here to write.
+ * The devices being created could have been changed while the
+ * lock was not held (during the prompts). So, each device in
+ * arg_create should now be checked again to verify if was
+ * changed while the lock was not held. If so, simply fail.
+ * We can probably just read the labels from the devices in
+ * arg_create and verify they match pd->dev->pvid.
+ */
+ dm_list_iterate_items(pd, &pp->arg_create) {
+ log_debug("Verifying that device %s pvid %s is unchanged",
+ pd->name, pd->dev->pvid);
+ /* TODO: label_read(pd->dev) ? */
+ }
+
+ /*
+ * wipe signatures on devices being created
+ */
+ dm_list_iterate_items_safe(pd, pd2, &pp->arg_create) {
+ log_verbose("Wiping signatures on new PV %s", pd->name);
+
+ if (!wipe_known_signatures(cmd, pd->dev, pd->name, TYPE_LVM1_MEMBER | TYPE_LVM2_MEMBER,
+ 0, pp->yes, pp->force, &pd->wiped)) {
+ dm_list_move(&pp->arg_fail, &pd->list);
+ }
+
+ if (pd->wiped) {
+ filter_refresh_needed = 1;
+ scan_needed = 1;
+ wiped = 1;
+ }
+
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ goto_out;
+
+ if (sigint_caught())
+ goto_out;
+ }
+
+#if 0
+ /*
+ * FIXME: why are filter_refresh and label_scan needed?
+ * Is this only for the subsequent dev_cache_get()?
+ */
+
+ if (filter_refresh_needed) {
+ if (!refresh_filters(cmd)) {
+ stack;
+ ret_max = ECMD_FAILED;
+ }
+ }
+
+ if (scan_needed) {
+ lvmcache_force_next_label_scan();
+ if (!lvmcache_label_scan(cmd)) {
+ stack;
+ ret_max = ECMD_FAILED;
+ }
+ }
+
+ /*
+ * wipe_known_signatures fires WATCH event to update udev database. But
+ * at the moment, we have no way to synchronize with such event - we
+ * may end up still seeing the old info in udev db and pvcreate can
+ * fail to proceed because of the device still being filtered (because
+ * of the stale info in udev db). Disable udev dev-ext source
+ * temporarily here for this reason and rescan with DEV_EXT_NONE
+ * dev-ext source (so filters use DEV_EXT_NONE source).
+ */
+ dev_ext_src = external_device_info_source();
+ if (wiped && (dev_ext_src == DEV_EXT_UDEV))
+ init_external_device_info_source(DEV_EXT_NONE);
+
+ /*
+ * FIXME: why look up the device again?
+ * How would wiping + filter refresh + label scan
+ * cause the dev to not be found here when it was
+ * previously found above?
+ */
+ dm_list_iterate_items_safe(pd, pd2, &pp->arg_create) {
+ if (!(dev = dev_cache_get(pd->name, cmd->full_filter))) {
+ log_error("%s: Couldn't find device. Check your filters?", pd->name);
+ dm_list_move(&pp->arg_fail, &pd->list);
+ }
+
+ /* Is this possible? */
+ if (dev != pd->dev)
+ log_warn("WARNING: device changed for %s.", pd->name);
+ }
+
+ init_external_device_info_source(dev_ext_src);
+
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ goto_out;
+#endif
+
+ /*
+ * create pvs on devices
+ */
+ dm_list_iterate_items_safe(pd, pd2, &pp->arg_create) {
+
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ break;
+
+ pv_name = pd->name;
+
+ log_verbose("Creating new PV %s", pv_name);
+
+ /* FIXME: get rid of rp usage in pv_create to avoid this. */
+ memset(&rp, 0, sizeof(rp));
+ rp.restorefile = pp->restorefile;
+ if (pp->uuid_str) {
+ rp.id = pp->id;
+ rp.idp = &pp->id;
+ }
+ rp.ba_start = pp->ba_start;
+ rp.ba_size = pp->ba_size;
+ rp.pe_start = pp->pe_start;
+ rp.extent_count = pp->extent_count;
+ rp.extent_size = pp->extent_size;
+
+ if (!(pv = pv_create(cmd, pd->dev, pp->size, pp->data_alignment,
+ pp->data_alignment_offset, pp->labelsector,
+ pp->pvmetadatacopies, pp->pvmetadatasize,
+ pp->metadataignore, &rp))) {
+ log_error("Failed to setup physical volume \"%s\"", pv_name);
+ dm_list_move(&pp->arg_fail, &pd->list);
+ continue;
+ }
+
+ log_verbose("Set up physical volume for \"%s\" with %" PRIu64
+ " available sectors", pv_name, pv_size(pv));
+
+ pv->status |= UNLABELLED_PV;
+
+ if (!label_remove(pv->dev)) {
+ log_error("Failed to wipe existing label on %s", pv_name);
+ dm_list_move(&pp->arg_fail, &pd->list);
+ continue;
+ }
+
+ if (pp->zero) {
+ log_verbose("Zeroing start of device %s", pv_name);
+
+ if (!dev_open_quiet(pv->dev)) {
+ log_error("%s not opened: device not zeroed", pv_name);
+ dm_list_move(&pp->arg_fail, &pd->list);
+ continue;
+ }
+
+ if (!dev_set(pv->dev, UINT64_C(0), (size_t) 2048, 0)) {
+ log_error("%s not wiped: aborting", pv_name);
+ if (!dev_close(pv->dev))
+ stack;
+ dm_list_move(&pp->arg_fail, &pd->list);
+ continue;
+ }
+ if (!dev_close(pv->dev))
+ stack;
+ }
+
+ log_verbose("Writing physical volume data to disk \"%s\"", pv_name);
+
+ if (!pv_write(cmd, pv, 1)) {
+ log_error("Failed to write physical volume \"%s\"", pv_name);
+ dm_list_move(&pp->arg_fail, &pd->list);
+ }
+
+ log_print_unless_silent("Physical volume \"%s\" successfully created", pv_name);
+ }
+
+ unlock_vg(cmd, VG_ORPHANS);
+
+ if (!dm_list_empty(&pp->arg_fail) && must_use_all)
+ return_ECMD_FAILED;
+
+ return ECMD_PROCESSED;
+
+out:
+ unlock_vg(cmd, VG_ORPHANS);
+ return ECMD_FAILED;
+}
+
diff --git a/tools/toollib.h b/tools/toollib.h
index 400bac520..b8dade218 100644
--- a/tools/toollib.h
+++ b/tools/toollib.h
@@ -101,8 +101,8 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv,
struct processing_handle *handle,
process_single_vg_fn_t process_single_vg);
-int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
- const char *vg_name, uint32_t lock_type,
+int process_each_pv(struct cmd_context *cmd, int argc, char **argv, const char *vg_name,
+ int all_is_set, uint32_t read_flags,
struct processing_handle *handle,
process_single_pv_fn_t process_single_pv);
@@ -136,6 +136,8 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
int stop_on_error, struct processing_handle *handle,
process_single_lv_fn_t process_single_lv);
+int pvcreate_each_device(struct cmd_context *cmd, struct pvcreate_each_params *pp);
+
struct processing_handle *init_processing_handle(struct cmd_context *cmd);
int init_selection_handle(struct cmd_context *cmd, struct processing_handle *handle,
report_type_t initial_report_type);
diff --git a/tools/tools.h b/tools/tools.h
index 4179cc8f4..de7064970 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -107,6 +107,8 @@ struct arg_value_group_list {
#define NO_METADATA_PROCESSING 0x00000040
/* Command wants to scan for new devices and force labels to be read from them all. */
#define REQUIRES_FULL_LABEL_SCAN 0x00000080
+/* Command must use all specified arg names and fail if all cannot be used. */
+#define MUST_USE_ALL_ARGS 0x00000100
/* a register of the lvm commands */
struct command {
diff --git a/tools/vgreduce.c b/tools/vgreduce.c
index 5d43c94e1..bc95c3a8d 100644
--- a/tools/vgreduce.c
+++ b/tools/vgreduce.c
@@ -188,7 +188,7 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
/* FIXME: Pass private struct through to all these functions */
/* and update in batch afterwards? */
return process_each_pv(cmd, argc, argv, vg_name,
- READ_FOR_UPDATE, NULL,
+ 0, READ_FOR_UPDATE, NULL,
_vgreduce_single);
log_verbose("Finding volume group \"%s\"", vg_name);