mirror of
git://sourceware.org/git/lvm2.git
synced 2025-09-27 05:44:18 +03:00
Compare commits
71 Commits
dev-dct-cm
...
dev-dct-cm
Author | SHA1 | Date | |
---|---|---|---|
|
fdf3d0501d | ||
|
072ef008ea | ||
|
5b5e37791c | ||
|
e916797ec2 | ||
|
51205a1bc0 | ||
|
a7840b31a1 | ||
|
4f60bcd9f2 | ||
|
6c119560ab | ||
|
2cf8f93fe6 | ||
|
d658ddfc70 | ||
|
268374c235 | ||
|
45e23131b8 | ||
|
6de05cf5f5 | ||
|
cd0736a945 | ||
|
68d6d342f8 | ||
|
e7da8e7e1f | ||
|
a7691cdebb | ||
|
6db5b91231 | ||
|
1bdcb01f63 | ||
|
b38564b8dc | ||
|
de3d054f78 | ||
|
4a2250f9ce | ||
|
d8fc4d093e | ||
|
e54cce245f | ||
|
59b29716e5 | ||
|
f8b3b0bc9a | ||
|
b11f4f93d7 | ||
|
0b1c796420 | ||
|
c1862ea84c | ||
|
2ec3e7dca8 | ||
|
ada5733c56 | ||
|
9e03fc3c2a | ||
|
eed708dbd9 | ||
|
6dce0e9489 | ||
|
461d340bd7 | ||
|
ee0c9e7b23 | ||
|
a9ee86ccf2 | ||
|
4e26024add | ||
|
0d95082aa9 | ||
|
9cbe4c1af9 | ||
|
cc19cc07f7 | ||
|
e3775173b4 | ||
|
ee13f265f0 | ||
|
221d8ff2a4 | ||
|
28b210f4fa | ||
|
1db4b81d5a | ||
|
4b4d19e3aa | ||
|
cd468b6218 | ||
|
33dd1f7747 | ||
|
e50d434a35 | ||
|
6af26273cb | ||
|
96118a2508 | ||
|
95abadd13c | ||
|
60de09b00c | ||
|
38dd79307a | ||
|
24803bbaad | ||
|
c8e8439b3d | ||
|
68e7d34965 | ||
|
4585785613 | ||
|
a9651adc84 | ||
|
e611f82a11 | ||
|
8270ff5702 | ||
|
e118b65d65 | ||
|
61ae07966d | ||
|
ff05ed7afd | ||
|
e84f527cd3 | ||
|
0468f5da6d | ||
|
021715e897 | ||
|
5eda393488 | ||
|
de78e8eae7 | ||
|
34da83d729 |
@@ -1 +1 @@
|
||||
1.02.136-git (2016-09-26)
|
||||
1.02.137-git (2016-11-05)
|
||||
|
17
WHATS_NEW
17
WHATS_NEW
@@ -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.
|
||||
|
15
WHATS_NEW_DM
15
WHATS_NEW_DM
@@ -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.
|
||||
|
@@ -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)
|
||||
|
@@ -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 */
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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.",
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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):
|
||||
|
@@ -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)
|
||||
|
@@ -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:
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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.
|
||||
|
@@ -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()
|
||||
|
@@ -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(
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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")
|
||||
|
@@ -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';
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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: "
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
@@ -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 */
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
*
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
@@ -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"
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
||||
---
|
||||
|
@@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@@ -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");
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
||||
|
922
tools/lvchange.c
922
tools/lvchange.c
File diff suppressed because it is too large
Load Diff
2299
tools/lvconvert.c
2299
tools/lvconvert.c
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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 :
|
||||
|
309
tools/toollib.c
309
tools/toollib.c
@@ -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;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user