1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

vgreduce --removemissing to remove missing PVs & deps & make VG consistent

This commit is contained in:
Alasdair Kergon 2003-01-17 21:04:26 +00:00
parent 8ae908a085
commit a421f74371
8 changed files with 273 additions and 43 deletions

View File

@ -33,10 +33,12 @@ static void _put_extents(struct lv_segment *seg)
for (s = 0; s < seg->stripes; s++) { for (s = 0; s < seg->stripes; s++) {
pv = seg->area[s].pv; pv = seg->area[s].pv;
count = seg->len / seg->stripes;
assert(pv->pe_alloc_count >= count); if (pv) {
pv->pe_alloc_count -= count; count = seg->len / seg->stripes;
assert(pv->pe_alloc_count >= count);
pv->pe_alloc_count -= count;
}
} }
} }

View File

@ -318,6 +318,18 @@ struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name)
return NULL; return NULL;
} }
int pv_is_in_vg(struct volume_group *vg, struct physical_volume *pv)
{
struct list *pvh;
list_iterate(pvh, &vg->pvs) {
if (pv == list_item(pvh, struct pv_list)->pv)
return 1;
}
return 0;
}
struct physical_volume *find_pv_in_vg_by_uuid(struct volume_group *vg, struct physical_volume *find_pv_in_vg_by_uuid(struct volume_group *vg,
struct id *id) struct id *id)
{ {

View File

@ -424,6 +424,8 @@ int lv_merge_segments(struct logical_volume *lv);
int lv_is_origin(const struct logical_volume *lv); int lv_is_origin(const struct logical_volume *lv);
int lv_is_cow(const struct logical_volume *lv); int lv_is_cow(const struct logical_volume *lv);
int pv_is_in_vg(struct volume_group *vg, struct physical_volume *pv);
struct snapshot *find_cow(const struct logical_volume *lv); struct snapshot *find_cow(const struct logical_volume *lv);
struct snapshot *find_origin(const struct logical_volume *lv); struct snapshot *find_origin(const struct logical_volume *lv);
struct list *find_snapshots(const struct logical_volume *lv); struct list *find_snapshots(const struct logical_volume *lv);

View File

@ -4,6 +4,7 @@ vgreduce \- reduce a volume group
.SH SYNOPSIS .SH SYNOPSIS
.B vgreduce .B vgreduce
[\-a/\-\-all] [\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-h/\-?/\-\-help] [\-a/\-\-all] [\-A/\-\-autobackup y/n] [\-d/\-\-debug] [\-h/\-?/\-\-help]
[\-\-removemissing]
[\-t/\-\-test] [\-t/\-\-test]
[\-v/\-\-verbose] VolumeGroupName [\-v/\-\-verbose] VolumeGroupName
[PhysicalVolumePath...] [PhysicalVolumePath...]
@ -15,6 +16,22 @@ See \fBlvm\fP for common options.
.TP .TP
.I \-a, \-\-all .I \-a, \-\-all
Removes all empty physical volumes if none are given on command line. Removes all empty physical volumes if none are given on command line.
.TP
.I \-\-removemissing
Removes all missing physical volumes from the volume group and makes
the volume group consistent again.
It's a good idea to run this option with --test first to find out what it
would remove before running it for real.
Any logical volumes and dependent snapshots that were partly on the
missing disks get removed completely. This includes those parts
that lie on disks that are still present.
If your logical volumes spanned several disks including the ones that are
lost, you might want to try to salvage data first by activating your
logical volumes with --partial as described in \fBlvm (8)\fP.
.SH SEE ALSO .SH SEE ALSO
.BR lvm (8), .BR lvm (8),
.BR vgextend (8) .BR vgextend (8)

View File

@ -23,6 +23,7 @@ arg(noheadings_ARG, '\0', "noheadings", NULL)
arg(segments_ARG, '\0', "segments", NULL) arg(segments_ARG, '\0', "segments", NULL)
arg(units_ARG, '\0', "units", string_arg) arg(units_ARG, '\0', "units", string_arg)
arg(nosuffix_ARG, '\0', "nosuffix", NULL) arg(nosuffix_ARG, '\0', "nosuffix", NULL)
arg(removemissing_ARG, '\0', "removemissing", NULL)
/* Allow some variations */ /* Allow some variations */
arg(resizable_ARG, '\0', "resizable", yes_no_arg) arg(resizable_ARG, '\0', "resizable", yes_no_arg)

View File

@ -678,13 +678,14 @@ xx(vgreduce,
"\t[-A|--autobackup y|n]\n" "\t[-A|--autobackup y|n]\n"
"\t[-d|--debug]\n" "\t[-d|--debug]\n"
"\t[-h|--help]\n" "\t[-h|--help]\n"
"\t[--removemissing]\n"
"\t[-t|--test]\n" "\t[-t|--test]\n"
"\t[-v|--verbose]\n" "\t[-v|--verbose]\n"
"\t[--version]" "\n" "\t[--version]" "\n"
"\tVolumeGroupName\n" "\tVolumeGroupName\n"
"\t[PhysicalVolumePath...]\n", "\t[PhysicalVolumePath...]\n",
all_ARG, autobackup_ARG, test_ARG) all_ARG, autobackup_ARG, removemissing_ARG, test_ARG)
xx(vgremove, xx(vgremove,
"Remove volume group(s)", "Remove volume group(s)",

View File

@ -20,9 +20,155 @@
#include "tools.h" #include "tools.h"
static int _remove_pv(struct volume_group *vg, struct pv_list *pvl)
{
char uuid[64];
if (vg->pv_count == 1) {
log_error("Volume Groups must always contain at least one PV");
return 0;
}
if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid))) {
stack;
return 0;
}
log_verbose("Removing PV with UUID %s from VG %s", uuid, vg->name);
if (pvl->pv->pe_alloc_count) {
log_error("LVs still present on PV with UUID %s: Can't remove "
"from VG %s", uuid, vg->name);
return 0;
}
vg->free_count -= pvl->pv->pe_count;
vg->extent_count -= pvl->pv->pe_count;
vg->pv_count--;
list_del(&pvl->list);
return 1;
}
static int _remove_lv(struct cmd_context *cmd, struct logical_volume *lv,
int *list_unsafe)
{
struct snapshot *snap;
struct list *snaplist, *snh;
log_verbose("%s/%s has missing extents: removing (including "
"dependencies)", lv->vg->name, lv->name);
/* Deactivate if necessary */
if (!lv_is_cow(lv)) {
log_verbose("Deactivating (if active) logical volume %s",
lv->name);
if (!lock_vol(cmd, lv->lvid.s,
LCK_LV_DEACTIVATE | LCK_NONBLOCK)) {
log_error("Failed to deactivate LV %s", lv->name);
return 0;
}
} else if ((snap = find_cow(lv))) {
log_verbose("Deactivating (if active) logical volume %s "
"(origin of %s)", snap->origin->name, lv->name);
if (!lock_vol(cmd, snap->origin->lvid.s,
LCK_LV_DEACTIVATE | LCK_NONBLOCK)) {
log_error("Failed to deactivate LV %s",
snap->origin->name);
return 0;
}
/* Use the origin LV */
lv = snap->origin;
}
/* Remove snapshot dependencies */
if (!(snaplist = find_snapshots(lv))) {
stack;
return 0;
}
/* List may be empty */
list_iterate(snh, snaplist) {
*list_unsafe = 1; /* May remove caller's lvht! */
snap = list_item(snh, struct snapshot_list)->snapshot;
if (!vg_remove_snapshot(lv->vg, snap->cow)) {
stack;
return 0;
}
log_verbose("Removing LV %s from VG %s", snap->cow->name,
lv->vg->name);
if (!lv_remove(lv->vg, snap->cow)) {
stack;
return 0;
}
}
/* Remove the LV itself */
log_verbose("Removing LV %s from VG %s", lv->name, lv->vg->name);
if (!lv_remove(lv->vg, lv)) {
stack;
return 0;
}
return 1;
}
static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
{
struct list *pvh, *pvht;
struct list *lvh, *lvht;
struct pv_list *pvl;
struct logical_volume *lv;
struct physical_volume *pv;
struct lv_segment *seg;
unsigned int s;
int list_unsafe;
struct list *segh;
/* Deactivate & remove necessary LVs */
restart_loop:
list_unsafe = 0; /* Set if we delete a different list-member */
list_iterate_safe(lvh, lvht, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
/* Are any segments of this LV on missing PVs? */
list_iterate(segh, &lv->segments) {
seg = list_item(segh, struct lv_segment);
for (s = 0; s < seg->stripes; s++) {
pv = seg->area[s].pv;
if (!pv || !pv->dev) {
if (!_remove_lv(cmd, lv, &list_unsafe)) {
stack;
return 0;
}
if (list_unsafe)
goto restart_loop;
}
}
}
}
/* Remove missing PVs */
list_iterate_safe(pvh, pvht, &vg->pvs) {
pvl = list_item(pvh, struct pv_list);
if (pvl->pv->dev)
continue;
if (!_remove_pv(vg, pvl)) {
stack;
return 0;
}
}
return 1;
}
/* Or take pv_name instead? */ /* Or take pv_name instead? */
static int vgreduce_single(struct cmd_context *cmd, struct volume_group *vg, static int _vgreduce_single(struct cmd_context *cmd, struct volume_group *vg,
struct physical_volume *pv, void *handle) struct physical_volume *pv, void *handle)
{ {
struct pv_list *pvl; struct pv_list *pvl;
const char *name = dev_name(pv->dev); const char *name = dev_name(pv->dev);
@ -72,20 +218,27 @@ static int vgreduce_single(struct cmd_context *cmd, struct volume_group *vg,
return 0; return 0;
} }
int vgreduce(struct cmd_context *cmd, int argc, char **argv) int vgreduce(struct cmd_context *cmd, int argc, char **argv)
{ {
struct volume_group *vg; struct volume_group *vg;
char *vg_name; char *vg_name;
int ret; int ret = 1;
int consistent = 1; int consistent = 1;
if (!argc) { if (!argc & !arg_count(cmd, removemissing_ARG)) {
log_error("Please give volume group name and " log_error("Please give volume group name and "
"physical volume paths"); "physical volume paths");
return EINVALID_CMD_LINE; return EINVALID_CMD_LINE;
} }
if (argc == 1 && !arg_count(cmd, all_ARG)) { if (!argc & arg_count(cmd, removemissing_ARG)) {
log_error("Please give volume group name");
return EINVALID_CMD_LINE;
}
if (argc == 1 && !arg_count(cmd, all_ARG)
&& !arg_count(cmd, removemissing_ARG)) {
log_error("Please enter physical volume paths or option -a"); log_error("Please enter physical volume paths or option -a");
return EINVALID_CMD_LINE; return EINVALID_CMD_LINE;
} }
@ -96,6 +249,11 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
return EINVALID_CMD_LINE; return EINVALID_CMD_LINE;
} }
if (argc > 1 && arg_count(cmd, removemissing_ARG)) {
log_error("Please only specify the volume group");
return EINVALID_CMD_LINE;
}
vg_name = argv[0]; vg_name = argv[0];
argv++; argv++;
argc--; argc--;
@ -106,33 +264,82 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
return ECMD_FAILED; return ECMD_FAILED;
} }
if (!(vg = vg_read(cmd, vg_name, &consistent)) || !consistent) { if ((!(vg = vg_read(cmd, vg_name, &consistent)) || !consistent) &&
!arg_count(cmd, removemissing_ARG)) {
log_error("Volume group \"%s\" doesn't exist", vg_name); log_error("Volume group \"%s\" doesn't exist", vg_name);
unlock_vg(cmd, vg_name); unlock_vg(cmd, vg_name);
return ECMD_FAILED; return ECMD_FAILED;
} }
if (vg->status & EXPORTED_VG) { if (arg_count(cmd, removemissing_ARG)) {
log_error("Volume group \"%s\" is exported", vg->name); if (vg && consistent) {
unlock_vg(cmd, vg_name); log_error("Volume group \"%s\" is already consistent",
return ECMD_FAILED; vg_name);
} unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
if (!(vg->status & LVM_WRITE)) { init_partial(1);
log_error("Volume group \"%s\" is read-only", vg_name); consistent = 0;
unlock_vg(cmd, vg_name); if (!(vg = vg_read(cmd, vg_name, &consistent))) {
return ECMD_FAILED; log_error("Volume group \"%s\" not found", vg_name);
} unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
if (!archive(vg)) {
init_partial(0);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
if (!(vg->status & RESIZEABLE_VG)) { if (!_make_vg_consistent(cmd, vg)) {
log_error("Volume group \"%s\" is not reducable", vg_name); init_partial(0);
unlock_vg(cmd, vg_name); unlock_vg(cmd, vg_name);
return ECMD_FAILED; return ECMD_FAILED;
} }
/* FIXME: Pass private structure through to all these functions */ init_partial(0);
/* and update in batch here? */
ret = process_each_pv(cmd, argc, argv, vg, NULL, vgreduce_single); vg->status &= ~PARTIAL_VG;
vg->status |= LVM_WRITE;
if (!vg_write(vg)) {
log_error("Failed to write out a consistent VG for %s",
vg_name);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
backup(vg);
log_print("Wrote out consistent volume group %s", vg_name);
} else {
if (vg->status & EXPORTED_VG) {
log_error("Volume group \"%s\" is exported", vg->name);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
if (!(vg->status & LVM_WRITE)) {
log_error("Volume group \"%s\" is read-only", vg_name);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
if (!(vg->status & RESIZEABLE_VG)) {
log_error("Volume group \"%s\" is not reducible",
vg_name);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
/* FIXME: Pass private struct through to all these functions */
/* and update in batch here? */
ret = process_each_pv(cmd, argc, argv, vg, NULL,
_vgreduce_single);
}
unlock_vg(cmd, vg_name); unlock_vg(cmd, vg_name);

View File

@ -49,18 +49,6 @@ static int _move_pv(struct volume_group *vg_from, struct volume_group *vg_to,
return 1; return 1;
} }
static int _pv_is_in_vg(struct volume_group *vg, struct physical_volume *pv)
{
struct list *pvh;
list_iterate(pvh, &vg->pvs) {
if (pv == list_item(pvh, struct pv_list)->pv)
return 1;
}
return 0;
}
static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to) static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to)
{ {
struct list *lvh, *lvht, *segh; struct list *lvh, *lvht, *segh;
@ -81,7 +69,7 @@ static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to)
for (s = 0; s < seg->stripes; s++) { for (s = 0; s < seg->stripes; s++) {
pv = seg->area[s].pv; pv = seg->area[s].pv;
if (vg_with) { if (vg_with) {
if (!_pv_is_in_vg(vg_with, pv)) { if (!pv_is_in_vg(vg_with, pv)) {
log_error("Logical Volume %s " log_error("Logical Volume %s "
"split between " "split between "
"Volume Groups", "Volume Groups",
@ -91,11 +79,11 @@ static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to)
continue; continue;
} }
if (_pv_is_in_vg(vg_from, pv)) { if (pv_is_in_vg(vg_from, pv)) {
vg_with = vg_from; vg_with = vg_from;
continue; continue;
} }
if (_pv_is_in_vg(vg_to, pv)) { if (pv_is_in_vg(vg_to, pv)) {
vg_with = vg_to; vg_with = vg_to;
continue; continue;
} }