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

512 lines
12 KiB
C
Raw Normal View History

2002-05-31 23:30:51 +04:00
/*
2008-01-30 17:00:02 +03:00
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
2002-05-31 23:30:51 +04:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2.
2002-05-31 23:30:51 +04:00
*
2004-03-30 23:35:44 +04:00
* 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.
2002-05-31 23:30:51 +04:00
*
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2002-05-31 23:30:51 +04:00
*/
#include "tools.h"
2005-04-07 16:39:44 +04:00
/* FIXME Why not (lv->vg == vg) ? */
static int _lv_is_in_vg(struct volume_group *vg, struct logical_volume *lv)
{
struct lv_list *lvl;
dm_list_iterate_items(lvl, &vg->lvs)
2005-04-07 16:39:44 +04:00
if (lv == lvl->lv)
return 1;
return 0;
}
static int _move_one_lv(struct volume_group *vg_from,
2008-04-10 23:59:43 +04:00
struct volume_group *vg_to,
struct dm_list *lvh)
{
struct logical_volume *lv = dm_list_item(lvh, struct lv_list)->lv;
dm_list_move(&vg_to->lvs, lvh);
lv->vg = vg_to;
if (lv_is_active(lv)) {
log_error("Logical volume \"%s\" must be inactive", lv->name);
return 0;
}
return 1;
}
2005-04-07 16:39:44 +04:00
2002-05-31 23:30:51 +04:00
static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to)
{
struct dm_list *lvh, *lvht;
2002-05-31 23:30:51 +04:00
struct logical_volume *lv;
2002-11-18 17:04:08 +03:00
struct lv_segment *seg;
2002-05-31 23:30:51 +04:00
struct physical_volume *pv;
struct volume_group *vg_with;
unsigned s;
2002-05-31 23:30:51 +04:00
dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
lv = dm_list_item(lvh, struct lv_list)->lv;
2002-05-31 23:30:51 +04:00
2005-04-07 16:39:44 +04:00
if ((lv->status & SNAPSHOT))
continue;
if ((lv->status & MIRRORED))
continue;
2002-05-31 23:30:51 +04:00
/* Ensure all the PVs used by this LV remain in the same */
/* VG as each other */
vg_with = NULL;
dm_list_iterate_items(seg, &lv->segments) {
for (s = 0; s < seg->area_count; s++) {
/* FIXME Check AREA_LV too */
2005-06-01 20:51:55 +04:00
if (seg_type(seg, s) != AREA_PV)
continue;
2005-06-01 20:51:55 +04:00
pv = seg_pv(seg, s);
2002-05-31 23:30:51 +04:00
if (vg_with) {
if (!pv_is_in_vg(vg_with, pv)) {
log_error("Can't split Logical "
"Volume %s between "
"two Volume Groups",
2002-05-31 23:30:51 +04:00
lv->name);
return 0;
}
continue;
}
if (pv_is_in_vg(vg_from, pv)) {
2002-05-31 23:30:51 +04:00
vg_with = vg_from;
continue;
}
if (pv_is_in_vg(vg_to, pv)) {
2002-05-31 23:30:51 +04:00
vg_with = vg_to;
continue;
}
log_error("Physical Volume %s not found",
pv_dev_name(pv));
2002-05-31 23:30:51 +04:00
return 0;
}
2005-04-07 16:39:44 +04:00
}
2002-05-31 23:30:51 +04:00
if (vg_with == vg_from)
continue;
/* Move this LV */
if (!_move_one_lv(vg_from, vg_to, lvh))
2008-04-15 18:57:12 +04:00
return_0;
2002-05-31 23:30:51 +04:00
}
/* FIXME Ensure no LVs contain segs pointing at LVs in the other VG */
2002-05-31 23:30:51 +04:00
return 1;
}
/*
* Move the hidden / internal "snapshotN" LVs.from 'vg_from' to 'vg_to'.
*/
2005-04-07 16:39:44 +04:00
static int _move_snapshots(struct volume_group *vg_from,
struct volume_group *vg_to)
2002-05-31 23:30:51 +04:00
{
struct dm_list *lvh, *lvht;
2005-04-07 16:39:44 +04:00
struct logical_volume *lv;
struct lv_segment *seg;
int cow_from = 0;
int origin_from = 0;
2002-05-31 23:30:51 +04:00
dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
lv = dm_list_item(lvh, struct lv_list)->lv;
2002-05-31 23:30:51 +04:00
2005-04-07 16:39:44 +04:00
if (!(lv->status & SNAPSHOT))
continue;
2002-05-31 23:30:51 +04:00
dm_list_iterate_items(seg, &lv->segments) {
2005-04-07 16:39:44 +04:00
cow_from = _lv_is_in_vg(vg_from, seg->cow);
origin_from = _lv_is_in_vg(vg_from, seg->origin);
if (cow_from && origin_from)
continue;
if ((!cow_from && origin_from) ||
(cow_from && !origin_from)) {
log_error("Can't split snapshot %s between"
" two Volume Groups", seg->cow->name);
return 0;
}
2005-04-07 16:39:44 +04:00
/*
* At this point, the cow and origin should already be
* in vg_to.
*/
if (_lv_is_in_vg(vg_to, seg->cow) &&
_lv_is_in_vg(vg_to, seg->origin)) {
if (!_move_one_lv(vg_from, vg_to, lvh))
2008-04-15 18:57:12 +04:00
return_0;
}
}
2005-04-07 16:39:44 +04:00
2002-05-31 23:30:51 +04:00
}
return 1;
}
static int _move_mirrors(struct volume_group *vg_from,
struct volume_group *vg_to)
{
struct dm_list *lvh, *lvht;
struct logical_volume *lv;
struct lv_segment *seg, *log_seg;
unsigned s, seg_in, log_in;
dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
lv = dm_list_item(lvh, struct lv_list)->lv;
if (!(lv->status & MIRRORED))
continue;
2008-01-30 17:00:02 +03:00
seg = first_seg(lv);
seg_in = 0;
for (s = 0; s < seg->area_count; s++)
if (_lv_is_in_vg(vg_to, seg_lv(seg, s)))
seg_in++;
log_in = !seg->log_lv;
if (seg->log_lv) {
log_seg = first_seg(seg->log_lv);
if (seg_is_mirrored(log_seg)) {
log_in = 1;
/* Ensure each log dev is in vg_to */
for (s = 0; s < log_seg->area_count; s++)
log_in = log_in &&
_lv_is_in_vg(vg_to,
seg_lv(log_seg, s));
} else
log_in = _lv_is_in_vg(vg_to, seg->log_lv);
}
2008-01-30 17:00:02 +03:00
if ((seg_in && seg_in < seg->area_count) ||
(seg_in && seg->log_lv && !log_in) ||
(!seg_in && seg->log_lv && log_in)) {
log_error("Can't split mirror %s between "
"two Volume Groups", lv->name);
return 0;
}
if (seg_in == seg->area_count && log_in) {
if (!_move_one_lv(vg_from, vg_to, lvh))
2008-04-15 18:57:12 +04:00
return_0;
}
}
return 1;
}
/*
* Create or open the destination of the vgsplit operation.
* Returns
* - non-NULL: VG handle w/VG lock held
* - NULL: no VG lock held
*/
static struct volume_group *_vgsplit_to(struct cmd_context *cmd,
const char *vg_name_to,
int *existing_vg)
{
struct volume_group *vg_to = NULL;
log_verbose("Checking for new volume group \"%s\"", vg_name_to);
/*
* First try to create a new VG. If we cannot create it,
* and we get FAILED_EXIST (we will not be holding a lock),
* a VG must already exist with this name. We then try to
* read the existing VG - the vgsplit will be into an existing VG.
*
* Otherwise, if the lock was successful, it must be the case that
* we obtained a WRITE lock and could not find the vgname in the
* system. Thus, the split will be into a new VG.
*/
vg_to = vg_create(cmd, vg_name_to);
if (vg_read_error(vg_to) == FAILED_LOCKING) {
log_error("Can't get lock for %s", vg_name_to);
release_vg(vg_to);
return NULL;
}
if (vg_read_error(vg_to) == FAILED_EXIST) {
*existing_vg = 1;
release_vg(vg_to);
vg_to = vg_read_for_update(cmd, vg_name_to, NULL, 0);
if (vg_read_error(vg_to)) {
release_vg(vg_to);
stack;
return NULL;
}
} else if (vg_read_error(vg_to) == SUCCESS) {
*existing_vg = 0;
}
return vg_to;
}
/*
* Open the source of the vgsplit operation.
* Returns
* - non-NULL: VG handle w/VG lock held
* - NULL: no VG lock held
*/
static struct volume_group *_vgsplit_from(struct cmd_context *cmd,
const char *vg_name_from)
{
struct volume_group *vg_from;
log_verbose("Checking for volume group \"%s\"", vg_name_from);
vg_from = vg_read_for_update(cmd, vg_name_from, NULL, 0);
if (vg_read_error(vg_from)) {
release_vg(vg_from);
return NULL;
}
return vg_from;
}
/*
* Has the user given an option related to a new vg as the split destination?
*/
static int new_vg_option_specified(struct cmd_context *cmd)
{
return(arg_count(cmd, clustered_ARG) ||
arg_count(cmd, alloc_ARG) ||
arg_count(cmd, maxphysicalvolumes_ARG) ||
arg_count(cmd, maxlogicalvolumes_ARG) ||
arg_count(cmd, vgmetadatacopies_ARG));
}
2002-05-31 23:30:51 +04:00
int vgsplit(struct cmd_context *cmd, int argc, char **argv)
{
struct vgcreate_params vp_new;
struct vgcreate_params vp_def;
const char *vg_name_from, *vg_name_to;
struct volume_group *vg_to = NULL, *vg_from = NULL;
2002-05-31 23:30:51 +04:00
int opt;
2009-06-10 15:21:10 +04:00
int existing_vg = 0;
int r = ECMD_FAILED;
const char *lv_name;
int lock_vg_from_first = 1;
2002-05-31 23:30:51 +04:00
if ((arg_count(cmd, name_ARG) + argc) < 3) {
log_error("Existing VG, new VG and either physical volumes "
"or logical volume required.");
2002-05-31 23:30:51 +04:00
return EINVALID_CMD_LINE;
}
if (arg_count(cmd, name_ARG) && (argc > 2)) {
log_error("A logical volume name cannot be given with "
"physical volumes.");
return ECMD_FAILED;
}
if (arg_count(cmd, name_ARG))
lv_name = arg_value(cmd, name_ARG);
else
lv_name = NULL;
vg_name_from = skip_dev_dir(cmd, argv[0], NULL);
vg_name_to = skip_dev_dir(cmd, argv[1], NULL);
2002-05-31 23:30:51 +04:00
argc -= 2;
argv += 2;
if (!strcmp(vg_name_to, vg_name_from)) {
log_error("Duplicate volume group name \"%s\"", vg_name_from);
return ECMD_FAILED;
}
if (strcmp(vg_name_to, vg_name_from) < 0)
lock_vg_from_first = 0;
if (lock_vg_from_first) {
vg_from = _vgsplit_from(cmd, vg_name_from);
if (!vg_from) {
stack;
return ECMD_FAILED;
}
/*
* Set metadata format of original VG.
* NOTE: We must set the format before calling vg_create()
* since vg_create() calls the per-format constructor.
*/
cmd->fmt = vg_from->fid->fmt;
vg_to = _vgsplit_to(cmd, vg_name_to, &existing_vg);
if (!vg_to) {
unlock_and_release_vg(cmd, vg_from, vg_name_from);
stack;
return ECMD_FAILED;
}
} else {
vg_to = _vgsplit_to(cmd, vg_name_to, &existing_vg);
if (!vg_to) {
stack;
return ECMD_FAILED;
}
vg_from = _vgsplit_from(cmd, vg_name_from);
if (!vg_from) {
unlock_and_release_vg(cmd, vg_to, vg_name_to);
stack;
return ECMD_FAILED;
}
Change vg_create() to take only minimal parameters and obtain a lock. vg_t *vg_create(struct cmd_context *cmd, const char *vg_name); This is the first step towards the API called to create a VG. Call vg_lock_newname() inside this function. Use _vg_make_handle() where possible. Now we have 2 ways to construct a volume group: 1) vg_read: Used when constructing an existing VG from disks 2) vg_create: Used when constructing a new VG Both of these interfaces obtain a lock, and return a vg_t *. The usage of _vg_make_handle() inside vg_create() doesn't fit perfectly but it's ok for now. Needs some cleanup though and I've noted "FIXME" in the code. Add the new vg_create() plus vg 'set' functions for non-default VG parameters in the following tools: - vgcreate: Fairly straightforward refactoring. We just moved vg_lock_newname inside vg_create so we check the return via vg_read_error. - vgsplit: The refactoring here is a bit more tricky. Originally we called vg_lock_newname and depending on the error code, we either read the existing vg or created the new one. Now vg_create() calls vg_lock_newname, so we first try to create the VG. If this fails with FAILED_EXIST, we can then do the vg_read. If the create succeeds, we check the input parameters and set any new values on the VG. TODO in future patches: 1. The VG_ORPHAN lock needs some thought. We may want to treat this as any other VG, and require the application to obtain a handle and pass it to other API calls (for example, vg_extend). Or, we may find that hiding the VG_ORPHAN lock inside other APIs is the way to go. I thought of placing the VG_ORPHAN lock inside vg_create() and tying it to the vg handle, but was not certain this was the right approach. 2. Cleanup error paths. Integrate vg_read_error() with vg_create and vg_read* error codes and/or the new error APIs. Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2009-07-09 14:09:33 +04:00
if (cmd->fmt != vg_from->fid->fmt) {
/* In this case we don't know the vg_from->fid->fmt */
log_error("Unable to set new VG metadata type based on "
"source VG format - use -M option.");
goto bad;
}
}
if (existing_vg) {
if (new_vg_option_specified(cmd)) {
log_error("Volume group \"%s\" exists, but new VG "
"option specified", vg_name_to);
goto bad;
}
if (!vgs_are_compatible(cmd, vg_from,vg_to))
2008-04-10 23:59:43 +04:00
goto_bad;
} else {
vgcreate_params_set_defaults(&vp_def, vg_from);
vp_def.vg_name = vg_name_to;
if (!vgcreate_params_set_from_args(cmd, &vp_new, &vp_def)) {
r = EINVALID_CMD_LINE;
goto_bad;
}
if (!vgcreate_params_validate(cmd, &vp_new)) {
r = EINVALID_CMD_LINE;
goto_bad;
}
Change vg_create() to take only minimal parameters and obtain a lock. vg_t *vg_create(struct cmd_context *cmd, const char *vg_name); This is the first step towards the API called to create a VG. Call vg_lock_newname() inside this function. Use _vg_make_handle() where possible. Now we have 2 ways to construct a volume group: 1) vg_read: Used when constructing an existing VG from disks 2) vg_create: Used when constructing a new VG Both of these interfaces obtain a lock, and return a vg_t *. The usage of _vg_make_handle() inside vg_create() doesn't fit perfectly but it's ok for now. Needs some cleanup though and I've noted "FIXME" in the code. Add the new vg_create() plus vg 'set' functions for non-default VG parameters in the following tools: - vgcreate: Fairly straightforward refactoring. We just moved vg_lock_newname inside vg_create so we check the return via vg_read_error. - vgsplit: The refactoring here is a bit more tricky. Originally we called vg_lock_newname and depending on the error code, we either read the existing vg or created the new one. Now vg_create() calls vg_lock_newname, so we first try to create the VG. If this fails with FAILED_EXIST, we can then do the vg_read. If the create succeeds, we check the input parameters and set any new values on the VG. TODO in future patches: 1. The VG_ORPHAN lock needs some thought. We may want to treat this as any other VG, and require the application to obtain a handle and pass it to other API calls (for example, vg_extend). Or, we may find that hiding the VG_ORPHAN lock inside other APIs is the way to go. I thought of placing the VG_ORPHAN lock inside vg_create() and tying it to the vg handle, but was not certain this was the right approach. 2. Cleanup error paths. Integrate vg_read_error() with vg_create and vg_read* error codes and/or the new error APIs. Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2009-07-09 14:09:33 +04:00
if (!vg_set_extent_size(vg_to, vp_new.extent_size) ||
!vg_set_max_lv(vg_to, vp_new.max_lv) ||
!vg_set_max_pv(vg_to, vp_new.max_pv) ||
!vg_set_alloc_policy(vg_to, vp_new.alloc) ||
!vg_set_clustered(vg_to, vp_new.clustered) ||
!vg_set_mda_copies(vg_to, vp_new.vgmetadatacopies))
2008-04-10 23:59:43 +04:00
goto_bad;
}
2002-05-31 23:30:51 +04:00
/* Archive vg_from before changing it */
if (!archive(vg_from))
2008-04-10 23:59:43 +04:00
goto_bad;
2002-05-31 23:30:51 +04:00
/* Move PVs across to new structure */
for (opt = 0; opt < argc; opt++) {
dm_unescape_colons_and_at_signs(argv[opt], NULL, NULL);
if (!move_pv(vg_from, vg_to, argv[opt]))
2008-04-10 23:59:43 +04:00
goto_bad;
}
/* If an LV given on the cmdline, move used_by PVs */
if (lv_name && !move_pvs_used_by_lv(vg_from, vg_to, lv_name))
2008-04-10 23:59:43 +04:00
goto_bad;
2002-05-31 23:30:51 +04:00
/* Move required LVs across, checking consistency */
if (!(_move_lvs(vg_from, vg_to)))
2008-04-10 23:59:43 +04:00
goto_bad;
2002-05-31 23:30:51 +04:00
2009-10-26 13:01:56 +03:00
/* FIXME Separate the 'move' from the 'validation' to fix dev stacks */
/* Move required mirrors across */
if (!(_move_mirrors(vg_from, vg_to)))
2008-04-10 23:59:43 +04:00
goto_bad;
2009-10-26 13:01:56 +03:00
/* Move required snapshots across */
if (!(_move_snapshots(vg_from, vg_to)))
goto_bad;
/* Split metadata areas and check if both vgs have at least one area */
if (!(vg_split_mdas(cmd, vg_from, vg_to)) && vg_from->pv_count) {
log_error("Cannot split: Nowhere to store metadata for new Volume Group");
goto bad;
}
/* Set proper name for all PVs in new VG */
if (!vg_rename(cmd, vg_to, vg_name_to))
2008-04-10 23:59:43 +04:00
goto_bad;
2002-11-18 17:04:08 +03:00
2002-05-31 23:30:51 +04:00
/* store it on disks */
log_verbose("Writing out updated volume groups");
/*
* First, write out the new VG as EXPORTED. We do this first in case
* there is a crash - we will still have the new VG information, in an
* exported state. Recovery after this point would be removal of the
* new VG and redoing the vgsplit.
* FIXME: recover automatically or instruct the user?
*/
2002-05-31 23:30:51 +04:00
vg_to->status |= EXPORTED_VG;
if (!archive(vg_to))
2008-04-10 23:59:43 +04:00
goto_bad;
2002-05-31 23:30:51 +04:00
if (!vg_write(vg_to) || !vg_commit(vg_to))
2008-04-10 23:59:43 +04:00
goto_bad;
2002-05-31 23:30:51 +04:00
backup(vg_to);
/*
* Next, write out the updated old VG. If we crash after this point,
* recovery is a vgimport on the new VG.
2008-04-10 23:59:43 +04:00
* FIXME: recover automatically or instruct the user?
*/
if (vg_from->pv_count) {
if (!vg_write(vg_from) || !vg_commit(vg_from))
2008-04-10 23:59:43 +04:00
goto_bad;
2002-05-31 23:30:51 +04:00
backup(vg_from);
}
2002-05-31 23:30:51 +04:00
/*
* Finally, remove the EXPORTED flag from the new VG and write it out.
*/
if (!test_mode()) {
release_vg(vg_to);
vg_to = vg_read_for_update(cmd, vg_name_to, NULL,
READ_ALLOW_EXPORTED);
if (vg_read_error(vg_to)) {
log_error("Volume group \"%s\" became inconsistent: "
"please fix manually", vg_name_to);
goto bad;
}
2002-11-18 17:04:08 +03:00
}
2002-05-31 23:30:51 +04:00
vg_to->status &= ~EXPORTED_VG;
2003-09-15 19:03:54 +04:00
if (!vg_write(vg_to) || !vg_commit(vg_to))
2008-04-10 23:59:43 +04:00
goto_bad;
2002-05-31 23:30:51 +04:00
backup(vg_to);
log_print_unless_silent("%s volume group \"%s\" successfully split from \"%s\"",
existing_vg ? "Existing" : "New",
vg_to->name, vg_from->name);
2002-05-31 23:30:51 +04:00
r = ECMD_PROCESSED;
bad:
/*
2011-04-29 04:21:13 +04:00
* vg_to references elements moved from vg_from
* so vg_to has to be freed first.
*/
unlock_and_release_vg(cmd, vg_to, vg_name_to);
unlock_and_release_vg(cmd, vg_from, vg_name_from);
return r;
2002-05-31 23:30:51 +04:00
}