mirror of
git://sourceware.org/git/lvm2.git
synced 2025-09-17 21:44:24 +03:00
Compare commits
3 Commits
v2_03_15
...
dev-dct-pv
Author | SHA1 | Date | |
---|---|---|---|
|
9388ca6e90 | ||
|
e719522e29 | ||
|
27a19e46f4 |
@@ -320,6 +320,33 @@ static int _parse_debug_classes(struct cmd_context *cmd)
|
||||
return debug_classes;
|
||||
}
|
||||
|
||||
static uint32_t _parse_log_journal(struct cmd_context *cmd, int cfg, const char *cfgname)
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
const struct dm_config_value *cv;
|
||||
uint32_t fields = 0;
|
||||
uint32_t val;
|
||||
|
||||
if (!(cn = find_config_tree_array(cmd, cfg, NULL))) {
|
||||
log_debug("Unable to find configuration for log/%s.", cfgname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (cv = cn->v; cv; cv = cv->next) {
|
||||
if (cv->type != DM_CFG_STRING) {
|
||||
log_verbose("log/%s contains a value which is not a string. Ignoring.", cfgname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((val = log_journal_str_to_val(cv->v.str)))
|
||||
fields |= val;
|
||||
else
|
||||
log_verbose("Unrecognised value for log/%s: %s", cfgname, cv->v.str);
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
static void _init_logging(struct cmd_context *cmd)
|
||||
{
|
||||
int append = 1;
|
||||
@@ -388,6 +415,9 @@ static void _init_logging(struct cmd_context *cmd)
|
||||
init_debug_file_fields(_parse_debug_fields(cmd, log_debug_file_fields_CFG, "debug_file_fields"));
|
||||
init_debug_output_fields(_parse_debug_fields(cmd, log_debug_output_fields_CFG, "debug_output_fields"));
|
||||
|
||||
cmd->default_settings.journal = _parse_log_journal(cmd, log_journal_CFG, "journal");
|
||||
init_log_journal(cmd->default_settings.journal);
|
||||
|
||||
t = time(NULL);
|
||||
ctime_r(&t, &timebuf[0]);
|
||||
timebuf[24] = '\0';
|
||||
|
@@ -29,6 +29,7 @@ struct config_info {
|
||||
int debug_classes;
|
||||
int verbose;
|
||||
int silent;
|
||||
int suppress;
|
||||
int test;
|
||||
int syslog;
|
||||
int activation;
|
||||
@@ -40,6 +41,7 @@ struct config_info {
|
||||
int udev_sync;
|
||||
int udev_fallback;
|
||||
int issue_discards;
|
||||
uint32_t journal;
|
||||
const char *msg_prefix;
|
||||
const char *fmt_name;
|
||||
const char *dmeventd_executable;
|
||||
|
@@ -876,6 +876,12 @@ cfg(log_syslog_CFG, "syslog", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_SYSLOG,
|
||||
cfg(log_file_CFG, "file", log_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Write error and debug log messages to a file specified here.\n")
|
||||
|
||||
cfg_array(log_journal_CFG, "journal", log_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, NULL, vsn(2, 3, 12), NULL, 0, NULL,
|
||||
"Record lvm information in the systemd journal.\n"
|
||||
"command: record commands that are run.\n"
|
||||
"output: record default output from commands.\n"
|
||||
"debug: record debug messages from commands.\n")
|
||||
|
||||
cfg(log_overwrite_CFG, "overwrite", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_OVERWRITE, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Overwrite the log file each time the program is run.\n")
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include <syslog.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <systemd/sd-journal.h>
|
||||
|
||||
static FILE *_log_file;
|
||||
static char _log_file_path[PATH_MAX];
|
||||
@@ -40,6 +41,7 @@ static char _msg_prefix[30] = " ";
|
||||
static int _abort_on_internal_errors_config = 0;
|
||||
static uint32_t _debug_file_fields;
|
||||
static uint32_t _debug_output_fields;
|
||||
static uint32_t _log_journal = 0;
|
||||
|
||||
static lvm2_log_fn_t _lvm2_log_fn = NULL;
|
||||
|
||||
@@ -455,6 +457,11 @@ void init_debug_output_fields(uint32_t debug_fields)
|
||||
_debug_output_fields = debug_fields;
|
||||
}
|
||||
|
||||
void init_log_journal(uint32_t fields)
|
||||
{
|
||||
_log_journal = fields;
|
||||
}
|
||||
|
||||
static void _set_time_prefix(char *prefix, int buflen)
|
||||
{
|
||||
|
||||
@@ -609,6 +616,33 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
|
||||
}
|
||||
|
||||
log_it:
|
||||
|
||||
if (_log_journal) {
|
||||
int to_journal = 0;
|
||||
|
||||
/* By default the visible command output is _LOG_WARN or less. */
|
||||
|
||||
if (_log_journal & LOG_JOURNAL_DEBUG)
|
||||
to_journal = 1;
|
||||
if ((_log_journal & LOG_JOURNAL_OUTPUT) && (log_level(level) <= _LOG_WARN))
|
||||
to_journal = 1;
|
||||
|
||||
if (to_journal) {
|
||||
int prio;
|
||||
switch (log_level(level)) {
|
||||
case _LOG_ERR: prio = LOG_ERR; break;
|
||||
case _LOG_WARN: prio = LOG_WARNING; break;
|
||||
case _LOG_INFO: prio = LOG_INFO; break;
|
||||
case _LOG_NOTICE: prio = LOG_NOTICE; break;
|
||||
case _LOG_DEBUG: prio = LOG_DEBUG; break;
|
||||
default: prio = LOG_INFO;
|
||||
}
|
||||
va_copy(ap, orig_ap);
|
||||
sd_journal_printv(prio, trformat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
if (!logged_via_report && ((verbose_level() >= level) && !_log_suppress)) {
|
||||
if (verbose_level() > _LOG_DEBUG) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
@@ -792,3 +826,60 @@ void log_set_report_object_name_and_id(const char *name, const char *id)
|
||||
_log_report.object_name = name;
|
||||
_log_report.object_id = id;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: log/journal=["daemon_command"]
|
||||
* daemon_command: record commands that are run by an lvm daemon.
|
||||
* (i.e. not commands run directly by a user.)
|
||||
* For this we need to be able to clearly identify when a command is
|
||||
* being run by dmeventd/lvmpolld/lvmdbusd.
|
||||
*
|
||||
* TODO: log/journal_commmand_names=["lvcreate","lvconvert"]
|
||||
* This would restrict log/journal=["command"] to the listed command names.
|
||||
* Also allow "!command" to exclude a command, e.g. ["!pvs"]
|
||||
*
|
||||
* TODO: log/journal_daemon_command_names=["lvcreate","lvconvert"]
|
||||
* This would restrict log/journal=["dameon_command"] to the listed command names.
|
||||
*
|
||||
* TODO: log/journal_daemon_names=["dmeventd"]
|
||||
* This would restrict log/journal=["daemon_command"] to commands run by
|
||||
* the named daemon.
|
||||
*
|
||||
* TODO: log/command_to_file=<path> would write this info to the file.
|
||||
*
|
||||
* TODO: log/debug_to_file=<path> would write full debugging to the file.
|
||||
* (the same effect as log/file=<path> log/level=7)
|
||||
*/
|
||||
|
||||
void log_command(const char *cmd_line, const char *cmd_name, const char *cmd_id)
|
||||
{
|
||||
if (_log_journal & LOG_JOURNAL_COMMAND) {
|
||||
|
||||
/*
|
||||
* TODO: DAEMON=dmeventd|lvmpolld|lvmdbusd,
|
||||
* Could we include caller info such as libblkid, udev rule, etc?
|
||||
* Does systemd already record the caller for us?
|
||||
*/
|
||||
|
||||
/* The command line, pid, and other things are automatically included. */
|
||||
|
||||
sd_journal_send("MESSAGE=lvm command %s", cmd_name,
|
||||
"MESSAGE_ID=3ca432788c374e4ba684b834188eca36",
|
||||
"LVM_CMD_NAME=%s", cmd_name,
|
||||
"LVM_CMD_ID=%s", cmd_id,
|
||||
"PRIORITY=%i", LOG_INFO,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t log_journal_str_to_val(const char *str)
|
||||
{
|
||||
if (!strcasecmp(str, "command"))
|
||||
return LOG_JOURNAL_COMMAND;
|
||||
if (!strcasecmp(str, "output"))
|
||||
return LOG_JOURNAL_OUTPUT;
|
||||
if (!strcasecmp(str, "debug"))
|
||||
return LOG_JOURNAL_DEBUG;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -63,6 +63,10 @@
|
||||
#define LOG_DEBUG_FIELD_FILELINE 0x0004
|
||||
#define LOG_DEBUG_FIELD_MESSAGE 0x0008
|
||||
|
||||
#define LOG_JOURNAL_COMMAND 0x0001
|
||||
#define LOG_JOURNAL_OUTPUT 0x0002
|
||||
#define LOG_JOURNAL_DEBUG 0x0004
|
||||
|
||||
|
||||
/*
|
||||
* Classes available for debug log messages.
|
||||
|
@@ -62,6 +62,12 @@ void reset_log_duplicated(void);
|
||||
void init_syslog(int facility);
|
||||
void fin_syslog(void);
|
||||
|
||||
void init_log_journal(uint32_t fields);
|
||||
uint32_t log_journal_str_to_val(const char *str);
|
||||
|
||||
void log_command(const char *cmd_line, const char *cmd_name, const char *cmd_id);
|
||||
|
||||
|
||||
int error_message_produced(void);
|
||||
void reset_lvm_errno(int store_errmsg);
|
||||
int stored_errno(void);
|
||||
|
@@ -5264,3 +5264,36 @@ struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_
|
||||
|
||||
return vg;
|
||||
}
|
||||
|
||||
int get_visible_lvs_using_pv(struct cmd_context *cmd, struct volume_group *vg, struct device *dev,
|
||||
struct dm_list *lvs_list)
|
||||
{
|
||||
struct pv_list *pvl;
|
||||
struct lv_list *lvl, *lvl2;
|
||||
struct physical_volume *pv = NULL;
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (pvl->pv->dev == dev) {
|
||||
pv = pvl->pv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pv)
|
||||
return_0;
|
||||
|
||||
dm_list_iterate_items(lvl, &vg->lvs) {
|
||||
if (!lv_is_visible(lvl->lv))
|
||||
continue;
|
||||
if (!lv_is_on_pv(lvl->lv, pv))
|
||||
continue;
|
||||
|
||||
if (!(lvl2 = dm_pool_zalloc(cmd->mem, sizeof(*lvl2))))
|
||||
return_0;
|
||||
lvl2->lv = lvl->lv;
|
||||
dm_list_add(lvs_list, &lvl2->list);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@@ -539,4 +539,8 @@ char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tagsl);
|
||||
|
||||
void set_pv_devices(struct format_instance *fid, struct volume_group *vg);
|
||||
|
||||
int get_visible_lvs_using_pv(struct cmd_context *cmd, struct volume_group *vg, struct device *dev,
|
||||
struct dm_list *lvs_list);
|
||||
|
||||
|
||||
#endif
|
||||
|
12
scripts/lvm-vgchange.service
Normal file
12
scripts/lvm-vgchange.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=LVM event activation of VG %i
|
||||
Documentation=man:vgchange(8)
|
||||
DefaultDependencies=no
|
||||
StartLimitIntervalSec=0
|
||||
Before=shutdown.target
|
||||
Conflicts=shutdown.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=no
|
||||
ExecStart=@SBINDIR@/lvm vgchange -aay %i
|
28
tools/args.h
28
tools/args.h
@@ -307,12 +307,33 @@ arg(ignoreunsupported_ARG, '\0', "ignoreunsupported", 0, 0, 0,
|
||||
arg(importdevices_ARG, '\0', "importdevices", 0, 0, 0,
|
||||
"Add devices to the devices file.\n")
|
||||
|
||||
arg(journal_ARG, '\0', "journal", string_VAL, 0, 0,
|
||||
"Record information in the systemd journal.\n"
|
||||
"This information is in addition to information\n"
|
||||
"enabled by the lvm.conf log/journal setting.\n"
|
||||
"command: record information about the command.\n"
|
||||
"output: record the default command output.\n"
|
||||
"debug: record full command debugging.\n")
|
||||
|
||||
arg(labelsector_ARG, '\0', "labelsector", number_VAL, 0, 0,
|
||||
"By default the PV is labelled with an LVM2 identifier in its second\n"
|
||||
"sector (sector 1). This lets you use a different sector near the\n"
|
||||
"start of the disk (between 0 and 3 inclusive - see LABEL_SCAN_SECTORS\n"
|
||||
"in the source). Use with care.\n")
|
||||
|
||||
arg(listlvs_ARG, '\0', "listlvs", 0, 0, 0,
|
||||
"Print a list of LVs that use the device.\n")
|
||||
|
||||
arg(listvg_ARG, '\0', "listvg", 0, 0, 0,
|
||||
"Print the VG that uses the device.\n")
|
||||
|
||||
arg(checkcomplete_ARG, '\0', "checkcomplete", 0, 0, 0,
|
||||
"Check if all the devices used by a VG or LV are present,\n"
|
||||
"and print \"complete\" or \"incomplete\" for each listed\n"
|
||||
"VG or LV. This option is used as a part of event-based\n"
|
||||
"autoactivation, so pvscan will do nothing if this option\n"
|
||||
"is set and event_activation=0 in the config settings.\n")
|
||||
|
||||
arg(lockopt_ARG, '\0', "lockopt", string_VAL, 0, 0,
|
||||
"Used to pass options for special cases to lvmlockd.\n"
|
||||
"See \\fBlvmlockd\\fP(8) for more information.\n")
|
||||
@@ -795,6 +816,9 @@ arg(type_ARG, '\0', "type", segtype_VAL, 0, 0,
|
||||
"(e.g. --stripes, --mirrors, --snapshot, --virtualsize, --thin, --cache, --vdo).\n"
|
||||
"Use inferred types with care because it can lead to unexpected results.\n")
|
||||
|
||||
arg(udevoutput_ARG, '\0', "udevoutput", 0, 0, 0,
|
||||
"Command output is modified to be imported from a udev rule.\n")
|
||||
|
||||
arg(unbuffered_ARG, '\0', "unbuffered", 0, 0, 0,
|
||||
"Produce output immediately without sorting or aligning the columns properly.\n")
|
||||
|
||||
@@ -871,6 +895,10 @@ arg(vgmetadatacopies_ARG, '\0', "vgmetadatacopies", vgmetadatacopies_VAL, 0, 0,
|
||||
"\\fBall\\fP causes LVM to first clear the metadataignore flags on\n"
|
||||
"all PVs, and then to become unmanaged.\n")
|
||||
|
||||
arg(vgonline_ARG, '\0', "vgonline", 0, 0, 0,
|
||||
"The first command to see a complete VG will report it uniquely.\n"
|
||||
"Other commands to see the complete VG will report it differently.\n")
|
||||
|
||||
arg(withsummary_ARG, '\0', "withsummary", 0, 0, 0,
|
||||
"Display a one line comment for each configuration node.\n")
|
||||
|
||||
|
@@ -188,7 +188,7 @@
|
||||
#
|
||||
OO_ALL: --commandprofile String, --config String, --debug,
|
||||
--driverloaded Bool, --help, --nolocking, --lockopt String, --longhelp, --profile String, --quiet,
|
||||
--verbose, --version, --yes, --test, --devicesfile String, --devices PV
|
||||
--verbose, --version, --yes, --test, --devicesfile String, --devices PV, --journal String
|
||||
|
||||
#
|
||||
# options for pvs, lvs, vgs, fullreport
|
||||
@@ -1603,11 +1603,37 @@ DESC: Display PV information.
|
||||
|
||||
pvscan --cache_long
|
||||
OO: --ignorelockingfailure, --reportformat ReportFmt,
|
||||
--activate ay, --major Number, --minor Number, --noudevsync
|
||||
--major Number, --minor Number, --noudevsync
|
||||
OP: PV|String ...
|
||||
IO: --background
|
||||
ID: pvscan_cache
|
||||
DESC: Autoactivate a VG when all PVs are online.
|
||||
DESC: Record that a PV is online or offline.
|
||||
|
||||
pvscan --cache_long --activate ay
|
||||
OO: --ignorelockingfailure, --reportformat ReportFmt,
|
||||
--major Number, --minor Number, --noudevsync
|
||||
OP: PV|String ...
|
||||
IO: --background
|
||||
ID: pvscan_cache
|
||||
DESC: Record that a PV is online and autoactivate the VG if complete.
|
||||
|
||||
pvscan --cache_long --listvg PV
|
||||
OO: --ignorelockingfailure, --checkcomplete, --vgonline, --udevoutput
|
||||
ID: pvscan_cache
|
||||
DESC: Record that a PV is online and list the VG using the PV.
|
||||
|
||||
pvscan --cache_long --listlvs PV
|
||||
OO: --ignorelockingfailure, --checkcomplete, --vgonline
|
||||
ID: pvscan_cache
|
||||
DESC: Record that a PV is online and list LVs using the PV.
|
||||
|
||||
pvscan --listlvs PV
|
||||
ID: pvscan_cache
|
||||
DESC: List LVs using the PV.
|
||||
|
||||
pvscan --listvg PV
|
||||
ID: pvscan_cache
|
||||
DESC: List the VG using the PV.
|
||||
|
||||
---
|
||||
|
||||
|
@@ -2007,6 +2007,8 @@ out:
|
||||
log_debug("Recognised command %s (id %d / enum %d).",
|
||||
commands[best_i].command_id, best_i, commands[best_i].command_enum);
|
||||
|
||||
log_command(cmd->cmd_line, commands[best_i].name, commands[best_i].command_id);
|
||||
|
||||
return &commands[best_i];
|
||||
}
|
||||
|
||||
@@ -2381,6 +2383,9 @@ static void _reset_current_settings_to_default(struct cmd_context *cmd)
|
||||
|
||||
static void _get_current_output_settings_from_args(struct cmd_context *cmd)
|
||||
{
|
||||
if (arg_is_set(cmd, udevoutput_ARG))
|
||||
cmd->current_settings.suppress = 1;
|
||||
|
||||
if (arg_is_set(cmd, debug_ARG))
|
||||
cmd->current_settings.debug = _LOG_FATAL + (arg_count(cmd, debug_ARG) - 1);
|
||||
|
||||
@@ -2392,14 +2397,25 @@ static void _get_current_output_settings_from_args(struct cmd_context *cmd)
|
||||
cmd->current_settings.verbose = 0;
|
||||
cmd->current_settings.silent = (arg_count(cmd, quiet_ARG) > 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* default_settings.journal is already set from config and has already been
|
||||
* applied using init_log_journal().
|
||||
* current_settings have been set to default_settings.
|
||||
* now --journal value adds to current_settings.
|
||||
*/
|
||||
if (arg_is_set(cmd, journal_ARG))
|
||||
cmd->current_settings.journal |= log_journal_str_to_val(arg_str_value(cmd, journal_ARG, ""));
|
||||
}
|
||||
|
||||
static void _apply_current_output_settings(struct cmd_context *cmd)
|
||||
{
|
||||
log_suppress(cmd->current_settings.suppress);
|
||||
init_debug(cmd->current_settings.debug);
|
||||
init_debug_classes_logged(cmd->default_settings.debug_classes);
|
||||
init_verbose(cmd->current_settings.verbose + VERBOSE_BASE_LEVEL);
|
||||
init_silent(cmd->current_settings.silent);
|
||||
init_log_journal(cmd->current_settings.journal);
|
||||
}
|
||||
|
||||
static int _read_devices_list(struct cmd_context *cmd)
|
||||
|
411
tools/pvscan.c
411
tools/pvscan.c
@@ -179,6 +179,27 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Avoid a duplicate pvscan[%d] prefix when logging to the journal.
|
||||
* FIXME: this should probably replace if (udevoutput) with
|
||||
* if (log_journal & LOG_JOURNAL_OUTPUT)
|
||||
*/
|
||||
#define log_print_pvscan(cmd, fmt, args...) \
|
||||
do \
|
||||
if (arg_is_set(cmd, udevoutput_ARG)) \
|
||||
log_print(fmt, ##args); \
|
||||
else \
|
||||
log_print("pvscan[%d] " fmt, getpid(), ##args); \
|
||||
while (0)
|
||||
|
||||
#define log_error_pvscan(cmd, fmt, args...) \
|
||||
do \
|
||||
if (arg_is_set(cmd, udevoutput_ARG)) \
|
||||
log_error(fmt, ##args); \
|
||||
else \
|
||||
log_error("pvscan[%d] " fmt, getpid(), ##args); \
|
||||
while (0)
|
||||
|
||||
static char *_vgname_in_pvid_file_buf(char *buf)
|
||||
{
|
||||
char *p, *n;
|
||||
@@ -259,7 +280,7 @@ static void _lookup_file_remove(char *vgname)
|
||||
* that the vg will be activated again when it becomes complete.
|
||||
*/
|
||||
|
||||
static void _online_vg_file_remove(const char *vgname)
|
||||
void online_vg_file_remove(const char *vgname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
@@ -314,7 +335,7 @@ static void _online_pvid_file_remove_devno(int major, int minor)
|
||||
log_sys_debug("unlink", path);
|
||||
|
||||
if (file_vgname[0]) {
|
||||
_online_vg_file_remove(file_vgname);
|
||||
online_vg_file_remove(file_vgname);
|
||||
_lookup_file_remove(file_vgname);
|
||||
}
|
||||
}
|
||||
@@ -345,7 +366,7 @@ static void _online_files_remove(const char *dirpath)
|
||||
log_sys_debug("closedir", dirpath);
|
||||
}
|
||||
|
||||
static int _online_pvid_file_create(struct device *dev, const char *vgname)
|
||||
static int _online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[MAX_PVID_FILE_SIZE] = { 0 };
|
||||
@@ -362,18 +383,18 @@ static int _online_pvid_file_create(struct device *dev, const char *vgname)
|
||||
minor = (int)MINOR(dev->dev);
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, dev->pvid) < 0) {
|
||||
log_error("Path %s/%s is too long.", _pvs_online_dir, dev->pvid);
|
||||
log_error_pvscan(cmd, "Path %s/%s is too long.", _pvs_online_dir, dev->pvid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((len1 = dm_snprintf(buf, sizeof(buf), "%d:%d\n", major, minor)) < 0) {
|
||||
log_error("Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor);
|
||||
log_error_pvscan(cmd, "Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vgname) {
|
||||
if ((len2 = dm_snprintf(buf + len1, sizeof(buf) - len1, "vg:%s\n", vgname)) < 0) {
|
||||
log_warn("Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
|
||||
log_print_pvscan(cmd, "Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
|
||||
/* can still continue without vgname */
|
||||
len2 = 0;
|
||||
}
|
||||
@@ -387,7 +408,7 @@ static int _online_pvid_file_create(struct device *dev, const char *vgname)
|
||||
if (fd < 0) {
|
||||
if (errno == EEXIST)
|
||||
goto check_duplicate;
|
||||
log_error("Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
|
||||
log_error_pvscan(cmd, "Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -435,12 +456,12 @@ check_duplicate:
|
||||
/* Don't know how vgname might not match, but it's not good so fail. */
|
||||
|
||||
if ((file_major != major) || (file_minor != minor))
|
||||
log_error("pvscan[%d] PV %s is duplicate for PVID %s on %d:%d and %d:%d.",
|
||||
getpid(), dev_name(dev), dev->pvid, major, minor, file_major, file_minor);
|
||||
log_error_pvscan(cmd, "PV %s is duplicate for PVID %s on %d:%d and %d:%d.",
|
||||
dev_name(dev), dev->pvid, major, minor, file_major, file_minor);
|
||||
|
||||
if (file_vgname[0] && vgname && strcmp(file_vgname, vgname))
|
||||
log_error("pvscan[%d] PV %s has unexpected VG %s vs %s.",
|
||||
getpid(), dev_name(dev), vgname, file_vgname);
|
||||
log_error_pvscan(cmd, "PV %s has unexpected VG %s vs %s.",
|
||||
dev_name(dev), vgname, file_vgname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -475,7 +496,7 @@ static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
|
||||
int fd;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, vg->name) < 0) {
|
||||
log_error("Path %s/%s is too long.", _pvs_lookup_dir, vg->name);
|
||||
log_error_pvscan(cmd, "Path %s/%s is too long.", _pvs_lookup_dir, vg->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -638,7 +659,7 @@ static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct de
|
||||
return (vgname) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void _online_dir_setup(void)
|
||||
static void _online_dir_setup(struct cmd_context *cmd)
|
||||
{
|
||||
struct stat st;
|
||||
int rv;
|
||||
@@ -652,7 +673,7 @@ static void _online_dir_setup(void)
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
|
||||
log_error("Failed to create %s %d", DEFAULT_RUN_DIR, errno);
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", DEFAULT_RUN_DIR, errno);
|
||||
|
||||
do_pvs:
|
||||
if (!stat(_pvs_online_dir, &st))
|
||||
@@ -664,7 +685,7 @@ do_pvs:
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(_pvs_online_dir, &st))
|
||||
log_error("Failed to create %s %d", _pvs_online_dir, errno);
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", _pvs_online_dir, errno);
|
||||
|
||||
do_vgs:
|
||||
if (!stat(_vgs_online_dir, &st))
|
||||
@@ -676,7 +697,7 @@ do_vgs:
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(_vgs_online_dir, &st))
|
||||
log_error("Failed to create %s %d", _vgs_online_dir, errno);
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", _vgs_online_dir, errno);
|
||||
|
||||
do_lookup:
|
||||
if (!stat(_pvs_lookup_dir, &st))
|
||||
@@ -688,7 +709,7 @@ do_lookup:
|
||||
dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if ((rv < 0) && stat(_pvs_lookup_dir, &st))
|
||||
log_error("Failed to create %s %d", _pvs_lookup_dir, errno);
|
||||
log_error_pvscan(cmd, "Failed to create %s %d", _pvs_lookup_dir, errno);
|
||||
|
||||
|
||||
}
|
||||
@@ -725,7 +746,7 @@ static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
|
||||
log_debug("pvscan autoactivating VG %s.", vg_name);
|
||||
|
||||
if (!vgchange_activate(cmd, vg, CHANGE_AAY)) {
|
||||
log_error("%s: autoactivation failed.", vg->name);
|
||||
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
|
||||
pp->activate_errors++;
|
||||
}
|
||||
|
||||
@@ -738,7 +759,7 @@ static int _online_vg_file_create(struct cmd_context *cmd, const char *vgname)
|
||||
int fd;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/%s", _vgs_online_dir, vgname) < 0) {
|
||||
log_error("Path %s/%s is too long.", _vgs_online_dir, vgname);
|
||||
log_error_pvscan(cmd, "Path %s/%s is too long.", _vgs_online_dir, vgname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -826,15 +847,15 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
|
||||
_online_pvid_file_read(path, &file_major, &file_minor, file_vgname);
|
||||
|
||||
if (file_vgname[0] && strcmp(vgname, file_vgname)) {
|
||||
log_error("Wrong VG found for %d:%d PVID %s: %s vs %s",
|
||||
file_major, file_minor, pvid, vgname, file_vgname);
|
||||
log_error_pvscan(cmd, "Wrong VG found for %d:%d PVID %s: %s vs %s",
|
||||
file_major, file_minor, pvid, vgname, file_vgname);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
devno = MKDEV(file_major, file_minor);
|
||||
|
||||
if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
|
||||
log_error("No device found for %d:%d PVID %s", file_major, file_minor, pvid);
|
||||
log_error_pvscan(cmd, "No device found for %d:%d PVID %s", file_major, file_minor, pvid);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
@@ -844,7 +865,7 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
|
||||
if (strcmp(name1, name2)) {
|
||||
if (!id_write_format((const struct id *)pvid, uuidstr, sizeof(uuidstr)))
|
||||
uuidstr[0] = '\0';
|
||||
log_print("PVID %s read from %s last written to %s.", uuidstr, name1, name2);
|
||||
log_print_pvscan(cmd, "PVID %s read from %s last written to %s.", uuidstr, name1, name2);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
@@ -921,7 +942,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
|
||||
* The dev_cache gives us struct devices from the devnums.
|
||||
*/
|
||||
if (!_get_devs_from_saved_vg(cmd, vgname, &devs)) {
|
||||
log_print("pvscan[%d] VG %s not using quick activation.", getpid(), vgname);
|
||||
log_print_pvscan(cmd, "VG %s not using quick activation.", vgname);
|
||||
*no_quick = 1;
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
@@ -932,7 +953,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
|
||||
* label rescan are then disabled in vg_read.)
|
||||
*/
|
||||
if (!lock_vol(cmd, vgname, LCK_VG_WRITE, NULL)) {
|
||||
log_error("pvscan activation for VG %s failed to lock VG.", vgname);
|
||||
log_error_pvscan(cmd, "activation for VG %s failed to lock VG.", vgname);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
@@ -945,7 +966,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
|
||||
label_scan_devs(cmd, NULL, &devs);
|
||||
|
||||
if (!(vgid = lvmcache_vgid_from_vgname(cmd, vgname))) {
|
||||
log_error("pvscan activation for VG %s failed to find vgid.", vgname);
|
||||
log_error_pvscan(cmd, "activation for VG %s failed to find vgid.", vgname);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
@@ -965,7 +986,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
|
||||
* original device arg scan. There will be very few and unusual
|
||||
* cases that would be caught here.
|
||||
*/
|
||||
log_error("pvscan activation for VG %s cannot read (%x).", vgname, error_flags);
|
||||
log_error_pvscan(cmd, "activation for VG %s cannot read (%x).", vgname, error_flags);
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
@@ -987,7 +1008,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (dev_in_device_list(pvl->pv->dev, &devs))
|
||||
continue;
|
||||
log_error("pvscan activation for VG %s found different devices.", vgname);
|
||||
log_error_pvscan(cmd, "activation for VG %s found different devices.", vgname);
|
||||
ret = ECMD_FAILED;
|
||||
goto out;
|
||||
}
|
||||
@@ -995,7 +1016,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
|
||||
log_debug("pvscan autoactivating VG %s.", vgname);
|
||||
|
||||
if (!vgchange_activate(cmd, vg, CHANGE_AAY)) {
|
||||
log_error("%s: autoactivation failed.", vg->name);
|
||||
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
|
||||
pp->activate_errors++;
|
||||
}
|
||||
|
||||
@@ -1014,7 +1035,7 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
|
||||
int ret = ECMD_FAILED;
|
||||
|
||||
if (!(handle = init_processing_handle(cmd, NULL))) {
|
||||
log_error("Failed to initialize processing handle.");
|
||||
log_error_pvscan(cmd, "Failed to initialize processing handle.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1029,11 +1050,11 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
|
||||
*/
|
||||
dm_list_iterate_items_safe(sl, sl2, vgnames) {
|
||||
if (!_online_vg_file_create(cmd, sl->str)) {
|
||||
log_print("pvscan[%d] VG %s skip autoactivation.", getpid(), sl->str);
|
||||
log_print_pvscan(cmd, "VG %s skip autoactivation.", sl->str);
|
||||
str_list_del(vgnames, sl->str);
|
||||
continue;
|
||||
}
|
||||
log_print("pvscan[%d] VG %s run autoactivation.", getpid(), sl->str);
|
||||
log_print_pvscan(cmd, "VG %s run autoactivation.", sl->str);
|
||||
}
|
||||
|
||||
if (dm_list_empty(vgnames)) {
|
||||
@@ -1165,6 +1186,64 @@ static int _get_args_devs(struct cmd_context *cmd, struct dm_list *pvscan_args,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char file_vgname[NAME_LEN];
|
||||
char pvid[ID_LEN+1] = { 0 };
|
||||
struct pv_list *pvl;
|
||||
struct device *dev;
|
||||
int major, minor;
|
||||
dev_t devno;
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
memcpy(&pvid, &pvl->pv->id.uuid, ID_LEN);
|
||||
|
||||
if (pvl->pv->status & MISSING_PV) {
|
||||
log_debug("set_pv_devices_online vg %s pv %s missing flag already set",
|
||||
vg->name, pvid);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_online_pvid_file_exists(pvid)) {
|
||||
log_debug("set_pv_devices_online vg %s pv %s no online file",
|
||||
vg->name, pvid);
|
||||
pvl->pv->status |= MISSING_PV;
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, pvid);
|
||||
|
||||
major = 0;
|
||||
minor = 0;
|
||||
file_vgname[0] = '\0';
|
||||
|
||||
_online_pvid_file_read(path, &major, &minor, file_vgname);
|
||||
|
||||
if (file_vgname[0] && strcmp(vg->name, file_vgname)) {
|
||||
log_warn("WARNING: VG %s PV %s wrong vgname in online file %s",
|
||||
vg->name, pvid, file_vgname);
|
||||
pvl->pv->status |= MISSING_PV;
|
||||
continue;
|
||||
}
|
||||
|
||||
devno = MKDEV(major, minor);
|
||||
|
||||
if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
|
||||
log_print_pvscan(cmd, "VG %s PV %s no device found for %d:%d",
|
||||
vg->name, pvid, major, minor);
|
||||
pvl->pv->status |= MISSING_PV;
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("set_pv_devices_online vg %s pv %s is online %s",
|
||||
vg->name, pvid, dev_name(dev));
|
||||
|
||||
pvl->pv->dev = dev;
|
||||
}
|
||||
}
|
||||
|
||||
static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvscan_devs,
|
||||
int *pv_count, struct dm_list *complete_vgnames)
|
||||
{
|
||||
@@ -1177,15 +1256,20 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
struct metadata_area *mda1, *mda2;
|
||||
struct volume_group *vg;
|
||||
struct physical_volume *pv;
|
||||
const char *vgname;
|
||||
uint32_t ext_version, ext_flags;
|
||||
const char *vgname = NULL;
|
||||
uint64_t devsize;
|
||||
uint32_t ext_version, ext_flags;
|
||||
int do_cache = arg_is_set(cmd, cache_long_ARG);
|
||||
int do_activate = arg_is_set(cmd, activate_ARG);
|
||||
int do_full_check;
|
||||
int do_list_lvs = arg_is_set(cmd, listlvs_ARG);
|
||||
int do_list_vg = arg_is_set(cmd, listvg_ARG);
|
||||
int do_check_complete = arg_is_set(cmd, checkcomplete_ARG);
|
||||
int do_vgonline = arg_is_set(cmd, vgonline_ARG);
|
||||
int pvs_online;
|
||||
int pvs_offline;
|
||||
int pvs_unknown;
|
||||
int vg_complete;
|
||||
int do_full_check;
|
||||
int ret = 1;
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl2, pvscan_devs) {
|
||||
@@ -1199,20 +1283,20 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
* using udev and the native version didn't catch it.
|
||||
*/
|
||||
if (udev_dev_is_mpath_component(dev)) {
|
||||
log_print("pvscan[%d] ignore multipath component %s.", getpid(), dev_name(dev));
|
||||
log_print_pvscan(cmd, "ignore multipath component %s.", dev_name(dev));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
|
||||
if (!do_all)
|
||||
log_print("pvscan[%d] ignore %s with no lvm info.", getpid(), dev_name(dev));
|
||||
log_print_pvscan(cmd, "ignore %s with no lvm info.", dev_name(dev));
|
||||
continue;
|
||||
}
|
||||
|
||||
ext_version = lvmcache_ext_version(info);
|
||||
ext_flags = lvmcache_ext_flags(info);
|
||||
if ((ext_version >= 2) && !(ext_flags & PV_EXT_USED)) {
|
||||
log_print("pvscan[%d] PV %s not used.", getpid(), dev_name(dev));
|
||||
log_print_pvscan(cmd, "PV %s not used.", dev_name(dev));
|
||||
(*pv_count)++;
|
||||
continue;
|
||||
}
|
||||
@@ -1231,7 +1315,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
vg = mda2->ops->vg_read(cmd, fid, "", mda2, NULL, NULL);
|
||||
|
||||
if (!vg) {
|
||||
log_print("pvscan[%d] PV %s has no VG metadata.", getpid(), dev_name(dev));
|
||||
log_print_pvscan(cmd, "PV %s has no VG metadata.", dev_name(dev));
|
||||
fmt->ops->destroy_instance(fid);
|
||||
goto online;
|
||||
}
|
||||
@@ -1239,7 +1323,7 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
set_pv_devices(fid, vg);
|
||||
|
||||
if (!(pv = find_pv(vg, dev))) {
|
||||
log_print("pvscan[%d] PV %s not found in VG %s.", getpid(), dev_name(dev), vg->name);
|
||||
log_print_pvscan(cmd, "PV %s not found in VG %s.", dev_name(dev), vg->name);
|
||||
release_vg(vg);
|
||||
continue;
|
||||
}
|
||||
@@ -1257,13 +1341,13 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
do_full_check = 1;
|
||||
}
|
||||
if (do_full_check && dev_is_md_component(dev, NULL, 1)) {
|
||||
log_print("pvscan[%d] ignore md component %s.", getpid(), dev_name(dev));
|
||||
log_print_pvscan(cmd, "ignore md component %s.", dev_name(dev));
|
||||
release_vg(vg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vg_is_shared(vg)) {
|
||||
log_print("pvscan[%d] PV %s ignore shared VG.", getpid(), dev_name(dev));
|
||||
log_print_pvscan(cmd, "PV %s ignore shared VG.", dev_name(dev));
|
||||
release_vg(vg);
|
||||
continue;
|
||||
}
|
||||
@@ -1273,7 +1357,13 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
vg_is_foreign(vg)) {
|
||||
log_verbose("Ignore PV %s with VG system id %s with our system id %s",
|
||||
dev_name(dev), vg->system_id, cmd->system_id);
|
||||
log_print("pvscan[%d] PV %s ignore foreign VG.", getpid(), dev_name(dev));
|
||||
log_print_pvscan(cmd, "PV %s ignore foreign VG.", dev_name(dev));
|
||||
release_vg(vg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vg_is_exported(vg)) {
|
||||
log_print_pvscan(cmd, "PV %s ignore exported VG.", dev_name(dev));
|
||||
release_vg(vg);
|
||||
continue;
|
||||
}
|
||||
@@ -1292,19 +1382,20 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
|
||||
/*
|
||||
* Create file named for pvid to record this PV is online.
|
||||
* The command creates/checks online files only when --cache is used.
|
||||
*/
|
||||
if (!_online_pvid_file_create(dev, vg ? vg->name : NULL)) {
|
||||
log_error("pvscan[%d] PV %s failed to create online file.", getpid(), dev_name(dev));
|
||||
if (do_cache && !_online_pvid_file_create(cmd, dev, vg ? vg->name : NULL)) {
|
||||
log_error_pvscan(cmd, "PV %s failed to create online file.", dev_name(dev));
|
||||
release_vg(vg);
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* When not activating we don't need to know about vg completeness.
|
||||
* A plain pvscan --cache <dev> just creates the online file.
|
||||
*/
|
||||
if (!do_activate) {
|
||||
log_print("pvscan[%d] PV %s online.", getpid(), dev_name(dev));
|
||||
if (!do_activate && !do_list_lvs && !do_list_vg) {
|
||||
log_print_pvscan(cmd, "PV %s online.", dev_name(dev));
|
||||
release_vg(vg);
|
||||
continue;
|
||||
}
|
||||
@@ -1312,58 +1403,159 @@ static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvs
|
||||
/*
|
||||
* Check if all the PVs for this VG are online. If the arrival
|
||||
* of this dev completes the VG, then save the vgname in
|
||||
* complete_vgnames so it will be activated.
|
||||
* complete_vgnames (activation phase will want to know which
|
||||
* VGs to activate.)
|
||||
*/
|
||||
pvs_online = 0;
|
||||
pvs_offline = 0;
|
||||
pvs_unknown = 0;
|
||||
vg_complete = 0;
|
||||
if (do_activate || do_check_complete) {
|
||||
pvs_online = 0;
|
||||
pvs_offline = 0;
|
||||
pvs_unknown = 0;
|
||||
vg_complete = 0;
|
||||
|
||||
if (vg) {
|
||||
/*
|
||||
* Use the VG metadata from this PV for a list of all
|
||||
* PVIDs. Write a lookup file of PVIDs in case another
|
||||
* pvscan needs it. After writing lookup file, recheck
|
||||
* pvid files to resolve a possible race with another
|
||||
* pvscan reading the lookup file that missed it.
|
||||
*/
|
||||
log_debug("checking all pvid files from vg %s", vg->name);
|
||||
_count_pvid_files(vg, &pvs_online, &pvs_offline);
|
||||
|
||||
if (pvs_offline && _write_lookup_file(cmd, vg)) {
|
||||
log_debug("rechecking all pvid files from vg %s", vg->name);
|
||||
if (vg) {
|
||||
/*
|
||||
* Use the VG metadata from this PV for a list of all
|
||||
* PVIDs. Write a lookup file of PVIDs in case another
|
||||
* pvscan needs it. After writing lookup file, recheck
|
||||
* pvid files to resolve a possible race with another
|
||||
* pvscan reading the lookup file that missed it.
|
||||
*/
|
||||
log_debug("checking all pvid files from vg %s", vg->name);
|
||||
_count_pvid_files(vg, &pvs_online, &pvs_offline);
|
||||
if (!pvs_offline)
|
||||
log_print("pvscan[%d] VG %s complete after recheck.", getpid(), vg->name);
|
||||
|
||||
if (pvs_offline && _write_lookup_file(cmd, vg)) {
|
||||
log_debug("rechecking all pvid files from vg %s", vg->name);
|
||||
_count_pvid_files(vg, &pvs_online, &pvs_offline);
|
||||
if (!pvs_offline)
|
||||
log_print_pvscan(cmd, "VG %s complete after recheck.", vg->name);
|
||||
}
|
||||
|
||||
vgname = vg->name;
|
||||
} else {
|
||||
/*
|
||||
* No VG metadata on this PV, so try to use a lookup
|
||||
* file written by a prior pvscan for a list of all
|
||||
* PVIDs. A lookup file may not exist for this PV if
|
||||
* it's the first to appear from the VG.
|
||||
*/
|
||||
log_debug("checking all pvid files from lookup file");
|
||||
if (!_count_pvid_files_from_lookup_file(cmd, dev, &pvs_online, &pvs_offline, &vgname))
|
||||
pvs_unknown = 1;
|
||||
}
|
||||
|
||||
if (pvs_unknown) {
|
||||
log_print_pvscan(cmd, "PV %s online, VG unknown.", dev_name(dev));
|
||||
vg_complete = 0;
|
||||
|
||||
} else if (pvs_offline) {
|
||||
log_print_pvscan(cmd, "PV %s online, VG %s incomplete (need %d).",
|
||||
dev_name(dev), vgname, pvs_offline);
|
||||
vg_complete = 0;
|
||||
|
||||
} else {
|
||||
log_print_pvscan(cmd, "PV %s online, VG %s is complete.", dev_name(dev), vgname);
|
||||
if (!str_list_add(cmd->mem, complete_vgnames, dm_pool_strdup(cmd->mem, vgname)))
|
||||
stack;
|
||||
vg_complete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vgname && vg)
|
||||
vgname = vg->name;
|
||||
|
||||
if (do_list_vg || do_list_lvs) {
|
||||
if (!vgname) {
|
||||
log_print("VG unknown");
|
||||
} else if (!do_check_complete) {
|
||||
log_print("VG %s", vgname);
|
||||
} else if (vg_complete) {
|
||||
if (do_vgonline && !_online_vg_file_create(cmd, vgname)) {
|
||||
log_print("VG %s finished", vgname);
|
||||
} else {
|
||||
/*
|
||||
* A udev rule imports KEY=val from a program's stdout.
|
||||
* Other output causes udev to ignore everything.
|
||||
* Run pvscan from udev rule using --udevoutput to
|
||||
* enable this printf, and suppress all log output
|
||||
*/
|
||||
if (arg_is_set(cmd, udevoutput_ARG))
|
||||
printf("LVM_VG_NAME_COMPLETE='%s'\n", vgname);
|
||||
else
|
||||
log_print("VG %s complete", vgname);
|
||||
}
|
||||
} else {
|
||||
if (arg_is_set(cmd, udevoutput_ARG))
|
||||
printf("LVM_VG_NAME_INCOMPLETE='%s'\n", vgname);
|
||||
else
|
||||
log_print("VG %s incomplete", vgname);
|
||||
}
|
||||
|
||||
vgname = vg->name;
|
||||
} else {
|
||||
/*
|
||||
* No VG metadata on this PV, so try to use a lookup
|
||||
* file written by a prior pvscan for a list of all
|
||||
* PVIDs. A lookup file may not exist for this PV if
|
||||
* it's the first to appear from the VG.
|
||||
* When the VG is complete|finished, we could print
|
||||
* a list of devices in the VG, by reading the pvid files
|
||||
* that were counted, which provides major:minor of each
|
||||
* device and using that to get the struct dev and dev_name.
|
||||
* The user could pass this list of devices to --devices
|
||||
* to optimize a subsequent command (activation) on the VG.
|
||||
* Just call set_pv_devices_online (if not done othewise)
|
||||
* since that finds the devs.
|
||||
*/
|
||||
log_debug("checking all pvid files from lookup file");
|
||||
if (!_count_pvid_files_from_lookup_file(cmd, dev, &pvs_online, &pvs_offline, &vgname))
|
||||
pvs_unknown = 1;
|
||||
}
|
||||
|
||||
if (pvs_unknown) {
|
||||
log_print("pvscan[%d] PV %s online, VG unknown.",
|
||||
getpid(), dev_name(dev));
|
||||
} else if (pvs_offline) {
|
||||
log_print("pvscan[%d] PV %s online, VG %s incomplete (need %d).",
|
||||
getpid(), dev_name(dev), vgname, pvs_offline);
|
||||
} else {
|
||||
log_print("pvscan[%d] PV %s online, VG %s is complete.", getpid(), dev_name(dev), vgname);
|
||||
if (!str_list_add(cmd->mem, complete_vgnames, dm_pool_strdup(cmd->mem, vgname)))
|
||||
stack;
|
||||
vg_complete = 1;
|
||||
if (do_list_lvs && !vg) {
|
||||
/* require all PVs used for booting have metadata */
|
||||
log_print_pvscan(cmd, "Cannot list LVs from device without metadata.");
|
||||
}
|
||||
|
||||
if (!saved_vg && vg && vg_complete && !do_all && (dm_list_size(pvscan_devs) == 1))
|
||||
if (do_list_lvs && vg) {
|
||||
struct dm_list lvs_list;
|
||||
struct lv_list *lvl;
|
||||
|
||||
dm_list_init(&lvs_list);
|
||||
|
||||
/*
|
||||
* For each vg->pvs entry, get the dev based on the online file
|
||||
* for the pvid and set pv->dev or pv->status MISSING_PV.
|
||||
*/
|
||||
_set_pv_devices_online(cmd, vg);
|
||||
|
||||
/*
|
||||
* lvs_list are LVs that use dev.
|
||||
*/
|
||||
if (!get_visible_lvs_using_pv(cmd, vg, dev, &lvs_list))
|
||||
log_print_pvscan(cmd, "Failed to find LVs using %s.", dev_name(dev));
|
||||
|
||||
if (!do_check_complete) {
|
||||
dm_list_iterate_items(lvl, &lvs_list)
|
||||
log_print("LV %s", display_lvname(lvl->lv));
|
||||
} else if (vg_complete) {
|
||||
/*
|
||||
* A shortcut; the vg complete implies all lvs are complete.
|
||||
*/
|
||||
dm_list_iterate_items(lvl, &lvs_list)
|
||||
log_print("LV %s complete", display_lvname(lvl->lv));
|
||||
} else {
|
||||
/*
|
||||
* For each LV in VG, check if all devs are present.
|
||||
* Sets the PARTIAL flag on LVs that are not complete.
|
||||
*/
|
||||
if (!vg_mark_partial_lvs(vg, 1))
|
||||
log_print_pvscan(cmd, "Failed to check partial lvs.");
|
||||
|
||||
dm_list_iterate_items(lvl, &lvs_list) {
|
||||
if (!lv_is_partial(lvl->lv))
|
||||
log_print("LV %s complete", display_lvname(lvl->lv));
|
||||
else
|
||||
log_print("LV %s incomplete", display_lvname(lvl->lv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When "pvscan --cache -aay <dev>" completes the vg, save the
|
||||
* struct vg to use for quick activation function.
|
||||
*/
|
||||
if (do_activate && !saved_vg && vg && vg_complete && !do_all && (dm_list_size(pvscan_devs) == 1))
|
||||
saved_vg = vg;
|
||||
else
|
||||
release_vg(vg);
|
||||
@@ -1452,7 +1644,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
|
||||
cmd->pvscan_cache_single = 1;
|
||||
|
||||
if (!setup_devices(cmd)) {
|
||||
log_error("Failed to set up devices.");
|
||||
log_error_pvscan(cmd, "Failed to set up devices.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1518,8 +1710,8 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
|
||||
|
||||
dm_list_iterate_items_safe(devl, devl2, &pvscan_devs) {
|
||||
if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
|
||||
log_print("pvscan[%d] %s excluded by filters: %s.", getpid(),
|
||||
dev_name(devl->dev), dev_filtered_reason(devl->dev));
|
||||
log_print_pvscan(cmd, "%s excluded by filters: %s.",
|
||||
dev_name(devl->dev), dev_filtered_reason(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
}
|
||||
}
|
||||
@@ -1547,7 +1739,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
|
||||
dm_list_iterate_items_safe(devl, devl2, &pvscan_devs) {
|
||||
if (!label_read_pvid(devl->dev)) {
|
||||
/* Not an lvm device */
|
||||
log_print("pvscan[%d] %s not an lvm device.", getpid(), dev_name(devl->dev));
|
||||
log_print_pvscan(cmd, "%s not an lvm device.", dev_name(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
continue;
|
||||
}
|
||||
@@ -1558,8 +1750,8 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
|
||||
*/
|
||||
if (relax_deviceid_filter) {
|
||||
if (!get_du_for_pvid(cmd, devl->dev->pvid)) {
|
||||
log_print("pvscan[%d] %s excluded by devices file (checking PVID).",
|
||||
getpid(), dev_name(devl->dev));
|
||||
log_print_pvscan(cmd, "%s excluded by devices file (checking PVID).",
|
||||
dev_name(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
continue;
|
||||
}
|
||||
@@ -1567,8 +1759,8 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
|
||||
|
||||
/* Applies all filters, including those that need data from dev. */
|
||||
if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
|
||||
log_print("pvscan[%d] %s excluded by filters: %s.", getpid(),
|
||||
dev_name(devl->dev), dev_filtered_reason(devl->dev));
|
||||
log_print_pvscan(cmd, "%s excluded by filters: %s.",
|
||||
dev_name(devl->dev), dev_filtered_reason(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
}
|
||||
}
|
||||
@@ -1624,6 +1816,29 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
/*
|
||||
* lvm udev rules call:
|
||||
* pvscan --cache --listvg|--listlvs --checkcomplete PV
|
||||
* when PVs appear, even if event_activation=0 in lvm.conf.
|
||||
*
|
||||
* The udev rules will do autoactivation if they see complete
|
||||
* VGs/LVs reported from the pvscan.
|
||||
*
|
||||
* When event_activation=0 we do not want to do autoactivation
|
||||
* from udev events, so we need the pvscan to not report any
|
||||
* complete VGs/LVs when event_activation=0 so that the udev
|
||||
* rules do not attempt to autoactivate.
|
||||
*/
|
||||
|
||||
if (arg_is_set(cmd, checkcomplete_ARG) &&
|
||||
!find_config_tree_bool(cmd, global_event_activation_CFG, NULL)) {
|
||||
if (arg_is_set(cmd, udevoutput_ARG))
|
||||
printf("LVM_EVENT_ACTIVATION=0\n");
|
||||
else
|
||||
log_print_pvscan(cmd, "Ignoring pvscan with --checkcomplete because event_activation is disabled.");
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, major_ARG) + arg_is_set(cmd, minor_ARG))
|
||||
devno_args = 1;
|
||||
|
||||
@@ -1634,7 +1849,7 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
do_all = !argc && !devno_args;
|
||||
|
||||
_online_dir_setup();
|
||||
_online_dir_setup(cmd);
|
||||
|
||||
if (do_all) {
|
||||
if (!_pvscan_cache_all(cmd, argc, argv, &complete_vgnames))
|
||||
|
@@ -810,6 +810,24 @@ int lv_change_activate(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
lv_clear_integrity_recalculate_metadata(lv);
|
||||
}
|
||||
|
||||
/*
|
||||
* When LVs are deactivated, then autoactivation of the VG is
|
||||
* "re-armed" by removing the vg online file. So, after deactivation
|
||||
* of LVs, if PVs are disconnected and reconnected again, event
|
||||
* activation will trigger autoactivation again. This secondary
|
||||
* autoactivation is somewhat different from, and not as important as
|
||||
* the initial autoactivation during system startup. The secondary
|
||||
* autoactivation will happen to a VG on a running system and may be
|
||||
* mixing with user commands, so the end result is unpredictable.
|
||||
*
|
||||
* It's possible that we might want a config setting for usersto
|
||||
* disable secondary autoactivations. Once a system is up, the
|
||||
* user may want to take charge of activation changes to the VG
|
||||
* and not have the system autoactivation interfere.
|
||||
*/
|
||||
if (!is_change_activating(activate) && find_config_tree_bool(cmd, global_event_activation_CFG, NULL))
|
||||
online_vg_file_remove(lv->vg->name);
|
||||
|
||||
set_lv_notify(lv->vg->cmd);
|
||||
|
||||
return r;
|
||||
|
@@ -291,4 +291,6 @@ int lvconvert_cachevol_attach_single(struct cmd_context *cmd,
|
||||
struct logical_volume *lv,
|
||||
struct processing_handle *handle);
|
||||
|
||||
void online_vg_file_remove(const char *vgname);
|
||||
|
||||
#endif
|
||||
|
77
udev/69-dm-lvm.rules
Normal file
77
udev/69-dm-lvm.rules
Normal file
@@ -0,0 +1,77 @@
|
||||
# Copyright (C) 2012,2021 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
|
||||
# Udev rules for LVM.
|
||||
#
|
||||
# This rule requires blkid to be called on block devices before so only devices
|
||||
# used as LVM PVs are processed (ID_FS_TYPE="LVM2_member").
|
||||
|
||||
SUBSYSTEM!="block", GOTO="lvm_end"
|
||||
|
||||
ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", GOTO="lvm_end"
|
||||
|
||||
# Only process devices already marked as a PV - this requires blkid to be called before.
|
||||
ENV{ID_FS_TYPE}!="LVM2_member", GOTO="lvm_end"
|
||||
ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="lvm_end"
|
||||
ACTION=="remove", GOTO="lvm_end"
|
||||
|
||||
# Create /dev/disk/by-id/lvm-pv-uuid-<PV_UUID> symlink for each PV
|
||||
ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-id/lvm-pv-uuid-$env{ID_FS_UUID_ENC}"
|
||||
|
||||
# If the PV is a special device listed below, scan only if the device is
|
||||
# properly activated. These devices are not usable after an ADD event,
|
||||
# but they require an extra setup and they are ready after a CHANGE event.
|
||||
# Also support coldplugging with ADD event but only if the device is already
|
||||
# properly activated.
|
||||
# This logic should be eventually moved to rules where those particular
|
||||
# devices are processed primarily (MD and loop).
|
||||
|
||||
# DM device:
|
||||
KERNEL!="dm-[0-9]*", GOTO="next"
|
||||
ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}=="1", ENV{DM_ACTIVATION}=="1", GOTO="lvm_scan"
|
||||
GOTO="lvm_end"
|
||||
|
||||
# MD device:
|
||||
LABEL="next"
|
||||
KERNEL!="md[0-9]*", GOTO="next"
|
||||
IMPORT{db}="LVM_MD_PV_ACTIVATED"
|
||||
ACTION=="add", ENV{LVM_MD_PV_ACTIVATED}=="1", GOTO="lvm_scan"
|
||||
ACTION=="change", ENV{LVM_MD_PV_ACTIVATED}!="1", TEST=="md/array_state", ENV{LVM_MD_PV_ACTIVATED}="1", GOTO="lvm_scan"
|
||||
ACTION=="add", KERNEL=="md[0-9]*p[0-9]*", GOTO="lvm_scan"
|
||||
ENV{LVM_MD_PV_ACTIVATED}!="1", ENV{SYSTEMD_READY}="0"
|
||||
GOTO="lvm_end"
|
||||
|
||||
# Loop device:
|
||||
LABEL="next"
|
||||
KERNEL!="loop[0-9]*", GOTO="next"
|
||||
ACTION=="add", ENV{LVM_LOOP_PV_ACTIVATED}=="1", GOTO="lvm_scan"
|
||||
ACTION=="change", ENV{LVM_LOOP_PV_ACTIVATED}!="1", TEST=="loop/backing_file", ENV{LVM_LOOP_PV_ACTIVATED}="1", GOTO="lvm_scan"
|
||||
ENV{LVM_LOOP_PV_ACTIVATED}!="1", ENV{SYSTEMD_READY}="0"
|
||||
GOTO="lvm_end"
|
||||
|
||||
LABEL="next"
|
||||
ACTION!="add", GOTO="lvm_end"
|
||||
|
||||
LABEL="lvm_scan"
|
||||
|
||||
ENV{SYSTEMD_READY}="1"
|
||||
|
||||
# pvscan will check if this device completes a VG,
|
||||
# i.e. all PVs in the VG are now present with the
|
||||
# arrival of this PV. If so, it prints to stdout:
|
||||
# LVM_VG_NAME_COMPLETE='foo'
|
||||
#
|
||||
# When the VG is complete it can be activated, so
|
||||
# the lvm-vgchange service is started which runs
|
||||
# vgchange -aay <vgname>.
|
||||
#
|
||||
# pvscan only reads the single device specified,
|
||||
# and uses temp files under /run/lvm to check if
|
||||
# other PVs in the VG are present.
|
||||
|
||||
IMPORT{program}="/usr/sbin/lvm pvscan --cache --listvg --checkcomplete --vgonline --udevoutput --journal=output $env{DEVNAME}"
|
||||
ENV{LVM_VG_NAME_COMPLETE}=="?*", RUN+="/usr/bin/systemctl start lvm-vgchange@$env{LVM_VG_NAME_COMPLETE}.service"
|
||||
GOTO="lvm_end"
|
||||
|
||||
LABEL="lvm_end"
|
Reference in New Issue
Block a user