mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
Allow system.devices to be automatically created on first boot
This is intended for image-based OS deployments, where an installer is not run on the target machine to create a custom system.devices. Instead, the OS image preparation can configure the image so that lvm will automatically create system.devices for the root VG on first boot. image preparation: - create empty file /etc/lvm/devices/auto-import-rootvg - remove any existing /etc/lvm/devices/system.devices - enable lvm-devices-init.path and lvm-devices-init.service on first boot: - udev triggers vgchange -aay --autoactivation event <rootvg> - vgchange activates LVs in the root VG - vgchange finds finds auto-import-rootvg, and no system.devices, so it creates /run/lvm/lvm-devices-init - lvm-devices-init.path is run when /run/lvm/lvm-devices-init appears, and triggers lvm-devices-init.service - lvm-devices-init.service runs vgimportdevices --rootvg --auto - vgimportdevices finds auto-import-rootvg, and no system.devices, so it creates system.devices containing PVs in the root VG, and removes /etc/lvm/devices/auto-import-rootvg and /run/lvm/lvm-devices-init
This commit is contained in:
parent
85b711caae
commit
e9f7918c57
@ -337,6 +337,8 @@
|
||||
#define VGS_ONLINE_DIR DEFAULT_RUN_DIR "/vgs_online"
|
||||
#define PVS_LOOKUP_DIR DEFAULT_RUN_DIR "/pvs_lookup"
|
||||
|
||||
#define DEVICES_IMPORT_PATH DEFAULT_RUN_DIR "/lvm-devices-import"
|
||||
|
||||
#define DEFAULT_DEVICE_ID_SYSFS_DIR "/sys/" /* trailing / to match dm_sysfs_dir() */
|
||||
|
||||
#define DEFAULT_DEVICESFILE_BACKUP_LIMIT 50
|
||||
|
12
scripts/lvm-devices-import.path
Normal file
12
scripts/lvm-devices-import.path
Normal file
@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=lvm-devices-import to create system.devices
|
||||
|
||||
# /run/lvm/lvm-devices-import created by vgchange -aay <rootvg>
|
||||
|
||||
[Path]
|
||||
PathExists=/run/lvm/lvm-devices-import
|
||||
Unit=lvm-devices-import.service
|
||||
ConditionPathExists=!/etc/lvm/devices/system.devices
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
12
scripts/lvm-devices-import.service
Normal file
12
scripts/lvm-devices-import.service
Normal file
@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Create lvm system.devices
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=no
|
||||
ExecStart=/usr/sbin/vgimportdevices --rootvg --auto
|
||||
ConditionPathExists=!/etc/lvm/devices/system.devices
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@ -94,6 +94,9 @@ arg(atversion_ARG, '\0', "atversion", string_VAL, 0, 0,
|
||||
"which does not contain any newer settings for which LVM would\n"
|
||||
"issue a warning message when checking the configuration.\n")
|
||||
|
||||
arg(auto_ARG, '\0', "auto", 0, 0, 0,
|
||||
"Manage files for auto device import.\n")
|
||||
|
||||
arg(autoactivation_ARG, '\0', "autoactivation", string_VAL, 0, 0,
|
||||
"Specify if autoactivation is being used from an event.\n"
|
||||
"This allows the command to apply settings that are specific\n"
|
||||
@ -754,6 +757,9 @@ arg(resync_ARG, '\0', "resync", 0, 0, 0,
|
||||
"which the LV is without a complete redundant copy of the data.\n"
|
||||
"See \\fBlvmraid\\fP(7) for more information.\n")
|
||||
|
||||
arg(rootvg_ARG, '\0', "rootvg", 0, 0, 0,
|
||||
"Import devices used for the root VG.\n")
|
||||
|
||||
arg(rows_ARG, '\0', "rows", 0, 0, 0,
|
||||
"Output columns as rows.\n")
|
||||
|
||||
|
@ -1907,6 +1907,11 @@ OO: --foreign, --reportformat ReportFmt
|
||||
ID: vgimportdevices_all
|
||||
DESC: Add devices from all accessible VGs to the devices file.
|
||||
|
||||
vgimportdevices --rootvg
|
||||
OO: --auto, --reportformat ReportFmt
|
||||
ID: vgimportdevices_root
|
||||
DESC: Add devices from root VG to the devices file.
|
||||
|
||||
---
|
||||
|
||||
vgmerge VG VG
|
||||
|
@ -495,7 +495,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, 1)) {
|
||||
if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1, NULL)) {
|
||||
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
|
||||
pp->activate_errors++;
|
||||
}
|
||||
@ -755,7 +755,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, 1)) {
|
||||
if (!vgchange_activate(cmd, vg, CHANGE_AAY, 1, NULL)) {
|
||||
log_error_pvscan(cmd, "%s: autoactivation failed.", vg->name);
|
||||
pp->activate_errors++;
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ int mirror_remove_missing(struct cmd_context *cmd,
|
||||
|
||||
|
||||
int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
activation_change_t activate, int vg_complete_to_activate);
|
||||
activation_change_t activate, int vg_complete_to_activate, char *root_dm_uuid);
|
||||
|
||||
int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg);
|
||||
|
||||
|
155
tools/vgchange.c
155
tools/vgchange.c
@ -16,11 +16,14 @@
|
||||
#include "tools.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lib/label/hints.h"
|
||||
#include "device_mapper/misc/dm-ioctl.h"
|
||||
#include <mntent.h>
|
||||
|
||||
struct vgchange_params {
|
||||
int lock_start_count;
|
||||
unsigned int lock_start_sanlock : 1;
|
||||
unsigned int vg_complete_to_activate : 1;
|
||||
char *root_dm_uuid; /* dm uuid of LV under root fs */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -197,7 +200,7 @@ int vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg
|
||||
}
|
||||
|
||||
int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
activation_change_t activate, int vg_complete_to_activate)
|
||||
activation_change_t activate, int vg_complete_to_activate, char *root_dm_uuid)
|
||||
{
|
||||
int lv_open, active, monitored = 0, r = 1;
|
||||
const struct lv_list *lvl;
|
||||
@ -279,6 +282,43 @@ int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
r = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Possibly trigger auto-generation of system.devices:
|
||||
* - if root_dm_uuid contains vg->id, and
|
||||
* - /etc/lvm/devices/auto-import-rootvg exists, and
|
||||
* - /etc/lvm/devices/system.devices does not exist, then
|
||||
* - create /run/lvm/lvm-devices-import to
|
||||
* trigger lvm-devices-import.path and .service
|
||||
* - lvm-devices-import will run vgimportdevices --rootvg
|
||||
* to create system.devices
|
||||
*/
|
||||
if (root_dm_uuid) {
|
||||
char path[PATH_MAX];
|
||||
struct stat info;
|
||||
FILE *fp;
|
||||
|
||||
if (memcmp(root_dm_uuid + 4, &vg->id, ID_LEN))
|
||||
goto out;
|
||||
|
||||
if (cmd->enable_devices_file || devices_file_exists(cmd))
|
||||
goto out;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/devices/auto-import-rootvg", cmd->system_dir) < 0)
|
||||
goto out;
|
||||
|
||||
if (stat(path, &info) < 0)
|
||||
goto out;
|
||||
|
||||
log_debug("Found %s creating %s", path, DEVICES_IMPORT_PATH);
|
||||
|
||||
if (!(fp = fopen(DEVICES_IMPORT_PATH, "w"))) {
|
||||
log_debug("failed to create %s", DEVICES_IMPORT_PATH);
|
||||
goto out;
|
||||
}
|
||||
if (fclose(fp))
|
||||
stack;
|
||||
}
|
||||
out:
|
||||
/* Print message only if there was not found a missing VG */
|
||||
log_print_unless_silent("%d logical volume(s) in volume group \"%s\" now active",
|
||||
lvs_in_vg_activated(vg), vg->name);
|
||||
@ -714,7 +754,7 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
|
||||
|
||||
if (arg_is_set(cmd, activate_ARG)) {
|
||||
activate = (activation_change_t) arg_uint_value(cmd, activate_ARG, 0);
|
||||
if (!vgchange_activate(cmd, vg, activate, vp->vg_complete_to_activate))
|
||||
if (!vgchange_activate(cmd, vg, activate, vp->vg_complete_to_activate, vp->root_dm_uuid))
|
||||
return_ECMD_FAILED;
|
||||
} else if (arg_is_set(cmd, refresh_ARG)) {
|
||||
/* refreshes the visible LVs (which starts polling) */
|
||||
@ -735,6 +775,115 @@ static int _vgchange_single(struct cmd_context *cmd, const char *vg_name,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Automatic creation of system.devices for root VG on first boot
|
||||
* is useful for OS images where the OS installer is not used to
|
||||
* customize the OS for system.
|
||||
*
|
||||
* - OS image prep:
|
||||
* . rm /etc/lvm/devices/system.devices (if it exists)
|
||||
* . touch /etc/lvm/devices/auto-import-rootvg
|
||||
* . enable lvm-devices-import.path
|
||||
* . enable lvm-devices-import.service
|
||||
*
|
||||
* - lvchange -ay <rootvg>/<rootlv>
|
||||
* . run by initrd so root fs can be mounted
|
||||
* . does not use system.devices
|
||||
* . named <rootvg>/<rootlv> comes from kernel command line rd.lvm
|
||||
* . uses first device that appears containing the named root LV
|
||||
*
|
||||
* - vgchange -aay <rootvg>
|
||||
* . triggered by udev when all PVs from root VG are online
|
||||
* . activate LVs in root VG (in addition to the already active root LV)
|
||||
* . check for /etc/lvm/devices/auto-import-rootvg (found)
|
||||
* . check for /etc/lvm/devices/system.devices (not found)
|
||||
* . create /run/lvm/lvm-devices-import because
|
||||
* auto-import-rootvg was found and system.devices was not found
|
||||
*
|
||||
* - lvm-devices-import.path
|
||||
* . triggered by /run/lvm/lvm-devices-import
|
||||
* . start lvm-devices-import.service
|
||||
*
|
||||
* - lvm-devices-import.service
|
||||
* . check for /etc/lvm/devices/system.devices, do nothing if found
|
||||
* . run vgimportdevices --rootvg --auto
|
||||
*
|
||||
* - vgimportdevices --rootvg --auto
|
||||
* . check for /etc/lvm/devices/auto-import-rootvg (found)
|
||||
* . check for /etc/lvm/devices/system.devices (not found)
|
||||
* . creates /etc/lvm/devices/system.devices for PVs in root VG
|
||||
* . removes /etc/lvm/devices/auto-import-rootvg
|
||||
* . removes /run/lvm/lvm-devices-import
|
||||
*
|
||||
* On future startup, /etc/lvm/devices/system.devices will exist,
|
||||
* and /etc/lvm/devices/auto-import-rootvg will not exist, so
|
||||
* vgchange -aay <rootvg> will not create /run/lvm/lvm-devices-import,
|
||||
* and lvm-devices-import.path and lvm-device-import.service will not run.
|
||||
*
|
||||
* lvm-devices-import.path:
|
||||
* [Path]
|
||||
* PathExists=/run/lvm/lvm-devices-import
|
||||
* Unit=lvm-devices-import.service
|
||||
* ConditionPathExists=!/etc/lvm/devices/system.devices
|
||||
*
|
||||
* lvm-devices-import.service:
|
||||
* [Service]
|
||||
* Type=oneshot
|
||||
* RemainAfterExit=no
|
||||
* ExecStart=/usr/sbin/vgimportdevices --rootvg --auto
|
||||
* ConditionPathExists=!/etc/lvm/devices/system.devices
|
||||
*/
|
||||
|
||||
static void _get_rootvg_dev(struct cmd_context *cmd, char **dm_uuid_out)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char dm_uuid[DM_UUID_LEN];
|
||||
struct stat info;
|
||||
FILE *fme = NULL;
|
||||
struct mntent *me;
|
||||
int found = 0;
|
||||
|
||||
if (cmd->enable_devices_file || devices_file_exists(cmd))
|
||||
return;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/devices/auto-import-rootvg", cmd->system_dir) < 0)
|
||||
return;
|
||||
|
||||
if (stat(path, &info) < 0)
|
||||
return;
|
||||
|
||||
if (!(fme = setmntent("/etc/mtab", "r")))
|
||||
return;
|
||||
|
||||
while ((me = getmntent(fme))) {
|
||||
if ((me->mnt_dir[0] == '/') && (me->mnt_dir[1] == '\0')) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
endmntent(fme);
|
||||
|
||||
if (!found)
|
||||
return;
|
||||
|
||||
if (stat(me->mnt_dir, &info) < 0)
|
||||
return;
|
||||
|
||||
if (!get_dm_uuid_from_sysfs(dm_uuid, sizeof(dm_uuid), (int)MAJOR(info.st_dev), (int)MINOR(info.st_dev)))
|
||||
return;
|
||||
|
||||
log_debug("Found root dm_uuid %s", dm_uuid);
|
||||
|
||||
/* UUID_PREFIX = "LVM-" */
|
||||
if (strncmp(dm_uuid, UUID_PREFIX, 4))
|
||||
return;
|
||||
|
||||
if (strlen(dm_uuid) < 4 + ID_LEN)
|
||||
return;
|
||||
|
||||
*dm_uuid_out = dm_pool_strdup(cmd->mem, dm_uuid);
|
||||
}
|
||||
|
||||
static int _vgchange_autoactivation_setup(struct cmd_context *cmd,
|
||||
struct vgchange_params *vp,
|
||||
int *skip_command,
|
||||
@ -778,6 +927,8 @@ static int _vgchange_autoactivation_setup(struct cmd_context *cmd,
|
||||
|
||||
get_single_vgname_cmd_arg(cmd, NULL, &vgname);
|
||||
|
||||
_get_rootvg_dev(cmd, &vp->root_dm_uuid);
|
||||
|
||||
/*
|
||||
* Lock the VG before scanning the PVs so _vg_read can avoid the normal
|
||||
* lock_vol+rescan (READ_WITHOUT_LOCK avoids the normal lock_vol and
|
||||
|
@ -15,11 +15,16 @@
|
||||
#include "tools.h"
|
||||
#include "lib/cache/lvmcache.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "device_mapper/misc/dm-ioctl.h"
|
||||
/* coverity[unnecessary_header] needed for MuslC */
|
||||
#include <sys/file.h>
|
||||
#include <mntent.h>
|
||||
|
||||
struct vgimportdevices_params {
|
||||
uint32_t added_devices;
|
||||
int root_vg_found;
|
||||
char *root_dm_uuid;
|
||||
char *root_vg_name;
|
||||
};
|
||||
|
||||
static int _vgimportdevices_single(struct cmd_context *cmd,
|
||||
@ -35,6 +40,13 @@ static int _vgimportdevices_single(struct cmd_context *cmd,
|
||||
int updated_pvs = 0;
|
||||
const char *idtypestr = NULL; /* deviceidtype_ARG ? */
|
||||
|
||||
if (vp->root_dm_uuid) {
|
||||
if (memcmp(vp->root_dm_uuid + 4, &vg->id, ID_LEN))
|
||||
return ECMD_PROCESSED;
|
||||
vp->root_vg_found = 1;
|
||||
vp->root_vg_name = dm_pool_strdup(cmd->mem, vg_name);
|
||||
}
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (is_missing_pv(pvl->pv) || !pvl->pv->dev) {
|
||||
memcpy(pvid, &pvl->pv->id.uuid, ID_LEN);
|
||||
@ -86,6 +98,80 @@ static int _vgimportdevices_single(struct cmd_context *cmd,
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
static int _get_rootvg_dev(struct cmd_context *cmd, char **dm_uuid_out, int *skip)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char dm_uuid[DM_UUID_LEN];
|
||||
struct stat info;
|
||||
FILE *fme = NULL;
|
||||
struct mntent *me;
|
||||
int found = 0;
|
||||
|
||||
/*
|
||||
* When --auto is set, the command does nothing
|
||||
* if /etc/lvm/devices/system.devices exists, or
|
||||
* if /etc/lvm/devices/auto-import-rootvg does not exist.
|
||||
*/
|
||||
if (arg_is_set(cmd, auto_ARG)) {
|
||||
if (devices_file_exists(cmd)) {
|
||||
*skip = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/devices/auto-import-rootvg", cmd->system_dir) < 0)
|
||||
return_0;
|
||||
|
||||
if (stat(path, &info) < 0) {
|
||||
*skip = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(fme = setmntent("/etc/mtab", "r")))
|
||||
return_0;
|
||||
|
||||
while ((me = getmntent(fme))) {
|
||||
if ((me->mnt_dir[0] == '/') && (me->mnt_dir[1] == '\0')) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
endmntent(fme);
|
||||
|
||||
if (!found)
|
||||
return_0;
|
||||
|
||||
if (stat(me->mnt_dir, &info) < 0)
|
||||
return_0;
|
||||
|
||||
if (!get_dm_uuid_from_sysfs(dm_uuid, sizeof(dm_uuid), (int)MAJOR(info.st_dev), (int)MINOR(info.st_dev)))
|
||||
return_0;
|
||||
|
||||
/* UUID_PREFIX = "LVM-" */
|
||||
if (strncmp(dm_uuid, UUID_PREFIX, 4))
|
||||
return_0;
|
||||
|
||||
if (strlen(dm_uuid) < 4 + ID_LEN)
|
||||
return_0;
|
||||
|
||||
*dm_uuid_out = dm_pool_strdup(cmd->mem, dm_uuid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _clear_rootvg_auto(struct cmd_context *cmd)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s/devices/auto-import-rootvg", cmd->system_dir) < 0)
|
||||
return;
|
||||
|
||||
if (unlink(path) < 0)
|
||||
log_debug("Failed to unlink %s", path);
|
||||
|
||||
if (unlink(DEVICES_IMPORT_PATH) < 0)
|
||||
log_debug("Failed to unlink %s", DEVICES_IMPORT_PATH);
|
||||
}
|
||||
|
||||
/*
|
||||
* This command always scans all devices on the system,
|
||||
* any pre-existing devices_file does not limit the scope.
|
||||
@ -130,6 +216,19 @@ int vgimportdevices(struct cmd_context *cmd, int argc, char **argv)
|
||||
/* So that we can warn about this. */
|
||||
cmd->handles_missing_pvs = 1;
|
||||
|
||||
/* Import devices for the root VG. */
|
||||
if (arg_is_set(cmd, rootvg_ARG)) {
|
||||
int skip = 0;
|
||||
if (!_get_rootvg_dev(cmd, &vp.root_dm_uuid, &skip)) {
|
||||
log_error("Failed to find root VG.");
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
if (skip) {
|
||||
log_print("Root VG auto import is not enabled.");
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lock_global(cmd, "ex"))
|
||||
return ECMD_FAILED;
|
||||
|
||||
@ -230,7 +329,13 @@ int vgimportdevices(struct cmd_context *cmd, int argc, char **argv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (vp.root_vg_found)
|
||||
log_print("Added %u devices to devices file for root VG %s.", vp.added_devices, vp.root_vg_name);
|
||||
else
|
||||
log_print("Added %u devices to devices file.", vp.added_devices);
|
||||
|
||||
if (vp.root_vg_found && arg_is_set(cmd, auto_ARG))
|
||||
_clear_rootvg_auto(cmd);
|
||||
out:
|
||||
if ((ret == ECMD_FAILED) && created_file)
|
||||
if (unlink(cmd->devices_file_path) < 0)
|
||||
|
Loading…
Reference in New Issue
Block a user