diff options
Diffstat (limited to 'tools/vgimportclone.c')
-rw-r--r-- | tools/vgimportclone.c | 404 |
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; } |