1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-09-27 05:44:18 +03:00

Compare commits

..

71 Commits

Author SHA1 Message Date
David Teigland
fdf3d0501d lvconvert: use command defs for thin and cache 2016-12-07 16:59:58 -06:00
David Teigland
072ef008ea lvconvert: use command defs for pool creation 2016-12-07 15:56:30 -06:00
David Teigland
5b5e37791c lvconvert: add startpoll command using command def
This is a new explicit version of 'lvconvert LV'
which has never been well defined or understood.
2016-12-02 14:06:17 -06:00
David Teigland
e916797ec2 lvconvert: add hidden lv access restrictions
This copies the previous logic which is probably
not correct.
2016-12-02 14:00:24 -06:00
David Teigland
51205a1bc0 lvconvert: remove unused calls for snapshots
snapshot commands are no longer called from the
monolithic lvconvert code, so remove the unused code.
2016-12-02 14:00:24 -06:00
David Teigland
a7840b31a1 lvconvert: snapshot: use command definitions
Lift all the snapshot utilities (merge, split, combine)
out of the monolithic lvconvert implementation, using
the command definitions.  The old code associated with
these commands is now unused and will be removed separately.
2016-12-02 14:00:23 -06:00
David Teigland
4f60bcd9f2 lvconvert: remove unused calls for repair and replace
repair and replace are no longer called from the
monolithic lvconvert code, so remove the unused code.
2016-12-02 14:00:23 -06:00
David Teigland
6c119560ab lvconvert: repair and replace: use command definitions
This lifts the lvconvert --repair and --replace commands
out of the monolithic lvconvert implementation.  The
previous calls into repair/replace can no longer be
reached and will be removed in a separate commit.
2016-12-02 14:00:23 -06:00
David Teigland
2cf8f93fe6 lvchange: make use of command definitions
Reorganize the lvchange code to take advantage of
the command definition, and remove the validation
that is done by the command definintion rules.
2016-12-02 14:00:18 -06:00
David Teigland
d658ddfc70 process_each_lv: command def validation for LV in other position
Determine which position arg the LV being processed is.
Use this position to check the LV against the command def.
2016-12-01 15:51:24 -06:00
David Teigland
268374c235 process_each_lv: add check_single_lv function
The new check_single_lv() function is called prior to the
existing process_single_lv().  If the check function returns 0,
the LV will not be processed.

The check_single_lv function is meant to be a standard method
to validate the combination of specific command + specific LV,
and decide if the combination is allowed.  The check_single
function can be used by anything that calls process_each_lv.

As commands are migrated to take advantage of command
definitions, each command definition gets its own entry
point which calls process_each for itself, passing a
pair of check_single/process_single functions which can
be specific to the narrowly defined command def.
2016-11-30 16:37:29 -06:00
David Teigland
45e23131b8 commands: new method for defining commands
. Define a prototype for every lvm command.
. Match every user command with one definition.
. Generate help text and man pages from them.

The new file command-lines.in defines a prototype for every
unique lvm command.  A unique lvm command is a unique
combination of: command name + required option args +
required positional args.  Each of these prototypes also
includes the optional option args and optional positional
args that the command will accept, a description, and a
unique string ID for the definition.  Any valid command
will match one of the prototypes.

Here's an example of the lvresize command definitions from
command-lines.in, there are three unique lvresize commands:

lvresize --size SizeMB LV
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync, --reportformat String, --resizefs,
--stripes Number, --stripesize SizeKB, --poolmetadatasize SizeMB
OP: PV ...
ID: lvresize_by_size
DESC: Resize an LV by a specified size.

lvresize LV PV ...
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat String, --resizefs, --stripes Number, --stripesize SizeKB
ID: lvresize_by_pv
DESC: Resize an LV by specified PV extents.
FLAGS: SECONDARY_SYNTAX

lvresize --poolmetadatasize SizeMB LV_thinpool
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat String, --stripes Number, --stripesize SizeKB
OP: PV ...
ID: lvresize_pool_metadata_by_size
DESC: Resize a pool metadata SubLV by a specified size.

The three commands have separate definitions because they have
different required parameters.  Required parameters are specified
on the first line of the definition.  Optional options are
listed after OO, and optional positional args are listed after OP.

This data is used to generate corresponding command definition
structures for lvm in command-lines.h.  usage/help output is also
auto generated, so it is always in sync with the definitions.

Example of the corresponding generated structure in
command-lines.h for the first lvresize prototype
(these structures are never edited directly):

commands[83].name = "lvresize";
commands[83].command_line_id = "lvresize_by_size";
commands[83].command_line_enum = lvresize_by_size_CMD;
commands[83].fn = lvresize;
commands[83].ro_count = 1;
commands[83].rp_count = 1;
commands[83].oo_count = 22;
commands[83].op_count = 1;
commands[83].cmd_flags = 0;
commands[83].desc = "DESC: Resize an LV by a specified size.";
commands[83].usage = "lvresize --size Number[m|unit] LV"
" [ --resizefs, --poolmetadatasize Number[m|unit], COMMON_OPTIONS ]"
" [ PV ... ]";
commands[83].usage_common =
" [ --alloc contiguous|cling|cling_by_tags|normal|anywhere|inherit, --nosync, --reportformat String, --autobackup y|n, --stripes Number, --stripesize Number[k|unit], --nofsck, --commandprofile String, --config String, --debug, --driverloaded y|n, --help, --profile String, --quiet, --verbose, --version, --yes, --test, --force, --noudevsync ]";
commands[83].required_opt_args[0].opt = size_ARG;
commands[83].required_opt_args[0].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[83].required_pos_args[0].pos = 1;
commands[83].required_pos_args[0].def.val_bits = val_enum_to_bit(lv_VAL);
commands[83].optional_opt_args[0].opt = commandprofile_ARG;
commands[83].optional_opt_args[0].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[1].opt = config_ARG;
commands[83].optional_opt_args[1].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[2].opt = debug_ARG;
commands[83].optional_opt_args[3].opt = driverloaded_ARG;
commands[83].optional_opt_args[3].def.val_bits = val_enum_to_bit(bool_VAL);
commands[83].optional_opt_args[4].opt = help_ARG;
commands[83].optional_opt_args[5].opt = profile_ARG;
commands[83].optional_opt_args[5].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[6].opt = quiet_ARG;
commands[83].optional_opt_args[7].opt = verbose_ARG;
commands[83].optional_opt_args[8].opt = version_ARG;
commands[83].optional_opt_args[9].opt = yes_ARG;
commands[83].optional_opt_args[10].opt = test_ARG;
commands[83].optional_opt_args[11].opt = alloc_ARG;
commands[83].optional_opt_args[11].def.val_bits = val_enum_to_bit(alloc_VAL);
commands[83].optional_opt_args[12].opt = autobackup_ARG;
commands[83].optional_opt_args[12].def.val_bits = val_enum_to_bit(bool_VAL);
commands[83].optional_opt_args[13].opt = force_ARG;
commands[83].optional_opt_args[14].opt = nofsck_ARG;
commands[83].optional_opt_args[15].opt = nosync_ARG;
commands[83].optional_opt_args[16].opt = noudevsync_ARG;
commands[83].optional_opt_args[17].opt = reportformat_ARG;
commands[83].optional_opt_args[17].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[18].opt = resizefs_ARG;
commands[83].optional_opt_args[19].opt = stripes_ARG;
commands[83].optional_opt_args[19].def.val_bits = val_enum_to_bit(number_VAL);
commands[83].optional_opt_args[20].opt = stripesize_ARG;
commands[83].optional_opt_args[20].def.val_bits = val_enum_to_bit(sizekb_VAL);
commands[83].optional_opt_args[21].opt = poolmetadatasize_ARG;
commands[83].optional_opt_args[21].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[83].optional_pos_args[0].pos = 2;
commands[83].optional_pos_args[0].def.val_bits = val_enum_to_bit(pv_VAL);
commands[83].optional_pos_args[0].def.flags = ARG_DEF_FLAG_MAY_REPEAT;

Every user-entered command is compared against the set of
command structures, and matched with one.  An error is
reported if an entered command does not have the required
parameters for any definition.  The closest match is printed
as a suggestion, and running lvresize --help will display
the usage for each possible lvresize command.

The prototype syntax used for help/man output includes
required --option and positional args on the first line,
and optional --option and positional args enclosed in [ ]
on subsequent lines.

  command_name <required_opt_args> <required_pos_args>
          [ <optional_opt_args> ]
          [ <optional_pos_args> ]

$ lvresize --help
  lvresize - Resize a logical volume

  Resize an LV by a specified size.
  lvresize --size Number[m|unit] LV
        [ --resizefs,
          --poolmetadatasize Number[m|unit],
          COMMON_OPTIONS ]
        [ PV ... ]

  Resize a pool metadata SubLV by a specified size.
  lvresize --poolmetadatasize Number[m|unit] LV_thinpool
        [ COMMON_OPTIONS ]
        [ PV ... ]

  Common options:
        [ --alloc contiguous|cling|cling_by_tags|normal|anywhere|inherit,
          --nosync,
          --reportformat String,
          --autobackup y|n,
          --stripes Number,
          --stripesize Number[k|unit],
          --nofsck,
          --commandprofile String,
          --config String,
          --debug,
          --driverloaded y|n,
          --help,
          --profile String,
          --quiet,
          --verbose,
          --version,
          --yes,
          --test,
          --force,
          --noudevsync ]

  (Use --help --help for usage notes.)

$ lvresize --poolmetadatasize 4
  Failed to find a matching command definition.
  Closest command usage is:
  lvresize --poolmetadatasize Number[m|unit] LV_thinpool

Command definitions that are not to be advertised/suggested
have the flag SECONDARY_SYNTAX.  These commands will not be
printed in the normal help output.

Man page prototypes are also generated from the same original
command definitions, and are always in sync with the code
and help text.

Very early in command execution, a matching command definition
is found.  lvm then knows the operation being done, and that
the provided args conform to the definition.  This will allow
lots of ad hoc checking/validation to be removed throughout
the code.

Each command definition can also be routed to a specific
function to implement it.  The function is associated with
an enum value for the command definition (generated from
the ID string.)  These per-command-definition implementation
functions have not yet been created, so all commands
currently fall back to the existing per-command-name
implementation functions.

Using per-command-definition functions will allow lots of
code to be removed which tries to figure out what the
command is meant to do.  This is currently based on ad hoc
and complicated option analysis.  When using the new
functions, what the command is doing is already known
from the associated command definition.

So, this first phase validates every user-entered command
against the set of command prototypes, then calls the existing
implementation.  The second phase can associate an implementation
function with each definition, and take further advantage of the
known operation to avoid the complicated option analysis.
2016-11-30 16:37:20 -06:00
Alasdair G Kergon
6de05cf5f5 raid: Remove fixed FIXME 2016-11-15 20:45:55 +00:00
Peter Rajnoha
cd0736a945 dbus: also recognize error state for missing service that comes from original D-Bus 2016-11-15 10:09:11 +01:00
Peter Rajnoha
68d6d342f8 dbus: only log msg as debug if lvm2-lvmdbusd unit missing for D-Bus notification
Do not emit warning message but only log debug message if
lvm2-lvmdbusd.service unit is missing and at the same time
we have global/notify_dbus=1 (which is used by default if we
configured sources with "--enable-notify-dbus"). We don't want
hard dependency between LVM2 and lvmdbusd so it's enough to log
only debug message in this case.
2016-11-14 14:53:19 +01:00
Zdenek Kabelac
e7da8e7e1f tests: fix checking for pvmove LV
Use consitently egrep.

TODO: make probably aux func
2016-11-14 12:55:43 +01:00
Zdenek Kabelac
a7691cdebb cleanup: debug trace and indent change 2016-11-11 16:58:20 +01:00
Zdenek Kabelac
6db5b91231 cleanup: avoid using double __ in extracted image name 2016-11-11 16:58:20 +01:00
Zdenek Kabelac
1bdcb01f63 cleanup: add dots to debug messages 2016-11-11 16:58:20 +01:00
Zdenek Kabelac
b38564b8dc tests: do not set zero interval in aux lvm.conf
0 interval leads as of now to a busy loop with lvmetad and command.

Avoid testing this patological case.

TODO: Code should possibly translate zero interval into some small
sleep. With lvmpolld it's already 1/10s
2016-11-11 16:58:20 +01:00
Zdenek Kabelac
de3d054f78 tests: avoid using polling 2016-11-11 16:58:20 +01:00
Zdenek Kabelac
4a2250f9ce tests: update make targets
Add  new targets:

make check_lvmpolld
make check_cluster_lvmpolld
make check_lvmetad_lvmpolld
make check_all_lvmpolld

So check_lvmetad runs only base lvmetad test - to much
logic of remaining targets.

Previous behavior is available via  check_all_lvmpolld.
2016-11-11 16:58:20 +01:00
Zdenek Kabelac
d8fc4d093e conf: support zero for missing_stripe_filler
Make it easier to replace missing segments with 'zero' returning
target - otherwise user would have to create some extra target
to provide zeros as /dev/zero can't be used (not a block device).

Also break code loop when segment is found and make it an INTERNAL_ERROR
where it's missing.
2016-11-11 16:58:16 +01:00
Zdenek Kabelac
e54cce245f cleanup: skip checking for just assigned string
When 'stripe_filler' has been just set to 'error',
do not check it again for not being 'error'.
2016-11-11 16:52:24 +01:00
Zdenek Kabelac
59b29716e5 lvconvert: repair accepts interval and background
For mirror and raid we expose --interval and --background on
command line (instead of using just always using compiled-in defaults).
2016-11-11 16:51:33 +01:00
Alasdair G Kergon
f8b3b0bc9a lvconvert: Introduce enum to separate cases.
Start to separate out the different things that lvconvert does by using
an enum to identify 12 distinct cases.
2016-11-11 00:27:01 +00:00
Heinz Mauelshagen
b11f4f93d7 dmsetup: [v2] return 0 for [--]{version,help}
Leverage 0b1c796420 to cope with "dmsetup SubCommand --{help,version}"
and bail out directly when _report_init() fails.

Related: rhbz1393692
2016-11-10 18:09:37 +01:00
Heinz Mauelshagen
0b1c796420 dmsetup: return 0 for [--]{version,help}
dmsetup regressed returning 1 when properly processing
version or help subcommands.

Resolves: rhbz1393692
2016-11-10 17:03:04 +01:00
Zdenek Kabelac
c1862ea84c tests: for repair we need neewer version
Older cache targets were not able to report write failures.
2016-11-08 16:01:35 +01:00
Zdenek Kabelac
2ec3e7dca8 tests: test raid0_meta type
Enable testing of raid0_meta type.
Also slow down devices a bit more.
2016-11-08 16:01:35 +01:00
Zdenek Kabelac
ada5733c56 raid: faster rmeta clearing
Instead of clearing multiple rmeta device with sequential activation
process and waiting for udev for every _rmeta device separately,
activate all _rmeta devices first and then clear them and deactivate
afterwards.

Also update some tracing messages.

When anyhing goes wrong during clearing process, always try to
deactivate as much _rmeta devices as possible before fail.
2016-11-08 16:00:14 +01:00
Alasdair G Kergon
9e03fc3c2a post-release 2016-11-05 01:14:06 +00:00
Alasdair G Kergon
eed708dbd9 pre-release 2016-11-05 01:03:32 +00:00
Alasdair G Kergon
6dce0e9489 tests: Disable 3-leg raid1 pre-sync repair attempt
Test fails as it doesn't ensure device isn't already in sync.
2016-11-04 00:23:08 +00:00
Tony Asleson
461d340bd7 lvmdbusd: Remove the periodic timer task
This code is no longer needed because the back ground task has been
removed.  Will add back if we change the design and end up utilizing
multiple worker threads.
2016-11-03 18:38:10 -05:00
Tony Asleson
ee0c9e7b23 lvmdbusd: Take out background thread
There is no reason to create another background task when the task that
created it is going to block waiting for it to finish.  Instead we will
just execute the logic in the worker thread that is servicing the worker
queue.
2016-11-03 18:29:06 -05:00
Zdenek Kabelac
a9ee86ccf2 dmeventd: provide message context
Show device name with printed message.
Also use different level for printing error message and
standard informational message.
2016-11-03 17:49:07 +01:00
Zdenek Kabelac
4e26024add cleanup: use WARNING prefix for log_warn
Use capital WARNING prefix for log_warn() messages.
2016-11-03 17:49:07 +01:00
Zdenek Kabelac
0d95082aa9 cleanup: remove goto
Move goto path into 'if()' branch.
2016-11-03 17:49:07 +01:00
Zdenek Kabelac
9cbe4c1af9 log_info to log_very_verbose
Translate log_info() into log_very_verbose() which is macro
supposed to be used by our code.

log_info() is internal macro with eventually some 'symbolic' meaning
in syslogging daemons.
2016-11-03 17:49:07 +01:00
Zdenek Kabelac
cc19cc07f7 log_info to log_warn
Switch to log_warn level when we are reporting these message.
2016-11-03 17:49:07 +01:00
Zdenek Kabelac
e3775173b4 cleanup: log_info to log_error
Switch to more appropriate logging level.
2016-11-03 17:49:07 +01:00
Zdenek Kabelac
ee13f265f0 libdm: use dm_log_with_errno always
Instead of compiling 2 log call for 2 different logging functions,
and runtime decide which version to use - use only 'newer' function
and when user sets his own OLD dm_log logging translate it runtime
for old arg list set.

The positive part is - we get shorter generated library,
on the negative part this translation means, we always have evaluate
all args and print the message into local on stack buffer, before
we can pass this buffer to the users' logging function with proper
expected parameters (and such function may later decide to discard
logging based on message level so whole printing was unnecessary).
2016-11-03 17:49:07 +01:00
David Teigland
221d8ff2a4 tests: check that pvscan cannot activate exported VG 2016-11-03 11:43:37 -05:00
Zdenek Kabelac
28b210f4fa cleanup: add 'static' for local struct 2016-11-03 12:43:09 +01:00
Zdenek Kabelac
1db4b81d5a cleanup: drop unused attribute
In this function we pass args through so make the function
header look the same as with _default_log().
2016-11-03 12:43:09 +01:00
Zdenek Kabelac
4b4d19e3aa dmeventd: separate dm and dmeventd logging
Ensure different logging function for dmeventd.c logging
and dm and lvm library.

We can recognize we want to show every log_info() and
log_notice() message from dmeventd.c code while not
exposing those from libdm/libdevmapper-event

Also switch to use log with errno - it's not changing
anything and doesn't bring any more features yet to dmeventd
logging but we just properly pass dm_errno_or_class properly
through the whole code stack for possible future use
(i.e. support of class logging for dmeventd).
2016-11-03 12:43:09 +01:00
Zdenek Kabelac
cd468b6218 dmeventd: debug only subsystemd with # sign
Reword the logging logic and try to restore previous logging
behavior for 'standalone' running daemon while preserving
debuggable feautures it has gained.

So actual rules:

dmeventd without any '-d' option will syslog all messages
from dmeventd.c it dmeventd plugins.

log_notice()==log_verbose()
log_info()==log_very_verbose()
But to show also log_debug() used has to give '-ddd'.

When user specified '-d, -dd, -ddd, -dddd' it
will also enable tracing of messages from libdm & lib
executed code - which is mainly useful for testing
i.e.: 'dmeventd -fldddd'
2016-11-03 12:43:09 +01:00
Zdenek Kabelac
33dd1f7747 dmeventd: abort on internal error
Provide same support for DM_ABORT_ON_INTERNAL_ERRORS we do have
with default libdm logging  as  dmeventd is libdm based tool.
2016-11-03 12:43:09 +01:00
Zdenek Kabelac
e50d434a35 libdm: report logging with errno as changed default
When user changes logging with 'dm_log_with_errno_init()'
also report this as non default dm logging.
2016-11-03 12:43:09 +01:00
Zdenek Kabelac
6af26273cb logging: add more log macros
Introduce macros:

log_level(), log_stderr(), log_once(), log_bypass_report()

For easier and more consisten way how to 'decoder' bits
of info from passed 'level'.

This patch fixes potential problem when 'level' of message
might not have always masked right bits.
2016-11-03 12:43:09 +01:00
Tony Asleson
96118a2508 lvmdbusd: Stop using threads for job wait
Instead of creating a thread to handle the case where a client
is calling job.Wait, we will utilize a timer.  This significantly
reduces the number of threads that get created and destroyed while
the service is running.
2016-11-02 16:39:13 -05:00
Tony Asleson
95abadd13c lvmdbusd: main.py: change debug msg text 2016-11-02 16:39:13 -05:00
Tony Asleson
60de09b00c lvmdbusd: Don't use dbus lib in worker thread
Simplify the code paths so that we don't utilize the dbus library code
when we are in worker thread context.

ref. https://bugs.freedesktop.org/show_bug.cgi?id=98521
2016-11-02 16:39:03 -05:00
Tony Asleson
38dd79307a lvmdbusd: Execute load in main thread
We will fetch the lvm state in non-main thread and only process the new
data with the main thread to prevent hanging the main thread event loop.

ref. https://bugs.freedesktop.org/show_bug.cgi?id=98521
2016-11-02 16:38:49 -05:00
Tony Asleson
24803bbaad lvmdbusd: Return results in main thread
Also introduce some additional new code to execute code other code
in main thread too.

ref. https://bugs.freedesktop.org/show_bug.cgi?id=98521
2016-11-02 16:38:00 -05:00
Tony Asleson
c8e8439b3d lvmdbusd: Use timer instead of thread
We had a thread sitting around for cleaning up other processes, changed to
a periodic timer task.
2016-11-02 16:35:45 -05:00
David Teigland
68e7d34965 pvscan: fix autoactivation of exported VG
pvscan --cache -aay was activating LVs in exported VGs
when it should not.

It appears that this was a regression from commit 9b640c3684
"pvscan: use process_each_vg for autoactivate".
2016-11-02 16:29:52 -05:00
Peter Rajnoha
4585785613 blkdeactivate: deactivate dev stack if dev on top already unmounted
If blkdeactivate finds out that the device on top of device stack
is already unmounted, it still proceeds with device stack deactivation
underneath now.

This situation can happen if blkdeactivate is started and the mount
point is unmounted in parallel by chance (so when blkdeactivate
gets the the actual umount call, the device is not mounted anymore).
Before, the blkdeactivate added such device to skip list which caused
all the stack underneath to be skipped too on deactivation. Now, we
proceed just as if blkdeactivate did the umount itself.

For example, in the example below, the vg-lvol0 is mounted on /mnt/test
when blkdeactivate is called, but it gets unmounted in parallel later
on when blkdeactivate gets to the actual umount call.

Before this patch (vg-lvol0 underneath not deactivated):

  $ blkdeactivate -u
  Deactivating block devices:
    [UMOUNT]: unmounting vg-lvol0 (dm-2) mounted on /mnt/test... skipping

With this patch applied (vg-lvol0 underneath still deactivated):

  $ blkdeactivate -u
  Deactivating block devices:
    [UMOUNT]: unmounting vg-lvol0 (dm-2) mounted on /mnt/test... already unmounted
    [LVM]: deactivating Logical Volume vg/lvol0... done
2016-11-01 16:52:51 +01:00
Heinz Mauelshagen
a9651adc84 test: add raid4 checks to respective tests
Add missing checks for valid raid4 mapping to tests.
2016-10-28 21:54:10 +02:00
Heinz Mauelshagen
e611f82a11 lvconvert: fix raid repair regression
Limit prevention to raid1 as intended with commit 8270ff5702.

Related to rhbz1311765
2016-10-28 21:45:00 +02:00
Heinz Mauelshagen
8270ff5702 lvconvert: prevent non-synced raid1 primary leg repair
(Automatic) repair may not be allowed during the initial sync of an upconverted
linear LV, because the data on the failing, primary leg hasn't been completely
synchronized to the N-1 other legs of the raid1 LV (replacing failed legs during
repair involves discontinuing access to any replaced legs data, thus preventing
data recovery on the primary leg e.g. via dd_rescue).

Even though repair would not cause data loss when adding legs to a fully synced
raid1 LV, we don't have information yet defining this state yet (e.g. a raid1
LV flag telling the fully synchronized status before any legs were added),
hence can't automatically decide to allow to repair.

If nonetheless a repair on a non-synced raid1 LVs is intended, the "--force"
option has to be provided.

Resolves: rhbz1311765
2016-10-28 15:55:10 +02:00
Heinz Mauelshagen
e118b65d65 lvconvert: check for supported raid0/raid4 segtypes
Validate kernel support for raid0/raid4 on given and
requested segtype before requesting conversions on them.

Because raid10 wasn't present in old RAID targets, add
the same validation to be prepared once we support them.
2016-10-27 16:44:32 +02:00
Heinz Mauelshagen
61ae07966d lvconvert-raid-takeover.sh: fix test 2016-10-27 16:38:15 +02:00
Heinz Mauelshagen
ff05ed7afd lvchange/vgchange/lvconvert: prevent raid4 creation/activation/conversion on non-supporting raid targets
Check for dm-raid target version with non-standard raid4 mapping expecting the dedicated
parity device in the last rather than the first slot and prohibit to create, activate or
convert to such LVs from striped/raid0* or vice-versa in order to avoid data corruption.

Add related tests to lvconvert-raid-takeover.sh

Resolves: rhbz1388962
2016-10-27 11:42:07 +02:00
Heinz Mauelshagen
e84f527cd3 lvconvert: revert to only letting raid4 through to lv_raid_convert()
Commit de78e8eae7 allowed to let any raid layout through
which we want to avoid until further validation cleanups.

Related to rhbz1386184
2016-10-26 17:54:19 +02:00
Heinz Mauelshagen
0468f5da6d raid_manip: fix typo
Related to rhbz1386184
2016-10-26 17:53:55 +02:00
Bryn M. Reeves
021715e897 dmsetup: remove stray '\n' in delete log message 2016-10-24 17:21:35 +01:00
Bryn M. Reeves
5eda393488 dmsetup: obey --programid when deleting regions 2016-10-24 17:21:18 +01:00
Heinz Mauelshagen
de78e8eae7 lvconvert: position dedicated parity device in raid4 conversions porperly
On conversions between striped/raid0* and raid4, the kernel expects
the dedicated raid4 parity SubLVs in the first segment area rather than
in the last it's been allocated to, thus the data mapping ain't proper.

Enhance lvconvert (lib/metadata/raid_manip.c) to shift the dedicated
parity SubLVs on conversions from striped/raid0* to raid4 and vice-versa.

In case of raid0_meta -> raid4 where the MD raid0 personality already has
stored RAID array device positions in the superblocks, the MetaLVs have to
be cleared so that the kernel doesn't fail validating the array positions
after lvm has shifted them up by one.

Add more tests to lvconvert-raid-takeover.sh including one to check for
mapping flaws by converting a created raid4 with filesystem -> striped
and fsck it.

Whilst on it:
- add missing direct striped -> raid4 conversion to the takeover array
  to avoid an intermim conversion from striped -> raid0*
- clean up the takeover array
- allow lvconvert to actually call lv_raid_convert() on all takeover requests
  in order to check parameters and display messages provided by takeover
  functions rather than just "...not supported" from within lvconvert
- fix a typo

Resolves: rhbz1386148
2016-10-21 19:00:31 +02:00
Alasdair G Kergon
34da83d729 dmsetup: Produce partial output if dev disappears.
If a device disappears after obtaining the list of devices but before
processing it as a member of that list, dmsetup exits with a failure code.

Most commands still produce what output they can in these circumstances,
but 'ls --tree' and 'info -c' with fields depending on device dependencies
didn't.  Change this.
2016-10-18 18:01:52 +01:00
84 changed files with 3782 additions and 1772 deletions

View File

@@ -1 +1 @@
2.02.167(2)-git (2016-09-26)
2.02.168(2)-git (2016-11-05)

View File

@@ -1 +1 @@
1.02.136-git (2016-09-26)
1.02.137-git (2016-11-05)

View File

@@ -1,5 +1,18 @@
Version 2.02.167 -
======================================
Version 2.02.168 -
====================================
Only log msg as debug if lvm2-lvmdbusd unit missing for D-Bus notification.
Missing stripe filler now could be also 'zero'.
lvconvert --repair accepts --interval and --background option.
More efficiently prepare _rmeta devices when creating a new raid LV.
Version 2.02.167 - 5th November 2016
====================================
Use log_error in regex and sysfs filter to describe reason of failure.
Fix blkdeactivate to deactivate dev stack if dev on top already unmounted.
Prevent non-synced raid1 repair unless --force
Prevent raid4 creation/conversion on non-supporting kernels
Add direct striped -> raid4 conversion
Fix raid4 parity image pair position on conversions from striped/raid0*
Fix a few unconverted return code values for some lvconvert error path.
Disable lvconvert of thin pool to raid while active.
Disable systemd service start rate limiting for lvm2-pvscan@.service.

View File

@@ -1,5 +1,16 @@
Version 1.02.136 -
======================================
Version 1.02.137 -
====================================
Version 1.02.136 - 5th November 2016
====================================
Log failure of raid device with log_error level.
Use dm_log_with_errno and translate runtime to dm_log only when needed.
Make log messages from dm and lvm library different from dmeventd.
Notice and Info messages are again logged from dmeventd and its plugins.
Dmeventd now also respects DM_ABORT_ON_INTERNAL_ERRORS as libdm based tool.
Report as non default dm logging also when logging with errno was changed.
Use log_level() macro to consistently decode message log level in dmeventd.
Still produce output when dmsetup dependency tree building finds dev missing.
Check and report pthread_sigmask() failure in dmeventd.
Check mem alloc fail in _canonicalize_field_ids().
Use unsigned math when checking more then 31 legs of raid.

View File

@@ -99,13 +99,28 @@ static time_t _idle_since = 0;
static char **_initial_registrations = 0;
/* FIXME Make configurable at runtime */
__attribute__((format(printf, 4, 5)))
static void _dmeventd_log(int level, const char *file, int line,
const char *format, ...)
/* All libdm messages */
__attribute__((format(printf, 5, 6)))
static void _libdm_log(int level, const char *file, int line,
int dm_errno_or_class, const char *format, ...)
{
va_list ap;
va_start(ap, format);
dm_event_log("dm", level, file, line, 0, format, ap);
dm_event_log("#dm", level, file, line, dm_errno_or_class, format, ap);
va_end(ap);
}
/* All dmeventd messages */
#undef LOG_MESG
#define LOG_MESG(l, f, ln, e, x...) _dmeventd_log(l, f, ln, e, ## x)
__attribute__((format(printf, 5, 6)))
static void _dmeventd_log(int level, const char *file, int line,
int dm_errno_or_class, const char *format, ...)
{
va_list ap;
va_start(ap, format);
dm_event_log("dmeventd", level, file, line, dm_errno_or_class, format, ap);
va_end(ap);
}
@@ -2191,7 +2206,7 @@ int main(int argc, char *argv[])
openlog("dmeventd", LOG_PID, LOG_DAEMON);
dm_event_log_set(_debug_level, _use_syslog);
dm_log_init(_dmeventd_log);
dm_log_with_errno_init(_libdm_log);
(void) dm_prepare_selinux_context(DMEVENTD_PIDFILE, S_IFREG);
if (dm_create_lockfile(DMEVENTD_PIDFILE) == 0)

View File

@@ -865,28 +865,38 @@ void dm_event_log(const char *subsys, int level, const char *file,
int line, int dm_errno_or_class,
const char *format, va_list ap)
{
static int _abort_on_internal_errors = -1;
static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;
static time_t start = 0;
const char *indent = "";
FILE *stream = stdout;
FILE *stream = log_stderr(level) ? stderr : stdout;
int prio;
time_t now;
int log_with_debug = 0;
switch (level & ~(_LOG_STDERR | _LOG_ONCE)) {
if (subsys[0] == '#') {
/* Subsystems starting with '#' are logged
* only when debugging is enabled. */
log_with_debug++;
subsys++;
}
switch (log_level(level)) {
case _LOG_DEBUG:
/* Never shown without -ddd */
if (_debug_level < 3)
return;
prio = LOG_DEBUG;
indent = " ";
break;
case _LOG_INFO:
if (_debug_level < 2)
if (log_with_debug && _debug_level < 2)
return;
prio = LOG_INFO;
indent = " ";
break;
case _LOG_NOTICE:
if (_debug_level < 1)
if (log_with_debug && _debug_level < 1)
return;
prio = LOG_NOTICE;
indent = " ";
@@ -912,12 +922,13 @@ void dm_event_log(const char *subsys, int level, const char *file,
if (!start)
start = now;
now -= start;
fprintf(stream, "[%2d:%02d] %8x:%-6s%s",
(int)now / 60, (int)now % 60,
// TODO: Maybe use shorter ID
// ((int)(pthread_self()) >> 6) & 0xffff,
(int)pthread_self(), subsys,
(_debug_level > 3) ? "" : indent);
if (_debug_level)
fprintf(stream, "[%2d:%02d] %8x:%-6s%s",
(int)now / 60, (int)now % 60,
// TODO: Maybe use shorter ID
// ((int)(pthread_self()) >> 6) & 0xffff,
(int)pthread_self(), subsys,
(_debug_level > 3) ? "" : indent);
if (_debug_level > 3)
fprintf(stream, "%28s:%4d %s", file, line, indent);
vfprintf(stream, _(format), ap);
@@ -926,6 +937,15 @@ void dm_event_log(const char *subsys, int level, const char *file,
}
pthread_mutex_unlock(&_log_mutex);
if (_abort_on_internal_errors < 0)
/* Set when env DM_ABORT_ON_INTERNAL_ERRORS is not "0" */
_abort_on_internal_errors =
strcmp(getenv("DM_ABORT_ON_INTERNAL_ERRORS") ? : "0", "0");
if (_abort_on_internal_errors &&
!strncmp(format, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1))
abort();
}
#if 0 /* left out for now */

View File

@@ -32,7 +32,7 @@ static int _register_count = 0;
static struct dm_pool *_mem_pool = NULL;
static void *_lvm_handle = NULL;
DM_EVENT_LOG_FN("lvm")
DM_EVENT_LOG_FN("#lvm")
static void _lvm2_print_log(int level, const char *file, int line,
int dm_errno_or_class, const char *msg)

View File

@@ -73,8 +73,10 @@ static int _get_mirror_event(struct dso_state *state, char *params)
unsigned i;
struct dm_status_mirror *ms;
if (!dm_get_status_mirror(state->mem, params, &ms))
goto_out;
if (!dm_get_status_mirror(state->mem, params, &ms)) {
log_error("Unable to parse mirror status string.");
return ME_IGNORE;
}
/* Check for bad mirror devices */
for (i = 0; i < ms->dev_count; ++i)
@@ -95,27 +97,23 @@ static int _get_mirror_event(struct dso_state *state, char *params)
dm_pool_free(state->mem, ms);
return r;
out:
log_error("Unable to parse mirror status string.");
return ME_IGNORE;
}
static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert)
static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert,
const char *device)
{
int r;
if (!dmeventd_lvm2_run_with_lock(cmd_lvscan))
log_info("Re-scan of mirrored device failed.");
log_warn("WARNING: Re-scan of mirrored device %s failed.", device);
/* if repair goes OK, report success even if lvscan has failed */
r = dmeventd_lvm2_run_with_lock(cmd_lvconvert);
if (!dmeventd_lvm2_run_with_lock(cmd_lvconvert)) {
log_error("Repair of mirrored device %s failed.", device);
return 0;
}
log_info("Repair of mirrored device %s.",
(r) ? "finished successfully" : "failed");
log_info("Repair of mirrored device %s finished successfully.", device);
return r;
return 1;
}
void process_event(struct dm_task *dmt,
@@ -154,7 +152,8 @@ void process_event(struct dm_task *dmt,
case ME_FAILURE:
log_error("Device failure in %s.", device);
if (!_remove_failed_devices(state->cmd_lvscan,
state->cmd_lvconvert))
state->cmd_lvconvert,
device))
/* FIXME Why are all the error return codes unused? Get rid of them? */
log_error("Failed to remove faulty devices in %s.",
device);
@@ -168,7 +167,7 @@ void process_event(struct dm_task *dmt,
break;
default:
/* FIXME Provide value then! */
log_info("Unknown event received.");
log_warn("WARNING: %s received unknown event.", device);
}
} while (next);
}

View File

@@ -76,7 +76,7 @@ static int _process_raid_event(struct dso_state *state, char *params, const char
/* if repair goes OK, report success even if lvscan has failed */
if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) {
log_info("Repair of RAID device %s failed.", device);
log_error("Repair of RAID device %s failed.", device);
r = 0;
}
} else {

View File

@@ -301,7 +301,7 @@ out:
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
{
#if THIN_DEBUG
log_info("dmeventd executes: %s.", state->cmd_str);
log_debug("dmeventd executes: %s.", state->cmd_str);
#endif
if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) {
log_error("Failed to extend thin pool %s.",

View File

@@ -7,19 +7,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import threading
import subprocess
from . import cfg
import time
from .cmdhandler import options_to_cli_args
import dbus
from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug
import traceback
import os
_rlock = threading.RLock()
_thread_list = list()
def pv_move_lv_cmd(move_options, lv_full_name,
pv_source, pv_source_range, pv_dest_range_list):
@@ -42,15 +36,40 @@ def lv_merge_cmd(merge_options, lv_full_name):
return cmd
def _move_merge(interface_name, cmd, job_state):
add(cmd, job_state)
def _move_merge(interface_name, command, job_state):
# We need to execute these command stand alone by forking & exec'ing
# the command always as we will be getting periodic output from them on
# the status of the long running operation.
command.insert(0, cfg.LVM_CMD)
process = subprocess.Popen(command, stdout=subprocess.PIPE,
env=os.environ,
stderr=subprocess.PIPE, close_fds=True)
done = job_state.Wait(-1)
if not done:
ec, err_msg = job_state.GetError
log_debug("Background process for %s is %d" %
(str(command), process.pid))
lines_iterator = iter(process.stdout.readline, b"")
for line in lines_iterator:
line_str = line.decode("utf-8")
# Check to see if the line has the correct number of separators
try:
if line_str.count(':') == 2:
(device, ignore, percentage) = line_str.split(':')
job_state.Percent = round(
float(percentage.strip()[:-1]), 1)
except ValueError:
log_error("Trying to parse percentage which failed for %s" %
line_str)
out = process.communicate()
if process.returncode == 0:
job_state.Percent = 100
else:
raise dbus.exceptions.DBusException(
interface_name,
'Exit code %s, stderr = %s' % (str(ec), err_msg))
'Exit code %s, stderr = %s' % (str(process.returncode), out[1]))
cfg.load()
return '/'
@@ -85,8 +104,6 @@ def move(interface_name, lv_name, pv_src_obj, pv_source_range,
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
# Generate the command line for this command, but don't
# execute it.
cmd = pv_move_lv_cmd(move_options,
lv_name,
pv_src.lvm_id,
@@ -109,106 +126,3 @@ def merge(interface_name, lv_uuid, lv_name, merge_options, job_state):
raise dbus.exceptions.DBusException(
interface_name,
'LV with uuid %s and name %s not present!' % (lv_uuid, lv_name))
def background_reaper():
while cfg.run.value != 0:
with _rlock:
num_threads = len(_thread_list) - 1
if num_threads >= 0:
for i in range(num_threads, -1, -1):
_thread_list[i].join(0)
if not _thread_list[i].is_alive():
log_debug("Removing thread: %s" % _thread_list[i].name)
_thread_list.pop(i)
time.sleep(3)
def background_execute(command, background_job):
# Wrap this whole operation in an exception handler, otherwise if we
# hit a code bug we will silently exit this thread without anyone being
# the wiser.
try:
# We need to execute these command stand alone by forking & exec'ing
# the command always!
command.insert(0, cfg.LVM_CMD)
process = subprocess.Popen(command, stdout=subprocess.PIPE,
env=os.environ,
stderr=subprocess.PIPE, close_fds=True)
log_debug("Background process for %s is %d" %
(str(command), process.pid))
lines_iterator = iter(process.stdout.readline, b"")
for line in lines_iterator:
line_str = line.decode("utf-8")
# Check to see if the line has the correct number of separators
try:
if line_str.count(':') == 2:
(device, ignore, percentage) = line_str.split(':')
background_job.Percent = round(
float(percentage.strip()[:-1]), 1)
except ValueError:
log_error("Trying to parse percentage which failed for %s" %
line_str)
out = process.communicate()
if process.returncode == 0:
background_job.Percent = 100
else:
log_error("Failed to execute background job %s, STDERR= %s"
% (str(command), out[1]))
background_job.set_result(process.returncode, out[1])
log_debug("Background process %d complete!" % process.pid)
except Exception:
# In the unlikely event that we blow up, we need to unblock caller which
# is waiting on an answer.
st = traceback.format_exc()
error = "Exception in background thread: \n%s" % st
log_error(error)
background_job.set_result(1, error)
def add(command, reporting_job):
# Create the thread, get it running and then add it to the list
t = threading.Thread(
target=background_execute,
name="thread: " + ' '.join(command),
args=(command, reporting_job))
t.start()
with _rlock:
_thread_list.append(t)
def wait_thread(job, timeout, cb, cbe):
# We need to put the wait on it's own thread, so that we don't block the
# entire dbus queue processing thread
try:
cb(job.state.Wait(timeout))
except Exception as e:
cbe("Wait exception: %s" % str(e))
return 0
def add_wait(job, timeout, cb, cbe):
if timeout == 0:
# Users are basically polling, do not create thread
cb(job.Complete)
else:
t = threading.Thread(
target=wait_thread,
name="thread job.Wait: %s" % job.dbus_object_path(),
args=(job, timeout, cb, cbe)
)
t.start()
with _rlock:
_thread_list.append(t)

View File

@@ -11,20 +11,37 @@ from .pv import load_pvs
from .vg import load_vgs
from .lv import load_lvs
from . import cfg
from .utils import MThreadRunner, log_debug
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True):
def _main_thread_load(refresh=True, emit_signal=True):
num_total_changes = 0
num_total_changes += load_pvs(
refresh=refresh,
emit_signal=emit_signal,
cache_refresh=False)[1]
num_total_changes += load_vgs(
refresh=refresh,
emit_signal=emit_signal,
cache_refresh=False)[1]
num_total_changes += load_lvs(
refresh=refresh,
emit_signal=emit_signal,
cache_refresh=False)[1]
return num_total_changes
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True,
need_main_thread=True):
# Go through and load all the PVs, VGs and LVs
if cache_refresh:
cfg.db.refresh(log)
num_total_changes += load_pvs(refresh=refresh, emit_signal=emit_signal,
cache_refresh=False)[1]
num_total_changes += load_vgs(refresh=refresh, emit_signal=emit_signal,
cache_refresh=False)[1]
num_total_changes += load_lvs(refresh=refresh, emit_signal=emit_signal,
cache_refresh=False)[1]
if need_main_thread:
rc = MThreadRunner(_main_thread_load, refresh, emit_signal).done()
else:
rc = _main_thread_load(refresh, emit_signal)
return num_total_changes
return rc

View File

@@ -8,12 +8,54 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .automatedproperties import AutomatedProperties
from .utils import job_obj_path_generate
from .utils import job_obj_path_generate, mt_async_result, log_debug
from . import cfg
from .cfg import JOB_INTERFACE
import dbus
import threading
from . import background
from gi.repository import GLib
# Class that handles a client waiting for something to be complete. We either
# get a timeout or the operation is done.
class WaitingClient(object):
# A timeout occurred
@staticmethod
def _timeout(wc):
with wc.rlock:
if wc.in_use:
wc.in_use = False
# Remove ourselves from waiting client
wc.job_state.remove_waiting_client(wc)
wc.timer_id = -1
mt_async_result(wc.cb, wc.job_state.Complete)
wc.job_state = None
def __init__(self, job_state, tmo, cb, cbe):
self.rlock = threading.RLock()
self.job_state = job_state
self.cb = cb
self.cbe = cbe
self.in_use = True # Indicates if object is in play
self.timer_id = -1
if tmo > 0:
self.timer_id = GLib.timeout_add_seconds(
tmo, WaitingClient._timeout, self)
# The job finished before the timer popped and we are being notified that
# it's done
def notify(self):
with self.rlock:
if self.in_use:
self.in_use = False
# Clear timer
if self.timer_id != -1:
GLib.source_remove(self.timer_id)
self.timer_id = -1
mt_async_result(self.cb, self.job_state.Complete)
self.job_state = None
# noinspection PyPep8Naming
@@ -24,9 +66,9 @@ class JobState(object):
self._percent = 0
self._complete = False
self._request = request
self._cond = threading.Condition(self.rlock)
self._ec = 0
self._stderr = ''
self._waiting_clients = []
# This is an lvm command that is just taking too long and doesn't
# support background operation
@@ -57,7 +99,7 @@ class JobState(object):
with self.rlock:
self._complete = value
self._percent = 100
self._cond.notify_all()
self.notify_waiting_clients()
@property
def GetError(self):
@@ -71,29 +113,10 @@ class JobState(object):
else:
return (-1, 'Job is not complete!')
def set_result(self, ec, msg):
with self.rlock:
self.Complete = True
self._ec = ec
self._stderr = msg
def dtor(self):
with self.rlock:
self._request = None
def Wait(self, timeout):
try:
with self._cond:
# Check to see if we are done, before we wait
if not self.Complete:
if timeout != -1:
self._cond.wait(timeout)
else:
self._cond.wait()
return self.Complete
except RuntimeError:
return False
@property
def Result(self):
with self.rlock:
@@ -101,6 +124,36 @@ class JobState(object):
return self._request.result()
return '/'
def add_waiting_client(self, client):
with self.rlock:
# Avoid race condition where it goes complete before we get added
# to the list of waiting clients
if self.Complete:
client.notify()
else:
self._waiting_clients.append(client)
def remove_waiting_client(self, client):
# If a waiting client timer pops before the job is done we will allow
# the client to remove themselves from the list. As we have a lock
# here and a lock in the waiting client too, and they can be obtained
# in different orders, a dead lock can occur.
# As this remove is really optional, we will try to acquire the lock
# and remove. If we are unsuccessful it's not fatal, we just delay
# the time when the objects can be garbage collected by python
if self.rlock.acquire(False):
try:
self._waiting_clients.remove(client)
finally:
self.rlock.release()
def notify_waiting_clients(self):
with self.rlock:
for c in self._waiting_clients:
c.notify()
self._waiting_clients = []
# noinspection PyPep8Naming
class Job(AutomatedProperties):
@@ -122,10 +175,6 @@ class Job(AutomatedProperties):
def Percent(self):
return dbus.Double(float(self.state.Percent))
@Percent.setter
def Percent(self, value):
self.state.Percent = value
@property
def Complete(self):
return dbus.Boolean(self.state.Complete)
@@ -138,9 +187,6 @@ class Job(AutomatedProperties):
def GetError(self):
return dbus.Struct(self.state.GetError, signature="(is)")
def set_result(self, ec, msg):
self.state.set_result(ec, msg)
@dbus.service.method(dbus_interface=JOB_INTERFACE)
def Remove(self):
if self.state.Complete:
@@ -155,7 +201,11 @@ class Job(AutomatedProperties):
out_signature='b',
async_callbacks=('cb', 'cbe'))
def Wait(self, timeout, cb, cbe):
background.add_wait(self, timeout, cb, cbe)
if timeout == 0 or self.state.Complete:
cb(dbus.Boolean(self.state.Complete))
else:
self.state.add_waiting_client(
WaitingClient(self.state, timeout, cb, cbe))
@property
def Result(self):

View File

@@ -21,7 +21,7 @@ from .utils import n, n32
from .loader import common
from .state import State
from . import background
from .utils import round_size
from .utils import round_size, mt_remove_dbus_objects
from .job import JobState
@@ -415,7 +415,6 @@ class Lv(LvCommon):
rc, out, err = cmdhandler.lv_remove(lv_name, remove_options)
if rc == 0:
cfg.om.remove_object(dbo, True)
cfg.load()
else:
# Need to work on error handling, need consistent
@@ -515,15 +514,9 @@ class Lv(LvCommon):
rc, out, err = cmdhandler.vg_lv_snapshot(
lv_name, snapshot_options, name, optional_size)
if rc == 0:
return_path = '/'
cfg.load()
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
lvs = load_lvs([full_name], emit_signal=True)[0]
for l in lvs:
return_path = l.dbus_object_path()
# Refresh self and all included PVs
cfg.load(cache_refresh=False)
return return_path
return cfg.om.get_object_path_by_lvm_id(full_name)
else:
raise dbus.exceptions.DBusException(
LV_INTERFACE,
@@ -752,9 +745,8 @@ class LvThinPool(Lv):
lv_name, create_options, name, size_bytes)
if rc == 0:
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
lvs = load_lvs([full_name], emit_signal=True)[0]
for l in lvs:
lv_created = l.dbus_object_path()
cfg.load()
lv_created = cfg.om.get_object_path_by_lvm_id(full_name)
else:
raise dbus.exceptions.DBusException(
LV_INTERFACE,
@@ -816,8 +808,7 @@ class LvCachePool(Lv):
# When we cache an LV, the cache pool and the lv that is getting
# cached need to be removed from the object manager and
# re-created as their interfaces have changed!
cfg.om.remove_object(dbo, emit_signal=True)
cfg.om.remove_object(lv_to_cache, emit_signal=True)
mt_remove_dbus_objects((dbo, lv_to_cache))
cfg.load()
lv_converted = cfg.om.get_object_path_by_lvm_id(fcn)
@@ -879,8 +870,7 @@ class LvCacheLv(Lv):
if rc == 0:
# The cache pool gets removed as hidden and put back to
# visible, so lets delete
cfg.om.remove_object(cache_pool, emit_signal=True)
cfg.om.remove_object(dbo, emit_signal=True)
mt_remove_dbus_objects((cache_pool, dbo))
cfg.load()
uncached_lv_path = cfg.om.get_object_path_by_lvm_id(lv_name)

View File

@@ -22,7 +22,6 @@ from . import lvmdb
from gi.repository import GLib
from .fetch import load
from .manager import Manager
from .background import background_reaper
import traceback
import queue
from . import udevwatch
@@ -64,6 +63,7 @@ def _discard_pending_refreshes():
def process_request():
while cfg.run.value != 0:
# noinspection PyBroadException
try:
req = cfg.worker_q.get(True, 5)
@@ -85,7 +85,7 @@ def process_request():
log_debug(
"Inspect method %s for too many refreshes" %
(str(req.method)))
log_debug("Complete ")
log_debug("Method complete ")
except queue.Empty:
pass
except Exception:
@@ -156,14 +156,11 @@ def main():
cfg.db = lvmdb.DataStore(cfg.args.use_json)
# Start up thread to monitor pv moves
thread_list.append(
threading.Thread(target=background_reaper, name="pv_move_reaper"))
# Using a thread to process requests.
# Using a thread to process requests, we cannot hang the dbus library
# thread that is handling the dbus interface
thread_list.append(threading.Thread(target=process_request))
cfg.load(refresh=False, emit_signal=False)
cfg.load(refresh=False, emit_signal=False, need_main_thread=False)
cfg.loop = GLib.MainLoop()
for process in thread_list:

View File

@@ -42,12 +42,10 @@ class Manager(AutomatedProperties):
raise dbus.exceptions.DBusException(
MANAGER_INTERFACE, "PV Already exists!")
created_pv = []
rc, out, err = cmdhandler.pv_create(create_options, [device])
if rc == 0:
pvs = load_pvs([device], emit_signal=True)[0]
for p in pvs:
created_pv = p.dbus_object_path()
cfg.load()
created_pv = cfg.om.get_object_path_by_lvm_id(device)
else:
raise dbus.exceptions.DBusException(
MANAGER_INTERFACE,
@@ -80,20 +78,14 @@ class Manager(AutomatedProperties):
MANAGER_INTERFACE, 'object path = %s not found' % p)
rc, out, err = cmdhandler.vg_create(create_options, pv_devices, name)
created_vg = "/"
if rc == 0:
vgs = load_vgs([name], emit_signal=True)[0]
for v in vgs:
created_vg = v.dbus_object_path()
# Update the PVS
load_pvs(refresh=True, emit_signal=True, cache_refresh=False)
cfg.load()
return cfg.om.get_object_path_by_lvm_id(name)
else:
raise dbus.exceptions.DBusException(
MANAGER_INTERFACE,
'Exit code %s, stderr = %s' % (str(rc), err))
return created_vg
@dbus.service.method(
dbus_interface=MANAGER_INTERFACE,

View File

@@ -18,7 +18,7 @@ from .utils import vg_obj_path_generate, n, pv_obj_path_generate, \
from .loader import common
from .request import RequestEntry
from .state import State
from .utils import round_size
from .utils import round_size, mt_remove_dbus_objects
# noinspection PyUnusedLocal
@@ -140,7 +140,7 @@ class Pv(AutomatedProperties):
if dbo:
rc, out, err = cmdhandler.pv_remove(pv_name, remove_options)
if rc == 0:
cfg.om.remove_object(dbo, True)
mt_remove_dbus_objects((dbo,))
else:
# Need to work on error handling, need consistent
raise dbus.exceptions.DBusException(
@@ -174,7 +174,7 @@ class Pv(AutomatedProperties):
rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes,
resize_options)
if rc == 0:
dbo.refresh()
cfg.load()
else:
raise dbus.exceptions.DBusException(
PV_INTERFACE,

View File

@@ -13,7 +13,7 @@ from gi.repository import GLib
from .job import Job
from . import cfg
import traceback
from .utils import log_error
from .utils import log_error, mt_async_result
class RequestEntry(object):
@@ -57,9 +57,9 @@ class RequestEntry(object):
self._job = Job(self, self._job_state)
cfg.om.register_object(self._job, True)
if self._return_tuple:
self.cb(('/', self._job.dbus_object_path()))
mt_async_result(self.cb, ('/', self._job.dbus_object_path()))
else:
self.cb(self._job.dbus_object_path())
mt_async_result(self.cb, self._job.dbus_object_path())
def run_cmd(self):
try:
@@ -110,9 +110,9 @@ class RequestEntry(object):
if error_rc == 0:
if self.cb:
if self._return_tuple:
self.cb((result, '/'))
mt_async_result(self.cb, (result, '/'))
else:
self.cb(result)
mt_async_result(self.cb, result)
else:
if self.cb_error:
if not error_exception:
@@ -123,7 +123,7 @@ class RequestEntry(object):
else:
error_exception = Exception(error_msg)
self.cb_error(error_exception)
mt_async_result(self.cb_error, error_exception)
else:
# We have a job and it's complete, indicate that it's done.
# TODO: We need to signal the job is done too.

View File

@@ -17,6 +17,8 @@ import datetime
import dbus
from lvmdbusd import cfg
from gi.repository import GLib
import threading
STDOUT_TTY = os.isatty(sys.stdout.fileno())
@@ -494,3 +496,64 @@ def validate_tag(interface, tag):
raise dbus.exceptions.DBusException(
interface, 'tag (%s) contains invalid character, allowable set(%s)'
% (tag, _ALLOWABLE_TAG_CH))
# The methods below which start with mt_* are used to execute the desired code
# on the the main thread of execution to alleviate any issues the dbus-python
# library with regards to multi-threaded access. Essentially, we are trying to
# ensure all dbus library interaction is done from the same thread!
def _async_result(call_back, results):
log_debug('Results = %s' % str(results))
call_back(results)
# Return result in main thread
def mt_async_result(call_back, results):
GLib.idle_add(_async_result, call_back, results)
# Run the supplied function and arguments on the main thread and wait for them
# to complete while allowing the ability to get the return value too.
#
# Example:
# result = MThreadRunner(foo, arg1, arg2).done()
#
class MThreadRunner(object):
@staticmethod
def runner(obj):
obj._run()
with obj.cond:
obj.function_complete = True
obj.cond.notify_all()
def __init__(self, function, *args):
self.f = function
self.rc = None
self.args = args
self.function_complete = False
self.cond = threading.Condition(threading.Lock())
def done(self):
GLib.idle_add(MThreadRunner.runner, self)
with self.cond:
if not self.function_complete:
self.cond.wait()
return self.rc
def _run(self):
if len(self.args):
self.rc = self.f(*self.args)
else:
self.rc = self.f()
def _remove_objects(dbus_objects_rm):
for o in dbus_objects_rm:
cfg.om.remove_object(o, emit_signal=True)
# Remove dbus objects from main thread
def mt_remove_dbus_objects(objs):
MThreadRunner(_remove_objects, objs).done()

View File

@@ -19,7 +19,7 @@ from .request import RequestEntry
from .loader import common
from .state import State
from . import background
from .utils import round_size
from .utils import round_size, mt_remove_dbus_objects
from .job import JobState
@@ -191,14 +191,7 @@ class Vg(AutomatedProperties):
rc, out, err = cmdhandler.vg_remove(vg_name, remove_options)
if rc == 0:
# Remove the VG
cfg.om.remove_object(dbo, True)
# If an LV has hidden LVs, things can get quite involved,
# especially if it's the last thin pool to get removed, so
# lets refresh all
cfg.load()
else:
# Need to work on error handling, need consistent
raise dbus.exceptions.DBusException(
@@ -605,9 +598,7 @@ class Vg(AutomatedProperties):
rc, out, err = create_method(
md.lv_full_name(), data.lv_full_name(), create_options)
if rc == 0:
cfg.om.remove_object(md, emit_signal=True)
cfg.om.remove_object(data, emit_signal=True)
mt_remove_dbus_objects((md, data))
cache_pool_lv = Vg.fetch_new_lv(vg_name, new_name)
else:
raise dbus.exceptions.DBusException(

View File

@@ -370,6 +370,11 @@ void activation_exit(void)
{
}
int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype)
{
return 1;
}
int lv_is_active(const struct logical_volume *lv)
{
return 0;
@@ -1489,6 +1494,26 @@ out:
return r || l;
}
/*
* Check if "raid4" @segtype is supported by kernel.
*
* if segment type is not raid4, return 1.
*/
int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype)
{
unsigned attrs;
if (segtype_is_raid4(segtype) &&
(!segtype->ops->target_present ||
!segtype->ops->target_present(cmd, NULL, &attrs) ||
!(attrs & RAID_FEATURE_RAID4))) {
log_error("RAID module does not support RAID4.");
return 0;
}
return 1;
}
int lv_is_active(const struct logical_volume *lv)
{
return _lv_is_active(lv, NULL, NULL, NULL);
@@ -1683,7 +1708,7 @@ int target_register_events(struct cmd_context *cmd, const char *dso, const struc
if (!r)
return_0;
log_info("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
log_very_verbose("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
return 1;
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -99,6 +99,7 @@ int target_present(struct cmd_context *cmd, const char *target_name,
int use_modprobe);
int target_version(const char *target_name, uint32_t *maj,
uint32_t *min, uint32_t *patchlevel);
int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype);
int lvm_dm_prefix_check(int major, int minor, const char *prefix);
int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
struct dm_list *modules);

View File

@@ -2250,8 +2250,8 @@ bad:
return NULL;
}
static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
struct lv_segment *seg, int s)
static char *_add_error_or_zero_device(struct dev_manager *dm, struct dm_tree *dtree,
struct lv_segment *seg, int s, int use_zero)
{
char *dlid, *name;
char errid[32];
@@ -2262,13 +2262,15 @@ static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
uint64_t size = (uint64_t) seg->len * seg->lv->vg->extent_size;
dm_list_iterate_items(seg_i, &seg->lv->segments) {
if (seg == seg_i)
if (seg == seg_i) {
segno = i;
break;
}
++i;
}
if (segno < 0) {
log_error("_add_error_device called with bad segment");
log_error(INTERNAL_ERROR "_add_error_or_zero_device called with bad segment.");
return NULL;
}
@@ -2281,7 +2283,7 @@ static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
seg->lv->name, errid)))
return_NULL;
log_debug_activation("Getting device info for %s [%s]", name, dlid);
log_debug_activation("Getting device info for %s [%s].", name, dlid);
if (!_info(dm->cmd, dlid, 1, 0, &info, NULL, NULL)) {
log_error("Failed to get info for %s [%s].", name, dlid);
return 0;
@@ -2291,14 +2293,19 @@ static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
/* Create new node */
if (!(node = dm_tree_add_new_dev(dtree, name, dlid, 0, 0, 0, 0, 0)))
return_NULL;
if (!dm_tree_node_add_error_target(node, size))
return_NULL;
if (use_zero) {
if (!dm_tree_node_add_zero_target(node, size))
return_NULL;
} else
if (!dm_tree_node_add_error_target(node, size))
return_NULL;
} else {
/* Already exists */
if (!dm_tree_add_dev(dtree, info.major, info.minor)) {
log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree",
log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree.",
info.major, info.minor);
return_NULL;
return NULL;
}
}
@@ -2310,14 +2317,15 @@ static int _add_error_area(struct dev_manager *dm, struct dm_tree_node *node,
{
char *dlid;
uint64_t extent_size = seg->lv->vg->extent_size;
int use_zero = !strcmp(dm->cmd->stripe_filler, TARGET_NAME_ZERO) ? 1 : 0;
if (!strcmp(dm->cmd->stripe_filler, TARGET_NAME_ERROR)) {
if (!strcmp(dm->cmd->stripe_filler, TARGET_NAME_ERROR) || use_zero) {
/*
* FIXME, the tree pointer is first field of dm_tree_node, but
* we don't have the struct definition available.
*/
struct dm_tree **tree = (struct dm_tree **) node;
if (!(dlid = _add_error_device(dm, *tree, seg, s)))
if (!(dlid = _add_error_or_zero_device(dm, *tree, seg, s, use_zero)))
return_0;
if (!dm_tree_node_add_target_area(node, NULL, dlid, extent_size * seg_le(seg, s)))
return_0;
@@ -2825,7 +2833,7 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
if (!(name = dm_build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
return_0;
/* Even unused thin-pool still needs to get layered UUID -suffix */
/* Even unused thin-pool still needs to get layered UUID -suffix */
if (!layer && lv_is_new_thin_pool(lv))
layer = lv_layer(lv);

View File

@@ -640,8 +640,8 @@ static int _process_config(struct cmd_context *cmd)
if (!strcmp(cmd->stripe_filler, "/dev/ioerror") &&
stat(cmd->stripe_filler, &st))
cmd->stripe_filler = "error";
if (strcmp(cmd->stripe_filler, "error")) {
else if (strcmp(cmd->stripe_filler, "error") &&
strcmp(cmd->stripe_filler, "zero")) {
if (stat(cmd->stripe_filler, &st)) {
log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
"is invalid,", cmd->stripe_filler);

View File

@@ -1111,7 +1111,8 @@ cfg(activation_retry_deactivation_CFG, "retry_deactivation", activation_CFG_SECT
cfg(activation_missing_stripe_filler_CFG, "missing_stripe_filler", activation_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING, DEFAULT_STRIPE_FILLER, vsn(1, 0, 0), NULL, 0, NULL,
"Method to fill missing stripes when activating an incomplete LV.\n"
"Using 'error' will make inaccessible parts of the device return I/O\n"
"errors on access. You can instead use a device path, in which case,\n"
"errors on access. Using 'zero' will return success (and zero) on I/O\n"
"You can instead use a device path, in which case,\n"
"that device will be used in place of missing stripes. Using anything\n"
"other than 'error' with mirrored or snapshotted volumes is likely to\n"
"result in data corruption.\n")

View File

@@ -40,7 +40,7 @@ static int _extract_pattern(struct dm_pool *mem, const char *pat,
break;
default:
log_info("pattern must begin with 'a' or 'r'");
log_error("Pattern must begin with 'a' or 'r'.");
return 0;
}
pat++;
@@ -77,7 +77,7 @@ static int _extract_pattern(struct dm_pool *mem, const char *pat,
*/
ptr = r + strlen(r) - 1;
if (*ptr != sep) {
log_info("invalid separator at end of regex");
log_error("Invalid separator at end of regex.");
return 0;
}
*ptr = '\0';

View File

@@ -168,7 +168,7 @@ static int _parse_dev(const char *file, FILE *fp, dev_t *result)
}
if (sscanf(buffer, "%u:%u", &major, &minor) != 2) {
log_info("sysfs device file not correct format");
log_error("Incorrect format for sysfs device file: %s.", file);
return 0;
}

View File

@@ -140,18 +140,18 @@ static struct labeller *_find_labeller(struct device *dev, char *buf,
sector + scan_sector);
}
if (xlate64(lh->sector_xl) != sector + scan_sector) {
log_info("%s: Label for sector %" PRIu64
" found at sector %" PRIu64
" - ignoring", dev_name(dev),
(uint64_t)xlate64(lh->sector_xl),
sector + scan_sector);
log_very_verbose("%s: Label for sector %" PRIu64
" found at sector %" PRIu64
" - ignoring", dev_name(dev),
(uint64_t)xlate64(lh->sector_xl),
sector + scan_sector);
continue;
}
if (calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl, LABEL_SIZE -
((uint8_t *) &lh->offset_xl - (uint8_t *) lh)) !=
xlate32(lh->crc_xl)) {
log_info("Label checksum incorrect on %s - "
"ignoring", dev_name(dev));
log_very_verbose("Label checksum incorrect on %s - "
"ignoring", dev_name(dev));
continue;
}
if (found)
@@ -243,8 +243,8 @@ int label_remove(struct device *dev)
}
if (wipe) {
log_info("%s: Wiping label at sector %" PRIu64,
dev_name(dev), sector);
log_very_verbose("%s: Wiping label at sector %" PRIu64,
dev_name(dev), sector);
if (!dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE,
buf)) {
log_error("Failed to remove label from %s at "
@@ -333,9 +333,9 @@ int label_write(struct device *dev, struct label *label)
if (!dev_open(dev))
return_0;
log_info("%s: Writing label to sector %" PRIu64 " with stored offset %"
PRIu32 ".", dev_name(dev), label->sector,
xlate32(lh->offset_xl));
log_very_verbose("%s: Writing label to sector %" PRIu64 " with stored offset %"
PRIu32 ".", dev_name(dev), label->sector,
xlate32(lh->offset_xl));
if (!dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, buf)) {
log_debug_devs("Failed to write label to %s", dev_name(dev));
r = 0;

View File

@@ -67,7 +67,7 @@ struct log_stream_item {
char *buffer;
};
struct log_stream {
static struct log_stream {
struct log_stream_item out;
struct log_stream_item err;
struct log_stream_item report;
@@ -476,9 +476,9 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
int bufused, n;
const char *trformat; /* Translated format string */
char *newbuf;
int use_stderr = level & _LOG_STDERR;
int log_once = level & _LOG_ONCE;
int log_bypass_report = level & _LOG_BYPASS_REPORT;
int use_stderr = log_stderr(level);
int log_once = log_once(level);
int log_bypass_report = log_bypass_report(level);
int fatal_internal_error = 0;
size_t msglen;
const char *indent_spaces = "";
@@ -489,7 +489,7 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
struct dm_report *orig_report;
int logged_via_report = 0;
level &= ~(_LOG_STDERR|_LOG_ONCE|_LOG_BYPASS_REPORT);
level = log_level(level);
if (_abort_on_internal_errors_env_present < 0) {
if ((env_str = getenv("DM_ABORT_ON_INTERNAL_ERRORS"))) {
@@ -715,8 +715,8 @@ void print_log_libdm(int level, const char *file, int line, int dm_errno_or_clas
* LOG_WARN level and it's not going to stderr (so we're
* printing common message that is not an error/warning).
*/
if (!(level & _LOG_STDERR) &&
((level & ~(_LOG_STDERR|_LOG_ONCE|_LOG_BYPASS_REPORT)) == _LOG_WARN))
if (!log_stderr(level) &&
(log_level(level) == _LOG_WARN))
level |= _LOG_BYPASS_REPORT;
_log_stream.out.stream = report_stream;

View File

@@ -50,6 +50,10 @@
#define _LOG_STDERR 0x0080 /* force things to go to stderr, even if loglevel would make them go to stdout */
#define _LOG_ONCE 0x0100 /* downgrade to NOTICE if this has been already logged */
#define _LOG_BYPASS_REPORT 0x0200 /* do not log through report even if report available */
#define log_level(x) ((x) & 0x0f) /* obtain message level */
#define log_stderr(x) ((x) & _LOG_STDERR) /* obtain stderr bit */
#define log_once(x) ((x) & _LOG_ONCE) /* obtain once bit */
#define log_bypass_report(x) ((x) & _LOG_BYPASS_REPORT)/* obtain bypass bit */
#define INTERNAL_ERROR "Internal error: "

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved.
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -1418,6 +1418,7 @@ int lv_active_change(struct cmd_context *cmd, struct logical_volume *lv,
enum activation_change activate, int needs_exclusive)
{
const char *ay_with_mode = NULL;
struct lv_segment *seg = first_seg(lv);
if (activate == CHANGE_ASY)
ay_with_mode = "sh";
@@ -1454,6 +1455,9 @@ deactivate:
break;
case CHANGE_ALY:
case CHANGE_AAY:
if (!raid4_is_supported(cmd, seg->segtype))
goto no_raid4;
if (needs_exclusive || _lv_is_exclusive(lv)) {
log_verbose("Activating logical volume %s exclusively locally.",
display_lvname(lv));
@@ -1468,6 +1472,9 @@ deactivate:
break;
case CHANGE_AEY:
exclusive:
if (!raid4_is_supported(cmd, seg->segtype))
goto no_raid4;
log_verbose("Activating logical volume %s exclusively.",
display_lvname(lv));
if (!activate_lv_excl(cmd, lv))
@@ -1476,6 +1483,9 @@ exclusive:
case CHANGE_ASY:
case CHANGE_AY:
default:
if (!raid4_is_supported(cmd, seg->segtype))
goto no_raid4;
if (needs_exclusive || _lv_is_exclusive(lv))
goto exclusive;
log_verbose("Activating logical volume %s.", display_lvname(lv));
@@ -1488,6 +1498,10 @@ exclusive:
log_error("Failed to unlock logical volume %s.", display_lvname(lv));
return 1;
no_raid4:
log_error("Failed to activate %s LV %s", lvseg_name(seg), display_lvname(lv));
return 0;
}
char *lv_active_dup(struct dm_pool *mem, const struct logical_volume *lv)

View File

@@ -319,7 +319,6 @@ static int _lv_layout_and_role_thin(struct dm_pool *mem,
{
int top_level = 0;
unsigned snap_count;
struct lv_segment *seg;
/* non-top-level LVs */
if (lv_is_thin_pool_metadata(lv)) {
@@ -353,7 +352,7 @@ static int _lv_layout_and_role_thin(struct dm_pool *mem,
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_MULTITHINORIGIN]))
goto_bad;
}
if ((seg = first_seg(lv)) && (seg->origin || seg->external_lv))
if (lv_is_thin_snapshot(lv))
if (!str_list_add(mem, role, _lv_type_names[LV_TYPE_SNAPSHOT]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_THINSNAPSHOT]))
goto_bad;
@@ -3844,6 +3843,7 @@ static int _lv_extend_layered_lv(struct alloc_handle *ah,
uint32_t fa, s;
int clear_metadata = 0;
uint32_t area_multiple = 1;
int fail;
if (!(segtype = get_segtype_from_string(lv->vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
@@ -3917,45 +3917,60 @@ static int _lv_extend_layered_lv(struct alloc_handle *ah,
if (!vg_write(lv->vg) || !vg_commit(lv->vg))
return_0;
for (s = 0; s < seg->area_count; s++) {
meta_lv = seg_metalv(seg, s);
if (test_mode())
log_verbose("Test mode: Skipping wiping of metadata areas.");
else {
fail = 0;
/* Activate all rmeta devices locally first (more efficient) */
for (s = 0; !fail && s < seg->area_count; s++) {
meta_lv = seg_metalv(seg, s);
if (test_mode()) {
lv_set_hidden(meta_lv);
continue;
if (!activate_lv_local(meta_lv->vg->cmd, meta_lv)) {
log_error("Failed to activate %s for clearing.",
display_lvname(meta_lv));
fail = 1;
}
}
/* For clearing, simply activate locally */
if (!activate_lv_local(meta_lv->vg->cmd, meta_lv)) {
log_error("Failed to activate %s/%s for clearing",
meta_lv->vg->name, meta_lv->name);
return 0;
/* Clear all rmeta devices */
for (s = 0; !fail && s < seg->area_count; s++) {
meta_lv = seg_metalv(seg, s);
log_verbose("Clearing metadata area of %s.",
display_lvname(meta_lv));
/*
* Rather than wiping meta_lv->size, we can simply
* wipe '1' to remove the superblock of any previous
* RAID devices. It is much quicker.
*/
if (!wipe_lv(meta_lv, (struct wipe_params)
{ .do_zero = 1, .zero_sectors = 1 })) {
stack;
fail = 1;
}
}
log_verbose("Clearing metadata area of %s",
display_lvname(meta_lv));
/*
* Rather than wiping meta_lv->size, we can simply
* wipe '1' to remove the superblock of any previous
* RAID devices. It is much quicker.
*/
if (!wipe_lv(meta_lv, (struct wipe_params)
{ .do_zero = 1, .zero_sectors = 1 })) {
log_error("Failed to zero %s/%s",
meta_lv->vg->name, meta_lv->name);
return 0;
/* Deactivate all rmeta devices */
for (s = 0; s < seg->area_count; s++) {
meta_lv = seg_metalv(seg, s);
if (!deactivate_lv(meta_lv->vg->cmd, meta_lv)) {
log_error("Failed to deactivate %s after clearing.",
display_lvname(meta_lv));
fail = 1;
}
/* Wipe any temporary tags required for activation. */
str_list_wipe(&meta_lv->tags);
}
if (!deactivate_lv(meta_lv->vg->cmd, meta_lv)) {
log_error("Failed to deactivate %s/%s",
meta_lv->vg->name, meta_lv->name);
return 0;
}
lv_set_hidden(meta_lv);
/* Wipe any temporary tags required for activation. */
str_list_wipe(&meta_lv->tags);
if (fail)
/* Fail, after trying to deactivate all we could */
return_0;
}
for (s = 0; s < seg->area_count; s++)
lv_set_hidden(seg_metalv(seg, s));
}
seg->area_len += extents / area_multiple;

View File

@@ -1066,9 +1066,16 @@ struct lv_segment *get_only_segment_using_this_lv(const struct logical_volume *l
* Useful functions for managing snapshots.
*/
int lv_is_origin(const struct logical_volume *lv);
#define lv_is_thick_origin lv_is_origin
int lv_is_thin_origin(const struct logical_volume *lv, unsigned *snap_count);
int lv_is_cache_origin(const struct logical_volume *lv);
int lv_is_thin_snapshot(const struct logical_volume *lv);
int lv_is_cow(const struct logical_volume *lv);
#define lv_is_thick_snapshot lv_is_cow
int lv_is_cache_origin(const struct logical_volume *lv);
int lv_is_merging_cow(const struct logical_volume *cow);
uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_size);
int cow_has_min_chunks(const struct volume_group *vg, uint32_t cow_extents, uint32_t chunk_size);
@@ -1208,8 +1215,8 @@ int lv_raid_convert(struct logical_volume *lv,
const uint32_t new_region_size,
struct dm_list *allocate_pvs);
int lv_raid_rebuild(struct logical_volume *lv, struct dm_list *rebuild_pvs);
int lv_raid_replace(struct logical_volume *lv, struct dm_list *remove_pvs,
struct dm_list *allocate_pvs);
int lv_raid_replace(struct logical_volume *lv, int force,
struct dm_list *remove_pvs, struct dm_list *allocate_pvs);
int lv_raid_remove_missing(struct logical_volume *lv);
int partial_raid_lv_supports_degraded_activation(const struct logical_volume *lv);
/* -- metadata/raid_manip.c */

View File

@@ -266,19 +266,16 @@ static int _deactivate_and_remove_lvs(struct volume_group *vg, struct dm_list *r
*
* Returns: 1 if in-sync, 0 otherwise.
*/
#define _RAID_IN_SYNC_RETRIES 6
static int _raid_in_sync(struct logical_volume *lv)
{
int retries = _RAID_IN_SYNC_RETRIES;
dm_percent_t sync_percent;
if (seg_is_striped(first_seg(lv)))
return 1;
if (!lv_raid_percent(lv, &sync_percent)) {
log_error("Unable to determine sync status of %s/%s.",
lv->vg->name, lv->name);
return 0;
}
if (sync_percent == DM_PERCENT_0) {
do {
/*
* FIXME We repeat the status read here to workaround an
* unresolved kernel bug when we see 0 even though the
@@ -290,14 +287,34 @@ static int _raid_in_sync(struct logical_volume *lv)
lv->vg->name, lv->name);
return 0;
}
if (sync_percent == DM_PERCENT_100)
if (sync_percent > DM_PERCENT_0)
break;
if (retries == _RAID_IN_SYNC_RETRIES)
log_warn("WARNING: Sync status for %s is inconsistent.",
display_lvname(lv));
}
usleep(500000);
} while (--retries);
return (sync_percent == DM_PERCENT_100) ? 1 : 0;
}
/* Check if RaidLV @lv is synced or any raid legs of @lv are not synced */
static int _raid_devs_sync_healthy(struct logical_volume *lv)
{
char *raid_health;
if (!_raid_in_sync(lv))
return 0;
if (!seg_is_raid1(first_seg(lv)))
return 1;
if (!lv_raid_dev_health(lv, &raid_health))
return_0;
return (strchr(raid_health, 'a') || strchr(raid_health, 'D')) ? 0 : 1;
}
/*
* _raid_remove_top_layer
* @lv
@@ -1038,11 +1055,10 @@ static int _extract_image_components(struct lv_segment *seg, uint32_t idx,
seg_type(seg, idx) = AREA_UNASSIGNED;
seg_metatype(seg, idx) = AREA_UNASSIGNED;
/* FIXME Remove duplicated prefix? */
if (!(data_lv->name = _generate_raid_name(data_lv, "_extracted", -1)))
if (!(data_lv->name = _generate_raid_name(data_lv, "extracted", -1)))
return_0;
if (!(meta_lv->name = _generate_raid_name(meta_lv, "_extracted", -1)))
if (!(meta_lv->name = _generate_raid_name(meta_lv, "extracted", -1)))
return_0;
*extracted_rmeta = meta_lv;
@@ -1054,6 +1070,7 @@ static int _extract_image_components(struct lv_segment *seg, uint32_t idx,
/*
* _raid_extract_images
* @lv
* @force: force a replacement in case of primary mirror leg
* @new_count: The absolute count of images (e.g. '2' for a 2-way mirror)
* @target_pvs: The list of PVs that are candidates for removal
* @shift: If set, use _shift_and_rename_image_components().
@@ -1068,7 +1085,8 @@ static int _extract_image_components(struct lv_segment *seg, uint32_t idx,
*
* Returns: 1 on success, 0 on failure
*/
static int _raid_extract_images(struct logical_volume *lv, uint32_t new_count,
static int _raid_extract_images(struct logical_volume *lv,
int force, uint32_t new_count,
struct dm_list *target_pvs, int shift,
struct dm_list *extracted_meta_lvs,
struct dm_list *extracted_data_lvs)
@@ -1136,11 +1154,16 @@ static int _raid_extract_images(struct logical_volume *lv, uint32_t new_count,
!lv_is_on_pvs(seg_metalv(seg, s), target_pvs))
continue;
if (!_raid_in_sync(lv) &&
(!seg_is_mirrored(seg) || (s == 0))) {
/*
* Kernel may report raid LV in-sync but still
* image devices may not be in-sync or faulty.
*/
if (!_raid_devs_sync_healthy(lv) &&
(!seg_is_mirrored(seg) || (s == 0 && !force))) {
log_error("Unable to extract %sRAID image"
" while RAID array is not in-sync",
seg_is_mirrored(seg) ? "primary " : "");
" while RAID array is not in-sync%s",
seg_is_mirrored(seg) ? "primary " : "",
seg_is_mirrored(seg) ? " (use --force option to replace)" : "");
return 0;
}
}
@@ -1185,7 +1208,7 @@ static int _raid_remove_images(struct logical_volume *lv,
if (!removal_lvs)
removal_lvs = &removed_lvs;
if (!_raid_extract_images(lv, new_count, allocate_pvs, 1,
if (!_raid_extract_images(lv, 0, new_count, allocate_pvs, 1,
removal_lvs, removal_lvs)) {
log_error("Failed to extract images from %s/%s",
lv->vg->name, lv->name);
@@ -1375,7 +1398,7 @@ int lv_raid_split(struct logical_volume *lv, const char *split_name,
return_0;
}
if (!_raid_extract_images(lv, new_count, splittable_pvs, 1,
if (!_raid_extract_images(lv, 0, new_count, splittable_pvs, 1,
&removal_lvs, &data_list)) {
log_error("Failed to extract images from %s/%s",
lv->vg->name, lv->name);
@@ -2459,7 +2482,7 @@ static struct lv_segment *_convert_striped_to_raid0(struct logical_volume *lv,
0 /* chunk_size */,
0 /* seg->region_size */, 0u /* extents_copied */ ,
NULL /* pvmove_source_seg */))) {
log_error("Failed to allocate new raid0 segement for LV %s.", display_lvname(lv));
log_error("Failed to allocate new raid0 segment for LV %s.", display_lvname(lv));
return NULL;
}
@@ -2519,42 +2542,51 @@ static struct possible_takeover_reshape_type _possible_takeover_reshape_types[]
{ .current_types = SEG_STRIPED_TARGET, /* linear, i.e. seg->area_count = 1 */
.possible_types = SEG_RAID1,
.current_areas = 1,
.options = ALLOW_NONE },
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
{ .current_types = SEG_STRIPED_TARGET, /* linear, i.e. seg->area_count = 1 */
.possible_types = SEG_RAID0|SEG_RAID0_META,
.current_areas = 1,
.options = ALLOW_STRIPE_SIZE },
{ .current_types = SEG_STRIPED_TARGET, /* striped, i.e. seg->area_count > 1 */
{ .current_types = SEG_STRIPED_TARGET, /* striped -> raid0*, i.e. seg->area_count > 1 */
.possible_types = SEG_RAID0|SEG_RAID0_META,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_STRIPED_TARGET, /* striped -> raid4 , i.e. seg->area_count > 1 */
.possible_types = SEG_RAID4,
.current_areas = ~0U,
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
/* raid0* -> */
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* seg->area_count = 1 */
.possible_types = SEG_RAID1,
.current_areas = 1,
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* raid0* -> striped, i.e. seg->area_count > 1 */
.possible_types = SEG_STRIPED_TARGET,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* seg->area_count > 1 */
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* raid0* -> raid0*, i.e. seg->area_count > 1 */
.possible_types = SEG_RAID0_META|SEG_RAID0,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* raid0* -> raid4, i.e. seg->area_count > 1 */
.possible_types = SEG_RAID4,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_RAID0|SEG_RAID0_META, /* raid0 striped, i.e. seg->area_count > 0 */
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
/* raid4 -> -> */
{ .current_types = SEG_RAID4, /* raid4 ->striped/raid0*, i.e. seg->area_count > 1 */
.possible_types = SEG_STRIPED_TARGET|SEG_RAID0|SEG_RAID0_META,
.current_areas = ~0U,
.options = ALLOW_NONE },
/* raid1 -> */
/* raid1 -> mirror */
{ .current_types = SEG_RAID1,
.possible_types = SEG_RAID1|SEG_MIRROR,
.possible_types = SEG_MIRROR,
.current_areas = ~0U,
.options = ALLOW_NONE },
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
/* mirror -> raid1 with arbitrary number of legs */
{ .current_types = SEG_MIRROR,
.possible_types = SEG_MIRROR|SEG_RAID1,
.possible_types = SEG_RAID1,
.current_areas = ~0U,
.options = ALLOW_NONE },
{ .current_types = SEG_RAID4,
.possible_types = SEG_STRIPED_TARGET|SEG_RAID0|SEG_RAID0_META,
.current_areas = ~0U,
.options = ALLOW_NONE },
.options = ALLOW_NONE }, /* FIXME: ALLOW_REGION_SIZE */
/* END */
{ .current_types = 0 }
@@ -2861,9 +2893,176 @@ static int _raid1_to_mirrored_wrapper(TAKEOVER_FN_ARGS)
allocate_pvs, 1, &removal_lvs);
}
/*
* HM Helper: (raid0_meta -> raid4)
*
* To convert raid0_meta to raid4, which involves shifting the
* parity device to lv segment area 0 and thus changing MD
* array roles, detach the MetaLVs and reload as raid0 in
* order to wipe them then reattach and set back to raid0_meta.
*/
static int _clear_meta_lvs(struct logical_volume *lv)
{
uint32_t s;
struct lv_segment *seg = first_seg(lv);
struct lv_segment_area *tmp_areas;
const struct segment_type *tmp_segtype;
struct dm_list meta_lvs;
struct lv_list *lvl_array, *lvl;
/* Reject non-raid0_meta segment types cautiously */
if (!seg_is_raid0_meta(seg) ||
!seg->meta_areas)
return_0;
if (!(lvl_array = dm_pool_alloc(lv->vg->vgmem, seg->area_count * sizeof(*lvl_array))))
return_0;
dm_list_init(&meta_lvs);
tmp_areas = seg->meta_areas;
/* Extract all MetaLVs listing them on @meta_lvs */
log_debug_metadata("Extracting all MetaLVs of %s to activate as raid0",
display_lvname(lv));
if (!_extract_image_component_sublist(seg, RAID_META, 0, seg->area_count, &meta_lvs, 0))
return_0;
/* Memorize meta areas and segtype to set again after initializing. */
seg->meta_areas = NULL;
tmp_segtype = seg->segtype;
if (!(seg->segtype = get_segtype_from_flag(lv->vg->cmd, SEG_RAID0)) ||
!lv_update_and_reload(lv))
return_0;
/*
* Now deactivate the MetaLVs before clearing, so
* that _clear_lvs() will activate them visible.
*/
log_debug_metadata("Deactivating pulled out MetaLVs of %s before initializing.",
display_lvname(lv));
dm_list_iterate_items(lvl, &meta_lvs)
if (!deactivate_lv(lv->vg->cmd, lvl->lv))
return_0;
log_debug_metadata("Clearing allocated raid0_meta metadata LVs for conversion to raid4");
if (!_clear_lvs(&meta_lvs)) {
log_error("Failed to initialize metadata LVs");
return 0;
}
/* Set memorized meta areas and raid0_meta segtype */
seg->meta_areas = tmp_areas;
seg->segtype = tmp_segtype;
log_debug_metadata("Adding metadata LVs back into %s", display_lvname(lv));
s = 0;
dm_list_iterate_items(lvl, &meta_lvs) {
lv_set_hidden(lvl->lv);
if (!set_lv_segment_area_lv(seg, s++, lvl->lv, 0, RAID_META))
return 0;
}
return 1;
}
/*
* HM Helper: (raid0* <-> raid4)
*
* Rename SubLVs (pairs) allowing to shift names w/o collisions with active ones.
*/
#define SLV_COUNT 2
static int _rename_area_lvs(struct logical_volume *lv, const char *suffix)
{
uint32_t s;
size_t sz = strlen("rimage") + (suffix ? strlen(suffix) : 0) + 1;
char *sfx[SLV_COUNT] = { NULL, NULL };
struct lv_segment *seg = first_seg(lv);
/* Create _generate_raid_name() suffixes w/ or w/o passed in @suffix */
for (s = 0; s < SLV_COUNT; s++)
if (!(sfx[s] = dm_pool_alloc(lv->vg->cmd->mem, sz)) ||
dm_snprintf(sfx[s], sz, suffix ? "%s%s" : "%s", s ? "rmeta" : "rimage", suffix) < 0)
return_0;
/* Change names (temporarily) to be able to shift numerical name suffixes */
for (s = 0; s < seg->area_count; s++) {
if (!(seg_lv(seg, s)->name = _generate_raid_name(lv, sfx[0], s)))
return_0;
if (seg->meta_areas &&
!(seg_metalv(seg, s)->name = _generate_raid_name(lv, sfx[1], s)))
return_0;
}
for (s = 0; s < SLV_COUNT; s++)
dm_pool_free(lv->vg->cmd->mem, sfx[s]);
return 1;
}
/*
* HM Helper: (raid0* <-> raid4)
*
* Switch area LVs in lv segment @seg indexed by @s1 and @s2
*/
static void _switch_area_lvs(struct lv_segment *seg, uint32_t s1, uint32_t s2)
{
struct logical_volume *lvt;
lvt = seg_lv(seg, s1);
seg_lv(seg, s1) = seg_lv(seg, s2);
seg_lv(seg, s2) = lvt;
/* Be cautious */
if (seg->meta_areas) {
lvt = seg_metalv(seg, s1);
seg_metalv(seg, s1) = seg_metalv(seg, s2);
seg_metalv(seg, s2) = lvt;
}
}
/*
* HM Helper:
*
* shift range of area LVs in @seg in range [ @s1, @s2 ] up if @s1 < @s2,
* else down bubbling the parity SubLVs up/down whilst shifting.
*/
static void _shift_area_lvs(struct lv_segment *seg, uint32_t s1, uint32_t s2)
{
uint32_t s;
if (s1 < s2)
/* Forward shift n+1 -> n */
for (s = s1; s < s2; s++)
_switch_area_lvs(seg, s, s + 1);
else
/* Reverse shift n-1 -> n */
for (s = s1; s > s2; s--)
_switch_area_lvs(seg, s, s - 1);
}
/*
* Switch position of first and last area lv within
* @lv to move parity SubLVs from end to end.
*
* Direction depends on segment type raid4 / raid0_meta.
*/
static int _shift_parity_dev(struct lv_segment *seg)
{
if (seg_is_raid0_meta(seg))
_shift_area_lvs(seg, seg->area_count - 1, 0);
else if (seg_is_raid4(seg))
_shift_area_lvs(seg, 0, seg->area_count - 1);
else
return 0;
return 1;
}
/* raid45 -> raid0* / striped */
static int _raid456_to_raid0_or_striped_wrapper(TAKEOVER_FN_ARGS)
{
int rename_sublvs = 0;
struct lv_segment *seg = first_seg(lv);
struct dm_list removal_lvs;
@@ -2879,10 +3078,39 @@ static int _raid456_to_raid0_or_striped_wrapper(TAKEOVER_FN_ARGS)
if (!_raid_in_sync(lv))
return 0;
if (!yes && yes_no_prompt("Are you sure you want to convert \"%s\" LV %s to \"%s\" "
"type losing all resilience? [y/n]: ",
lvseg_name(seg), display_lvname(lv), new_segtype->name) == 'n') {
log_error("Logical volume %s NOT converted to \"%s\"",
display_lvname(lv), new_segtype->name);
return 0;
}
if (sigint_caught())
return_0;
/* Archive metadata */
if (!archive(lv->vg))
return_0;
/*
* raid4 (which actually gets mapped to raid5/dedicated first parity disk)
* needs shifting of SubLVs to move the parity SubLV pair in the first area
* to the last one before conversion to raid0[_meta]/striped to allow for
* SubLV removal from the end of the areas arrays.
*/
if (seg_is_raid4(seg)) {
/* Shift parity SubLV pair "PDD..." -> "DD...P" to be able to remove it off the end */
if (!_shift_parity_dev(seg))
return 0;
if (segtype_is_any_raid0(new_segtype) &&
!(rename_sublvs = _rename_area_lvs(lv, "_"))) {
log_error("Failed to rename %s LV %s MetaLVs", lvseg_name(seg), display_lvname(lv));
return 0;
}
}
/* Remove meta and data LVs requested */
if (!_lv_raid_change_image_count(lv, new_image_count, allocate_pvs, &removal_lvs, 0, 0))
return 0;
@@ -2902,7 +3130,19 @@ static int _raid456_to_raid0_or_striped_wrapper(TAKEOVER_FN_ARGS)
seg->region_size = 0;
return _lv_update_reload_fns_reset_eliminate_lvs(lv, &removal_lvs);
if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, &removal_lvs))
return_0;
if (rename_sublvs) {
if (!_rename_area_lvs(lv, NULL)) {
log_error("Failed to rename %s LV %s MetaLVs", lvseg_name(seg), display_lvname(lv));
return 0;
}
if (!lv_update_and_reload(lv))
return_0;
}
return 1;
}
static int _striped_to_raid0_wrapper(struct logical_volume *lv,
@@ -2930,6 +3170,9 @@ static int _striped_to_raid0_wrapper(struct logical_volume *lv,
static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
{
struct lv_segment *seg = first_seg(lv);
struct dm_list removal_lvs;
dm_list_init(&removal_lvs);
if (seg_is_raid10(seg))
return _takeover_unsupported_yet(lv, new_stripes, new_segtype);
@@ -2944,6 +3187,13 @@ static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
return 0;
}
/* FIXME: restricted to raid4 for the time being... */
if (!segtype_is_raid4(new_segtype)) {
/* Can't convert striped/raid0* to e.g. raid10_offset */
log_error("Can't convert %s to %s", display_lvname(lv), new_segtype->name);
return 0;
}
/* Archive metadata */
if (!archive(lv->vg))
return_0;
@@ -2961,7 +3211,10 @@ static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
log_debug_metadata("Adding metadata LVs to %s", display_lvname(lv));
if (!_raid0_add_or_remove_metadata_lvs(lv, 1 /* update_and_reload */, allocate_pvs, NULL))
return 0;
}
/* raid0_meta -> raid4 needs clearing of MetaLVs in order to avoid raid disk role cahnge issues in the kernel */
} else if (segtype_is_raid4(new_segtype) &&
!_clear_meta_lvs(lv))
return 0;
/* Add the additional component LV pairs */
log_debug_metadata("Adding %" PRIu32 " component LV pair(s) to %s", new_image_count - lv_raid_image_count(lv),
@@ -2969,8 +3222,9 @@ static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
if (!_lv_raid_change_image_count(lv, new_image_count, allocate_pvs, NULL, 0, 1))
return 0;
if (!segtype_is_raid4(new_segtype)) {
/* Can't convert striped/raid0* to e.g. raid10_offset */
if (segtype_is_raid4(new_segtype) &&
(!_shift_parity_dev(seg) ||
!_rename_area_lvs(lv, "_"))) {
log_error("Can't convert %s to %s", display_lvname(lv), new_segtype->name);
return 0;
}
@@ -2987,6 +3241,14 @@ static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS)
if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, NULL))
return_0;
if (segtype_is_raid4(new_segtype)) {
/* We had to rename SubLVs because of collision free sgifting, rename back... */
if (!_rename_area_lvs(lv, NULL))
return 0;
if (!lv_update_and_reload(lv))
return_0;
}
return 1;
}
@@ -3630,6 +3892,7 @@ has_enough_space:
* new SubLVS are allocated on PVs on list @allocate_pvs.
*/
static int _lv_raid_rebuild_or_replace(struct logical_volume *lv,
int force,
struct dm_list *remove_pvs,
struct dm_list *allocate_pvs,
int rebuild)
@@ -3804,7 +4067,8 @@ try_again:
* supplied - knowing that only the image with the error target
* will be affected.
*/
if (!_raid_extract_images(lv, raid_seg->area_count - match_count,
if (!_raid_extract_images(lv, force,
raid_seg->area_count - match_count,
partial_segment_removed ?
&lv->vg->pvs : remove_pvs, 0,
&old_lvs, &old_lvs)) {
@@ -3909,7 +4173,7 @@ skip_alloc:
int lv_raid_rebuild(struct logical_volume *lv,
struct dm_list *rebuild_pvs)
{
return _lv_raid_rebuild_or_replace(lv, rebuild_pvs, NULL, 1);
return _lv_raid_rebuild_or_replace(lv, 0, rebuild_pvs, NULL, 1);
}
/*
@@ -3922,10 +4186,11 @@ int lv_raid_rebuild(struct logical_volume *lv,
* allocating new SubLVs from PVs on list @allocate_pvs.
*/
int lv_raid_replace(struct logical_volume *lv,
int force,
struct dm_list *remove_pvs,
struct dm_list *allocate_pvs)
{
return _lv_raid_rebuild_or_replace(lv, remove_pvs, allocate_pvs, 0);
return _lv_raid_rebuild_or_replace(lv, force, remove_pvs, allocate_pvs, 0);
}
int lv_raid_remove_missing(struct logical_volume *lv)

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -268,6 +268,7 @@ struct segment_type *init_unknown_segtype(struct cmd_context *cmd,
#define RAID_FEATURE_RAID10 (1U << 0) /* version 1.3 */
#define RAID_FEATURE_RAID0 (1U << 1) /* version 1.7 */
#define RAID_FEATURE_RESHAPING (1U << 2) /* version 1.8 */
#define RAID_FEATURE_RAID4 (1U << 3) /* ! version 1.8 or 1.9.0 */
#ifdef RAID_INTERNAL
int init_raid_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);

View File

@@ -748,6 +748,19 @@ int lv_is_thin_origin(const struct logical_volume *lv, unsigned int *snap_count)
return r;
}
int lv_is_thin_snapshot(const struct logical_volume *lv)
{
struct lv_segment *seg;
if (!lv_is_thin_volume(lv))
return 0;
if ((seg = first_seg(lv)) && (seg->origin || seg->external_lv))
return 1;
return 0;
}
/*
* Explict check of new thin pool for usability
*

View File

@@ -15,6 +15,8 @@
#define LVM_DBUS_DESTINATION "com.redhat.lvmdbus1"
#define LVM_DBUS_PATH "/com/redhat/lvmdbus1/Manager"
#define LVM_DBUS_INTERFACE "com.redhat.lvmdbus1.Manager"
#define SD_BUS_SYSTEMD_NO_SUCH_UNIT_ERROR "org.freedesktop.systemd1.NoSuchUnit"
#define SD_BUS_DBUS_SERVICE_UNKNOWN_ERROR "org.freedesktop.DBus.Error.ServiceUnknown"
#ifdef NOTIFYDBUS_SUPPORT
#include <systemd/sd-bus.h>
@@ -26,6 +28,7 @@ int lvmnotify_is_supported(void)
void lvmnotify_send(struct cmd_context *cmd)
{
static const char _dbus_notification_failed_msg[] = "D-Bus notification failed";
sd_bus *bus = NULL;
sd_bus_message *m = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -61,7 +64,11 @@ void lvmnotify_send(struct cmd_context *cmd)
cmd_name);
if (ret < 0) {
log_warn("WARNING: D-Bus notification failed: %s", error.message);
if (sd_bus_error_has_name(&error, SD_BUS_SYSTEMD_NO_SUCH_UNIT_ERROR) ||
sd_bus_error_has_name(&error, SD_BUS_DBUS_SERVICE_UNKNOWN_ERROR))
log_debug_dbus("%s: %s", _dbus_notification_failed_msg, error.message);
else
log_warn("WARNING: %s: %s", _dbus_notification_failed_msg, error.message);
goto out;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2013 Red Hat, Inc. All rights reserved.
* Copyright (C) 2011-2016 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -366,7 +366,7 @@ static int _raid_target_present(struct cmd_context *cmd,
static int _raid_checked = 0;
static int _raid_present = 0;
static int _raid_attrs = 0;
static unsigned _raid_attrs = 0;
uint32_t maj, min, patchlevel;
unsigned i;
@@ -389,6 +389,12 @@ static int _raid_target_present(struct cmd_context *cmd,
else
log_very_verbose("Target raid does not support %s.",
_features[i].feature);
if (!(maj == 1 && (min == 8 || (min == 9 && patchlevel == 0))))
_raid_attrs |= RAID_FEATURE_RAID4;
else
log_very_verbose("Target raid does not support %s.",
SEG_TYPE_NAME_RAID4);
}
if (attributes)

View File

@@ -114,9 +114,9 @@ static void _default_log_line(int level,
const char *f, va_list ap)
{
static int _abort_on_internal_errors = -1;
FILE *out = (level & _LOG_STDERR) ? stderr : stdout;
FILE *out = log_stderr(level) ? stderr : stdout;
level &= ~(_LOG_STDERR | _LOG_BYPASS_REPORT);
level = log_level(level);
if (level <= _LOG_WARN || _verbose) {
if (level < _LOG_WARN)
@@ -137,8 +137,7 @@ static void _default_log_line(int level,
__attribute__((format(printf, 5, 6)))
static void _default_log_with_errno(int level,
const char *file __attribute__((unused)),
int line __attribute__((unused)), int dm_errno_or_class,
const char *file, int line, int dm_errno_or_class,
const char *f, ...)
{
va_list ap;
@@ -162,29 +161,75 @@ static void _default_log(int level, const char *file,
dm_log_fn dm_log = _default_log;
dm_log_with_errno_fn dm_log_with_errno = _default_log_with_errno;
/*
* Wrapper function to reformat new messages to and
* old style logging which had not used errno parameter
*
* As we cannot simply pass '...' to old function we
* need to process arg list locally and just pass '%s' + buffer
*/
__attribute__((format(printf, 5, 6)))
static void _log_to_default_log(int level,
const char *file, int line, int dm_errno_or_class,
const char *f, ...)
{
va_list ap;
char buf[2 * PATH_MAX + 256]; /* big enough for most messages */
va_start(ap, f);
vsnprintf(buf, sizeof(buf), f, ap);
va_end(ap);
dm_log(level, file, line, "%s", buf);
}
/*
* Wrapper function take 'old' style message without errno
* and log it via new logging function with errno arg
*
* This minor case may happen if new libdm is used with old
* recompiled tool that would decided to use new logging,
* but still would like to use old binary plugins.
*/
__attribute__((format(printf, 4, 5)))
static void _log_to_default_log_with_errno(int level,
const char *file, int line, const char *f, ...)
{
va_list ap;
char buf[2 * PATH_MAX + 256]; /* big enough for most messages */
va_start(ap, f);
vsnprintf(buf, sizeof(buf), f, ap);
va_end(ap);
dm_log_with_errno(level, file, line, 0, "%s", buf);
}
void dm_log_init(dm_log_fn fn)
{
if (fn)
if (fn) {
dm_log = fn;
else
dm_log_with_errno = _log_to_default_log;
} else {
dm_log = _default_log;
dm_log_with_errno = _default_log_with_errno;
dm_log_with_errno = _default_log_with_errno;
}
}
int dm_log_is_non_default(void)
{
return (dm_log == _default_log) ? 0 : 1;
return (dm_log == _default_log && dm_log_with_errno == _default_log_with_errno) ? 0 : 1;
}
void dm_log_with_errno_init(dm_log_with_errno_fn fn)
{
if (fn)
if (fn) {
dm_log = _log_to_default_log_with_errno;
dm_log_with_errno = fn;
else
} else {
dm_log = _default_log;
dm_log_with_errno = _default_log_with_errno;
dm_log = _default_log;
}
}
void dm_log_init_verbose(int level)

View File

@@ -3881,7 +3881,7 @@ merge:
continue;
if (_extents_overlap(ext, next)) {
log_warn("Warning: region IDs " FMTu64 " and "
log_warn("WARNING: region IDs " FMTu64 " and "
FMTu64 " overlap. Some events will be "
"counted twice.", ext->id, next->id);
/* merge larger extent into smaller */
@@ -4002,11 +4002,11 @@ int dm_stats_create_group(struct dm_stats *dms, const char *members,
}
if (precise && (precise != count))
log_warn("Grouping regions with different clock resolution: "
"precision may be lost");
log_warn("WARNING: Grouping regions with different clock resolution: "
"precision may be lost.");
if (!_stats_group_check_overlap(dms, regions, count))
log_info("Creating group with overlapping regions");
log_very_verbose("Creating group with overlapping regions.");
if (!_stats_create_group(dms, regions, alias, group_id))
goto bad;
@@ -4048,7 +4048,7 @@ int dm_stats_delete_group(struct dm_stats *dms, uint64_t group_id,
if (dm_bit(regions, i)) {
dm_bit_clear(regions, i);
if (remove_regions && !dm_stats_delete_region(dms, i))
log_warn("Failed to delete region "
log_warn("WARNING: Failed to delete region "
FMTu64 " on %s.", i, dms->name);
}
}
@@ -4142,7 +4142,7 @@ static int _stats_group_file_regions(struct dm_stats *dms, uint64_t *region_ids,
* returned by FIEMAP imply a kernel bug or a corrupt fs.
*/
if (!_stats_group_check_overlap(dms, regions, count))
log_info("Creating group with overlapping regions.");
log_very_verbose("Creating group with overlapping regions.");
if (!_stats_create_group(dms, regions, alias, &group_id))
goto bad;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -18,16 +18,10 @@
#include "libdevmapper.h"
extern dm_log_fn dm_log;
extern dm_log_with_errno_fn dm_log_with_errno;
#define LOG_MESG(l, f, ln, e, x...) \
do { \
if (dm_log_is_non_default()) \
dm_log(l, f, ln, ## x); \
else \
dm_log_with_errno(l, f, ln, e, ## x); \
} while (0)
dm_log_with_errno(l, f, ln, e, ## x)
#define LOG_LINE(l, x...) LOG_MESG(l, __FILE__, __LINE__, 0, ## x)
#define LOG_LINE_WITH_ERRNO(l, e, x...) LOG_MESG(l, __FILE__, __LINE__, e, ## x)

View File

@@ -365,6 +365,9 @@ See corresponding operation --splitmirrors.
VG/RaidLV
.br
\[bu]
Options \-\-background, \-\-interval.
.br
\[bu]
Replace failed PVs in RaidLV.
.B lvconvert \-\-replace
@@ -502,6 +505,9 @@ Change the type of log used by MirrorLV.
VG/MirrorLV
.br
\[bu]
Options \-\-background, \-\-interval.
.br
\[bu]
Replace failed PVs in MirrorLV.
.B lvconvert \-\-type linear

View File

@@ -34,6 +34,7 @@ TOOL=blkdeactivate
DEV_DIR='/dev'
SYS_BLK_DIR='/sys/block'
MOUNTPOINT="/bin/mountpoint"
UMOUNT="/bin/umount"
DMSETUP="@sbindir@/dmsetup"
LVM="@sbindir@/lvm"
@@ -157,9 +158,11 @@ device_umount_one() {
echo -n " [UMOUNT]: unmounting $name ($kname) mounted on $mnt... "
if eval $UMOUNT $UMOUNT_OPTS "$(printf "%s" "$mnt")" $OUT $ERR; then
echo "done"
else
elif $MOUNTPOINT -q "$mnt"; then
echo "skipping"
add_device_to_skip_list
else
echo "already unmounted"
fi
else
echo " [SKIP]: unmount of $name ($kname) mounted on $mnt"

View File

@@ -77,6 +77,9 @@ help:
@echo " check_cluster Run tests with cluster daemon."
@echo " check_lvmetad Run tests with lvmetad daemon."
@echo " check_lvmpolld Run tests with lvmpolld daemon."
@echo " check_cluster_lvmpolld Run tests with clvmd and lvmpolld daemon."
@echo " check_lvmetad_lvmpolld Run tests with lvmetad and lvmpolld daemon."
@echo " check_all_lvmpolld Run all tests with lvmpolld daemon."
@echo " check_lvmlockd_sanlock Run tests with lvmlockd and sanlock."
@echo " check_lvmlockd_dlm Run tests with lvmlockd and dlm."
@echo " check_lvmlockd_test Run tests with lvmlockd --test."
@@ -144,6 +147,21 @@ endif
ifeq ("@BUILD_LVMPOLLD@", "yes")
check_lvmpolld: .tests-stamp
VERBOSE=$(VERBOSE) ./lib/runner \
--testdir . --outdir results \
--flavours ndev-lvmpolld --only $(T) --skip $(S)
check_cluster_lvmpolld: .tests-stamp
VERBOSE=$(VERBOSE) ./lib/runner \
--testdir . --outdir results \
--flavours ndev-cluster-lvmpolld --only $(T) --skip $(S)
check_lvmetad_lvmpolld: .tests-stamp
VERBOSE=$(VERBOSE) ./lib/runner \
--testdir . --outdir results \
--flavours ndev-lvmetad-lvmpolld --only $(T) --skip $(S)
check_all_lvmpolld: .tests-stamp
VERBOSE=$(VERBOSE) ./lib/runner \
--testdir . --outdir results \
--flavours ndev-lvmpolld,ndev-cluster-lvmpolld,ndev-lvmetad-lvmpolld --only $(T) --skip $(S)

View File

@@ -1063,7 +1063,7 @@ generate_config() {
cat > "$config_values" <<-EOF
activation/checks = 1
activation/monitoring = 0
activation/polling_interval = 0
activation/polling_interval = 1
activation/retry_deactivation = 1
activation/snapshot_autoextend_percent = 50
activation/snapshot_autoextend_threshold = 50
@@ -1376,6 +1376,15 @@ have_raid() {
esac
}
have_raid4 () {
local r=1
have_raid 1 8 0 && r=0
have_raid 1 9 1 && r=1
return $r
}
have_cache() {
test "$CACHE" = shared -o "$CACHE" = internal || {
echo "Cache is not built-in." >&2

View File

@@ -16,7 +16,7 @@ TEST_RAID=raid456
aux raid456_replace_works || skip
aux have_raid 1 5 2 || skip
run_types raid4 -i 2 "$dev1" "$dev2" "$dev3" "$dev4"
aux have_raid4 && run_types raid4 -i 2 "$dev1" "$dev2" "$dev3" "$dev4"
run_types raid5 -i 2 "$dev1" "$dev2" "$dev3" "$dev4"
run_types raid6 -i 3 "$dev1" "$dev2" "$dev3" "$dev4" "$dev5"

View File

@@ -85,12 +85,9 @@ offset=$(( offset + 2 ))
# update in case mirror ever gets faster and allows parallel read
aux delay_dev "$dev2" 0 2000 ${offset}:1
lvcreate -aey -l5 -Zn -Wn --type mirror --regionsize 16K -m2 -n $lv1 $vg "$dev1" "$dev2" "$dev4" "$dev3:$DEVRANGE"
# FIXME: add a new explicit option to define the polling behavior
# done here with 'lvconvert vg/lv'. That option can specify
# that the command succeeds even if the LV doesn't need polling.
should not lvconvert -m-1 $vg/$lv1 "$dev1"
aux enable_dev "$dev2"
should lvconvert $vg/$lv1 # wait
lvconvert --startpoll $vg/$lv1 || true # wait
lvconvert -m2 $vg/$lv1 "$dev1" "$dev2" "$dev4" "$dev3:0" # If the above "should" failed...
aux wait_for_sync $vg $lv1
@@ -116,7 +113,7 @@ LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+1 -b $vg/$lv1 "$dev4"
# Next convert should fail b/c we can't have 2 at once
should not lvconvert -m+1 $vg/$lv1 "$dev5"
aux enable_dev "$dev4"
should lvconvert $vg/$lv1 # wait
lvconvert --startpoll $vg/$lv1 || true # wait
lvconvert -m2 $vg/$lv1 # In case the above "should" actually failed
check mirror $vg $lv1 "$dev3"
@@ -159,7 +156,7 @@ lvcreate -aey -l2 --type mirror -m1 -n $lv1 $vg "$dev1" "$dev2" "$dev3:$DEVRANGE
lvchange -an $vg/$lv1
lvconvert -m+1 $vg/$lv1 "$dev4"
lvchange -aey $vg/$lv1
should lvconvert $vg/$lv1 # wait
lvconvert --startpoll $vg/$lv1 || true # wait
check mirror $vg $lv1 "$dev3"
check mirror_no_temporaries $vg $lv1
lvremove -ff $vg
@@ -171,7 +168,7 @@ lvremove -ff $vg
lvcreate -aey -l2 --type mirror -m1 -n $lv1 $vg "$dev1" "$dev2" "$dev3:$DEVRANGE"
LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+1 -b $vg/$lv1 "$dev4"
lvconvert -m-1 $vg/$lv1 "$dev4"
should lvconvert $vg/$lv1 # wait
lvconvert --startpoll $vg/$lv1 || true # wait
check mirror $vg $lv1 "$dev3"
check mirror_no_temporaries $vg $lv1
@@ -182,7 +179,7 @@ lvremove -ff $vg
lvcreate -aey -l2 --type mirror -m1 -n $lv1 $vg "$dev1" "$dev2" "$dev3:$DEVRANGE"
LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+2 -b $vg/$lv1 "$dev4" "$dev5"
lvconvert -m-1 $vg/$lv1 "$dev4"
should lvconvert $vg/$lv1 # wait
lvconvert --startpoll $vg/$lv1 || true # wait
check mirror $vg $lv1 "$dev3"
check mirror_no_temporaries $vg $lv1
@@ -195,9 +192,9 @@ LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+1 -b $vg/$lv1 "$dev4"
# FIXME: Extra wait here for mirror upconvert synchronization
# otherwise we may fail her on parallel upconvert and downconvert
# lvconvert-mirror-updown.sh tests this errornous case separately
should lvconvert $vg/$lv1
lvconvert --startpoll $vg/$lv1 || true
lvconvert -m-1 $vg/$lv1 "$dev2"
should lvconvert $vg/$lv1
lvconvert --startpoll $vg/$lv1 || true
check mirror $vg $lv1 "$dev3"
check mirror_no_temporaries $vg $lv1
@@ -210,9 +207,9 @@ LVM_TEST_TAG="kill_me_$PREFIX" lvconvert -m+1 -b $vg/$lv1 "$dev4"
# FIXME: Extra wait here for mirror upconvert synchronization
# otherwise we may fail her on parallel upconvert and downconvert
# lvconvert-mirror-updown.sh tests this errornous case separately
should lvconvert $vg/$lv1
lvconvert --startpoll $vg/$lv1 || true
lvconvert -m-1 $vg/$lv1 "$dev2"
should lvconvert $vg/$lv1
lvconvert --startpoll $vg/$lv1 || true
check mirror $vg $lv1 "$dev3"
check mirror_no_temporaries $vg $lv1

View File

@@ -16,6 +16,9 @@ SKIP_WITH_LVMPOLLD=1
aux have_raid 1 9 0 || skip
correct_raid4_layout=0
aux have_raid 1 9 1 && correct_raid4_layout=1
aux prepare_vg 9 288
# Delay 1st leg so that rebuilding status characters
@@ -78,22 +81,61 @@ aux wait_for_sync $vg $lv1
# Clean up
lvremove --yes $vg/$lv1
# Create 3-way raid0
lvcreate -y -aey --type raid0 -i 3 -L 64M -n $lv1 $vg
check lv_field $vg/$lv1 segtype "raid0"
# Create 3-way striped
lvcreate -y -aey --type striped -i 3 -L 64M -n $lv1 $vg
check lv_field $vg/$lv1 segtype "striped"
check lv_field $vg/$lv1 stripes 3
echo y | mkfs -t ext4 /dev/mapper/$vg-$lv1
fsck -fn /dev/mapper/$vg-$lv1
# Convert raid0 -> raid4
# Create 3-way raid0
lvcreate -y -aey --type raid0 -i 3 -L 64M -n $lv2 $vg
check lv_field $vg/$lv2 segtype "raid0"
check lv_field $vg/$lv2 stripes 3
echo y | mkfs -t ext4 /dev/mapper/$vg-$lv2
fsck -fn /dev/mapper/$vg-$lv2
# Create 3-way raid0_meta
lvcreate -y -aey --type raid0_meta -i 3 -L 64M -n $lv3 $vg
check lv_field $vg/$lv3 segtype "raid0_meta"
check lv_field $vg/$lv3 stripes 3
echo y | mkfs -t ext4 /dev/mapper/$vg-$lv3
fsck -fn /dev/mapper/$vg-$lv3
if [ $correct_raid4_layout -eq 1 ]
then
# Create 3-way raid4
lvcreate -y -aey --type raid4 -i 3 -L 64M -n $lv4 $vg
check lv_field $vg/$lv4 segtype "raid4"
check lv_field $vg/$lv4 stripes 4
echo y | mkfs -t ext4 /dev/mapper/$vg-$lv4
fsck -fn /dev/mapper/$vg-$lv4
aux wait_for_sync $vg $lv4
fsck -fn /dev/mapper/$vg-$lv4
# Convert raid4 -> striped (correct raid4 mapping test!)
lvconvert -y --ty striped $vg/$lv4
check lv_field $vg/$lv4 segtype "striped"
check lv_field $vg/$lv4 stripes 3
fsck -fn /dev/mapper/$vg-$lv4
# Convert striped -> raid4
lvconvert -y --ty raid4 $vg/$lv1
lvchange --refresh $vg/$lv1
check lv_field $vg/$lv1 segtype "raid4"
check lv_field $vg/$lv1 stripes 4
fsck -fn /dev/mapper/$vg-$lv1
aux wait_for_sync $vg $lv1
fsck -fn /dev/mapper/$vg-$lv1
# Convert raid0 -> raid4
lvconvert -y --ty raid4 $vg/$lv2
check lv_field $vg/$lv2 segtype "raid4"
check lv_field $vg/$lv2 stripes 4
fsck -fn /dev/mapper/$vg-$lv2
aux wait_for_sync $vg $lv2
fsck -fn /dev/mapper/$vg-$lv2
# Convert raid4 -> raid0_meta
lvconvert -y --ty raid0_meta $vg/$lv1
check lv_field $vg/$lv1 segtype "raid0_meta"
@@ -116,11 +158,24 @@ fsck -fn /dev/mapper/$vg-$lv1
# Convert raid0 -> raid4
lvconvert -y --ty raid4 $vg/$lv1
lvchange --refresh $vg/$lv1
check lv_field $vg/$lv1 segtype "raid4"
check lv_field $vg/$lv1 stripes 4
fsck -fn /dev/mapper/$vg-$lv1
aux wait_for_sync $vg $lv1
fsck -fn /dev/mapper/$vg-$lv1
# Convert raid4 -> striped
lvconvert -y --ty striped $vg/$lv1
check lv_field $vg/$lv1 segtype "striped"
check lv_field $vg/$lv1 stripes 3
fsck -fn /dev/mapper/$vg-$lv1
else
not lvcreate -y -aey --type raid4 -i 3 -L 64M -n $lv4 $vg
not lvconvert -y --ty raid4 $vg/$lv1
not lvconvert -y --ty raid4 $vg/$lv2
fi
vgremove -ff $vg

View File

@@ -31,8 +31,11 @@ aux have_raid 1 3 0 || skip
aux prepare_pvs 7 # 7 devices for 2 dev replacement of 5-dev RAID6
vgcreate -s 256k $vg $(cat DEVICES)
levels="5 6"
aux have_raid4 && levels="4 5 6"
# RAID 4/5/6 (can replace up to 'parity' devices)
for i in 4 5 6; do
for i in $levels; do
lvcreate --type raid$i -i 3 -l 3 -n $lv1 $vg
if [ $i -eq 6 ]; then

View File

@@ -25,7 +25,8 @@ which "$FSCK" || skip
#
# Main
#
aux have_cache 1 5 0 || skip
# older versions of cache target reported unreliably write failures
aux have_cache 1 7 0 || skip
aux prepare_vg 4

View File

@@ -53,7 +53,8 @@ delay 50
# RAID1 triple-leg single replace during initial sync
lvcreate --type raid1 -m 2 -L $RAID_SIZE -n $lv1 $vg "$dev1" "$dev2" "$dev3"
aux disable_dev "$dev2" "$dev3"
not lvconvert -y --repair $vg/$lv1
# FIXME 2016/11/04 AGK: Disabled next line as it fails to guarantee it is not already in sync.
#not lvconvert -y --repair $vg/$lv1
aux wait_for_sync $vg $lv1
lvconvert -y --repair $vg/$lv1
vgreduce --removemissing $vg

View File

@@ -21,6 +21,9 @@ aux can_use_16T || skip
aux have_raid 1 3 0 || skip
segtypes="raid5"
aux have_raid4 && segtypes="raid4 raid5"
# Prepare 5x ~1P sized devices
aux prepare_pvs 5 1000000000
@@ -53,7 +56,7 @@ check raid_leg_status $vg1 $lv1 "AA"
lvremove -ff $vg1
# 750 TiB raid4/5
for segtype in raid4 raid5; do
for segtype in $segtypes; do
lvcreate --type $segtype -i 3 -L 750T -n $lv1 $vg1 --nosync
check lv_field $vg1/$lv1 size "750.00t"
check raid_leg_status $vg1 $lv1 "AAAA"

View File

@@ -16,17 +16,20 @@ SKIP_WITH_LVMPOLLD=1
aux have_raid 1 7 0 || skip
segtypes=raid5
aux have_raid4 && segtypes="raid4 raid5"
aux prepare_vg 6
# Delay 1st leg so that rebuilding status characters
# can be read before resync finished too quick.
aux delay_dev "$dev1" 0 10 $(get first_extent_sector "$dev1")
aux delay_dev "$dev1" 0 50 $(get first_extent_sector "$dev1")
# raid0/raid0_meta don't support resynchronization
for r in raid0 raid0_meta
do
lvcreate --yes --type raid0 -i 3 -l 1 -n $lv1 $vg
lvcreate --yes --type $r -i 3 -l 1 -n $lv1 $vg
check raid_leg_status $vg $lv1 "AAA"
lvremove --yes $vg/$lv1
done
@@ -43,8 +46,8 @@ lvcreate --yes --type raid1 --nosync -m 2 -l 1 -n $lv1 $vg
check raid_leg_status $vg $lv1 "AAA"
lvremove --yes $vg/$lv1
for r in raid4 raid5
do
for r in $segtypes
do
# raid4/5 support resynchronization
lvcreate --yes --type $r -i 3 -l 2 -n $lv1 $vg
check raid_leg_status $vg $lv1 "aaaa"

View File

@@ -23,6 +23,9 @@ lv_devices() {
########################################################
aux have_raid 1 3 0 || skip
RAID4=""
aux have_raid4 && RAID4=raid4
aux prepare_pvs 6 20 # 6 devices for RAID10 (2-mirror,3-stripe) test
vgcreate -s 512k $vg $(cat DEVICES)
@@ -54,7 +57,7 @@ aux wait_for_sync $vg $lv1
lvremove -ff $vg
# Create RAID 4/5/6 (explicit 3-stripe + parity devs)
for i in raid4 \
for i in $RAID4 \
raid5 raid5_ls raid5_la raid5_rs raid5_ra \
raid6 raid6_zr raid6_nr raid6_nc; do
@@ -64,7 +67,7 @@ for i in raid4 \
done
# Create RAID 4/5/6 (explicit 3-stripe + parity devs) - Set min/max recovery
for i in raid4 \
for i in $RAID4 \
raid5 raid5_ls raid5_la raid5_rs raid5_ra \
raid6 raid6_zr raid6_nr raid6_nc; do

View File

@@ -54,7 +54,7 @@ mkdir test_mnt
setup_merge_ $vg1 $lv1
mount "$(lvdev_ $vg1 $lv1)" test_mnt
lvconvert --merge $vg1/$(snap_lv_name_ $lv1)
lvconvert --mergesnapshot $vg1/$(snap_lv_name_ $lv1)
umount test_mnt
vgchange -an $vg1

View File

@@ -24,6 +24,28 @@ pvscan --cache
vgs | grep $vg1
# Check that an LV cannot be activated by lvchange while VG is exported
lvcreate -n $lv1 -l 4 -a n $vg1
check lv_exists $vg1
vgexport $vg1
fail lvs $vg1
fail lvchange -ay $vg1/$lv1
vgimport $vg1
check lv_exists $vg1
check lv_field $vg/$lv1 lv_active ""
# Check that an LV cannot be activated by pvscan while VG is exported
vgexport $vg1
pvscan --cache -aay "$dev1"
pvscan --cache -aay "$dev2"
vgimport $vg1
check lv_exists $vg1
check lv_field $vg1/$lv1 lv_active ""
pvscan --cache -aay "$dev1"
pvscan --cache -aay "$dev2"
check lv_field $vg1/$lv1 lv_active "active"
lvchange -an $vg1/$lv1
# When MDA is ignored on PV, do not read any VG
# metadata from such PV as it may contain old
# metadata which hasn't been updated for some

View File

@@ -16,6 +16,9 @@ SKIP_WITH_LVMPOLLD=1
aux have_raid 1 3 0 || skip
levels="5 6"
aux have_raid4 && levels="4 5 6"
aux prepare_pvs 6 80
vgcreate -s 256K $vg $(cat DEVICES)
@@ -37,7 +40,7 @@ for deactivate in true false; do
#check raid_images_contiguous $vg $lv1
# Extend and reduce 3-striped RAID 4/5/6
for i in 4 5 6 ; do
for i in $levels ; do
lvcreate --type raid$i -i 3 -l 3 -n $lv2 $vg
test $deactivate && {
@@ -59,7 +62,7 @@ done
# Bug 1005434
# Ensure extend is contiguous
lvcreate --type raid4 -l 2 -i 2 -n $lv1 $vg "$dev4" "$dev5" "$dev6"
lvcreate --type raid5 -l 2 -i 2 -n $lv1 $vg "$dev4" "$dev5" "$dev6"
lvextend -l +2 --alloc contiguous $vg/$lv1
check lv_tree_on $vg $lv1 "$dev4" "$dev5" "$dev6"

View File

@@ -76,7 +76,7 @@ test_pvmove_resume() {
aux enable_dev "$dev2"
i=0
while get lv_field $vg name -a | grep "^pvmove"; do
while get lv_field $vg name -a | egrep "^\[?pvmove"; do
# wait for 30 secs at max
test $i -ge 300 && die "Pvmove is too slow or does not progress."
sleep .1

View File

@@ -89,7 +89,7 @@ test_pvmove_resume() {
aux enable_dev "$dev5"
i=0
while get lv_field $vg name -a | grep "^\[?pvmove"; do
while get lv_field $vg name -a | egrep "^\[?pvmove"; do
# wait for 30 secs at max
test $i -ge 300 && die "Pvmove is too slow or does not progress."
sleep .1

View File

@@ -34,19 +34,20 @@ snap_and_merge() {
SLEEP_PID=$!
# initiate background merge
lvconvert -b --merge $vg/$lv2
lvconvert -b --mergesnapshot $vg/$lv2
lvs -a -o+lv_merging,lv_merge_failed $vg
kill $SLEEP_PID
aux delay_dev "$dev1" 0 200 $(get first_extent_sector "$dev1"):
lvchange --refresh $vg/$lv1
lvchange --poll n --refresh $vg/$lv1
dmsetup table
lvs -a -o+lv_merging,lv_merge_failed $vg
sleep 1
check lv_attr_bit state $vg/$lv1 "a"
check lv_attr_bit state $vg/$lv2 "a"
aux error_dev "$dev2" $(get first_extent_sector "$dev2"):
aux enable_dev "$dev1"
# delay to let snapshot merge 'discover' failing COW device
sleep 1
sync
@@ -56,7 +57,7 @@ snap_and_merge() {
check lv_attr_bit state $vg/$lv2 "m"
# device OK and running in full speed
aux enable_dev "$dev1" "$dev2"
aux enable_dev "$dev2"
# reactivate so merge can finish
lvchange -an $vg

View File

@@ -51,15 +51,15 @@ mkdir test_mnt
# test full merge of a single LV
setup_merge_ $vg $lv1
# make sure lvconvert --merge requires explicit LV listing
not lvconvert --merge
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
# make sure lvconvert --mergesnapshot requires explicit LV listing
not lvconvert --mergesnapshot
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
lvremove -f $vg/$lv1
# test that an actively merging snapshot may not be removed
setup_merge_ $vg $lv1
lvconvert -i+100 --merge --background $vg/$(snap_lv_name_ $lv1)
lvconvert -i+100 --mergesnapshot --background $vg/$(snap_lv_name_ $lv1)
not lvremove -f $vg/$(snap_lv_name_ $lv1)
lvremove -f $vg/$lv1
@@ -67,7 +67,7 @@ lvremove -f $vg/$lv1
# "onactivate merge" test
setup_merge_ $vg $lv1
mount "$(lvdev_ $vg $lv1)" test_mnt
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
# -- refresh LV while FS is still mounted (merge must not start),
# verify 'snapshot-origin' target is still being used
lvchange --refresh $vg/$lv1
@@ -88,7 +88,7 @@ lvremove -f $vg/$lv1
# to make sure preload of origin's metadata is _not_ performed
setup_merge_ $vg $lv1
mount "$(lvdev_ $vg $lv1)" test_mnt
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
# -- refresh LV while FS is still mounted (merge must not start),
# verify 'snapshot-origin' target is still being used
lvchange --refresh $vg/$lv1
@@ -99,7 +99,7 @@ lvremove -f $vg/$lv1
# test multiple snapshot merge; tests copy out that is driven by merge
setup_merge_ $vg $lv1 1
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
lvremove -f $vg/$lv1
@@ -108,7 +108,7 @@ setup_merge_ $vg $lv1
setup_merge_ $vg $lv2
lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv1)
lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv2)
lvconvert --merge @this_is_a_test
lvconvert --mergesnapshot @this_is_a_test
lvs $vg | tee out
not grep $(snap_lv_name_ $lv1) out
not grep $(snap_lv_name_ $lv2) out

View File

@@ -102,7 +102,7 @@ lvcreate -s -n snap $vg/$lv1
lvcreate -s -L10 -n oldsnapof_${lv1} $vg/$lv1
not lvconvert --merge $vg/snap
$MKFS "$DM_DEV_DIR/$vg/oldsnapof_${lv1}"
lvconvert --merge $vg/oldsnapof_${lv1}
lvconvert --mergesnapshot $vg/oldsnapof_${lv1}
fsck -n "$DM_DEV_DIR/$vg/$lv1"
check lv_not_exists $vg oldsnapof_${lv1}
# Add old snapshot to thin snapshot

View File

@@ -60,6 +60,7 @@ arg(locktype_ARG, '\0', "locktype", locktype_VAL, 0, 0)
arg(logonly_ARG, '\0', "logonly", 0, 0, 0)
arg(maxrecoveryrate_ARG, '\0', "maxrecoveryrate", sizekb_VAL, 0, 0)
arg(merge_ARG, '\0', "merge", 0, 0, 0)
arg(mergesnapshot_ARG, '\0', "mergesnapshot", 0, 0, 0)
arg(mergedconfig_ARG, '\0', "mergedconfig", 0, 0, 0)
arg(metadatacopies_ARG, '\0', "metadatacopies", metadatacopies_VAL, 0, 0)
arg(metadataignore_ARG, '\0', "metadataignore", bool_VAL, 0, 0)
@@ -117,6 +118,7 @@ arg(splitmirrors_ARG, '\0', "splitmirrors", number_VAL, 0, 0)
arg(splitsnapshot_ARG, '\0', "splitsnapshot", 0, 0, 0)
arg(showdeprecated_ARG, '\0', "showdeprecated", 0, 0, 0)
arg(showunsupported_ARG, '\0', "showunsupported", 0, 0, 0)
arg(startpoll_ARG, '\0', "startpoll", 0, 0, 0)
arg(stripes_long_ARG, '\0', "stripes", number_VAL, 0, 0)
arg(syncaction_ARG, '\0', "syncaction", syncaction_VAL, 0, 0)
arg(sysinit_ARG, '\0', "sysinit", 0, 0, 0)

View File

@@ -3,8 +3,8 @@
# and tools/command-lines-count.h must be regenerated
# with:
#
# scripts/create-commands --output count scripts/command-lines.in > tools/command-lines-count.h
# scripts/create-commands --output struct scripts/command-lines.in > tools/command-lines.h
# tools/create-commands --output count tools/command-lines.in > tools/command-lines-count.h
# tools/create-commands --output struct tools/command-lines.in > tools/command-lines.h
#
#
@@ -83,6 +83,13 @@
# Also, --thinpool VG/LV or --cachepool VG/LV can be used in
# place of --name to provide the VG name instead of pos 1.
#
# Note that one the most difficult aspect of these definitions is
# the variants of --thin / --type thin / --type thin-pool,
# --cache / --type cache / --type cache-pool.
# There are no consistent rules to follow and the behaviors are
# unpredictable; each possible variation and combination needs
# to be tested individually to see what it means.
#
# Some options have multiple names, but only one form of the name
# is used in these definitions. Synonyms will be recognized when
# matching a command to a command definition.
@@ -118,19 +125,39 @@
# RULE: rules that a given command must follow, i.e. required (and)
# or invalid (not) combinations of options, LV types or LV properties.
#
# RULE: --opt|LV_type|lv_is_prop|all and|not --opt|LV_type|lv_is_prop
# RULE: A and|not B
#
# Conditions in A are applied to a given command+LV.
# If the conditions in A are true, then the checks in B
# are applied. If the checks in B are true|false according
# to and|not, then the command fails|continues.
#
# When A is "all", the conditions in B are always applied.
#
# Conditions:
# . if any --option listed is set, the checks may apply
# . if any LV_type listed matches LV, the checks may apply
# . if all lv_is_prop listed matches LV, the checks may apply
# . if all of the above pass, then perform the checks
#
# Checks for "and":
# . if any --option listed is not set, then fail
# . if none of the LV_types matches the LV, then fail
# . if any of the lv_is_prop do not match the LV, then fail
#
# Checks for "not":
# . if any --option listed is set, then fail
# . if any of the LV_types matches the LV, then fail
# . if any of the lv_is_prop match the LV, then fail
#
# RULE: --option|LV_type|lv_is_prop|all ... and|not --option|LV_type|lv_is_prop ...
#
# RULE: --opt1 not --opt2
# RULE: --opt1 and --opt2
# RULE: --opt1 LV_type1 lv_is_prop1 and --opt2
# RULE: --opt1 LV_type1 and lv_is_prop1
# RULE: LV_type1 and lv_is_prop1
#
# Note that one the most difficult aspect of these definitions is
# the variants of --thin / --type thin / --type thin-pool,
# --cache / --type cache / --type cache-pool.
# There are no consistent rules to follow and the behaviors are
# unpredictable; each possible variation and combination needs
# to be tested individually to see what it means.
#
#
@@ -212,34 +239,36 @@ OO_LVCHANGE_META: --addtag Tag, --deltag Tag,
--minrecoveryrate SizeKB, --maxrecoveryrate SizeKB,
--writebehind Number, --writemostly WriteMostlyPV, --persistent n
# It's unfortunate that activate needs to be optionally allowed here;
# it should only be used explicitly, but it's been previously allowed
# in combination with unrelated metadata changes.
lvchange OO_LVCHANGE_META VG|LV|Tag|Select ...
OO: OO_LVCHANGE
OO: --activate Active, OO_LVCHANGE
ID: lvchange_properties
DESC: Change a general LV property.
RULE: all not lv_is_pvmove lv_is_origin lv_is_mirror_log lv_is_mirror_image
RULE: all not lv_is_pvmove lv_is_mirror_log lv_is_mirror_image
RULE: all and lv_is_vg_writable
RULE: --contiguous not --alloc
RULE: --profile not --detachprofile
RULE: --metadataprofile not --detachprofile
RULE: --minrecoveryrate and LV_raid
RULE: --maxrecoveryrate and LV_raid
RULE: --writebehind and LV_raid1
RULE: --writemostly and LV_raid1
RULE: --cachemode and LV_cache LV_cachepool
RULE: --cachepolicy and LV_cache LV_cachepool
RULE: --cachesettings and LV_cache LV_cachepool
RULE: --errorwhenfull and LV_thinpool
RULE: --discards and LV_thinpool
RULE: --zero and LV_thinpool
RULE: --minrecoveryrate --maxrecoveryrate and LV_raid
RULE: --writebehind --writemostly and LV_raid1
RULE: --cachemode --cachepolicy --cachesettings and LV_cache LV_cachepool
RULE: --errorwhenfull --discards --zero and LV_thinpool
RULE: --permission not lv_is_external_origin lv_is_raid_metadata lv_is_raid_image LV_thinpool
RULE: --alloc --contiguous --metadataprofile --permission --persistent --profile --readahead not lv_is_thick_origin
RULE: --alloc --discards --zero --cachemode --cachepolicy --cachesettings not lv_is_partial
# It's unfortunate that acativate needs to be optionally allowed here,
# like above, it was previouly allowed in combination.
lvchange --resync VG|LV_raid_mirror|Tag|Select ...
OO: OO_LVCHANGE
OO: --activate Activate, OO_LVCHANGE
ID: lvchange_resync
DESC: Resyncronize a mirror or raid LV.
RULE: all not lv_is_pvmove lv_is_locked
RULE: all not LV_raid0
RULE: all and LV_mirror LV_raid
lvchange --syncaction SyncAction VG|LV_raid|Tag|Select ...
OO: OO_LVCHANGE
@@ -253,15 +282,14 @@ ID: lvchange_rebuild
DESC: Reconstruct data on specific PVs of a raid LV.
RULE: all not LV_raid0
# try removing the META change options from here?
lvchange --activate Active VG|LV|Tag|Select ...
OO: --activationmode ActivationMode, --partial, --ignoreactivationskip,
--ignorelockingfailure, --sysinit, OO_LVCHANGE_META, OO_LVCHANGE
--ignorelockingfailure, --sysinit, OO_LVCHANGE
ID: lvchange_activate
DESC: Activate or deactivate an LV.
lvchange --refresh VG|LV|Tag|Select ...
OO: --partial, OO_LVCHANGE
OO: --partial, --poll Bool, OO_LVCHANGE
ID: lvchange_refresh
DESC: Reactivate an LV using the latest metadata.
@@ -360,6 +388,8 @@ lvconvert --type thin --thinpool LV LV_linear_striped_raid
OO: --thin, --originname LV_new, --zero Bool, OO_LVCONVERT_POOL, OO_LVCONVERT
ID: lvconvert_to_thin_with_external
DESC: Convert LV to type thin with an external origin.
RULE: all and lv_is_visible
RULE: all not lv_is_locked
# alternate form of lvconvert --type thin
lvconvert --thin --thinpool LV LV_linear_striped_raid
@@ -368,6 +398,8 @@ ID: lvconvert_to_thin_with_external
DESC: Convert LV to type thin with an external origin
DESC: (variant, infers --type thin).
FLAGS: SECONDARY_SYNTAX
RULE: all and lv_is_visible
RULE: all not lv_is_locked
---
@@ -376,6 +408,7 @@ OO: --cache, --cachemode CacheMode, --cachepolicy String,
--cachesettings String, --zero Bool, OO_LVCONVERT_POOL, OO_LVCONVERT
ID: lvconvert_to_cache_vol
DESC: Convert LV to type cache.
RULE: all and lv_is_visible
# alternate form of lvconvert --type cache
lvconvert --cache --cachepool LV LV_linear_striped_raid_thinpool
@@ -384,6 +417,7 @@ OO: --type cache, --cachemode CacheMode, --cachepolicy String,
ID: lvconvert_to_cache_vol
DESC: Convert LV to type cache (variant, infers --type cache).
FLAGS: SECONDARY_SYNTAX
RULE: all and lv_is_visible
---
@@ -393,14 +427,18 @@ OO: --stripes_long Number, --stripesize SizeKB,
OP: PV ...
ID: lvconvert_to_thinpool
DESC: Convert LV to type thin-pool.
RULE: all and lv_is_visible
RULE: all not lv_is_locked lv_is_origin lv_is_merging_origin lv_is_external_origin lv_is_virtual
# alternate form of lvconvert --type thin-pool
# deprecated because of non-standard syntax (missing positional arg)
# Commands in this form are converted to standard form so that
# the validation of LV types and rules specified above will apply.
lvconvert --thinpool LV_linear_striped_raid_cache
OO: --type thin-pool, --stripes_long Number, --stripesize SizeKB,
--discards Discards, --zero Bool, OO_LVCONVERT_POOL, OO_LVCONVERT
OP: PV ...
ID: lvconvert_to_thinpool
ID: lvconvert_to_thinpool_noarg
DESC: Convert LV to type thin-pool (variant, use --type thin-pool).
FLAGS: SECONDARY_SYNTAX
@@ -415,10 +453,13 @@ DESC: Convert LV to type cache-pool.
# alternate form of lvconvert --type cache-pool
# deprecated because of non-standard syntax (missing positional arg)
# Commands in this form are converted to standard form so that
# the validation of LV types and rules specified above will apply.
lvconvert --cachepool LV_linear_striped_raid
OO: --type cache-pool, OO_LVCONVERT_POOL, OO_LVCONVERT,
--cachemode CacheMode, --cachepolicy String, --cachesettings String
ID: lvconvert_to_cachepool
OP: PV ...
ID: lvconvert_to_cachepool_noarg
DESC: Convert LV to type cache-pool (variant, use --type cache-pool).
FLAGS: SECONDARY_SYNTAX
@@ -448,9 +489,6 @@ FLAGS: SECONDARY_SYNTAX
---
# lvconvert utilities related to snapshots and repair.
# Create a new command set for these and migrate them out of lvconvert?
# FIXME: lvconvert --merge is an extremely ambiguous command.
# It can do very different operations, but which one depends
# on knowing the LV type. So, the command doesn't know what
@@ -481,29 +519,58 @@ ID: lvconvert_merge
DESC: Merge LV that was previously split from a mirror.
DESC: Merge thin LV into its origin LV.
DESC: Merge COW snapshot LV into its origin.
RULE: all not lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow
RULE: all not lv_is_locked lv_is_pvmove lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow
---
# FIXME: by using two different positional args, this is the
# single violation of the standard method of using process_each_lv().
# Before calling process_each, it steals the first positional arg
# and adjusts argv/argc so it's not seen by process_each.
# lvconvert snapshot-related utilities
# Create a new command set for these and migrate them out of lvconvert?
lvconvert --mergesnapshot LV_snapshot ...
OO: --background, --interval Number, OO_LVCONVERT
ID: lvconvert_merge_snapshot
DESC: Merge LV that was previously split from a mirror.
RULE: all not lv_is_locked lv_is_pvmove lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow
---
lvconvert --splitsnapshot LV_snapshot
OO: OO_LVCONVERT
ID: lvconvert_split_cow_snapshot
DESC: Separate a COW snapshot from its origin LV.
RULE: all not lv_is_locked lv_is_pvmove lv_is_origin lv_is_external_origin lv_is_merging_cow
---
# NB: an unsual use of position args here, the first pos arg
# (will become origin LV) is not passed to process_each,
# the second pos arg (will become cow LV) is given to
# process_each. Because the first pos LV is not handled
# by process_each_lv, it cannot be checked against this
# command def, so a specific LV type in the first pos
# will not be checked.
# alternate form of lvconvert --snapshot
lvconvert --type snapshot LV_linear_striped_raid LV_snapshot
lvconvert --type snapshot LV LV_linear
OO: --snapshot, --chunksize SizeKB, --zero Bool, OO_LVCONVERT
ID: lvconvert_combine_split_snapshot
DESC: Combine LV with a previously split snapshot LV.
DESC: Combine a former COW snapshot (second arg) with a former
DESC: origin LV (first arg) to reverse a splitsnapshot command.
FLAGS: SECONDARY_SYNTAX
RULE: all not lv_is_locked lv_is_pvmove
lvconvert --snapshot LV_linear_striped_raid LV_snapshot
lvconvert --snapshot LV LV_linear
OO: --type snapshot, --chunksize SizeKB, --zero Bool, OO_LVCONVERT
ID: lvconvert_combine_split_snapshot
DESC: Combine LV with a previously split snapshot LV.
DESC: Combine a former COW snapshot (second arg) with a former
DESC: origin LV (first arg) to reverse a splitsnapshot command.
RULE: all not lv_is_locked lv_is_pvmove
---
# lvconvert repair/replace utilitiles
# Create a new command set for these and migrate them out of lvconvert?
# FIXME: use specific option names to distinguish these two
# very different commands, e.g.
#
@@ -519,36 +586,38 @@ DESC: Combine LV with a previously split snapshot LV.
# and the LV type is known.
lvconvert --repair LV_raid_mirror_thinpool
OO: --usepolicies, OO_LVCONVERT
OO: --usepolicies, --interval Number, OO_LVCONVERT
OP: PV ...
ID: lvconvert_repair_pvs_or_thinpool
DESC: Replace failed PVs in a raid or mirror LV.
DESC: Repair a thin pool.
---
RULE: all not lv_is_locked lv_is_pvmove
lvconvert --replace PV LV_raid
OO: OO_LVCONVERT
OP: PV ...
ID: lvconvert_replace_pv
DESC: Replace specific PV(s) in a raid* LV with another PV.
RULE: all not lv_is_locked lv_is_pvmove
---
lvconvert --splitsnapshot LV_snapshot
# This command just (re)starts the polling process on the LV
# to continue a previous conversion.
lvconvert --startpoll LV_mirror
OO: OO_LVCONVERT
ID: lvconvert_split_cow_snapshot
DESC: Separate a COW snapshot from its origin LV.
---
# FIXME: add a new option defining this operation, e.g. --poll-mirror
# The purpose of this command is not entirely clear.
ID: lvconvert_start_poll
DESC: Poll LV to continue conversion.
RULE: all and lv_is_converting
# alternate form of lvconvert --startpoll, this is only kept
# for compat since this was how it used to be done.
lvconvert LV_mirror
OO: OO_LVCONVERT
ID: lvconvert_poll_start
DESC: Poll mirror LV to collapse resync layers.
ID: lvconvert_start_poll
DESC: Poll LV to continue conversion.
RULE: all and lv_is_converting
FLAGS: SECONDARY_SYNTAX
---

View File

@@ -109,34 +109,30 @@ struct pos_arg {
};
/*
* When all values before the require|invalid match a given command,
* then all the values after are verified to be true|false.
*
* Rules that include only options can be verified before the VG
* is read. Otherwise, the rules are verified in process_each
* after the VG is read.
*
* RULE: --opt|LV_type|lv_is_prop|all require|invalid --opt|LV_type|lv_is_prop
* RULE: --opt1 invalid --opt2
* RULE: --opt1 require --opt2
* RULE: --opt1 LV_type1 lv_is_prop1 require --opt2
* RULE: --opt1 LV_type1 require lv_is_prop1
* RULE: LV_type1 require lv_is_prop1
* Commands using a given command definition must follow a set
* of rules. If a given command+LV matches the conditions in
* opts/lvt_bits/lvp_bits, then the checks are applied.
* If one condition is not met, the checks are not applied.
* If no conditions are set, the checks are always applied.
*/
#define RULE_INVALID 1
#define RULE_REQUIRE 2
struct cmd_rule {
int opt; /* apply rule when this option is set (foo_ARG) */
uint64_t lvt_bits; /* apply rule when LV has one of these types (lvt_enum_to_bit) */
uint64_t lvp_bits; /* apply rule when LV has all these properties (lvp_enum_to_bit) */
int *opts; /* if any option in this list is set, the check may apply */
uint64_t lvt_bits; /* if LV has one of these types (lvt_enum_to_bit), the check may apply */
uint64_t lvp_bits; /* if LV has all of these properties (lvp_enum_to_bit), the check may apply */
uint32_t rule; /* RULE_INVALID, RULE_REQUIRE: check values must [not] be true */
int *check_opts; /* used options must [not] be in this list */
uint64_t check_lvt_bits; /* LV must [not] have one of these type */
uint64_t check_lvp_bits; /* LV must [not] have all of these properties */
uint32_t rule; /* RULE_INVALID, RULE_REQUIRE: check values must [not] be true */
int opts_count; /* entries in opts[] */
int check_opts_count; /* entries in check_opts[] */
int check_opt; /* this option must [not] be set */
uint64_t check_lvt_bits; /* LV must [not] have this type */
uint64_t check_lvp_bits; /* LV must [not] have these properties */
};
/*

View File

@@ -1162,6 +1162,8 @@ static void add_flags(struct command *cmd, char *line)
cmd->cmd_flags |= CMD_FLAG_SECONDARY_SYNTAX;
}
#define MAX_RULE_OPTS 64
static void add_rule(struct command *cmd, char *line)
{
struct cmd_rule *rule;
@@ -1199,10 +1201,26 @@ static void add_rule(struct command *cmd, char *line)
}
else if (!strncmp(arg, "--", 2)) {
if (!rule->opts) {
if (!(rule->opts = malloc(MAX_RULE_OPTS * sizeof(int)))) {
printf("no mem\n");
exit(1);
}
memset(rule->opts, 0, MAX_RULE_OPTS * sizeof(int));
}
if (!rule->check_opts) {
if (!(rule->check_opts = malloc(MAX_RULE_OPTS * sizeof(int)))) {
printf("no mem\n");
exit(1);
}
memset(rule->check_opts, 0, MAX_RULE_OPTS * sizeof(int));
}
if (check)
rule->check_opt = opt_str_to_num(arg);
rule->check_opts[rule->check_opts_count++] = opt_str_to_num(arg);
else
rule->opt = opt_str_to_num(arg);
rule->opts[rule->opts_count++] = opt_str_to_num(arg);
}
else if (!strncmp(arg, "LV_", 3)) {
@@ -1260,8 +1278,8 @@ void print_command_count(void)
struct command *cmd;
int i, j;
printf("/* Do not edit. This file is generated by scripts/create-commands */\n");
printf("/* using command definitions from scripts/command-lines.in */\n");
printf("/* Do not edit. This file is generated by tools/create-commands */\n");
printf("/* using command definitions from tools/command-lines.in */\n");
printf("#define COMMAND_COUNT %d\n", cmd_count);
printf("enum {\n");
@@ -2361,12 +2379,12 @@ void print_man_command(void)
void print_command_struct(int only_usage)
{
struct command *cmd;
int i, j, ro, rp, oo, op, ru;
int i, j, ro, rp, oo, op, ru, ruo;
include_optional_opt_args(&lvm_all, "OO_USAGE_COMMON");
printf("/* Do not edit. This file is generated by scripts/create-commands */\n");
printf("/* using command definitions from scripts/command-lines.in */\n");
printf("/* Do not edit. This file is generated by tools/create-commands */\n");
printf("/* using command definitions from tools/command-lines.in */\n");
printf("\n");
for (i = 0; i < cmd_count; i++) {
@@ -2523,8 +2541,36 @@ void print_command_struct(int only_usage)
if (cmd->rule_count) {
for (ru = 0; ru < cmd->rule_count; ru++) {
printf("commands[%d].rules[%d].opt = %s;\n", i, ru,
cmd->rules[ru].opt ? opt_to_enum_str(cmd->rules[ru].opt) : "0");
printf("commands[%d].rules[%d].opts_count = %d;\n", i, ru, cmd->rules[ru].opts_count);
if (cmd->rules[ru].opts_count) {
printf("static int _command%d_rule%d_opts[] = { ", i, ru);
for (ruo = 0; ruo < cmd->rules[ru].opts_count; ruo++) {
if (ruo)
printf(", ");
printf("%s", opt_to_enum_str(cmd->rules[ru].opts[ruo]));
}
printf(" };\n");
printf("commands[%d].rules[%d].opts = _command%d_rule%d_opts;\n", i, ru, i, ru);
} else {
printf("commands[%d].rules[%d].opts = NULL;\n", i, ru);
}
printf("commands[%d].rules[%d].check_opts_count = %d;\n", i, ru, cmd->rules[ru].check_opts_count);
if (cmd->rules[ru].check_opts_count) {
printf("static int _command%d_rule%d_check_opts[] = { ", i, ru);
for (ruo = 0; ruo < cmd->rules[ru].check_opts_count; ruo++) {
if (ruo)
printf(",");
printf("%s ", opt_to_enum_str(cmd->rules[ru].check_opts[ruo]));
}
printf(" };\n");
printf("commands[%d].rules[%d].check_opts = _command%d_rule%d_check_opts;\n", i, ru, i, ru);
} else {
printf("commands[%d].rules[%d].check_opts = NULL;\n", i, ru);
}
printf("commands[%d].rules[%d].lvt_bits = %s;\n", i, ru,
cmd->rules[ru].lvt_bits ? lvt_bits_to_str(cmd->rules[ru].lvt_bits) : "0");
@@ -2535,9 +2581,6 @@ void print_command_struct(int only_usage)
printf("commands[%d].rules[%d].rule = %s;\n", i, ru,
rule_to_define_str(cmd->rules[ru].rule));
printf("commands[%d].rules[%d].check_opt = %s;\n", i, ru,
cmd->rules[ru].check_opt ? opt_to_enum_str(cmd->rules[ru].check_opt) : "0");
printf("commands[%d].rules[%d].check_lvt_bits = %s;\n", i, ru,
cmd->rules[ru].check_lvt_bits ? lvt_bits_to_str(cmd->rules[ru].check_lvt_bits) : "0");

View File

@@ -1611,7 +1611,7 @@ static int _udevcomplete_all(CMD_ARGS)
}
if (!_switches[YES_ARG]) {
log_warn("This operation will destroy all semaphores %s%.0d%swith keys "
log_warn("WARNING: This operation will destroy all semaphores %s%.0d%swith keys "
"that have a prefix %" PRIu16 " (0x%" PRIx16 ").",
age ? "older than " : "", age, age ? " minutes " : "",
DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
@@ -2707,6 +2707,9 @@ static int _add_dep(CMD_ARGS)
/*
* Create and walk dependency tree
*
* An incomplete _dtree may still be used by the caller,
* but the error must be reported.
*/
static int _build_whole_deptree(const struct command *cmd)
{
@@ -2724,12 +2727,14 @@ static int _build_whole_deptree(const struct command *cmd)
static int _display_tree(CMD_ARGS)
{
if (!_build_whole_deptree(cmd))
return_0;
int r;
_display_tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
r = _build_whole_deptree(cmd);
return 1;
if (_dtree)
_display_tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
return r;
}
/*
@@ -4471,9 +4476,14 @@ static int _report_init(const struct command *cmd, const char *subcommand)
selection, NULL, NULL)))
goto_out;
if ((_report_type & DR_TREE) && cmd && !_build_whole_deptree(cmd)) {
err("Internal device dependency tree creation failed.");
goto out;
r = 1;
if ((_report_type & DR_TREE) && cmd) {
r = _build_whole_deptree(cmd);
if (!_dtree) {
err("Internal device dependency tree creation failed.");
goto out;
}
}
if (!_switches[INTERVAL_ARG])
@@ -4484,8 +4494,6 @@ static int _report_init(const struct command *cmd, const char *subcommand)
if (field_prefixes)
dm_report_set_output_field_name_prefix(_report, "dm_");
r = 1;
out:
if (len)
dm_free(options);
@@ -5270,6 +5278,8 @@ static int _stats_delete(CMD_ARGS)
name = argv[0];
}
if (_switches[PROGRAM_ID_ARG])
program_id = _string_args[PROGRAM_ID_ARG];
if (_switches[ALL_PROGRAMS_ARG])
program_id = DM_STATS_ALL_PROGRAMS;
@@ -5313,7 +5323,7 @@ static int _stats_delete(CMD_ARGS)
log_error("Could not delete statistics region");
goto out;
}
log_info("Deleted statistics region " FMTu64 ".\n", region_id);
log_info("Deleted statistics region " FMTu64 ".", region_id);
}
r = 1;
@@ -6782,8 +6792,15 @@ unknown:
argc--, argv++;
}
if (_switches[COLS_ARG] && !_report_init(cmd, subcommand))
goto_out;
/* Default to success */
ret = 0;
if (_switches[COLS_ARG]) {
if (!_report_init(cmd, subcommand))
ret = 1;
if (ret || !_report)
goto_out;
}
if (_switches[COUNT_ARG])
_count = ((uint32_t)_int_args[COUNT_ARG]) ? : UINT32_MAX;
@@ -6795,14 +6812,17 @@ unknown:
&_disp_units);
if (!_disp_factor) {
log_error("Invalid --units argument.");
ret = 1;
goto out;
}
}
/* Start interval timer. */
if (_count > 1)
if (!_start_timer())
if (!_start_timer()) {
ret = 1;
goto_out;
}
doit:
multiple_devices = (cmd->repeatable_cmd && argc != 1 &&
@@ -6819,18 +6839,19 @@ doit:
if (_count > 1 && r) {
printf("\n");
/* wait for --interval and update timestamps */
if (!_do_report_wait())
if (!_do_report_wait()) {
ret = 1;
goto_out;
}
}
}
if (!r)
if (!r) {
ret = 1;
goto_out;
}
} while (--_count);
/* Success */
ret = 0;
out:
if (_report)
dm_report_free(_report);
@@ -6843,5 +6864,5 @@ out:
if (_initial_timestamp)
dm_timestamp_destroy(_initial_timestamp);
return ret;
return (_switches[HELP_ARG] || _switches[VERSION_ARG]) ? 0 : ret;
}

View File

@@ -36,8 +36,16 @@ lvp(is_mirror_log_LVP, "lv_is_mirror_log", NULL)
lvp(is_raid_image_LVP, "lv_is_raid_image", NULL)
lvp(is_raid_metadata_LVP, "lv_is_raid_metadata", NULL)
/*
* is_thick_origin should be used instead of is_origin
* is_thick_snapshot is generally used as LV_snapshot from lv_types.h
*/
lvp(is_origin_LVP, "lv_is_origin", NULL)
lvp(is_thick_origin_LVP, "lv_is_thick_origin", NULL)
lvp(is_thick_snapshot_LVP, "lv_is_thick_snapshot", NULL)
lvp(is_thin_origin_LVP, "lv_is_thin_origin", NULL)
lvp(is_thin_snapshot_LVP, "lv_is_thin_snapshot", NULL)
lvp(is_cache_origin_LVP, "lv_is_cache_origin", NULL)
lvp(is_merging_cow_LVP, "lv_is_merging_cow", NULL)
lvp(is_cow_covering_origin_LVP, "lv_is_cow_covering_origin", NULL)

View File

@@ -15,7 +15,7 @@
lvt(LVT_NONE, "", NULL)
lvt(linear_LVT, "linear", NULL)
lvt(striped_LVT, "striped", NULL)
lvt(snapshot_LVT, "snapshot", NULL)
lvt(snapshot_LVT, "snapshot", NULL) /* lv_is_cow, lv_is_thick_snapshot */
lvt(thin_LVT, "thin", NULL)
lvt(thinpool_LVT, "thinpool", NULL)
lvt(cache_LVT, "cache", NULL)
@@ -28,5 +28,7 @@ lvt(raid4_LVT, "raid4", NULL)
lvt(raid5_LVT, "raid5", NULL)
lvt(raid6_LVT, "raid6", NULL)
lvt(raid10_LVT, "raid10", NULL)
lvt(error_LVT, "error", NULL)
lvt(zero_LVT, "zero", NULL)
lvt(LVT_COUNT, "", NULL)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1054,6 +1054,12 @@ static int _lvcreate_params(struct cmd_context *cmd,
return 0;
}
if (segtype_is_raid4(lp->segtype) &&
!(lp->target_attr & RAID_FEATURE_RAID4)) {
log_error("RAID module does not support RAID4.");
return 0;
}
if (segtype_is_raid10(lp->segtype) && !(lp->target_attr & RAID_FEATURE_RAID10)) {
log_error("RAID module does not support RAID10.");
return 0;

View File

@@ -58,5 +58,5 @@ int lvdisplay(struct cmd_context *cmd, int argc, char **argv)
return EINVALID_CMD_LINE;
}
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, &_lvdisplay_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, NULL, &_lvdisplay_single);
}

View File

@@ -113,36 +113,52 @@ static struct cmdline_context _cmdline;
*/
struct command_function command_functions[COMMAND_ID_COUNT] = {
{ lvmconfig_general_CMD, lvmconfig },
{ lvchange_properties_CMD, lvchange_properties_cmd },
{ lvchange_resync_CMD, lvchange_resync_cmd },
{ lvchange_syncaction_CMD, lvchange_syncaction_cmd },
{ lvchange_rebuild_CMD, lvchange_rebuild_cmd },
{ lvchange_activate_CMD, lvchange_activate_cmd },
{ lvchange_refresh_CMD, lvchange_refresh_cmd },
{ lvchange_monitor_CMD, lvchange_monitor_poll_cmd },
{ lvchange_poll_CMD, lvchange_monitor_poll_cmd },
{ lvchange_persistent_CMD, lvchange_persistent_cmd },
/* lvconvert utilities related to repair. */
{ lvconvert_repair_pvs_or_thinpool_CMD, lvconvert_repair_pvs_or_thinpool_cmd },
{ lvconvert_replace_pv_CMD, lvconvert_replace_pv_cmd },
/* lvconvert utilities related to snapshots. */
{ lvconvert_split_cow_snapshot_CMD, lvconvert_split_snapshot_cmd },
{ lvconvert_merge_snapshot_CMD, lvconvert_merge_snapshot_cmd },
{ lvconvert_combine_split_snapshot_CMD, lvconvert_combine_split_snapshot_cmd },
/* lvconvert utility to trigger polling on an LV. */
{ lvconvert_start_poll_CMD, lvconvert_start_poll_cmd },
/* lvconvert utilities for creating/maintaining thin and cache objects. */
{ lvconvert_to_thinpool_CMD, lvconvert_to_pool_cmd },
{ lvconvert_to_thinpool_noarg_CMD, lvconvert_to_pool_noarg_cmd },
{ lvconvert_to_cachepool_CMD, lvconvert_to_pool_cmd },
{ lvconvert_to_cachepool_noarg_CMD, lvconvert_to_pool_noarg_cmd },
{ lvconvert_to_thin_with_external_CMD, lvconvert_to_thin_with_external_cmd },
{ lvconvert_to_cache_vol_CMD, lvconvert_to_cache_vol_cmd },
};
#if 0
/* all raid-related type conversions */
{ lvconvert_raid_types_CMD, lvconvert_raid_types_fn },
/* raid-related utilities (move into lvconvert_raid_types?) */
{ lvconvert_split_mirror_images_CMD, lvconvert_split_mirror_images_fn },
{ lvconvert_change_mirrorlog_CMD, lvconvert_change_mirrorlog_fn },
/* utilities for creating/maintaining thin and cache objects. */
{ lvconvert_to_thin_with_external_CMD, lvconvert_to_thin_with_external_fn },
{ lvconvert_to_cache_vol_CMD, lvconvert_to_cache_vol_fn },
{ lvconvert_to_thinpool_CMD, lvconvert_to_thinpool_fn },
{ lvconvert_to_cachepool_CMD, lvconvert_to_cachepool_fn },
{ lvconvert_split_and_keep_cachepool_CMD, lvconvert_split_and_keep_cachepool_fn },
{ lvconvert_split_and_delete_cachepool_CMD, lvconvert_split_and_delete_cachepool_fn },
{ lvconvert_swap_pool_metadata_CMD, lvconvert_swap_pool_metadata_fn },
/* utilities related to snapshots and repair. */
/* directed to one of the other merges (snap,thin,mirror) when all are implemented */
{ lvconvert_merge_CMD, lvconvert_merge_fn },
{ lvconvert_combine_split_snapshot_CMD, lvconvert_combine_split_snapshot_fn },
{ lvconvert_repair_pvs_or_thinpool_CMD, lvconvert_repair_pvs_or_thinpool_fn },
{ lvconvert_replace_pv_CMD, lvconvert_replace_pv_fn },
{ lvconvert_split_cow_snapshot_CMD, lvconvert_split_cow_snapshot_fn },
{ lvconvert_poll_start_CMD, lvconvert_poll_start_fn },
#endif
/* Command line args */
@@ -1112,6 +1128,18 @@ struct lv_types *get_lv_type(int lvt_enum)
return &_lv_types[lvt_enum];
}
struct command *get_command(int cmd_enum)
{
int i;
for (i = 0; i < COMMAND_COUNT; i++) {
if (commands[i].command_line_enum == cmd_enum)
return &commands[i];
}
return NULL;
}
/*
* Also see merge_synonym(). The command definitions
* are written using just one variation of the option
@@ -1446,6 +1474,7 @@ static void _print_description(int ci)
static struct command *_find_command(struct cmd_context *cmd, const char *path, int *argc, char **argv)
{
const char *name;
char buf[64];
int match_required, match_ro, match_rp, match_type, match_unused, mismatch_required;
int best_i = 0, best_required = 0, best_type = 0, best_unused = 0;
int close_i = 0, close_ro = 0, close_type;
@@ -1453,11 +1482,11 @@ static struct command *_find_command(struct cmd_context *cmd, const char *path,
int temp_unused_count;
int best_unused_options[MAX_UNUSED_COUNT] = { 0 };
int best_unused_count = 0;
int opts_match_count, opts_unmatch_count;
int ro, rp;
int i, j;
int opt_enum, opt_i;
int accepted, count;
int check_is_set;
name = last_path_component(path);
@@ -1690,35 +1719,38 @@ out:
rule = &commands[best_i].rules[i];
/*
* The rule wants to check an option (check_opt), and the
* qualification for applying the rule can't include knowing a
* specific LV type (lvt_bits) or LV property (lvp_bits).
* The rule wants to validate options (check_opts). That can be
* done here if the only qualification for the validation is
* other options (and not specific LV type or LV property which
* are not known here.)
*/
if (rule->check_opt && !rule->lvt_bits && !rule->lvp_bits) {
if (rule->check_opts_count && !rule->lvt_bits && !rule->lvp_bits) {
/*
* When no opt is specified for applying the rule, then
* the rule is always applied, otherwise the rule is
* applied when the specific option is set.
*/
if (rule->opt && !arg_is_set(cmd, rule->opt))
if (rule->opts_count &&
!opt_in_list_is_set(cmd, rule->opts, rule->opts_count, NULL, NULL))
continue;
check_is_set = arg_is_set(cmd, rule->check_opt);
opt_in_list_is_set(cmd, rule->check_opts, rule->check_opts_count,
&opts_match_count, &opts_unmatch_count);
if (check_is_set && (rule->rule == RULE_INVALID)) {
log_error("Invalid option combination for command (%s %d): %s and %s",
commands[best_i].command_line_id, best_i,
arg_long_option_name(rule->opt),
arg_long_option_name(rule->check_opt));
if (opts_match_count && (rule->rule == RULE_INVALID)) {
memset(buf, 0, sizeof(buf));
opt_array_to_str(cmd, rule->check_opts, rule->check_opts_count, buf, sizeof(buf));
log_error("Invalid options for command (%s %d): %s",
commands[best_i].command_line_id, best_i, buf);
return NULL;
}
if (!check_is_set && (rule->rule == RULE_REQUIRE)) {
log_error("Invalid option usage for command (%s %d): %s requires %s",
commands[best_i].command_line_id, best_i,
arg_long_option_name(rule->opt),
arg_long_option_name(rule->check_opt));
if (opts_unmatch_count && (rule->rule == RULE_REQUIRE)) {
memset(buf, 0, sizeof(buf));
opt_array_to_str(cmd, rule->check_opts, rule->check_opts_count, buf, sizeof(buf));
log_error("Required options for command (%s %d): %s",
commands[best_i].command_line_id, best_i, buf);
return NULL;
}
}

View File

@@ -27,5 +27,5 @@ int lvremove(struct cmd_context *cmd, int argc, char **argv)
cmd->include_historical_lvs = 1;
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, NULL,
&lvremove_single);
NULL, &lvremove_single);
}

View File

@@ -119,5 +119,5 @@ int lvscan(struct cmd_context *cmd, int argc, char **argv)
*/
}
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, &lvscan_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, NULL, &lvscan_single);
}

View File

@@ -194,6 +194,9 @@ static int _pvscan_autoactivate_single(struct cmd_context *cmd, const char *vg_n
if (vg_is_clustered(vg))
return ECMD_PROCESSED;
if (vg_is_exported(vg))
return ECMD_PROCESSED;
if (is_lockd_type(vg->lock_type))
return ECMD_PROCESSED;

View File

@@ -517,14 +517,14 @@ static int _report_all_in_vg(struct cmd_context *cmd, struct processing_handle *
r = _vgs_single(cmd, vg->name, vg, handle);
break;
case LVS:
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle,
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle, NULL,
do_lv_info && !do_lv_seg_status ? &_lvs_with_info_single :
!do_lv_info && do_lv_seg_status ? &_lvs_with_status_single :
do_lv_info && do_lv_seg_status ? &_lvs_with_info_and_status_single :
&_lvs_single);
break;
case SEGS:
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle,
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle, NULL,
do_lv_info && !do_lv_seg_status ? &_lvsegs_with_info_single :
!do_lv_info && do_lv_seg_status ? &_lvsegs_with_status_single :
do_lv_info && do_lv_seg_status ? &_lvsegs_with_info_and_status_single :
@@ -1099,7 +1099,7 @@ static int _do_report(struct cmd_context *cmd, struct processing_handle *handle,
if (args->full_report_vg)
r = _report_all_in_vg(cmd, handle, args->full_report_vg, LVS, lv_info_needed, lv_segment_status_needed);
else
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle,
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle, NULL,
lv_info_needed && !lv_segment_status_needed ? &_lvs_with_info_single :
!lv_info_needed && lv_segment_status_needed ? &_lvs_with_status_single :
lv_info_needed && lv_segment_status_needed ? &_lvs_with_info_and_status_single :
@@ -1133,7 +1133,7 @@ static int _do_report(struct cmd_context *cmd, struct processing_handle *handle,
if (args->full_report_vg)
r = _report_all_in_vg(cmd, handle, args->full_report_vg, SEGS, lv_info_needed, lv_segment_status_needed);
else
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle,
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle, NULL,
lv_info_needed && !lv_segment_status_needed ? &_lvsegs_with_info_single :
!lv_info_needed && lv_segment_status_needed ? &_lvsegs_with_status_single :
lv_info_needed && lv_segment_status_needed ? &_lvsegs_with_info_and_status_single :

View File

@@ -2326,6 +2326,45 @@ static struct lv_segment _historical_lv_segment = {
.origin_list = DM_LIST_HEAD_INIT(_historical_lv_segment.origin_list),
};
int opt_in_list_is_set(struct cmd_context *cmd, int *opts, int count,
int *match_count, int *unmatch_count)
{
int match = 0;
int unmatch = 0;
int i;
for (i = 0; i < count; i++) {
if (arg_is_set(cmd, opts[i]))
match++;
else
unmatch++;
}
if (match_count)
*match_count = match;
if (unmatch_count)
*unmatch_count = unmatch;
return match ? 1 : 0;
}
void opt_array_to_str(struct cmd_context *cmd, int *opts, int count,
char *buf, int len)
{
int pos = 0;
int ret;
int i;
for (i = 0; i < count; i++) {
ret = snprintf(buf + pos, len - pos, "%s ", arg_long_option_name(opts[i]));
if (ret >= len - pos)
break;
pos += ret;
}
buf[len - 1] = '\0';
}
static void lvp_bits_to_str(uint64_t bits, char *buf, int len)
{
struct lv_props *prop;
@@ -2347,20 +2386,25 @@ static void lvp_bits_to_str(uint64_t bits, char *buf, int len)
buf[len - 1] = '\0';
}
static const char *lvt_bits_to_str(uint64_t bits)
static void lvt_bits_to_str(uint64_t bits, char *buf, int len)
{
struct lv_types *type;
int lvt_enum;
int pos = 0;
int ret;
for (lvt_enum = 0; lvt_enum < LVT_COUNT; lvt_enum++) {
if (!(type = get_lv_type(lvt_enum)))
continue;
if (lvt_bit_is_set(bits, lvt_enum))
return type->name;
if (lvt_bit_is_set(bits, lvt_enum)) {
ret = snprintf(buf + pos, len - pos, "%s ", type->name);
if (ret >= len - pos)
break;
pos += ret;
}
}
return "unknown";
buf[len - 1] = '\0';
}
/*
@@ -2415,10 +2459,16 @@ static int _lv_is_prop(struct cmd_context *cmd, struct logical_volume *lv, int l
return lv_is_raid_image(lv);
case is_raid_metadata_LVP:
return lv_is_raid_metadata(lv);
case is_origin_LVP:
case is_origin_LVP: /* use lv_is_thick_origin */
return lv_is_origin(lv);
case is_thick_origin_LVP:
return lv_is_thick_origin(lv);
case is_thick_snapshot_LVP:
return lv_is_thick_snapshot(lv);
case is_thin_origin_LVP:
return lv_is_thin_origin(lv, NULL);
case is_thin_snapshot_LVP:
return lv_is_thin_snapshot(lv);
case is_cache_origin_LVP:
return lv_is_cache_origin(lv);
case is_merging_cow_LVP:
@@ -2479,6 +2529,10 @@ static int _lv_is_type(struct cmd_context *cmd, struct logical_volume *lv, int l
#endif
case raid10_LVT:
return seg_is_raid10(seg);
case error_LVT:
return !strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR);
case zero_LVT:
return !strcmp(seg->segtype->name, SEG_TYPE_NAME_ZERO);
default:
log_error(INTERNAL_ERROR "unknown lv type value lvt_enum %d", lvt_enum);
}
@@ -2523,7 +2577,11 @@ static int _get_lvt_enum(struct logical_volume *lv)
if (seg_is_raid10(seg))
return raid10_LVT;
log_error(INTERNAL_ERROR "unknown lv type for %s", display_lvname(lv));
if (!strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR))
return error_LVT;
if (!strcmp(seg->segtype->name, SEG_TYPE_NAME_ZERO))
return zero_LVT;
return 0;
}
@@ -2619,35 +2677,29 @@ static int _lv_props_match(struct cmd_context *cmd, struct logical_volume *lv, u
return !found_a_mismatch;
}
/*
* If the command definition specifies one required positional
* LV (possibly repeatable), and specifies accepted LV types,
* then verify that the LV being processed matches one of those
* types.
*
* process_each_lv() can only be used for commands that have
* one positional LV arg (optionally repeating, where each is
* processed independently.) It cannot work for commands that
* have different required LVs in designated positions, like
* 'lvrename LV1 LV2', where each LV is not processed
* independently. That means that this LV type check only
* needs to check the lv_type of the first positional arg.
*/
static int _check_lv_types(struct cmd_context *cmd, struct logical_volume *lv)
static int _check_lv_types(struct cmd_context *cmd, struct logical_volume *lv, int pos)
{
int ret = 1;
if ((cmd->command->rp_count == 1) &&
val_bit_is_set(cmd->command->required_pos_args[0].def.val_bits, lv_VAL) &&
cmd->command->required_pos_args[0].def.lvt_bits) {
ret = _lv_types_match(cmd, lv, cmd->command->required_pos_args[0].def.lvt_bits, NULL, NULL);
if (!ret) {
int lvt_enum = _get_lvt_enum(lv);
struct lv_types *type = get_lv_type(lvt_enum);
log_warn("Operation on LV %s which has invalid type %s.",
display_lvname(lv), type ? type->name : "unknown");
}
if (!pos)
return 1;
if (!cmd->command->required_pos_args[pos-1].def.lvt_bits)
return 1;
if (!val_bit_is_set(cmd->command->required_pos_args[pos-1].def.val_bits, lv_VAL)) {
log_error(INTERNAL_ERROR "Command (%s %d) arg position %d does not permit an LV (%llx)",
cmd->command->command_line_id, cmd->command->command_line_enum,
pos, (unsigned long long)cmd->command->required_pos_args[pos-1].def.val_bits);
return 0;
}
ret = _lv_types_match(cmd, lv, cmd->command->required_pos_args[pos-1].def.lvt_bits, NULL, NULL);
if (!ret) {
int lvt_enum = _get_lvt_enum(lv);
struct lv_types *type = get_lv_type(lvt_enum);
log_warn("Operation on LV %s which has invalid type %s.",
display_lvname(lv), type ? type->name : "unknown");
}
return ret;
@@ -2662,8 +2714,8 @@ static int _check_lv_rules(struct cmd_context *cmd, struct logical_volume *lv)
struct lv_types *lvtype = NULL;
uint64_t lv_props_match_bits, lv_props_unmatch_bits;
uint64_t lv_types_match_bits, lv_types_unmatch_bits;
int opts_match_count, opts_unmatch_count;
int lvt_enum;
int opt_is_set;
int ret = 1;
int i;
@@ -2671,22 +2723,52 @@ static int _check_lv_rules(struct cmd_context *cmd, struct logical_volume *lv)
if (lvt_enum)
lvtype = get_lv_type(lvt_enum);
for (i = 0; i < cmd->command->rule_count; i++) {
rule = &cmd->command->rules[i];
/*
* For the rule to apply, any option, LV type or LV property
* that is specified before the not|and rule needs to match the
* command/LV. If multiple LV types are specifed, one needs to
* match for the rule to apply. If multiple LV properties are
* specified, all need to match for the rule to apply.
* RULE: <conditions> INVALID|REQUIRE <checks>
*
* If all qualifications are zero (!opt && !lvt_bits && !lvp_bits),
* then there are no qualifications and the rule always applies.
* If all the conditions apply to the command+LV, then
* the checks are performed. If all conditions are zero
* (!opts_count, !lvt_bits, !lvp_bits), then the check
* is always performed.
*
* Conditions:
*
* 1. options (opts): if any of the specified options are set,
* then the checks may apply.
*
* 2. LV types (lvt_bits): if any of the specified LV types
* match the LV, then the checks may apply.
*
* 3. LV properties (lvp_bits): if all of the specified
* LV properties match the LV, then the checks may apply.
*
* If conditions 1, 2, 3 all pass, then the checks apply.
*
* Checks:
*
* 1. options (check_opts):
* INVALID: if any of the specified options are set,
* then the command fails.
* REQUIRE: if any of the specified options are not set,
* then the command fails.
*
* 2. LV types (check_lvt_bits):
* INVALID: if any of the specified LV types match the LV,
* then the command fails.
* REQUIRE: if none of the specified LV types match the LV,
* then the command fails.
*
* 3. LV properties (check_lvp_bits):
* INVALID: if any of the specified LV properties match
* the LV, then the command fails.
* REQUIRE: if any of the specified LV properties do not match
* the LV, then the command fails.
*/
if (rule->opt && !arg_is_set(cmd, rule->opt))
if (rule->opts_count && !opt_in_list_is_set(cmd, rule->opts, rule->opts_count, NULL, NULL))
continue;
/* If LV matches one type in lvt_bits, this returns 1. */
@@ -2698,53 +2780,62 @@ static int _check_lv_rules(struct cmd_context *cmd, struct logical_volume *lv)
continue;
/*
* Check the option, LV types, LV properties specified after
* the not|and rule.
* Check the options, LV types, LV properties.
*/
if (rule->check_opt)
opt_is_set = arg_is_set(cmd, rule->check_opt);
if (rule->check_opts)
opt_in_list_is_set(cmd, rule->check_opts, rule->check_opts_count,
&opts_match_count, &opts_unmatch_count);
if (rule->check_lvt_bits)
_lv_types_match(cmd, lv, rule->check_lvt_bits, &lv_types_match_bits, &lv_types_unmatch_bits);
_lv_types_match(cmd, lv, rule->check_lvt_bits,
&lv_types_match_bits, &lv_types_unmatch_bits);
if (rule->check_lvp_bits)
_lv_props_match(cmd, lv, rule->check_lvp_bits, &lv_props_match_bits, &lv_props_unmatch_bits);
_lv_props_match(cmd, lv, rule->check_lvp_bits,
&lv_props_match_bits, &lv_props_unmatch_bits);
/*
* Evaluate if the check results pass based on the rule.
* The options are checked again here because the previous
* option validation (during command matching) does not cover
* cases where the option is combined with qualifying LV types
* cases where the option is combined with conditions of LV types
* or properties.
*/
if (rule->check_opt && (rule->rule == RULE_INVALID) && opt_is_set) {
log_warn("Command option %s invalid with option %s.",
arg_long_option_name(rule->opt), arg_long_option_name(rule->check_opt));
/* Fail if any invalid options are set. */
if (rule->check_opts && (rule->rule == RULE_INVALID) && opts_match_count) {
memset(buf, 0, sizeof(buf));
opt_array_to_str(cmd, rule->check_opts, rule->check_opts_count, buf, sizeof(buf));
log_warn("An invalid option is set: %s", buf);
ret = 0;
}
if (rule->check_opt && (rule->rule == RULE_REQUIRE) && !opt_is_set) {
log_warn("Command option %s requires option %s.",
arg_long_option_name(rule->opt), arg_long_option_name(rule->check_opt));
/* Fail if any required options are not set. */
if (rule->check_opts && (rule->rule == RULE_REQUIRE) && opts_unmatch_count) {
memset(buf, 0, sizeof(buf));
opt_array_to_str(cmd, rule->check_opts, rule->check_opts_count, buf, sizeof(buf));
log_warn("A required option is not set: %s", buf);
ret = 0;
}
/* Fail if the LV matches any of the invalid LV types. */
if (rule->check_lvt_bits && (rule->rule == RULE_INVALID) && lv_types_match_bits) {
log_warn("Command on LV %s with invalid type: %s.",
log_warn("Command on LV %s with invalid type: %s",
display_lvname(lv), lvtype ? lvtype->name : "unknown");
ret = 0;
}
/* Fail if the LV does not match any of the required LV types. */
if (rule->check_lvt_bits && (rule->rule == RULE_REQUIRE) && lv_types_unmatch_bits) {
log_warn("Command on LV %s type %s requires different type: %s.",
display_lvname(lv), lvtype ? lvtype->name : "unknown",
lvt_bits_to_str(rule->check_lvt_bits));
if (rule->check_lvt_bits && (rule->rule == RULE_REQUIRE) && !lv_types_match_bits) {
memset(buf, 0, sizeof(buf));
lvt_bits_to_str(rule->check_lvt_bits, buf, sizeof(buf));
log_warn("Command on LV %s with type %s does not match required type: %s",
display_lvname(lv), lvtype ? lvtype->name : "unknown", buf);
ret = 0;
}
@@ -2753,7 +2844,7 @@ static int _check_lv_rules(struct cmd_context *cmd, struct logical_volume *lv)
if (rule->check_lvp_bits && (rule->rule == RULE_INVALID) && lv_props_match_bits) {
memset(buf, 0, sizeof(buf));
lvp_bits_to_str(lv_props_match_bits, buf, sizeof(buf));
log_warn("Command on LV %s with invalid properties: %s.",
log_warn("Command on LV %s with invalid properties: %s",
display_lvname(lv), buf);
ret = 0;
}
@@ -2763,7 +2854,7 @@ static int _check_lv_rules(struct cmd_context *cmd, struct logical_volume *lv)
if (rule->check_lvp_bits && (rule->rule == RULE_REQUIRE) && lv_props_unmatch_bits) {
memset(buf, 0, sizeof(buf));
lvp_bits_to_str(lv_props_unmatch_bits, buf, sizeof(buf));
log_warn("Command on LV %s requires properties: %s.",
log_warn("Command on LV %s requires properties: %s",
display_lvname(lv), buf);
ret = 0;
}
@@ -2772,10 +2863,64 @@ static int _check_lv_rules(struct cmd_context *cmd, struct logical_volume *lv)
return ret;
}
/*
* Return which arg position the given LV is at,
* where 1 represents the first position arg.
* When the first position arg is repeatable,
* return 1 for all.
*
* Return 0 when the command has no required
* position args. (optional position args are
* not considered.)
*/
static int _find_lv_arg_position(struct cmd_context *cmd, struct logical_volume *lv)
{
const char *sep, *lvname;
int i;
if (cmd->command->rp_count == 0)
return 0;
if (cmd->command->rp_count == 1)
return 1;
for (i = 0; i < cmd->position_argc; i++) {
if (i == cmd->command->rp_count)
break;
if (!val_bit_is_set(cmd->command->required_pos_args[i].def.val_bits, lv_VAL))
continue;
if ((sep = strstr(cmd->position_argv[i], "/")))
lvname = sep + 1;
else
lvname = cmd->position_argv[i];
if (!strcmp(lvname, lv->name))
return i + 1;
}
/*
* If the last position arg is an LV and this
* arg is beyond that position, then the last
* LV position arg is repeatable, so return
* that position.
*/
if (i == cmd->command->rp_count) {
int last_pos = cmd->command->rp_count;
if (val_bit_is_set(cmd->command->required_pos_args[last_pos-1].def.val_bits, lv_VAL))
return last_pos;
}
return 0;
}
int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
struct dm_list *arg_lvnames, const struct dm_list *tags_in,
int stop_on_error,
struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv)
{
log_report_t saved_log_report_state = log_get_report_state();
@@ -2785,10 +2930,12 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
int ret = 0;
int whole_selected = 0;
int handle_supplied = handle != NULL;
int lv_is_named_arg;
unsigned process_lv;
unsigned process_all = 0;
unsigned tags_supplied = 0;
unsigned lvargs_supplied = 0;
int lv_arg_pos;
struct lv_list *lvl;
struct dm_str_list *sl;
struct dm_list final_lvs;
@@ -2946,42 +3093,40 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
if (lv_is_removed(lvl->lv))
continue;
lv_is_named_arg = str_list_match_item(&found_arg_lvnames, lvl->lv->name);
lv_arg_pos = _find_lv_arg_position(cmd, lvl->lv);
/*
* The command definition may include restrictions on the
* types and properties of LVs that can be processed.
*/
if (!_check_lv_types(cmd, lvl->lv)) {
if (!_check_lv_types(cmd, lvl->lv, lv_arg_pos)) {
/* FIXME: include this result in report log? */
/* FIXME: avoid duplicating message for each level */
if (str_list_match_item(&found_arg_lvnames, lvl->lv->name)) {
if (lv_is_named_arg) {
log_error("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
ret_max = ECMD_FAILED;
} else {
log_warn("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
}
continue;
}
if (!_check_lv_rules(cmd, lvl->lv)) {
/* FIXME: include this result in report log? */
/* FIXME: avoid duplicating message for each level */
if (str_list_match_item(&found_arg_lvnames, lvl->lv->name)) {
if (lv_is_named_arg) {
log_error("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
ret_max = ECMD_FAILED;
} else {
log_warn("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
}
}
continue;
}
if (check_single_lv && !check_single_lv(cmd, lvl->lv, handle, lv_is_named_arg)) {
if (lv_is_named_arg)
ret_max = ECMD_FAILED;
continue;
}
@@ -3221,6 +3366,7 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
struct dm_list *arg_lvnames,
struct dm_list *arg_tags,
struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv)
{
log_report_t saved_log_report_state = log_get_report_state();
@@ -3313,7 +3459,7 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
goto endvg;
ret = process_each_lv_in_vg(cmd, vg, &lvnames, tags_arg, 0,
handle, process_single_lv);
handle, check_single_lv, process_single_lv);
if (ret != ECMD_PROCESSED)
stack;
report_log_ret_code(ret);
@@ -3344,6 +3490,7 @@ int process_each_lv(struct cmd_context *cmd,
const char *one_vgname, const char *one_lvname,
uint32_t read_flags,
struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv)
{
log_report_t saved_log_report_state = log_get_report_state();
@@ -3459,7 +3606,7 @@ int process_each_lv(struct cmd_context *cmd,
_choose_vgs_to_process(cmd, &arg_vgnames, &vgnameids_on_system, &vgnameids_to_process);
ret = _process_lv_vgnameid_list(cmd, read_flags, &vgnameids_to_process, &arg_vgnames, &arg_lvnames,
&arg_tags, handle, process_single_lv);
&arg_tags, handle, check_single_lv, process_single_lv);
if (ret > ret_max)
ret_max = ret;

View File

@@ -98,6 +98,18 @@ typedef int (*process_single_pvseg_fn_t) (struct cmd_context * cmd,
struct pv_segment * pvseg,
struct processing_handle *handle);
/*
* Called prior to process_single_lv() to decide if the LV should be
* processed. If this returns 0, the LV is not processed.
*
* This can evaluate the combination of command definition and
* the LV object to decide if the combination is allowed.
*/
typedef int (*check_single_lv_fn_t) (struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg);
int process_each_vg(struct cmd_context *cmd,
int argc, char **argv,
const char *one_vgname,
@@ -125,6 +137,7 @@ int process_each_segment_in_pv(struct cmd_context *cmd,
int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
const char *one_vgname, const char *one_lvname,
uint32_t flags, struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv);
@@ -141,6 +154,7 @@ int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
struct dm_list *arg_lvnames, const struct dm_list *tagsl,
int stop_on_error, struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv);
struct processing_handle *init_processing_handle(struct cmd_context *cmd, struct processing_handle *parent_handle);
@@ -159,6 +173,12 @@ const char *extract_vgname(struct cmd_context *cmd, const char *lv_name);
const char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name,
unsigned *dev_dir_found);
int opt_in_list_is_set(struct cmd_context *cmd, int *opts, int count,
int *match_count, int *unmatch_count);
void opt_array_to_str(struct cmd_context *cmd, int *opts, int count,
char *buf, int len);
int pvcreate_params_from_args(struct cmd_context *cmd, struct pvcreate_params *pp);
int pvcreate_each_device(struct cmd_context *cmd, struct processing_handle *handle, struct pvcreate_params *pp);

View File

@@ -240,5 +240,29 @@ int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg
struct lv_props *get_lv_prop(int lvp_enum);
struct lv_types *get_lv_type(int lvt_enum);
struct command *get_command(int cmd_enum);
int lvchange_properties_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_activate_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_refresh_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_resync_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_syncaction_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_rebuild_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_monitor_poll_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_persistent_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_repair_pvs_or_thinpool_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_replace_pv_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_merge_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_split_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_combine_split_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_start_poll_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_pool_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_pool_noarg_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_cache_vol_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_thin_with_external_cmd(struct cmd_context *cmd, int argc, char **argv);
#endif

View File

@@ -38,7 +38,7 @@ static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name,
vgdisplay_extents(vg);
process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, NULL,
(process_single_lv_fn_t)lvdisplay_full);
NULL, (process_single_lv_fn_t)lvdisplay_full);
log_print("--- Physical volumes ---");
process_each_pv_in_vg(cmd, vg, NULL,

View File

@@ -33,5 +33,5 @@ int vgmknodes(struct cmd_context *cmd, int argc, char **argv)
if (!lv_mknodes(cmd, NULL))
return_ECMD_FAILED;
return process_each_lv(cmd, argc, argv, NULL, NULL, LCK_VG_READ, NULL, &_vgmknodes_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, LCK_VG_READ, NULL, NULL, &_vgmknodes_single);
}

View File

@@ -62,7 +62,7 @@ static int vgremove_single(struct cmd_context *cmd, const char *vg_name,
}
if ((ret = process_each_lv_in_vg(cmd, vg, NULL, NULL, 1, &void_handle,
(process_single_lv_fn_t)lvremove_single)) != ECMD_PROCESSED) {
NULL, (process_single_lv_fn_t)lvremove_single)) != ECMD_PROCESSED) {
stack;
return ret;
}