summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2016-05-25 13:57:33 -0500
committerDavid Teigland <teigland@redhat.com>2016-05-25 14:07:35 -0500
commit333767ca94d9387d17f17691199cfc0e176c73f1 (patch)
tree3e14662d02147ad652bb33d52a252e36081044b1
parent7fffcce9247cba8ecb40a1c7b0c6d18a5cde2165 (diff)
downloadlvm2-dev-dct-vgimportclone-4.tar.gz
vgimportclone: add native commanddev-dct-vgimportclone-4
to replace the previous script.
-rw-r--r--lib/Makefile.in1
-rw-r--r--lib/commands/toolcontext.c9
-rw-r--r--lib/filters/filter.h4
-rw-r--r--lib/metadata/metadata.c3
-rw-r--r--lib/misc/lvm-globals.c11
-rw-r--r--lib/misc/lvm-globals.h2
-rw-r--r--tools/Makefile.in1
-rw-r--r--tools/args.h2
-rw-r--r--tools/commands.h15
-rw-r--r--tools/vgimportclone.c341
10 files changed, 388 insertions, 1 deletions
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 467ef9077..451d96d8f 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -76,6 +76,7 @@ SOURCES =\
filters/filter-partitioned.c \
filters/filter-type.c \
filters/filter-usable.c \
+ filters/filter-internal.c \
format_text/archive.c \
format_text/archiver.c \
format_text/export.c \
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index 1e3f14a79..2df73c145 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -1053,7 +1053,7 @@ static int _init_dev_cache(struct cmd_context *cmd)
return 1;
}
-#define MAX_FILTERS 8
+#define MAX_FILTERS 9
static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd)
{
@@ -1078,6 +1078,13 @@ static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd)
nr_filt++;
}
+ /* internal filter used by command processing. */
+ if (!(filters[nr_filt] = internal_filter_create())) {
+ log_error("Failed to create internal device filter");
+ goto bad;
+ }
+ nr_filt++;
+
/* global regex filter. Optional. */
if ((cn = find_config_tree_node(cmd, devices_global_filter_CFG, NULL))) {
if (!(filters[nr_filt] = regex_filter_create(cn->v))) {
diff --git a/lib/filters/filter.h b/lib/filters/filter.h
index ebd25e2be..d75f6e11c 100644
--- a/lib/filters/filter.h
+++ b/lib/filters/filter.h
@@ -32,6 +32,10 @@ struct dev_filter *persistent_filter_create(struct dev_types *dt,
const char *file);
struct dev_filter *sysfs_filter_create(void);
+struct dev_filter *internal_filter_create(void);
+int internal_filter_allow(struct dm_pool *mem, struct device *dev);
+void internal_filter_clear(void);
+
/*
* patterns must be an array of strings of the form:
* [ra]<sep><regex><sep>, eg,
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 9e31ba45b..df413cc0e 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -3305,6 +3305,9 @@ static int _check_old_pv_ext_for_vg(struct volume_group *vg)
!pvl->pv->fmt->ops->pv_needs_rewrite)
continue;
+ if (_pv_in_pv_list(pvl->pv, &vg->pv_write_list))
+ continue;
+
if (!pvl->pv->fmt->ops->pv_needs_rewrite(pvl->pv->fmt, pvl->pv,
&pv_needs_rewrite))
return_0;
diff --git a/lib/misc/lvm-globals.c b/lib/misc/lvm-globals.c
index b7af353ae..9c78f3e33 100644
--- a/lib/misc/lvm-globals.c
+++ b/lib/misc/lvm-globals.c
@@ -26,6 +26,7 @@ static int _verbose_level = VERBOSE_BASE_LEVEL;
static int _silent = 0;
static int _test = 0;
static int _md_filtering = 0;
+static int _internal_filtering = 0;
static int _fwraid_filtering = 0;
static int _pvmove = 0;
static int _full_scan_done = 0; /* Restrict to one full scan during each cmd */
@@ -79,6 +80,11 @@ void init_md_filtering(int level)
_md_filtering = level;
}
+void init_internal_filtering(int level)
+{
+ _internal_filtering = level;
+}
+
void init_fwraid_filtering(int level)
{
_fwraid_filtering = level;
@@ -242,6 +248,11 @@ int md_filtering(void)
return _md_filtering;
}
+int internal_filtering(void)
+{
+ return _internal_filtering;
+}
+
int fwraid_filtering(void)
{
return _fwraid_filtering;
diff --git a/lib/misc/lvm-globals.h b/lib/misc/lvm-globals.h
index 77c01b413..14a7d4366 100644
--- a/lib/misc/lvm-globals.h
+++ b/lib/misc/lvm-globals.h
@@ -26,6 +26,7 @@ void init_verbose(int level);
void init_silent(int silent);
void init_test(int level);
void init_md_filtering(int level);
+void init_internal_filtering(int level);
void init_fwraid_filtering(int level);
void init_pvmove(int level);
void init_full_scan_done(int level);
@@ -60,6 +61,7 @@ void set_sysfs_dir_path(const char *path);
int test_mode(void);
int md_filtering(void);
+int internal_filtering(void);
int fwraid_filtering(void);
int pvmove_mode(void);
int full_scan_done(void);
diff --git a/tools/Makefile.in b/tools/Makefile.in
index f2f9faa56..9be8b7cd9 100644
--- a/tools/Makefile.in
+++ b/tools/Makefile.in
@@ -60,6 +60,7 @@ SOURCES =\
vgmerge.c \
vgmknodes.c \
lvpoll.c \
+ vgimportclone.c \
vgreduce.c \
vgremove.c \
vgrename.c \
diff --git a/tools/args.h b/tools/args.h
index ef9dbc951..1b12ddcb6 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -151,6 +151,7 @@ arg(autobackup_ARG, 'A', "autobackup", yes_no_arg, 0)
arg(activevolumegroups_ARG, 'A', "activevolumegroups", NULL, 0)
arg(background_ARG, 'b', "background", NULL, 0)
arg(backgroundfork_ARG, 'b', "background", NULL, 0)
+arg(basevgname_ARG, 'n', "basevgname", string_arg, 0)
arg(blockdevice_ARG, 'b', "blockdevice", NULL, 0)
arg(chunksize_ARG, 'c', "chunksize", size_kb_arg, 0)
arg(clustered_ARG, 'c', "clustered", yes_no_arg, 0)
@@ -167,6 +168,7 @@ arg(help_ARG, 'h', "help", NULL, 0)
arg(cache_ARG, 'H', "cache", NULL, 0)
arg(history_ARG, 'H', "history", NULL, 0)
arg(help2_ARG, '?', "", NULL, 0)
+arg(import_ARG, 'i', "import", NULL, 0)
arg(interval_ARG, 'i', "interval", int_arg, 0)
arg(iop_version_ARG, 'i', "iop_version", NULL, 0)
arg(stripes_ARG, 'i', "stripes", int_arg, 0)
diff --git a/tools/commands.h b/tools/commands.h
index da432a708..f0b30b67c 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -1260,6 +1260,21 @@ xx(vgimport,
all_ARG, force_ARG, select_ARG, test_ARG)
+xx(vgimportclone,
+ "Import a VG from cloned PVs",
+ 0,
+ "vgimportclone\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[-i|--import]\n"
+ "\t[-n|--basevgname]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]\n"
+ "\t[PhysicalVolumePath...]\n",
+
+ basevgname_ARG, test_ARG, import_ARG)
+
xx(vgmerge,
"Merge volume groups",
0,
diff --git a/tools/vgimportclone.c b/tools/vgimportclone.c
new file mode 100644
index 000000000..2f9a69cd0
--- /dev/null
+++ b/tools/vgimportclone.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * 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 Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+#include "tools.h"
+#include "lvmcache.h"
+#include "lvmetad-client.h"
+#include "filter.h"
+
+struct vgimportclone_params {
+ unsigned done;
+ unsigned total;
+
+ int import_vg;
+ int found_args;
+ struct dm_list arg_import;
+ const char *base_vgname;
+ const char *old_vgname;
+ const char *new_vgname;
+};
+
+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)
+{
+ struct vgimportclone_params *vp = (struct vgimportclone_params *) handle->custom_handle;
+ struct vgimportclone_device *vd;
+
+ 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;
+ }
+
+ 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;
+}
+
+static int _vgimportclone_vg_single(struct cmd_context *cmd, const char *vg_name,
+ struct volume_group *vg, struct processing_handle *handle)
+{
+ 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;
+ int devs_used_for_lv = 0;
+ int found;
+
+ if (vg_is_exported(vg) && !vp->import_vg) {
+ log_error("VG %s is exported, use the --import option.", vg->name);
+ goto_bad;
+ }
+
+ if (vg_status(vg) & PARTIAL_VG) {
+ log_error("VG %s is partial, it must be complete.", vg->name);
+ goto_bad;
+ }
+
+ /*
+ * N.B. lvs_in_vg_activated() is not smart enough to distinguish
+ * between LVs that are active in the original VG vs the cloned VG
+ * that's being imported, so check DEV_USED_FOR_LV.
+ */
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ 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++;
+ }
+ }
+
+ if (devs_used_for_lv)
+ goto_bad;
+
+ /*
+ * The arg_import 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 (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid)))
+ goto_bad;
+
+ /* all PVs in the VG must be imported together, pvl is missing from args. */
+ log_error("PV with UUID %s is part of VG %s, but is not included in the devices to import.",
+ uuid, vg->name);
+ log_error("All PVs in the VG must be imported together.");
+ goto_bad;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ /*
+ * Write changes.
+ */
+
+ if (!archive(vg))
+ return_ECMD_FAILED;
+
+ if (vp->import_vg)
+ vg->status &= ~EXPORTED_VG;
+
+ if (!id_create(&vg->id))
+ goto_bad;
+
+ /* Low level vg_write code needs old_name to be set! */
+ vg->old_name = vg->name;
+
+ if (!(vg->name = dm_pool_strdup(vg->vgmem, vp->new_vgname)))
+ goto_bad;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!(new_pvl = dm_pool_zalloc(vg->vgmem, sizeof(*new_pvl))))
+ goto_bad;
+
+ new_pvl->pv = pvl->pv;
+
+ if (!(pvl->pv->vg_name = dm_pool_strdup(vg->vgmem, vp->new_vgname)))
+ goto_bad;
+
+ if (vp->import_vg)
+ new_pvl->pv->status &= ~EXPORTED_VG;
+
+ /* Low level pv_write code needs old_id to be set! */
+ memcpy(&new_pvl->pv->old_id, &new_pvl->pv->id, sizeof(new_pvl->pv->id));
+
+ if (!id_create(&new_pvl->pv->id))
+ goto_bad;
+
+ dm_list_add(&vg->pv_write_list, &new_pvl->list);
+ }
+
+ dm_list_iterate_items(lvl, &vg->lvs)
+ memcpy(&lvl->lv->lvid, &vg->id, sizeof(vg->id));
+
+ if (!vg_write(vg) || !vg_commit(vg))
+ goto_bad;
+
+ return ECMD_PROCESSED;
+bad:
+ return ECMD_FAILED;
+}
+
+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 vgnameid_list *vgnl;
+ struct vgimportclone_device *vd;
+ struct lvmcache_info *info;
+ const char *vgname;
+ char base_vgname[NAME_LEN] = { 0 };
+ char tmp_vgname[NAME_LEN] = { 0 };
+ unsigned int vgname_count;
+ int ret = ECMD_FAILED;
+
+ if (!argc) {
+ log_error("PV names required.");
+ return EINVALID_CMD_LINE;
+ }
+
+ dm_list_init(&vgnameids_on_system);
+ dm_list_init(&vp.arg_import);
+
+ set_pv_notify(cmd);
+
+ vp.import_vg = arg_is_set(cmd, import_ARG);
+
+ if (!(handle = init_processing_handle(cmd))) {
+ log_error("Failed to initialize processing handle.");
+ return ECMD_FAILED;
+ }
+ handle->custom_handle = &vp;
+
+ if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_WRITE, NULL)) {
+ log_error("Unable to obtain global lock.");
+ destroy_processing_handle(cmd, handle);
+ return ECMD_FAILED;
+ }
+
+ if (!lockd_gl(cmd, "ex", 0))
+ goto_out;
+ cmd->lockd_gl_disable = 1;
+
+ lvmetad_set_disabled(cmd, LVMETAD_DISABLE_REASON_DUPLICATES);
+ lvmetad_make_unused(cmd);
+
+ /*
+ * Find the devices being imported which are named on the command line.
+ * They may be in the list of unchosen duplicates.
+ */
+
+ log_debug("Finding devices to import.");
+ cmd->command->flags |= ENABLE_DUPLICATE_DEVS;
+ process_each_pv(cmd, argc, argv, NULL, 0, READ_ALLOW_EXPORTED, handle, _vgimportclone_pv_single);
+
+ if (vp.found_args != argc) {
+ log_error("Failed to find all devices.");
+ 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.
+ */
+
+ dm_list_iterate_items(vd, &vp.arg_import) {
+ if (!(info = lvmcache_info_from_pvid(vd->dev->pvid, 0))) {
+ log_error("Failed to find PVID for device %s in lvmcache.", dev_name(vd->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;
+ }
+
+ 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;
+ }
+ }
+ }
+
+ /*
+ * 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.
+ */
+
+ if (arg_is_set(cmd, basevgname_ARG)) {
+ snprintf(base_vgname, sizeof(base_vgname) - 1, "%s", arg_str_value(cmd, basevgname_ARG, ""));
+ memcpy(tmp_vgname, base_vgname, NAME_LEN);
+ vgname_count = 0;
+ } else {
+ snprintf(base_vgname, sizeof(base_vgname) - 1, "%s", vp.old_vgname);
+ snprintf(tmp_vgname, sizeof(tmp_vgname) - 1, "%s1", vp.old_vgname);
+ vgname_count = 1;
+ }
+
+ if (!get_vgnameids(cmd, &vgnameids_on_system, NULL, 0))
+ goto_out;
+
+retry_name:
+ dm_list_iterate_items(vgnl, &vgnameids_on_system) {
+ if (!strcmp(vgnl->vg_name, tmp_vgname)) {
+ vgname_count++;
+ snprintf(tmp_vgname, sizeof(tmp_vgname) - 1, "%s%u", base_vgname, vgname_count);
+ goto retry_name;
+ }
+ }
+
+ if (!(vp.new_vgname = dm_pool_strdup(cmd->mem, tmp_vgname)))
+ goto_out;
+ log_debug("Using new VG name %s.", vp.new_vgname);
+
+ /*
+ * 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.
+ */
+
+ init_internal_filtering(1);
+ dm_list_iterate_items(vd, &vp.arg_import)
+ internal_filter_allow(cmd->mem, vd->dev);
+ lvmcache_destroy(cmd, 1, 0);
+ dev_cache_full_scan(cmd->full_filter);
+
+ log_debug("Changing VG %s to %s.", vp.old_vgname, vp.new_vgname);
+
+ /* We don't care if the new name comes before the old in lock order. */
+ lvmcache_lock_ordering(0);
+
+ 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;
+ }
+
+ ret = process_each_vg(cmd, 0, NULL, vp.old_vgname, NULL, READ_FOR_UPDATE | READ_ALLOW_EXPORTED, handle, _vgimportclone_vg_single);
+
+ unlock_vg(cmd, vp.new_vgname);
+out:
+ unlock_vg(cmd, VG_GLOBAL);
+ internal_filter_clear();
+ init_internal_filtering(0);
+ lvmcache_lock_ordering(1);
+ destroy_processing_handle(cmd, handle);
+ return ret;
+}
+