mirror of
git://sourceware.org/git/lvm2.git
synced 2026-01-06 08:32:48 +03:00
Compare commits
81 Commits
dm_v1_02_2
...
v2_02_30
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9397833ceb | ||
|
|
e9433e83cd | ||
|
|
f3c58100a0 | ||
|
|
8900231d99 | ||
|
|
c7a63b8a2b | ||
|
|
90e90672a4 | ||
|
|
fa51e5c762 | ||
|
|
911f55d005 | ||
|
|
d30eb4e570 | ||
|
|
67bcfb6947 | ||
|
|
3915a61b1e | ||
|
|
c170d321e8 | ||
|
|
2802c476ee | ||
|
|
1050cebf7f | ||
|
|
dad73465fc | ||
|
|
8e2ac98fe2 | ||
|
|
9aaf0c36d5 | ||
|
|
1cb07e9cfd | ||
|
|
f1ccdf25b1 | ||
|
|
6dca497b27 | ||
|
|
42a83262a1 | ||
|
|
63ee9cbee6 | ||
|
|
3862f8ca7c | ||
|
|
4ada7cffd0 | ||
|
|
a664ce4298 | ||
|
|
8d7b6c6905 | ||
|
|
16d22d404a | ||
|
|
ccb24d5779 | ||
|
|
8795b45cb4 | ||
|
|
628d3bff45 | ||
|
|
0336bc9de9 | ||
|
|
033cb21797 | ||
|
|
09b98a45df | ||
|
|
38857ba29e | ||
|
|
1c7520ec8f | ||
|
|
362b9769b2 | ||
|
|
b2d68bd3d2 | ||
|
|
0dc7e635d4 | ||
|
|
80e070a857 | ||
|
|
cc203245e4 | ||
|
|
2f9a65fc93 | ||
|
|
62738e8001 | ||
|
|
5ecacf0c7f | ||
|
|
06b103c8d4 | ||
|
|
d473b7bca8 | ||
|
|
50a1e81ba7 | ||
|
|
d9885b1b64 | ||
|
|
0a9c8cada2 | ||
|
|
60f55f8461 | ||
|
|
7bedaea38f | ||
|
|
9e4b87e798 | ||
|
|
95bf59095c | ||
|
|
e0f34a9720 | ||
|
|
f3797c2a8e | ||
|
|
30cbcccc80 | ||
|
|
f49c0d696f | ||
|
|
71f564ee5b | ||
|
|
4b0950aba5 | ||
|
|
8c3af822ec | ||
|
|
878a207d19 | ||
|
|
0537ad860a | ||
|
|
2cdbbb1aea | ||
|
|
7af977d36b | ||
|
|
9afff4cf30 | ||
|
|
48ba9734aa | ||
|
|
897fc59f72 | ||
|
|
947e44ae67 | ||
|
|
e44843beba | ||
|
|
147482ea69 | ||
|
|
09091c5cf8 | ||
|
|
50827a5f69 | ||
|
|
2d6444c924 | ||
|
|
1d2675d9aa | ||
|
|
ad98990a8e | ||
|
|
8e58c143f2 | ||
|
|
556a4a2395 | ||
|
|
5be987b40f | ||
|
|
066bc35e69 | ||
|
|
403779437c | ||
|
|
fb806f61d4 | ||
|
|
6ce306661c |
39
WHATS_NEW
39
WHATS_NEW
@@ -1,5 +1,32 @@
|
||||
Version 2.02.29 -
|
||||
==================================
|
||||
Version 2.02.30 - 17th January 2008
|
||||
===================================
|
||||
Set default readahead to twice maximium stripe size.
|
||||
Reinstate VG extent size and stripe size defaults (halved). (2.02.29)
|
||||
Add lists of stacked LV segments using each LV to the internal metadata.
|
||||
Change vgsplit -l (for unimplemented --list) into --maxlogicalvolumes.
|
||||
Fix process_all_pvs to detect non-orphans with no MDAs correctly.
|
||||
Don't use block_on_error with mirror targets version 1.12 and above.
|
||||
Update vgsplit to accept vgcreate options when new VG is destination.
|
||||
Update vgsplit to accept existing VG as destination.
|
||||
lvconvert waits for completion of initial sync by default.
|
||||
Refactor vgcreate for parameter validation and add tests.
|
||||
Add new convert_lv field to lvs output.
|
||||
Print warning when lvm tools are running as non-root.
|
||||
Add snapshot dmeventd library (enables dmeventd snapshot monitoring).
|
||||
Prevent pvcreate from overwriting MDA-less PVs belonging to active VGs.
|
||||
Fix a segfault if using pvs with --all argument. (2.02.29)
|
||||
Update --uuid argument description in man pages.
|
||||
Fix vgreduce PV list processing not to process every PV in the VG. (2.02.29)
|
||||
Extend lvconvert to use polldaemon.
|
||||
Add support for stacked mirrors.
|
||||
Major restructuring of pvmove and lvconvert layer manipulation code.
|
||||
Replace tools/fsadm with scripts/fsadm.sh.
|
||||
Append fields to report/pvsegs_cols_verbose.
|
||||
Permit LV segment fields with PV segment reports.
|
||||
Add seg_start_pe and seg_pe_ranges to reports.
|
||||
|
||||
Version 2.02.29 - 5th December 2007
|
||||
===================================
|
||||
Make clvmd backup vg metadata on remote nodes.
|
||||
Refactor pvmove allocation code.
|
||||
Decode cluster locking state in log message.
|
||||
@@ -36,14 +63,16 @@ Version 2.02.29 -
|
||||
Add const attributes to pv accessor functions.
|
||||
Refactor vg_add_snapshot() and lv_create_empty().
|
||||
Handle new sysfs subsystem/block/devices directory structure.
|
||||
Tests are run with LVM_SYSTEM_DIR pointing to private root and /dev dirs.
|
||||
Run test with LVM_SYSTEM_DIR pointing to private root and /dev dirs.
|
||||
Fix a bug in lvm_dump.sh checks for lvm/dmsetup binaries.
|
||||
Fix underquotations in lvm_dump.sh.
|
||||
Refactor lvcreate stripe and mirror parameter validation.
|
||||
All tools: print --help output to stdout, not stderr.
|
||||
After a diagnostic, suggest --help, rather than printing all --help output.
|
||||
Print --help output to stdout, not stderr.
|
||||
After a cmdline processing error, don't print help text but suggest --help.
|
||||
Add %PVS extents option to lvresize, lvextend, and lvcreate.
|
||||
Add 'make check' to run tests in new subdirectory 'test'.
|
||||
Moved the obsolete test subdirectory to old-tests.
|
||||
Cope with relative paths in configure --with-dmdir.
|
||||
Remove no-longer-correct restrictions on PV arg count with stripes/mirrors.
|
||||
Fix strdup memory leak in str_list_dup().
|
||||
Link with -lpthread when static SELinux libraries require that.
|
||||
|
||||
14
WHATS_NEW_DM
14
WHATS_NEW_DM
@@ -1,10 +1,22 @@
|
||||
Version 1.02.25 -
|
||||
====================================
|
||||
Change cluster mirror log type name (s/clustered_/clustered-/)
|
||||
|
||||
Version 1.02.24 - 20th December 2007
|
||||
====================================
|
||||
Fix deptree to pass new name to _resume_node after a rename.
|
||||
Suppress other node operations if node is deleted.
|
||||
Add node operation stack debug messages.
|
||||
Report error when empty device name passed to readahead functions.
|
||||
Fix minimum readahead debug message.
|
||||
|
||||
Version 1.02.23 - 5th December 2007
|
||||
===================================
|
||||
Update dm-ioctl.h after removal of compat code.
|
||||
Add readahead support to libdevmapper and dmsetup.
|
||||
Fix double free in a libdevmapper-event error path.
|
||||
Fix configure --with-dmeventd-path substitution.
|
||||
Allow a DM_DEV_DIR environment variable to override /dev.
|
||||
Allow a DM_DEV_DIR environment variable to override /dev in dmsetup.
|
||||
Create a libdevmapper.so.$LIB_VERSION symlink within the build tree.
|
||||
Avoid static link failure with some SELinux libraries that require libpthread.
|
||||
Remove obsolete dmfs code from tree and update INSTALL.
|
||||
|
||||
11
configure.in
11
configure.in
@@ -1,6 +1,6 @@
|
||||
##
|
||||
## Copyright (C) 2000-2004 Sistina Software, Inc. All rights reserved.
|
||||
## Copyright (C) 2004, 2007 Red Hat, Inc. All rights reserved.
|
||||
## Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
|
||||
##
|
||||
## This file is part of LVM2.
|
||||
##
|
||||
@@ -363,7 +363,7 @@ AC_MSG_RESULT($CMDLIB)
|
||||
|
||||
################################################################################
|
||||
dnl -- Enable fsadm
|
||||
AC_MSG_CHECKING(whether to build fsadm)
|
||||
AC_MSG_CHECKING(whether to install fsadm)
|
||||
AC_ARG_ENABLE(fsadm, [ --enable-fsadm Enable fsadm],
|
||||
FSADM=$enableval)
|
||||
AC_MSG_RESULT($FSADM)
|
||||
@@ -558,11 +558,6 @@ if test x$CLVMD != xnone; then
|
||||
AC_FUNC_SELECT_ARGTYPES
|
||||
fi
|
||||
|
||||
if test x$FSADM = xyes; then
|
||||
AC_CHECK_HEADERS(fstab.h sys/mount.h sys/vfs.h,,AC_MSG_ERROR(bailing out))
|
||||
AC_CHECK_FUNCS(memmove,,AC_MSG_ERROR(bailing out))
|
||||
fi
|
||||
|
||||
if test x$CLUSTER != xnone; then
|
||||
AC_CHECK_HEADERS(sys/socket.h sys/un.h,,AC_MSG_ERROR(bailing out))
|
||||
AC_CHECK_FUNCS(socket,,AC_MSG_ERROR(bailing out))
|
||||
@@ -647,6 +642,7 @@ daemons/Makefile
|
||||
daemons/clvmd/Makefile
|
||||
dmeventd/Makefile
|
||||
dmeventd/mirror/Makefile
|
||||
dmeventd/snapshot/Makefile
|
||||
doc/Makefile
|
||||
include/Makefile
|
||||
lib/Makefile
|
||||
@@ -661,7 +657,6 @@ po/Makefile
|
||||
scripts/Makefile
|
||||
tools/Makefile
|
||||
tools/version.h
|
||||
tools/fsadm/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SUBDIRS += mirror
|
||||
SUBDIRS += mirror snapshot
|
||||
|
||||
include $(top_srcdir)/make.tmpl
|
||||
|
||||
|
||||
3
daemons/dmeventd/plugins/snapshot/.exported_symbols
Normal file
3
daemons/dmeventd/plugins/snapshot/.exported_symbols
Normal file
@@ -0,0 +1,3 @@
|
||||
process_event
|
||||
register_device
|
||||
unregister_device
|
||||
@@ -1,8 +1,8 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
# This file is part of the LVM2.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
@@ -16,16 +16,21 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SOURCES = fsadm.c
|
||||
INCLUDES += -I${top_srcdir}/tools
|
||||
CLDFLAGS += -L${top_srcdir}/tools -ldevmapper -llvm2cmd
|
||||
|
||||
TARGETS = fsadm
|
||||
SOURCES = dmeventd_snapshot.c
|
||||
|
||||
ifeq ("@LIB_SUFFIX@","dylib")
|
||||
LIB_SHARED = libdevmapper-event-lvm2snapshot.dylib
|
||||
else
|
||||
LIB_SHARED = libdevmapper-event-lvm2snapshot.so
|
||||
endif
|
||||
|
||||
include $(top_srcdir)/make.tmpl
|
||||
|
||||
fsadm: $(OBJECTS)
|
||||
$(CC) -o $@ $(CFLAGS) $(OBJECTS) -rdynamic
|
||||
|
||||
install: fsadm
|
||||
$(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) fsadm \
|
||||
$(sbindir)/fsadm
|
||||
install: libdevmapper-event-lvm2snapshot.$(LIB_SUFFIX)
|
||||
$(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) $< \
|
||||
$(libdir)/$<.$(LIB_VERSION)
|
||||
$(LN_S) -f $<.$(LIB_VERSION) $(libdir)/$<
|
||||
|
||||
223
daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
Normal file
223
daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "libdevmapper.h"
|
||||
#include "libdevmapper-event.h"
|
||||
#include "lvm2cmd.h"
|
||||
#include "lvm-string.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <syslog.h> /* FIXME Replace syslog with multilog */
|
||||
/* FIXME Missing openlog? */
|
||||
|
||||
/* First warning when snapshot is 80% full. */
|
||||
#define WARNING_THRESH 80
|
||||
/* Further warnings at 85%, 90% and 95% fullness. */
|
||||
#define WARNING_STEP 5
|
||||
|
||||
static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* Number of active registrations.
|
||||
*/
|
||||
static int _register_count = 0;
|
||||
|
||||
static struct dm_pool *_mem_pool = NULL;
|
||||
static void *_lvm_handle = NULL;
|
||||
|
||||
struct snap_status {
|
||||
int invalid;
|
||||
int used;
|
||||
int max;
|
||||
};
|
||||
|
||||
/*
|
||||
* Currently only one event can be processed at a time.
|
||||
*/
|
||||
static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void _temporary_log_fn(int level, const char *file,
|
||||
int line, const char *format)
|
||||
{
|
||||
if (!strncmp(format, "WARNING: ", 9) && (level < 5))
|
||||
syslog(LOG_CRIT, "%s", format);
|
||||
else
|
||||
syslog(LOG_DEBUG, "%s", format);
|
||||
}
|
||||
|
||||
/* FIXME possibly reconcile this with target_percent when we gain
|
||||
access to regular LVM library here. */
|
||||
static void _parse_snapshot_params(char *params, struct snap_status *stat)
|
||||
{
|
||||
char *p;
|
||||
/*
|
||||
* xx/xx -- fractions used/max
|
||||
* Invalid -- snapshot invalidated
|
||||
* Unknown -- status unknown
|
||||
*/
|
||||
stat->used = stat->max = 0;
|
||||
|
||||
if (!strncmp(params, "Invalid", 7)) {
|
||||
stat->invalid = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we return without setting non-zero max, the parent is
|
||||
* responsible for reporting errors.
|
||||
*/
|
||||
if (!strncmp(params, "Unknown", 7))
|
||||
return;
|
||||
|
||||
if (!(p = strstr(params, "/")))
|
||||
return;
|
||||
|
||||
*p = '\0';
|
||||
p++;
|
||||
|
||||
stat->used = atoi(params);
|
||||
stat->max = atoi(p);
|
||||
}
|
||||
|
||||
/* send unregister command to itself */
|
||||
static void _unregister_self(struct dm_task *dmt)
|
||||
{
|
||||
const char *name = dm_task_get_name(dmt);
|
||||
struct dm_event_handler *dmevh;
|
||||
|
||||
if (!(dmevh = dm_event_handler_create()))
|
||||
return;
|
||||
|
||||
if (dm_event_handler_set_dev_name(dmevh, name))
|
||||
goto fail;
|
||||
|
||||
dm_event_handler_set_event_mask(dmevh, DM_EVENT_ALL_ERRORS|DM_EVENT_TIMEOUT);
|
||||
dm_event_unregister_handler(dmevh);
|
||||
fail:
|
||||
dm_event_handler_destroy(dmevh);
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt, enum dm_event_mask event,
|
||||
void **private)
|
||||
{
|
||||
void *next = NULL;
|
||||
uint64_t start, length;
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
struct snap_status stat = { 0 };
|
||||
const char *device = dm_task_get_name(dmt);
|
||||
int percent, *percent_warning = (int*)private;
|
||||
|
||||
/* No longer monitoring, waiting for remove */
|
||||
if (!*percent_warning)
|
||||
return;
|
||||
|
||||
if (pthread_mutex_trylock(&_event_mutex)) {
|
||||
syslog(LOG_NOTICE, "Another thread is handling an event. Waiting...");
|
||||
pthread_mutex_lock(&_event_mutex);
|
||||
}
|
||||
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
if (!target_type)
|
||||
goto out;
|
||||
|
||||
_parse_snapshot_params(params, &stat);
|
||||
/*
|
||||
* If the snapshot has been invalidated or we failed to parse
|
||||
* the status string. Report the full status string to syslog.
|
||||
*/
|
||||
if (stat.invalid || !stat.max) {
|
||||
syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params);
|
||||
_unregister_self(dmt);
|
||||
*percent_warning = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
percent = 100 * stat.used / stat.max;
|
||||
if (percent >= *percent_warning) {
|
||||
syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent);
|
||||
/* Print warning on the next multiple of WARNING_STEP. */
|
||||
*percent_warning = (percent / WARNING_STEP) * WARNING_STEP + WARNING_STEP;
|
||||
}
|
||||
out:
|
||||
pthread_mutex_unlock(&_event_mutex);
|
||||
}
|
||||
|
||||
int register_device(const char *device, const char *uuid, int major, int minor,
|
||||
void **private)
|
||||
{
|
||||
int r = 0;
|
||||
int *percent_warning = (int*)private;
|
||||
|
||||
pthread_mutex_lock(&_register_mutex);
|
||||
|
||||
/*
|
||||
* Need some space for allocations. 1024 should be more
|
||||
* than enough for what we need (device mapper name splitting)
|
||||
*/
|
||||
if (!_mem_pool && !(_mem_pool = dm_pool_create("snapshot_dso", 1024)))
|
||||
goto out;
|
||||
|
||||
*percent_warning = WARNING_THRESH; /* Print warning if snapshot is full */
|
||||
|
||||
if (!_lvm_handle) {
|
||||
lvm2_log_fn(_temporary_log_fn);
|
||||
if (!(_lvm_handle = lvm2_init())) {
|
||||
dm_pool_destroy(_mem_pool);
|
||||
_mem_pool = NULL;
|
||||
goto out;
|
||||
}
|
||||
lvm2_log_level(_lvm_handle, LVM2_LOG_SUPPRESS);
|
||||
/* FIXME Temporary: move to dmeventd core */
|
||||
lvm2_run(_lvm_handle, "_memlock_inc");
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "Monitoring snapshot %s\n", device);
|
||||
|
||||
_register_count++;
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&_register_mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int unregister_device(const char *device, const char *uuid, int major, int minor,
|
||||
void **unused __attribute((unused)))
|
||||
{
|
||||
pthread_mutex_lock(&_register_mutex);
|
||||
|
||||
syslog(LOG_INFO, "No longer monitoring snapshot %s\n",
|
||||
device);
|
||||
|
||||
if (!--_register_count) {
|
||||
dm_pool_destroy(_mem_pool);
|
||||
_mem_pool = NULL;
|
||||
lvm2_run(_lvm_handle, "_memlock_dec");
|
||||
lvm2_exit(_lvm_handle);
|
||||
_lvm_handle = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_register_mutex);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SUBDIRS += mirror
|
||||
SUBDIRS += mirror snapshot
|
||||
|
||||
include $(top_srcdir)/make.tmpl
|
||||
|
||||
|
||||
3
dmeventd/snapshot/.exported_symbols
Normal file
3
dmeventd/snapshot/.exported_symbols
Normal file
@@ -0,0 +1,3 @@
|
||||
process_event
|
||||
register_device
|
||||
unregister_device
|
||||
36
dmeventd/snapshot/Makefile.in
Normal file
36
dmeventd/snapshot/Makefile.in
Normal file
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of the 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
INCLUDES += -I${top_srcdir}/tools
|
||||
CLDFLAGS += -L${top_srcdir}/tools -ldevmapper -llvm2cmd
|
||||
|
||||
SOURCES = dmeventd_snapshot.c
|
||||
|
||||
ifeq ("@LIB_SUFFIX@","dylib")
|
||||
LIB_SHARED = libdevmapper-event-lvm2snapshot.dylib
|
||||
else
|
||||
LIB_SHARED = libdevmapper-event-lvm2snapshot.so
|
||||
endif
|
||||
|
||||
include $(top_srcdir)/make.tmpl
|
||||
|
||||
install: libdevmapper-event-lvm2snapshot.$(LIB_SUFFIX)
|
||||
$(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) $< \
|
||||
$(libdir)/$<.$(LIB_VERSION)
|
||||
$(LN_S) -f $<.$(LIB_VERSION) $(libdir)/$<
|
||||
|
||||
223
dmeventd/snapshot/dmeventd_snapshot.c
Normal file
223
dmeventd/snapshot/dmeventd_snapshot.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "libdevmapper.h"
|
||||
#include "libdevmapper-event.h"
|
||||
#include "lvm2cmd.h"
|
||||
#include "lvm-string.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <syslog.h> /* FIXME Replace syslog with multilog */
|
||||
/* FIXME Missing openlog? */
|
||||
|
||||
/* First warning when snapshot is 80% full. */
|
||||
#define WARNING_THRESH 80
|
||||
/* Further warnings at 85%, 90% and 95% fullness. */
|
||||
#define WARNING_STEP 5
|
||||
|
||||
static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* Number of active registrations.
|
||||
*/
|
||||
static int _register_count = 0;
|
||||
|
||||
static struct dm_pool *_mem_pool = NULL;
|
||||
static void *_lvm_handle = NULL;
|
||||
|
||||
struct snap_status {
|
||||
int invalid;
|
||||
int used;
|
||||
int max;
|
||||
};
|
||||
|
||||
/*
|
||||
* Currently only one event can be processed at a time.
|
||||
*/
|
||||
static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void _temporary_log_fn(int level, const char *file,
|
||||
int line, const char *format)
|
||||
{
|
||||
if (!strncmp(format, "WARNING: ", 9) && (level < 5))
|
||||
syslog(LOG_CRIT, "%s", format);
|
||||
else
|
||||
syslog(LOG_DEBUG, "%s", format);
|
||||
}
|
||||
|
||||
/* FIXME possibly reconcile this with target_percent when we gain
|
||||
access to regular LVM library here. */
|
||||
static void _parse_snapshot_params(char *params, struct snap_status *stat)
|
||||
{
|
||||
char *p;
|
||||
/*
|
||||
* xx/xx -- fractions used/max
|
||||
* Invalid -- snapshot invalidated
|
||||
* Unknown -- status unknown
|
||||
*/
|
||||
stat->used = stat->max = 0;
|
||||
|
||||
if (!strncmp(params, "Invalid", 7)) {
|
||||
stat->invalid = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we return without setting non-zero max, the parent is
|
||||
* responsible for reporting errors.
|
||||
*/
|
||||
if (!strncmp(params, "Unknown", 7))
|
||||
return;
|
||||
|
||||
if (!(p = strstr(params, "/")))
|
||||
return;
|
||||
|
||||
*p = '\0';
|
||||
p++;
|
||||
|
||||
stat->used = atoi(params);
|
||||
stat->max = atoi(p);
|
||||
}
|
||||
|
||||
/* send unregister command to itself */
|
||||
static void _unregister_self(struct dm_task *dmt)
|
||||
{
|
||||
const char *name = dm_task_get_name(dmt);
|
||||
struct dm_event_handler *dmevh;
|
||||
|
||||
if (!(dmevh = dm_event_handler_create()))
|
||||
return;
|
||||
|
||||
if (dm_event_handler_set_dev_name(dmevh, name))
|
||||
goto fail;
|
||||
|
||||
dm_event_handler_set_event_mask(dmevh, DM_EVENT_ALL_ERRORS|DM_EVENT_TIMEOUT);
|
||||
dm_event_unregister_handler(dmevh);
|
||||
fail:
|
||||
dm_event_handler_destroy(dmevh);
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt, enum dm_event_mask event,
|
||||
void **private)
|
||||
{
|
||||
void *next = NULL;
|
||||
uint64_t start, length;
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
struct snap_status stat = { 0 };
|
||||
const char *device = dm_task_get_name(dmt);
|
||||
int percent, *percent_warning = (int*)private;
|
||||
|
||||
/* No longer monitoring, waiting for remove */
|
||||
if (!*percent_warning)
|
||||
return;
|
||||
|
||||
if (pthread_mutex_trylock(&_event_mutex)) {
|
||||
syslog(LOG_NOTICE, "Another thread is handling an event. Waiting...");
|
||||
pthread_mutex_lock(&_event_mutex);
|
||||
}
|
||||
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
if (!target_type)
|
||||
goto out;
|
||||
|
||||
_parse_snapshot_params(params, &stat);
|
||||
/*
|
||||
* If the snapshot has been invalidated or we failed to parse
|
||||
* the status string. Report the full status string to syslog.
|
||||
*/
|
||||
if (stat.invalid || !stat.max) {
|
||||
syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params);
|
||||
_unregister_self(dmt);
|
||||
*percent_warning = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
percent = 100 * stat.used / stat.max;
|
||||
if (percent >= *percent_warning) {
|
||||
syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent);
|
||||
/* Print warning on the next multiple of WARNING_STEP. */
|
||||
*percent_warning = (percent / WARNING_STEP) * WARNING_STEP + WARNING_STEP;
|
||||
}
|
||||
out:
|
||||
pthread_mutex_unlock(&_event_mutex);
|
||||
}
|
||||
|
||||
int register_device(const char *device, const char *uuid, int major, int minor,
|
||||
void **private)
|
||||
{
|
||||
int r = 0;
|
||||
int *percent_warning = (int*)private;
|
||||
|
||||
pthread_mutex_lock(&_register_mutex);
|
||||
|
||||
/*
|
||||
* Need some space for allocations. 1024 should be more
|
||||
* than enough for what we need (device mapper name splitting)
|
||||
*/
|
||||
if (!_mem_pool && !(_mem_pool = dm_pool_create("snapshot_dso", 1024)))
|
||||
goto out;
|
||||
|
||||
*percent_warning = WARNING_THRESH; /* Print warning if snapshot is full */
|
||||
|
||||
if (!_lvm_handle) {
|
||||
lvm2_log_fn(_temporary_log_fn);
|
||||
if (!(_lvm_handle = lvm2_init())) {
|
||||
dm_pool_destroy(_mem_pool);
|
||||
_mem_pool = NULL;
|
||||
goto out;
|
||||
}
|
||||
lvm2_log_level(_lvm_handle, LVM2_LOG_SUPPRESS);
|
||||
/* FIXME Temporary: move to dmeventd core */
|
||||
lvm2_run(_lvm_handle, "_memlock_inc");
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "Monitoring snapshot %s\n", device);
|
||||
|
||||
_register_count++;
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&_register_mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int unregister_device(const char *device, const char *uuid, int major, int minor,
|
||||
void **unused __attribute((unused)))
|
||||
{
|
||||
pthread_mutex_lock(&_register_mutex);
|
||||
|
||||
syslog(LOG_INFO, "No longer monitoring snapshot %s\n",
|
||||
device);
|
||||
|
||||
if (!--_register_count) {
|
||||
dm_pool_destroy(_mem_pool);
|
||||
_mem_pool = NULL;
|
||||
lvm2_run(_lvm_handle, "_memlock_dec");
|
||||
lvm2_exit(_lvm_handle);
|
||||
_lvm_handle = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_register_mutex);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -384,10 +384,20 @@ activation {
|
||||
# dmeventd {
|
||||
# mirror_library is the library used when monitoring a mirror device.
|
||||
#
|
||||
# "libdevmapper-event-lvm2mirror.so" attempts to recover from failures.
|
||||
# It removes failed devices from a volume group and reconfigures a
|
||||
# mirror as necessary.
|
||||
#
|
||||
# "libdevmapper-event-lvm2mirror.so" attempts to recover from
|
||||
# failures. It removes failed devices from a volume group and
|
||||
# reconfigures a mirror as necessary. If no mirror library is
|
||||
# provided, mirrors are not monitored through dmeventd.
|
||||
|
||||
# mirror_library = "libdevmapper-event-lvm2mirror.so"
|
||||
|
||||
# snapshot_library is the library used when monitoring a snapshot device.
|
||||
#
|
||||
# "libdevmapper-event-lvm2snapshot.so" monitors the filling of
|
||||
# snapshots and emits a warning through syslog, when the use of
|
||||
# snapshot exceedes 80%. The warning is repeated when 85%, 90% and
|
||||
# 95% of the snapshot are filled.
|
||||
|
||||
# snapshot_library = "libdevmapper-event-lvm2snapshot.so"
|
||||
#}
|
||||
|
||||
|
||||
@@ -508,6 +508,13 @@ int lv_mirror_percent(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
struct dev_manager *dm;
|
||||
struct lvinfo info;
|
||||
|
||||
/* If mirrored LV is temporarily shrinked to 1 area (= linear),
|
||||
* it should be considered in-sync. */
|
||||
if (list_size(&lv->segments) == 1 && first_seg(lv)->area_count == 1) {
|
||||
*percent = 100.0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!activation())
|
||||
return 0;
|
||||
|
||||
@@ -669,7 +676,7 @@ int monitor_dev_for_events(struct cmd_context *cmd,
|
||||
#ifdef DMEVENTD
|
||||
int i, pending = 0, monitored;
|
||||
int r = 1;
|
||||
struct list *tmp;
|
||||
struct list *tmp, *snh, *snht;
|
||||
struct lv_segment *seg;
|
||||
int (*monitor_fn) (struct lv_segment *s, int e);
|
||||
|
||||
@@ -683,6 +690,28 @@ int monitor_dev_for_events(struct cmd_context *cmd,
|
||||
if (monitor && !dmeventd_monitor_mode())
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* In case of a snapshot device, we monitor lv->snapshot->lv,
|
||||
* not the actual LV itself.
|
||||
*/
|
||||
if (lv_is_cow(lv))
|
||||
return monitor_dev_for_events(cmd, lv->snapshot->lv, monitor);
|
||||
|
||||
/*
|
||||
* In case this LV is a snapshot origin, we instead monitor
|
||||
* each of its respective snapshots (the origin itself does
|
||||
* not need to be monitored).
|
||||
*
|
||||
* TODO: This may change when snapshots of mirrors are allowed.
|
||||
*/
|
||||
if (lv_is_origin(lv)) {
|
||||
list_iterate_safe(snh, snht, &lv->snapshot_segs)
|
||||
if (!monitor_dev_for_events(cmd, list_struct_base(snh,
|
||||
struct lv_segment, origin_list)->cow, monitor))
|
||||
r = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
list_iterate(tmp, &lv->segments) {
|
||||
seg = list_item(tmp, struct lv_segment);
|
||||
|
||||
|
||||
@@ -952,10 +952,11 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
max_stripe_size = seg->stripe_size;
|
||||
}
|
||||
|
||||
if (read_ahead == DM_READ_AHEAD_AUTO)
|
||||
read_ahead = max_stripe_size;
|
||||
else
|
||||
if (read_ahead == DM_READ_AHEAD_AUTO) {
|
||||
/* we need RA at least twice a whole stripe - see the comment in md/raid0.c */
|
||||
read_ahead = max_stripe_size * 2;
|
||||
read_ahead_flags = DM_READ_AHEAD_MINIMUM_FLAG;
|
||||
}
|
||||
|
||||
dm_tree_node_set_read_ahead(dnode, read_ahead, read_ahead_flags);
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
#define DEFAULT_PVMETADATACOPIES 1
|
||||
#define DEFAULT_LABELSECTOR UINT64_C(1)
|
||||
#define DEFAULT_READ_AHEAD "auto"
|
||||
#define DEFAULT_EXTENT_SIZE 4096 /* In KB */
|
||||
|
||||
#define DEFAULT_MSG_PREFIX " "
|
||||
#define DEFAULT_CMD_NAME 0
|
||||
@@ -103,17 +104,17 @@
|
||||
#define DEFAULT_REP_HEADINGS 1
|
||||
#define DEFAULT_REP_SEPARATOR " "
|
||||
|
||||
#define DEFAULT_LVS_COLS "lv_name,vg_name,lv_attr,lv_size,origin,snap_percent,move_pv,mirror_log,copy_percent"
|
||||
#define DEFAULT_LVS_COLS "lv_name,vg_name,lv_attr,lv_size,origin,snap_percent,move_pv,mirror_log,copy_percent,convert_lv"
|
||||
#define DEFAULT_VGS_COLS "vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free"
|
||||
#define DEFAULT_PVS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free"
|
||||
#define DEFAULT_SEGS_COLS "lv_name,vg_name,lv_attr,stripes,segtype,seg_size"
|
||||
#define DEFAULT_PVSEGS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
|
||||
|
||||
#define DEFAULT_LVS_COLS_VERB "lv_name,vg_name,seg_count,lv_attr,lv_size,lv_major,lv_minor,lv_kernel_major,lv_kernel_minor,origin,snap_percent,move_pv,copy_percent,mirror_log,lv_uuid"
|
||||
#define DEFAULT_LVS_COLS_VERB "lv_name,vg_name,seg_count,lv_attr,lv_size,lv_major,lv_minor,lv_kernel_major,lv_kernel_minor,origin,snap_percent,move_pv,copy_percent,mirror_log,convert_lv,lv_uuid"
|
||||
#define DEFAULT_VGS_COLS_VERB "vg_name,vg_attr,vg_extent_size,pv_count,lv_count,snap_count,vg_size,vg_free,vg_uuid"
|
||||
#define DEFAULT_PVS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,dev_size,pv_uuid"
|
||||
#define DEFAULT_SEGS_COLS_VERB "lv_name,vg_name,lv_attr,seg_start,seg_size,stripes,segtype,stripesize,chunksize"
|
||||
#define DEFAULT_PVSEGS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
|
||||
#define DEFAULT_PVSEGS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
||||
|
||||
#define DEFAULT_LVS_SORT "vg_name,lv_name"
|
||||
#define DEFAULT_VGS_SORT "vg_name"
|
||||
|
||||
@@ -361,6 +361,7 @@ int import_lv(struct dm_pool *mem, struct logical_volume *lv, struct lv_disk *lv
|
||||
list_init(&lv->snapshot_segs);
|
||||
list_init(&lv->segments);
|
||||
list_init(&lv->tags);
|
||||
list_init(&lv->segs_using_this_lv);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ int import_pool_lvs(struct volume_group *vg, struct dm_pool *mem, struct list *p
|
||||
list_init(&lv->snapshot_segs);
|
||||
list_init(&lv->segments);
|
||||
list_init(&lv->tags);
|
||||
list_init(&lv->segs_using_this_lv);
|
||||
|
||||
list_iterate_items(pl, pls) {
|
||||
lv->size += pl->pd.pl_blocks;
|
||||
|
||||
@@ -60,6 +60,7 @@ static struct flag _lv_flags[] = {
|
||||
{VIRTUAL, NULL},
|
||||
{SNAPSHOT, NULL},
|
||||
{ACTIVATE_EXCL, NULL},
|
||||
{CONVERTING, NULL},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
|
||||
@@ -86,6 +86,22 @@ static int _check_version(struct config_tree *cft)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _is_converting(struct logical_volume *lv)
|
||||
{
|
||||
struct lv_segment *seg;
|
||||
|
||||
if (lv->status & MIRRORED) {
|
||||
seg = first_seg(lv);
|
||||
/* Can't use is_temporary_mirror() because the metadata for
|
||||
* seg_lv may not be read in and flags may not be set yet. */
|
||||
if (seg_type(seg, 0) == AREA_LV &&
|
||||
strstr(seg_lv(seg, 0)->name, MIRROR_SYNC_LAYER))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _read_id(struct id *id, struct config_node *cn, const char *path)
|
||||
{
|
||||
struct config_value *cv;
|
||||
@@ -343,6 +359,9 @@ static int _read_segment(struct dm_pool *mem, struct volume_group *vg,
|
||||
if (seg_is_virtual(seg))
|
||||
lv->status |= VIRTUAL;
|
||||
|
||||
if (_is_converting(lv))
|
||||
lv->status |= CONVERTING;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -389,8 +408,10 @@ int text_import_areas(struct lv_segment *seg, const struct config_node *sn,
|
||||
return 0;
|
||||
}
|
||||
} else if ((lv1 = find_lv(seg->lv->vg, cv->v.str))) {
|
||||
set_lv_segment_area_lv(seg, s, lv1, (uint32_t) cv->next->v.i,
|
||||
flags);
|
||||
if (!set_lv_segment_area_lv(seg, s, lv1,
|
||||
(uint32_t) cv->next->v.i,
|
||||
flags))
|
||||
return_0;
|
||||
} else {
|
||||
log_error("Couldn't find volume '%s' "
|
||||
"for segment '%s'.",
|
||||
@@ -543,6 +564,7 @@ static int _read_lvnames(struct format_instance *fid __attribute((unused)),
|
||||
list_init(&lv->snapshot_segs);
|
||||
list_init(&lv->segments);
|
||||
list_init(&lv->tags);
|
||||
list_init(&lv->segs_using_this_lv);
|
||||
|
||||
/* Optional tags */
|
||||
if ((cn = find_config_node(lvn, "tags")) &&
|
||||
|
||||
@@ -33,9 +33,9 @@ struct lv_segment *alloc_snapshot_seg(struct logical_volume *lv,
|
||||
|
||||
int set_lv_segment_area_pv(struct lv_segment *seg, uint32_t area_num,
|
||||
struct physical_volume *pv, uint32_t pe);
|
||||
void set_lv_segment_area_lv(struct lv_segment *seg, uint32_t area_num,
|
||||
struct logical_volume *lv, uint32_t le,
|
||||
uint32_t flags);
|
||||
int set_lv_segment_area_lv(struct lv_segment *seg, uint32_t area_num,
|
||||
struct logical_volume *lv, uint32_t le,
|
||||
uint32_t flags);
|
||||
int move_lv_segment_area(struct lv_segment *seg_to, uint32_t area_to,
|
||||
struct lv_segment *seg_from, uint32_t area_from);
|
||||
void release_lv_segment_area(struct lv_segment *seg, uint32_t s,
|
||||
@@ -57,27 +57,21 @@ int lv_add_segment(struct alloc_handle *ah,
|
||||
struct logical_volume *lv,
|
||||
const struct segment_type *segtype,
|
||||
uint32_t stripe_size,
|
||||
struct physical_volume *mirrored_pv,
|
||||
uint32_t mirrored_pe,
|
||||
uint32_t status,
|
||||
uint32_t region_size,
|
||||
struct logical_volume *log_lv);
|
||||
|
||||
int lv_add_mirror_areas(struct alloc_handle *ah,
|
||||
struct logical_volume *lv, uint32_t le,
|
||||
uint32_t region_size);
|
||||
int lv_add_mirror_lvs(struct logical_volume *lv,
|
||||
struct logical_volume **sub_lvs,
|
||||
uint32_t num_extra_areas,
|
||||
uint32_t status, uint32_t region_size);
|
||||
|
||||
int lv_add_log_segment(struct alloc_handle *ah, struct logical_volume *log_lv);
|
||||
int lv_add_virtual_segment(struct logical_volume *lv, uint32_t status,
|
||||
uint32_t extents, const struct segment_type *segtype);
|
||||
int lv_add_mirror_segment(struct alloc_handle *ah,
|
||||
struct logical_volume *lv,
|
||||
struct logical_volume **sub_lvs,
|
||||
uint32_t mirrors,
|
||||
const struct segment_type *segtype,
|
||||
uint32_t status,
|
||||
uint32_t region_size,
|
||||
struct logical_volume *log_lv);
|
||||
int lv_add_more_mirrored_areas(struct logical_volume *lv,
|
||||
struct logical_volume **sub_lvs,
|
||||
uint32_t new_area_count,
|
||||
uint32_t status);
|
||||
|
||||
void alloc_destroy(struct alloc_handle *ah);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -62,9 +62,10 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
|
||||
{
|
||||
struct lv_segment *seg, *seg2;
|
||||
uint32_t le = 0;
|
||||
unsigned seg_count = 0;
|
||||
unsigned seg_count = 0, seg_found;
|
||||
int r = 1;
|
||||
uint32_t area_multiplier, s;
|
||||
struct seg_list *sl;
|
||||
|
||||
list_iterate_items(seg, &lv->segments) {
|
||||
seg_count++;
|
||||
@@ -101,7 +102,7 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
|
||||
}
|
||||
|
||||
if (!(seg2 = first_seg(seg->log_lv)) ||
|
||||
seg2->mirror_seg != seg) {
|
||||
find_mirror_seg(seg2) != seg) {
|
||||
log_error("LV %s: segment %u log LV does not "
|
||||
"point back to mirror segment",
|
||||
lv->name, seg_count);
|
||||
@@ -110,8 +111,8 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
|
||||
}
|
||||
|
||||
if (complete_vg && seg->status & MIRROR_IMAGE) {
|
||||
if (!seg->mirror_seg ||
|
||||
!seg_is_mirrored(seg->mirror_seg)) {
|
||||
if (!find_mirror_seg(seg) ||
|
||||
!seg_is_mirrored(find_mirror_seg(seg))) {
|
||||
log_error("LV %s: segment %u mirror image "
|
||||
"is not mirrored",
|
||||
lv->name, seg_count);
|
||||
@@ -157,7 +158,7 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
|
||||
(seg_lv(seg, s)->status & MIRROR_IMAGE) &&
|
||||
(!(seg2 = find_seg_by_le(seg_lv(seg, s),
|
||||
seg_le(seg, s))) ||
|
||||
seg2->mirror_seg != seg)) {
|
||||
find_mirror_seg(seg2) != seg)) {
|
||||
log_error("LV %s: segment %u mirror "
|
||||
"image %u missing mirror ptr",
|
||||
lv->name, seg_count, s);
|
||||
@@ -173,12 +174,57 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
|
||||
r = 0;
|
||||
}
|
||||
*/
|
||||
seg_found = 0;
|
||||
list_iterate_items(sl, &seg_lv(seg, s)->segs_using_this_lv)
|
||||
if (sl->seg == seg)
|
||||
seg_found++;
|
||||
if (!seg_found) {
|
||||
log_error("LV %s segment %d uses LV %s,"
|
||||
" but missing ptr from %s to %s",
|
||||
lv->name, seg_count,
|
||||
seg_lv(seg, s)->name,
|
||||
seg_lv(seg, s)->name, lv->name);
|
||||
r = 0;
|
||||
} else if (seg_found > 1) {
|
||||
log_error("LV %s has duplicated links "
|
||||
"to LV %s segment %d",
|
||||
seg_lv(seg, s)->name,
|
||||
lv->name, seg_count);
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
le += seg->len;
|
||||
}
|
||||
|
||||
list_iterate_items(sl, &lv->segs_using_this_lv) {
|
||||
seg = sl->seg;
|
||||
seg_found = 0;
|
||||
for (s = 0; s < seg->area_count; s++) {
|
||||
if (seg_type(seg, s) != AREA_LV)
|
||||
continue;
|
||||
if (lv == seg_lv(seg, s))
|
||||
seg_found++;
|
||||
}
|
||||
if (seg->log_lv == lv)
|
||||
seg_found++;
|
||||
if (!seg_found) {
|
||||
log_error("LV %s is used by LV %s:%" PRIu32 ", "
|
||||
"but missing ptr from %s to %s",
|
||||
lv->name, seg->lv->name, seg->le,
|
||||
seg->lv->name, lv->name);
|
||||
r = 0;
|
||||
} else if (seg_found != sl->count) {
|
||||
log_error("Reference count mismatch: LV %s has %d "
|
||||
"links to LV %s:%" PRIu32
|
||||
", which has %d links",
|
||||
lv->name, sl->count,
|
||||
seg->lv->name, seg->le, seg_found);
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (le != lv->le_count) {
|
||||
log_error("LV %s: inconsistent LE count %u != %u",
|
||||
lv->name, le, lv->le_count);
|
||||
@@ -243,9 +289,9 @@ static int _lv_split_segment(struct logical_volume *lv, struct lv_segment *seg,
|
||||
/* Split area at the offset */
|
||||
switch (seg_type(seg, s)) {
|
||||
case AREA_LV:
|
||||
seg_lv(split_seg, s) = seg_lv(seg, s);
|
||||
seg_le(split_seg, s) =
|
||||
seg_le(seg, s) + seg->area_len;
|
||||
if (!set_lv_segment_area_lv(split_seg, s, seg_lv(seg, s),
|
||||
seg_le(seg, s) + seg->area_len, 0))
|
||||
return_0;
|
||||
log_debug("Split %s:%u[%u] at %u: %s LE %u", lv->name,
|
||||
seg->le, s, le, seg_lv(seg, s)->name,
|
||||
seg_le(split_seg, s));
|
||||
|
||||
@@ -41,6 +41,9 @@ struct pv_segment;
|
||||
#define PV_MIN_SIZE ( 512L * 1024L >> SECTOR_SHIFT) /* 512 KB in sectors */
|
||||
#define MAX_RESTRICTED_LVS 255 /* Used by FMT_RESTRICTED_LVIDS */
|
||||
|
||||
/* Layer suffix */
|
||||
#define MIRROR_SYNC_LAYER "_mimagetmp"
|
||||
|
||||
/* Various flags */
|
||||
/* Note that the bits no longer necessarily correspond to LVM1 disk format */
|
||||
|
||||
@@ -66,6 +69,7 @@ struct pv_segment;
|
||||
#define MIRROR_NOTSYNCED 0x00080000U /* LV */
|
||||
//#define ACTIVATE_EXCL 0x00100000U /* LV - internal use only */
|
||||
//#define PRECOMMITTED 0x00200000U /* VG - internal use only */
|
||||
#define CONVERTING 0x00400000U /* LV */
|
||||
|
||||
#define LVM_READ 0x00000100U /* LV VG */
|
||||
#define LVM_WRITE 0x00000200U /* LV VG */
|
||||
@@ -88,6 +92,10 @@ struct pv_segment;
|
||||
#define CORRECT_INCONSISTENT 0x00000001U /* Correct inconsistent metadata */
|
||||
#define FAIL_INCONSISTENT 0x00000002U /* Fail if metadata inconsistent */
|
||||
|
||||
/* Mirror conversion type flags */
|
||||
#define MIRROR_BY_SEG 0x00000001U /* segment-by-segment mirror */
|
||||
#define MIRROR_BY_LV 0x00000002U /* mirror using whole mimage LVs */
|
||||
|
||||
/* Ordered list - see lv_manip.c */
|
||||
typedef enum {
|
||||
ALLOC_INVALID,
|
||||
@@ -234,7 +242,6 @@ struct lv_segment {
|
||||
uint32_t region_size; /* For mirrors - in sectors */
|
||||
uint32_t extents_copied;
|
||||
struct logical_volume *log_lv;
|
||||
struct lv_segment *mirror_seg;
|
||||
|
||||
struct list tags;
|
||||
|
||||
@@ -266,6 +273,7 @@ struct logical_volume {
|
||||
|
||||
struct list segments;
|
||||
struct list tags;
|
||||
struct list segs_using_this_lv;
|
||||
};
|
||||
|
||||
struct pe_range {
|
||||
@@ -302,11 +310,16 @@ struct list *get_pvs(struct cmd_context *cmd);
|
||||
/* Set full_scan to 1 to re-read every (filtered) device label */
|
||||
struct list *get_vgs(struct cmd_context *cmd, int full_scan);
|
||||
struct list *get_vgids(struct cmd_context *cmd, int full_scan);
|
||||
int scan_vgs_for_pvs(struct cmd_context *cmd);
|
||||
|
||||
int pv_write(struct cmd_context *cmd, struct physical_volume *pv,
|
||||
struct list *mdas, int64_t label_sector);
|
||||
int is_pv(pv_t *pv);
|
||||
int is_orphan_vg(const char *vg_name);
|
||||
int is_orphan(pv_t *pv);
|
||||
int vgs_are_compatible(struct cmd_context *cmd,
|
||||
struct volume_group *vg_from,
|
||||
struct volume_group *vg_to);
|
||||
vg_t *vg_lock_and_read(struct cmd_context *cmd, const char *vg_name,
|
||||
const char *vgid,
|
||||
uint32_t lock_flags, uint32_t status_flags,
|
||||
@@ -355,12 +368,19 @@ struct logical_volume *lv_create_empty(const char *name,
|
||||
int import,
|
||||
struct volume_group *vg);
|
||||
|
||||
/* Write out LV contents */
|
||||
int set_lv(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
uint64_t sectors, int value);
|
||||
|
||||
/* Reduce the size of an LV by extents */
|
||||
int lv_reduce(struct logical_volume *lv, uint32_t extents);
|
||||
|
||||
/* Empty an LV prior to deleting it */
|
||||
int lv_empty(struct logical_volume *lv);
|
||||
|
||||
/* Empty an LV and add error segment */
|
||||
int replace_lv_with_error_segment(struct logical_volume *lv);
|
||||
|
||||
/* Entry point for all LV extent allocations */
|
||||
int lv_extend(struct logical_volume *lv,
|
||||
const struct segment_type *segtype,
|
||||
@@ -379,6 +399,32 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
int lv_rename(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
const char *new_name);
|
||||
|
||||
/*
|
||||
* Functions for layer manipulation
|
||||
*/
|
||||
int insert_layer_for_segments_on_pv(struct cmd_context *cmd,
|
||||
struct logical_volume *lv_where,
|
||||
struct logical_volume *layer_lv,
|
||||
uint32_t status,
|
||||
struct pv_list *pv,
|
||||
struct list *lvs_changed);
|
||||
int remove_layers_for_segments(struct cmd_context *cmd,
|
||||
struct logical_volume *lv,
|
||||
struct logical_volume *layer_lv,
|
||||
uint32_t status_mask, struct list *lvs_changed);
|
||||
int remove_layers_for_segments_all(struct cmd_context *cmd,
|
||||
struct logical_volume *layer_lv,
|
||||
uint32_t status_mask,
|
||||
struct list *lvs_changed);
|
||||
int split_parent_segments_for_layer(struct cmd_context *cmd,
|
||||
struct logical_volume *layer_lv);
|
||||
int remove_layer_from_lv(struct logical_volume *lv,
|
||||
struct logical_volume *layer_lv);
|
||||
struct logical_volume *insert_layer_for_lv(struct cmd_context *cmd,
|
||||
struct logical_volume *lv_where,
|
||||
uint32_t status,
|
||||
const char *layer_suffix);
|
||||
|
||||
/* Find a PV within a given VG */
|
||||
struct pv_list *find_pv_in_vg(struct volume_group *vg, const char *pv_name);
|
||||
pv_t *find_pv_in_vg_by_uuid(struct volume_group *vg, struct id *id);
|
||||
@@ -392,7 +438,7 @@ struct physical_volume *find_pv_by_name(struct cmd_context *cmd,
|
||||
const char *pv_name);
|
||||
|
||||
/* Find LV segment containing given LE */
|
||||
struct lv_segment *first_seg(struct logical_volume *lv);
|
||||
struct lv_segment *first_seg(const struct logical_volume *lv);
|
||||
|
||||
|
||||
/*
|
||||
@@ -422,32 +468,42 @@ int vg_check_status(const struct volume_group *vg, uint32_t status);
|
||||
/*
|
||||
* Mirroring functions
|
||||
*/
|
||||
struct alloc_handle;
|
||||
struct lv_segment *find_mirror_seg(struct lv_segment *seg);
|
||||
int lv_add_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
uint32_t mirrors, uint32_t stripes,
|
||||
uint32_t region_size, uint32_t log_count,
|
||||
struct list *pvs, alloc_policy_t alloc, uint32_t flags);
|
||||
int lv_remove_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
uint32_t mirrors, uint32_t log_count,
|
||||
struct list *pvs, uint32_t status_mask);
|
||||
|
||||
int is_temporary_mirror_layer(const struct logical_volume *lv);
|
||||
struct logical_volume * find_temporary_mirror(const struct logical_volume *lv);
|
||||
uint32_t lv_mirror_count(const struct logical_volume *lv);
|
||||
uint32_t adjusted_mirror_region_size(uint32_t extent_size, uint32_t extents,
|
||||
uint32_t region_size);
|
||||
int create_mirror_layers(struct alloc_handle *ah,
|
||||
uint32_t first_area,
|
||||
uint32_t num_mirrors,
|
||||
struct logical_volume *lv,
|
||||
const struct segment_type *segtype,
|
||||
uint32_t status,
|
||||
uint32_t region_size,
|
||||
struct logical_volume *log_lv);
|
||||
int remove_mirrors_from_segments(struct logical_volume *lv,
|
||||
uint32_t new_mirrors, uint32_t status_mask);
|
||||
int add_mirrors_to_segments(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
uint32_t mirrors, uint32_t region_size,
|
||||
struct list *allocatable_pvs, alloc_policy_t alloc);
|
||||
|
||||
int remove_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors,
|
||||
int remove_mirror_images(struct logical_volume *lv, uint32_t num_mirrors,
|
||||
struct list *removable_pvs, unsigned remove_log);
|
||||
int add_mirror_images(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
uint32_t mirrors, uint32_t stripes, uint32_t region_size,
|
||||
struct list *allocatable_pvs, alloc_policy_t alloc,
|
||||
uint32_t log_count);
|
||||
int remove_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
struct list *removable_pvs);
|
||||
int add_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
uint32_t log_count, uint32_t region_size,
|
||||
struct list *allocatable_pvs, alloc_policy_t alloc);
|
||||
|
||||
int reconfigure_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors,
|
||||
struct list *removable_pvs, unsigned remove_log);
|
||||
int collapse_mirrored_lv(struct logical_volume *lv);
|
||||
|
||||
int insert_pvmove_mirrors(struct cmd_context *cmd,
|
||||
struct logical_volume *lv_mirr,
|
||||
struct list *source_pvl,
|
||||
struct logical_volume *lv,
|
||||
struct list *allocatable_pvs,
|
||||
alloc_policy_t alloc,
|
||||
struct list *lvs_changed);
|
||||
int remove_pvmove_mirrors(struct volume_group *vg,
|
||||
struct logical_volume *lv_mirr);
|
||||
struct logical_volume *find_pvmove_lv(struct volume_group *vg,
|
||||
struct device *dev, uint32_t lv_type);
|
||||
struct logical_volume *find_pvmove_lv_from_pvname(struct cmd_context *cmd,
|
||||
@@ -479,4 +535,19 @@ uint32_t pv_pe_alloc_count(const pv_t *pv);
|
||||
|
||||
uint32_t vg_status(const vg_t *vg);
|
||||
|
||||
struct vgcreate_params {
|
||||
char *vg_name;
|
||||
uint32_t extent_size;
|
||||
size_t max_pv;
|
||||
size_t max_lv;
|
||||
alloc_policy_t alloc;
|
||||
int clustered; /* FIXME: put this into a 'status' variable instead? */
|
||||
};
|
||||
|
||||
int validate_vg_create_params(struct cmd_context *cmd,
|
||||
struct vgcreate_params *vp);
|
||||
|
||||
int validate_vg_rename_params(struct cmd_context *cmd,
|
||||
const char *vg_name_old,
|
||||
const char *vg_name_new);
|
||||
#endif
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "metadata.h"
|
||||
#include "toolcontext.h"
|
||||
#include "lvm-string.h"
|
||||
#include "lvm-file.h"
|
||||
#include "lvmcache.h"
|
||||
#include "memlock.h"
|
||||
#include "str_list.h"
|
||||
@@ -227,6 +228,53 @@ int get_pv_from_vg_by_id(const struct format_type *fmt, const char *vg_name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_new_vg_name(struct cmd_context *cmd, const char *vg_name)
|
||||
{
|
||||
char vg_path[PATH_MAX];
|
||||
|
||||
if (!validate_name(vg_name))
|
||||
return_0;
|
||||
|
||||
snprintf(vg_path, PATH_MAX, "%s%s", cmd->dev_dir, vg_name);
|
||||
if (path_exists(vg_path)) {
|
||||
log_error("%s: already exists in filesystem", vg_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int validate_vg_rename_params(struct cmd_context *cmd,
|
||||
const char *vg_name_old,
|
||||
const char *vg_name_new)
|
||||
{
|
||||
unsigned length;
|
||||
char *dev_dir;
|
||||
|
||||
dev_dir = cmd->dev_dir;
|
||||
length = strlen(dev_dir);
|
||||
|
||||
/* Check sanity of new name */
|
||||
if (strlen(vg_name_new) > NAME_LEN - length - 2) {
|
||||
log_error("New volume group path exceeds maximum length "
|
||||
"of %d!", NAME_LEN - length - 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!validate_new_vg_name(cmd, vg_name_new)) {
|
||||
log_error("New volume group name \"%s\" is invalid",
|
||||
vg_name_new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(vg_name_old, vg_name_new)) {
|
||||
log_error("Old and new volume group names must differ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vg_rename(struct cmd_context *cmd, struct volume_group *vg,
|
||||
const char *new_name)
|
||||
{
|
||||
@@ -378,6 +426,45 @@ const char *strip_dir(const char *vg_name, const char *dev_dir)
|
||||
return vg_name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate parameters to vg_create() before calling.
|
||||
* FIXME: Move inside vg_create library function.
|
||||
* FIXME: Change vgcreate_params struct to individual gets/sets
|
||||
*/
|
||||
int validate_vg_create_params(struct cmd_context *cmd,
|
||||
struct vgcreate_params *vp)
|
||||
{
|
||||
if (!validate_new_vg_name(cmd, vp->vg_name)) {
|
||||
log_error("New volume group name \"%s\" is invalid",
|
||||
vp->vg_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vp->alloc == ALLOC_INHERIT) {
|
||||
log_error("Volume Group allocation policy cannot inherit "
|
||||
"from anything");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!vp->extent_size) {
|
||||
log_error("Physical extent size may not be zero");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(cmd->fmt->features & FMT_UNLIMITED_VOLS)) {
|
||||
if (!vp->max_lv)
|
||||
vp->max_lv = 255;
|
||||
if (!vp->max_pv)
|
||||
vp->max_pv = 255;
|
||||
if (vp->max_lv > 255 || vp->max_pv > 255) {
|
||||
log_error("Number of volumes may not exceed 255");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name,
|
||||
uint32_t extent_size, uint32_t max_pv,
|
||||
uint32_t max_lv, alloc_policy_t alloc,
|
||||
@@ -927,7 +1014,16 @@ static struct physical_volume *_find_pv_by_name(struct cmd_context *cmd,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME Can fail when no PV mda */
|
||||
if (is_orphan_vg(pv->vg_name)) {
|
||||
/* If a PV has no MDAs - need to search all VGs for it */
|
||||
if (!scan_vgs_for_pvs(cmd))
|
||||
return_NULL;
|
||||
if (!(pv = _pv_read(cmd, pv_name, NULL, NULL, 1))) {
|
||||
log_error("Physical volume %s not found", pv_name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_orphan_vg(pv->vg_name)) {
|
||||
log_error("Physical volume %s not in a volume group", pv_name);
|
||||
return NULL;
|
||||
@@ -937,7 +1033,7 @@ static struct physical_volume *_find_pv_by_name(struct cmd_context *cmd,
|
||||
}
|
||||
|
||||
/* Find segment at a given logical extent in an LV */
|
||||
struct lv_segment *find_seg_by_le(struct logical_volume *lv, uint32_t le)
|
||||
struct lv_segment *find_seg_by_le(const struct logical_volume *lv, uint32_t le)
|
||||
{
|
||||
struct lv_segment *seg;
|
||||
|
||||
@@ -948,7 +1044,7 @@ struct lv_segment *find_seg_by_le(struct logical_volume *lv, uint32_t le)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lv_segment *first_seg(struct logical_volume *lv)
|
||||
struct lv_segment *first_seg(const struct logical_volume *lv)
|
||||
{
|
||||
struct lv_segment *seg = NULL;
|
||||
|
||||
@@ -959,7 +1055,7 @@ struct lv_segment *first_seg(struct logical_volume *lv)
|
||||
}
|
||||
|
||||
/* Find segment at a given physical extent in a PV */
|
||||
struct pv_segment *find_peg_by_pe(struct physical_volume *pv, uint32_t pe)
|
||||
struct pv_segment *find_peg_by_pe(const struct physical_volume *pv, uint32_t pe)
|
||||
{
|
||||
struct pv_segment *peg;
|
||||
|
||||
@@ -987,6 +1083,86 @@ int vg_remove(struct volume_group *vg)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine whether two vgs are compatible for merging.
|
||||
*/
|
||||
int vgs_are_compatible(struct cmd_context *cmd,
|
||||
struct volume_group *vg_from,
|
||||
struct volume_group *vg_to)
|
||||
{
|
||||
struct lv_list *lvl1, *lvl2;
|
||||
struct pv_list *pvl;
|
||||
char *name1, *name2;
|
||||
|
||||
if (lvs_in_vg_activated(vg_from)) {
|
||||
log_error("Logical volumes in \"%s\" must be inactive",
|
||||
vg_from->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check compatibility */
|
||||
if (vg_to->extent_size != vg_from->extent_size) {
|
||||
log_error("Extent sizes differ: %d (%s) and %d (%s)",
|
||||
vg_to->extent_size, vg_to->name,
|
||||
vg_from->extent_size, vg_from->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vg_to->max_pv &&
|
||||
(vg_to->max_pv < vg_to->pv_count + vg_from->pv_count)) {
|
||||
log_error("Maximum number of physical volumes (%d) exceeded "
|
||||
" for \"%s\" and \"%s\"", vg_to->max_pv, vg_to->name,
|
||||
vg_from->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vg_to->max_lv &&
|
||||
(vg_to->max_lv < vg_to->lv_count + vg_from->lv_count)) {
|
||||
log_error("Maximum number of logical volumes (%d) exceeded "
|
||||
" for \"%s\" and \"%s\"", vg_to->max_lv, vg_to->name,
|
||||
vg_from->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check no conflicts with LV names */
|
||||
list_iterate_items(lvl1, &vg_to->lvs) {
|
||||
name1 = lvl1->lv->name;
|
||||
|
||||
list_iterate_items(lvl2, &vg_from->lvs) {
|
||||
name2 = lvl2->lv->name;
|
||||
|
||||
if (!strcmp(name1, name2)) {
|
||||
log_error("Duplicate logical volume "
|
||||
"name \"%s\" "
|
||||
"in \"%s\" and \"%s\"",
|
||||
name1, vg_to->name, vg_from->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check no PVs are constructed from either VG */
|
||||
list_iterate_items(pvl, &vg_to->pvs) {
|
||||
if (pv_uses_vg(pvl->pv, vg_from)) {
|
||||
log_error("Physical volume %s might be constructed "
|
||||
"from same volume group %s.",
|
||||
pv_dev_name(pvl->pv), vg_from->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
list_iterate_items(pvl, &vg_from->pvs) {
|
||||
if (pv_uses_vg(pvl->pv, vg_to)) {
|
||||
log_error("Physical volume %s might be constructed "
|
||||
"from same volume group %s.",
|
||||
pv_dev_name(pvl->pv), vg_to->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vg_validate(struct volume_group *vg)
|
||||
{
|
||||
struct pv_list *pvl, *pvl2;
|
||||
@@ -1700,7 +1876,7 @@ struct list *get_vgids(struct cmd_context *cmd, int full_scan)
|
||||
return lvmcache_get_vgids(cmd, full_scan);
|
||||
}
|
||||
|
||||
struct list *get_pvs(struct cmd_context *cmd)
|
||||
static int _get_pvs(struct cmd_context *cmd, struct list **pvslist)
|
||||
{
|
||||
struct str_list *strl;
|
||||
struct list *results;
|
||||
@@ -1714,17 +1890,19 @@ struct list *get_pvs(struct cmd_context *cmd)
|
||||
|
||||
lvmcache_label_scan(cmd, 0);
|
||||
|
||||
if (!(results = dm_pool_alloc(cmd->mem, sizeof(*results)))) {
|
||||
log_error("PV list allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
if (pvslist) {
|
||||
if (!(results = dm_pool_alloc(cmd->mem, sizeof(*results)))) {
|
||||
log_error("PV list allocation failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_init(results);
|
||||
list_init(results);
|
||||
}
|
||||
|
||||
/* Get list of VGs */
|
||||
if (!(vgids = get_vgids(cmd, 0))) {
|
||||
log_error("get_pvs: get_vgs failed");
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read every VG to ensure cache consistency */
|
||||
@@ -1751,16 +1929,36 @@ struct list *get_pvs(struct cmd_context *cmd)
|
||||
vgname);
|
||||
|
||||
/* Move PVs onto results list */
|
||||
list_iterate_safe(pvh, tmp, &vg->pvs) {
|
||||
list_add(results, pvh);
|
||||
}
|
||||
if (pvslist)
|
||||
list_iterate_safe(pvh, tmp, &vg->pvs)
|
||||
list_add(results, pvh);
|
||||
}
|
||||
init_pvmove(old_pvmove);
|
||||
init_partial(old_partial);
|
||||
|
||||
if (pvslist)
|
||||
*pvslist = results;
|
||||
else
|
||||
dm_pool_free(cmd->mem, vgids);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct list *get_pvs(struct cmd_context *cmd)
|
||||
{
|
||||
struct list *results;
|
||||
|
||||
if (!_get_pvs(cmd, &results))
|
||||
return NULL;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
int scan_vgs_for_pvs(struct cmd_context *cmd)
|
||||
{
|
||||
return _get_pvs(cmd, NULL);
|
||||
}
|
||||
|
||||
/* FIXME: liblvm todo - make into function that takes handle */
|
||||
int pv_write(struct cmd_context *cmd __attribute((unused)),
|
||||
struct physical_volume *pv,
|
||||
@@ -1832,6 +2030,15 @@ int is_orphan(pv_t *pv)
|
||||
return is_orphan_vg(pv_field(pv, vg_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* is_pv - Determine whether a pv is a real pv or dummy one
|
||||
* @pv: handle to device
|
||||
*/
|
||||
int is_pv(pv_t *pv)
|
||||
{
|
||||
return (pv_field(pv, vg_name) ? 1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* 0 - fail
|
||||
|
||||
@@ -158,6 +158,12 @@ struct peg_list {
|
||||
struct pv_segment *peg;
|
||||
};
|
||||
|
||||
struct seg_list {
|
||||
struct list list;
|
||||
unsigned count;
|
||||
struct lv_segment *seg;
|
||||
};
|
||||
|
||||
/*
|
||||
* Ownership of objects passes to caller.
|
||||
*/
|
||||
@@ -264,10 +270,10 @@ struct logical_volume *lv_from_lvid(struct cmd_context *cmd,
|
||||
struct physical_volume *find_pv(struct volume_group *vg, struct device *dev);
|
||||
|
||||
/* Find LV segment containing given LE */
|
||||
struct lv_segment *find_seg_by_le(struct logical_volume *lv, uint32_t le);
|
||||
struct lv_segment *find_seg_by_le(const struct logical_volume *lv, uint32_t le);
|
||||
|
||||
/* Find PV segment containing given LE */
|
||||
struct pv_segment *find_peg_by_pe(struct physical_volume *pv, uint32_t pe);
|
||||
struct pv_segment *find_peg_by_pe(const struct physical_volume *pv, uint32_t pe);
|
||||
|
||||
/*
|
||||
* Remove a dev_dir if present.
|
||||
@@ -292,19 +298,21 @@ int lv_merge_segments(struct logical_volume *lv);
|
||||
*/
|
||||
int lv_split_segment(struct logical_volume *lv, uint32_t le);
|
||||
|
||||
/*
|
||||
* Add/remove upward link from underlying LV to the segment using it
|
||||
* FIXME: ridiculously long name
|
||||
*/
|
||||
int add_seg_to_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
|
||||
int remove_seg_from_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
|
||||
struct lv_segment *get_only_segment_using_this_lv(struct logical_volume *lv);
|
||||
|
||||
/*
|
||||
* Mirroring functions
|
||||
*/
|
||||
int add_mirror_layers(struct alloc_handle *ah,
|
||||
uint32_t num_mirrors,
|
||||
uint32_t existing_mirrors,
|
||||
struct logical_volume *lv,
|
||||
const struct segment_type *segtype);
|
||||
|
||||
/*
|
||||
* Given mirror image or mirror log segment, find corresponding mirror segment
|
||||
*/
|
||||
struct lv_segment *find_mirror_seg(struct lv_segment *seg);
|
||||
int fixup_imported_mirrors(struct volume_group *vg);
|
||||
|
||||
/*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -283,6 +283,13 @@ static int _mirrored_add_target_line(struct dev_manager *dm, struct dm_pool *mem
|
||||
|
||||
mirr_state = *target_state;
|
||||
|
||||
/*
|
||||
* Mirror segment could have only 1 area temporarily
|
||||
* if the segment is under conversion.
|
||||
*/
|
||||
if (seg->area_count == 1)
|
||||
mirror_status = MIRR_DISABLED;
|
||||
|
||||
/*
|
||||
* For pvmove, only have one mirror segment RUNNING at once.
|
||||
* Segments before this are COMPLETED and use 2nd area.
|
||||
@@ -348,14 +355,14 @@ static int _mirrored_target_present(const struct lv_segment *seg __attribute((un
|
||||
_mirrored_present = target_present("mirror", 1);
|
||||
|
||||
/*
|
||||
* block_on_error available with mirror target >= 1.1
|
||||
* block_on_error available with mirror target >= 1.1 and <= 1.11
|
||||
* or with 1.0 in RHEL4U3 driver >= 4.5
|
||||
*/
|
||||
/* FIXME Move this into libdevmapper */
|
||||
|
||||
if (target_version("mirror", &maj, &min, &patchlevel) &&
|
||||
maj == 1 &&
|
||||
(min >= 1 ||
|
||||
((min >= 1 && min <= 11) ||
|
||||
(min == 0 && driver_version(vsn, sizeof(vsn)) &&
|
||||
sscanf(vsn, "%u.%u.%u", &maj2, &min2, &patchlevel2) == 3 &&
|
||||
maj2 == 4 && min2 == 5 && patchlevel2 == 0))) /* RHEL4U3 */
|
||||
|
||||
@@ -55,9 +55,6 @@
|
||||
/* Define to 1 if you have the `fork' function. */
|
||||
#undef HAVE_FORK
|
||||
|
||||
/* Define to 1 if you have the <fstab.h> header file. */
|
||||
#undef HAVE_FSTAB_H
|
||||
|
||||
/* Define to 1 if you have the `gethostname' function. */
|
||||
#undef HAVE_GETHOSTNAME
|
||||
|
||||
@@ -291,9 +288,6 @@
|
||||
/* Define to 1 if you have the <sys/utsname.h> header file. */
|
||||
#undef HAVE_SYS_UTSNAME_H
|
||||
|
||||
/* Define to 1 if you have the <sys/vfs.h> header file. */
|
||||
#undef HAVE_SYS_VFS_H
|
||||
|
||||
/* Define to 1 if you have the <sys/wait.h> header file. */
|
||||
#undef HAVE_SYS_WAIT_H
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ FIELD(LVS, lv, STR, "Origin", lvid, 6, origin, "origin", "For snapshots, the ori
|
||||
FIELD(LVS, lv, NUM, "Snap%", lvid, 6, snpercent, "snap_percent", "For snapshots, the percentage full if LV is active.")
|
||||
FIELD(LVS, lv, NUM, "Copy%", lvid, 6, copypercent, "copy_percent", "For mirrors and pvmove, current percentage in-sync.")
|
||||
FIELD(LVS, lv, STR, "Move", lvid, 4, movepv, "move_pv", "For pvmove, Source PV of temporary LV created by pvmove")
|
||||
FIELD(LVS, lv, STR, "Convert", lvid, 7, convertlv, "convert_lv", "For lvconvert, Name of temporary LV created by lvconvert")
|
||||
FIELD(LVS, lv, STR, "LV Tags", tags, 7, tags, "lv_tags", "Tags, if any.")
|
||||
FIELD(LVS, lv, STR, "Log", lvid, 3, loglv, "mirror_log", "For mirrors, the LV holding the synchronisation log.")
|
||||
FIELD(LVS, lv, STR, "Modules", lvid, 7, modules, "modules", "Kernel device-mapper modules required for this LV.")
|
||||
@@ -81,8 +82,10 @@ FIELD(SEGS, seg, NUM, "Region", region_size, 6, size32, "region_size", "For mirr
|
||||
FIELD(SEGS, seg, NUM, "Chunk", list, 5, chunksize, "chunksize", "For snapshots, the unit of data used when tracking changes.")
|
||||
FIELD(SEGS, seg, NUM, "Chunk", list, 5, chunksize, "chunk_size", "For snapshots, the unit of data used when tracking changes.")
|
||||
FIELD(SEGS, seg, NUM, "Start", list, 5, segstart, "seg_start", "Offset within the LV to the start of the segment in current units.")
|
||||
FIELD(SEGS, seg, NUM, "Start", list, 5, segstartpe, "seg_start_pe", "Offset within the LV to the start of the segment in physical extents.")
|
||||
FIELD(SEGS, seg, NUM, "SSize", list, 5, segsize, "seg_size", "Size of segment in current units.")
|
||||
FIELD(SEGS, seg, STR, "Seg Tags", tags, 8, tags, "seg_tags", "Tags, if any.")
|
||||
FIELD(SEGS, seg, STR, "PE Ranges", list, 9, peranges, "seg_pe_ranges", "Ranges of Physical Extents of underlying devices in command line format.")
|
||||
FIELD(SEGS, seg, STR, "Devices", list, 5, devices, "devices", "Underlying devices used with starting extent numbers.")
|
||||
|
||||
FIELD(PVSEGS, pvseg, NUM, "Start", pe, 5, uint32, "pvseg_start", "Physical Extent number of start of segment.")
|
||||
|
||||
@@ -80,9 +80,8 @@ static int _dev_name_disp(struct dm_report *rh, struct dm_pool *mem __attribute(
|
||||
return dm_report_field_string(rh, field, &name);
|
||||
}
|
||||
|
||||
static int _devices_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private __attribute((unused)))
|
||||
static int _format_pvsegs(struct dm_pool *mem, struct dm_report_field *field,
|
||||
const void *data, int range_format)
|
||||
{
|
||||
const struct lv_segment *seg = (const struct lv_segment *) data;
|
||||
unsigned int s;
|
||||
@@ -115,19 +114,32 @@ static int _devices_disp(struct dm_report *rh __attribute((unused)), struct dm_p
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dm_snprintf(extent_str, sizeof(extent_str), "(%" PRIu32
|
||||
")", extent) < 0) {
|
||||
if (dm_snprintf(extent_str, sizeof(extent_str),
|
||||
"%s%" PRIu32 "%s",
|
||||
range_format ? ":" : "(", extent,
|
||||
range_format ? "-" : ")") < 0) {
|
||||
log_error("Extent number dm_snprintf failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
|
||||
log_error("dm_pool_grow_object failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (range_format) {
|
||||
if (dm_snprintf(extent_str, sizeof(extent_str),
|
||||
"%" PRIu32, extent + seg->area_len - 1) < 0) {
|
||||
log_error("Extent number dm_snprintf failed");
|
||||
return 0;
|
||||
}
|
||||
if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
|
||||
log_error("dm_pool_grow_object failed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((s != seg->area_count - 1) &&
|
||||
!dm_pool_grow_object(mem, ",", 1)) {
|
||||
!dm_pool_grow_object(mem, range_format ? " " : ",", 1)) {
|
||||
log_error("dm_pool_grow_object failed");
|
||||
return 0;
|
||||
}
|
||||
@@ -143,6 +155,20 @@ static int _devices_disp(struct dm_report *rh __attribute((unused)), struct dm_p
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _devices_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private __attribute((unused)))
|
||||
{
|
||||
return _format_pvsegs(mem, field, data, 0);
|
||||
}
|
||||
|
||||
static int _peranges_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private __attribute((unused)))
|
||||
{
|
||||
return _format_pvsegs(mem, field, data, 1);
|
||||
}
|
||||
|
||||
static int _tags_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private __attribute((unused)))
|
||||
@@ -246,6 +272,23 @@ static int _lvkmin_disp(struct dm_report *rh, struct dm_pool *mem __attribute((u
|
||||
return dm_report_field_uint64(rh, field, &_minusone);
|
||||
}
|
||||
|
||||
static int _lv_mimage_in_sync(const struct logical_volume *lv)
|
||||
{
|
||||
float percent;
|
||||
struct lv_segment *mirror_seg = find_mirror_seg(first_seg(lv));
|
||||
|
||||
if (!(lv->status & MIRROR_IMAGE) || !mirror_seg)
|
||||
return_0;
|
||||
|
||||
if (!lv_mirror_percent(lv->vg->cmd, mirror_seg->lv, 0, &percent, NULL))
|
||||
return_0;
|
||||
|
||||
if (percent >= 100.0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _lvstatus_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private __attribute((unused)))
|
||||
@@ -262,13 +305,18 @@ static int _lvstatus_disp(struct dm_report *rh __attribute((unused)), struct dm_
|
||||
|
||||
if (lv->status & PVMOVE)
|
||||
repstr[0] = 'p';
|
||||
else if (lv->status & CONVERTING)
|
||||
repstr[0] = 'c';
|
||||
else if (lv->status & MIRRORED) {
|
||||
if (lv->status & MIRROR_NOTSYNCED)
|
||||
repstr[0] = 'M';
|
||||
else
|
||||
repstr[0] = 'm';
|
||||
}else if (lv->status & MIRROR_IMAGE)
|
||||
repstr[0] = 'i';
|
||||
if (_lv_mimage_in_sync(lv))
|
||||
repstr[0] = 'i';
|
||||
else
|
||||
repstr[0] = 'I';
|
||||
else if (lv->status & MIRROR_LOG)
|
||||
repstr[0] = 'l';
|
||||
else if (lv->status & VIRTUAL)
|
||||
@@ -501,6 +549,32 @@ static int _movepv_disp(struct dm_report *rh, struct dm_pool *mem __attribute((u
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _convertlv_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private __attribute((unused)))
|
||||
{
|
||||
const struct logical_volume *lv = (const struct logical_volume *) data;
|
||||
const char *name = NULL;
|
||||
struct lv_segment *seg;
|
||||
|
||||
if (lv->status & CONVERTING) {
|
||||
if (lv->status & MIRRORED) {
|
||||
seg = first_seg(lv);
|
||||
|
||||
/* Temporary mirror is always area_num == 0 */
|
||||
if (seg_type(seg, 0) == AREA_LV &&
|
||||
is_temporary_mirror_layer(seg_lv(seg, 0)))
|
||||
name = seg_lv(seg, 0)->name;
|
||||
}
|
||||
}
|
||||
|
||||
if (name)
|
||||
return dm_report_field_string(rh, field, &name);
|
||||
|
||||
dm_report_field_set_value(field, "", NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _size32_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private)
|
||||
@@ -613,6 +687,17 @@ static int _segstart_disp(struct dm_report *rh, struct dm_pool *mem,
|
||||
return _size64_disp(rh, mem, field, &start, private);
|
||||
}
|
||||
|
||||
static int _segstartpe_disp(struct dm_report *rh,
|
||||
struct dm_pool *mem __attribute((unused)),
|
||||
struct dm_report_field *field,
|
||||
const void *data,
|
||||
void *private __attribute((unused)))
|
||||
{
|
||||
const struct lv_segment *seg = (const struct lv_segment *) data;
|
||||
|
||||
return dm_report_field_uint32(rh, field, &seg->le);
|
||||
}
|
||||
|
||||
static int _segsize_disp(struct dm_report *rh, struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -21,6 +21,10 @@
|
||||
#include "config.h"
|
||||
#include "activate.h"
|
||||
#include "str_list.h"
|
||||
#ifdef DMEVENTD
|
||||
# include "sharedlib.h"
|
||||
# include <libdevmapper-event.h>
|
||||
#endif
|
||||
|
||||
static const char *_snap_name(const struct lv_segment *seg)
|
||||
{
|
||||
@@ -125,6 +129,133 @@ static int _snap_target_present(const struct lv_segment *seg __attribute((unused
|
||||
|
||||
return _snap_present;
|
||||
}
|
||||
|
||||
#ifdef DMEVENTD
|
||||
static int _get_snapshot_dso_path(struct cmd_context *cmd, char **dso)
|
||||
{
|
||||
char *path;
|
||||
const char *libpath;
|
||||
|
||||
if (!(path = dm_pool_alloc(cmd->mem, PATH_MAX))) {
|
||||
log_error("Failed to allocate dmeventd library path.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
libpath = find_config_tree_str(cmd, "dmeventd/snapshot_library", NULL);
|
||||
if (!libpath)
|
||||
return 0;
|
||||
|
||||
get_shared_library_path(cmd, libpath, path, PATH_MAX);
|
||||
|
||||
*dso = path;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dm_event_handler *_create_dm_event_handler(const char *dmname,
|
||||
const char *dso,
|
||||
const int timeout,
|
||||
enum dm_event_mask mask)
|
||||
{
|
||||
struct dm_event_handler *dmevh;
|
||||
|
||||
if (!(dmevh = dm_event_handler_create()))
|
||||
return_0;
|
||||
|
||||
if (dm_event_handler_set_dso(dmevh, dso))
|
||||
goto fail;
|
||||
|
||||
if (dm_event_handler_set_dev_name(dmevh, dmname))
|
||||
goto fail;
|
||||
|
||||
dm_event_handler_set_timeout(dmevh, timeout);
|
||||
dm_event_handler_set_event_mask(dmevh, mask);
|
||||
return dmevh;
|
||||
|
||||
fail:
|
||||
dm_event_handler_destroy(dmevh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _target_registered(struct lv_segment *seg, int *pending)
|
||||
{
|
||||
char *dso, *name;
|
||||
struct logical_volume *lv;
|
||||
struct volume_group *vg;
|
||||
enum dm_event_mask evmask = 0;
|
||||
struct dm_event_handler *dmevh;
|
||||
|
||||
lv = seg->lv;
|
||||
vg = lv->vg;
|
||||
|
||||
*pending = 0;
|
||||
if (!_get_snapshot_dso_path(vg->cmd, &dso))
|
||||
return_0;
|
||||
|
||||
if (!(name = build_dm_name(vg->cmd->mem, vg->name, seg->cow->name, NULL)))
|
||||
return_0;
|
||||
|
||||
if (!(dmevh = _create_dm_event_handler(name, dso, 0, DM_EVENT_ALL_ERRORS)))
|
||||
return_0;
|
||||
|
||||
if (dm_event_get_registered_device(dmevh, 0)) {
|
||||
dm_event_handler_destroy(dmevh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
evmask = dm_event_handler_get_event_mask(dmevh);
|
||||
if (evmask & DM_EVENT_REGISTRATION_PENDING) {
|
||||
*pending = 1;
|
||||
evmask &= ~DM_EVENT_REGISTRATION_PENDING;
|
||||
}
|
||||
|
||||
dm_event_handler_destroy(dmevh);
|
||||
|
||||
return evmask;
|
||||
}
|
||||
|
||||
/* FIXME This gets run while suspended and performs banned operations. */
|
||||
static int _target_set_events(struct lv_segment *seg, int events, int set)
|
||||
{
|
||||
char *dso, *name;
|
||||
struct volume_group *vg = seg->lv->vg;
|
||||
struct dm_event_handler *dmevh;
|
||||
int r;
|
||||
|
||||
if (!_get_snapshot_dso_path(vg->cmd, &dso))
|
||||
return_0;
|
||||
|
||||
if (!(name = build_dm_name(vg->cmd->mem, vg->name, seg->cow->name, NULL)))
|
||||
return_0;
|
||||
|
||||
/* FIXME: make timeout configurable */
|
||||
if (!(dmevh = _create_dm_event_handler(name, dso, 10,
|
||||
DM_EVENT_ALL_ERRORS|DM_EVENT_TIMEOUT)))
|
||||
return_0;
|
||||
|
||||
r = set ? dm_event_register_handler(dmevh) : dm_event_unregister_handler(dmevh);
|
||||
dm_event_handler_destroy(dmevh);
|
||||
if (!r)
|
||||
return_0;
|
||||
|
||||
log_info("%s %s for events", set ? "Registered" : "Unregistered", name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _target_register_events(struct lv_segment *seg,
|
||||
int events)
|
||||
{
|
||||
return _target_set_events(seg, events, 1);
|
||||
}
|
||||
|
||||
static int _target_unregister_events(struct lv_segment *seg,
|
||||
int events)
|
||||
{
|
||||
return _target_set_events(seg, events, 0);
|
||||
}
|
||||
|
||||
#endif /* DMEVENTD */
|
||||
#endif
|
||||
|
||||
static int _snap_modules_needed(struct dm_pool *mem,
|
||||
@@ -151,6 +282,11 @@ static struct segtype_handler _snapshot_ops = {
|
||||
#ifdef DEVMAPPER_SUPPORT
|
||||
.target_percent = _snap_target_percent,
|
||||
.target_present = _snap_target_present,
|
||||
#ifdef DMEVENTD
|
||||
.target_monitored = _target_registered,
|
||||
.target_monitor_events = _target_register_events,
|
||||
.target_unmonitor_events = _target_unregister_events,
|
||||
#endif
|
||||
#endif
|
||||
.modules_needed = _snap_modules_needed,
|
||||
.destroy = _snap_destroy,
|
||||
@@ -164,6 +300,9 @@ struct segment_type *init_segtype(struct cmd_context *cmd)
|
||||
#endif
|
||||
{
|
||||
struct segment_type *segtype = dm_malloc(sizeof(*segtype));
|
||||
#ifdef DMEVENTD
|
||||
char *dso;
|
||||
#endif
|
||||
|
||||
if (!segtype) {
|
||||
stack;
|
||||
@@ -176,6 +315,10 @@ struct segment_type *init_segtype(struct cmd_context *cmd)
|
||||
segtype->private = NULL;
|
||||
segtype->flags = SEG_SNAPSHOT;
|
||||
|
||||
#ifdef DMEVENTD
|
||||
if (_get_snapshot_dso_path(cmd, &dso))
|
||||
segtype->flags |= SEG_MONITORED;
|
||||
#endif
|
||||
log_very_verbose("Initialised segtype: %s", segtype->name);
|
||||
|
||||
return segtype;
|
||||
|
||||
@@ -300,6 +300,8 @@ static int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Created %s", path);
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
if (!dm_set_selinux_context(path, S_IFBLK))
|
||||
return 0;
|
||||
@@ -341,6 +343,8 @@ static int _rename_dev_node(const char *old_name, const char *new_name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Renamed %s to %s", oldpath, newpath);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -359,6 +363,8 @@ static int _rm_dev_node(const char *dev_name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Removed %s", path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -382,6 +388,11 @@ int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead)
|
||||
int fd;
|
||||
long read_ahead_long;
|
||||
|
||||
if (!*dev_name) {
|
||||
log_error("Empty device name passed to BLKRAGET");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((fd = _open_dev_node(dev_name)) < 0)
|
||||
return_0;
|
||||
|
||||
@@ -406,6 +417,11 @@ static int _set_read_ahead(const char *dev_name, uint32_t read_ahead)
|
||||
int fd;
|
||||
long read_ahead_long = (long) read_ahead;
|
||||
|
||||
if (!*dev_name) {
|
||||
log_error("Empty device name passed to BLKRAGET");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((fd = _open_dev_node(dev_name)) < 0)
|
||||
return_0;
|
||||
|
||||
@@ -438,8 +454,8 @@ static int _set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
|
||||
return_0;
|
||||
|
||||
if (current_read_ahead > read_ahead) {
|
||||
log_debug("%s: read ahead %" PRIu32
|
||||
" below minimum of %" PRIu32,
|
||||
log_debug("%s: retaining kernel read ahead of %" PRIu32
|
||||
" (requested %" PRIu32 ")",
|
||||
dev_name, current_read_ahead, read_ahead);
|
||||
return 1;
|
||||
}
|
||||
@@ -521,9 +537,23 @@ static int _stack_node_op(node_op_t type, const char *dev_name, uint32_t major,
|
||||
uint32_t read_ahead_flags)
|
||||
{
|
||||
struct node_op_parms *nop;
|
||||
struct list *noph, *nopht;
|
||||
size_t len = strlen(dev_name) + strlen(old_name) + 2;
|
||||
char *pos;
|
||||
|
||||
/*
|
||||
* Ignore any outstanding operations on the node if deleting it
|
||||
*/
|
||||
if (type == NODE_DEL) {
|
||||
list_iterate_safe(noph, nopht, &_node_ops) {
|
||||
nop = list_item(noph, struct node_op_parms);
|
||||
if (!strcmp(dev_name, nop->dev_name)) {
|
||||
list_del(&nop->list);
|
||||
dm_free(nop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(nop = dm_malloc(sizeof(*nop) + len))) {
|
||||
log_error("Insufficient memory to stack mknod operation");
|
||||
return 0;
|
||||
@@ -565,18 +595,25 @@ static void _pop_node_ops(void)
|
||||
int add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
|
||||
uid_t uid, gid_t gid, mode_t mode)
|
||||
{
|
||||
log_debug("%s: Stacking NODE_ADD (%" PRIu32 ",%" PRIu32 ") %u:%u 0%o",
|
||||
dev_name, major, minor, uid, gid, mode);
|
||||
|
||||
return _stack_node_op(NODE_ADD, dev_name, major, minor, uid, gid, mode,
|
||||
"", 0, 0);
|
||||
}
|
||||
|
||||
int rename_dev_node(const char *old_name, const char *new_name)
|
||||
{
|
||||
log_debug("%s: Stacking NODE_RENAME to %s", old_name, new_name);
|
||||
|
||||
return _stack_node_op(NODE_RENAME, new_name, 0, 0, 0, 0, 0, old_name,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
int rm_dev_node(const char *dev_name)
|
||||
{
|
||||
log_debug("%s: Stacking NODE_DEL (replaces other stacked ops)", dev_name);
|
||||
|
||||
return _stack_node_op(NODE_DEL, dev_name, 0, 0, 0, 0, 0, "", 0, 0);
|
||||
}
|
||||
|
||||
@@ -586,6 +623,9 @@ int set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
|
||||
if (read_ahead == DM_READ_AHEAD_AUTO)
|
||||
return 1;
|
||||
|
||||
log_debug("%s: Stacking NODE_READ_AHEAD %" PRIu32 " (flags=%" PRIu32
|
||||
")", dev_name, read_ahead, read_ahead_flags);
|
||||
|
||||
return _stack_node_op(NODE_READ_AHEAD, dev_name, 0, 0, 0, 0, 0, "",
|
||||
read_ahead, read_ahead_flags);
|
||||
}
|
||||
|
||||
@@ -1160,11 +1160,11 @@ int dm_tree_activate_children(struct dm_tree_node *dnode,
|
||||
if (!child->info.inactive_table && !child->info.suspended)
|
||||
continue;
|
||||
|
||||
if (!_resume_node(name, child->info.major, child->info.minor,
|
||||
if (!_resume_node(child->name, child->info.major, child->info.minor,
|
||||
child->props.read_ahead,
|
||||
child->props.read_ahead_flags, &newinfo)) {
|
||||
log_error("Unable to resume %s (%" PRIu32
|
||||
":%" PRIu32 ")", name, child->info.major,
|
||||
":%" PRIu32 ")", child->name, child->info.major,
|
||||
child->info.minor);
|
||||
continue;
|
||||
}
|
||||
@@ -1289,7 +1289,7 @@ static int _emit_segment_line(struct dm_task *dmt, struct load_segment *seg, uin
|
||||
if (seg->clustered) {
|
||||
if (seg->uuid)
|
||||
log_parm_count++;
|
||||
if ((tw = _dm_snprintf(params + pos, paramsize - pos, "clustered_")) < 0) {
|
||||
if ((tw = _dm_snprintf(params + pos, paramsize - pos, "clustered-")) < 0) {
|
||||
stack; /* Out of space */
|
||||
return -1;
|
||||
}
|
||||
@@ -1510,7 +1510,6 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
|
||||
void *handle = NULL;
|
||||
struct dm_tree_node *child;
|
||||
struct dm_info newinfo;
|
||||
const char *name;
|
||||
|
||||
/* Preload children first */
|
||||
while ((child = dm_tree_next_child(&handle, dnode, 0))) {
|
||||
@@ -1526,11 +1525,6 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
|
||||
if (dm_tree_node_num_children(child, 0))
|
||||
dm_tree_preload_children(child, uuid_prefix, uuid_prefix_len);
|
||||
|
||||
if (!(name = dm_tree_node_get_name(child))) {
|
||||
stack;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* FIXME Cope if name exists with no uuid? */
|
||||
if (!child->info.exists) {
|
||||
if (!_create_node(child)) {
|
||||
@@ -1553,11 +1547,11 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
|
||||
if (!child->info.inactive_table && !child->info.suspended)
|
||||
continue;
|
||||
|
||||
if (!_resume_node(name, child->info.major, child->info.minor,
|
||||
if (!_resume_node(child->name, child->info.major, child->info.minor,
|
||||
child->props.read_ahead,
|
||||
child->props.read_ahead_flags, &newinfo)) {
|
||||
log_error("Unable to resume %s (%" PRIu32
|
||||
":%" PRIu32 ")", name, child->info.major,
|
||||
":%" PRIu32 ")", child->name, child->info.major,
|
||||
child->info.minor);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
ifeq ("@FSADM@", "yes")
|
||||
FSADMMAN = fsadm.8
|
||||
else
|
||||
FSADMMAN =
|
||||
endif
|
||||
|
||||
MAN5=lvm.conf.5
|
||||
MAN8=lvchange.8 lvconvert.8 lvcreate.8 lvdisplay.8 lvextend.8 lvm.8 \
|
||||
lvmchange.8 lvmdiskscan.8 lvmdump.8 \
|
||||
@@ -24,7 +30,7 @@ MAN8=lvchange.8 lvconvert.8 lvcreate.8 lvdisplay.8 lvextend.8 lvm.8 \
|
||||
pvresize.8 pvs.8 pvscan.8 vgcfgbackup.8 vgcfgrestore.8 vgchange.8 \
|
||||
vgck.8 vgcreate.8 vgconvert.8 vgdisplay.8 vgexport.8 vgextend.8 \
|
||||
vgimport.8 vgmerge.8 vgmknodes.8 vgreduce.8 vgremove.8 vgrename.8 \
|
||||
vgs.8 vgscan.8 vgsplit.8
|
||||
vgs.8 vgscan.8 vgsplit.8 $(FSADMMAN)
|
||||
MAN8CLUSTER=clvmd.8
|
||||
MAN5DIR=${mandir}/man5
|
||||
MAN8DIR=${mandir}/man8
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH DMSETUP 8 "Apr 06 2006" "Linux" "MAINTENTANCE COMMANDS"
|
||||
.TH DMSETUP 8 "Apr 06 2006" "Linux" "MAINTENANCE COMMANDS"
|
||||
.SH NAME
|
||||
dmsetup \- low level logical volume management
|
||||
.SH SYNOPSIS
|
||||
|
||||
56
man/fsadm.8
Normal file
56
man/fsadm.8
Normal file
@@ -0,0 +1,56 @@
|
||||
.TH "FSADM" "8" "LVM TOOLS" "Red Hat, Inc" "\""
|
||||
.SH "NAME"
|
||||
fsadm \- utility to resize or check filesystem on a device
|
||||
.SH "SYNOPSIS"
|
||||
.B fsdam
|
||||
.RI [options]\ check\ device
|
||||
|
||||
.B fsdam
|
||||
.RI [options]\ resize\ device\ [new_size[BKMGTEP]]
|
||||
|
||||
.SH "DESCRIPTION"
|
||||
\fBfsadm\fR utility resizes or checks the filesystem on a device. It tries to use the same API for \fBExt2/3\fR, \fBReiserFS\fR and \fBXFS\fR filesystem and simply resize and filesystem check operation.
|
||||
.SH "OPTIONS"
|
||||
.TP
|
||||
\fB\-h \-\-help\fR
|
||||
\(em print help message
|
||||
.TP
|
||||
\fB\-v \-\-verbose\fR
|
||||
\(em be more verbose
|
||||
.TP
|
||||
\fB\-e \-\-ext\-offline\fR
|
||||
\(em unmount Ext2/3 filesystem before doing resize
|
||||
.TP
|
||||
\fB\-f \-\-force\fR
|
||||
\(em bypass some sanity checks
|
||||
.TP
|
||||
\fB\-n \-\-dry\-run\fR
|
||||
\(em print commands without running them
|
||||
.TP
|
||||
\fB\-y \-\-yes\fR
|
||||
\(em answer "yes" at any prompts
|
||||
.TP
|
||||
\fBnew_size\fR
|
||||
\(em Absolute number of filesystem blocks to be in the filesystem, or an absolute size using a suffix (in powers of 1024). If new_size is not supplied, the whole device is used.
|
||||
|
||||
|
||||
.SH "EXAMPLES"
|
||||
"fsadm \-e \-y resize /dev/vg/test 1000M" tries to resize the size of the filesystem on logical volume /dev/vg/test. If /dev/vg/test contains Ext2/3 filesystem it will be unmounted prior the resize. All [y|n] questions will be answered 'y'.
|
||||
.SH "ENVIRONMENT VARIABLES"
|
||||
.TP
|
||||
\fBTMPDIR\fP
|
||||
Where the temporary directory should be created.
|
||||
.TP
|
||||
.BR
|
||||
.SH "SEE ALSO"
|
||||
.BR lvm (8),
|
||||
.BR lvresize (8),
|
||||
.BR lvm.conf (5),
|
||||
.BR tune2fs (8),
|
||||
.BR resize2fs (8),
|
||||
.BR reiserfstune (8),
|
||||
.BR resize_reiserfs (8),
|
||||
.BR xfs_info (8),
|
||||
.BR xfs_growfs (8),
|
||||
.BR xfs_check (8)
|
||||
|
||||
@@ -75,11 +75,10 @@ Change access permission to read-only or read/write.
|
||||
.I \-r, \-\-readahead ReadAheadSectors|auto|none
|
||||
Set read ahead sector count of this logical volume.
|
||||
For volume groups with metadata in lvm1 format, this must
|
||||
be a value between 2 and 120.
|
||||
be a value between 2 and 120 sectors.
|
||||
The default value is "auto" which allows the kernel to choose
|
||||
a suitable value automatically.
|
||||
"None" is equivalent to specifying zero.
|
||||
N.B. This setting is currently disregarded and "auto" is always used.
|
||||
.TP
|
||||
.I \-\-refresh
|
||||
If the logical volume is active, reload its metadata.
|
||||
|
||||
@@ -5,6 +5,7 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot
|
||||
.B lvconvert
|
||||
\-m/\-\-mirrors Mirrors [\-\-mirrorlog {disk|core}] [\-\-corelog] [\-R/\-\-regionsize MirrorLogRegionSize]
|
||||
[\-A/\-\-alloc AllocationPolicy]
|
||||
[\-b/\-\-background] [\-i/\-\-interval Seconds]
|
||||
[\-h/\-?/\-\-help]
|
||||
[\-v/\-\-verbose]
|
||||
[\-\-version]
|
||||
@@ -52,6 +53,12 @@ The optional argument "--corelog" is the same as specifying "--mirrorlog core".
|
||||
.I \-R, \-\-regionsize MirrorLogRegionSize
|
||||
A mirror is divided into regions of this size (in MB), and the mirror log
|
||||
uses this granularity to track which regions are in sync.
|
||||
.TP
|
||||
.I \-b, \-\-background
|
||||
Run the daemon in the background.
|
||||
.TP
|
||||
.I \-i, \-\-interval Seconds
|
||||
Report progress as a percentage at regular intervals.
|
||||
.br
|
||||
.TP
|
||||
.I \-s, \-\-snapshot
|
||||
|
||||
@@ -125,7 +125,6 @@ be a value between 2 and 120.
|
||||
The default value is "auto" which allows the kernel to choose
|
||||
a suitable value automatically.
|
||||
"None" is equivalent to specifying zero.
|
||||
N.B. This setting is currently disregarded and "auto" is always used.
|
||||
.TP
|
||||
.I \-R, \-\-regionsize MirrorLogRegionSize
|
||||
A mirror is divided into regions of this size (in MB), and the mirror log
|
||||
|
||||
88
man/lvm.8
88
man/lvm.8
@@ -50,8 +50,92 @@ loading \fBlvm.conf\fP (5) and any other configuration files.
|
||||
.TP
|
||||
\fBversion\fP \(em Display version information.
|
||||
.LP
|
||||
The following commands are not implemented in LVM2 but might be
|
||||
in future: lvmsadc, lvmsar, pvdata, pvresize.
|
||||
.SH COMMANDS
|
||||
The following commands implement the core LVM functionality.
|
||||
.TP
|
||||
\fBpvchange\fP \(em Change attributes of a physical volume.
|
||||
.TP
|
||||
\fBpvck\fP \(em Check physical volume metadata.
|
||||
.TP
|
||||
\fBpvcreate\fP \(em Initialize a disk or partition for use by LVM.
|
||||
.TP
|
||||
\fBpvdisplay\fP \(em Display attributes of a physical volume.
|
||||
.TP
|
||||
\fBpvmove\fP \(em Move physical extents.
|
||||
.TP
|
||||
\fBpvremove\fP \(em Remove a physical volume.
|
||||
.TP
|
||||
\fBpvresize\fP \(em Resize a disk or partition in use by LVM2.
|
||||
.TP
|
||||
\fBpvs\fP \(em Report information about physical volumes.
|
||||
.TP
|
||||
\fBpvscan\fP \(em Scan all disks for physical volumes.
|
||||
.TP
|
||||
\fBvgcfgbackup\fP \(em Backup volume group descriptor area.
|
||||
.TP
|
||||
\fBvgcfgrestore\fP \(em Restore volume group descriptor area.
|
||||
.TP
|
||||
\fBvgchange\fP \(em Change attributes of a volume group.
|
||||
.TP
|
||||
\fBvgck\fP \(em Check volume group metadata.
|
||||
.TP
|
||||
\fBvgconvert\fP \(em Convert volume group metadata format.
|
||||
.TP
|
||||
\fBvgcreate\fP \(em Create a volume group.
|
||||
.TP
|
||||
\fBvgdisplay\fP \(em Display attributes of volume groups.
|
||||
.TP
|
||||
\fBvgexport\fP \(em Make volume groups unknown to the system.
|
||||
.TP
|
||||
\fBvgextend\fP \(em Add physical volumes to a volume group.
|
||||
.TP
|
||||
\fBvgimport\fP \(em Make exported volume groups known to the system.
|
||||
.TP
|
||||
\fBvgmerge\fP \(em Merge two volume groups.
|
||||
.TP
|
||||
\fBvgmknodes\fP \(em Recreate volume group directory and logical volume special files
|
||||
.TP
|
||||
\fBvgreduce\fP \(em Reduce a volume group by removing one or more physical volumes.
|
||||
.TP
|
||||
\fBvgremove\fP \(em Remove a volume group.
|
||||
.TP
|
||||
\fBvgrename\fP \(em Rename a volume group.
|
||||
.TP
|
||||
\fBvgs\fP \(em Report information about volume groups.
|
||||
.TP
|
||||
\fBvgscan\fP \(em Scan all disks for volume groups and rebuild caches.
|
||||
.TP
|
||||
\fBvgsplit\fP \(em Split a volume group into two, moving any logical volumes from one volume group to another by moving entire physical volumes.
|
||||
.TP
|
||||
\fBlvchange\fP \(em Change attributes of a logical volume.
|
||||
.TP
|
||||
\fBlvconvert\fP \(em Convert a logical volume from linear to mirror or snapshot.
|
||||
.TP
|
||||
\fBlvcreate\fP \(em Create a logical volume in an existing volume group.
|
||||
.TP
|
||||
\fBlvdisplay\fP \(em Display attributes of a logical volume.
|
||||
.TP
|
||||
\fBlvextend\fP \(em Extend the size of a logical volume.
|
||||
.TP
|
||||
\fBlvmchange\fP \(em Change attributes of the logical volume manager.
|
||||
.TP
|
||||
\fBlvmdiskscan\fP \(em Scan for all devices visible to LVM2.
|
||||
.TP
|
||||
\fBlvmdump\fP \(em Create lvm2 information dumps for diagnostic purposes.
|
||||
.TP
|
||||
\fBlvreduce\fP \(em Reduce the size of a logical volume.
|
||||
.TP
|
||||
\fBlvremove\fP \(em Remove a logical volume.
|
||||
.TP
|
||||
\fBlvrename\fP \(em Rename a logical volume.
|
||||
.TP
|
||||
\fBlvresize\fP \(em Resize a logical volume.
|
||||
.TP
|
||||
\fBlvs\fP \(em Report information about logical volumes.
|
||||
.TP
|
||||
\fBlvscan\fP \(em Scan (all disks) for logical volumes.
|
||||
.TP
|
||||
The following commands are not implemented in LVM2 but might be in the future: lvmsadc, lvmsar, pvdata.
|
||||
.SH OPTIONS
|
||||
The following options are available for many of the commands.
|
||||
They are implemented generically and documented here rather
|
||||
|
||||
@@ -47,7 +47,8 @@ The lv_attr bits are:
|
||||
.RS
|
||||
.IP 1 3
|
||||
Volume type: (m)irrored, (M)irrored without initial sync, (o)rigin, (p)vmove, (s)napshot,
|
||||
invalid (S)napshot, (v)irtual
|
||||
invalid (S)napshot, (v)irtual, mirror (i)mage, mirror (I)mage out-of-sync,
|
||||
under (c)onversion
|
||||
.IP 2 3
|
||||
Permissions: (w)riteable, (r)ead-only
|
||||
.IP 3 3
|
||||
@@ -82,6 +83,7 @@ All sizes are output in these units: (h)uman-readable, (s)ectors, (b)ytes,
|
||||
of 1000 (S.I.) instead of 1024. Can also specify custom (u)nits e.g.
|
||||
\-\-units 3M
|
||||
.SH SEE ALSO
|
||||
.BR lvm (8),
|
||||
.BR lvdisplay (8),
|
||||
.BR pvs (8),
|
||||
.BR vgs (8)
|
||||
|
||||
@@ -8,7 +8,8 @@ pvchange \- change attributes of a physical volume
|
||||
[\-\-deltag Tag]
|
||||
[\-h/\-?/\-\-help]
|
||||
[\-t/\-\-test]
|
||||
[\-v/\-\-verbose] [\-a/\-\-all] [\-x/\-\-allocatable y/n] [PhysicalVolumePath...]
|
||||
[\-v/\-\-verbose] [\-a/\-\-all] [\-x/\-\-allocatable y/n]
|
||||
[\-u/\-\-uuid] [PhysicalVolumePath...]
|
||||
.SH DESCRIPTION
|
||||
pvchange allows you to change the allocation permissions of one or
|
||||
more physical volumes.
|
||||
@@ -19,6 +20,9 @@ See \fBlvm\fP for common options.
|
||||
If PhysicalVolumePath is not specified on the command line all
|
||||
physical volumes are searched for and used.
|
||||
.TP
|
||||
.I \-u, \-\-uuid
|
||||
Generate new random UUID for specified physical volumes.
|
||||
.TP
|
||||
.I \-x, \-\-allocatable y/n
|
||||
Enable or disable allocation of physical extents on this physical volume.
|
||||
.SH Example
|
||||
|
||||
@@ -15,6 +15,7 @@ pvcreate \- initialize a disk or partition for use by LVM
|
||||
.RB [ \-\-metadatasize size ]
|
||||
.RB [ \-\-restorefile file ]
|
||||
.RB [ \-\-setphysicalvolumesize size ]
|
||||
.RB [ \-u | \-\-uuid uuid ]
|
||||
.RB [ \-\-version ]
|
||||
.RB [ \-Z | \-\-zero y/n ]
|
||||
.IR PhysicalVolume " [" PhysicalVolume ...]
|
||||
|
||||
@@ -58,6 +58,7 @@ All sizes are output in these units: (h)uman-readable, (s)ectors, (b)ytes,
|
||||
of 1000 (S.I.) instead of 1024. Can also specify custom (u)nits e.g.
|
||||
\-\-units 3M
|
||||
.SH SEE ALSO
|
||||
.BR lvm (8),
|
||||
.BR pvdisplay (8),
|
||||
.BR lvs (8),
|
||||
.BR vgs (8)
|
||||
|
||||
@@ -11,6 +11,7 @@ vgchange \- change attributes of a volume group
|
||||
.RB [ \-a | \-\-available " [e|l] {" y | n }]
|
||||
.RB [ \-\-monitor " {" y | n }]
|
||||
.RB [ \-c | \-\-clustered " {" y | n }]
|
||||
.RB [ \-u | \-\-uuid ]
|
||||
.RB [ \-d | \-\-debug]
|
||||
.RB [ \-\-deltag
|
||||
.IR Tag ]
|
||||
@@ -70,6 +71,9 @@ If the cluster infrastructure is unavailable on a particular node at a
|
||||
particular time, you may still be able to use Volume Groups that
|
||||
are not marked as clustered.
|
||||
.TP
|
||||
.BR \-u ", " \-\-uuid
|
||||
Generate new random UUID for specified Volume Groups.
|
||||
.TP
|
||||
.BR \-\-monitor " " { y | n }
|
||||
Controls whether or not a mirrored logical volume is monitored by
|
||||
dmeventd, if it is installed.
|
||||
|
||||
@@ -71,6 +71,7 @@ All sizes are output in these units: (h)uman-readable, (s)ectors, (b)ytes,
|
||||
of 1000 (S.I.) instead of 1024. Can also specify custom (u)nits e.g.
|
||||
\-\-units 3M
|
||||
.SH SEE ALSO
|
||||
.BR lvm (8),
|
||||
.BR vgdisplay (8),
|
||||
.BR pvs (8),
|
||||
.BR lvs (8)
|
||||
|
||||
@@ -3,27 +3,53 @@
|
||||
vgsplit \- split a volume group into two
|
||||
.SH SYNOPSIS
|
||||
.B vgsplit
|
||||
[\-A/\-\-autobackup y/n]
|
||||
[\-d/\-\-debug]
|
||||
[\-h/\-?/\-\-help]
|
||||
[\-l/\-\-list]
|
||||
[\-M/\-\-metadatatype 1/2]
|
||||
[\-t/\-\-test]
|
||||
[\-v/\-\-verbose]
|
||||
ExistingVolumeGroupName NewVolumeGroupName
|
||||
.RB [ \-\-alloc
|
||||
.IR AllocationPolicy ]
|
||||
.RB [ \-A | \-\-autobackup " {" y | n }]
|
||||
.RB [ \-c | \-\-clustered " {" y | n }]
|
||||
.RB [ \-d | \-\-debug ]
|
||||
.RB [ \-h | \-\-help ]
|
||||
.RB [ \-l | \-\-maxlogicalvolumes
|
||||
.IR MaxLogicalVolumes ]
|
||||
.RB [ -M | \-\-metadatatype
|
||||
.IR type ]
|
||||
.RB [ -p | \-\-maxphysicalvolumes
|
||||
.IR MaxPhysicalVolumes ]
|
||||
.RB [ \-s | \-\-physicalextentsize
|
||||
.IR PhysicalExtentSize [ \fBkKmMgGtT\fR ]]
|
||||
.RB [ \-t | \-\-test ]
|
||||
.RB [ \-v | \-\-verbose ]
|
||||
SourceVolumeGroupName DestinationVolumeGroupName
|
||||
PhysicalVolumePath [PhysicalVolumePath...]
|
||||
.SH DESCRIPTION
|
||||
.B vgsplit
|
||||
creates
|
||||
.I NewVolumeGroupName
|
||||
and moves
|
||||
moves
|
||||
.IR PhysicalVolumePath (s)
|
||||
from
|
||||
.I ExistingVolumeGroupName
|
||||
into it.
|
||||
.I SourceVolumeGroupName
|
||||
into
|
||||
.I DestinationVolumeGroupName\fP.
|
||||
|
||||
If
|
||||
.I DestinationVolumeGroupName
|
||||
does not exist, a new volume group will be created. The default attributes
|
||||
for the new volume group can be specified with \fB\-\-alloc\fR,
|
||||
\fB\-\-clustered\fR, \fB\-\-maxlogicalvolumes\fR, \fB\-\-metadatatype\fR,
|
||||
\fB\-\-maxphysicalvolumes\fR, and \fB\-\-physicalextentsize\fR
|
||||
(see \fBvgcreate(8)\fR for a description of these options).
|
||||
If any of these options are not given, the default attribute(s) are taken
|
||||
from
|
||||
.I SourceVolumeGroupName\fP.
|
||||
|
||||
If
|
||||
.I DestinationVolumeGroupName
|
||||
does exist, it will be checked for compatibility with
|
||||
.I SourceVolumeGroupName
|
||||
before the physical volumes are moved.
|
||||
|
||||
Logical Volumes cannot be split between Volume Groups.
|
||||
Each existing Logical Volumes must be entirely on the Physical Volumes forming
|
||||
either the old or the new Volume Group.
|
||||
Each existing Logical Volume must be entirely on the Physical Volumes forming
|
||||
either the source or the destination Volume Group.
|
||||
.SH OPTIONS
|
||||
See \fBlvm\fP for common options.
|
||||
.SH SEE ALSO
|
||||
|
||||
@@ -20,4 +20,6 @@ include $(top_srcdir)/make.tmpl
|
||||
install:
|
||||
$(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) lvm_dump.sh \
|
||||
$(sbindir)/lvmdump
|
||||
$(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) fsadm.sh \
|
||||
$(sbindir)/fsadm
|
||||
|
||||
|
||||
365
scripts/fsadm.sh
Normal file
365
scripts/fsadm.sh
Normal file
@@ -0,0 +1,365 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2007 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Author: Zdenek Kabelac <zkabelac at redhat.com>
|
||||
#
|
||||
# Script for resizing devices (usable for LVM resize)
|
||||
#
|
||||
# Needed utilities:
|
||||
# mount, umount, grep, readlink, blockdev, blkid, fsck, xfs_check
|
||||
#
|
||||
# ext2/ext3: resize2fs, tune2fs
|
||||
# reiserfs: resize_reiserfs, reiserfstune
|
||||
# xfs: xfs_growfs, xfs_info
|
||||
#
|
||||
|
||||
TOOL=fsadm
|
||||
|
||||
PATH=/sbin:/usr/sbin:/bin:/usr/sbin:$PATH
|
||||
|
||||
# utilities
|
||||
TUNE_EXT=tune2fs
|
||||
RESIZE_EXT=resize2fs
|
||||
TUNE_REISER=reiserfstune
|
||||
RESIZE_REISER=resize_reiserfs
|
||||
TUNE_XFS=xfs_info
|
||||
RESIZE_XFS=xfs_growfs
|
||||
|
||||
MOUNT=mount
|
||||
UMOUNT=umount
|
||||
MKDIR=mkdir
|
||||
RMDIR=rmdir
|
||||
BLOCKDEV=blockdev
|
||||
BLKID=blkid
|
||||
GREP=grep
|
||||
READLINK=readlink
|
||||
FSCK=fsck
|
||||
XFS_CHECK=xfs_check
|
||||
|
||||
YES=
|
||||
DRY=0
|
||||
VERB=0
|
||||
FORCE=
|
||||
EXTOFF=0
|
||||
FSTYPE=unknown
|
||||
VOLUME=unknown
|
||||
TEMPDIR="${TMPDIR:-/tmp}/${TOOL}_${RANDOM}$$/m"
|
||||
BLOCKSIZE=
|
||||
BLOCKCOUNT=
|
||||
MOUNTPOINT=
|
||||
MOUNTED=
|
||||
REMOUNT=
|
||||
|
||||
IFS_OLD=$IFS
|
||||
|
||||
tool_usage() {
|
||||
echo "${TOOL}: Utility to resize or check the filesystem on a device"
|
||||
echo
|
||||
echo " ${TOOL} [options] check device"
|
||||
echo " - Check the filesystem on device using fsck"
|
||||
echo
|
||||
echo " ${TOOL} [options] resize device [new_size[BKMGTPE]]"
|
||||
echo " - Change the size of the filesystem on device to new_size"
|
||||
echo
|
||||
echo " Options:"
|
||||
echo " -h | --help Show this help message"
|
||||
echo " -v | --verbose Be verbose"
|
||||
echo " -e | --ext-offline unmount filesystem before Ext2/3 resize"
|
||||
echo " -f | --force Bypass sanity checks"
|
||||
echo " -n | --dry-run Print commands without running them"
|
||||
echo " -y | --yes Answer \"yes\" at any prompts"
|
||||
echo
|
||||
echo " new_size - Absolute number of filesystem blocks to be in the filesystem,"
|
||||
echo " or an absolute size using a suffix (in powers of 1024)."
|
||||
echo " If new_size is not supplied, the whole device is used."
|
||||
|
||||
exit
|
||||
}
|
||||
|
||||
verbose() {
|
||||
test "$VERB" -eq 1 && echo "$TOOL: $@" || true
|
||||
}
|
||||
|
||||
error() {
|
||||
echo "$TOOL: $@" >&2
|
||||
cleanup 1
|
||||
}
|
||||
|
||||
dry() {
|
||||
if [ "$DRY" -ne 0 ]; then
|
||||
verbose "Dry execution $@"
|
||||
return 0
|
||||
fi
|
||||
verbose "Executing $@"
|
||||
$@
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
trap '' 2
|
||||
# reset MOUNTPOINT - avoid recursion
|
||||
test "$MOUNTPOINT" = "$TEMPDIR" && MOUNTPOINT="" temp_umount
|
||||
if [ -n "$REMOUNT" ]; then
|
||||
verbose "Remounting unmounted filesystem back"
|
||||
dry $MOUNT "$VOLUME" "$MOUNTED"
|
||||
fi
|
||||
IFS=$IFS_OLD
|
||||
trap 2
|
||||
exit $1
|
||||
}
|
||||
|
||||
# convert parameter from Exa/Peta/Tera/Giga/Mega/Kilo/Bytes and blocks
|
||||
# (2^(60/50/40/30/20/10/0))
|
||||
decode_size() {
|
||||
case "$1" in
|
||||
*[eE]) NEWSIZE=$(( ${1%[eE]} * 1152921504606846976 )) ;;
|
||||
*[pP]) NEWSIZE=$(( ${1%[pP]} * 1125899906842624 )) ;;
|
||||
*[tT]) NEWSIZE=$(( ${1%[tT]} * 1099511627776 )) ;;
|
||||
*[gG]) NEWSIZE=$(( ${1%[gG]} * 1073741824 )) ;;
|
||||
*[mM]) NEWSIZE=$(( ${1%[mM]} * 1048576 )) ;;
|
||||
*[kK]) NEWSIZE=$(( ${1%[kK]} * 1024 )) ;;
|
||||
*[bB]) NEWSIZE=${1%[bB]} ;;
|
||||
*) NEWSIZE=$(( $1 * $2 )) ;;
|
||||
esac
|
||||
#NEWBLOCKCOUNT=$(round_block_size $NEWSIZE $2)
|
||||
NEWBLOCKCOUNT=$(( $NEWSIZE / $2 ))
|
||||
}
|
||||
|
||||
# detect filesystem on the given device
|
||||
# dereference device name if it is symbolic link
|
||||
detect_fs() {
|
||||
VOLUME=$($READLINK -e -n "$1") || error "Cannot get readlink $1"
|
||||
# use /dev/null as cache file to be sure about the result
|
||||
FSTYPE=$($BLKID -c /dev/null -o value -s TYPE "$VOLUME") || error "Cannot get FSTYPE of \"$VOLUME\""
|
||||
verbose "\"$FSTYPE\" filesystem found on \"$VOLUME\""
|
||||
}
|
||||
|
||||
# check if the given device is already mounted and where
|
||||
detect_mounted() {
|
||||
$MOUNT >/dev/null || error "Cannot detect mounted device $VOLUME"
|
||||
MOUNTED=$($MOUNT | $GREP "$VOLUME")
|
||||
MOUNTED=${MOUNTED##* on }
|
||||
MOUNTED=${MOUNTED% type *} # allow type in the mount name
|
||||
test -n "$MOUNTED"
|
||||
}
|
||||
|
||||
# get the full size of device in bytes
|
||||
detect_device_size() {
|
||||
DEVSIZE=$($BLOCKDEV --getsize64 "$VOLUME") || error "Cannot read device \"$VOLUME\""
|
||||
}
|
||||
|
||||
# round up $1 / $2
|
||||
# could be needed to gaurantee 'at least given size'
|
||||
# but it makes many troubles
|
||||
round_up_block_size() {
|
||||
echo $(( ($1 + $2 - 1) / $2 ))
|
||||
}
|
||||
|
||||
temp_mount() {
|
||||
dry $MKDIR -p -m 0000 "$TEMPDIR" || error "Failed to create $TEMPDIR"
|
||||
dry $MOUNT "$VOLUME" "$TEMPDIR" || error "Failed to mount $TEMPDIR"
|
||||
}
|
||||
|
||||
temp_umount() {
|
||||
dry $UMOUNT "$TEMPDIR" || error "Failed to umount $TEMPDIR"
|
||||
dry $RMDIR "${TEMPDIR}" || error "Failed to remove $TEMPDIR"
|
||||
dry $RMDIR "${TEMPDIR%%m}" || error "Failed to remove ${TEMPDIR%%m}"
|
||||
}
|
||||
|
||||
yes_no() {
|
||||
echo -n "$@? [Y|n] "
|
||||
if [ -n "$YES" ]; then
|
||||
ANS="y"; echo -n $ANS
|
||||
else
|
||||
read -n 1 ANS
|
||||
fi
|
||||
test -n "$ANS" && echo
|
||||
case "$ANS" in
|
||||
"y" | "Y" | "" ) return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
try_umount() {
|
||||
yes_no "Do you want to unmount \"$MOUNTED\"" && dry $UMOUNT "$MOUNTED" && return 0
|
||||
error "Cannot proceed test with mounted filesystem \"$MOUNTED\""
|
||||
}
|
||||
|
||||
validate_parsing() {
|
||||
test -n "$BLOCKSIZE" -a -n "$BLOCKCOUNT" || error "Cannot parse $1 output"
|
||||
}
|
||||
####################################
|
||||
# Resize ext2/ext3 filesystem
|
||||
# - unmounted or mounted for upsize
|
||||
# - unmounted for downsize
|
||||
####################################
|
||||
resize_ext() {
|
||||
verbose "Parsing $TUNE_EXT -l \"$VOLUME\""
|
||||
for i in $($TUNE_EXT -l "$VOLUME"); do
|
||||
case "$i" in
|
||||
"Block size"*) BLOCKSIZE=${i##* } ;;
|
||||
"Block count"*) BLOCKCOUNT=${i##* } ;;
|
||||
esac
|
||||
done
|
||||
validate_parsing $TUNE_EXT
|
||||
decode_size $1 $BLOCKSIZE
|
||||
FSFORCE=$FORCE
|
||||
|
||||
if [ "$NEWBLOCKCOUNT" -lt "$BLOCKCOUNT" -o "$EXTOFF" -eq 1 ]; then
|
||||
detect_mounted && verbose "$RESIZE_EXT needs unmounted filesystem" && try_umount
|
||||
REMOUNT=$MOUNTED
|
||||
# CHECKME: after umount resize2fs requires fsck or -f flag.
|
||||
FSFORCE="-f"
|
||||
fi
|
||||
|
||||
verbose "Resizing \"$VOLUME\" $BLOCKCOUNT -> $NEWBLOCKCOUNT blocks ($NEWSIZE bytes, bs:$BLOCKSIZE)"
|
||||
dry $RESIZE_EXT $FSFORCE "$VOLUME" $NEWBLOCKCOUNT
|
||||
}
|
||||
|
||||
#############################
|
||||
# Resize reiserfs filesystem
|
||||
# - unmounted for upsize
|
||||
# - unmounted for downsize
|
||||
#############################
|
||||
resize_reiser() {
|
||||
detect_mounted
|
||||
if [ -n "$MOUNTED" ]; then
|
||||
verbose "ReiserFS resizes only unmounted filesystem"
|
||||
try_umount
|
||||
REMOUNT=$MOUNTED
|
||||
fi
|
||||
verbose "Parsing $TUNE_REISER \"$VOLUME\""
|
||||
for i in $($TUNE_REISER "$VOLUME"); do
|
||||
case "$i" in
|
||||
"Blocksize"*) BLOCKSIZE=${i##*: } ;;
|
||||
"Count of blocks"*) BLOCKCOUNT=${i##*: } ;;
|
||||
esac
|
||||
done
|
||||
validate_parsing $TUNE_REISER
|
||||
decode_size $1 $BLOCKSIZE
|
||||
verbose "Resizing \"$VOLUME\" $BLOCKCOUNT -> $NEWBLOCKCOUNT blocks ($NEWSIZE bytes, bs: $NEWBLOCKCOUNT)"
|
||||
if [ -n "$YES" ]; then
|
||||
dry echo y | $RESIZE_REISER -s $NEWSIZE "$VOLUME"
|
||||
else
|
||||
dry $RESIZE_REISER -s $NEWSIZE "$VOLUME"
|
||||
fi
|
||||
}
|
||||
|
||||
########################
|
||||
# Resize XFS filesystem
|
||||
# - mounted for upsize
|
||||
# - can not downsize
|
||||
########################
|
||||
resize_xfs() {
|
||||
detect_mounted
|
||||
MOUNTPOINT=$MOUNTED
|
||||
if [ -z "$MOUNTED" ]; then
|
||||
MOUNTPOINT=$TEMPDIR
|
||||
temp_mount || error "Cannot mount Xfs filesystem"
|
||||
fi
|
||||
verbose "Parsing $TUNE_XFS \"$MOUNTPOINT\""
|
||||
for i in $($TUNE_XFS "$MOUNTPOINT"); do
|
||||
case "$i" in
|
||||
"data"*) BLOCKSIZE=${i##*bsize=} ; BLOCKCOUNT=${i##*blocks=} ;;
|
||||
esac
|
||||
done
|
||||
BLOCKSIZE=${BLOCKSIZE%%[^0-9]*}
|
||||
BLOCKCOUNT=${BLOCKCOUNT%%[^0-9]*}
|
||||
validate_parsing $TUNE_XFS
|
||||
decode_size $1 $BLOCKSIZE
|
||||
if [ $NEWBLOCKCOUNT -gt $BLOCKCOUNT ]; then
|
||||
verbose "Resizing Xfs mounted on \"$MOUNTPOINT\" to fill device \"$VOLUME\""
|
||||
dry $RESIZE_XFS $MOUNTPOINT
|
||||
elif [ $NEWBLOCKCOUNT -eq $BLOCKCOUNT ]; then
|
||||
verbose "Xfs filesystem already has the right size"
|
||||
else
|
||||
error "Xfs filesystem shrinking is unsupported"
|
||||
fi
|
||||
}
|
||||
|
||||
####################
|
||||
# Resize filesystem
|
||||
####################
|
||||
resize() {
|
||||
detect_fs "$1"
|
||||
detect_device_size
|
||||
verbose "Device \"$VOLUME\" has $DEVSIZE bytes"
|
||||
# if the size parameter is missing use device size
|
||||
NEWSIZE=$2
|
||||
test -z "$NEWSIZE" && NEWSIZE=${DEVSIZE}b
|
||||
trap cleanup 2
|
||||
#IFS=$'\n' # don't use bash-ism ??
|
||||
IFS="$(printf \"\\n\")" # needed for parsing output
|
||||
case "$FSTYPE" in
|
||||
"ext3"|"ext2") resize_ext $NEWSIZE ;;
|
||||
"reiserfs") resize_reiser $NEWSIZE ;;
|
||||
"xfs") resize_xfs $NEWSIZE ;;
|
||||
*) error "Filesystem \"$FSTYPE\" on device \"$VOLUME\" is not supported by this tool" ;;
|
||||
esac || error "Resize $FSTYPE failed"
|
||||
cleanup
|
||||
}
|
||||
|
||||
###################
|
||||
# Check filesystem
|
||||
###################
|
||||
check() {
|
||||
detect_fs "$1"
|
||||
case "$FSTYPE" in
|
||||
"xfs") dry $XFS_CHECK "$VOLUME" ;;
|
||||
*) dry $FSCK $YES "$VOLUME" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
#############################
|
||||
# start point of this script
|
||||
# - parsing parameters
|
||||
#############################
|
||||
|
||||
# test some prerequisities
|
||||
test -n "$TUNE_EXT" -a -n "$RESIZE_EXT" -a -n "$TUNE_REISER" -a -n "$RESIZE_REISER" \
|
||||
-a -n "$TUNE_XFS" -a -n "$RESIZE_XFS" -a -n "$MOUNT" -a -n "$UMOUNT" -a -n "$MKDIR" \
|
||||
-a -n "$RMDIR" -a -n "$BLOCKDEV" -a -n "$BLKID" -a -n "$GREP" -a -n "$READLINK" \
|
||||
-a -n "$FSCK" -a -n "$XFS_CHECK" || error "Required command definitions in the script are missing!"
|
||||
$($READLINK -e -n / >/dev/null 2>&1) || error "$READLINK does not support options -e -n"
|
||||
TEST64BIT=$(( 1000 * 1000000000000 ))
|
||||
test $TEST64BIT -eq 1000000000000000 || error "Shell does not handle 64bit arithmetic"
|
||||
$(echo Y | $GREP Y >/dev/null) || error "Grep does not work properly"
|
||||
|
||||
|
||||
if [ "$1" = "" ] ; then
|
||||
tool_usage
|
||||
fi
|
||||
|
||||
while [ "$1" != "" ]
|
||||
do
|
||||
case "$1" in
|
||||
"-h"|"--help") tool_usage ;;
|
||||
"-v"|"--verbose") VERB=1 ;;
|
||||
"-n"|"--dry-run") DRY=1 ;;
|
||||
"-f"|"--force") FORCE="-f" ;;
|
||||
"-e"|"--ext-offline") EXTOFF=1 ;;
|
||||
"-y"|"--yes") YES="-y" ;;
|
||||
"check") shift; CHECK=$1 ;;
|
||||
"resize") shift; RESIZE=$1; shift; NEWSIZE=$1 ;;
|
||||
*) error "Wrong argument \"$1\". (see: $TOOL --help)"
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -n "$CHECK" ]; then
|
||||
check "$CHECK"
|
||||
elif [ -n "$RESIZE" ]; then
|
||||
resize "$RESIZE" "$NEWSIZE"
|
||||
else
|
||||
error "Missing command. (see: $TOOL --help)"
|
||||
fi
|
||||
@@ -56,6 +56,7 @@ $(T): init.sh
|
||||
for i in lvm $$(cat $(top_srcdir)/tools/.commands); do \
|
||||
ln -s ../lvm-wrapper bin/$$i; \
|
||||
done
|
||||
test -n "@DMDIR@" && ln -s "@DMDIR@/dmsetup/dmsetup" bin/dmsetup
|
||||
touch $@
|
||||
|
||||
lvm-wrapper: Makefile
|
||||
|
||||
@@ -50,6 +50,11 @@ loop_setup_()
|
||||
return 0;
|
||||
}
|
||||
|
||||
check_vg_field_()
|
||||
{
|
||||
return $(test $(vgs --noheadings -o $2 $1) == $3)
|
||||
}
|
||||
|
||||
check_pv_size_()
|
||||
{
|
||||
return $(test $(pvs --noheadings -o pv_free $1) == $2)
|
||||
@@ -65,7 +70,8 @@ dmsetup_has_dm_devdir_support_()
|
||||
# Detect support for the envvar. If it's supported, the
|
||||
# following command will fail with the expected diagnostic.
|
||||
out=$(DM_DEV_DIR=j dmsetup version 2>&1)
|
||||
test "$?:$out" = "1:Invalid DM_DEV_DIR envvar value."
|
||||
test "$?:$out" = "1:Invalid DM_DEV_DIR envvar value." -o \
|
||||
"$?:$out" = "1:Invalid DM_DEV_DIR environment variable value."
|
||||
}
|
||||
|
||||
# set up private /dev and /etc
|
||||
|
||||
@@ -67,7 +67,7 @@ test_expect_success \
|
||||
# Exercise the range overlap code. Allocate every 2 extents.
|
||||
#
|
||||
# Physical Extents
|
||||
# 1 2
|
||||
# 1 2
|
||||
#012345678901234567890123
|
||||
#
|
||||
#aaXXaaXXaaXXaaXXaaXXaaXX - (a)llocated
|
||||
@@ -88,7 +88,8 @@ test_expect_success \
|
||||
'Reset LV to 12 extents, allocate every other 2 extents' \
|
||||
'create_pvs=`for i in $(seq 0 4 20); do echo -n "\$d1:$i-$(($i + 1)) "; done` &&
|
||||
lvremove -f $vg/$lv; test $? = 0 &&
|
||||
lvcreate -l 12 -n $lv $vg $create_pvs; test $? = 0'
|
||||
lvcreate -l 12 -n $lv $vg $create_pvs; test $? = 0 &&
|
||||
check_lv_size_ $vg/$lv "48.00M"'
|
||||
|
||||
test_expect_success \
|
||||
'lvextend with partially allocated PVs and extents 100%PVS with PE ranges' \
|
||||
|
||||
307
test/t-mirror-basic.sh
Executable file
307
test/t-mirror-basic.sh
Executable file
@@ -0,0 +1,307 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2007 NEC Corporation
|
||||
#
|
||||
# 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
test_description="ensure that basic operations on mirrored LV works"
|
||||
privileges_required_=1
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
dmsetup_has_dm_devdir_support_ ||
|
||||
{
|
||||
say "Your version of dmsetup lacks support for changing DM_DEVDIR."
|
||||
say "Skipping this test"
|
||||
exit 0
|
||||
}
|
||||
|
||||
cleanup_()
|
||||
{
|
||||
test -n "$vg" && {
|
||||
lvremove -ff $vg
|
||||
vgremove $vg
|
||||
} > /dev/null
|
||||
test -n "$pvs" && {
|
||||
pvremove $pvs > /dev/null
|
||||
for d in $pvs; do
|
||||
dmsetup remove $(basename $d)
|
||||
done
|
||||
}
|
||||
losetup -d $lodev
|
||||
rm -f $lofile
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# config
|
||||
|
||||
nr_pvs=5
|
||||
pvsize=$((80 * 1024 * 2))
|
||||
|
||||
vg=mirror-basic-vg-$$
|
||||
lv1=lv1
|
||||
lv2=lv2
|
||||
lv3=lv3
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Utilities
|
||||
|
||||
pv_()
|
||||
{
|
||||
echo "$G_dev_/mapper/pv$1"
|
||||
}
|
||||
|
||||
lvdev_()
|
||||
{
|
||||
echo "$G_dev_/$1/$2"
|
||||
}
|
||||
|
||||
mimages_are_redundant_ ()
|
||||
{
|
||||
local vg=$1
|
||||
local lv=$vg/$2
|
||||
local i
|
||||
|
||||
rm -f out
|
||||
for i in $(lvs -odevices --noheadings $lv | sed 's/([^)]*)//g; s/,/ /g'); do
|
||||
lvs -a -odevices --noheadings $vg/$i | sed 's/([^)]*)//g; s/,/ /g' | \
|
||||
sort | uniq >> out
|
||||
done
|
||||
|
||||
# if any duplication is found, it's not redundant
|
||||
sort out | uniq -d | grep . && return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
lv_is_contiguous_ ()
|
||||
{
|
||||
local lv=$1
|
||||
|
||||
# if the lv has multiple segments, it's not contiguous
|
||||
[ $(lvs -a --segments --noheadings $lv | wc -l) -ne 1 ] && return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
lv_is_clung_ ()
|
||||
{
|
||||
local lv=$1
|
||||
|
||||
# if any duplication is found, it's not redundant
|
||||
[ $(lvs -a -odevices --noheadings $lv | sed 's/([^)]*)//g' | \
|
||||
sort | uniq | wc -l) -ne 1 ] && return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
mimages_are_contiguous_ ()
|
||||
{
|
||||
local vg=$1
|
||||
local lv=$vg/$2
|
||||
local i
|
||||
|
||||
for i in $(lvs -odevices --noheadings $lv | sed 's/([^)]*)//g; s/,/ /g'); do
|
||||
lv_is_contiguous_ $vg/$i || return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
mimages_are_clung_ ()
|
||||
{
|
||||
local vg=$1
|
||||
local lv=$vg/$2
|
||||
local i
|
||||
|
||||
for i in $(lvs -odevices --noheadings $lv | sed 's/([^)]*)//g; s/,/ /g'); do
|
||||
lv_is_clung_ $vg/$i || return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
mirrorlog_is_on_()
|
||||
{
|
||||
local lv="$1"_mlog
|
||||
shift 1
|
||||
lvs -a -odevices --noheadings $lv | sed 's/,/\n/g' > out
|
||||
for d in $*; do grep "$d(" out || return 1; done
|
||||
for d in $*; do grep -v "$d(" out > out2; mv out2 out; done
|
||||
grep . out && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
save_dev_sum_()
|
||||
{
|
||||
mkfs.ext3 $1 > /dev/null &&
|
||||
md5sum $1 > md5.$(basename $1)
|
||||
}
|
||||
|
||||
check_dev_sum_()
|
||||
{
|
||||
md5sum $1 > md5.tmp && cmp md5.$(basename $1) md5.tmp
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Initialize PVs and VGs
|
||||
|
||||
test_expect_success \
|
||||
'set up temp file and loopback device' \
|
||||
'lofile=$(pwd)/lofile && lodev=$(loop_setup_ "$lofile")'
|
||||
|
||||
offset=0
|
||||
pvs=
|
||||
for n in $(seq 1 $nr_pvs); do
|
||||
test_expect_success \
|
||||
"create pv$n" \
|
||||
'echo "0 $pvsize linear $lodev $offset" > in &&
|
||||
dmsetup create pv$n < in'
|
||||
offset=$(($offset + $pvsize))
|
||||
done
|
||||
|
||||
for n in $(seq 1 $nr_pvs); do
|
||||
pvs="$pvs $(pv_ $n)"
|
||||
done
|
||||
|
||||
test_expect_success \
|
||||
"Run this: pvcreate $pvs" \
|
||||
'pvcreate $pvs'
|
||||
|
||||
test_expect_success \
|
||||
'set up a VG' \
|
||||
'vgcreate $vg $pvs'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Common environment setup/cleanup for each sub testcases
|
||||
|
||||
prepare_lvs_()
|
||||
{
|
||||
lvremove -ff $vg;
|
||||
:
|
||||
}
|
||||
|
||||
check_and_cleanup_lvs_()
|
||||
{
|
||||
lvs -a -o+devices $vg &&
|
||||
lvremove -ff $vg
|
||||
}
|
||||
|
||||
test_expect_success "check environment setup/cleanup" \
|
||||
'prepare_lvs_ &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# mirrored LV tests
|
||||
|
||||
# ---
|
||||
# create
|
||||
|
||||
test_expect_success "create 2-way mirror with disklog from 3 PVs" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "create 2-way mirror with corelog from 2 PVs" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $(pv_ 1) $(pv_ 2) &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "create 3-way mirror with disklog from 4 PVs" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m2 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 4) $(pv_ 3):0-1 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# convert
|
||||
|
||||
test_expect_success "convert from linear to 2-way mirror" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -n $lv1 $vg $(pv_ 1) &&
|
||||
lvconvert -m+1 $vg/$lv1 $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "convert from 2-way mirror to linear" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
lvconvert -m-1 $vg/$lv1 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "convert from disklog to corelog" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
lvconvert --mirrorlog core $vg/$lv1 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "convert from corelog to disklog" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $(pv_ 1) $(pv_ 2) &&
|
||||
lvconvert --mirrorlog disk $vg/$lv1 $(pv_ 3):0-1 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# resize
|
||||
|
||||
test_expect_success "extend 2-way mirror" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
lvchange -an $vg/$lv1 &&
|
||||
lvextend -l+2 $vg/$lv1 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mimages_are_contiguous_ $vg $lv1 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "reduce 2-way mirror" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l4 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
lvchange -an $vg/$lv1 &&
|
||||
lvreduce -l-2 $vg/$lv1 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "extend 2-way mirror (cling if not contiguous)" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
lvcreate -l1 -n $lv2 $vg $(pv_ 1) &&
|
||||
lvcreate -l1 -n $lv3 $vg $(pv_ 2) &&
|
||||
lvchange -an $vg/$lv1 &&
|
||||
lvextend -l+2 $vg/$lv1 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mimages_are_clung_ $vg $lv1 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# failure cases
|
||||
|
||||
test_expect_failure "create 2-way mirror with disklog from 2 PVs" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2)'
|
||||
test_expect_success "(cleanup previous test)" \
|
||||
'check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_failure "convert linear to 2-way mirror with 1 PV" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -n $lv1 $vg $(pv_ 1) &&
|
||||
lvconvert -m+1 --mirrorlog core $vg/$lv1 $(pv_ 1)'
|
||||
test_expect_success "(cleanup previous test)" \
|
||||
'check_and_cleanup_lvs_'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
test_done
|
||||
392
test/t-mirror-lvconvert.sh
Executable file
392
test/t-mirror-lvconvert.sh
Executable file
@@ -0,0 +1,392 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2007 NEC Corporation
|
||||
#
|
||||
# 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
test_description="ensure that lvconvert for mirrored LV works"
|
||||
privileges_required_=1
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
dmsetup_has_dm_devdir_support_ ||
|
||||
{
|
||||
say "Your version of dmsetup lacks support for changing DM_DEVDIR."
|
||||
say "Skipping this test"
|
||||
exit 0
|
||||
}
|
||||
|
||||
cleanup_()
|
||||
{
|
||||
test -n "$vg" && {
|
||||
lvremove -ff $vg
|
||||
vgremove $vg
|
||||
} > /dev/null
|
||||
test -n "$pvs" && {
|
||||
pvremove $pvs > /dev/null
|
||||
for d in $pvs; do
|
||||
dmsetup remove $(basename $d)
|
||||
done
|
||||
}
|
||||
losetup -d $lodev
|
||||
rm -f $lofile
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# config
|
||||
|
||||
nr_pvs=5
|
||||
pvsize=$((80 * 1024 * 2))
|
||||
|
||||
vg=mirror-lvconvert-vg-$$
|
||||
lv1=lv1
|
||||
lv2=lv2
|
||||
lv3=lv3
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Utilities
|
||||
|
||||
pv_()
|
||||
{
|
||||
echo "$G_dev_/mapper/pv$1"
|
||||
}
|
||||
|
||||
lvdev_()
|
||||
{
|
||||
echo "$G_dev_/$1/$2"
|
||||
}
|
||||
|
||||
mimages_are_redundant_ ()
|
||||
{
|
||||
local vg=$1
|
||||
local lv=$vg/$2
|
||||
local i
|
||||
|
||||
rm -f out
|
||||
for i in $(lvs -odevices --noheadings $lv | sed 's/([^)]*)//g; s/,/ /g'); do
|
||||
lvs -a -odevices --noheadings $vg/$i | sed 's/([^)]*)//g; s/,/ /g' | \
|
||||
sort | uniq >> out
|
||||
done
|
||||
|
||||
# if any duplication is found, it's not redundant
|
||||
sort out | uniq -d | grep . && return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
lv_is_contiguous_ ()
|
||||
{
|
||||
local lv=$1
|
||||
|
||||
# if the lv has multiple segments, it's not contiguous
|
||||
[ $(lvs -a --segments --noheadings $lv | wc -l) -ne 1 ] && return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
lv_is_clung_ ()
|
||||
{
|
||||
local lv=$1
|
||||
|
||||
# if any duplication is found, it's not redundant
|
||||
[ $(lvs -a -odevices --noheadings $lv | sed 's/([^)]*)//g' | \
|
||||
sort | uniq | wc -l) -ne 1 ] && return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
mimages_are_contiguous_ ()
|
||||
{
|
||||
local vg=$1
|
||||
local lv=$vg/$2
|
||||
local i
|
||||
|
||||
for i in $(lvs -odevices --noheadings $lv | sed 's/([^)]*)//g; s/,/ /g'); do
|
||||
lv_is_contiguous_ $vg/$i || return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
mimages_are_clung_ ()
|
||||
{
|
||||
local vg=$1
|
||||
local lv=$vg/$2
|
||||
local i
|
||||
|
||||
for i in $(lvs -odevices --noheadings $lv | sed 's/([^)]*)//g; s/,/ /g'); do
|
||||
lv_is_clung_ $vg/$i || return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
mirrorlog_is_on_()
|
||||
{
|
||||
local lv="$1"_mlog
|
||||
shift 1
|
||||
lvs -a -odevices --noheadings $lv | sed 's/,/\n/g' > out
|
||||
for d in $*; do grep "$d(" out || return 1; done
|
||||
for d in $*; do grep -v "$d(" out > out2; mv out2 out; done
|
||||
grep . out && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
save_dev_sum_()
|
||||
{
|
||||
mkfs.ext3 $1 > /dev/null &&
|
||||
md5sum $1 > md5.$(basename $1)
|
||||
}
|
||||
|
||||
check_dev_sum_()
|
||||
{
|
||||
md5sum $1 > md5.tmp && cmp md5.$(basename $1) md5.tmp
|
||||
}
|
||||
|
||||
check_mirror_count_()
|
||||
{
|
||||
local lv=$1
|
||||
local mirrors=$2
|
||||
[ "$mirrors" -eq "$(lvs --noheadings -ostripes $lv)" ]
|
||||
}
|
||||
|
||||
check_mirror_log_()
|
||||
{
|
||||
local lv=$1
|
||||
local mlog=$(lvs --noheadings -omirror_log $lv | sed -e 's/ //g')
|
||||
[ "$(basename $lv)_mlog" = "$mlog" ]
|
||||
}
|
||||
|
||||
wait_conversion_()
|
||||
{
|
||||
local lv=$1
|
||||
while (lvs --noheadings -oattr "$lv" | grep -q '^ *c'); do sleep 1; done
|
||||
}
|
||||
|
||||
check_no_tmplvs_()
|
||||
{
|
||||
local lv=$1
|
||||
lvs -a --noheadings -oname $(dirname $lv) > out
|
||||
! grep tmp out
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Initialize PVs and VGs
|
||||
|
||||
test_expect_success \
|
||||
'set up temp file and loopback device' \
|
||||
'lofile=$(pwd)/lofile && lodev=$(loop_setup_ "$lofile")'
|
||||
|
||||
offset=0
|
||||
pvs=
|
||||
for n in $(seq 1 $nr_pvs); do
|
||||
test_expect_success \
|
||||
"create pv$n" \
|
||||
'echo "0 $pvsize linear $lodev $offset" > in &&
|
||||
dmsetup create pv$n < in'
|
||||
offset=$(($offset + $pvsize))
|
||||
done
|
||||
|
||||
for n in $(seq 1 $nr_pvs); do
|
||||
pvs="$pvs $(pv_ $n)"
|
||||
done
|
||||
|
||||
test_expect_success \
|
||||
"Run this: pvcreate $pvs" \
|
||||
'pvcreate $pvs'
|
||||
|
||||
test_expect_success \
|
||||
'set up a VG' \
|
||||
'vgcreate $vg $pvs'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Common environment setup/cleanup for each sub testcases
|
||||
|
||||
prepare_lvs_()
|
||||
{
|
||||
lvremove -ff $vg;
|
||||
:
|
||||
}
|
||||
|
||||
check_and_cleanup_lvs_()
|
||||
{
|
||||
lvs -a -o+devices $vg &&
|
||||
lvremove -ff $vg
|
||||
}
|
||||
|
||||
test_expect_success "check environment setup/cleanup" \
|
||||
'prepare_lvs_ &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# mirrored LV tests
|
||||
|
||||
# ---
|
||||
# add mirror to mirror
|
||||
|
||||
test_expect_success "add 1 mirror" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
check_mirror_log_ $vg/$lv1 &&
|
||||
lvconvert -m+1 -i1 $vg/$lv1 $(pv_ 4) &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 3 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "add 2 mirrors" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
check_mirror_log_ $vg/$lv1 &&
|
||||
lvconvert -m+2 -i1 $vg/$lv1 $(pv_ 4) $(pv_ 5) &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 4 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "add 1 mirror to core log mirror" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $(pv_ 1) $(pv_ 2) &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
!(check_mirror_log_ $vg/$lv1) &&
|
||||
lvconvert -m+1 -i1 --mirrorlog core $vg/$lv1 $(pv_ 4) &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 3 &&
|
||||
!(check_mirror_log_ $vg/$lv1) &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "add 2 mirrors to core log mirror" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $(pv_ 1) $(pv_ 2) &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
!(check_mirror_log_ $vg/$lv1) &&
|
||||
lvconvert -m+2 -i1 --mirrorlog core $vg/$lv1 $(pv_ 4) $(pv_ 5) &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 4 &&
|
||||
!(check_mirror_log_ $vg/$lv1) &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# add to converting mirror
|
||||
|
||||
test_expect_success "add 1 mirror then add 1 more mirror during conversion" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
check_mirror_log_ $vg/$lv1 &&
|
||||
lvconvert -m+1 -b -i100 $vg/$lv1 $(pv_ 4) &&
|
||||
lvconvert -m+1 -i3 $vg/$lv1 $(pv_ 5) &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 4 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# add mirror and disk log
|
||||
|
||||
test_expect_success "add 1 mirror and disk log" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $(pv_ 1) $(pv_ 2) &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
!(check_mirror_log_ $vg/$lv1) &&
|
||||
lvconvert -m+1 --mirrorlog disk -i1 $vg/$lv1 $(pv_ 4) $(pv_ 3):0-1 &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 3 &&
|
||||
check_mirror_log_ $vg/$lv1 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# check polldaemon restarts
|
||||
|
||||
test_expect_success "convert inactive mirror and start polling" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
lvchange -an $vg/$lv1 &&
|
||||
lvconvert -m+1 $vg/$lv1 $(pv_ 4) &&
|
||||
lvchange -ay $vg/$lv1 &&
|
||||
wait_conversion_ $vg/$lv1 &&
|
||||
lvs -a &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# removal during conversion
|
||||
|
||||
test_expect_success "remove newly added mirror" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
check_mirror_log_ $vg/$lv1 &&
|
||||
lvconvert -m+1 -b -i100 $vg/$lv1 $(pv_ 4) &&
|
||||
lvconvert -m-1 $vg/$lv1 $(pv_ 4) &&
|
||||
wait_conversion_ $vg/$lv1 &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "remove one of newly added mirrors" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
check_mirror_log_ $vg/$lv1 &&
|
||||
lvconvert -m+2 -b -i100 $vg/$lv1 $(pv_ 4) $(pv_ 5) &&
|
||||
lvconvert -m-1 $vg/$lv1 $(pv_ 4) &&
|
||||
lvconvert -i1 $vg/$lv1 &&
|
||||
wait_conversion_ $vg/$lv1 &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 3 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "remove from original mirror (the original is still mirror)" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m2 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 5) $(pv_ 3):0-1 &&
|
||||
check_mirror_count_ $vg/$lv1 3 &&
|
||||
check_mirror_log_ $vg/$lv1 &&
|
||||
lvconvert -m+1 -b -i100 $vg/$lv1 $(pv_ 4) &&
|
||||
lvconvert -m-1 $vg/$lv1 $(pv_ 2) &&
|
||||
lvconvert -i1 $vg/$lv1 &&
|
||||
wait_conversion_ $vg/$lv1 &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 3 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "remove from original mirror (the original becomes linear)" \
|
||||
'prepare_lvs_ &&
|
||||
lvcreate -l2 -m1 -n $lv1 $vg $(pv_ 1) $(pv_ 2) $(pv_ 3):0-1 &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
check_mirror_log_ $vg/$lv1 &&
|
||||
lvconvert -m+1 -b -i100 $vg/$lv1 $(pv_ 4) &&
|
||||
lvconvert -m-1 $vg/$lv1 $(pv_ 2) &&
|
||||
lvconvert -i1 $vg/$lv1 &&
|
||||
wait_conversion_ $vg/$lv1 &&
|
||||
check_no_tmplvs_ $vg/$lv1 &&
|
||||
check_mirror_count_ $vg/$lv1 2 &&
|
||||
mimages_are_redundant_ $vg $lv1 &&
|
||||
mirrorlog_is_on_ $vg/$lv1 $(pv_ 3) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
test_done
|
||||
428
test/t-pvmove-basic.sh
Executable file
428
test/t-pvmove-basic.sh
Executable file
@@ -0,0 +1,428 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2007 NEC Corporation
|
||||
#
|
||||
# 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
test_description="ensure that pvmove works with basic options"
|
||||
privileges_required_=1
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
dmsetup_has_dm_devdir_support_ ||
|
||||
{
|
||||
say "Your version of dmsetup lacks support for changing DM_DEVDIR."
|
||||
say "Skipping this test"
|
||||
exit 0
|
||||
}
|
||||
|
||||
cleanup_()
|
||||
{
|
||||
test -n "$vg" && {
|
||||
lvremove -ff $vg
|
||||
vgremove $vg
|
||||
} > /dev/null
|
||||
test -n "$pvs" && {
|
||||
pvremove $pvs > /dev/null
|
||||
for d in $pvs; do
|
||||
dmsetup remove $(basename $d)
|
||||
done
|
||||
}
|
||||
losetup -d $lodev
|
||||
rm -f $lofile
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# config
|
||||
|
||||
nr_pvs=5
|
||||
pvsize=$((80 * 1024 * 2))
|
||||
|
||||
vg=pvmove-basic-vg-$$
|
||||
lv1=lv1
|
||||
lv2=lv2
|
||||
lv3=lv3
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Utilities
|
||||
|
||||
pv_()
|
||||
{
|
||||
echo "$G_dev_/mapper/pv$1"
|
||||
}
|
||||
|
||||
lvdev_()
|
||||
{
|
||||
echo "$G_dev_/$1/$2"
|
||||
}
|
||||
|
||||
lv_is_on_()
|
||||
{
|
||||
local lv=$1
|
||||
shift 1
|
||||
lvs -a -odevices --noheadings $lv | sed 's/,/\n/g' > out
|
||||
for d in $*; do grep "$d(" out || return 1; done
|
||||
for d in $*; do grep -v "$d(" out > out2; mv out2 out; done
|
||||
grep . out && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
save_dev_sum_()
|
||||
{
|
||||
mkfs.ext3 $1 > /dev/null &&
|
||||
md5sum $1 > md5.$(basename $1)
|
||||
}
|
||||
|
||||
check_dev_sum_()
|
||||
{
|
||||
md5sum $1 > md5.tmp && cmp md5.$(basename $1) md5.tmp
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Initialize PVs and VGs
|
||||
|
||||
test_expect_success \
|
||||
'set up temp file and loopback device' \
|
||||
'lofile=$(pwd)/lofile && lodev=$(loop_setup_ "$lofile")'
|
||||
|
||||
offset=0
|
||||
pvs=
|
||||
for n in $(seq 1 $nr_pvs); do
|
||||
test_expect_success \
|
||||
"create pv$n" \
|
||||
'echo "0 $pvsize linear $lodev $offset" > in &&
|
||||
dmsetup create pv$n < in'
|
||||
offset=$(($offset + $pvsize))
|
||||
done
|
||||
|
||||
for n in $(seq 1 $nr_pvs); do
|
||||
pvs="$pvs $(pv_ $n)"
|
||||
done
|
||||
|
||||
test_expect_success \
|
||||
"Run this: pvcreate $pvs" \
|
||||
'pvcreate $pvs'
|
||||
|
||||
test_expect_success \
|
||||
'set up a VG' \
|
||||
'vgcreate $vg $pvs'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Common environment setup/cleanup for each sub testcases
|
||||
|
||||
prepare_lvs_()
|
||||
{
|
||||
lvcreate -l2 -n $lv1 $vg $(pv_ 1) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) &&
|
||||
lvcreate -l9 -i3 -n $lv2 $vg $(pv_ 2) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 3) $(pv_ 4) &&
|
||||
lvextend -l+2 $vg/$lv1 $(pv_ 2) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 2) &&
|
||||
lvextend -l+2 $vg/$lv1 $(pv_ 3) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 2) $(pv_ 3) &&
|
||||
lvextend -l+2 $vg/$lv1 $(pv_ 1) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 2) $(pv_ 3) $(pv_ 1) &&
|
||||
lvcreate -l1 -n $lv3 $vg $(pv_ 2) &&
|
||||
lv_is_on_ $vg/$lv3 $(pv_ 2) &&
|
||||
save_dev_sum_ $(lvdev_ $vg $lv1) &&
|
||||
save_dev_sum_ $(lvdev_ $vg $lv2) &&
|
||||
save_dev_sum_ $(lvdev_ $vg $lv3) &&
|
||||
lvs -a -o devices --noheadings $vg/$lv1 > lv1_devs &&
|
||||
lvs -a -o devices --noheadings $vg/$lv2 > lv2_devs &&
|
||||
lvs -a -o devices --noheadings $vg/$lv3 > lv3_devs
|
||||
}
|
||||
|
||||
lv_not_changed_()
|
||||
{
|
||||
lvs -a -o devices --noheadings $1 > out &&
|
||||
diff $(basename $1)_devs out
|
||||
}
|
||||
|
||||
check_and_cleanup_lvs_()
|
||||
{
|
||||
lvs -a -o+devices $vg &&
|
||||
check_dev_sum_ $(lvdev_ $vg $lv1) &&
|
||||
check_dev_sum_ $(lvdev_ $vg $lv2) &&
|
||||
check_dev_sum_ $(lvdev_ $vg $lv3) &&
|
||||
lvs -a -o name $vg > out && ! grep ^pvmove out &&
|
||||
lvremove -ff $vg
|
||||
}
|
||||
|
||||
test_expect_success "check environment setup/cleanup" \
|
||||
'prepare_lvs_ &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# pvmove tests
|
||||
|
||||
# ---
|
||||
# filter by LV
|
||||
|
||||
test_expect_success "only specified LV is moved: from pv2 to pv5 only for lv1" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 -n $vg/$lv1 $(pv_ 2) $(pv_ 5) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 5) $(pv_ 3) $(pv_ 1) &&
|
||||
lv_not_changed_ $vg/$lv2 &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# segments in a LV
|
||||
|
||||
test_expect_success "the 1st seg of 3-segs LV is moved: from pv1 of lv1 to pv4" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 -n $vg/$lv1 $(pv_ 1) $(pv_ 4) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 4) $(pv_ 2) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv2 &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "the 2nd seg of 3-segs LV is moved: from pv2 of lv1 to pv4" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 -n $vg/$lv1 $(pv_ 2) $(pv_ 4) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 4) $(pv_ 3) $(pv_ 1) &&
|
||||
lv_not_changed_ $vg/$lv2 &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "the 3rd seg of 3-segs LV is moved: from pv3 of lv1 to pv4" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 -n $vg/$lv1 $(pv_ 3) $(pv_ 4) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 2) $(pv_ 4) $(pv_ 1) &&
|
||||
lv_not_changed_ $vg/$lv2 &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# multiple LVs matching
|
||||
|
||||
test_expect_success "1 out of 3 LVs is moved: from pv4 to pv5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 4) $(pv_ 5) &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 3) $(pv_ 5) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "2 out of 3 LVs are moved: from pv3 to pv5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 3) $(pv_ 5) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 2) $(pv_ 5) $(pv_ 1) &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 5) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "3 out of 3 LVs are moved: from pv2 to pv5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 2) $(pv_ 5) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 5) $(pv_ 3) $(pv_ 1) &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 5) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_is_on_ $vg/$lv3 $(pv_ 5) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# areas of striping
|
||||
|
||||
test_expect_success "move the 1st stripe: from pv2 of lv2 to pv1" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 -n $vg/$lv2 $(pv_ 2) $(pv_ 1) &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 1) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "move the 2nd stripe: from pv3 of lv2 to pv1" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 -n $vg/$lv2 $(pv_ 3) $(pv_ 1) &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 1) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "move the 3rd stripe: from pv4 of lv2 to pv1" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 -n $vg/$lv2 $(pv_ 4) $(pv_ 1) &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 3) $(pv_ 1) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# partial segment match (source segment splitted)
|
||||
|
||||
test_expect_success "match to the start of segment:from pv2:0-0 to pv5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 2):0-0 $(pv_ 5) &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 5) $(pv_ 2) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "match to the middle of segment: from pv2:1-1 to pv5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 2):1-1 $(pv_ 5) &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 5) $(pv_ 2) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "match to the end of segment: from pv2:2-2 to pv5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 2):2-2 $(pv_ 5) &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 5) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# destination segment splitted
|
||||
|
||||
test_expect_success "no destination split: from pv2:0-2 to pv5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 2):0-2 $(pv_ 5) &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 5) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "destination split into 2: from pv2:0-2 to pv5:5-5 and pv4:5-6" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 2):0-2 $(pv_ 5):5-5 $(pv_ 4):5-6 &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 5) $(pv_ 4) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "destination split into 3: from pv2:0-2 to {pv3,4,5}:5-5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 2):0-2 $(pv_ 3):5-5 $(pv_ 4):5-5 $(pv_ 5):5-5 &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 3) $(pv_ 4) $(pv_ 5) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# alloc policy (anywhere, contiguous) with both success and failure cases
|
||||
|
||||
test_expect_failure "alloc normal on same PV for source and destination: from pv3:0-2 to pv3:5-7" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 3):0-2 $(pv_ 3):5-7'
|
||||
test_expect_success "(cleanup previous test)" \
|
||||
'lv_not_changed_ $vg/$lv1 &&
|
||||
lv_not_changed_ $vg/$lv2 &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "alloc anywhere on same PV for source and destination: from pv3:0-2 to pv3:5-7" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 --alloc anywhere $(pv_ 3):0-2 $(pv_ 3):5-7 &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "alloc anywhere but better area available: from pv3:0-2 to pv3:5-7 or pv5:5-6,pv4:5-5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 --alloc anywhere $(pv_ 3):0-2 $(pv_ 3):5-7 $(pv_ 5):5-6 $(pv_ 4):5-5 &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 5) $(pv_ 4) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_failure "alloc contiguous but area not available: from pv2:0-2 to pv5:5-5 and pv4:5-6" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 --alloc contiguous $(pv_ 2):0-2 $(pv_ 5):5-5 $(pv_ 4):5-6'
|
||||
test_expect_success "(cleanup previous test)" \
|
||||
'lv_not_changed_ $vg/$lv1 &&
|
||||
lv_not_changed_ $vg/$lv2 &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_success "alloc contiguous and contiguous area available: from pv2:0-2 to pv5:0-0,pv5:3-5 and pv4:5-6" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 --alloc contiguous $(pv_ 2):0-2 $(pv_ 5):0-0 $(pv_ 5):3-5 $(pv_ 4):5-6 &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 5) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# multiple segments in a LV
|
||||
|
||||
test_expect_success "multiple source LVs: from pv3 to pv5" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 3) $(pv_ 5) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 2) $(pv_ 5) &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 5) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# move inactive LV
|
||||
|
||||
test_expect_success "move inactive LV: from pv2 to pv5" \
|
||||
'prepare_lvs_ &&
|
||||
lvchange -an $vg/$lv1 &&
|
||||
lvchange -an $vg/$lv3 &&
|
||||
pvmove -i1 $(pv_ 2) $(pv_ 5) &&
|
||||
lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 5) $(pv_ 3) &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 5) $(pv_ 3) $(pv_ 4) &&
|
||||
lv_is_on_ $vg/$lv3 $(pv_ 5) &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---
|
||||
# other failure cases
|
||||
|
||||
test_expect_failure "no PEs to move: from pv3 to pv1" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 3) $(pv_ 1) &&
|
||||
pvmove -i1 $(pv_ 3) $(pv_ 1)'
|
||||
test_expect_success "(cleanup previous test)" \
|
||||
'lv_is_on_ $vg/$lv1 $(pv_ 1) $(pv_ 2) $(pv_ 1) &&
|
||||
lv_is_on_ $vg/$lv2 $(pv_ 2) $(pv_ 1) $(pv_ 4) &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_failure "no space available: from pv2:0-0 to pv1:0-0" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 2):0-0 $(pv_ 1):0-0'
|
||||
test_expect_success "(cleanup previous test)" \
|
||||
'lv_not_changed_ $vg/$lv1 &&
|
||||
lv_not_changed_ $vg/$lv2 &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_failure 'same source and destination: from pv1 to pv1' \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i1 $(pv_ 1) $(pv_ 1)'
|
||||
test_expect_success "(cleanup previous test)" \
|
||||
'lv_not_changed_ $vg/$lv1 &&
|
||||
lv_not_changed_ $vg/$lv2 &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_expect_failure "sum of specified destination PEs is large enough, but it includes source PEs and the free PEs are not enough" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove --alloc anywhere $(pv_ 1):0-2 $(pv_ 1):0-2 $(pv_ 5):0-0 2> err'
|
||||
test_expect_success "(cleanup previous test)" \
|
||||
'grep "Insufficient free space" err &&
|
||||
lv_not_changed_ $vg/$lv1 &&
|
||||
lv_not_changed_ $vg/$lv2 &&
|
||||
lv_not_changed_ $vg/$lv3 &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
test_expect_success "pvmove abort" \
|
||||
'prepare_lvs_ &&
|
||||
pvmove -i100 -b $(pv_ 1) $(pv_ 3) &&
|
||||
pvmove --abort &&
|
||||
check_and_cleanup_lvs_'
|
||||
|
||||
test_done
|
||||
93
test/t-vgcreate-usage.sh
Executable file
93
test/t-vgcreate-usage.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
test_description='Exercise some vgcreate diagnostics'
|
||||
privileges_required_=1
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
cleanup_()
|
||||
{
|
||||
test -n "$d1" && losetup -d "$d1"
|
||||
test -n "$d2" && losetup -d "$d2"
|
||||
rm -f "$f1" "$f2"
|
||||
}
|
||||
|
||||
test_expect_success \
|
||||
'set up temp files, loopback devices, PVs, vgname' \
|
||||
'f1=$(pwd)/1 && d1=$(loop_setup_ "$f1") &&
|
||||
f2=$(pwd)/2 && d2=$(loop_setup_ "$f2") &&
|
||||
vg=$(this_test_)-test-vg-$$ &&
|
||||
pvcreate $d1 $d2'
|
||||
|
||||
lv=vgcreate-usage-$$
|
||||
|
||||
test_expect_success \
|
||||
'vgcreate accepts 8.00M physicalextentsize for VG' \
|
||||
'vgcreate $vg --physicalextentsize 8.00M $d1 $d2 &&
|
||||
check_vg_field_ $vg vg_extent_size 8.00M &&
|
||||
vgremove $vg'
|
||||
|
||||
test_expect_success \
|
||||
'vgcreate accepts smaller (128) maxlogicalvolumes for VG' \
|
||||
'vgcreate $vg --maxlogicalvolumes 128 $d1 $d2 &&
|
||||
check_vg_field_ $vg max_lv 128 &&
|
||||
vgremove $vg'
|
||||
|
||||
test_expect_success \
|
||||
'vgcreate accepts smaller (128) maxphysicalvolumes for VG' \
|
||||
'vgcreate $vg --maxphysicalvolumes 128 $d1 $d2 &&
|
||||
check_vg_field_ $vg max_pv 128 &&
|
||||
vgremove $vg'
|
||||
|
||||
test_expect_success \
|
||||
'vgcreate rejects a zero physical extent size' \
|
||||
'vgcreate --physicalextentsize 0 $vg $d1 $d2 2>err;
|
||||
status=$?; echo status=$?; test $status = 3 &&
|
||||
grep "^ Physical extent size may not be zero\$" err'
|
||||
|
||||
test_expect_success \
|
||||
'vgcreate rejects "inherit" allocation policy' \
|
||||
'vgcreate --alloc inherit $vg $d1 $d2 2>err;
|
||||
status=$?; echo status=$?; test $status = 3 &&
|
||||
grep "^ Volume Group allocation policy cannot inherit from anything\$" err'
|
||||
|
||||
test_expect_success \
|
||||
'vgcreate rejects vgname "."' \
|
||||
'vg=.; vgcreate $vg $d1 $d2 2>err;
|
||||
status=$?; echo status=$?; test $status = 3 &&
|
||||
grep "New volume group name \"$vg\" is invalid\$" err'
|
||||
|
||||
test_expect_success \
|
||||
'vgcreate rejects vgname greater than 128 characters' \
|
||||
'vginvalid=thisnameisridiculouslylongtotestvalidationcodecheckingmaximumsizethisiswhathappenswhenprogrammersgetboredandorarenotcreativedonttrythisathome;
|
||||
vgcreate $vginvalid $d1 $d2 2>err;
|
||||
status=$?; echo status=$?; test $status = 3 &&
|
||||
grep "New volume group name \"$vginvalid\" is invalid\$" err'
|
||||
|
||||
test_expect_success \
|
||||
'vgcreate rejects already existing vgname "/tmp/$vg"' \
|
||||
'touch /tmp/$vg; vgcreate $vg $d1 $d2 2>err;
|
||||
status=$?; echo status=$?; test $status = 3 &&
|
||||
grep "New volume group name \"$vg\" is invalid\$" err'
|
||||
|
||||
# FIXME: Not sure why this fails
|
||||
#test_expect_success \
|
||||
# 'vgcreate rejects MaxLogicalVolumes > 255' \
|
||||
# 'vgcreate --metadatatype 1 --maxlogicalvolumes 1024 $vg $d1 $d2 2>err;
|
||||
# cp err save;
|
||||
# status=$?; echo status=$?; test $status = 3 &&
|
||||
# grep "^ Number of volumes may not exceed 255\$" err'
|
||||
|
||||
test_done
|
||||
# Local Variables:
|
||||
# indent-tabs-mode: nil
|
||||
# End:
|
||||
102
test/t-vgmerge-usage.sh
Executable file
102
test/t-vgmerge-usage.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
test_description='Exercise some vgmerge diagnostics'
|
||||
privileges_required_=1
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
cleanup_()
|
||||
{
|
||||
test -n "$d1" && losetup -d "$d1"
|
||||
test -n "$d2" && losetup -d "$d2"
|
||||
test -n "$d3" && losetup -d "$d3"
|
||||
test -n "$d4" && losetup -d "$d4"
|
||||
rm -f "$f1" "$f2" "$f3" "$f4"
|
||||
}
|
||||
|
||||
test_expect_success \
|
||||
'set up temp files, loopback devices, PVs, vgnames' \
|
||||
'f1=$(pwd)/1 && d1=$(loop_setup_ "$f1") &&
|
||||
f2=$(pwd)/2 && d2=$(loop_setup_ "$f2") &&
|
||||
f3=$(pwd)/3 && d3=$(loop_setup_ "$f3") &&
|
||||
f4=$(pwd)/4 && d4=$(loop_setup_ "$f4") &&
|
||||
vg1=$(this_test_)-test-vg1-$$ &&
|
||||
vg2=$(this_test_)-test-vg2-$$ &&
|
||||
pvcreate $d1 $d2 $d3 $d4'
|
||||
|
||||
test_expect_success \
|
||||
'vgmerge normal operation' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgcreate $vg2 $d3 $d4 &&
|
||||
vgmerge $vg1 $vg2 &&
|
||||
vgremove $vg1'
|
||||
|
||||
test_expect_success \
|
||||
'vgmerge rejects duplicate vg name' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgcreate $vg2 $d3 $d4 &&
|
||||
vgmerge $vg1 $vg1 2>err;
|
||||
status=$?; echo status=$?; test $status = 5 &&
|
||||
grep "^ Duplicate volume group name \"$vg1\"\$" err &&
|
||||
vgremove $vg2 &&
|
||||
vgremove $vg1'
|
||||
|
||||
test_expect_success \
|
||||
'vgmerge rejects vgs with incompatible extent_size' \
|
||||
'vgcreate --physicalextentsize 4M $vg1 $d1 $d2 &&
|
||||
vgcreate --physicalextentsize 8M $vg2 $d3 $d4 &&
|
||||
vgmerge $vg1 $vg2 2>err;
|
||||
status=$?; echo status=$?; test $status = 5 &&
|
||||
grep "^ Extent sizes differ" err &&
|
||||
vgremove $vg2 &&
|
||||
vgremove $vg1'
|
||||
|
||||
test_expect_success \
|
||||
'vgmerge rejects vgmerge because max_pv is exceeded' \
|
||||
'vgcreate --maxphysicalvolumes 2 $vg1 $d1 $d2 &&
|
||||
vgcreate --maxphysicalvolumes 2 $vg2 $d3 $d4 &&
|
||||
vgmerge $vg1 $vg2 2>err;
|
||||
status=$?; echo status=$?; test $status = 5 &&
|
||||
grep "^ Maximum number of physical volumes (2) exceeded" err &&
|
||||
vgremove $vg2 &&
|
||||
vgremove $vg1'
|
||||
|
||||
test_expect_success \
|
||||
'vgmerge rejects vg with active lv' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgcreate $vg2 $d3 $d4 &&
|
||||
lvcreate -l 4 -n lv1 $vg2 &&
|
||||
vgmerge $vg1 $vg2 2>err;
|
||||
status=$?; echo status=$?; test $status = 5 &&
|
||||
grep "^ Logical volumes in \"$vg2\" must be inactive\$" err &&
|
||||
vgremove -f $vg2 &&
|
||||
vgremove -f $vg1'
|
||||
|
||||
test_expect_success \
|
||||
'vgmerge rejects vgmerge because max_lv is exceeded' \
|
||||
'vgcreate --maxlogicalvolumes 2 $vg1 $d1 $d2 &&
|
||||
vgcreate --maxlogicalvolumes 2 $vg2 $d3 $d4 &&
|
||||
lvcreate -l 4 -n lv1 $vg1 &&
|
||||
lvcreate -l 4 -n lv2 $vg1 &&
|
||||
lvcreate -l 4 -n lv3 $vg2 &&
|
||||
vgchange -an $vg1 &&
|
||||
vgchange -an $vg2 &&
|
||||
vgmerge $vg1 $vg2 2>err;
|
||||
status=$?; echo status=$?; test $status = 5 &&
|
||||
grep "^ Maximum number of logical volumes (2) exceeded" err &&
|
||||
vgremove -f $vg2 &&
|
||||
vgremove -f $vg1'
|
||||
|
||||
test_done
|
||||
# Local Variables:
|
||||
# indent-tabs-mode: nil
|
||||
# End:
|
||||
46
test/t-vgrename-usage.sh
Executable file
46
test/t-vgrename-usage.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
test_description='Exercise some vgrename diagnostics'
|
||||
privileges_required_=1
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
cleanup_()
|
||||
{
|
||||
test -n "$d1" && losetup -d "$d1"
|
||||
test -n "$d2" && losetup -d "$d2"
|
||||
test -n "$d3" && losetup -d "$d3"
|
||||
test -n "$d4" && losetup -d "$d4"
|
||||
rm -f "$f1" "$f2" "$f3" "$f4"
|
||||
}
|
||||
|
||||
test_expect_success \
|
||||
'set up temp files, loopback devices, PVs, vgnames' \
|
||||
'f1=$(pwd)/1 && d1=$(loop_setup_ "$f1") &&
|
||||
f2=$(pwd)/2 && d2=$(loop_setup_ "$f2") &&
|
||||
f3=$(pwd)/3 && d3=$(loop_setup_ "$f3") &&
|
||||
f4=$(pwd)/4 && d4=$(loop_setup_ "$f4") &&
|
||||
vg1=$(this_test_)-1-$$ &&
|
||||
vg2=$(this_test_)-2-$$ &&
|
||||
pvcreate $d1 $d2 $d3 $d4'
|
||||
|
||||
test_expect_success \
|
||||
'vgrename normal operation - rename vg1 to vg2' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgrename $vg1 $vg2 &&
|
||||
check_vg_field_ $vg2 vg_name $vg2 &&
|
||||
vgremove $vg2'
|
||||
|
||||
test_done
|
||||
# Local Variables:
|
||||
# indent-tabs-mode: nil
|
||||
# End:
|
||||
130
test/t-vgsplit-operation.sh
Executable file
130
test/t-vgsplit-operation.sh
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
test_description='Exercise some vgsplit diagnostics'
|
||||
privileges_required_=1
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
cleanup_()
|
||||
{
|
||||
test -n "$d1" && losetup -d "$d1"
|
||||
test -n "$d2" && losetup -d "$d2"
|
||||
test -n "$d3" && losetup -d "$d3"
|
||||
test -n "$d4" && losetup -d "$d4"
|
||||
rm -f "$f1" "$f2" "$f3" "$f4"
|
||||
}
|
||||
|
||||
test_expect_success \
|
||||
'set up temp files, loopback devices, PVs, vgnames' \
|
||||
'f1=$(pwd)/1 && d1=$(loop_setup_ "$f1") &&
|
||||
f2=$(pwd)/2 && d2=$(loop_setup_ "$f2") &&
|
||||
f3=$(pwd)/3 && d3=$(loop_setup_ "$f3") &&
|
||||
f4=$(pwd)/4 && d4=$(loop_setup_ "$f4") &&
|
||||
vg1=$(this_test_)-test-vg1-$$ &&
|
||||
vg2=$(this_test_)-test-vg2-$$ &&
|
||||
pvcreate $d1 $d2 $d3 $d4'
|
||||
|
||||
test_expect_success \
|
||||
'vgsplit accepts new vg as destination of split' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgsplit $vg1 $vg2 $d1 &&
|
||||
vgremove $vg1 &&
|
||||
vgremove $vg2'
|
||||
|
||||
test_expect_success \
|
||||
'vgsplit accepts existing vg as destination of split' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgcreate $vg2 $d3 $d4 &&
|
||||
vgsplit $vg1 $vg2 $d1 &&
|
||||
vgremove $vg1 &&
|
||||
vgremove $vg2'
|
||||
|
||||
#test_expect_success \
|
||||
# 'vgcreate accepts 8.00M physicalextentsize for VG' \
|
||||
# 'vgcreate $vg --physicalextentsize 8.00M $d1 $d2 &&
|
||||
# check_vg_field_ $vg vg_extent_size 8.00M &&
|
||||
# vgremove $vg'
|
||||
|
||||
test_expect_success \
|
||||
'vgsplit accepts 8.00M physicalextentsize for new VG' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgsplit --physicalextentsize 8.00M $vg1 $vg2 $d1 &&
|
||||
check_vg_field_ $vg2 vg_extent_size 8.00M &&
|
||||
vgremove $vg1 &&
|
||||
vgremove $vg2'
|
||||
|
||||
test_expect_success \
|
||||
'vgsplit accepts --maxphysicalvolumes 128 on new VG' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgsplit --maxphysicalvolumes 128 $vg1 $vg2 $d1 &&
|
||||
check_vg_field_ $vg2 max_pv 128 &&
|
||||
vgremove $vg1 &&
|
||||
vgremove $vg2'
|
||||
|
||||
test_expect_success \
|
||||
'vgsplit accepts --maxlogicalvolumes 128 on new VG' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgsplit --maxlogicalvolumes 128 $vg1 $vg2 $d1 &&
|
||||
check_vg_field_ $vg2 max_lv 128 &&
|
||||
vgremove $vg1 &&
|
||||
vgremove $vg2'
|
||||
|
||||
test_expect_success \
|
||||
'vgsplit rejects vgs with incompatible extent_size' \
|
||||
'vgcreate --physicalextentsize 4M $vg1 $d1 $d2 &&
|
||||
vgcreate --physicalextentsize 8M $vg2 $d3 $d4 &&
|
||||
vgsplit $vg1 $vg2 $d1 2>err;
|
||||
status=$?; echo status=$?; test $status = 5 &&
|
||||
grep "^ Extent sizes differ" err &&
|
||||
vgremove $vg2 &&
|
||||
vgremove $vg1'
|
||||
|
||||
test_expect_success \
|
||||
'vgsplit rejects split because max_pv of destination would be exceeded' \
|
||||
'vgcreate --maxphysicalvolumes 2 $vg1 $d1 $d2 &&
|
||||
vgcreate --maxphysicalvolumes 2 $vg2 $d3 $d4 &&
|
||||
vgsplit $vg1 $vg2 $d1 2>err;
|
||||
status=$?; echo status=$?; test $status = 5 &&
|
||||
grep "^ Maximum number of physical volumes (2) exceeded" err &&
|
||||
vgremove $vg2 &&
|
||||
vgremove $vg1'
|
||||
|
||||
test_expect_success \
|
||||
'vgsplit rejects vg with active lv' \
|
||||
'vgcreate $vg1 $d1 $d2 &&
|
||||
vgcreate $vg2 $d3 $d4 &&
|
||||
lvcreate -l 4 -n lv1 $vg1 &&
|
||||
vgsplit $vg1 $vg2 $d1 2>err;
|
||||
status=$?; echo status=$?; test $status = 5 &&
|
||||
grep "^ Logical volumes in \"$vg1\" must be inactive\$" err &&
|
||||
vgremove -f $vg2 &&
|
||||
vgremove -f $vg1'
|
||||
|
||||
test_expect_success \
|
||||
'vgsplit rejects split because max_lv is exceeded' \
|
||||
'vgcreate --maxlogicalvolumes 2 $vg1 $d1 $d2 &&
|
||||
vgcreate --maxlogicalvolumes 2 $vg2 $d3 $d4 &&
|
||||
lvcreate -l 4 -n lv1 $vg1 &&
|
||||
lvcreate -l 4 -n lv2 $vg1 &&
|
||||
lvcreate -l 4 -n lv3 $vg2 &&
|
||||
vgchange -an $vg1 &&
|
||||
vgchange -an $vg2 &&
|
||||
vgsplit $vg1 $vg2 $d1 2>err;
|
||||
status=$?; echo status=$?; test $status = 5 &&
|
||||
grep "^ Maximum number of logical volumes (2) exceeded" err &&
|
||||
vgremove -f $vg2 &&
|
||||
vgremove -f $vg1'
|
||||
|
||||
test_done
|
||||
# Local Variables:
|
||||
# indent-tabs-mode: nil
|
||||
# End:
|
||||
@@ -16,10 +16,6 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
ifeq ("@FSADM@", "yes")
|
||||
SUBDIRS += fsadm
|
||||
endif
|
||||
|
||||
SOURCES =\
|
||||
dumpconfig.c \
|
||||
formats.c \
|
||||
|
||||
@@ -91,8 +91,10 @@ xx(lvconvert,
|
||||
"[-m|--mirrors Mirrors [{--mirrorlog {disk|core}|--corelog}]]\n"
|
||||
"\t[-R|--regionsize MirrorLogRegionSize]\n"
|
||||
"\t[--alloc AllocationPolicy]\n"
|
||||
"\t[-b|--background]\n"
|
||||
"\t[-d|--debug]\n"
|
||||
"\t[-h|-?|--help]\n"
|
||||
"\t[-i|--interval seconds]\n"
|
||||
"\t[-v|--verbose]\n"
|
||||
"\t[--version]" "\n"
|
||||
"\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n\n"
|
||||
@@ -107,8 +109,8 @@ xx(lvconvert,
|
||||
"\t[--version]" "\n"
|
||||
"\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n",
|
||||
|
||||
alloc_ARG, chunksize_ARG, corelog_ARG, mirrorlog_ARG, mirrors_ARG,
|
||||
regionsize_ARG, snapshot_ARG, test_ARG, zero_ARG)
|
||||
alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG,
|
||||
mirrorlog_ARG, mirrors_ARG, regionsize_ARG, snapshot_ARG, test_ARG, zero_ARG)
|
||||
|
||||
xx(lvcreate,
|
||||
"Create a logical volume",
|
||||
@@ -872,17 +874,23 @@ xx(vgsplit,
|
||||
"Move physical volumes into a new volume group",
|
||||
"vgsplit " "\n"
|
||||
"\t[-A|--autobackup {y|n}] " "\n"
|
||||
"\t[--alloc AllocationPolicy] " "\n"
|
||||
"\t[-c|--clustered {y|n}] " "\n"
|
||||
"\t[-d|--debug] " "\n"
|
||||
"\t[-h|--help] " "\n"
|
||||
"\t[-l|--list]" "\n"
|
||||
"\t[-l|--maxlogicalvolumes MaxLogicalVolumes]" "\n"
|
||||
"\t[-M|--metadatatype 1|2] " "\n"
|
||||
"\t[-p|--maxphysicalvolumes MaxPhysicalVolumes] " "\n"
|
||||
"\t[-s|--physicalextentsize PhysicalExtentSize[kKmMgGtTpPeE]] " "\n"
|
||||
"\t[-t|--test] " "\n"
|
||||
"\t[-v|--verbose] " "\n"
|
||||
"\t[--version]" "\n"
|
||||
"\tExistingVolumeGroupName NewVolumeGroupName" "\n"
|
||||
"\tSourceVolumeGroupName DestinationVolumeGroupName" "\n"
|
||||
"\tPhysicalVolumePath [PhysicalVolumePath...]\n",
|
||||
|
||||
autobackup_ARG, list_ARG, metadatatype_ARG, test_ARG)
|
||||
alloc_ARG, autobackup_ARG, clustered_ARG,
|
||||
maxlogicalvolumes_ARG, maxphysicalvolumes_ARG,
|
||||
metadatatype_ARG, physicalextentsize_ARG, test_ARG)
|
||||
|
||||
xx(version,
|
||||
"Display software and driver version information",
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Red Hat GmbH. All rights reserved.
|
||||
* Copyright (C) 2007 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 General Public License v.2.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* FIXME: pass smart resizer arguments through from lvresize
|
||||
* (eg, for xfs_growfs)
|
||||
*/
|
||||
|
||||
/* FIXME All funcs to return 0 on success or an error from errno.h on failure */
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#define MAX_ARGS 8
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <inttypes.h>
|
||||
#include <fstab.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "last-path-component.h"
|
||||
|
||||
#define log_error(str, x...) fprintf(stderr, "%s(%u): " str "\n", __FILE__, __LINE__, x)
|
||||
|
||||
/* Filesystem related information */
|
||||
struct fsinfo {
|
||||
struct fstab *fsent;
|
||||
struct statfs statfs;
|
||||
uint64_t new_size;
|
||||
const char *cmd;
|
||||
};
|
||||
|
||||
static void _usage(const char *cmd)
|
||||
{
|
||||
log_error("Usage: %s [check <filesystem> | resize <filesystem> <size>]",
|
||||
last_path_component(cmd));
|
||||
}
|
||||
|
||||
/* FIXME Make this more robust - /proc, multiple mounts, TMPDIR + security etc. */
|
||||
/* FIXME Ensure filesystem is not mounted anywhere before running fsck/resize */
|
||||
/* Gather filesystem information (VFS type, special file and block size) */
|
||||
static int _get_fsinfo(const char *file, struct fsinfo *fsinfo)
|
||||
{
|
||||
char *dir, template[] = "/tmp/fscmd_XXXXXX";
|
||||
struct stat info;
|
||||
int ret = 0;
|
||||
|
||||
if (stat(file, &info)) {
|
||||
log_error("%s: stat failed: %s", file, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
/* FIXME: are we limited to /etc/fstab entries ? */
|
||||
if (!(fsinfo->fsent = info.st_rdev ? getfsspec(file) : getfsfile(file))) {
|
||||
log_error("%s: getfsspec/getfsfile failed: "
|
||||
"Missing from /etc/fstab?", file);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME: any other way to retrieve fs blocksize avoiding mounting ? */
|
||||
if (!(dir = (mkdtemp(template)))) {
|
||||
log_error("%s: mkdtemp failed: %s", template, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (mount(fsinfo->fsent->fs_spec, dir, fsinfo->fsent->fs_vfstype,
|
||||
MS_RDONLY, NULL)) {
|
||||
log_error("%s: mount %s failed: %s", fsinfo->fsent->fs_spec,
|
||||
dir, strerror(errno));
|
||||
ret = errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (statfs(dir, &fsinfo->statfs)) {
|
||||
log_error("%s: statfs failed: %s", dir, strerror(errno));
|
||||
ret = errno;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
out1:
|
||||
if (umount(dir))
|
||||
log_error("%s: umount failed: %s", dir, strerror(errno));
|
||||
|
||||
out:
|
||||
if (rmdir(dir))
|
||||
log_error("%s: rmdir failed: %s", dir, strerror(errno));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum {
|
||||
BLOCKS,
|
||||
KILOBYTES
|
||||
};
|
||||
|
||||
#define LEN 32 /* Length of temporary string */
|
||||
|
||||
/* Create size string in units of blocks or kilobytes
|
||||
* (size expected in kilobytes)
|
||||
*/
|
||||
static char *_size_str(struct fsinfo *fsinfo, int unit)
|
||||
{
|
||||
uint64_t new_size = fsinfo->new_size;
|
||||
static char s[LEN];
|
||||
|
||||
/* Avoid switch() as long as there's only 2 cases */
|
||||
snprintf(s, LEN, "%" PRIu64,
|
||||
unit == BLOCKS ? new_size / (fsinfo->statfs.f_bsize >> 10) :
|
||||
new_size);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Allocate memory and store string updating string pointer */
|
||||
static int _store(char **arg, char **str)
|
||||
{
|
||||
size_t len = 0;
|
||||
char *s = *str;
|
||||
|
||||
while (*s && *s != '%' && *(s++) != ' ')
|
||||
len++;
|
||||
|
||||
if ((*arg = (char *) malloc(len + 1))) {
|
||||
strncpy(*arg, *str, len);
|
||||
(*arg)[len] = '\0';
|
||||
*str = s - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Construct a new argument vector for the real external command */
|
||||
static int _new_argv(char **new_argv, struct fsinfo *fsinfo)
|
||||
{
|
||||
int i = 0, b = 0, d = 0, k = 0, m = 0;
|
||||
char *s1;
|
||||
char *s = (char *) fsinfo->cmd;
|
||||
|
||||
if (!s || !strlen(s))
|
||||
return 1;
|
||||
|
||||
do {
|
||||
switch (*s) {
|
||||
case ' ':
|
||||
break;
|
||||
|
||||
case '%':
|
||||
s++;
|
||||
|
||||
switch (tolower(*s)) {
|
||||
case 'b':
|
||||
s1 = _size_str(fsinfo, BLOCKS);
|
||||
if (b++ + k || _store(&new_argv[i++], &s1))
|
||||
goto error;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
s1 = _size_str(fsinfo, KILOBYTES);
|
||||
if (b + k++ || _store(&new_argv[i++], &s1))
|
||||
goto error;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
s1 = fsinfo->fsent->fs_spec;
|
||||
if (d++ + m || _store(&new_argv[i++], &s1))
|
||||
goto error;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
s1 = fsinfo->fsent->fs_file;
|
||||
if (d + m++ || _store(&new_argv[i++], &s1))
|
||||
goto error;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_store(&new_argv[i++], &s))
|
||||
goto error;
|
||||
}
|
||||
} while (*++s);
|
||||
|
||||
new_argv[i] = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
new_argv[i] = NULL;
|
||||
log_error("Failed constructing arguments for %s", s);
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get filesystem command arguments derived from a command definition string
|
||||
*
|
||||
* Command definition syntax: 'cmd [-option]{0,} [(option)argument]{0,}'
|
||||
*
|
||||
* (option)argument can be: '%{bdkm}'
|
||||
*
|
||||
* Command definition is parsed into argument strings of
|
||||
* an argument vector with:
|
||||
*
|
||||
* %b replaced by the size in filesystem blocks
|
||||
* %k replaced by the size in kilobytes
|
||||
* %d replaced by the name of the device special of the LV
|
||||
* %m replaced by the mountpoint of the filesystem
|
||||
*
|
||||
*/
|
||||
static int _get_cmd(char *command, struct fsinfo *fsinfo)
|
||||
{
|
||||
const char *vfstype = fsinfo->fsent->fs_vfstype;
|
||||
struct fscmd {
|
||||
const char *vfstype;
|
||||
const char *fsck;
|
||||
const char *fsresize;
|
||||
} fscmds[] = {
|
||||
{ "ext2", "fsck -fy %d", "ext2resize %d %b"},
|
||||
{"ext3", "fsck -fy %d", "ext2resize %d %b"},
|
||||
{"reiserfs", "", "resize_reiserfs -s%k %d"},
|
||||
{"xfs", "", "xfs_growfs -D %b %m"}, /* simple xfs grow */
|
||||
{NULL, NULL, NULL},
|
||||
}, *p = &fscmds[0];
|
||||
|
||||
for (; p->vfstype; p++) {
|
||||
if (!strcmp(vfstype, p->vfstype)) {
|
||||
if (!strcmp(command, "resize"))
|
||||
fsinfo->cmd = p->fsresize;
|
||||
else if (!strcmp(command, "check"))
|
||||
fsinfo->cmd = p->fsck;
|
||||
else {
|
||||
log_error("Unrecognised command: %s", command);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fsinfo->cmd) {
|
||||
log_error("%s: Unrecognised filesystem type", vfstype);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Collapse multiple slashes */
|
||||
static char *_collapse_slashes(char *path)
|
||||
{
|
||||
char *s = path;
|
||||
|
||||
/* Slight overhead but short ;) */
|
||||
while ((s = strchr(s, '/')) && *(s + 1))
|
||||
*(s + 1) == '/' ? memmove(s, s + 1, strlen(s)) : s++;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/* Free the argument array */
|
||||
static void _free_argv(char **new_argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; new_argv[i]; i++)
|
||||
free(new_argv[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* check/resize a filesystem
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fsinfo fsinfo;
|
||||
char *new_argv[MAX_ARGS];
|
||||
char *command, *path;
|
||||
|
||||
if (argc < 3)
|
||||
goto error;
|
||||
|
||||
command = argv[1];
|
||||
path = _collapse_slashes(argv[2]);
|
||||
|
||||
if (!strcmp(command, "resize")) {
|
||||
if (argc != 4)
|
||||
goto error;
|
||||
/* FIXME sanity checks */
|
||||
fsinfo.new_size = strtoul(argv[3], NULL, 10);
|
||||
} else if (argc != 3)
|
||||
goto error;
|
||||
|
||||
/* Retrieve filesystem information (block size...) */
|
||||
if ((ret = _get_fsinfo(path, &fsinfo))) {
|
||||
log_error("Can't get filesystem information from %s", path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get filesystem command info */
|
||||
if ((ret = _get_cmd(command, &fsinfo))) {
|
||||
log_error("Can't get filesystem command for %s", command);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (_new_argv(new_argv, &fsinfo))
|
||||
return EINVAL;
|
||||
|
||||
if (!new_argv[0])
|
||||
return 0;
|
||||
|
||||
execvp(new_argv[0], new_argv);
|
||||
ret = errno;
|
||||
log_error("%s: execvp %s failed", new_argv[0], strerror(errno));
|
||||
_free_argv(new_argv);
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
_usage(argv[0]);
|
||||
return EINVAL;
|
||||
}
|
||||
@@ -106,6 +106,8 @@ static int lvchange_availability(struct cmd_context *cmd,
|
||||
{
|
||||
int activate;
|
||||
const char *pvname;
|
||||
char *lv_full_name;
|
||||
uint32_t len;
|
||||
|
||||
activate = arg_uint_value(cmd, available_ARG, 0);
|
||||
|
||||
@@ -158,6 +160,18 @@ static int lvchange_availability(struct cmd_context *cmd,
|
||||
pvname);
|
||||
pvmove_poll(cmd, pvname, 1);
|
||||
}
|
||||
|
||||
if (lv->status & CONVERTING) {
|
||||
len = strlen(lv->vg->name) + strlen(lv->name) + 2;
|
||||
if (!(lv_full_name = alloca(len)))
|
||||
return_0;
|
||||
if (!dm_snprintf(lv_full_name, len, "%s/%s",
|
||||
lv->vg->name, lv->name))
|
||||
return_0;
|
||||
log_verbose("Spawning background lvconvert process for %s",
|
||||
lv->name);
|
||||
lvconvert_poll(cmd, lv_full_name, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -372,6 +386,7 @@ static int lvchange_readahead(struct cmd_context *cmd,
|
||||
struct logical_volume *lv)
|
||||
{
|
||||
unsigned read_ahead = 0;
|
||||
unsigned pagesize = (unsigned) lvm_getpagesize() >> SECTOR_SHIFT;
|
||||
|
||||
read_ahead = arg_uint_value(cmd, readahead_ARG, 0);
|
||||
|
||||
@@ -382,6 +397,13 @@ static int lvchange_readahead(struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (read_ahead != DM_READ_AHEAD_AUTO &&
|
||||
read_ahead != DM_READ_AHEAD_NONE && read_ahead % pagesize) {
|
||||
read_ahead = (read_ahead / pagesize) * pagesize;
|
||||
log_verbose("Rounding down readahead to %u sectors, a multiple "
|
||||
"of page size %u.", read_ahead, pagesize);
|
||||
}
|
||||
|
||||
if (lv->read_ahead == read_ahead) {
|
||||
log_error("Read ahead is already %u for \"%s\"",
|
||||
read_ahead, lv->name);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
|
||||
#include "tools.h"
|
||||
#include "polldaemon.h"
|
||||
#include "lv_alloc.h"
|
||||
|
||||
struct lvconvert_params {
|
||||
@@ -21,7 +22,10 @@ struct lvconvert_params {
|
||||
|
||||
const char *origin;
|
||||
const char *lv_name;
|
||||
const char *lv_name_full;
|
||||
const char *vg_name;
|
||||
int wait_completion;
|
||||
int need_polling;
|
||||
|
||||
uint32_t chunk_size;
|
||||
uint32_t region_size;
|
||||
@@ -70,11 +74,11 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
lp->lv_name = (*pargv)[0];
|
||||
lp->lv_name = lp->lv_name_full = (*pargv)[0];
|
||||
(*pargv)++, (*pargc)--;
|
||||
|
||||
if (strchr(lp->lv_name, '/') &&
|
||||
(vg_name = extract_vgname(cmd, lp->lv_name)) &&
|
||||
if (strchr(lp->lv_name_full, '/') &&
|
||||
(vg_name = extract_vgname(cmd, lp->lv_name_full)) &&
|
||||
lp->vg_name && strcmp(vg_name, lp->vg_name)) {
|
||||
log_error("Please use a single volume group name "
|
||||
"(\"%s\" or \"%s\")", vg_name, lp->vg_name);
|
||||
@@ -89,7 +93,7 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((ptr = strrchr(lp->lv_name, '/')))
|
||||
if ((ptr = strrchr(lp->lv_name_full, '/')))
|
||||
lp->lv_name = ptr + 1;
|
||||
|
||||
if (!apply_lvname_restrictions(lp->lv_name))
|
||||
@@ -113,6 +117,9 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!arg_count(cmd, background_ARG))
|
||||
lp->wait_completion = 1;
|
||||
|
||||
if (arg_count(cmd, snapshot_ARG))
|
||||
lp->snapshot = 1;
|
||||
|
||||
@@ -227,20 +234,165 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd,
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static struct volume_group *_get_lvconvert_vg(struct cmd_context *cmd,
|
||||
const char *lv_name)
|
||||
{
|
||||
dev_close_all();
|
||||
|
||||
return vg_lock_and_read(cmd, extract_vgname(cmd, lv_name),
|
||||
NULL, LCK_VG_WRITE,
|
||||
CLUSTERED | EXPORTED_VG | LVM_WRITE,
|
||||
CORRECT_INCONSISTENT | FAIL_INCONSISTENT);
|
||||
}
|
||||
|
||||
static struct logical_volume *_get_lvconvert_lv(struct cmd_context *cmd __attribute((unused)),
|
||||
struct volume_group *vg,
|
||||
const char *name,
|
||||
uint32_t lv_type __attribute((unused)))
|
||||
{
|
||||
return find_lv(vg, name);
|
||||
}
|
||||
|
||||
static int _update_lvconvert_mirror(struct cmd_context *cmd __attribute((unused)),
|
||||
struct volume_group *vg __attribute((unused)),
|
||||
struct logical_volume *lv __attribute((unused)),
|
||||
struct list *lvs_changed __attribute((unused)),
|
||||
int first_time __attribute((unused)))
|
||||
{
|
||||
/* lvconvert mirror doesn't require periodical metadata update */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _finish_lvconvert_mirror(struct cmd_context *cmd,
|
||||
struct volume_group *vg,
|
||||
struct logical_volume *lv,
|
||||
struct list *lvs_changed __attribute((unused)))
|
||||
{
|
||||
if (!collapse_mirrored_lv(lv)) {
|
||||
log_error("Failed to remove temporary sync layer.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv->status &= ~CONVERTING;
|
||||
|
||||
log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
|
||||
|
||||
if (!vg_write(vg))
|
||||
return_0;
|
||||
|
||||
backup(vg);
|
||||
|
||||
if (!suspend_lv(cmd, lv)) {
|
||||
log_error("Failed to lock %s", lv->name);
|
||||
vg_revert(vg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!vg_commit(vg)) {
|
||||
resume_lv(cmd, lv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_very_verbose("Updating \"%s\" in kernel", lv->name);
|
||||
|
||||
if (!resume_lv(cmd, lv)) {
|
||||
log_error("Problem reactivating %s", lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_print("Logical volume %s converted.", lv->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct poll_functions _lvconvert_mirror_fns = {
|
||||
.get_copy_vg = _get_lvconvert_vg,
|
||||
.get_copy_lv = _get_lvconvert_lv,
|
||||
.update_metadata = _update_lvconvert_mirror,
|
||||
.finish_copy = _finish_lvconvert_mirror,
|
||||
};
|
||||
|
||||
int lvconvert_poll(struct cmd_context *cmd, const char *lv_name,
|
||||
unsigned background)
|
||||
{
|
||||
return poll_daemon(cmd, lv_name, background, 0, &_lvconvert_mirror_fns,
|
||||
"Converted");
|
||||
}
|
||||
|
||||
static int _insert_lvconvert_layer(struct cmd_context *cmd,
|
||||
struct logical_volume *lv)
|
||||
{
|
||||
char *format, *layer_name;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We would like to give the same number for this layer
|
||||
* and the newly added mimage.
|
||||
* However, LV name of newly added mimage is determined *after*
|
||||
* the LV name of this layer is determined.
|
||||
*
|
||||
* So, use generate_lv_name() to generate mimage name first
|
||||
* and take the number from it.
|
||||
*/
|
||||
|
||||
len = strlen(lv->name) + 32;
|
||||
if (!(format = alloca(len)) ||
|
||||
!(layer_name = alloca(len)) ||
|
||||
dm_snprintf(format, len, "%s_mimage_%%d", lv->name) < 0) {
|
||||
log_error("lvconvert: layer name allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!generate_lv_name(lv->vg, format, layer_name, len) ||
|
||||
sscanf(layer_name, format, &i) != 1) {
|
||||
log_error("lvconvert: layer name generation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dm_snprintf(layer_name, len, MIRROR_SYNC_LAYER "_%d", i) < 0) {
|
||||
log_error("layer name allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!insert_layer_for_lv(cmd, lv, 0, layer_name)) {
|
||||
log_error("Failed to insert resync layer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* walk down the stacked mirror LV to the original mirror LV */
|
||||
static struct logical_volume *_original_lv(struct logical_volume *lv)
|
||||
{
|
||||
struct logical_volume *next_lv = lv, *tmp_lv;
|
||||
|
||||
while ((tmp_lv = find_temporary_mirror(next_lv)))
|
||||
next_lv = tmp_lv;
|
||||
|
||||
return next_lv;
|
||||
}
|
||||
|
||||
static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * lv,
|
||||
struct lvconvert_params *lp)
|
||||
{
|
||||
struct lv_segment *seg;
|
||||
uint32_t existing_mirrors;
|
||||
struct alloc_handle *ah = NULL;
|
||||
struct logical_volume *log_lv;
|
||||
struct list *parallel_areas;
|
||||
float sync_percent;
|
||||
const char *mirrorlog;
|
||||
unsigned corelog = 0;
|
||||
struct logical_volume *original_lv;
|
||||
|
||||
seg = first_seg(lv);
|
||||
existing_mirrors = (lv->status & MIRRORED) ? seg->area_count : 1;
|
||||
existing_mirrors = lv_mirror_count(lv);
|
||||
|
||||
/* If called with no argument, try collapsing the resync layers */
|
||||
if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, mirrorlog_ARG) &&
|
||||
!arg_count(cmd, corelog_ARG)) {
|
||||
lp->need_polling = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust required number of mirrors
|
||||
@@ -312,8 +464,8 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!remove_mirror_images(seg, 1,
|
||||
lp->pv_count ? lp->pvh : NULL, 1))
|
||||
if (!lv_remove_mirrors(cmd, lv, existing_mirrors - 1, 1,
|
||||
lp->pv_count ? lp->pvh : NULL, 0))
|
||||
return_0;
|
||||
goto commit_changes;
|
||||
}
|
||||
@@ -332,34 +484,16 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
|
||||
}
|
||||
}
|
||||
|
||||
if (!(parallel_areas = build_parallel_areas_from_lv(cmd, lv)))
|
||||
return_0;
|
||||
|
||||
if (!(ah = allocate_extents(lv->vg, NULL, lp->segtype,
|
||||
1, lp->mirrors - 1,
|
||||
corelog ? 0U : 1U,
|
||||
lv->le_count, lp->pvh, lp->alloc,
|
||||
parallel_areas)))
|
||||
return_0;
|
||||
|
||||
lp->region_size = adjusted_mirror_region_size(lv->vg->extent_size,
|
||||
lv->le_count,
|
||||
lp->region_size);
|
||||
|
||||
log_lv = NULL;
|
||||
if (!corelog &&
|
||||
!(log_lv = create_mirror_log(cmd, lv->vg, ah,
|
||||
lp->alloc,
|
||||
lv->name, 0, &lv->tags))) {
|
||||
log_error("Failed to create mirror log.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!create_mirror_layers(ah, 1, lp->mirrors, lv,
|
||||
lp->segtype, 0,
|
||||
lp->region_size,
|
||||
log_lv))
|
||||
if (!lv_add_mirrors(cmd, lv, lp->mirrors - 1, 1,
|
||||
adjusted_mirror_region_size(
|
||||
lv->vg->extent_size,
|
||||
lv->le_count,
|
||||
lp->region_size),
|
||||
corelog ? 0U : 1U, lp->pvh, lp->alloc,
|
||||
MIRROR_BY_LV))
|
||||
return_0;
|
||||
if (lp->wait_completion)
|
||||
lp->need_polling = 1;
|
||||
goto commit_changes;
|
||||
}
|
||||
|
||||
@@ -374,55 +508,18 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
|
||||
}
|
||||
|
||||
if (lp->mirrors == existing_mirrors) {
|
||||
if (!seg->log_lv && !corelog) {
|
||||
/* No disk log present, add one. */
|
||||
if (!(parallel_areas = build_parallel_areas_from_lv(cmd, lv)))
|
||||
original_lv = _original_lv(lv);
|
||||
if (!first_seg(original_lv)->log_lv && !corelog) {
|
||||
if (!add_mirror_log(cmd, original_lv, 1,
|
||||
adjusted_mirror_region_size(
|
||||
lv->vg->extent_size,
|
||||
lv->le_count,
|
||||
lp->region_size),
|
||||
lp->pvh, lp->alloc))
|
||||
return_0;
|
||||
if (!lv_mirror_percent(cmd, lv, 0, &sync_percent, NULL)) {
|
||||
log_error("Unable to determine mirror sync status.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(ah = allocate_extents(lv->vg, NULL, lp->segtype, 0,
|
||||
0, 1, 0, lp->pvh, lp->alloc,
|
||||
parallel_areas))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sync_percent >= 100.0)
|
||||
init_mirror_in_sync(1);
|
||||
else
|
||||
init_mirror_in_sync(0);
|
||||
|
||||
if (!(log_lv = create_mirror_log(cmd, lv->vg, ah,
|
||||
lp->alloc, lv->name,
|
||||
(sync_percent >= 100.0) ?
|
||||
1 : 0, &lv->tags))) {
|
||||
log_error("Failed to create mirror log.");
|
||||
return 0;
|
||||
}
|
||||
seg->log_lv = log_lv;
|
||||
log_lv->status |= MIRROR_LOG;
|
||||
first_seg(log_lv)->mirror_seg = seg;
|
||||
} else if (seg->log_lv && corelog) {
|
||||
/* Had disk log, switch to core. */
|
||||
if (!lv_mirror_percent(cmd, lv, 0, &sync_percent, NULL)) {
|
||||
log_error("Unable to determine mirror sync status.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sync_percent >= 100.0)
|
||||
init_mirror_in_sync(1);
|
||||
else {
|
||||
/* A full resync will take place */
|
||||
lv->status &= ~MIRROR_NOTSYNCED;
|
||||
init_mirror_in_sync(0);
|
||||
}
|
||||
|
||||
if (!remove_mirror_images(seg, lp->mirrors,
|
||||
lp->pv_count ?
|
||||
lp->pvh : NULL, 1))
|
||||
} else if (first_seg(original_lv)->log_lv && corelog) {
|
||||
if (!remove_mirror_log(cmd, original_lv,
|
||||
lp->pv_count ? lp->pvh : NULL))
|
||||
return_0;
|
||||
} else {
|
||||
/* No change */
|
||||
@@ -432,19 +529,52 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
|
||||
return 1;
|
||||
}
|
||||
} else if (lp->mirrors > existing_mirrors) {
|
||||
/* FIXME Unless anywhere, remove PV of log_lv
|
||||
* from allocatable_pvs & allocate
|
||||
* (mirrors - existing_mirrors) new areas
|
||||
if (lv->status & MIRROR_NOTSYNCED) {
|
||||
log_error("Not adding mirror to mirrored LV "
|
||||
"without initial resync");
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Log addition/removal should be done before the layer
|
||||
* insertion to make the end result consistent with
|
||||
* linear-to-mirror conversion.
|
||||
*/
|
||||
/* FIXME Create mirror hierarchy to sync */
|
||||
log_error("Adding mirror images is not "
|
||||
"supported yet.");
|
||||
return 0;
|
||||
original_lv = _original_lv(lv);
|
||||
if (!first_seg(original_lv)->log_lv && !corelog) {
|
||||
if (!add_mirror_log(cmd, original_lv, 1,
|
||||
adjusted_mirror_region_size(
|
||||
lv->vg->extent_size,
|
||||
lv->le_count,
|
||||
lp->region_size),
|
||||
lp->pvh, lp->alloc))
|
||||
return_0;
|
||||
} else if (first_seg(original_lv)->log_lv && corelog) {
|
||||
if (!remove_mirror_log(cmd, original_lv,
|
||||
lp->pv_count ? lp->pvh : NULL))
|
||||
return_0;
|
||||
}
|
||||
/* Insert a temporary layer for syncing,
|
||||
* only if the original lv is using disk log. */
|
||||
if (seg->log_lv && !_insert_lvconvert_layer(cmd, lv)) {
|
||||
log_error("Failed to insert resync layer");
|
||||
return 0;
|
||||
}
|
||||
/* FIXME: can't have multiple mlogs. force corelog. */
|
||||
if (!lv_add_mirrors(cmd, lv, lp->mirrors - existing_mirrors, 1,
|
||||
adjusted_mirror_region_size(
|
||||
lv->vg->extent_size,
|
||||
lv->le_count,
|
||||
lp->region_size),
|
||||
0U, lp->pvh, lp->alloc,
|
||||
MIRROR_BY_LV))
|
||||
return_0;
|
||||
lv->status |= CONVERTING;
|
||||
lp->need_polling = 1;
|
||||
} else {
|
||||
/* Reduce number of mirrors */
|
||||
if (!remove_mirror_images(seg, lp->mirrors,
|
||||
lp->pv_count ?
|
||||
lp->pvh : NULL, 0))
|
||||
if (!lv_remove_mirrors(cmd, lv, existing_mirrors - lp->mirrors,
|
||||
corelog ? 1U : 0U,
|
||||
lp->pv_count ? lp->pvh : NULL, 0))
|
||||
return_0;
|
||||
}
|
||||
|
||||
@@ -474,7 +604,8 @@ commit_changes:
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_print("Logical volume %s converted.", lv->name);
|
||||
if (!lp->need_polling)
|
||||
log_print("Logical volume %s converted.", lv->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -574,8 +705,7 @@ static int lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (arg_count(cmd, mirrors_ARG) ||
|
||||
((lv->status & MIRRORED) && arg_count(cmd, mirrorlog_ARG))) {
|
||||
if (arg_count(cmd, mirrors_ARG) || (lv->status & MIRRORED)) {
|
||||
if (!archive(lv->vg))
|
||||
return ECMD_FAILED;
|
||||
if (!lvconvert_mirrors(cmd, lv, lp))
|
||||
@@ -596,6 +726,7 @@ int lvconvert(struct cmd_context * cmd, int argc, char **argv)
|
||||
struct lv_list *lvl;
|
||||
struct lvconvert_params lp;
|
||||
int ret = ECMD_FAILED;
|
||||
struct lvinfo info;
|
||||
|
||||
if (!_read_params(&lp, cmd, argc, argv)) {
|
||||
stack;
|
||||
@@ -628,5 +759,15 @@ int lvconvert(struct cmd_context * cmd, int argc, char **argv)
|
||||
|
||||
error:
|
||||
unlock_vg(cmd, lp.vg_name);
|
||||
|
||||
if (ret == ECMD_PROCESSED && lp.need_polling) {
|
||||
if (!lv_info(cmd, lvl->lv, &info, 1, 0) || !info.exists) {
|
||||
log_print("Conversion starts after activation");
|
||||
return ret;
|
||||
}
|
||||
ret = lvconvert_poll(cmd, lp.lv_name_full,
|
||||
lp.wait_completion ? 0 : 1U);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ static int _validate_stripe_params(struct cmd_context *cmd,
|
||||
if (lp->stripes > 1 && !lp->stripe_size) {
|
||||
lp->stripe_size = find_config_tree_int(cmd,
|
||||
"metadata/stripesize",
|
||||
DEFAULT_STRIPESIZE);
|
||||
DEFAULT_STRIPESIZE) * 2;
|
||||
log_print("Using default stripesize %s",
|
||||
display_size(cmd, (uint64_t) lp->stripe_size));
|
||||
}
|
||||
@@ -333,6 +333,7 @@ static int _lvcreate_params(struct lvcreate_params *lp, struct cmd_context *cmd,
|
||||
int argc, char **argv)
|
||||
{
|
||||
int contiguous;
|
||||
unsigned pagesize;
|
||||
|
||||
memset(lp, 0, sizeof(*lp));
|
||||
|
||||
@@ -408,7 +409,7 @@ static int _lvcreate_params(struct lvcreate_params *lp, struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(lp->segtype = get_segtype_from_string(cmd, "mirror"))) {
|
||||
if (!(lp->segtype = get_segtype_from_string(cmd, "striped"))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
@@ -462,8 +463,17 @@ static int _lvcreate_params(struct lvcreate_params *lp, struct cmd_context *cmd,
|
||||
/*
|
||||
* Read ahead.
|
||||
*/
|
||||
if (arg_count(cmd, readahead_ARG))
|
||||
if (arg_count(cmd, readahead_ARG)) {
|
||||
lp->read_ahead = arg_uint_value(cmd, readahead_ARG, 0);
|
||||
pagesize = lvm_getpagesize() >> SECTOR_SHIFT;
|
||||
if (lp->read_ahead != DM_READ_AHEAD_AUTO &&
|
||||
lp->read_ahead != DM_READ_AHEAD_NONE &&
|
||||
lp->read_ahead % pagesize) {
|
||||
lp->read_ahead = (lp->read_ahead / pagesize) * pagesize;
|
||||
log_verbose("Rounding down readahead to %u sectors, a multiple "
|
||||
"of page size %u.", lp->read_ahead, pagesize);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Permissions.
|
||||
@@ -514,11 +524,10 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
uint32_t size_rest;
|
||||
uint32_t status = 0;
|
||||
uint64_t tmp_size;
|
||||
struct logical_volume *lv, *org = NULL, *log_lv = NULL;
|
||||
struct list *pvh, tags;
|
||||
struct logical_volume *lv, *org = NULL;
|
||||
struct list *pvh;
|
||||
const char *tag = NULL;
|
||||
int origin_active = 0;
|
||||
struct alloc_handle *ah = NULL;
|
||||
char lv_name_buf[128];
|
||||
const char *lv_name;
|
||||
struct lvinfo info;
|
||||
@@ -736,17 +745,6 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
}
|
||||
|
||||
if (lp->mirrors > 1) {
|
||||
/* FIXME Calculate how many extents needed for the log */
|
||||
|
||||
if (!(ah = allocate_extents(vg, NULL, lp->segtype, lp->stripes,
|
||||
lp->mirrors, lp->corelog ? 0U : 1U,
|
||||
lp->extents, pvh, lp->alloc, NULL)))
|
||||
return_0;
|
||||
|
||||
lp->region_size = adjusted_mirror_region_size(vg->extent_size,
|
||||
lp->extents,
|
||||
lp->region_size);
|
||||
|
||||
init_mirror_in_sync(lp->nosync);
|
||||
|
||||
if (lp->nosync) {
|
||||
@@ -754,17 +752,6 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
"Don't read what you didn't write!");
|
||||
status |= MIRROR_NOTSYNCED;
|
||||
}
|
||||
|
||||
list_init(&tags);
|
||||
if (tag)
|
||||
str_list_add(cmd->mem, &tags, tag);
|
||||
|
||||
if (!lp->corelog &&
|
||||
!(log_lv = create_mirror_log(cmd, vg, ah, lp->alloc,
|
||||
lv_name, lp->nosync, &tags))) {
|
||||
log_error("Failed to create mirror log.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(lv = lv_create_empty(lv_name ? lv_name : "lvol%d", NULL,
|
||||
@@ -792,20 +779,23 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (lp->mirrors > 1) {
|
||||
if (!create_mirror_layers(ah, 0, lp->mirrors, lv,
|
||||
lp->segtype, 0,
|
||||
lp->region_size, log_lv)) {
|
||||
stack;
|
||||
goto error;
|
||||
}
|
||||
|
||||
alloc_destroy(ah);
|
||||
ah = NULL;
|
||||
} else if (!lv_extend(lv, lp->segtype, lp->stripes, lp->stripe_size,
|
||||
lp->mirrors, lp->extents, NULL, 0u, 0u, pvh, lp->alloc))
|
||||
if (!lv_extend(lv, lp->segtype, lp->stripes, lp->stripe_size,
|
||||
1, lp->extents, NULL, 0u, 0u, pvh, lp->alloc))
|
||||
return_0;
|
||||
|
||||
if (lp->mirrors > 1) {
|
||||
if (!lv_add_mirrors(cmd, lv, lp->mirrors - 1, lp->stripes,
|
||||
adjusted_mirror_region_size(
|
||||
vg->extent_size,
|
||||
lv->le_count,
|
||||
lp->region_size),
|
||||
lp->corelog ? 0U : 1U, pvh, lp->alloc,
|
||||
MIRROR_BY_LV)) {
|
||||
stack;
|
||||
goto revert_new_lv;
|
||||
}
|
||||
}
|
||||
|
||||
/* store vg on disk(s) */
|
||||
if (!vg_write(vg))
|
||||
return_0;
|
||||
@@ -891,8 +881,6 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
return 1;
|
||||
|
||||
error:
|
||||
if (ah)
|
||||
alloc_destroy(ah);
|
||||
return 0;
|
||||
|
||||
deactivate_and_revert_new_lv:
|
||||
|
||||
@@ -1140,6 +1140,12 @@ static void _exec_lvm1_command(char **argv)
|
||||
log_sys_error("execvp", path);
|
||||
}
|
||||
|
||||
static void _nonroot_warning()
|
||||
{
|
||||
if (getuid() || geteuid())
|
||||
log_warn("WARNING: Running as a non-root user. Functionality may be unavailable.");
|
||||
}
|
||||
|
||||
int lvm2_main(int argc, char **argv, unsigned is_static)
|
||||
{
|
||||
char *base;
|
||||
@@ -1186,6 +1192,7 @@ int lvm2_main(int argc, char **argv, unsigned is_static)
|
||||
}
|
||||
#ifdef READLINE_SUPPORT
|
||||
if (!alias && argc == 1) {
|
||||
_nonroot_warning();
|
||||
ret = lvm_shell(cmd, &_cmdline);
|
||||
goto out;
|
||||
}
|
||||
@@ -1203,6 +1210,7 @@ int lvm2_main(int argc, char **argv, unsigned is_static)
|
||||
argv++;
|
||||
}
|
||||
|
||||
_nonroot_warning();
|
||||
ret = lvm_run_command(cmd, argc, argv);
|
||||
if ((ret == ENO_SUCH_CMD) && (!alias))
|
||||
ret = _run_script(cmd, argc, argv);
|
||||
|
||||
@@ -435,7 +435,7 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
|
||||
if ((lp->extents > lv->le_count)) {
|
||||
list_iterate_back_items(seg, &lv->segments) {
|
||||
if (seg_is_mirrored(seg))
|
||||
seg_mirrors = seg->area_count;
|
||||
seg_mirrors = lv_mirror_count(seg->lv);
|
||||
else
|
||||
seg_mirrors = 0;
|
||||
break;
|
||||
@@ -469,7 +469,7 @@ static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
|
||||
}
|
||||
|
||||
if (seg_is_mirrored(seg))
|
||||
seg_mirrors = seg->area_count;
|
||||
seg_mirrors = lv_mirror_count(seg->lv);
|
||||
else
|
||||
seg_mirrors = 0;
|
||||
|
||||
|
||||
@@ -93,9 +93,11 @@ static int _check_mirror_status(struct cmd_context *cmd,
|
||||
|
||||
overall_percent = copy_percent(lv_mirr);
|
||||
if (parms->progress_display)
|
||||
log_print("%s: Moved: %.1f%%", name, overall_percent);
|
||||
log_print("%s: %s: %.1f%%", name, parms->progress_title,
|
||||
overall_percent);
|
||||
else
|
||||
log_verbose("%s: Moved: %.1f%%", name, overall_percent);
|
||||
log_verbose("%s: %s: %.1f%%", name, parms->progress_title,
|
||||
overall_percent);
|
||||
|
||||
if (segment_percent < 100.0) {
|
||||
/* The only case the caller *should* try again later */
|
||||
@@ -224,7 +226,8 @@ static void _poll_for_all_vgs(struct cmd_context *cmd,
|
||||
}
|
||||
|
||||
int poll_daemon(struct cmd_context *cmd, const char *name, unsigned background,
|
||||
uint32_t lv_type, struct poll_functions *poll_fns)
|
||||
uint32_t lv_type, struct poll_functions *poll_fns,
|
||||
const char *progress_title)
|
||||
{
|
||||
struct daemon_parms parms;
|
||||
|
||||
@@ -232,6 +235,7 @@ int poll_daemon(struct cmd_context *cmd, const char *name, unsigned background,
|
||||
parms.background = background;
|
||||
parms.interval = arg_uint_value(cmd, interval_ARG, DEFAULT_INTERVAL);
|
||||
parms.progress_display = 1;
|
||||
parms.progress_title = progress_title;
|
||||
parms.lv_type = lv_type;
|
||||
parms.poll_fns = poll_fns;
|
||||
|
||||
|
||||
@@ -42,11 +42,13 @@ struct daemon_parms {
|
||||
unsigned background;
|
||||
unsigned outstanding_count;
|
||||
unsigned progress_display;
|
||||
const char *progress_title;
|
||||
uint32_t lv_type;
|
||||
struct poll_functions *poll_fns;
|
||||
};
|
||||
|
||||
int poll_daemon(struct cmd_context *cmd, const char *name, unsigned background,
|
||||
uint32_t lv_type, struct poll_functions *poll_fns);
|
||||
uint32_t lv_type, struct poll_functions *poll_fns,
|
||||
const char *progress_title);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "tools.h"
|
||||
#include "metadata.h"
|
||||
|
||||
struct pvcreate_params {
|
||||
int zero;
|
||||
@@ -43,6 +44,18 @@ static int pvcreate_check(struct cmd_context *cmd, const char *name)
|
||||
/* FIXME Use partial mode here? */
|
||||
pv = pv_read(cmd, name, NULL, NULL, 0);
|
||||
|
||||
/*
|
||||
* If a PV has no MDAs it may appear to be an orphan until the
|
||||
* metadata is read off another PV in the same VG. Detecting
|
||||
* this means checking every VG by scanning every PV on the
|
||||
* system.
|
||||
*/
|
||||
if (pv && is_orphan(pv)) {
|
||||
if (!scan_vgs_for_pvs(cmd))
|
||||
return_0;
|
||||
pv = pv_read(cmd, name, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
/* Allow partial & exported VGs to be destroyed. */
|
||||
/* We must have -ff to overwrite a non orphan */
|
||||
if (pv && !is_orphan(pv) && arg_count(cmd, force_ARG) != 2) {
|
||||
|
||||
@@ -26,7 +26,7 @@ static int _pvdisplay_single(struct cmd_context *cmd,
|
||||
const char *pv_name = pv_dev_name(pv);
|
||||
const char *vg_name = NULL;
|
||||
|
||||
if (!is_orphan(pv) && !vg) {
|
||||
if (!is_orphan(pv) && !vg) {
|
||||
vg_name = pv_vg_name(pv);
|
||||
if (!(vg = vg_lock_and_read(cmd, vg_name, (char *)&pv->vgid,
|
||||
LCK_VG_READ, CLUSTERED, 0))) {
|
||||
|
||||
@@ -106,6 +106,40 @@ static struct list *_get_allocatable_pvs(struct cmd_context *cmd, int argc,
|
||||
return allocatable_pvs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace any LV segments on given PV with temporary mirror.
|
||||
* Returns list of LVs changed.
|
||||
*/
|
||||
static int _insert_pvmove_mirrors(struct cmd_context *cmd,
|
||||
struct logical_volume *lv_mirr,
|
||||
struct list *source_pvl,
|
||||
struct logical_volume *lv,
|
||||
struct list *lvs_changed)
|
||||
|
||||
{
|
||||
struct pv_list *pvl;
|
||||
uint32_t prev_le_count;
|
||||
|
||||
/* Only 1 PV may feature in source_pvl */
|
||||
pvl = list_item(source_pvl->n, struct pv_list);
|
||||
|
||||
prev_le_count = lv_mirr->le_count;
|
||||
if (!insert_layer_for_segments_on_pv(cmd, lv, lv_mirr, PVMOVE,
|
||||
pvl, lvs_changed))
|
||||
return_0;
|
||||
|
||||
/* check if layer was inserted */
|
||||
if (lv_mirr->le_count - prev_le_count) {
|
||||
lv->status |= LOCKED;
|
||||
|
||||
log_verbose("Moving %u extents of logical volume %s/%s",
|
||||
lv_mirr->le_count - prev_le_count,
|
||||
lv->vg->name, lv->name);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Create new LV with mirror segments for the required copies */
|
||||
static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
|
||||
struct volume_group *vg,
|
||||
@@ -117,6 +151,7 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
|
||||
{
|
||||
struct logical_volume *lv_mirr, *lv;
|
||||
struct lv_list *lvl;
|
||||
uint32_t log_count = 0;
|
||||
|
||||
/* FIXME Cope with non-contiguous => splitting existing segments */
|
||||
if (!(lv_mirr = lv_create_empty("pvmove%d", NULL,
|
||||
@@ -161,9 +196,8 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
|
||||
log_print("Skipping locked LV %s", lv->name);
|
||||
continue;
|
||||
}
|
||||
if (!insert_pvmove_mirrors(cmd, lv_mirr, source_pvl, lv,
|
||||
allocatable_pvs, alloc,
|
||||
*lvs_changed)) {
|
||||
if (!_insert_pvmove_mirrors(cmd, lv_mirr, source_pvl, lv,
|
||||
*lvs_changed)) {
|
||||
stack;
|
||||
return NULL;
|
||||
}
|
||||
@@ -175,6 +209,17 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!lv_add_mirrors(cmd, lv_mirr, 1, 1, 0, log_count,
|
||||
allocatable_pvs, alloc, MIRROR_BY_SEG)) {
|
||||
log_error("Failed to convert pvmove LV to mirrored");
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
if (!split_parent_segments_for_layer(cmd, lv_mirr)) {
|
||||
log_error("Failed to split segments being moved");
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
return lv_mirr;
|
||||
}
|
||||
|
||||
@@ -372,13 +417,22 @@ static int _finish_pvmove(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct list *lvs_changed)
|
||||
{
|
||||
int r = 1;
|
||||
struct list lvs_completed;
|
||||
struct lv_list *lvl;
|
||||
|
||||
/* Update metadata to remove mirror segments and break dependencies */
|
||||
if (!remove_pvmove_mirrors(vg, lv_mirr)) {
|
||||
list_init(&lvs_completed);
|
||||
if (!lv_remove_mirrors(cmd, lv_mirr, 1, 0, NULL, PVMOVE) ||
|
||||
!remove_layers_for_segments_all(cmd, lv_mirr, PVMOVE,
|
||||
&lvs_completed)) {
|
||||
log_error("ABORTING: Removal of temporary mirror failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_iterate_items(lvl, &lvs_completed)
|
||||
/* FIXME Assumes only one pvmove at a time! */
|
||||
lvl->lv->status &= ~LOCKED;
|
||||
|
||||
/* Store metadata without dependencies on mirror segments */
|
||||
if (!vg_write(vg)) {
|
||||
log_error("ABORTING: Failed to write new data locations "
|
||||
@@ -471,7 +525,8 @@ static struct poll_functions _pvmove_fns = {
|
||||
int pvmove_poll(struct cmd_context *cmd, const char *pv_name,
|
||||
unsigned background)
|
||||
{
|
||||
return poll_daemon(cmd, pv_name, background, PVMOVE, &_pvmove_fns);
|
||||
return poll_daemon(cmd, pv_name, background, PVMOVE, &_pvmove_fns,
|
||||
"Moved");
|
||||
}
|
||||
|
||||
int pvmove(struct cmd_context *cmd, int argc, char **argv)
|
||||
@@ -479,6 +534,17 @@ int pvmove(struct cmd_context *cmd, int argc, char **argv)
|
||||
char *pv_name = NULL;
|
||||
char *colon;
|
||||
int ret;
|
||||
const struct segment_type *segtype;
|
||||
|
||||
if (!(segtype = get_segtype_from_string(cmd, "mirror")))
|
||||
return_0;
|
||||
|
||||
if (activation() && segtype->ops->target_present &&
|
||||
!segtype->ops->target_present(NULL)) {
|
||||
log_error("%s: Required device-mapper target(s) not "
|
||||
"detected in your kernel", segtype->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc) {
|
||||
pv_name = argv[0];
|
||||
|
||||
@@ -59,8 +59,10 @@ static int _pvsegs_sub_single(struct cmd_context *cmd __attribute((unused)),
|
||||
struct pv_segment *pvseg, void *handle)
|
||||
{
|
||||
int ret = ECMD_PROCESSED;
|
||||
struct lv_segment *seg = pvseg->lvseg;
|
||||
|
||||
if (!report_object(handle, vg, NULL, pvseg->pv, NULL, pvseg))
|
||||
if (!report_object(handle, vg, seg ? seg->lv : NULL, pvseg->pv, seg,
|
||||
pvseg))
|
||||
ret = ECMD_FAILED;
|
||||
|
||||
return ret;
|
||||
@@ -89,7 +91,7 @@ static int _pvs_single(struct cmd_context *cmd, struct volume_group *vg,
|
||||
int ret = ECMD_PROCESSED;
|
||||
const char *vg_name = NULL;
|
||||
|
||||
if (!is_orphan(pv) && !vg) {
|
||||
if (is_pv(pv) && !is_orphan(pv) && !vg) {
|
||||
vg_name = pv_vg_name(pv);
|
||||
|
||||
if (!(vg = vg_lock_and_read(cmd, vg_name, (char *)&pv->vgid,
|
||||
@@ -279,21 +281,22 @@ static int _report(struct cmd_context *cmd, int argc, char **argv,
|
||||
report_type |= LVS;
|
||||
if (report_type & PVSEGS)
|
||||
report_type |= PVS;
|
||||
if ((report_type & LVS) && (report_type & PVS)) {
|
||||
if ((report_type & LVS) && (report_type & PVS) && !args_are_pvs) {
|
||||
log_error("Can't report LV and PV fields at the same time");
|
||||
dm_report_free(report_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Change report type if fields specified makes this necessary */
|
||||
if (report_type & SEGS)
|
||||
report_type = SEGS;
|
||||
else if (report_type & LVS)
|
||||
report_type = LVS;
|
||||
else if (report_type & PVSEGS)
|
||||
if ((report_type & PVSEGS) ||
|
||||
((report_type & PVS) && (report_type & LVS)))
|
||||
report_type = PVSEGS;
|
||||
else if (report_type & PVS)
|
||||
report_type = PVS;
|
||||
else if (report_type & SEGS)
|
||||
report_type = SEGS;
|
||||
else if (report_type & LVS)
|
||||
report_type = LVS;
|
||||
|
||||
switch (report_type) {
|
||||
case LVS:
|
||||
|
||||
290
tools/toollib.c
290
tools/toollib.c
@@ -20,10 +20,6 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
/* From linux/drivers/md/dm-log.c */
|
||||
#define MIRROR_MAGIC 0x4D695272
|
||||
#define MIRROR_DISK_VERSION 2
|
||||
|
||||
/* Command line args */
|
||||
unsigned arg_count(const struct cmd_context *cmd, int a)
|
||||
{
|
||||
@@ -696,6 +692,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
|
||||
struct str_list *sll;
|
||||
char *tagname;
|
||||
int consistent = 1;
|
||||
int scanned = 0;
|
||||
|
||||
list_init(&tags);
|
||||
|
||||
@@ -738,6 +735,30 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
|
||||
ret_max = ECMD_FAILED;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a PV has no MDAs it may appear to be an
|
||||
* orphan until the metadata is read off
|
||||
* another PV in the same VG. Detecting this
|
||||
* means checking every VG by scanning every
|
||||
* PV on the system.
|
||||
*/
|
||||
if (!scanned && is_orphan(pv)) {
|
||||
if (!scan_vgs_for_pvs(cmd)) {
|
||||
stack;
|
||||
ret_max = ECMD_FAILED;
|
||||
continue;
|
||||
}
|
||||
scanned = 1;
|
||||
if (!(pv = pv_read(cmd, argv[opt],
|
||||
NULL, NULL, 1))) {
|
||||
log_error("Failed to read "
|
||||
"physical volume "
|
||||
"\"%s\"", argv[opt]);
|
||||
ret_max = ECMD_FAILED;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = process_single(cmd, vg, pv, handle);
|
||||
@@ -746,14 +767,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
|
||||
if (sigint_caught())
|
||||
return ret_max;
|
||||
}
|
||||
if (vg) {
|
||||
ret = process_each_pv_in_vg(cmd, vg, &tags,
|
||||
handle, process_single);
|
||||
if (ret > ret_max)
|
||||
ret_max = ret;
|
||||
if (sigint_caught())
|
||||
return ret_max;
|
||||
} else if (!list_empty(&tags) && (vgnames = get_vgs(cmd, 0)) &&
|
||||
if (!list_empty(&tags) && (vgnames = get_vgs(cmd, 0)) &&
|
||||
!list_empty(vgnames)) {
|
||||
list_iterate_items(sll, vgnames) {
|
||||
if (!lock_vol(cmd, sll->str, lock_type)) {
|
||||
@@ -1225,231 +1239,49 @@ int apply_lvname_restrictions(const char *name)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int validate_new_vg_name(struct cmd_context *cmd, const char *vg_name)
|
||||
{
|
||||
char vg_path[PATH_MAX];
|
||||
|
||||
if (!validate_name(vg_name))
|
||||
return 0;
|
||||
|
||||
snprintf(vg_path, PATH_MAX, "%s%s", cmd->dev_dir, vg_name);
|
||||
if (path_exists(vg_path)) {
|
||||
log_error("%s: already exists in filesystem", vg_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int generate_log_name_format(struct volume_group *vg __attribute((unused)),
|
||||
const char *lv_name, char *buffer, size_t size)
|
||||
{
|
||||
if (dm_snprintf(buffer, size, "%s_mlog", lv_name) < 0) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME I think we can cope without this. Cf. _add_lv_to_dtree()
|
||||
if (find_lv_in_vg(vg, buffer) &&
|
||||
dm_snprintf(buffer, size, "%s_mlog_%%d",
|
||||
lv_name) < 0) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
*******/
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the LV with 'value'.
|
||||
* Set members of struct vgcreate_params from cmdline.
|
||||
* Do preliminary validation with arg_*() interface.
|
||||
* Further, more generic validation is done in validate_vgcreate_params().
|
||||
* This function is to remain in tools directory.
|
||||
*/
|
||||
int set_lv(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
uint64_t sectors, int value)
|
||||
int fill_vg_create_params(struct cmd_context *cmd,
|
||||
char *vg_name, struct vgcreate_params *vp_new,
|
||||
struct vgcreate_params *vp_def)
|
||||
{
|
||||
struct device *dev;
|
||||
char *name;
|
||||
vp_new->vg_name = skip_dev_dir(cmd, vg_name, NULL);
|
||||
vp_new->max_lv = arg_uint_value(cmd, maxlogicalvolumes_ARG,
|
||||
vp_def->max_lv);
|
||||
vp_new->max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG,
|
||||
vp_def->max_pv);
|
||||
vp_new->alloc = arg_uint_value(cmd, alloc_ARG, vp_def->alloc);
|
||||
|
||||
/*
|
||||
* FIXME:
|
||||
* <clausen> also, more than 4k
|
||||
* <clausen> say, reiserfs puts it's superblock 32k in, IIRC
|
||||
* <ejt_> k, I'll drop a fixme to that effect
|
||||
* (I know the device is at least 4k, but not 32k)
|
||||
*/
|
||||
if (!(name = dm_pool_alloc(cmd->mem, PATH_MAX))) {
|
||||
log_error("Name allocation failed - device not cleared");
|
||||
return 0;
|
||||
/* Units of 512-byte sectors */
|
||||
vp_new->extent_size =
|
||||
arg_uint_value(cmd, physicalextentsize_ARG, vp_def->extent_size);
|
||||
|
||||
if (arg_count(cmd, clustered_ARG))
|
||||
vp_new->clustered =
|
||||
!strcmp(arg_str_value(cmd, clustered_ARG,
|
||||
vp_def->clustered ? "y":"n"), "y");
|
||||
else
|
||||
/* Default depends on current locking type */
|
||||
vp_new->clustered = locking_is_clustered();
|
||||
|
||||
if (arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) {
|
||||
log_error("Physical extent size may not be negative");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (dm_snprintf(name, PATH_MAX, "%s%s/%s", cmd->dev_dir,
|
||||
lv->vg->name, lv->name) < 0) {
|
||||
log_error("Name too long - device not cleared (%s)", lv->name);
|
||||
return 0;
|
||||
if (arg_sign_value(cmd, maxlogicalvolumes_ARG, 0) == SIGN_MINUS) {
|
||||
log_error("Max Logical Volumes may not be negative");
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_verbose("Clearing start of logical volume \"%s\"", lv->name);
|
||||
|
||||
if (!(dev = dev_cache_get(name, NULL))) {
|
||||
log_error("%s: not found: device not cleared", name);
|
||||
return 0;
|
||||
if (arg_sign_value(cmd, maxphysicalvolumes_ARG, 0) == SIGN_MINUS) {
|
||||
log_error("Max Physical Volumes may not be negative");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!dev_open_quiet(dev))
|
||||
return 0;
|
||||
|
||||
dev_set(dev, UINT64_C(0),
|
||||
sectors ? (size_t) sectors << SECTOR_SHIFT : (size_t) 4096,
|
||||
value);
|
||||
dev_flush(dev);
|
||||
dev_close_immediate(dev);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function writes a new header to the mirror log header to the lv
|
||||
*
|
||||
* Returns: 1 on success, 0 on failure
|
||||
*/
|
||||
static int _write_log_header(struct cmd_context *cmd, struct logical_volume *lv)
|
||||
{
|
||||
struct device *dev;
|
||||
char *name;
|
||||
struct { /* The mirror log header */
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint64_t nr_regions;
|
||||
} log_header;
|
||||
|
||||
log_header.magic = xlate32(MIRROR_MAGIC);
|
||||
log_header.version = xlate32(MIRROR_DISK_VERSION);
|
||||
log_header.nr_regions = xlate64((uint64_t)-1);
|
||||
|
||||
if (!(name = dm_pool_alloc(cmd->mem, PATH_MAX))) {
|
||||
log_error("Name allocation failed - log header not written (%s)",
|
||||
lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dm_snprintf(name, PATH_MAX, "%s%s/%s", cmd->dev_dir,
|
||||
lv->vg->name, lv->name) < 0) {
|
||||
log_error("Name too long - log header not written (%s)", lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_verbose("Writing log header to device, %s", lv->name);
|
||||
|
||||
if (!(dev = dev_cache_get(name, NULL))) {
|
||||
log_error("%s: not found: log header not written", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dev_open_quiet(dev))
|
||||
return 0;
|
||||
|
||||
if (!dev_write(dev, UINT64_C(0), sizeof(log_header), &log_header)) {
|
||||
log_error("Failed to write log header to %s", name);
|
||||
dev_close_immediate(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_close_immediate(dev);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct logical_volume *create_mirror_log(struct cmd_context *cmd,
|
||||
struct volume_group *vg,
|
||||
struct alloc_handle *ah,
|
||||
alloc_policy_t alloc,
|
||||
const char *lv_name,
|
||||
int in_sync,
|
||||
struct list *tags)
|
||||
{
|
||||
struct logical_volume *log_lv;
|
||||
char *log_name;
|
||||
size_t len;
|
||||
struct str_list *sl;
|
||||
|
||||
if (!activation() && in_sync) {
|
||||
log_error("Aborting. Unable to create in-sync mirror log "
|
||||
"while activation is disabled.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = strlen(lv_name) + 32;
|
||||
if (!(log_name = alloca(len)) ||
|
||||
!(generate_log_name_format(vg, lv_name, log_name, len))) {
|
||||
log_error("log_name allocation failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(log_lv = lv_create_empty(log_name, NULL,
|
||||
VISIBLE_LV | LVM_READ | LVM_WRITE,
|
||||
alloc, 0, vg)))
|
||||
return_NULL;
|
||||
|
||||
if (!lv_add_log_segment(ah, log_lv))
|
||||
return_NULL;
|
||||
|
||||
/* Temporary tag mirror log */
|
||||
list_iterate_items(sl, tags)
|
||||
if (!str_list_add(cmd->mem, &log_lv->tags, sl->str)) {
|
||||
log_error("Aborting. Unable to tag mirror log.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* store mirror log on disk(s) */
|
||||
if (!vg_write(vg))
|
||||
return_NULL;
|
||||
|
||||
backup(vg);
|
||||
|
||||
if (!vg_commit(vg))
|
||||
return_NULL;
|
||||
|
||||
if (!activate_lv(cmd, log_lv)) {
|
||||
log_error("Aborting. Failed to activate mirror log.");
|
||||
goto revert_new_lv;
|
||||
}
|
||||
|
||||
list_iterate_items(sl, tags)
|
||||
if (!str_list_del(&log_lv->tags, sl->str))
|
||||
log_error("Failed to remove tag %s from mirror log.",
|
||||
sl->str);
|
||||
|
||||
if (activation() && !set_lv(cmd, log_lv, log_lv->size,
|
||||
in_sync ? -1 : 0)) {
|
||||
log_error("Aborting. Failed to wipe mirror log.");
|
||||
goto deactivate_and_revert_new_lv;
|
||||
}
|
||||
|
||||
if (activation() && !_write_log_header(cmd, log_lv)) {
|
||||
log_error("Aborting. Failed to write mirror log header.");
|
||||
goto deactivate_and_revert_new_lv;
|
||||
}
|
||||
|
||||
if (!deactivate_lv(cmd, log_lv)) {
|
||||
log_error("Aborting. Failed to deactivate mirror log. "
|
||||
"Manual intervention required.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log_lv->status &= ~VISIBLE_LV;
|
||||
|
||||
return log_lv;
|
||||
|
||||
deactivate_and_revert_new_lv:
|
||||
if (!deactivate_lv(cmd, log_lv)) {
|
||||
log_error("Unable to deactivate mirror log LV. "
|
||||
"Manual intervention required.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
revert_new_lv:
|
||||
if (!lv_remove(log_lv) || !vg_write(vg) || (backup(vg), !vg_commit(vg)))
|
||||
log_error("Manual intervention may be required to remove "
|
||||
"abandoned log LV before retrying.");
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -96,20 +96,7 @@ struct list *clone_pv_list(struct dm_pool *mem, struct list *pvs);
|
||||
|
||||
int apply_lvname_restrictions(const char *name);
|
||||
|
||||
int validate_new_vg_name(struct cmd_context *cmd, const char *vg_name);
|
||||
|
||||
int generate_log_name_format(struct volume_group *vg, const char *lv_name,
|
||||
char *buffer, size_t size);
|
||||
|
||||
struct logical_volume *create_mirror_log(struct cmd_context *cmd,
|
||||
struct volume_group *vg,
|
||||
struct alloc_handle *ah,
|
||||
alloc_policy_t alloc,
|
||||
const char *lv_name,
|
||||
int in_sync,
|
||||
struct list *tags);
|
||||
|
||||
int set_lv(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
uint64_t sectors, int value);
|
||||
|
||||
int fill_vg_create_params(struct cmd_context *cmd,
|
||||
char *vg_name, struct vgcreate_params *vp_new,
|
||||
struct vgcreate_params *vp_def);
|
||||
#endif
|
||||
|
||||
@@ -165,5 +165,6 @@ int arg_count_increment(struct cmd_context *cmd, int a);
|
||||
const char *command_name(struct cmd_context *cmd);
|
||||
|
||||
int pvmove_poll(struct cmd_context *cmd, const char *pv, unsigned background);
|
||||
int lvconvert_poll(struct cmd_context *cmd, const char *lv_name, unsigned background);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,17 +15,12 @@
|
||||
|
||||
#include "tools.h"
|
||||
|
||||
#define DEFAULT_EXTENT 4096 /* In KB */
|
||||
|
||||
int vgcreate(struct cmd_context *cmd, int argc, char **argv)
|
||||
{
|
||||
size_t max_lv, max_pv;
|
||||
uint32_t extent_size;
|
||||
char *vg_name;
|
||||
struct vgcreate_params vp_new;
|
||||
struct vgcreate_params vp_def;
|
||||
struct volume_group *vg;
|
||||
const char *tag;
|
||||
alloc_policy_t alloc;
|
||||
int clustered;
|
||||
|
||||
if (!argc) {
|
||||
log_error("Please provide volume group name and "
|
||||
@@ -38,67 +33,29 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv)
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
||||
max_lv = arg_uint_value(cmd, maxlogicalvolumes_ARG, 0);
|
||||
max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG, 0);
|
||||
alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_NORMAL);
|
||||
|
||||
if (alloc == ALLOC_INHERIT) {
|
||||
log_error("Volume Group allocation policy cannot inherit "
|
||||
"from anything");
|
||||
vp_def.vg_name = NULL;
|
||||
vp_def.extent_size = DEFAULT_EXTENT_SIZE * 2;
|
||||
vp_def.max_pv = 0;
|
||||
vp_def.max_lv = 0;
|
||||
vp_def.alloc = ALLOC_NORMAL;
|
||||
vp_def.clustered = 0;
|
||||
if (fill_vg_create_params(cmd, argv[0], &vp_new, &vp_def))
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (!(cmd->fmt->features & FMT_UNLIMITED_VOLS)) {
|
||||
if (!max_lv)
|
||||
max_lv = 255;
|
||||
if (!max_pv)
|
||||
max_pv = 255;
|
||||
if (max_lv > 255 || max_pv > 255) {
|
||||
log_error("Number of volumes may not exceed 255");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) {
|
||||
log_error("Physical extent size may not be negative");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (arg_sign_value(cmd, maxlogicalvolumes_ARG, 0) == SIGN_MINUS) {
|
||||
log_error("Max Logical Volumes may not be negative");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (arg_sign_value(cmd, maxphysicalvolumes_ARG, 0) == SIGN_MINUS) {
|
||||
log_error("Max Physical Volumes may not be negative");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
/* Units of 512-byte sectors */
|
||||
extent_size =
|
||||
arg_uint_value(cmd, physicalextentsize_ARG, DEFAULT_EXTENT);
|
||||
|
||||
if (!extent_size) {
|
||||
log_error("Physical extent size may not be zero");
|
||||
return EINVALID_CMD_LINE;
|
||||
}
|
||||
|
||||
if (!validate_new_vg_name(cmd, vg_name)) {
|
||||
log_error("New volume group name \"%s\" is invalid", vg_name);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
if (validate_vg_create_params(cmd, &vp_new))
|
||||
return EINVALID_CMD_LINE;
|
||||
|
||||
/* Create the new VG */
|
||||
if (!(vg = vg_create(cmd, vg_name, extent_size, max_pv, max_lv, alloc,
|
||||
if (!(vg = vg_create(cmd, vp_new.vg_name, vp_new.extent_size,
|
||||
vp_new.max_pv, vp_new.max_lv, vp_new.alloc,
|
||||
argc - 1, argv + 1)))
|
||||
return ECMD_FAILED;
|
||||
|
||||
if (max_lv != vg->max_lv)
|
||||
if (vp_new.max_lv != vg->max_lv)
|
||||
log_warn("WARNING: Setting maxlogicalvolumes to %d "
|
||||
"(0 means unlimited)", vg->max_lv);
|
||||
|
||||
if (max_pv != vg->max_pv)
|
||||
if (vp_new.max_pv != vg->max_pv)
|
||||
log_warn("WARNING: Setting maxphysicalvolumes to %d "
|
||||
"(0 means unlimited)", vg->max_pv);
|
||||
|
||||
@@ -115,18 +72,13 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
if (!str_list_add(cmd->mem, &vg->tags, tag)) {
|
||||
log_error("Failed to add tag %s to volume group %s",
|
||||
tag, vg_name);
|
||||
tag, vp_new.vg_name);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_count(cmd, clustered_ARG))
|
||||
clustered = !strcmp(arg_str_value(cmd, clustered_ARG, "n"), "y");
|
||||
else
|
||||
/* Default depends on current locking type */
|
||||
clustered = locking_is_clustered();
|
||||
|
||||
if (clustered)
|
||||
/* FIXME: move this inside vg_create? */
|
||||
if (vp_new.clustered)
|
||||
vg->status |= CLUSTERED;
|
||||
else
|
||||
vg->status &= ~CLUSTERED;
|
||||
@@ -136,26 +88,26 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!lock_vol(cmd, vg_name, LCK_VG_WRITE | LCK_NONBLOCK)) {
|
||||
log_error("Can't get lock for %s", vg_name);
|
||||
if (!lock_vol(cmd, vp_new.vg_name, LCK_VG_WRITE | LCK_NONBLOCK)) {
|
||||
log_error("Can't get lock for %s", vp_new.vg_name);
|
||||
unlock_vg(cmd, VG_ORPHANS);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (!archive(vg)) {
|
||||
unlock_vg(cmd, vg_name);
|
||||
unlock_vg(cmd, vp_new.vg_name);
|
||||
unlock_vg(cmd, VG_ORPHANS);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
/* Store VG on disk(s) */
|
||||
if (!vg_write(vg) || !vg_commit(vg)) {
|
||||
unlock_vg(cmd, vg_name);
|
||||
unlock_vg(cmd, vp_new.vg_name);
|
||||
unlock_vg(cmd, VG_ORPHANS);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
unlock_vg(cmd, vg_name);
|
||||
unlock_vg(cmd, vp_new.vg_name);
|
||||
unlock_vg(cmd, VG_ORPHANS);
|
||||
|
||||
backup(vg);
|
||||
|
||||
@@ -20,8 +20,6 @@ static int _vgmerge_single(struct cmd_context *cmd, const char *vg_name_to,
|
||||
{
|
||||
struct volume_group *vg_to, *vg_from;
|
||||
struct lv_list *lvl1, *lvl2;
|
||||
struct pv_list *pvl;
|
||||
int active;
|
||||
|
||||
if (!strcmp(vg_name_to, vg_name_from)) {
|
||||
log_error("Duplicate volume group name \"%s\"", vg_name_from);
|
||||
@@ -43,71 +41,8 @@ static int _vgmerge_single(struct cmd_context *cmd, const char *vg_name_to,
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if ((active = lvs_in_vg_activated(vg_from))) {
|
||||
log_error("Logical volumes in \"%s\" must be inactive",
|
||||
vg_name_from);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Check compatibility */
|
||||
if (vg_to->extent_size != vg_from->extent_size) {
|
||||
log_error("Extent sizes differ: %d (%s) and %d (%s)",
|
||||
vg_to->extent_size, vg_to->name,
|
||||
vg_from->extent_size, vg_from->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (vg_to->max_pv &&
|
||||
(vg_to->max_pv < vg_to->pv_count + vg_from->pv_count)) {
|
||||
log_error("Maximum number of physical volumes (%d) exceeded "
|
||||
" for \"%s\" and \"%s\"", vg_to->max_pv, vg_to->name,
|
||||
vg_from->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (vg_to->max_lv &&
|
||||
(vg_to->max_lv < vg_to->lv_count + vg_from->lv_count)) {
|
||||
log_error("Maximum number of logical volumes (%d) exceeded "
|
||||
" for \"%s\" and \"%s\"", vg_to->max_lv, vg_to->name,
|
||||
vg_from->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Check no conflicts with LV names */
|
||||
list_iterate_items(lvl1, &vg_to->lvs) {
|
||||
char *name1 = lvl1->lv->name;
|
||||
|
||||
list_iterate_items(lvl2, &vg_from->lvs) {
|
||||
char *name2 = lvl2->lv->name;
|
||||
|
||||
if (!strcmp(name1, name2)) {
|
||||
log_error("Duplicate logical volume "
|
||||
"name \"%s\" "
|
||||
"in \"%s\" and \"%s\"",
|
||||
name1, vg_to->name, vg_from->name);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check no PVs are constructed from either VG */
|
||||
list_iterate_items(pvl, &vg_to->pvs) {
|
||||
if (pv_uses_vg(pvl->pv, vg_from)) {
|
||||
log_error("Physical volume %s might be constructed "
|
||||
"from same volume group %s.",
|
||||
pv_dev_name(pvl->pv), vg_from->name);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
list_iterate_items(pvl, &vg_from->pvs) {
|
||||
if (pv_uses_vg(pvl->pv, vg_to)) {
|
||||
log_error("Physical volume %s might be constructed "
|
||||
"from same volume group %s.",
|
||||
pv_dev_name(pvl->pv), vg_to->name);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (!vgs_are_compatible(cmd, vg_from, vg_to))
|
||||
goto error;
|
||||
|
||||
/* FIXME List arg: vg_show_with_pv_and_lv(vg_to); */
|
||||
|
||||
|
||||
@@ -50,11 +50,10 @@ static int _remove_pv(struct volume_group *vg, struct pv_list *pvl)
|
||||
static int _remove_lv(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
int *list_unsafe, struct list *lvs_changed)
|
||||
{
|
||||
struct lv_segment *snap_seg, *mirror_seg;
|
||||
struct lv_segment *snap_seg;
|
||||
struct list *snh, *snht;
|
||||
struct logical_volume *cow;
|
||||
struct lv_list *lvl;
|
||||
uint32_t extents;
|
||||
struct lvinfo info;
|
||||
int first = 1;
|
||||
|
||||
@@ -116,24 +115,10 @@ static int _remove_lv(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
* the mirrored LV also should be cleaned up.
|
||||
* Clean-up is currently done by caller (_make_vg_consistent()).
|
||||
*/
|
||||
if ((lv_info(cmd, lv, &info, 0, 0) && info.exists)
|
||||
|| first_seg(lv)->mirror_seg) {
|
||||
extents = lv->le_count;
|
||||
mirror_seg = first_seg(lv)->mirror_seg;
|
||||
if (!lv_empty(lv)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
if (!lv_add_virtual_segment(lv, 0, extents,
|
||||
get_segtype_from_string(cmd,
|
||||
"error"))) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
if (mirror_seg) {
|
||||
first_seg(lv)->status |= MIRROR_IMAGE;
|
||||
first_seg(lv)->mirror_seg = mirror_seg;
|
||||
}
|
||||
if ((lv_info(cmd, lv, &info, 0, 0) && info.exists) ||
|
||||
find_mirror_seg(first_seg(lv))) {
|
||||
if (!replace_lv_with_error_segment(lv))
|
||||
return_0;
|
||||
|
||||
if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) {
|
||||
log_error("lv_list alloc failed");
|
||||
@@ -260,8 +245,10 @@ static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
|
||||
}
|
||||
}
|
||||
|
||||
lvs_changed_altered:
|
||||
/* Remove lost mirror images from mirrors */
|
||||
list_iterate_items(lvl, &vg->lvs) {
|
||||
mirrored_seg_altered:
|
||||
mirrored_seg = first_seg(lvl->lv);
|
||||
if (!seg_is_mirrored(mirrored_seg))
|
||||
continue;
|
||||
@@ -320,6 +307,23 @@ static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
|
||||
vg_revert(vg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* mirrored LV no longer has valid mimages.
|
||||
* So add it to lvs_changed for removal.
|
||||
* For this LV may be an area of other mirror,
|
||||
* restart the loop. */
|
||||
if (!mimages) {
|
||||
if (!_remove_lv(cmd, lvl->lv,
|
||||
&list_unsafe, &lvs_changed))
|
||||
return_0;
|
||||
goto lvs_changed_altered;
|
||||
}
|
||||
|
||||
/* As a result of reconfigure_mirror_images(),
|
||||
* first_seg(lv) may now be different seg.
|
||||
* e.g. a temporary layer might be removed.
|
||||
* So check the mirrored_seg again. */
|
||||
goto mirrored_seg_altered;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path,
|
||||
const char *new_vg_path)
|
||||
{
|
||||
char *dev_dir;
|
||||
unsigned length;
|
||||
struct id id;
|
||||
int consistent = 1;
|
||||
int match = 0;
|
||||
@@ -35,25 +34,9 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path,
|
||||
vg_name_new = skip_dev_dir(cmd, new_vg_path, NULL);
|
||||
|
||||
dev_dir = cmd->dev_dir;
|
||||
length = strlen(dev_dir);
|
||||
|
||||
/* Check sanity of new name */
|
||||
if (strlen(vg_name_new) > NAME_LEN - length - 2) {
|
||||
log_error("New volume group path exceeds maximum length "
|
||||
"of %d!", NAME_LEN - length - 2);
|
||||
if (!validate_vg_rename_params(cmd, vg_name_old, vg_name_new))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!validate_new_vg_name(cmd, vg_name_new)) {
|
||||
log_error("New volume group name \"%s\" is invalid",
|
||||
vg_name_new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(vg_name_old, vg_name_new)) {
|
||||
log_error("Old and new volume group names must differ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_verbose("Checking for existing volume group \"%s\"", vg_name_old);
|
||||
|
||||
|
||||
@@ -212,6 +212,8 @@ static int _move_mirrors(struct volume_group *vg_from,
|
||||
|
||||
int vgsplit(struct cmd_context *cmd, int argc, char **argv)
|
||||
{
|
||||
struct vgcreate_params vp_new;
|
||||
struct vgcreate_params vp_def;
|
||||
char *vg_name_from, *vg_name_to;
|
||||
struct volume_group *vg_to, *vg_from;
|
||||
int opt;
|
||||
@@ -239,35 +241,48 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
|
||||
CORRECT_INCONSISTENT | FAIL_INCONSISTENT)))
|
||||
return ECMD_FAILED;
|
||||
|
||||
log_verbose("Checking for new volume group \"%s\"", vg_name_to);
|
||||
if ((vg_to = vg_lock_and_read(cmd, vg_name_to, NULL,
|
||||
LCK_VG_WRITE | LCK_NONBLOCK,
|
||||
0, 0))) {
|
||||
/* FIXME Remove this restriction */
|
||||
log_error("Volume group \"%s\" already exists", vg_name_to);
|
||||
unlock_vg(cmd, vg_name_from);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if ((active = lvs_in_vg_activated(vg_from))) {
|
||||
/* FIXME Remove this restriction */
|
||||
log_error("Logical volumes in \"%s\" must be inactive",
|
||||
vg_name_from);
|
||||
goto error;
|
||||
unlock_vg(cmd, vg_name_from);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
/* Set metadata format of original VG */
|
||||
/* FIXME: need some common logic */
|
||||
cmd->fmt = vg_from->fid->fmt;
|
||||
log_verbose("Checking for new volume group \"%s\"", vg_name_to);
|
||||
if ((vg_to = vg_lock_and_read(cmd, vg_name_to, NULL,
|
||||
LCK_VG_WRITE | LCK_NONBLOCK,
|
||||
0, 0))) {
|
||||
log_warn("Volume group \"%s\" already exists", vg_name_to);
|
||||
if (!vgs_are_compatible(cmd, vg_from,vg_to))
|
||||
goto error;
|
||||
} else {
|
||||
|
||||
/* Create new VG structure */
|
||||
if (!(vg_to = vg_create(cmd, vg_name_to, vg_from->extent_size,
|
||||
vg_from->max_pv, vg_from->max_lv,
|
||||
vg_from->alloc, 0, NULL)))
|
||||
goto error;
|
||||
/* Set metadata format of original VG */
|
||||
/* FIXME: need some common logic */
|
||||
cmd->fmt = vg_from->fid->fmt;
|
||||
|
||||
if (vg_from->status & CLUSTERED)
|
||||
vg_to->status |= CLUSTERED;
|
||||
vp_def.vg_name = NULL;
|
||||
vp_def.extent_size = vg_from->extent_size;
|
||||
vp_def.max_pv = vg_from->max_pv;
|
||||
vp_def.max_lv = vg_from->max_lv;
|
||||
vp_def.alloc = vg_from->alloc;
|
||||
vp_def.clustered = 0;
|
||||
|
||||
if (fill_vg_create_params(cmd, vg_name_to, &vp_new, &vp_def))
|
||||
return EINVALID_CMD_LINE;
|
||||
|
||||
if (validate_vg_create_params(cmd, &vp_new))
|
||||
return EINVALID_CMD_LINE;
|
||||
|
||||
if (!(vg_to = vg_create(cmd, vg_name_to, vp_new.extent_size,
|
||||
vp_new.max_pv, vp_new.max_lv,
|
||||
vp_new.alloc, 0, NULL)))
|
||||
goto error;
|
||||
|
||||
if (vg_from->status & CLUSTERED)
|
||||
vg_to->status |= CLUSTERED;
|
||||
}
|
||||
|
||||
/* Archive vg_from before changing it */
|
||||
if (!archive(vg_from))
|
||||
|
||||
Reference in New Issue
Block a user