summaryrefslogtreecommitdiff
path: root/tools/vgimportclone.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/vgimportclone.c')
-rw-r--r--tools/vgimportclone.c404
1 files changed, 277 insertions, 127 deletions
diff --git a/tools/vgimportclone.c b/tools/vgimportclone.c
index ee1c28fae..7c961c4bb 100644
--- a/tools/vgimportclone.c
+++ b/tools/vgimportclone.c
@@ -15,62 +15,39 @@
#include "tools.h"
#include "lib/cache/lvmcache.h"
#include "lib/filters/filter.h"
+#include "lib/device/device_id.h"
struct vgimportclone_params {
- unsigned done;
- unsigned total;
-
- int import_vg;
- int found_args;
- struct dm_list arg_import;
+ struct dm_list new_devs;
const char *base_vgname;
const char *old_vgname;
const char *new_vgname;
+ unsigned import_devices:1;
+ unsigned import_vg:1;
};
-struct vgimportclone_device {
- struct dm_list list;
- struct device *dev;
- unsigned found_in_vg : 1;
-};
-
-static int _vgimportclone_pv_single(struct cmd_context *cmd, struct volume_group *vg,
- struct physical_volume *pv, struct processing_handle *handle)
+static struct device_list *_get_device_list(struct dm_list *list, struct device *dev)
{
- struct vgimportclone_params *vp = (struct vgimportclone_params *) handle->custom_handle;
- struct vgimportclone_device *vd;
+ struct device_list *devl;
- if (vg && is_orphan_vg(vg->name)) {
- log_error("Cannot import clone of orphan PV %s.", dev_name(pv->dev));
- return ECMD_FAILED;
- }
-
- if (!(vd = dm_pool_zalloc(cmd->mem, sizeof(*vd)))) {
- log_error("alloc failed.");
- return ECMD_FAILED;
+ dm_list_iterate_items(devl, list) {
+ if (devl->dev == dev)
+ return devl;
}
-
- vd->dev = pv->dev;
- dm_list_add(&vp->arg_import, &vd->list);
-
- log_debug("vgimportclone dev %s VG %s found to import",
- dev_name(vd->dev), vg ? vg->name : "<none>");
-
- vp->found_args++;
-
- return ECMD_PROCESSED;
+ return NULL;
}
-static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name,
- struct volume_group *vg, struct processing_handle *handle)
+static int _update_vg(struct cmd_context *cmd, struct volume_group *vg,
+ struct vgimportclone_params *vp)
{
char uuid[64] __attribute__((aligned(8)));
- struct vgimportclone_params *vp = (struct vgimportclone_params *) handle->custom_handle;
- struct vgimportclone_device *vd;
struct pv_list *pvl, *new_pvl;
struct lv_list *lvl;
+ struct device_list *devl;
+ struct dm_list tmp_devs;
int devs_used_for_lv = 0;
- int found;
+
+ dm_list_init(&tmp_devs);
if (vg_is_exported(vg) && !vp->import_vg) {
log_error("VG %s is exported, use the --import option.", vg->name);
@@ -88,6 +65,10 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name
* that's being imported, so check DEV_USED_FOR_LV.
*/
dm_list_iterate_items(pvl, &vg->pvs) {
+ if (is_missing_pv(pvl->pv) || !pvl->pv->dev) {
+ log_error("VG is missing a device.");
+ goto bad;
+ }
if (pvl->pv->dev->flags & DEV_USED_FOR_LV) {
log_error("Device %s has active LVs, deactivate first.", dev_name(pvl->pv->dev));
devs_used_for_lv++;
@@ -98,21 +79,14 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name
goto_bad;
/*
- * The arg_import list must match the PVs in VG.
+ * The new_devs list must match the PVs in VG.
*/
dm_list_iterate_items(pvl, &vg->pvs) {
- found = 0;
-
- dm_list_iterate_items(vd, &vp->arg_import) {
- if (pvl->pv->dev != vd->dev)
- continue;
- vd->found_in_vg = 1;
- found = 1;
- break;
- }
-
- if (!found) {
+ if ((devl = _get_device_list(&vp->new_devs, pvl->pv->dev))) {
+ dm_list_del(&devl->list);
+ dm_list_add(&tmp_devs, &devl->list);
+ } else {
if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid)))
goto_bad;
@@ -124,21 +98,21 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name
}
}
- dm_list_iterate_items(vd, &vp->arg_import) {
- if (!vd->found_in_vg) {
- /* device arg is not in the VG. */
- log_error("Device %s was not found in VG %s.", dev_name(vd->dev), vg->name);
- log_error("The devices to import must match the devices in the VG.");
- goto bad;
- }
+ dm_list_iterate_items(devl, &vp->new_devs) {
+ /* device arg is not in the VG. */
+ log_error("Device %s was not found in VG %s.", dev_name(devl->dev), vg->name);
+ log_error("The devices to import must match the devices in the VG.");
+ goto bad;
}
+ dm_list_splice(&vp->new_devs, &tmp_devs);
+
/*
* Write changes.
*/
if (!archive(vg))
- return_ECMD_FAILED;
+ goto_bad;
if (vp->import_vg)
vg->status &= ~EXPORTED_VG;
@@ -175,6 +149,8 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name
if (!id_create(&new_pvl->pv->id))
goto_bad;
+ memcpy(&pvl->pv->dev->pvid, &new_pvl->pv->id.uuid, ID_LEN);
+
dm_list_add(&vg->pv_write_list, &new_pvl->list);
}
@@ -183,97 +159,255 @@ static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name
lvl->lv->lock_args = NULL;
}
+ /*
+ * Add the device id before writing the vg so that the device id
+ * will be included in the metadata. The device file is written
+ * (with these additions) at the end of the command.
+ */
+ if (vp->import_devices) {
+ dm_list_iterate_items(devl, &vp->new_devs) {
+ if (!device_id_add(cmd, devl->dev, devl->dev->pvid, NULL, NULL)) {
+ log_error("Failed to add device id for %s.", dev_name(devl->dev));
+ goto bad;
+ }
+ }
+ }
+
if (!vg_write(vg) || !vg_commit(vg))
goto_bad;
- return ECMD_PROCESSED;
+ return 1;
bad:
- return ECMD_FAILED;
+ return 0;
+}
+
+/*
+ * Create a list of devices that label_scan would scan excluding devs in
+ * new_devs.
+ */
+static int _get_other_devs(struct cmd_context *cmd, struct dm_list *new_devs, struct dm_list *other_devs)
+{
+ struct dev_iter *iter;
+ struct device *dev;
+ struct device_list *devl;
+
+ if (!(iter = dev_iter_create(cmd->filter, 0)))
+ return_0;
+
+ while ((dev = dev_iter_get(cmd, iter))) {
+ if (_get_device_list(new_devs, dev))
+ continue;
+ if (!(devl = malloc(sizeof(*devl))))
+ return_0;
+ devl->dev = dev;
+ dm_list_add(other_devs, &devl->list);
+ }
+
+ dev_iter_destroy(iter);
+ return 1;
}
int vgimportclone(struct cmd_context *cmd, int argc, char **argv)
{
struct vgimportclone_params vp = { 0 };
- struct processing_handle *handle = NULL;
- struct dm_list vgnameids_on_system; /* vgnameid_list */
+ struct dm_list vgnames;
struct vgnameid_list *vgnl;
- struct vgimportclone_device *vd;
- struct lvmcache_info *info;
+ struct device *dev;
+ struct device_list *devl;
+ struct dm_list other_devs;
+ struct volume_group *vg;
const char *vgname;
char base_vgname[NAME_LEN] = { 0 };
char tmp_vgname[NAME_LEN] = { 0 };
+ uint32_t lockd_state = 0;
+ uint32_t error_flags = 0;
unsigned int vgname_count;
int ret = ECMD_FAILED;
+ int i;
- if (!argc) {
- log_error("PV names required.");
- return EINVALID_CMD_LINE;
- }
-
- dm_list_init(&vgnameids_on_system);
- dm_list_init(&vp.arg_import);
+ dm_list_init(&vgnames);
+ dm_list_init(&other_devs);
+ dm_list_init(&vp.new_devs);
set_pv_notify(cmd);
+ vp.import_devices = arg_is_set(cmd, importdevices_ARG);
vp.import_vg = arg_is_set(cmd, import_ARG);
- if (!(handle = init_processing_handle(cmd, NULL))) {
- log_error("Failed to initialize processing handle.");
+ if (!lock_global(cmd, "ex"))
return ECMD_FAILED;
- }
- handle->custom_handle = &vp;
- if (!lock_global(cmd, "ex")) {
- destroy_processing_handle(cmd, handle);
+ clear_hint_file(cmd);
+
+ cmd->edit_devices_file = 1;
+
+ if (!setup_devices(cmd)) {
+ log_error("Failed to set up devices.");
return ECMD_FAILED;
}
/*
- * Find the devices being imported which are named on the command line.
- * They may be in the list of unchosen duplicates.
+ * When importing devices not in the devices file
+ * we cannot use the device id filter when looking
+ * for the devs.
*/
+ if (vp.import_devices) {
+ if (!cmd->enable_devices_file) {
+ log_print("Devices file not enabled, ignoring importdevices.");
+ vp.import_devices = 0;
+ } else if (!devices_file_exists(cmd)) {
+ log_print("Devices file does not exist, ignoring importdevices.");
+ vp.import_devices = 0;
+ } else {
+ cmd->filter_deviceid_skip = 1;
+ }
+ }
- log_debug("Finding devices to import.");
- cmd->cname->flags |= ENABLE_DUPLICATE_DEVS;
- process_each_pv(cmd, argc, argv, NULL, 0, 0, handle, _vgimportclone_pv_single);
+ /*
+ * For each device arg, get the dev from dev-cache.
+ * Only apply nodata filters when getting the devs
+ * from dev cache. The data filters will be applied
+ * next when label scan is done on them.
+ */
+ cmd->filter_nodata_only = 1;
- if (vp.found_args != argc) {
- log_error("Failed to find all devices.");
- goto out;
+ for (i = 0; i < argc; i++) {
+ if (!(dev = dev_cache_get(cmd, argv[i], cmd->filter))) {
+ /* FIXME: if filtered print which */
+ log_error("Failed to find device %s.", argv[i]);
+ goto out;
+ }
+
+ if (!(devl = malloc(sizeof(*devl))))
+ goto_out;
+
+ devl->dev = dev;
+ dm_list_add(&vp.new_devs, &devl->list);
+ }
+
+ /*
+ * Clear the result of nodata filtering so all
+ * filters will be applied in label_scan.
+ */
+ dm_list_iterate_items(devl, &vp.new_devs)
+ cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL);
+
+ /*
+ * Scan lvm info from each new dev, and apply the filters
+ * again, this time applying filters that use data.
+ */
+ log_debug("scan new devs");
+
+ label_scan_setup_bcache();
+
+ cmd->filter_nodata_only = 0;
+
+ label_scan_devs(cmd, cmd->filter, &vp.new_devs);
+
+ /*
+ * Check if any new devs were excluded by filters
+ * in label scan, where all filters were applied.
+ * (incl those that need data.)
+ */
+ dm_list_iterate_items(devl, &vp.new_devs) {
+ if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, "persistent")) {
+ /* FIXME: print which filter */
+ log_error("Device %s was excluded by filters.", dev_name(devl->dev));
+ goto out;
+ }
}
/*
- * Find the VG name of the PVs being imported, save as old_vgname.
- * N.B. If vd->dev is a duplicate, then it may not match info->dev.
+ * Look up vg info in lvmcache for each new_devs entry. This info was
+ * found by label scan. Verify all the new devs are from the same vg.
+ * The lvmcache at this point only reflects a label scan, not a vg_read
+ * which would assign PV info's for PVs without metadata. So this
+ * check is incomplete, and the same vg for devs is verified again
+ * later.
*/
+ dm_list_iterate_items(devl, &vp.new_devs) {
+ struct lvmcache_info *info;
- dm_list_iterate_items(vd, &vp.arg_import) {
- if (!(info = lvmcache_info_from_pvid(vd->dev->pvid, NULL, 0))) {
- log_error("Failed to find PVID for device %s in lvmcache.", dev_name(vd->dev));
+ if (!(info = lvmcache_info_from_pvid(devl->dev->pvid, devl->dev, 0))) {
+ log_error("Failed to find PVID for device %s.", dev_name(devl->dev));
goto out;
}
if (!(vgname = lvmcache_vgname_from_info(info))) {
- log_error("Failed to find VG name for device %s in lvmcache.", dev_name(vd->dev));
- goto out;
+ /* The PV may not have metadata, this will be resolved in
+ the process_each_vg/vg_read at the end. */
+ continue;
}
if (!vp.old_vgname) {
if (!(vp.old_vgname = dm_pool_strdup(cmd->mem, vgname)))
goto_out;
- } else {
- if (strcmp(vp.old_vgname, vgname)) {
- log_error("Devices must be from the same VG.");
- goto out;
- }
+ } else if (strcmp(vp.old_vgname, vgname)) {
+ log_error("Devices must be from the same VG.");
+ goto out;
}
}
+ if (!vp.old_vgname) {
+ log_error("No VG found on devices.");
+ goto out;
+ }
+
+ /*
+ * Get rid of lvmcache info from the new devs because we are going to
+ * read the other devs next (which conflict with the new devs because
+ * of the duplicated info.)
+ */
+ lvmcache_destroy(cmd, 1, 0);
+
+ /*
+ * Now processing other devs instead of new devs, so return to using
+ * the deviceid filter. (wiping filters not needed since these other
+ * devs have not been filtered yet.)
+ */
+ cmd->filter_deviceid_skip = 0;
+
+ /*
+ * Scan all other devs (devs that would normally be seen excluding new
+ * devs). This is necessary to check if the new vgname conflicts with
+ * an existing vgname on other devices. We don't need to actually
+ * process any existing VGs, we only process the VG on the new devs
+ * being imported after this.
+ *
+ * This only requires a label_scan of the other devs which is enough to
+ * see what the other vgnames are.
+ *
+ * Only apply nodata filters when creating the other_devs list.
+ * Then apply all filters when label_scan_devs processes the label.
+ */
+
+ log_debug("get other devices");
+
+ cmd->filter_nodata_only = 1;
+
+ if (!_get_other_devs(cmd, &vp.new_devs, &other_devs))
+ goto_out;
+
+ log_debug("scan other devices");
+
+ cmd->filter_nodata_only = 0;
+
+ /*
+ * Clear the result of nodata filtering so all
+ * filters will be applied in label_scan.
+ */
+ dm_list_iterate_items(devl, &other_devs)
+ cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL);
+
+ label_scan_devs(cmd, cmd->filter, &other_devs);
+
+ if (!lvmcache_get_vgnameids(cmd, &vgnames, NULL, 0))
+ goto_out;
+
/*
* Pick a new VG name, save as new_vgname. The new name begins with
* the basevgname or old_vgname, plus a $i suffix, if necessary, to
- * make it unique. This requires comparing the old_vgname with all the
- * VG names on the system.
+ * make it unique.
*/
if (arg_is_set(cmd, basevgname_ARG)) {
@@ -296,11 +430,8 @@ int vgimportclone(struct cmd_context *cmd, int argc, char **argv)
vgname_count = 1;
}
- if (!lvmcache_get_vgnameids(cmd, &vgnameids_on_system, NULL, 0))
- goto_out;
-
retry_name:
- dm_list_iterate_items(vgnl, &vgnameids_on_system) {
+ dm_list_iterate_items(vgnl, &vgnames) {
if (!strcmp(vgnl->vg_name, tmp_vgname)) {
vgname_count++;
if (dm_snprintf(tmp_vgname, sizeof(tmp_vgname), "%s%u", base_vgname, vgname_count) < 0) {
@@ -315,42 +446,61 @@ retry_name:
goto_out;
log_debug("Using new VG name %s.", vp.new_vgname);
- lvmcache_destroy(cmd, 1, 0);
-
/*
- * Create a device filter so that we are only working with the devices
- * in arg_import. With the original devs hidden (that arg_import were
- * cloned from), we can read and write the cloned PVs and VG without
- * touching the original PVs/VG.
+ * Get rid of lvmcache info from the other devs because we are going to
+ * read the new devs again, now to update them.
*/
+ lvmcache_destroy(cmd, 1, 0);
- init_internal_filtering(1);
- dm_list_iterate_items(vd, &vp.arg_import)
- internal_filter_allow(cmd->mem, vd->dev);
- refresh_filters(cmd);
-
- log_debug("Changing VG %s to %s.", vp.old_vgname, vp.new_vgname);
+ log_debug("import vg on new devices");
if (!lock_vol(cmd, vp.new_vgname, LCK_VG_WRITE, NULL)) {
log_error("Can't get lock for new VG name %s", vp.new_vgname);
goto out;
}
- /*
- * Trying to lock the duplicated VG would conflict with the original,
- * and it's not needed because the new VG will be imported as a local VG.
- */
- cmd->lockd_vg_disable = 1;
+ /* No filter used since these devs have already been filtered above. */
+ label_scan_devs_rw(cmd, NULL, &vp.new_devs);
- clear_hint_file(cmd);
+ cmd->can_use_one_scan = 1;
+ cmd->include_exported_vgs = 1;
- ret = process_each_vg(cmd, 0, NULL, vp.old_vgname, NULL, READ_FOR_UPDATE, 0, handle, _vgimportclone_vg_single);
+ vg = vg_read(cmd, vp.old_vgname, NULL, READ_WITHOUT_LOCK | READ_FOR_UPDATE, lockd_state, &error_flags, NULL);
+ if (!vg) {
+ log_error("Failed to read VG %s from devices being imported.", vp.old_vgname);
+ unlock_vg(cmd, NULL, vp.new_vgname);
+ goto out;
+ }
+
+ if (error_flags) {
+ log_error("Error reading VG %s from devices being imported.", vp.old_vgname);
+ release_vg(vg);
+ unlock_vg(cmd, NULL, vp.new_vgname);
+ goto out;
+ }
+
+ if (!_update_vg(cmd, vg, &vp)) {
+ log_error("Failed to update VG on devices being imported.");
+ release_vg(vg);
+ unlock_vg(cmd, NULL, vp.new_vgname);
+ goto out;
+ }
+ release_vg(vg);
unlock_vg(cmd, NULL, vp.new_vgname);
-out:
- internal_filter_clear();
- init_internal_filtering(0);
- destroy_processing_handle(cmd, handle);
+ /*
+ * Should we be using device_ids_validate to check/fix other
+ * devs in the devices file?
+ */
+ if (vp.import_devices) {
+ if (!device_ids_write(cmd)) {
+ log_error("Failed to write devices file.");
+ goto out;
+ }
+ }
+ ret = ECMD_PROCESSED;
+out:
+ unlock_devices_file(cmd);
return ret;
}