1
0
mirror of git://sourceware.org/git/lvm2.git synced 2026-01-06 08:32:48 +03:00

Compare commits

...

81 Commits

Author SHA1 Message Date
Alasdair Kergon
9397833ceb pre-release review cleanups 2008-01-17 17:17:09 +00:00
Dave Wysochanski
e9433e83cd Minor test fix 2008-01-17 15:56:53 +00:00
Alasdair Kergon
f3c58100a0 fix default stripesize 2008-01-17 15:53:01 +00:00
Alasdair Kergon
8900231d99 fix default extent_size 2008-01-17 15:31:18 +00:00
Alasdair Kergon
c7a63b8a2b pre-release 2008-01-17 15:02:59 +00:00
Alasdair Kergon
90e90672a4 rename lv_remap_error 2008-01-17 13:54:05 +00:00
Alasdair Kergon
fa51e5c762 mirror log stuff 2008-01-17 13:37:51 +00:00
Alasdair Kergon
911f55d005 lvconvert/vgreduce fixes 2008-01-17 13:13:54 +00:00
Dave Wysochanski
d30eb4e570 Fixup vgsplit man page 2008-01-17 03:18:18 +00:00
Dave Wysochanski
67bcfb6947 Fix descriptions in the newly added test cases 2008-01-17 02:20:48 +00:00
Alasdair Kergon
3915a61b1e another lvconvert fix 2008-01-16 22:54:49 +00:00
Alasdair Kergon
c170d321e8 fix a _get_vgs return 2008-01-16 22:52:46 +00:00
Dave Wysochanski
2802c476ee Fix 'make check' runnable with recent versions of dmsetup.
Fix 'make check' to use DMDIR to check DM_DEV_DIR support in dmsetup.
Add basic test cases for mirrored LV.
Add basic test cases for lvconvert mirror.
Add basic test cases for pvmove.
	Jun'ichi Nomura <j-nomura@ce.jp.nec.com>

Add new vgsplit and vgmerge tests.
	Dave Wysochanski <dwysocha@redhat.com>
2008-01-16 21:21:22 +00:00
Alasdair Kergon
1050cebf7f additional safety check on new segment list 2008-01-16 20:00:01 +00:00
Dave Wysochanski
dad73465fc Create vgs_are_compatible() fn to check whether vgs are compatible for merging.
Add new vgmerge and vgsplit tests to check rejection of incompatible vgs.
Cleanup comments.
Bugzilla: bz251992

---
 lib/metadata/metadata-exported.h |    3 +
 lib/metadata/metadata.c          |   89 +++++++++++++++++++++++++++++++++-
 test/t-vgmerge-usage.sh          |  101 +++++++++++++++++++++++++++++++++++++++
 test/t-vgsplit-operation.sh      |   20 +++++++
 tools/vgmerge.c                  |   69 --------------------------
 tools/vgsplit.c                  |    5 -
 6 files changed, 215 insertions(+), 72 deletions(-)
2008-01-16 19:54:39 +00:00
Alasdair Kergon
8e2ac98fe2 adjust mirror log error message 2008-01-16 19:50:23 +00:00
Alasdair Kergon
9aaf0c36d5 fix to earlier checkin 2008-01-16 19:40:42 +00:00
Alasdair Kergon
1cb07e9cfd cope with stacked LVs as well as PVs when deciding which bits of mirrors to remove 2008-01-16 19:38:39 +00:00
Alasdair Kergon
f1ccdf25b1 allow a mirror to contain only one mimage 2008-01-16 19:18:51 +00:00
Alasdair Kergon
6dca497b27 fix mirror log manipulation during lv convert 2008-01-16 19:16:48 +00:00
Alasdair Kergon
42a83262a1 export find_temporary_mirror() 2008-01-16 19:13:51 +00:00
Alasdair Kergon
63ee9cbee6 move removable_pvs checking 2008-01-16 19:11:39 +00:00
Alasdair Kergon
3862f8ca7c reorder funcs 2008-01-16 19:09:35 +00:00
Alasdair Kergon
4ada7cffd0 Maintain lists of stacked LV segments using each LV. 2008-01-16 19:00:59 +00:00
Alasdair Kergon
a664ce4298 use scan_vgs_for_pvs to detect non-orphans without MDAs 2008-01-16 18:15:26 +00:00
Dave Wysochanski
8d7b6c6905 Remove unused 'list' param from vgsplit - conflict with maxlogicalvolumes param.
Initialize lvm command getopt buffer to zero before building options string.
Bugzilla: bz251992

---
 man/vgsplit.8      |    3 +--
 tools/commands.h   |    3 +--
 tools/lvmcmdline.c |    1 +
 3 files changed, 3 insertions(+), 4 deletions(-)
2008-01-16 17:14:56 +00:00
Alasdair Kergon
16d22d404a revert temp change 2008-01-16 15:26:40 +00:00
Alasdair Kergon
ccb24d5779 reword 2008-01-16 15:25:10 +00:00
Alasdair Kergon
8795b45cb4 Don't use block_on_error with mirror targets above version 1.12. 2008-01-16 15:24:25 +00:00
Dave Wysochanski
628d3bff45 Move more parameter validation into the library.
Update vgrename to call validate_vg_rename_params().
Fix vgcreate and vgsplit default arguments by adding defaults parameter to
fill_vg_create_params().
Add t-vgrename-usage.sh test.
Bugzilla: bz251992
---
 tools/toollib.c  |   32 ++++++++------------------------
 tools/toollib.h  |    5 ++---
 tools/vgcreate.c |   35 +++++++++++++++++++++--------------
 tools/vgrename.c |   35 ++++++-----------------------------
 tools/vgsplit.c  |   21 ++++++++++++++-------
 5 files changed, 51 insertions(+), 77 deletions(-)
2008-01-15 22:56:30 +00:00
Jonathan Earl Brassow
0336bc9de9 - The automatic log module loading patch proposed for the upstream kernel
works on '-'s, not '_'s.  This is due to the preference to have log
  module file names that do not mix '_'s and '-'s.
2008-01-15 22:48:11 +00:00
Dave Wysochanski
033cb21797 Update WHATS_NEW for vgsplit changes 2008-01-15 20:37:49 +00:00
Alasdair Kergon
09b98a45df lvconvert waits for initial completion by default 2008-01-14 21:11:47 +00:00
Dave Wysochanski
38857ba29e Allow vgcreate options as input to vgsplit when new vg is split destination. 2008-01-14 21:07:58 +00:00
Dave Wysochanski
1c7520ec8f Allow vgsplit into existing vg.
Add vgsplit tests to validate operation for new and existing vg destinations.
2008-01-11 21:43:16 +00:00
Dave Wysochanski
362b9769b2 Fixup lvm man pg 2008-01-11 20:24:25 +00:00
Dave Wysochanski
b2d68bd3d2 Refactor vgsplit for accepting existing vg as destination 2008-01-11 20:17:18 +00:00
Dave Wysochanski
0dc7e635d4 Update lvm man page to enumerate lvm tools. 2008-01-11 19:24:25 +00:00
Dave Wysochanski
80e070a857 Fix warning on conditional compile, unused variable 2008-01-11 17:44:26 +00:00
Dave Wysochanski
cc203245e4 Refactor vgcreate for parameter validation and add tests 2008-01-11 07:02:35 +00:00
Alasdair Kergon
2f9a65fc93 convert_lv 2008-01-10 22:21:44 +00:00
Alasdair Kergon
62738e8001 correct field name 2008-01-10 22:21:25 +00:00
Alasdair Kergon
5ecacf0c7f Add lv_convert field to default lvs output. 2008-01-10 19:25:07 +00:00
Alasdair Kergon
06b103c8d4 Various lvconvert/polldaemon-related fixes from NEC. See lvm-devel
for original patches & explanations.
2008-01-10 18:35:51 +00:00
Petr Rockai
d473b7bca8 Print warning when lvm tools are running as non-root. 2008-01-09 15:55:44 +00:00
Petr Rockai
50a1e81ba7 Amend previous commit. * does not match .files... 2008-01-09 15:33:25 +00:00
Petr Rockai
d9885b1b64 Add snapshot dmeventd library (enables dmeventd snapshot monitoring). 2008-01-09 15:32:19 +00:00
Zdeněk Kabeláč
0a9c8cada2 install conditionally fsadm.8 manpage 2008-01-09 14:17:58 +00:00
Petr Rockai
60f55f8461 Prevent pvcreate from overwriting MDA-less PVs belonging to active VGs. 2008-01-09 00:18:36 +00:00
Zdeněk Kabeláč
7bedaea38f added manpage 2008-01-08 17:01:42 +00:00
Zdeněk Kabeláč
9e4b87e798 readahead at least twice the strip size (same as raid0 driver does) 2008-01-08 16:47:10 +00:00
Zdeněk Kabeláč
95bf59095c added more safety checks
fixed error reporting commands
extended with Exa and Peta support
2008-01-08 16:45:43 +00:00
Milan Broz
e0f34a9720 Fix a segfault if using pvs with --all argument. (2.02.29) 2008-01-07 20:42:57 +00:00
Milan Broz
f3797c2a8e Update --uuid argument description in man pages. 2008-01-04 11:48:40 +00:00
Alasdair Kergon
30cbcccc80 Fix vgreduce PV list processing not to process every PV in the VG. 2008-01-03 19:03:32 +00:00
Alasdair Kergon
f49c0d696f typo 2007-12-28 15:13:38 +00:00
Alasdair Kergon
71f564ee5b lvconvert uses polldaemon now 2007-12-22 12:13:29 +00:00
Alasdair Kergon
4b0950aba5 a few more changes/fixes to recent code 2007-12-22 02:13:00 +00:00
Alasdair Kergon
8c3af822ec auto-collapse layers 2007-12-21 01:08:18 +00:00
Alasdair Kergon
878a207d19 more fixes 2007-12-20 23:12:27 +00:00
Alasdair Kergon
0537ad860a various cleanups in recent patches 2007-12-20 22:37:42 +00:00
Alasdair Kergon
2cdbbb1aea stacked mirror support (incomplete) 2007-12-20 18:55:46 +00:00
Alasdair Kergon
7af977d36b avoid some compiler warnings 2007-12-20 16:49:37 +00:00
Alasdair Kergon
9afff4cf30 Major restructuring of pvmove and lvconvert layer manipulation code 2007-12-20 15:42:55 +00:00
Alasdair Kergon
48ba9734aa post-release 2007-12-20 15:16:14 +00:00
Alasdair Kergon
897fc59f72 pre-release 2007-12-20 15:12:57 +00:00
Alasdair Kergon
947e44ae67 tweak usage text 2007-12-17 14:47:22 +00:00
Alasdair Kergon
e44843beba replace fsadm.c with fsadm.sh 2007-12-17 12:31:50 +00:00
Alasdair Kergon
147482ea69 Build changes to replace fsadm C program with shell script. 2007-12-17 12:23:24 +00:00
Alasdair Kergon
09091c5cf8 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.
2007-12-14 21:53:02 +00:00
Alasdair Kergon
50827a5f69 more readahead node fixes/debug messages 2007-12-14 19:49:27 +00:00
Alasdair Kergon
2d6444c924 Fix deptree to pass new name to _resume_node after a rename. 2007-12-14 17:57:04 +00:00
Alasdair Kergon
1d2675d9aa Add node operation stack debug messages. 2007-12-14 17:26:09 +00:00
Alasdair Kergon
ad98990a8e Report error when empty device name passed to readahead functions. 2007-12-13 02:25:45 +00:00
Alasdair Kergon
8e58c143f2 post-release 2007-12-05 22:48:06 +00:00
Alasdair Kergon
556a4a2395 clarify 2007-12-05 22:45:56 +00:00
Alasdair Kergon
5be987b40f pre-release
N.B. This is a big release and some regressions are inevitable.
2007-12-05 22:19:24 +00:00
Alasdair Kergon
066bc35e69 export can_split parameter until rest of pvmove allocation restructuring gets done 2007-12-05 22:11:20 +00:00
Alasdair Kergon
403779437c round readahead to multiple of page size in tools 2007-12-05 19:24:32 +00:00
Alasdair Kergon
fb806f61d4 Fix minimum readahead debug message. 2007-12-05 18:57:34 +00:00
Alasdair Kergon
6ce306661c post-release 2007-12-05 17:14:30 +00:00
83 changed files with 7814 additions and 4057 deletions

View File

@@ -1 +1 @@
2.02.29-cvs (2007-08-24)
2.02.30-cvs (2008-01-17)

View File

@@ -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.

View File

@@ -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.

4330
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -16,7 +16,7 @@ srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
SUBDIRS += mirror
SUBDIRS += mirror snapshot
include $(top_srcdir)/make.tmpl

View File

@@ -0,0 +1,3 @@
process_event
register_device
unregister_device

View File

@@ -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)/$<

View 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, &params);
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;
}

View File

@@ -16,7 +16,7 @@ srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
SUBDIRS += mirror
SUBDIRS += mirror snapshot
include $(top_srcdir)/make.tmpl

View File

@@ -0,0 +1,3 @@
process_event
register_device
unregister_device

View 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)/$<

View 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, &params);
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;
}

View File

@@ -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"
#}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -60,6 +60,7 @@ static struct flag _lv_flags[] = {
{VIRTUAL, NULL},
{SNAPSHOT, NULL},
{ACTIVATE_EXCL, NULL},
{CONVERTING, NULL},
{0, NULL}
};

View File

@@ -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")) &&

View File

@@ -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

View File

@@ -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));

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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.")

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
View 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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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 ...]

View File

@@ -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)

View File

@@ -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.

View File

@@ -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)

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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:

View File

@@ -16,10 +16,6 @@ srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
ifeq ("@FSADM@", "yes")
SUBDIRS += fsadm
endif
SOURCES =\
dumpconfig.c \
formats.c \

View File

@@ -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",

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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) {

View File

@@ -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))) {

View File

@@ -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];

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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); */

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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))