mirror of
git://sourceware.org/git/lvm2.git
synced 2025-11-09 04:23:47 +03:00
Compare commits
1 Commits
dev-mcsont
...
dev-mcsont
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b51b031157 |
16
Makefile.in
16
Makefile.in
@@ -59,8 +59,6 @@ liblvm: lib
|
|||||||
daemons: lib libdaemon tools
|
daemons: lib libdaemon tools
|
||||||
tools: lib libdaemon device-mapper
|
tools: lib libdaemon device-mapper
|
||||||
po: tools daemons
|
po: tools daemons
|
||||||
man: tools
|
|
||||||
all_man: tools
|
|
||||||
scripts: liblvm libdm
|
scripts: liblvm libdm
|
||||||
|
|
||||||
lib.device-mapper: include.device-mapper
|
lib.device-mapper: include.device-mapper
|
||||||
@@ -97,10 +95,10 @@ endif
|
|||||||
DISTCLEAN_TARGETS += cscope.out
|
DISTCLEAN_TARGETS += cscope.out
|
||||||
CLEAN_DIRS += autom4te.cache
|
CLEAN_DIRS += autom4te.cache
|
||||||
|
|
||||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit: all
|
check check_system check_cluster check_local check_lvmetad check_lvmpolld unit: all
|
||||||
$(MAKE) -C test $(@)
|
$(MAKE) -C test $(@)
|
||||||
|
|
||||||
conf.generate man.generate: tools
|
conf.generate: tools
|
||||||
|
|
||||||
# how to use parenthesis in makefiles
|
# how to use parenthesis in makefiles
|
||||||
leftparen:=(
|
leftparen:=(
|
||||||
@@ -130,9 +128,8 @@ rpm: dist
|
|||||||
$(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc
|
$(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc
|
||||||
rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec
|
rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec
|
||||||
|
|
||||||
generate: conf.generate man.generate
|
generate: conf.generate
|
||||||
$(MAKE) -C conf generate
|
$(MAKE) -C conf generate
|
||||||
$(MAKE) -C man generate
|
|
||||||
|
|
||||||
all_man:
|
all_man:
|
||||||
$(MAKE) -C man all_man
|
$(MAKE) -C man all_man
|
||||||
@@ -169,11 +166,8 @@ install_tmpfiles_configuration:
|
|||||||
|
|
||||||
LCOV_TRACES = libdm.info lib.info liblvm.info tools.info \
|
LCOV_TRACES = libdm.info lib.info liblvm.info tools.info \
|
||||||
libdaemon/client.info libdaemon/server.info \
|
libdaemon/client.info libdaemon/server.info \
|
||||||
daemons/clvmd.info \
|
daemons/clvmd.info daemons/dmeventd.info \
|
||||||
daemons/dmeventd.info \
|
daemons/lvmetad.info
|
||||||
daemons/lvmetad.info \
|
|
||||||
daemons/lvmlockd.info \
|
|
||||||
daemons/lvmpolld.info
|
|
||||||
|
|
||||||
CLEAN_TARGETS += $(LCOV_TRACES)
|
CLEAN_TARGETS += $(LCOV_TRACES)
|
||||||
|
|
||||||
|
|||||||
18
README
18
README
@@ -6,17 +6,11 @@ Installation instructions are in INSTALL.
|
|||||||
There is no warranty - see COPYING and COPYING.LIB.
|
There is no warranty - see COPYING and COPYING.LIB.
|
||||||
|
|
||||||
Tarballs are available from:
|
Tarballs are available from:
|
||||||
ftp://sourceware.org/pub/lvm2/
|
|
||||||
ftp://sources.redhat.com/pub/lvm2/
|
ftp://sources.redhat.com/pub/lvm2/
|
||||||
https://github.com/lvmteam/lvm2/releases
|
|
||||||
|
|
||||||
The source code is stored in git:
|
The source code is stored in git:
|
||||||
https://sourceware.org/git/?p=lvm2.git
|
http://git.fedorahosted.org/git/lvm2.git
|
||||||
git clone git://sourceware.org/git/lvm2.git
|
git clone git://git.fedorahosted.org/git/lvm2.git
|
||||||
mirrored to:
|
|
||||||
https://github.com/lvmteam/lvm2
|
|
||||||
git clone https://github.com/lvmteam/lvm2.git
|
|
||||||
git clone git@github.com:lvmteam/lvm2.git
|
|
||||||
|
|
||||||
Mailing list for general discussion related to LVM2:
|
Mailing list for general discussion related to LVM2:
|
||||||
linux-lvm@redhat.com
|
linux-lvm@redhat.com
|
||||||
@@ -34,14 +28,6 @@ and multipath-tools:
|
|||||||
dm-devel@redhat.com
|
dm-devel@redhat.com
|
||||||
Subscribe from https://www.redhat.com/mailman/listinfo/dm-devel
|
Subscribe from https://www.redhat.com/mailman/listinfo/dm-devel
|
||||||
|
|
||||||
Website:
|
|
||||||
https://sourceware.org/lvm2/
|
|
||||||
|
|
||||||
Report upstream bugs at:
|
|
||||||
https://bugzilla.redhat.com/enter_bug.cgi?product=LVM%20and%20device-mapper
|
|
||||||
or open issues at:
|
|
||||||
https://github.com/lvmteam/lvm2/issues
|
|
||||||
|
|
||||||
The source code repository used until 7th June 2012 is accessible here:
|
The source code repository used until 7th June 2012 is accessible here:
|
||||||
http://sources.redhat.com/cgi-bin/cvsweb.cgi/LVM2/?cvsroot=lvm2.
|
http://sources.redhat.com/cgi-bin/cvsweb.cgi/LVM2/?cvsroot=lvm2.
|
||||||
|
|
||||||
|
|||||||
62
TESTING
62
TESTING
@@ -1,62 +0,0 @@
|
|||||||
LVM2 Test Suite
|
|
||||||
===============
|
|
||||||
|
|
||||||
The codebase contains many tests in the test subdirectory.
|
|
||||||
|
|
||||||
Before running tests
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Keep in mind the testsuite MUST run under root user.
|
|
||||||
|
|
||||||
It is recommended not to use LVM on the test machine, especially when running
|
|
||||||
tests with udev (`make check_system`.)
|
|
||||||
|
|
||||||
You MUST disable (or mask) any LVM daemons:
|
|
||||||
|
|
||||||
- lvmetad
|
|
||||||
- dmeventd
|
|
||||||
- lvmpolld
|
|
||||||
- lvmdbusd
|
|
||||||
- lvmlockd
|
|
||||||
- clvmd
|
|
||||||
- cmirrord
|
|
||||||
|
|
||||||
For running cluster tests, we are using singlenode locking. Pass
|
|
||||||
`--with-clvmd=singlenode` to configure.
|
|
||||||
|
|
||||||
NOTE: This is useful only for testing, and should not be used in produciton
|
|
||||||
code.
|
|
||||||
|
|
||||||
To run D-Bus daemon tests, existing D-Bus session is required.
|
|
||||||
|
|
||||||
Running tests
|
|
||||||
-------------
|
|
||||||
|
|
||||||
As root run:
|
|
||||||
|
|
||||||
make check
|
|
||||||
|
|
||||||
To run only tests matching a string:
|
|
||||||
|
|
||||||
make check T=test
|
|
||||||
|
|
||||||
To skip tests matching a string:
|
|
||||||
|
|
||||||
make check S=test
|
|
||||||
|
|
||||||
There are other targets and many environment variables can be used to tweak the
|
|
||||||
testsuite - for full list and description run `make -C test help`.
|
|
||||||
|
|
||||||
Installing testsuite
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
It is possible to install and run a testsuite against installed LVM. Run the
|
|
||||||
following:
|
|
||||||
|
|
||||||
make -C test install
|
|
||||||
|
|
||||||
Then lvm2-testsuite binary can be executed to test installed binaries.
|
|
||||||
|
|
||||||
See `lvm2-testsuite --help` for options. The same environment variables can be
|
|
||||||
used as with `make check`.
|
|
||||||
|
|
||||||
@@ -1 +1 @@
|
|||||||
1.02.146-git (2017-11-03)
|
1.02.117-git (2016-02-15)
|
||||||
|
|||||||
611
WHATS_NEW
611
WHATS_NEW
@@ -1,614 +1,5 @@
|
|||||||
Version 2.02.177 -
|
Version 2.02.143 -
|
||||||
====================================
|
|
||||||
When writing text metadata content, use complete 4096 byte blocks.
|
|
||||||
Change text format metadata alignment from 512 to 4096 bytes.
|
|
||||||
When writing metadata, consistently skip mdas marked as failed.
|
|
||||||
Refactor and adjust text format metadata alignment calculation.
|
|
||||||
Fix python3 path in lvmdbusd to use value detected by configure.
|
|
||||||
Reduce checks for active LVs in vgchange before background polling.
|
|
||||||
Ensure _node_send_message always uses clean status of thin pool.
|
|
||||||
Fix lvmlockd to use pool lock when accessing _tmeta volume.
|
|
||||||
Report expected sanlock_convert errors only when retries fail.
|
|
||||||
Avoid blocking in sanlock_convert on SH to EX lock conversion.
|
|
||||||
Deactivate missing raid LV legs (_rimage_X-missing_Y_Z) on decativation.
|
|
||||||
Skip read-modify-write when entire block is replaced.
|
|
||||||
Categorise I/O with reason annotations in debug messages.
|
|
||||||
Allow extending of raid LVs created with --nosync after a failed repair.
|
|
||||||
Command will lock memory only when suspending volumes.
|
|
||||||
Merge segments when pvmove is finished.
|
|
||||||
Remove label_verify that has never been used.
|
|
||||||
Ensure very large numbers used as arguments are not casted to lower values.
|
|
||||||
Enhance reading and validation of options stripes and stripes_size.
|
|
||||||
Fix printing of default stripe size when user is not using stripes.
|
|
||||||
Activation code for pvmove automatically discovers holding LVs for resume.
|
|
||||||
Make a pvmove LV locking holder.
|
|
||||||
Do not change critical section counter on resume path without real resume.
|
|
||||||
Enhance activation code to automatically suspend pvmove participants.
|
|
||||||
Prevent conversion of thin volumes to snapshot origin when lvmlockd is used.
|
|
||||||
Correct the steps to change lock type in lvmlockd man page.
|
|
||||||
Retry lock acquisition on recognized sanlock errors.
|
|
||||||
Fix lock manager error codes in lvmlockd.
|
|
||||||
Remove unnecessary single read from lvmdiskscan.
|
|
||||||
Check raid reshape flags in vg_validate().
|
|
||||||
Add support for pvmove of cache and snapshot origins.
|
|
||||||
Avoid using precommitted metadata for suspending pvmove tree.
|
|
||||||
Ehnance pvmove locking.
|
|
||||||
Deactivate activated LVs on error path when pvmove activation fails.
|
|
||||||
Add "io" to log/debug_classes for logging low-level I/O.
|
|
||||||
Eliminate redundant nested VG metadata in VG struct.
|
|
||||||
Avoid importing persistent filter in vgscan/pvscan/vgrename.
|
|
||||||
Fix memleak of string buffer when vgcfgbackup runs in secure mode.
|
|
||||||
Do not print error when clvmd cannot find running clvmd.
|
|
||||||
Prevent start of new merge of snapshot if origin is already being merged.
|
|
||||||
Fix offered type for raid6_n_6 to raid5 conversion (raid5_n).
|
|
||||||
Deactivate sub LVs when removing unused cache-pool.
|
|
||||||
Do not take backup with suspended devices.
|
|
||||||
Avoid RAID4 activation on incompatible kernels under all circumstances.
|
|
||||||
Reject conversion request to striped/raid0 on 2-legged raid4/5.
|
|
||||||
|
|
||||||
Version 2.02.176 - 3rd November 2017
|
|
||||||
====================================
|
|
||||||
Keep Install section only in lvm2-{lvmetad,lvmpolld}.socket systemd unit.
|
|
||||||
Fix segfault in lvm_pv_remove in liblvm. (2.02.173)
|
|
||||||
Do not allow storing VG metadata with LV without any segment.
|
|
||||||
Fix printed message when thin snapshot was already merged.
|
|
||||||
Remove created spare LV when creation of thin-pool failed.
|
|
||||||
Avoid reading ignored metadata when mda gets used again.
|
|
||||||
Fix detection of moved PVs in vgsplit. (2.02.175)
|
|
||||||
Ignore --stripes/--stripesize on RAID takeover
|
|
||||||
Improve used paths for generated systemd units and init shells.
|
|
||||||
Disallow creation of snapshot of mirror/raid subLV (was never supported).
|
|
||||||
Fix regression in more advanced vgname extraction in lvconvert (2.02.169).
|
|
||||||
Allow lvcreate to be used for caching of _tdata LV.
|
|
||||||
Avoid internal error when resizing cache type _tdata LV (not yet supported).
|
|
||||||
Show original converted names when lvconverting LV to pool volume.
|
|
||||||
Move lib code used only by liblvm into metadata-liblvm.c.
|
|
||||||
Distinguish between device not found and excluded by filter.
|
|
||||||
Monitor external origin LVs.
|
|
||||||
Remove the replicator code, including configure --with-replicators.
|
|
||||||
Allow lvcreate --type mirror to work with 100%FREE.
|
|
||||||
Improve selection of resource name for complex volume activation lock.
|
|
||||||
Avoid cutting first character of resource name for activation lock.
|
|
||||||
Support for encrypted devices in fsadm.
|
|
||||||
Improve thin pool overprovisioning and repair warning messages.
|
|
||||||
Fix incorrect adjustment of region size on striped RaidLVs.
|
|
||||||
|
|
||||||
Version 2.02.175 - 6th October 2017
|
|
||||||
===================================
|
|
||||||
Use --help with blockdev when checking for --getsize64 support in fsadm.
|
|
||||||
Dump lvmdbusd debug information with SIGUSR1.
|
|
||||||
Fix metadata corruption in vgsplit and vgmerge intermediate states.
|
|
||||||
Add PV_MOVED_VG PV status flag to mark PVs moving between VGs.
|
|
||||||
Fix lvmdbus hang and recognise unknown VG correctly.
|
|
||||||
Improve error messages when command rules fail.
|
|
||||||
Require LV name with pvmove in a shared VG.
|
|
||||||
Allow shared active mirror LVs with lvmlockd, dlm, and cmirrord.
|
|
||||||
Support lvconvert --repair with cache and cachepool volumes.
|
|
||||||
lvconvert --repair respects --poolmetadataspare option.
|
|
||||||
Mark that we don't plan to develop liblvm2app and python bindings any further.
|
|
||||||
Fix thin pool creation in shared VG. (2.02.173)
|
|
||||||
|
|
||||||
Version 2.02.174 - 13th September 2017
|
|
||||||
======================================
|
|
||||||
Prevent raid1 split with trackchanges in a shared VG.
|
|
||||||
Avoid double unlocking of client & lockspace mutexes in lvmlockd.
|
|
||||||
Fix leaking of file descriptor for non-blocking filebased locking.
|
|
||||||
Fix check for 2nd mda at end of disk fits if using pvcreate --restorefile.
|
|
||||||
Use maximum metadataarea size that fits with pvcreate --restorefile.
|
|
||||||
Always clear cached bootloaderarea when wiping label e.g. in pvcreate.
|
|
||||||
Disallow --bootloaderareasize with pvcreate --restorefile.
|
|
||||||
Fix lvmlockd check for running lock managers during lock adoption.
|
|
||||||
Add --withgeneralpreamble and --withlocalpreamble to lvmconfig.
|
|
||||||
Improve makefiles' linking.
|
|
||||||
Fix some paths in generated makefiles to respected configured settings.
|
|
||||||
Add warning when creating thin-pool with zeroing and chunk size >= 512KiB.
|
|
||||||
Introduce exit code 4 EINIT_FAILED to replace -1 when initialisation fails.
|
|
||||||
Add synchronization points with udev during reshape of raid LVs.
|
|
||||||
|
|
||||||
Version 2.02.173 - 20th July 2017
|
|
||||||
=================================
|
|
||||||
Add synchronization points with udev during conversion of raid LVs.
|
|
||||||
Improve --size args validation and report more detailed error message.
|
|
||||||
Initialize debugging mutex before any debug message in clvmd.
|
|
||||||
Log error instead of warn when noticing connection problem with lvmetad.
|
|
||||||
Fix memory leak in lvmetad when working with duplicates.
|
|
||||||
Remove restrictions on reshaping open and clustered raid devices.
|
|
||||||
Add incompatible data_offset to raid metadata to fix reshape activation.
|
|
||||||
Accept 'lvm -h' and 'lvm --help' as well as 'lvm help' for help.
|
|
||||||
Suppress error message from accept() on clean lvmetad shutdown.
|
|
||||||
Tidy clvmd client list processing and fix segfaults.
|
|
||||||
Protect clvmd debug log messages with mutex and add client id.
|
|
||||||
Fix shellcheck reported issues for script files.
|
|
||||||
|
|
||||||
Version 2.02.172 - 28th June 2017
|
|
||||||
=================================
|
|
||||||
Add missing NULL to argv array when spliting cmdline arguments.
|
|
||||||
Add display_percent helper function for printing percent values.
|
|
||||||
lvconvert --repair handles failing raid legs (present but marked 'D'ead).
|
|
||||||
Do not lvdisplay --maps unset settings of cache pool.
|
|
||||||
Fix lvdisplay --maps for cache pool without policy settings.
|
|
||||||
Support aborting of flushing cache LV.
|
|
||||||
Reenable conversion of data and metadata thin-pool volumes to raid.
|
|
||||||
Improve raid status reporting with lvs.
|
|
||||||
No longer necessary to '--force' a repair for RAID1.
|
|
||||||
Linear to RAID1 upconverts now use "recover" sync action, not "resync".
|
|
||||||
Improve lvcreate --cachepool arg validation.
|
|
||||||
Limit maximum size of thin-pool for specific chunk size.
|
|
||||||
Print a warning about in-use PVs with no VG using them.
|
|
||||||
Disable automatic clearing of PVs that look like in-use orphans.
|
|
||||||
Cache format2 flag is now using segment name type field.
|
|
||||||
Support storing status flags via segtype name field.
|
|
||||||
Stop using '--yes' mode when fsadm runs without terminal.
|
|
||||||
Extend validation of filesystems resized by fsadm.
|
|
||||||
Enhance lvconvert automatic settings of possible (raid) LV types.
|
|
||||||
Allow lvchange to change properties on a thin pool data sub LV.
|
|
||||||
Fix lvcreate extent percentage calculation for mirrors.
|
|
||||||
Don't reinstate still-missing devices when correcting inconsistent metadata.
|
|
||||||
Properly handle subshell return codes in fsadm.
|
|
||||||
Disallow cachepool creation with policy cleaner and mode writeback.
|
|
||||||
|
|
||||||
Version 2.02.171 - 3rd May 2017
|
|
||||||
===============================
|
|
||||||
Fix memory warnings by using mempools for command definition processing.
|
|
||||||
Fix running commands from a script file.
|
|
||||||
Add pvcreate prompt when device size doesn't match setphysicalvolumesize.
|
|
||||||
lvconvert - preserve region size on raid1 image count changes
|
|
||||||
Adjust pvresize/pvcreate messages and prompt if underlying dev size differs.
|
|
||||||
raid - sanely handle insufficient space on takeover.
|
|
||||||
Fix configure --enable-notify-dbus status message.
|
|
||||||
Change configure option name prefix from --enable-lockd to --enable-lvmlockd.
|
|
||||||
lvcreate - raise mirror/raid default regionsize to 2MiB
|
|
||||||
Add missing configurable prefix to configuration file installation directory.
|
|
||||||
|
|
||||||
Version 2.02.170 - 13th April 2017
|
|
||||||
==================================
|
|
||||||
Introduce global/fsadm_executable to make fsadm path configurable.
|
|
||||||
Look for limited thin pool metadata size when using 16G metadata.
|
|
||||||
Add lvconvert pool creation rule disallowing options with poolmetadata.
|
|
||||||
Fix lvconvert when the same LV is incorrectly reused in options.
|
|
||||||
Fix lvconvert VG name validation in option values.
|
|
||||||
Fix missing lvmlockd LV locks in lvchange and lvconvert.
|
|
||||||
Fix dmeventd setup for lvchange --poll.
|
|
||||||
Fix use of --poll and --monitor with lvchange and vgchange.
|
|
||||||
Disallow lvconvert of hidden LV to a pool.
|
|
||||||
Ignore --partial option when not used for activation.
|
|
||||||
Allow --activationmode option with lvchange --refresh.
|
|
||||||
Better message on lvconvert --regionsize
|
|
||||||
Allow valid lvconvert --regionsize change
|
|
||||||
Add raid10 alias raid10_near
|
|
||||||
Handle insufficient PVs on lvconvert takeover
|
|
||||||
Fix SIGINT blocking to prevent corrupted metadata
|
|
||||||
Fix systemd unit existence check for lvmconf --services --startstopservices.
|
|
||||||
Check and use PATH_MAX buffers when creating vgrename device paths.
|
|
||||||
|
|
||||||
Version 2.02.169 - 28th March 2017
|
|
||||||
==================================
|
|
||||||
Automatically decide whether '-' in a man page is a hyphen or a minus sign.
|
|
||||||
Add build-time configuration command line to 'lvm version' output.
|
|
||||||
Handle known table line parameter order change in specific raid target vsns.
|
|
||||||
Conditionally reject raid convert to striped/raid0* after reshape.
|
|
||||||
Ensure raid6 upconversion restrictions.
|
|
||||||
Adjust mirror & raid dmeventd plugins for new lvconvert --repair behaviour.
|
|
||||||
Disable lvmetad when lvconvert --repair is run.
|
|
||||||
Remove obsolete lvmchange binary - convert to built-in command.
|
|
||||||
Show more information for cached volumes in lvdisplay [-m].
|
|
||||||
Add option for lvcreate/lvconvert --cachemetadataformat auto|1|2.
|
|
||||||
Support cache segment with configurable metadata format.
|
|
||||||
Add allocation/cache_metadata_format profilable settings.
|
|
||||||
Use function cache_set_params() for both lvcreate and lvconvert.
|
|
||||||
Skip rounding on cache chunk size boudary when create cache LV.
|
|
||||||
Improve cache_set_params support for chunk_size selection.
|
|
||||||
Fix metadata profile allocation/cache_[mode|policy] setting.
|
|
||||||
Fix missing support for using allocation/cache_pool_chunk_size setting.
|
|
||||||
Upstream git moved to https://sourceware.org/git/?p=lvm2
|
|
||||||
Support conversion of raid type, stripesize and number of disks
|
|
||||||
Reject writemostly/writebehind in lvchange during resynchronization.
|
|
||||||
Deactivate active origin first before removal for improved workflow.
|
|
||||||
Fix regression of accepting both --type and -m with lvresize. (2.02.158)
|
|
||||||
Add lvconvert --swapmetadata, new specific way to swap pool metadata LVs.
|
|
||||||
Add lvconvert --startpoll, new specific way to start polling conversions.
|
|
||||||
Add lvconvert --mergethin, new specific way to merge thin snapshots.
|
|
||||||
Add lvconvert --mergemirrors, new specific way to merge split mirrors.
|
|
||||||
Add lvconvert --mergesnapshot, new specific way to combine cow LVs.
|
|
||||||
Split up lvconvert code based on command definitions.
|
|
||||||
Split up lvchange code based on command definitions.
|
|
||||||
Generate help output and man pages from command definitions.
|
|
||||||
Verify all command line items against command definition.
|
|
||||||
Match every command run to one command definition.
|
|
||||||
Specify every allowed command definition/syntax in command-lines.in.
|
|
||||||
Add extra memory page when limiting pthread stack size in clvmd.
|
|
||||||
Support striped/raid0* <-> raid10_near conversions.
|
|
||||||
Support shrinking of RaidLVs.
|
|
||||||
Support region size changes on existing RaidLVs.
|
|
||||||
Avoid parallel usage of cpg_mcast_joined() in clvmd with corosync.
|
|
||||||
Support raid6_{ls,rs,la,ra}_6 segment types and conversions from/to it.
|
|
||||||
Support raid6_n_6 segment type and conversions from/to it.
|
|
||||||
Support raid5_n segment type and conversions from/to it.
|
|
||||||
Support new internal command _dmeventd_thin_command.
|
|
||||||
Introduce new dmeventd/thin_command configurable setting.
|
|
||||||
Use new default units 'r' for displaying sizes.
|
|
||||||
Also unmount mount point on top of MD device if using blkdeactivate -u.
|
|
||||||
Restore check preventing resize of cache type volumes (2.02.158).
|
|
||||||
Add missing udev sync when flushing dirty cache content.
|
|
||||||
vgchange -p accepts only uint32 numbers.
|
|
||||||
Report thin LV date for merged LV when the merge is in progress.
|
|
||||||
Detect if snapshot merge really started before polling for progress.
|
|
||||||
Checking LV for merging origin requires also it has merged snapshot.
|
|
||||||
Extend validation of metadata processing.
|
|
||||||
Enable usage of cached volumes as snapshot origin LV.
|
|
||||||
Fix displayed lv name when splitting snapshot (2.02.146).
|
|
||||||
Warn about command not making metadata backup just once per command.
|
|
||||||
Enable usage of cached volume as thin volume's external origin.
|
|
||||||
Support cache volume activation with -real layer.
|
|
||||||
Improve search of lock-holder for external origin and thin-pool.
|
|
||||||
Support status checking of cache volume used in layer.
|
|
||||||
Avoid shifting by one number of blocks when clearing dirty cache volume.
|
|
||||||
Extend metadata validation of external origin LV use count.
|
|
||||||
Fix dm table when the last user of active external origin is removed.
|
|
||||||
Improve reported lvs status for active external origin volume.
|
|
||||||
Fix table load for splitted RAID LV and require explicit activation.
|
|
||||||
Always active splitted RAID LV exclusively locally.
|
|
||||||
Do not use LV RAID status bit for segment status.
|
|
||||||
Check segtype directly instead of checking RAID in segment status.
|
|
||||||
Reusing exiting code for raid image removal.
|
|
||||||
Fix pvmove leaving -pvmove0 error device in clustered VG.
|
|
||||||
Avoid adding extra '_' at end of raid extracted images or metadata.
|
|
||||||
Optimize another _rmeta clearing code.
|
|
||||||
Fix deactivation of raid orphan devices for clustered VG.
|
|
||||||
Fix lvconvert raid1 to mirror table reload order.
|
|
||||||
Add internal function for separate mirror log preparation.
|
|
||||||
Fix segfault in lvmetad from missing NULL in daemon_reply_simple.
|
|
||||||
Simplify internal _info_run() and use _setup_task_run() for mknod.
|
|
||||||
Better API for internal function _setup_task_run.
|
|
||||||
Avoid using lv_has_target_type() call within lv_info_with_seg_status.
|
|
||||||
Simplify internal lv_info_with_seg_status API.
|
|
||||||
Decide which status is needed in one place for lv_info_with_seg_status.
|
|
||||||
Fix matching of LV segment when checking for it info status.
|
|
||||||
Report log_warn when status cannot be parsed.
|
|
||||||
Test segment type before accessing segment members when checking status.
|
|
||||||
Implement compatible target function for stripe segment.
|
|
||||||
Use status info to report merge failed and snapshot invalid lvs fields.
|
|
||||||
|
|
||||||
Version 2.02.168 - 30th November 2016
|
|
||||||
=====================================
|
=====================================
|
||||||
Display correct sync_percent on large RaidLVs
|
|
||||||
lvmdbusd --blackboxsize <n> added, used to override default size of 16
|
|
||||||
Allow a transiently failed RaidLV to be refreshed
|
|
||||||
Use lv_update_and_reload() inside mirror code where it applies.
|
|
||||||
Preserve mirrored status for temporary layered mirrors.
|
|
||||||
Use transient raid check before repairing raid volume.
|
|
||||||
Implement transient status check for raid volumes.
|
|
||||||
Only log msg as debug if lvm2-lvmdbusd unit missing for D-Bus notification.
|
|
||||||
Avoid duplicated underscore in name of extracted LV image.
|
|
||||||
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.
|
|
||||||
|
|
||||||
Version 2.02.166 - 26th September 2016
|
|
||||||
======================================
|
|
||||||
Fix lvm2-activation-generator to read all LVM2 config sources. (2.02.155)
|
|
||||||
Fix lvchange-rebuild-raid.sh to cope with older target versions.
|
|
||||||
Use dm_config_parse_without_dup_node_check() to speedup metadata reading.
|
|
||||||
Fix lvconvert --repair regression
|
|
||||||
Fix reported origin lv field for cache volumes. (2.02.133)
|
|
||||||
Always specify snapshot cow LV for monitoring not internal LV. (2.02.165)
|
|
||||||
Fix lvchange --discard|--zero for active thin-pool.
|
|
||||||
Enforce 4MiB or 25% metadata free space for thin pool operations.
|
|
||||||
Fix lock-holder device for thin pool with inactive thin volumes.
|
|
||||||
Use --alloc normal for mirror logs even if the mimages were stricter.
|
|
||||||
Use O_DIRECT to gather metadata in lvmdump.
|
|
||||||
Ignore creation_time when checking for matching metadata for lvmetad.
|
|
||||||
Fix possible NULL pointer derefence when checking for monitoring.
|
|
||||||
Add lvmreport(7) man page.
|
|
||||||
Don't install lvmraid(7) man page when raid excluded. (2.02.165)
|
|
||||||
Report 0% as dirty (copy%) for cache without any used block.
|
|
||||||
Fix lvm2api reporting of cache data and metadata percent.
|
|
||||||
Restore reporting of metadata usage for cache volumes (2.02.155).
|
|
||||||
Support raid scrubbing on cache origin LV.
|
|
||||||
|
|
||||||
Version 2.02.165 - 7th September 2016
|
|
||||||
=====================================
|
|
||||||
Add lvmraid(7) man page.
|
|
||||||
Use udev db to check for mpath components before running pvscan for lvmetad.
|
|
||||||
Use lsblk -s and lsblk -O in lvmdump only if these options are supported.
|
|
||||||
Fix number of stripes shown in lvcreate raid10 message when too many.
|
|
||||||
Change lvmdbusd to use new lvm shell facilities.
|
|
||||||
Do not monitor cache-pool metadata when LV is just being cleared.
|
|
||||||
Add allocation/cache_pool_max_chunks to prevent misuse of cache target.
|
|
||||||
Give error not segfault in lvconvert --splitmirrors when PV lies outside LV.
|
|
||||||
Fix typo in report/columns_as_rows config option name recognition (2.02.99).
|
|
||||||
Avoid PV tags when checking allocation against parallel PVs.
|
|
||||||
Disallow mirror conversions of raid10 volumes.
|
|
||||||
Fix dmeventd unmonitoring when segment type (and dso) changes.
|
|
||||||
Don't allow lvconvert --repair on raid0 devices or attempt to monitor them.
|
|
||||||
No longer adjust incorrect number of raid stripes supplied to lvcreate.
|
|
||||||
Move lcm and gcd to lib/misc.
|
|
||||||
Fix vgsplit of external origins. (2.02.162)
|
|
||||||
Prohibit creation of RAID LVs unless VG extent size is at least the page size.
|
|
||||||
Suppress some unnecessary --stripesize parameter warnings.
|
|
||||||
Fix 'pvmove -n name ...' to prohibit collocation of RAID SubLVs
|
|
||||||
|
|
||||||
Version 2.02.164 - 15th August 2016
|
|
||||||
===================================
|
|
||||||
Fix selection of PVs when allocating raid0_meta.
|
|
||||||
Fix sdbus socket leak leading to hang in lvmnotify.
|
|
||||||
Specify max stripes for raid LV types: raid0:64; 1:10; 4,5:63; 6:62; 10:32.
|
|
||||||
Avoid double suffix when naming _rmeta LV paired with _rimage LV.
|
|
||||||
|
|
||||||
Version 2.02.163 - 10th August 2016
|
|
||||||
===================================
|
|
||||||
Add profile for lvmdbusd which uses lvm shell json report output.
|
|
||||||
Restrict in-command modification of some parms in lvm shell.
|
|
||||||
Apply LVM_COMMAND_PROFILE early for lvm shell.
|
|
||||||
Refactor reporting so lvm shell log report collects whole of cmd execution.
|
|
||||||
Support LVM_*_FD envvars to redirect output to file descriptors.
|
|
||||||
Limit use of --corelog and --mirrorlog to mirrors in lvconvert.
|
|
||||||
Reject --nosync option for RAID6 LVs in lvcreate.
|
|
||||||
Do not refresh whole cmd context if profile dropped after processing LVM cmd.
|
|
||||||
Support straightforward lvconvert between striped and raid4 LVs.
|
|
||||||
Support straightforward lvconvert between raid1 and mirror LVs.
|
|
||||||
Report supported conversions when asked for unsupported raid lvconvert.
|
|
||||||
Add "--rebuild PV" option to lvchange to allow for PV selective rebuilds.
|
|
||||||
Preserve existing mirror region size when using --repair.
|
|
||||||
Forbid stripe parameters with lvconvert --repair.
|
|
||||||
Unify stripe size validation into get_stripe_params to catch missing cases.
|
|
||||||
Further lvconvert validation logic refactoring.
|
|
||||||
|
|
||||||
Version 2.02.162 - 28th July 2016
|
|
||||||
=================================
|
|
||||||
Extend vg_validate also to check raid configurations thoroughly.
|
|
||||||
Support lvconvert -Zn also when doing full cache pool conversion.
|
|
||||||
Suppress not zeroing warn when converting to thin LV for non-zeroing tpool.
|
|
||||||
Fix automatic updates of PV extension headers to newest version.
|
|
||||||
Improve lvconvert --trackchanges validation to require --splitmirrors 1.
|
|
||||||
Add note about lastlog built-in command to lvm man page.
|
|
||||||
Fix unrecognised segtype flag message.
|
|
||||||
lvconvert not clears cache pool metadata ONLY with -Zn.
|
|
||||||
Add allocation/raid_stripe_all_devices to reinstate previous behaviour.
|
|
||||||
Create raid stripes across fixed small numbers of PVs instead of all PVs.
|
|
||||||
Enabled lvconvert --uncache to work with partial VG.
|
|
||||||
Disallow lvconvert --replace with raid0* LVs.
|
|
||||||
Fix some lvmetad changed VG metadata notifications that sent uncommitted data.
|
|
||||||
|
|
||||||
Version 2.02.161 - 15th July 2016
|
|
||||||
=================================
|
|
||||||
Prohibit some lvchange/lvresize that were failing on raid0 volumes.
|
|
||||||
Fix segfaults in complex vgsplits. (2.02.159)
|
|
||||||
Reformat unwieldy lvconvert man page.
|
|
||||||
Allow --force to be passed through to pvcreate from vgcreate. (2.02.144)
|
|
||||||
Fix lvresize of filesystem when LV has already right size (2.02.141)
|
|
||||||
New LVM_LOG_FILE_MAX_LINES env var to limit max size of created logs.
|
|
||||||
|
|
||||||
Version 2.02.160 - 6th July 2016
|
|
||||||
================================
|
|
||||||
Minor fixes from coverity.
|
|
||||||
|
|
||||||
Version 2.02.159 - 6th July 2016
|
|
||||||
================================
|
|
||||||
Add raid0_meta segment type that provides metadata space for raid conversions.
|
|
||||||
Fix created link for a used pool for vgmknode.
|
|
||||||
Introduce and use is_power_of_2 macro.
|
|
||||||
Support conversions between striped and raid0 segment types.
|
|
||||||
Add infrastructure for raid takeover lvconvert options.
|
|
||||||
|
|
||||||
Version 2.02.158 - 25th June 2016
|
|
||||||
=================================
|
|
||||||
Add a more efficient native vgimportclone command to replace the script.
|
|
||||||
Make lvmlockd always attempt to connect to lvmetad if no connection exists.
|
|
||||||
Let lvmetad handle new connections after shutdown signal.
|
|
||||||
Disable lvmetad when vgcfgrestore begins and enable it again after.
|
|
||||||
Make pvscan do activation if lvmetad is configured but not running.
|
|
||||||
Fix rescanning the PVs for a single VG when using lvmetad.
|
|
||||||
Pool metadata lvresize uses now same code as resize of normal volume.
|
|
||||||
Preserve monitoring status when updating thin-pool metadata.
|
|
||||||
Return 0 (inactive) when status cannot be queried in _lv_active().
|
|
||||||
Switch to log_warn() for failing activation status query.
|
|
||||||
Replace vgimportclone script with binary.
|
|
||||||
While lvmetad is shutting down, continue handling all connections cleanly.
|
|
||||||
Refactor lvconvert argument handling code.
|
|
||||||
Notify lvmetad when vgcfgrestore changes VG metadata.
|
|
||||||
Add --logonly option to report only cmd log for a command, not other reports.
|
|
||||||
Add log/command_log_selection to configure default selection used on cmd log.
|
|
||||||
Use 'orphan' object type in cmd log for groups to collect PVs not yet in VGs.
|
|
||||||
Add lvm lastlog command for query and display of last cmd's log in lvm shell.
|
|
||||||
Report per-object return codes via cmd log while processing multiple objects.
|
|
||||||
Annotate processing code with log report hooks for per-object command log.
|
|
||||||
Also pass common printed messages (besides warnings and errors) to log report.
|
|
||||||
Log warnings and errors via report during cmd processing if this is enabled.
|
|
||||||
Make it possible to iterate over internal 'orphan' VGs in process_each_vg fn.
|
|
||||||
Make -S|--select option groupable that allows this option to be repeated.
|
|
||||||
Make -O|--sort option groupable that allows this option to be repeated.
|
|
||||||
Add --configreport option to select report for which next options are applied.
|
|
||||||
Add support for priorities on grouping command arguments.
|
|
||||||
Add report/{pvs,vgs,lvs,pvsegs,segs}_{cols,sort}_full to lvm.conf.
|
|
||||||
Add lvm fullreport command for joined PV, VG, LV and segment report per VG.
|
|
||||||
Integrate report group handling and cmd log report into cmd processing code.
|
|
||||||
Add log/report_command_log to lvm.conf to enable or disable cmd log report.
|
|
||||||
Add log/report_output_format to lvm.conf for default report output format.
|
|
||||||
Recognize --reportformat {basic|json} option to select report output format.
|
|
||||||
Add log/command_log_{sort,cols} to lvm.conf to configure command log report.
|
|
||||||
Add log_object_{type,name,id,group,group_id} fields to cmd log.
|
|
||||||
Add log_{seq_num,type,context,message,errno,ret_code} fields to cmd log.
|
|
||||||
Add CMDLOG report type - a separate report type for command logging.
|
|
||||||
|
|
||||||
Version 2.02.157 - 17th June 2016
|
|
||||||
=================================
|
|
||||||
Change pvscan --cache -aay to scan locally if lvmetad fails.
|
|
||||||
|
|
||||||
Version 2.02.156 - 11th June 2016
|
|
||||||
=================================
|
|
||||||
Don't allow duplicate orphan PVs to be used with vgcreate/vgextend/pvcreate.
|
|
||||||
Improve handling of lvmetad update failures.
|
|
||||||
Yes/No prompt accepts '^[ ^t]*([Yy]([Ee]([Ss]|)|)|[Nn]([Oo]|))[ ^t]*$'.
|
|
||||||
If available, also collect output from lsblk command when running lvmdump -s.
|
|
||||||
|
|
||||||
Version 2.02.155 - 3rd June 2016
|
|
||||||
================================
|
|
||||||
Reject PV tags on pvmove cmdline because only 1 PV is supported. (2.02.141)
|
|
||||||
Fix compilation error when building with configure --disable-devmapper.
|
|
||||||
Fix lvmconfig --type diff to display complete diff if config cascade used.
|
|
||||||
Automatically filter out partitioned loop devices with partscan (losetup -P).
|
|
||||||
Fix lvm devtypes internal error if -S used with field name from pvs/vgs/lvs.
|
|
||||||
When reporting Data%,Snap%,Meta%,Cpy%Sync use single ioctl per LV.
|
|
||||||
Add lvseg_percent_with_info_and_seg_status() for percent retrieval.
|
|
||||||
Enhance internal seg_status handling to understand snapshots better.
|
|
||||||
When refresh failed in suspend, call resume upon error path.
|
|
||||||
Support passthrough cache mode when waiting for clean cache.
|
|
||||||
Check cache status only for 'in-use' cache pools.
|
|
||||||
Extend setup_task() to preset flushing for dm_task object.
|
|
||||||
When checking LV is a merging COW, validate its a COW LV first.
|
|
||||||
Correcting value in copy_percent() for 100%.
|
|
||||||
Update vgreduce to use process_each_vg.
|
|
||||||
Update lvconvert to use process_each_lv.
|
|
||||||
Update pvscan to use process_each_vg for autoactivation.
|
|
||||||
Add basic support for --type raid0 using md.
|
|
||||||
Add support for lvchange --cachemode for cached LV.
|
|
||||||
Fix liblvm2app error handling when setting up context.
|
|
||||||
Delay liblvm2app init in python code until it is needed.
|
|
||||||
Simplify thread locking in lvmetad to fix locking problems.
|
|
||||||
Allow pvremove -ff to remove a duplicate PV.
|
|
||||||
Fix lvm2-activation-generator to read lvm.conf without full command setup.
|
|
||||||
Allow a minimal context to be used in lvm2app for reading lvm.conf.
|
|
||||||
|
|
||||||
Version 2.02.154 - 14th May 2016
|
|
||||||
================================
|
|
||||||
Fix liblvm segfault after failure initialising lvmetad connection.
|
|
||||||
Retry open without O_NOATIME if it fails (not file owner/CAP_FOWNER).
|
|
||||||
Split _report into one fn for options and arguments and one for processing.
|
|
||||||
|
|
||||||
Version 2.02.153 - 7th May 2016
|
|
||||||
===============================
|
|
||||||
Change warning messages related to duplicate PVs.
|
|
||||||
A named device is always processed itself, not switched for a duplicate.
|
|
||||||
Add PV attr "d" and report field "duplicate" for duplicate PVs.
|
|
||||||
Add config setting to disallow VG changes when duplicate PVs exist.
|
|
||||||
Use device size and active LVs to choose the preferred duplicate PV.
|
|
||||||
Disable lvmetad when duplicate PVs are seen.
|
|
||||||
Support --chunksize option also when caching LV when possible.
|
|
||||||
Add function to check for target presence and version via 1 ioctl.
|
|
||||||
|
|
||||||
Version 2.02.152 - 30th April 2016
|
|
||||||
==================================
|
|
||||||
Use any inherited tags when wiping metadata sub LVs to ensure activation.
|
|
||||||
Add str_list_wipe.
|
|
||||||
Improve support for interrupting procesing of volumes during lvchange.
|
|
||||||
Use failed command return code when lvchanging read-only volume.
|
|
||||||
Show creation transaction_id and zeroing state of pool with thin volume.
|
|
||||||
Stop checking for dm_cache_mq policy with cache target 1.9 (alias to smq).
|
|
||||||
Check first /sys/module/dm_* dir existance before using modprobe.
|
|
||||||
Remove mpath from 10-dm.rules, superseded by 11-dm-mpath.rules (mpath>=0.6.0).
|
|
||||||
|
|
||||||
Version 2.02.151 - 23rd April 2016
|
|
||||||
==================================
|
|
||||||
Fix error path after reusing of _setup_task (2.02.150).
|
|
||||||
Fix memory access for empty sysfs values (2.02.149).
|
|
||||||
Disable lvmetad when lvm1 metadata is seen, so commands revert to scanning.
|
|
||||||
Suppress errors when snapshot merge gets delayed because volume is in use.
|
|
||||||
Avoid internal snapshot LV names in messages.
|
|
||||||
Autodetect and use /run/lock dir when available instead of /var/lock.
|
|
||||||
lvchange --refresh for merging thin origin will retry to deactivate snapshot.
|
|
||||||
Recognize in-progress snapshot merge for thin volumes from dm table.
|
|
||||||
Avoid deciding to initiate a pending snapshot merge during resume.
|
|
||||||
Improve retrying lvmetad requests while lvmetad is being updated.
|
|
||||||
Read devices instead of using the lvmetad cache if rescan fails.
|
|
||||||
Move lvmetad token/filter check and device rescan to the start of commands.
|
|
||||||
Don't try deactivating fictional internal LV before snapshot merge. (2.02.105)
|
|
||||||
When not obtaining devs from udev, check they exist before caching them.
|
|
||||||
Detect device mismatch also when compiling without udev support.
|
|
||||||
|
|
||||||
Version 2.02.150 - 9th April 2016
|
|
||||||
=================================
|
|
||||||
Avoid using flushing dm status ioctl when checking for usable DM device.
|
|
||||||
Check for devices without LVM- uuid prefix only with kernels < 3.X.
|
|
||||||
Reuse %FREE size aproximation with lvcreate -l%PVS thin-pool.
|
|
||||||
Allow the lvmdump directory to exist already provided it is empty.
|
|
||||||
Show lvconverted percentage with 2 decimal digits.
|
|
||||||
Fix regression in suspend when repairing --type mirror (2.02.133).
|
|
||||||
|
|
||||||
Version 2.02.149 - 1st April 2016
|
|
||||||
=================================
|
|
||||||
Do not flush thin-pool when checking metadata fullness.
|
|
||||||
Remove spurious error about no value in /sys/dev/block/major:minor/dm/uuid.
|
|
||||||
Fix device mismatch detection for LV if persistent .cache file is used.
|
|
||||||
Fix holder device not being found in /dev while sysfs has it during dev scan.
|
|
||||||
|
|
||||||
Version 2.02.148 - 26th March 2016
|
|
||||||
==================================
|
|
||||||
Introduce TARGET_NAME and MODULE NAME macros.
|
|
||||||
Replace hard-coded module and target names with macros.
|
|
||||||
Add pv_major and pv_minor report fields.
|
|
||||||
Detect and warn about mismatch between devices used and assumed for an LV.
|
|
||||||
|
|
||||||
Version 2.02.147 - 19th March 2016
|
|
||||||
==================================
|
|
||||||
If available, use /proc/self/mountinfo to detect mounted volume in fsadm.
|
|
||||||
Fix resize of stacked raid thin data volume (2.02.141).
|
|
||||||
Fix test for lvremove failure in lvconvert --uncache (2.02.146).
|
|
||||||
|
|
||||||
Version 2.02.146 - 11th March 2016
|
|
||||||
==================================
|
|
||||||
More man page cleanups in lvconvert.
|
|
||||||
Fix makefile vpath in /udev when generating udev rules files.
|
|
||||||
Another attempt to improve VG name parsing for lvconvert (2.02.144).
|
|
||||||
Use new cache status info and skip flushing for failed cache.
|
|
||||||
Support --uncache with missing PVs.
|
|
||||||
Tidy report field names, headings and widths.
|
|
||||||
Add vgscan --notifydbus to send a dbus notification.
|
|
||||||
Add dbus notification from commands after a PV/VG/LV changes state.
|
|
||||||
|
|
||||||
Version 2.02.145 - 4th March 2016
|
|
||||||
=================================
|
|
||||||
Make it possible to use lvremove and lvrename on historical LVs.
|
|
||||||
For historical LVs, report 'none' for lv_layout and 'history' for lv_role.
|
|
||||||
Add full_{ancestors,descendants} fields to report LV ancestry with history.
|
|
||||||
Report (h)istorical state within 5th bit (State) of the lv_attr field.
|
|
||||||
Add lv_historical reporting field to report if LV is historical or not.
|
|
||||||
Add lv_time_removed reporting field to display removal time for hist. LVs.
|
|
||||||
Report lv_name, lv_uuid, vg_name, lv_time for historical LVs.
|
|
||||||
Add --nohistory switch to lvremove to disable history recording on demand.
|
|
||||||
Add -H|--history switch to lvs and lvdisplay to include historical LVs.
|
|
||||||
Create historical LVs out of removed thin snapshot LVs and record in history.
|
|
||||||
Add metadata/lvs_history_retention_time for automatic removal of hist. LVs.
|
|
||||||
Add metadata/record_lvs_history config for switching LV history recording.
|
|
||||||
Add support and infrastructure for tracking historical LVs.
|
|
||||||
Improve lvconvert man page.
|
|
||||||
Add kernel_cache_policy lvs field.
|
|
||||||
Display [unknown] instead of 'unknown device' in pvs output.
|
|
||||||
Fix error path when pvcreate allocation fails (2.02.144).
|
|
||||||
Display [unknown] instead of blank for unknown VG names in pvs output.
|
|
||||||
|
|
||||||
Version 2.02.144 - 26th February 2016
|
|
||||||
=====================================
|
|
||||||
Use new PV processing code in pvcreate/vgcreate/vgextend/pvremove.
|
|
||||||
Add new PV processing code that prompts user without locks held.
|
|
||||||
Prevent lvmlockd blocking with new flag requiring sanlock 3.3.0.
|
|
||||||
Only show (u)sed pv_attr char when PV is not (a)llocatable. (2.02.143)
|
|
||||||
Update makefile to generate lcov output also for lvmpolld and lvmlockd.
|
|
||||||
Fix SystemdService lvm2-lvmdbusd.service name.
|
|
||||||
Improve support for env LVM_VG_NAME for reference VG name in lvconvert.
|
|
||||||
Fix regression when lvresize accepted zero sizes. (2.02.141)
|
|
||||||
Always warn user about PV in use even when pvremove uses --force --force.
|
|
||||||
Use uninitialized pool header detection in all cases.
|
|
||||||
Fix read error detection when checking for uninitialized thin-pool header.
|
|
||||||
Fix error path for internal error in lvmetad VG lookup code.
|
|
||||||
|
|
||||||
Version 2.02.143 - 21st February 2016
|
|
||||||
=====================================
|
|
||||||
Fix error path when sending thin-pool message fails in update_pool_lv().
|
|
||||||
Support reporting CheckNeeded and Fail state for thin-pool and thin LV.
|
|
||||||
For failing thin-pool and thin volume correctly report percentage as INVALID.
|
|
||||||
Report -1, not 'unkown' for lv_{snapshot_invalid,merge_failed} with --binary.
|
Report -1, not 'unkown' for lv_{snapshot_invalid,merge_failed} with --binary.
|
||||||
Add configure --enable-dbus-service for an LVM D-Bus service.
|
Add configure --enable-dbus-service for an LVM D-Bus service.
|
||||||
Replace configure --enable-python_bindings with python2 and python3 versions.
|
Replace configure --enable-python_bindings with python2 and python3 versions.
|
||||||
|
|||||||
215
WHATS_NEW_DM
215
WHATS_NEW_DM
@@ -1,216 +1,5 @@
|
|||||||
Version 1.02.146 -
|
Version 1.02.117 -
|
||||||
====================================
|
|
||||||
Activation tree of thin pool skips duplicated check of pool status.
|
|
||||||
Remove code supporting replicator target.
|
|
||||||
Do not ignore failure of _info_by_dev().
|
|
||||||
Propagate delayed resume for pvmove subvolumes.
|
|
||||||
Suppress integrity encryption keys in 'table' output unless --showkeys supplied.
|
|
||||||
|
|
||||||
Version 1.02.145 - 3rd November 2017
|
|
||||||
====================================
|
|
||||||
Keep Install section only in dm-event.socket systemd unit.
|
|
||||||
Issue a specific error with dmsetup status if device is unknown.
|
|
||||||
Fix RT_LIBS reference in generated libdevmapper.pc for pkg-config
|
|
||||||
|
|
||||||
Version 1.02.144 - 6th October 2017
|
|
||||||
===================================
|
|
||||||
Schedule exit when received SIGTERM in dmeventd.
|
|
||||||
Also try to unmount /boot on blkdeactivate -u if on top of supported device.
|
|
||||||
Use blkdeactivate -r wait in blk-availability systemd service/initscript.
|
|
||||||
Add blkdeactivate -r wait option to wait for MD resync/recovery/reshape.
|
|
||||||
Fix blkdeactivate regression with failing DM/MD devs deactivation (1.02.142).
|
|
||||||
Fix typo in blkdeactivate's '--{dm,lvm,mpath}options' option name.
|
|
||||||
Correct return value testing when get reserved values for reporting.
|
|
||||||
Take -S with dmsetup suspend/resume/clear/wipe_table/remove/deps/status/table.
|
|
||||||
|
|
||||||
Version 1.02.143 - 13th September 2017
|
|
||||||
======================================
|
|
||||||
Restore umask when creation of node fails.
|
|
||||||
Add --concise to dmsetup create for many devices with tables in one command.
|
|
||||||
Accept minor number without major in library when it knows dm major number.
|
|
||||||
Introduce single-line concise table output format: dmsetup table --concise
|
|
||||||
|
|
||||||
Version 1.02.142 - 20th July 2017
|
|
||||||
=================================
|
|
||||||
Create /dev/disk/by-part{uuid,label} and gpt-auto-root symlinks with udev.
|
|
||||||
|
|
||||||
Version 1.02.141 - 28th June 2017
|
|
||||||
=================================
|
|
||||||
Fix reusing of dm_task structure for status reading (used by dmeventd).
|
|
||||||
Add dm_percent_to_round_float for adjusted percentage rounding.
|
|
||||||
Reset array with dead rimage devices once raid gets in sync.
|
|
||||||
Drop unneeded --config option from raid dmeventd plugin.
|
|
||||||
dm_get_status_raid() handle better some incosistent md statuses.
|
|
||||||
Accept truncated files in calls to dm_stats_update_regions_from_fd().
|
|
||||||
Restore Warning by 5% increment when thin-pool is over 80% (1.02.138).
|
|
||||||
|
|
||||||
Version 1.02.140 - 3rd May 2017
|
|
||||||
===============================
|
|
||||||
Add missing configure --enable-dmfilemapd status message and fix --disable.
|
|
||||||
|
|
||||||
Version 1.02.139 - 13th April 2017
|
|
||||||
==================================
|
|
||||||
Fix assignment in _target_version() when dm task can't run.
|
|
||||||
Flush stdout on each iteration when using --count or --interval.
|
|
||||||
Show detailed error message when execvp fails while starting dmfilemapd.
|
|
||||||
Fix segmentation fault when dmfilemapd is run with no arguments.
|
|
||||||
Numerous minor dmfilemapd fixes from coverity.
|
|
||||||
|
|
||||||
Version 1.02.138 - 28th March 2017
|
|
||||||
==================================
|
|
||||||
Support additional raid5/6 configurations.
|
|
||||||
Provide dm_tree_node_add_cache_target@base compatible symbol.
|
|
||||||
Support DM_CACHE_FEATURE_METADATA2, new cache metadata format 2.
|
|
||||||
Improve code to handle mode mask for cache nodes.
|
|
||||||
Cache status check for passthrough also require trailing space.
|
|
||||||
Add extra memory page when limiting pthread stack size in dmeventd.
|
|
||||||
Avoids immediate resume when preloaded device is smaller.
|
|
||||||
Do not suppress kernel key description in dmsetup table output for dm-crypt.
|
|
||||||
Support configurable command executed from dmeventd thin plugin.
|
|
||||||
Support new R|r human readable units output format.
|
|
||||||
Thin dmeventd plugin reacts faster on lvextend failure path with umount.
|
|
||||||
Add dm_stats_bind_from_fd() to bind a stats handle from a file descriptor.
|
|
||||||
Do not try call callback when reverting activation on error path.
|
|
||||||
Fix file mapping for extents with physically adjacent extents in dmstats.
|
|
||||||
Validation vsnprintf result in runtime translate of dm_log (1.02.136).
|
|
||||||
Separate filemap extent allocation from region table in dmstats.
|
|
||||||
Fix segmentation fault when filemap region creation fails in dmstats.
|
|
||||||
Fix performance of region cleanup for failed filemap creation in dmstats.
|
|
||||||
Fix very slow region deletion with many regions in dmstats.
|
|
||||||
|
|
||||||
Version 1.02.137 - 30th November 2016
|
|
||||||
=====================================
|
=====================================
|
||||||
Document raid status values.
|
|
||||||
Always exit dmsetup with success when asked to display help/version.
|
|
||||||
|
|
||||||
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.
|
|
||||||
Fix 'dmstats delete' with dmsetup older than v1.02.129
|
|
||||||
Fix stats walk segfault with dmsetup older than v1.02.129
|
|
||||||
|
|
||||||
Version 1.02.135 - 26th September 2016
|
|
||||||
======================================
|
|
||||||
Fix man entry for dmsetup status.
|
|
||||||
Introduce new dm_config_parse_without_dup_node_check().
|
|
||||||
Don't omit last entry in dmstats list --group.
|
|
||||||
|
|
||||||
Version 1.02.134 - 7th September 2016
|
|
||||||
=====================================
|
|
||||||
Improve explanation of udev fallback in libdevmapper.h.
|
|
||||||
|
|
||||||
Version 1.02.133 - 10th August 2016
|
|
||||||
===================================
|
|
||||||
Add dm_report_destroy_rows/dm_report_group_output_and_pop_all for lvm shell.
|
|
||||||
Adjust group handling and json production for lvm shell.
|
|
||||||
|
|
||||||
Version 1.02.132 - 28th July 2016
|
|
||||||
=================================
|
|
||||||
Fix json reporting to escape '"' character that may appear in reported string.
|
|
||||||
|
|
||||||
Version 1.02.131 - 15th July 2016
|
|
||||||
=================================
|
|
||||||
Disable queueing on mpath devs in blk-availability systemd service/initscript.
|
|
||||||
Add new -m|--mpathoption disablequeueing to blkdeactivate.
|
|
||||||
Automatically group regions with 'create --segments' unless --nogroup.
|
|
||||||
Fix resource leak when deleting the first member of a group.
|
|
||||||
Allow --bounds with 'create --filemap' for dmstats.
|
|
||||||
Enable creation of filemap regions with histograms.
|
|
||||||
Enable histogram aggregation for regions with more than one area.
|
|
||||||
Enable histogram aggregation for groups of regions.
|
|
||||||
Add a --filemap option to 'dmstats create' to allow mapping of files.
|
|
||||||
Add dm_stats_create_regions_from_fd() to map file extents to regions.
|
|
||||||
|
|
||||||
Version 1.02.130 - 6th July 2016
|
|
||||||
================================
|
|
||||||
Minor fixes from coverity.
|
|
||||||
|
|
||||||
Version 1.02.129 - 6th July 2016
|
|
||||||
================================
|
|
||||||
Update default dmstats field selections for groups.
|
|
||||||
Add 'obj_type', 'group_id', and 'statsname' fields to dmstats reports.
|
|
||||||
Add --area, --region, and --group to dmstats to control object selection.
|
|
||||||
Add --alias, --groupid, --regions to dmstats for group creation and deletion.
|
|
||||||
Add 'group' and 'ungroup' commands to dmstats.
|
|
||||||
Allow dm_stats_delete_group() to optionally delete all group members.
|
|
||||||
Add dm_stats_get_object_type() to return the type of object present.
|
|
||||||
Add dm_stats_walk_init() allowing control of objects visited by walks.
|
|
||||||
Add dm_stats_get_group_descriptor() to return the member list as a string.
|
|
||||||
Introduce dm_stats_get_nr_groups() and dm_stats_group_present().
|
|
||||||
Add dm_stats_{get,set}_alias() to set and retrieve alias names for groups.
|
|
||||||
Add dm_stats_get_group_id() to return the group ID for a given region.
|
|
||||||
Add dm_stats_{create,delete}_group() to allow grouping of stats regions.
|
|
||||||
Add enum-driven dm_stats_get_{metric,counter}() interfaces.
|
|
||||||
Add dm_bitset_parse_list() to parse a string representation of a bitset.
|
|
||||||
Thin dmeventd plugin umounts lvm2 volume only when pool is 95% or more.
|
|
||||||
|
|
||||||
Version 1.02.128 - 25th June 2016
|
|
||||||
=================================
|
|
||||||
Recognize 'all' keyword used in selection as synonym for "" (no selection).
|
|
||||||
Add dm_report_set_selection to set selection for multiple output of report.
|
|
||||||
Add DM_REPORT_OUTPUT_MULTIPLE_TIMES flag for multiple output of same report.
|
|
||||||
Move field width handling/sort init from dm_report_object to dm_report_output.
|
|
||||||
Add _LOG_BYPASS_REPORT flag for bypassing any log report currently set.
|
|
||||||
Introduce DM_REPORT_GROUP_JSON for report group with JSON output format.
|
|
||||||
Introduce DM_REPORT_GROUP_BASIC for report group with basic report output.
|
|
||||||
Introduce DM_REPORT_GROUP_SINGLE for report group having single report only.
|
|
||||||
Add dm_report_group_{create,push,pop,destroy} to support report grouping.
|
|
||||||
|
|
||||||
Version 1.02.127 - 11th June 2016
|
|
||||||
=================================
|
|
||||||
Fix blkdeactivate regression causing skipping of dm + md devices. (1.02.126)
|
|
||||||
|
|
||||||
Version 1.02.126 - 3rd June 2016
|
|
||||||
================================
|
|
||||||
Report passthrough caching mode when parsing cache mode.
|
|
||||||
|
|
||||||
Version 1.02.125 - 14th May 2016
|
|
||||||
================================
|
|
||||||
Show library version in message even if dm driver version is unavailable.
|
|
||||||
|
|
||||||
Version 1.02.124 - 30th April 2016
|
|
||||||
==================================
|
|
||||||
Add dm_udev_wait_immediate to libdevmapper for waiting outside the library.
|
|
||||||
|
|
||||||
Version 1.02.123 - 23rd April 2016
|
|
||||||
==================================
|
|
||||||
Do not strip LVM- when debug reporting not found uuid.
|
|
||||||
|
|
||||||
Version 1.02.122 - 9th April 2016
|
|
||||||
=================================
|
|
||||||
Change log_debug ioctl flags from single characters into words.
|
|
||||||
|
|
||||||
Version 1.02.121 - 26th March 2016
|
|
||||||
==================================
|
|
||||||
Adjust raid status function.
|
|
||||||
|
|
||||||
Version 1.02.120 - 11th March 2016
|
|
||||||
==================================
|
|
||||||
Improve parsing of cache status and report Fail, Error, needs_check, ro.
|
|
||||||
|
|
||||||
Version 1.02.119 - 4th March 2016
|
|
||||||
=================================
|
|
||||||
Fix dm_config_write_node and variants to return error on subsection failures.
|
|
||||||
Remove 4096 char limit due to buffer size if writing dm_config_node.
|
|
||||||
|
|
||||||
Version 1.02.118 - 26th February 2016
|
|
||||||
=====================================
|
|
||||||
Fix string boundary check in _get_canonical_field_name().
|
|
||||||
Always initialized hist struct in _stats_parse_histogram().
|
|
||||||
|
|
||||||
Version 1.02.117 - 21st February 2016
|
|
||||||
=====================================
|
|
||||||
Improve status parsing for thin-pool and thin devices.
|
|
||||||
|
|
||||||
Version 1.02.116 - 15th February 2016
|
Version 1.02.116 - 15th February 2016
|
||||||
=====================================
|
=====================================
|
||||||
@@ -273,7 +62,7 @@ Version 1.02.110 - 30th October 2015
|
|||||||
Correct use of max_write_behind parameter when generating raid target line.
|
Correct use of max_write_behind parameter when generating raid target line.
|
||||||
Fix dm-event systemd service to make sure it is executed before mounting.
|
Fix dm-event systemd service to make sure it is executed before mounting.
|
||||||
|
|
||||||
Version 1.02.109 - 22nd September 2015
|
Version 1.02.109 - 22nd September 2016
|
||||||
======================================
|
======================================
|
||||||
Update man pages for dmsetup and dmstats.
|
Update man pages for dmsetup and dmstats.
|
||||||
Improve help text for dmsetup.
|
Improve help text for dmsetup.
|
||||||
|
|||||||
171
acinclude.m4
171
acinclude.m4
@@ -61,174 +61,3 @@ AC_DEFUN([AC_TRY_LDFLAGS],
|
|||||||
ifelse([$4], [], [:], [$4])
|
ifelse([$4], [], [:], [$4])
|
||||||
fi
|
fi
|
||||||
])
|
])
|
||||||
|
|
||||||
# ===========================================================================
|
|
||||||
# http://www.gnu.org/software/autoconf-archive/ax_gcc_builtin.html
|
|
||||||
# ===========================================================================
|
|
||||||
#
|
|
||||||
# SYNOPSIS
|
|
||||||
#
|
|
||||||
# AX_GCC_BUILTIN(BUILTIN)
|
|
||||||
#
|
|
||||||
# DESCRIPTION
|
|
||||||
#
|
|
||||||
# This macro checks if the compiler supports one of GCC's built-in
|
|
||||||
# functions; many other compilers also provide those same built-ins.
|
|
||||||
#
|
|
||||||
# The BUILTIN parameter is the name of the built-in function.
|
|
||||||
#
|
|
||||||
# If BUILTIN is supported define HAVE_<BUILTIN>. Keep in mind that since
|
|
||||||
# builtins usually start with two underscores they will be copied over
|
|
||||||
# into the HAVE_<BUILTIN> definition (e.g. HAVE___BUILTIN_EXPECT for
|
|
||||||
# __builtin_expect()).
|
|
||||||
#
|
|
||||||
# The macro caches its result in the ax_cv_have_<BUILTIN> variable (e.g.
|
|
||||||
# ax_cv_have___builtin_expect).
|
|
||||||
#
|
|
||||||
# The macro currently supports the following built-in functions:
|
|
||||||
#
|
|
||||||
# __builtin_assume_aligned
|
|
||||||
# __builtin_bswap16
|
|
||||||
# __builtin_bswap32
|
|
||||||
# __builtin_bswap64
|
|
||||||
# __builtin_choose_expr
|
|
||||||
# __builtin___clear_cache
|
|
||||||
# __builtin_clrsb
|
|
||||||
# __builtin_clrsbl
|
|
||||||
# __builtin_clrsbll
|
|
||||||
# __builtin_clz
|
|
||||||
# __builtin_clzl
|
|
||||||
# __builtin_clzll
|
|
||||||
# __builtin_complex
|
|
||||||
# __builtin_constant_p
|
|
||||||
# __builtin_ctz
|
|
||||||
# __builtin_ctzl
|
|
||||||
# __builtin_ctzll
|
|
||||||
# __builtin_expect
|
|
||||||
# __builtin_ffs
|
|
||||||
# __builtin_ffsl
|
|
||||||
# __builtin_ffsll
|
|
||||||
# __builtin_fpclassify
|
|
||||||
# __builtin_huge_val
|
|
||||||
# __builtin_huge_valf
|
|
||||||
# __builtin_huge_vall
|
|
||||||
# __builtin_inf
|
|
||||||
# __builtin_infd128
|
|
||||||
# __builtin_infd32
|
|
||||||
# __builtin_infd64
|
|
||||||
# __builtin_inff
|
|
||||||
# __builtin_infl
|
|
||||||
# __builtin_isinf_sign
|
|
||||||
# __builtin_nan
|
|
||||||
# __builtin_nand128
|
|
||||||
# __builtin_nand32
|
|
||||||
# __builtin_nand64
|
|
||||||
# __builtin_nanf
|
|
||||||
# __builtin_nanl
|
|
||||||
# __builtin_nans
|
|
||||||
# __builtin_nansf
|
|
||||||
# __builtin_nansl
|
|
||||||
# __builtin_object_size
|
|
||||||
# __builtin_parity
|
|
||||||
# __builtin_parityl
|
|
||||||
# __builtin_parityll
|
|
||||||
# __builtin_popcount
|
|
||||||
# __builtin_popcountl
|
|
||||||
# __builtin_popcountll
|
|
||||||
# __builtin_powi
|
|
||||||
# __builtin_powif
|
|
||||||
# __builtin_powil
|
|
||||||
# __builtin_prefetch
|
|
||||||
# __builtin_trap
|
|
||||||
# __builtin_types_compatible_p
|
|
||||||
# __builtin_unreachable
|
|
||||||
#
|
|
||||||
# Unsuppored built-ins will be tested with an empty parameter set and the
|
|
||||||
# result of the check might be wrong or meaningless so use with care.
|
|
||||||
#
|
|
||||||
# LICENSE
|
|
||||||
#
|
|
||||||
# Copyright (c) 2013 Gabriele Svelto <gabriele.svelto@gmail.com>
|
|
||||||
#
|
|
||||||
# Copying and distribution of this file, with or without modification, are
|
|
||||||
# permitted in any medium without royalty provided the copyright notice
|
|
||||||
# and this notice are preserved. This file is offered as-is, without any
|
|
||||||
# warranty.
|
|
||||||
|
|
||||||
#serial 3
|
|
||||||
|
|
||||||
AC_DEFUN([AX_GCC_BUILTIN], [
|
|
||||||
AS_VAR_PUSHDEF([ac_var], [ax_cv_have_$1])
|
|
||||||
|
|
||||||
AC_CACHE_CHECK([for $1], [ac_var], [
|
|
||||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [
|
|
||||||
m4_case([$1],
|
|
||||||
[__builtin_assume_aligned], [$1("", 0)],
|
|
||||||
[__builtin_bswap16], [$1(0)],
|
|
||||||
[__builtin_bswap32], [$1(0)],
|
|
||||||
[__builtin_bswap64], [$1(0)],
|
|
||||||
[__builtin_choose_expr], [$1(0, 0, 0)],
|
|
||||||
[__builtin___clear_cache], [$1("", "")],
|
|
||||||
[__builtin_clrsb], [$1(0)],
|
|
||||||
[__builtin_clrsbl], [$1(0)],
|
|
||||||
[__builtin_clrsbll], [$1(0)],
|
|
||||||
[__builtin_clz], [$1(0)],
|
|
||||||
[__builtin_clzl], [$1(0)],
|
|
||||||
[__builtin_clzll], [$1(0)],
|
|
||||||
[__builtin_complex], [$1(0.0, 0.0)],
|
|
||||||
[__builtin_constant_p], [$1(0)],
|
|
||||||
[__builtin_ctz], [$1(0)],
|
|
||||||
[__builtin_ctzl], [$1(0)],
|
|
||||||
[__builtin_ctzll], [$1(0)],
|
|
||||||
[__builtin_expect], [$1(0, 0)],
|
|
||||||
[__builtin_ffs], [$1(0)],
|
|
||||||
[__builtin_ffsl], [$1(0)],
|
|
||||||
[__builtin_ffsll], [$1(0)],
|
|
||||||
[__builtin_fpclassify], [$1(0, 1, 2, 3, 4, 0.0)],
|
|
||||||
[__builtin_huge_val], [$1()],
|
|
||||||
[__builtin_huge_valf], [$1()],
|
|
||||||
[__builtin_huge_vall], [$1()],
|
|
||||||
[__builtin_inf], [$1()],
|
|
||||||
[__builtin_infd128], [$1()],
|
|
||||||
[__builtin_infd32], [$1()],
|
|
||||||
[__builtin_infd64], [$1()],
|
|
||||||
[__builtin_inff], [$1()],
|
|
||||||
[__builtin_infl], [$1()],
|
|
||||||
[__builtin_isinf_sign], [$1(0.0)],
|
|
||||||
[__builtin_nan], [$1("")],
|
|
||||||
[__builtin_nand128], [$1("")],
|
|
||||||
[__builtin_nand32], [$1("")],
|
|
||||||
[__builtin_nand64], [$1("")],
|
|
||||||
[__builtin_nanf], [$1("")],
|
|
||||||
[__builtin_nanl], [$1("")],
|
|
||||||
[__builtin_nans], [$1("")],
|
|
||||||
[__builtin_nansf], [$1("")],
|
|
||||||
[__builtin_nansl], [$1("")],
|
|
||||||
[__builtin_object_size], [$1("", 0)],
|
|
||||||
[__builtin_parity], [$1(0)],
|
|
||||||
[__builtin_parityl], [$1(0)],
|
|
||||||
[__builtin_parityll], [$1(0)],
|
|
||||||
[__builtin_popcount], [$1(0)],
|
|
||||||
[__builtin_popcountl], [$1(0)],
|
|
||||||
[__builtin_popcountll], [$1(0)],
|
|
||||||
[__builtin_powi], [$1(0, 0)],
|
|
||||||
[__builtin_powif], [$1(0, 0)],
|
|
||||||
[__builtin_powil], [$1(0, 0)],
|
|
||||||
[__builtin_prefetch], [$1("")],
|
|
||||||
[__builtin_trap], [$1()],
|
|
||||||
[__builtin_types_compatible_p], [$1(int, int)],
|
|
||||||
[__builtin_unreachable], [$1()],
|
|
||||||
[m4_warn([syntax], [Unsupported built-in $1, the test may fail])
|
|
||||||
$1()]
|
|
||||||
)
|
|
||||||
])],
|
|
||||||
[AS_VAR_SET([ac_var], [yes])],
|
|
||||||
[AS_VAR_SET([ac_var], [no])])
|
|
||||||
])
|
|
||||||
|
|
||||||
AS_IF([test yes = AS_VAR_GET([ac_var])],
|
|
||||||
[AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1), 1,
|
|
||||||
[Define to 1 if the system has the `$1' built-in function])], [])
|
|
||||||
|
|
||||||
AS_VAR_POPDEF([ac_var])
|
|
||||||
])
|
|
||||||
|
|||||||
1
aclocal.m4
vendored
1
aclocal.m4
vendored
@@ -536,5 +536,4 @@ AC_DEFUN([AM_RUN_LOG],
|
|||||||
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
|
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
|
||||||
(exit $ac_status); }])
|
(exit $ac_status); }])
|
||||||
|
|
||||||
|
|
||||||
m4_include([acinclude.m4])
|
m4_include([acinclude.m4])
|
||||||
|
|||||||
@@ -24,16 +24,15 @@ PROFILES=$(PROFILE_TEMPLATES) \
|
|||||||
$(srcdir)/cache-mq.profile \
|
$(srcdir)/cache-mq.profile \
|
||||||
$(srcdir)/cache-smq.profile \
|
$(srcdir)/cache-smq.profile \
|
||||||
$(srcdir)/thin-generic.profile \
|
$(srcdir)/thin-generic.profile \
|
||||||
$(srcdir)/thin-performance.profile \
|
$(srcdir)/thin-performance.profile
|
||||||
$(srcdir)/lvmdbusd.profile
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
include $(top_builddir)/make.tmpl
|
||||||
|
|
||||||
.PHONY: install_conf install_localconf install_profiles
|
.PHONY: install_conf install_localconf install_profiles
|
||||||
|
|
||||||
generate:
|
generate:
|
||||||
LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withgeneralpreamble --withcomments --ignorelocal --withspaces > example.conf.in
|
(cat $(top_srcdir)/conf/example.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --ignorelocal --withspaces) > example.conf.in
|
||||||
LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withlocalpreamble --withcomments --withspaces local > lvmlocal.conf.in
|
(cat $(top_srcdir)/conf/lvmlocal.conf.base && LD_LIBRARY_PATH=$(top_builddir)/libdm:$(LD_LIBRARY_PATH) $(top_builddir)/tools/lvm dumpconfig --type default --unconfigured --withcomments --withspaces local) > lvmlocal.conf.in
|
||||||
|
|
||||||
install_conf: $(CONFSRC)
|
install_conf: $(CONFSRC)
|
||||||
@if [ ! -e $(confdir)/$(CONFDEST) ]; then \
|
@if [ ! -e $(confdir)/$(CONFDEST) ]; then \
|
||||||
@@ -48,8 +47,8 @@ install_localconf: $(CONFLOCAL)
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
install_profiles: $(PROFILES)
|
install_profiles: $(PROFILES)
|
||||||
$(INSTALL_DIR) $(profiledir)
|
$(INSTALL_DIR) $(DESTDIR)$(DEFAULT_PROFILE_DIR)
|
||||||
$(INSTALL_DATA) $(PROFILES) $(profiledir)/
|
$(INSTALL_DATA) $(PROFILES) $(DESTDIR)$(DEFAULT_PROFILE_DIR)/
|
||||||
|
|
||||||
install_lvm2: install_conf install_localconf install_profiles
|
install_lvm2: install_conf install_localconf install_profiles
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ allocation {
|
|||||||
cache_mode = "writethrough"
|
cache_mode = "writethrough"
|
||||||
cache_policy = "smq"
|
cache_policy = "smq"
|
||||||
cache_settings {
|
cache_settings {
|
||||||
# currently no settings for "smq" policy
|
# currently no settins for "smq" policy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,20 +16,15 @@ allocation {
|
|||||||
cache_settings {
|
cache_settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log {
|
|
||||||
report_command_log=0
|
|
||||||
command_log_sort="log_seq_num"
|
|
||||||
command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
|
||||||
command_log_selection="!(log_type=status && message=success)"
|
|
||||||
}
|
|
||||||
global {
|
global {
|
||||||
units="h"
|
units="h"
|
||||||
si_unit_consistency=1
|
si_unit_consistency=1
|
||||||
suffix=1
|
suffix=1
|
||||||
lvdisplay_shows_full_device_path=0
|
lvdisplay_shows_full_device_path=0
|
||||||
}
|
}
|
||||||
|
|
||||||
report {
|
report {
|
||||||
output_format="basic"
|
|
||||||
compact_output=0
|
compact_output=0
|
||||||
compact_output_cols=""
|
compact_output_cols=""
|
||||||
aligned=1
|
aligned=1
|
||||||
@@ -39,7 +34,7 @@ report {
|
|||||||
list_item_separator=","
|
list_item_separator=","
|
||||||
prefixes=0
|
prefixes=0
|
||||||
quoted=1
|
quoted=1
|
||||||
columns_as_rows=0
|
colums_as_rows=0
|
||||||
binary_values_as_numeric=0
|
binary_values_as_numeric=0
|
||||||
time_format="%Y-%m-%d %T %z"
|
time_format="%Y-%m-%d %T %z"
|
||||||
devtypes_sort="devtype_name"
|
devtypes_sort="devtype_name"
|
||||||
@@ -60,15 +55,5 @@ report {
|
|||||||
pvsegs_sort="pv_name,pvseg_start"
|
pvsegs_sort="pv_name,pvseg_start"
|
||||||
pvsegs_cols="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
|
pvsegs_cols="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
|
||||||
pvsegs_cols_verbose="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
pvsegs_cols_verbose="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
||||||
vgs_cols_full="vg_all"
|
|
||||||
pvs_cols_full="pv_all"
|
|
||||||
lvs_cols_full="lv_all"
|
|
||||||
pvsegs_cols_full="pvseg_all,pv_uuid,lv_uuid"
|
|
||||||
segs_cols_full="seg_all,lv_uuid"
|
|
||||||
vgs_sort_full="vg_name"
|
|
||||||
pvs_sort_full="pv_name"
|
|
||||||
lvs_sort_full="vg_name,lv_name"
|
|
||||||
pvsegs_sort_full="pv_uuid,pvseg_start"
|
|
||||||
segs_sort_full="lv_uuid,seg_start"
|
|
||||||
mark_hidden_devices=1
|
mark_hidden_devices=1
|
||||||
}
|
}
|
||||||
|
|||||||
23
conf/example.conf.base
Normal file
23
conf/example.conf.base
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# This is an example configuration file for the LVM2 system.
|
||||||
|
# It contains the default settings that would be used if there was no
|
||||||
|
# @DEFAULT_SYS_DIR@/lvm.conf file.
|
||||||
|
#
|
||||||
|
# Refer to 'man lvm.conf' for further information including the file layout.
|
||||||
|
#
|
||||||
|
# Refer to 'man lvm.conf' for information about how settings configured in
|
||||||
|
# this file are combined with built-in values and command line options to
|
||||||
|
# arrive at the final values used by LVM.
|
||||||
|
#
|
||||||
|
# Refer to 'man lvmconfig' for information about displaying the built-in
|
||||||
|
# and configured values used by LVM.
|
||||||
|
#
|
||||||
|
# If a default value is set in this file (not commented out), then a
|
||||||
|
# new version of LVM using this file will continue using that value,
|
||||||
|
# even if the new version of LVM changes the built-in default value.
|
||||||
|
#
|
||||||
|
# To put this file in a different directory and override @DEFAULT_SYS_DIR@ set
|
||||||
|
# the environment variable LVM_SYSTEM_DIR before running the tools.
|
||||||
|
#
|
||||||
|
# N.B. Take care that each setting only appears once if uncommenting
|
||||||
|
# example settings in this file.
|
||||||
|
|
||||||
@@ -299,19 +299,6 @@ devices {
|
|||||||
# generally do. If enabled, discards will only be issued if both the
|
# generally do. If enabled, discards will only be issued if both the
|
||||||
# storage and kernel provide support.
|
# storage and kernel provide support.
|
||||||
issue_discards = 0
|
issue_discards = 0
|
||||||
|
|
||||||
# Configuration option devices/allow_changes_with_duplicate_pvs.
|
|
||||||
# Allow VG modification while a PV appears on multiple devices.
|
|
||||||
# When a PV appears on multiple devices, LVM attempts to choose the
|
|
||||||
# best device to use for the PV. If the devices represent the same
|
|
||||||
# underlying storage, the choice has minimal consequence. If the
|
|
||||||
# devices represent different underlying storage, the wrong choice
|
|
||||||
# can result in data loss if the VG is modified. Disabling this
|
|
||||||
# setting is the safest option because it prevents modifying a VG
|
|
||||||
# or activating LVs in it while a PV appears on multiple devices.
|
|
||||||
# Enabling this setting allows the VG to be used as usual even with
|
|
||||||
# uncertain devices.
|
|
||||||
allow_changes_with_duplicate_pvs = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Configuration section allocation.
|
# Configuration section allocation.
|
||||||
@@ -377,30 +364,10 @@ allocation {
|
|||||||
# The default setting changed in version 2.02.85.
|
# The default setting changed in version 2.02.85.
|
||||||
mirror_logs_require_separate_pvs = 0
|
mirror_logs_require_separate_pvs = 0
|
||||||
|
|
||||||
# Configuration option allocation/raid_stripe_all_devices.
|
|
||||||
# Stripe across all PVs when RAID stripes are not specified.
|
|
||||||
# If enabled, all PVs in the VG or on the command line are used for
|
|
||||||
# raid0/4/5/6/10 when the command does not specify the number of
|
|
||||||
# stripes to use.
|
|
||||||
# This was the default behaviour until release 2.02.162.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# raid_stripe_all_devices = 0
|
|
||||||
|
|
||||||
# Configuration option allocation/cache_pool_metadata_require_separate_pvs.
|
# Configuration option allocation/cache_pool_metadata_require_separate_pvs.
|
||||||
# Cache pool metadata and data will always use different PVs.
|
# Cache pool metadata and data will always use different PVs.
|
||||||
cache_pool_metadata_require_separate_pvs = 0
|
cache_pool_metadata_require_separate_pvs = 0
|
||||||
|
|
||||||
# Configuration option allocation/cache_metadata_format.
|
|
||||||
# Sets default metadata format for new cache.
|
|
||||||
#
|
|
||||||
# Accepted values:
|
|
||||||
# 0 Automatically detected best available format
|
|
||||||
# 1 Original format
|
|
||||||
# 2 Improved 2nd. generation format
|
|
||||||
#
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# cache_metadata_format = 0
|
|
||||||
|
|
||||||
# Configuration option allocation/cache_mode.
|
# Configuration option allocation/cache_mode.
|
||||||
# The default cache mode used for new cache.
|
# The default cache mode used for new cache.
|
||||||
#
|
#
|
||||||
@@ -417,7 +384,7 @@ allocation {
|
|||||||
|
|
||||||
# Configuration option allocation/cache_policy.
|
# Configuration option allocation/cache_policy.
|
||||||
# The default cache policy used for new cache volume.
|
# The default cache policy used for new cache volume.
|
||||||
# Since kernel 4.2 the default policy is smq (Stochastic multiqueue),
|
# Since kernel 4.2 the default policy is smq (Stochastic multique),
|
||||||
# otherwise the older mq (Multiqueue) policy is selected.
|
# otherwise the older mq (Multiqueue) policy is selected.
|
||||||
# This configuration option does not have a default value defined.
|
# This configuration option does not have a default value defined.
|
||||||
|
|
||||||
@@ -440,12 +407,6 @@ allocation {
|
|||||||
# 32KiB to 1GiB in multiples of 32.
|
# 32KiB to 1GiB in multiples of 32.
|
||||||
# This configuration option does not have a default value defined.
|
# This configuration option does not have a default value defined.
|
||||||
|
|
||||||
# Configuration option allocation/cache_pool_max_chunks.
|
|
||||||
# The maximum number of chunks in a cache pool.
|
|
||||||
# For cache target v1.9 the recommended maximumm is 1000000 chunks.
|
|
||||||
# Using cache pool with more chunks may degrade cache performance.
|
|
||||||
# This configuration option does not have a default value defined.
|
|
||||||
|
|
||||||
# Configuration option allocation/thin_pool_metadata_require_separate_pvs.
|
# Configuration option allocation/thin_pool_metadata_require_separate_pvs.
|
||||||
# Thin pool metdata and data will always use different PVs.
|
# Thin pool metdata and data will always use different PVs.
|
||||||
thin_pool_metadata_require_separate_pvs = 0
|
thin_pool_metadata_require_separate_pvs = 0
|
||||||
@@ -504,55 +465,6 @@ allocation {
|
|||||||
# How LVM log information is reported.
|
# How LVM log information is reported.
|
||||||
log {
|
log {
|
||||||
|
|
||||||
# Configuration option log/report_command_log.
|
|
||||||
# Enable or disable LVM log reporting.
|
|
||||||
# If enabled, LVM will collect a log of operations, messages,
|
|
||||||
# per-object return codes with object identification and associated
|
|
||||||
# error numbers (errnos) during LVM command processing. Then the
|
|
||||||
# log is either reported solely or in addition to any existing
|
|
||||||
# reports, depending on LVM command used. If it is a reporting command
|
|
||||||
# (e.g. pvs, vgs, lvs, lvm fullreport), then the log is reported in
|
|
||||||
# addition to any existing reports. Otherwise, there's only log report
|
|
||||||
# on output. For all applicable LVM commands, you can request that
|
|
||||||
# the output has only log report by using --logonly command line
|
|
||||||
# option. Use log/command_log_cols and log/command_log_sort settings
|
|
||||||
# to define fields to display and sort fields for the log report.
|
|
||||||
# You can also use log/command_log_selection to define selection
|
|
||||||
# criteria used each time the log is reported.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# report_command_log = 0
|
|
||||||
|
|
||||||
# Configuration option log/command_log_sort.
|
|
||||||
# List of columns to sort by when reporting command log.
|
|
||||||
# See <lvm command> --logonly --configreport log -o help
|
|
||||||
# for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# command_log_sort = "log_seq_num"
|
|
||||||
|
|
||||||
# Configuration option log/command_log_cols.
|
|
||||||
# List of columns to report when reporting command log.
|
|
||||||
# See <lvm command> --logonly --configreport log -o help
|
|
||||||
# for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# command_log_cols = "log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
|
||||||
|
|
||||||
# Configuration option log/command_log_selection.
|
|
||||||
# Selection criteria used when reporting command log.
|
|
||||||
# You can define selection criteria that are applied each
|
|
||||||
# time log is reported. This way, it is possible to control the
|
|
||||||
# amount of log that is displayed on output and you can select
|
|
||||||
# only parts of the log that are important for you. To define
|
|
||||||
# selection criteria, use fields from log report. See also
|
|
||||||
# <lvm command> --logonly --configreport log -S help for the
|
|
||||||
# list of possible fields and selection operators. You can also
|
|
||||||
# define selection criteria for log report on command line directly
|
|
||||||
# using <lvm command> --configreport log -S <selection criteria>
|
|
||||||
# which has precedence over log/command_log_selection setting.
|
|
||||||
# For more information about selection criteria in general, see
|
|
||||||
# lvm(8) man page.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# command_log_selection = "!(log_type=status && message=success)"
|
|
||||||
|
|
||||||
# Configuration option log/verbose.
|
# Configuration option log/verbose.
|
||||||
# Controls the messages sent to stdout or stderr.
|
# Controls the messages sent to stdout or stderr.
|
||||||
verbose = 0
|
verbose = 0
|
||||||
@@ -611,9 +523,9 @@ log {
|
|||||||
# Select log messages by class.
|
# Select log messages by class.
|
||||||
# Some debugging messages are assigned to a class and only appear in
|
# Some debugging messages are assigned to a class and only appear in
|
||||||
# debug output if the class is listed here. Classes currently
|
# debug output if the class is listed here. Classes currently
|
||||||
# available: memory, devices, io, activation, allocation, lvmetad,
|
# available: memory, devices, activation, allocation, lvmetad,
|
||||||
# metadata, cache, locking, lvmpolld. Use "all" to see everything.
|
# metadata, cache, locking, lvmpolld. Use "all" to see everything.
|
||||||
debug_classes = [ "memory", "devices", "io", "activation", "allocation", "lvmetad", "metadata", "cache", "locking", "lvmpolld", "dbus" ]
|
debug_classes = [ "memory", "devices", "activation", "allocation", "lvmetad", "metadata", "cache", "locking", "lvmpolld" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Configuration section backup.
|
# Configuration section backup.
|
||||||
@@ -677,7 +589,7 @@ global {
|
|||||||
|
|
||||||
# Configuration option global/units.
|
# Configuration option global/units.
|
||||||
# Default value for --units argument.
|
# Default value for --units argument.
|
||||||
units = "r"
|
units = "h"
|
||||||
|
|
||||||
# Configuration option global/si_unit_consistency.
|
# Configuration option global/si_unit_consistency.
|
||||||
# Distinguish between powers of 1024 and 1000 bytes.
|
# Distinguish between powers of 1024 and 1000 bytes.
|
||||||
@@ -939,23 +851,13 @@ global {
|
|||||||
# devices/global_filter.
|
# devices/global_filter.
|
||||||
use_lvmetad = @DEFAULT_USE_LVMETAD@
|
use_lvmetad = @DEFAULT_USE_LVMETAD@
|
||||||
|
|
||||||
# Configuration option global/lvmetad_update_wait_time.
|
|
||||||
# Number of seconds a command will wait for lvmetad update to finish.
|
|
||||||
# After waiting for this period, a command will not use lvmetad, and
|
|
||||||
# will revert to disk scanning.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# lvmetad_update_wait_time = 10
|
|
||||||
|
|
||||||
# Configuration option global/use_lvmlockd.
|
# Configuration option global/use_lvmlockd.
|
||||||
# Use lvmlockd for locking among hosts using LVM on shared storage.
|
# Use lvmlockd for locking among hosts using LVM on shared storage.
|
||||||
# Applicable only if LVM is compiled with lockd support in which
|
# See lvmlockd(8) for more information.
|
||||||
# case there is also lvmlockd(8) man page available for more
|
|
||||||
# information.
|
|
||||||
use_lvmlockd = 0
|
use_lvmlockd = 0
|
||||||
|
|
||||||
# Configuration option global/lvmlockd_lock_retries.
|
# Configuration option global/lvmlockd_lock_retries.
|
||||||
# Retry lvmlockd lock requests this many times.
|
# Retry lvmlockd lock requests this many times.
|
||||||
# Applicable only if LVM is compiled with lockd support
|
|
||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
# lvmlockd_lock_retries = 3
|
# lvmlockd_lock_retries = 3
|
||||||
|
|
||||||
@@ -965,8 +867,7 @@ global {
|
|||||||
# LVs have been created, the internal LV needs to be extended. lvcreate
|
# LVs have been created, the internal LV needs to be extended. lvcreate
|
||||||
# will automatically extend the internal LV when needed by the amount
|
# will automatically extend the internal LV when needed by the amount
|
||||||
# specified here. Setting this to 0 disables the automatic extension
|
# specified here. Setting this to 0 disables the automatic extension
|
||||||
# and can cause lvcreate to fail. Applicable only if LVM is compiled
|
# and can cause lvcreate to fail.
|
||||||
# with lockd support
|
|
||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
# sanlock_lv_extend = 256
|
# sanlock_lv_extend = 256
|
||||||
|
|
||||||
@@ -1025,7 +926,7 @@ global {
|
|||||||
# Configuration option global/cache_disabled_features.
|
# Configuration option global/cache_disabled_features.
|
||||||
# Features to not use in the cache driver.
|
# Features to not use in the cache driver.
|
||||||
# This can be helpful for testing, or to avoid using a feature that is
|
# This can be helpful for testing, or to avoid using a feature that is
|
||||||
# causing problems. Features include: policy_mq, policy_smq, metadata2.
|
# causing problems. Features include: policy_mq, policy_smq.
|
||||||
#
|
#
|
||||||
# Example
|
# Example
|
||||||
# cache_disabled_features = [ "policy_smq" ]
|
# cache_disabled_features = [ "policy_smq" ]
|
||||||
@@ -1070,12 +971,6 @@ global {
|
|||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
# cache_repair_options = [ "" ]
|
# cache_repair_options = [ "" ]
|
||||||
|
|
||||||
# Configuration option global/fsadm_executable.
|
|
||||||
# The full path to the fsadm command.
|
|
||||||
# LVM uses this command to help with lvresize -r operations.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# fsadm_executable = "@FSADM_PATH@"
|
|
||||||
|
|
||||||
# Configuration option global/system_id_source.
|
# Configuration option global/system_id_source.
|
||||||
# The method LVM uses to set the local system ID.
|
# The method LVM uses to set the local system ID.
|
||||||
# Volume Groups can also be given a system ID (by vgcreate, vgchange,
|
# Volume Groups can also be given a system ID (by vgcreate, vgchange,
|
||||||
@@ -1119,14 +1014,7 @@ global {
|
|||||||
# a native systemd service, which allows it to be started on demand,
|
# a native systemd service, which allows it to be started on demand,
|
||||||
# and to use its own control group. When this option is disabled, LVM
|
# and to use its own control group. When this option is disabled, LVM
|
||||||
# commands will supervise long running operations by forking themselves.
|
# commands will supervise long running operations by forking themselves.
|
||||||
# Applicable only if LVM is compiled with lvmpolld support.
|
|
||||||
use_lvmpolld = @DEFAULT_USE_LVMPOLLD@
|
use_lvmpolld = @DEFAULT_USE_LVMPOLLD@
|
||||||
|
|
||||||
# Configuration option global/notify_dbus.
|
|
||||||
# Enable D-Bus notification from LVM commands.
|
|
||||||
# When enabled, an LVM command that changes PVs, changes VG metadata,
|
|
||||||
# or changes the activation state of an LV will send a notification.
|
|
||||||
notify_dbus = 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Configuration section activation.
|
# Configuration section activation.
|
||||||
@@ -1174,8 +1062,7 @@ activation {
|
|||||||
# Configuration option activation/missing_stripe_filler.
|
# Configuration option activation/missing_stripe_filler.
|
||||||
# Method to fill missing stripes when activating an incomplete LV.
|
# Method to fill missing stripes when activating an incomplete LV.
|
||||||
# Using 'error' will make inaccessible parts of the device return I/O
|
# Using 'error' will make inaccessible parts of the device return I/O
|
||||||
# errors on access. Using 'zero' will return success (and zero) on I/O
|
# errors on access. You can instead use a device path, in which case,
|
||||||
# You can instead use a device path, in which case,
|
|
||||||
# that device will be used in place of missing stripes. Using anything
|
# that device will be used in place of missing stripes. Using anything
|
||||||
# other than 'error' with mirrored or snapshotted volumes is likely to
|
# other than 'error' with mirrored or snapshotted volumes is likely to
|
||||||
# result in data corruption.
|
# result in data corruption.
|
||||||
@@ -1295,10 +1182,9 @@ activation {
|
|||||||
|
|
||||||
# Configuration option activation/raid_region_size.
|
# Configuration option activation/raid_region_size.
|
||||||
# Size in KiB of each raid or mirror synchronization region.
|
# Size in KiB of each raid or mirror synchronization region.
|
||||||
# The clean/dirty state of data is tracked for each region.
|
# For raid or mirror segment types, this is the amount of data that is
|
||||||
# The value is rounded down to a power of two if necessary, and
|
# copied at once when initializing, or moved at once by pvmove.
|
||||||
# is ignored if it is not a multiple of the machine memory page size.
|
raid_region_size = 512
|
||||||
raid_region_size = 2048
|
|
||||||
|
|
||||||
# Configuration option activation/error_when_full.
|
# Configuration option activation/error_when_full.
|
||||||
# Return errors if a thin pool runs out of space.
|
# Return errors if a thin pool runs out of space.
|
||||||
@@ -1540,22 +1426,6 @@ activation {
|
|||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
# check_pv_device_sizes = 1
|
# check_pv_device_sizes = 1
|
||||||
|
|
||||||
# Configuration option metadata/record_lvs_history.
|
|
||||||
# When enabled, LVM keeps history records about removed LVs in
|
|
||||||
# metadata. The information that is recorded in metadata for
|
|
||||||
# historical LVs is reduced when compared to original
|
|
||||||
# information kept in metadata for live LVs. Currently, this
|
|
||||||
# feature is supported for thin and thin snapshot LVs only.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# record_lvs_history = 0
|
|
||||||
|
|
||||||
# Configuration option metadata/lvs_history_retention_time.
|
|
||||||
# Retention time in seconds after which a record about individual
|
|
||||||
# historical logical volume is automatically destroyed.
|
|
||||||
# A value of 0 disables this feature.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# lvs_history_retention_time = 0
|
|
||||||
|
|
||||||
# Configuration option metadata/pvmetadatacopies.
|
# Configuration option metadata/pvmetadatacopies.
|
||||||
# Number of copies of metadata to store on each PV.
|
# Number of copies of metadata to store on each PV.
|
||||||
# The --pvmetadatacopies option overrides this setting.
|
# The --pvmetadatacopies option overrides this setting.
|
||||||
@@ -1634,22 +1504,6 @@ activation {
|
|||||||
# This configuration section has an automatic default value.
|
# This configuration section has an automatic default value.
|
||||||
# report {
|
# report {
|
||||||
|
|
||||||
# Configuration option report/output_format.
|
|
||||||
# Format of LVM command's report output.
|
|
||||||
# If there is more than one report per command, then the format
|
|
||||||
# is applied for all reports. You can also change output format
|
|
||||||
# directly on command line using --reportformat option which
|
|
||||||
# has precedence over log/output_format setting.
|
|
||||||
# Accepted values:
|
|
||||||
# basic
|
|
||||||
# Original format with columns and rows. If there is more than
|
|
||||||
# one report per command, each report is prefixed with report's
|
|
||||||
# name for identification.
|
|
||||||
# json
|
|
||||||
# JSON format.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# output_format = "basic"
|
|
||||||
|
|
||||||
# Configuration option report/compact_output.
|
# Configuration option report/compact_output.
|
||||||
# Do not print empty values for all report fields.
|
# Do not print empty values for all report fields.
|
||||||
# If enabled, all fields that don't have a value set for any of the
|
# If enabled, all fields that don't have a value set for any of the
|
||||||
@@ -1710,11 +1564,11 @@ activation {
|
|||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
# quoted = 1
|
# quoted = 1
|
||||||
|
|
||||||
# Configuration option report/columns_as_rows.
|
# Configuration option report/colums_as_rows.
|
||||||
# Output each column as a row.
|
# Output each column as a row.
|
||||||
# If set, this also implies report/prefixes=1.
|
# If set, this also implies report/prefixes=1.
|
||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
# columns_as_rows = 0
|
# colums_as_rows = 0
|
||||||
|
|
||||||
# Configuration option report/binary_values_as_numeric.
|
# Configuration option report/binary_values_as_numeric.
|
||||||
# Use binary values 0 or 1 instead of descriptive literal values.
|
# Use binary values 0 or 1 instead of descriptive literal values.
|
||||||
@@ -1966,76 +1820,10 @@ activation {
|
|||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
# pvsegs_cols_verbose = "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
# pvsegs_cols_verbose = "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
||||||
|
|
||||||
# Configuration option report/vgs_cols_full.
|
|
||||||
# List of columns to report for lvm fullreport's 'vgs' subreport.
|
|
||||||
# See 'vgs -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# vgs_cols_full = "vg_all"
|
|
||||||
|
|
||||||
# Configuration option report/pvs_cols_full.
|
|
||||||
# List of columns to report for lvm fullreport's 'vgs' subreport.
|
|
||||||
# See 'pvs -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# pvs_cols_full = "pv_all"
|
|
||||||
|
|
||||||
# Configuration option report/lvs_cols_full.
|
|
||||||
# List of columns to report for lvm fullreport's 'lvs' subreport.
|
|
||||||
# See 'lvs -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# lvs_cols_full = "lv_all"
|
|
||||||
|
|
||||||
# Configuration option report/pvsegs_cols_full.
|
|
||||||
# List of columns to report for lvm fullreport's 'pvseg' subreport.
|
|
||||||
# See 'pvs --segments -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# pvsegs_cols_full = "pvseg_all,pv_uuid,lv_uuid"
|
|
||||||
|
|
||||||
# Configuration option report/segs_cols_full.
|
|
||||||
# List of columns to report for lvm fullreport's 'seg' subreport.
|
|
||||||
# See 'lvs --segments -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# segs_cols_full = "seg_all,lv_uuid"
|
|
||||||
|
|
||||||
# Configuration option report/vgs_sort_full.
|
|
||||||
# List of columns to sort by when reporting lvm fullreport's 'vgs' subreport.
|
|
||||||
# See 'vgs -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# vgs_sort_full = "vg_name"
|
|
||||||
|
|
||||||
# Configuration option report/pvs_sort_full.
|
|
||||||
# List of columns to sort by when reporting lvm fullreport's 'vgs' subreport.
|
|
||||||
# See 'pvs -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# pvs_sort_full = "pv_name"
|
|
||||||
|
|
||||||
# Configuration option report/lvs_sort_full.
|
|
||||||
# List of columns to sort by when reporting lvm fullreport's 'lvs' subreport.
|
|
||||||
# See 'lvs -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# lvs_sort_full = "vg_name,lv_name"
|
|
||||||
|
|
||||||
# Configuration option report/pvsegs_sort_full.
|
|
||||||
# List of columns to sort by when reporting for lvm fullreport's 'pvseg' subreport.
|
|
||||||
# See 'pvs --segments -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# pvsegs_sort_full = "pv_uuid,pvseg_start"
|
|
||||||
|
|
||||||
# Configuration option report/segs_sort_full.
|
|
||||||
# List of columns to sort by when reporting lvm fullreport's 'seg' subreport.
|
|
||||||
# See 'lvs --segments -o help' for the list of possible fields.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# segs_sort_full = "lv_uuid,seg_start"
|
|
||||||
|
|
||||||
# Configuration option report/mark_hidden_devices.
|
# Configuration option report/mark_hidden_devices.
|
||||||
# Use brackets [] to mark hidden devices.
|
# Use brackets [] to mark hidden devices.
|
||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
# mark_hidden_devices = 1
|
# mark_hidden_devices = 1
|
||||||
|
|
||||||
# Configuration option report/two_word_unknown_device.
|
|
||||||
# Use the two words 'unknown device' in place of '[unknown]'.
|
|
||||||
# This is displayed when the device for a PV is not known.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# two_word_unknown_device = 0
|
|
||||||
# }
|
# }
|
||||||
|
|
||||||
# Configuration section dmeventd.
|
# Configuration section dmeventd.
|
||||||
@@ -2068,15 +1856,6 @@ dmeventd {
|
|||||||
# warning is repeated when 85%, 90% and 95% of the pool is filled.
|
# warning is repeated when 85%, 90% and 95% of the pool is filled.
|
||||||
thin_library = "libdevmapper-event-lvm2thin.so"
|
thin_library = "libdevmapper-event-lvm2thin.so"
|
||||||
|
|
||||||
# Configuration option dmeventd/thin_command.
|
|
||||||
# The plugin runs command with each 5% increment when thin-pool data volume
|
|
||||||
# or metadata volume gets above 50%.
|
|
||||||
# Command which starts with 'lvm ' prefix is internal lvm command.
|
|
||||||
# You can write your own handler to customise behaviour in more details.
|
|
||||||
# User handler is specified with the full path starting with '/'.
|
|
||||||
# This configuration option has an automatic default value.
|
|
||||||
# thin_command = "lvm lvextend --use-policies"
|
|
||||||
|
|
||||||
# Configuration option dmeventd/executable.
|
# Configuration option dmeventd/executable.
|
||||||
# The full path to the dmeventd binary.
|
# The full path to the dmeventd binary.
|
||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
#
|
|
||||||
# DO NOT EDIT THIS FILE!
|
|
||||||
#
|
|
||||||
# LVM configuration profile used by lvmdbusd daemon.
|
|
||||||
#
|
|
||||||
# This sets up LVM to produce output in the most suitable format for processing
|
|
||||||
# by lvmdbusd daemon which utilizes LVM shell to execute LVM commands.
|
|
||||||
#
|
|
||||||
# Do not edit this file in any way. This profile is distributed together with
|
|
||||||
# lvmdbusd and it contains configuration that is important for lvmdbusd to
|
|
||||||
# cooperate and interface with LVM correctly.
|
|
||||||
#
|
|
||||||
|
|
||||||
global {
|
|
||||||
# use bytes for expected and deterministic output
|
|
||||||
units=b
|
|
||||||
# no need for suffix if we have units set
|
|
||||||
suffix=0
|
|
||||||
}
|
|
||||||
|
|
||||||
report {
|
|
||||||
compact_output=0
|
|
||||||
compact_output_cols=""
|
|
||||||
binary_values_as_numeric=0
|
|
||||||
# time in number of seconds since the Epoch
|
|
||||||
time_format="%s"
|
|
||||||
mark_hidden_devices=1
|
|
||||||
# lvmdbusd expects JSON output
|
|
||||||
output_format=json
|
|
||||||
# *_cols_full for lvm fullreport's fields which lvmdbusd relies on to update its state
|
|
||||||
vgs_cols_full="vg_name,vg_uuid,vg_fmt,vg_size,vg_free,vg_sysid,vg_extent_size,vg_extent_count,vg_free_count,vg_profile,max_lv,max_pv,pv_count,lv_count,snap_count,vg_seqno,vg_mda_count,vg_mda_free,vg_mda_size,vg_mda_used_count,vg_attr,vg_tags"
|
|
||||||
pvs_cols_full="pv_name,pv_uuid,pv_fmt,pv_size,pv_free,pv_used,dev_size,pv_mda_size,pv_mda_free,pv_ba_start,pv_ba_size,pe_start,pv_pe_count,pv_pe_alloc_count,pv_attr,pv_tags,vg_name,vg_uuid"
|
|
||||||
lvs_cols_full="lv_uuid,lv_name,lv_path,lv_size,vg_name,pool_lv_uuid,pool_lv,origin_uuid,origin,data_percent,lv_attr,lv_tags,vg_uuid,lv_active,data_lv,metadata_lv,lv_parent,lv_role,lv_layout"
|
|
||||||
pvsegs_cols_full="pvseg_start,pvseg_size,segtype,pv_uuid,lv_uuid,pv_name"
|
|
||||||
segs_cols_full="seg_pe_ranges,segtype,lv_uuid"
|
|
||||||
vgs_sort_full="vg_name"
|
|
||||||
pvs_sort_full="pv_name"
|
|
||||||
lvs_sort_full="vg_name,lv_name"
|
|
||||||
pvsegs_sort_full="pv_uuid,pvseg_start"
|
|
||||||
segs_sort_full="lv_uuid,seg_start"
|
|
||||||
}
|
|
||||||
|
|
||||||
log {
|
|
||||||
# lvmdbusd relies on command log report to inspect LVM command's execution status
|
|
||||||
report_command_log=1
|
|
||||||
# display only outermost LVM shell-related log that lvmdbusd inspects first after LVM command execution (it calls 'lastlog' for more detailed log afterwards if needed)
|
|
||||||
command_log_selection="log_context=shell"
|
|
||||||
command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
|
||||||
command_log_sort="log_seq_num"
|
|
||||||
}
|
|
||||||
19
conf/lvmlocal.conf.base
Normal file
19
conf/lvmlocal.conf.base
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# This is a local configuration file template for the LVM2 system
|
||||||
|
# which should be installed as @DEFAULT_SYS_DIR@/lvmlocal.conf .
|
||||||
|
#
|
||||||
|
# Refer to 'man lvm.conf' for information about the file layout.
|
||||||
|
#
|
||||||
|
# To put this file in a different directory and override
|
||||||
|
# @DEFAULT_SYS_DIR@ set the environment variable LVM_SYSTEM_DIR before
|
||||||
|
# running the tools.
|
||||||
|
#
|
||||||
|
# The lvmlocal.conf file is normally expected to contain only the
|
||||||
|
# "local" section which contains settings that should not be shared or
|
||||||
|
# repeated among different hosts. (But if other sections are present,
|
||||||
|
# they *will* get processed. Settings in this file override equivalent
|
||||||
|
# ones in lvm.conf and are in turn overridden by ones in any enabled
|
||||||
|
# lvm_<tag>.conf files.)
|
||||||
|
#
|
||||||
|
# Please take care that each setting only appears once if uncommenting
|
||||||
|
# example settings in this file and never copy this file between hosts.
|
||||||
|
|
||||||
@@ -51,7 +51,6 @@ local {
|
|||||||
# Configuration option local/host_id.
|
# Configuration option local/host_id.
|
||||||
# The lvmlockd sanlock host_id.
|
# The lvmlockd sanlock host_id.
|
||||||
# This must be unique among all hosts, and must be between 1 and 2000.
|
# This must be unique among all hosts, and must be between 1 and 2000.
|
||||||
# Applicable only if LVM is compiled with lockd support
|
|
||||||
# This configuration option has an automatic default value.
|
# This configuration option has an automatic default value.
|
||||||
# host_id = 0
|
# host_id = 0
|
||||||
}
|
}
|
||||||
|
|||||||
237
configure.in
237
configure.in
@@ -15,7 +15,6 @@ AC_PREREQ(2.69)
|
|||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Process this file with autoconf to produce a configure script.
|
dnl -- Process this file with autoconf to produce a configure script.
|
||||||
AC_INIT
|
AC_INIT
|
||||||
CONFIGURE_LINE="$0 $@"
|
|
||||||
AC_CONFIG_SRCDIR([lib/device/dev-cache.h])
|
AC_CONFIG_SRCDIR([lib/device/dev-cache.h])
|
||||||
AC_CONFIG_HEADERS([include/configure.h])
|
AC_CONFIG_HEADERS([include/configure.h])
|
||||||
|
|
||||||
@@ -31,7 +30,6 @@ AS_IF([test -z "$CFLAGS"], [COPTIMISE_FLAG="-O2"])
|
|||||||
case "$host_os" in
|
case "$host_os" in
|
||||||
linux*)
|
linux*)
|
||||||
CLDFLAGS="$CLDFLAGS -Wl,--version-script,.export.sym"
|
CLDFLAGS="$CLDFLAGS -Wl,--version-script,.export.sym"
|
||||||
# equivalent to -rdynamic
|
|
||||||
ELDFLAGS="-Wl,--export-dynamic"
|
ELDFLAGS="-Wl,--export-dynamic"
|
||||||
# FIXME Generate list and use --dynamic-list=.dlopen.sym
|
# FIXME Generate list and use --dynamic-list=.dlopen.sym
|
||||||
CLDWHOLEARCHIVE="-Wl,-whole-archive"
|
CLDWHOLEARCHIVE="-Wl,-whole-archive"
|
||||||
@@ -39,8 +37,8 @@ case "$host_os" in
|
|||||||
LDDEPS="$LDDEPS .export.sym"
|
LDDEPS="$LDDEPS .export.sym"
|
||||||
LIB_SUFFIX=so
|
LIB_SUFFIX=so
|
||||||
DEVMAPPER=yes
|
DEVMAPPER=yes
|
||||||
BUILD_LVMETAD=no
|
LVMETAD=no
|
||||||
BUILD_LVMPOLLD=no
|
LVMPOLLD=no
|
||||||
LOCKDSANLOCK=no
|
LOCKDSANLOCK=no
|
||||||
LOCKDDLM=no
|
LOCKDDLM=no
|
||||||
ODIRECT=yes
|
ODIRECT=yes
|
||||||
@@ -85,12 +83,9 @@ AC_PROG_LN_S
|
|||||||
AC_PROG_MAKE_SET
|
AC_PROG_MAKE_SET
|
||||||
AC_PROG_MKDIR_P
|
AC_PROG_MKDIR_P
|
||||||
AC_PROG_RANLIB
|
AC_PROG_RANLIB
|
||||||
AC_CHECK_TOOL(AR, ar)
|
|
||||||
AC_PATH_TOOL(CFLOW_CMD, cflow)
|
AC_PATH_TOOL(CFLOW_CMD, cflow)
|
||||||
AC_PATH_TOOL(CSCOPE_CMD, cscope)
|
AC_PATH_TOOL(CSCOPE_CMD, cscope)
|
||||||
AC_PATH_TOOL(CHMOD, chmod)
|
AC_PATH_TOOL(CHMOD, chmod)
|
||||||
AC_PATH_TOOL(WC, wc)
|
|
||||||
AC_PATH_TOOL(SORT, sort)
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Check for header files.
|
dnl -- Check for header files.
|
||||||
@@ -108,7 +103,7 @@ AC_CHECK_HEADERS([assert.h ctype.h dirent.h errno.h fcntl.h float.h \
|
|||||||
sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \
|
sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \
|
||||||
unistd.h], , [AC_MSG_ERROR(bailing out)])
|
unistd.h], , [AC_MSG_ERROR(bailing out)])
|
||||||
|
|
||||||
AC_CHECK_HEADERS(termios.h sys/statvfs.h sys/timerfd.h sys/vfs.h linux/magic.h linux/fiemap.h)
|
AC_CHECK_HEADERS(termios.h sys/statvfs.h sys/timerfd.h)
|
||||||
|
|
||||||
case "$host_os" in
|
case "$host_os" in
|
||||||
linux*)
|
linux*)
|
||||||
@@ -123,7 +118,6 @@ AC_C_CONST
|
|||||||
AC_C_INLINE
|
AC_C_INLINE
|
||||||
AC_CHECK_MEMBERS([struct stat.st_rdev])
|
AC_CHECK_MEMBERS([struct stat.st_rdev])
|
||||||
AC_CHECK_TYPES([ptrdiff_t])
|
AC_CHECK_TYPES([ptrdiff_t])
|
||||||
AC_STRUCT_ST_BLOCKS
|
|
||||||
AC_STRUCT_TM
|
AC_STRUCT_TM
|
||||||
AC_TYPE_OFF_T
|
AC_TYPE_OFF_T
|
||||||
AC_TYPE_PID_T
|
AC_TYPE_PID_T
|
||||||
@@ -140,7 +134,6 @@ AC_TYPE_UINT8_T
|
|||||||
AC_TYPE_UINT16_T
|
AC_TYPE_UINT16_T
|
||||||
AC_TYPE_UINT32_T
|
AC_TYPE_UINT32_T
|
||||||
AC_TYPE_UINT64_T
|
AC_TYPE_UINT64_T
|
||||||
AX_GCC_BUILTIN([__builtin_clz])
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Check for functions
|
dnl -- Check for functions
|
||||||
@@ -191,15 +184,9 @@ AC_SUBST(HAVE_FULL_RELRO)
|
|||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Prefix is /usr by default, the exec_prefix default is setup later
|
dnl -- Prefix is /usr by default, the exec_prefix default is setup later
|
||||||
AC_PREFIX_DEFAULT(/usr)
|
AC_PREFIX_DEFAULT(/usr)
|
||||||
|
if test "$prefix" = NONE; then
|
||||||
################################################################################
|
datarootdir=${ac_default_prefix}/share
|
||||||
dnl -- Clear default exec_prefix - install into /sbin rather than /usr/sbin
|
fi
|
||||||
test "$exec_prefix" = NONE -a "$prefix" = NONE && exec_prefix=""
|
|
||||||
|
|
||||||
test "x$prefix" = xNONE && prefix=$ac_default_prefix
|
|
||||||
# Let make expand exec_prefix.
|
|
||||||
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
|
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Setup the ownership of the files
|
dnl -- Setup the ownership of the files
|
||||||
@@ -414,6 +401,22 @@ AC_DEFINE_UNQUOTED([DEFAULT_RAID10_SEGTYPE], ["$DEFAULT_RAID10_SEGTYPE"],
|
|||||||
[Default segtype used for raid10 volumes.])
|
[Default segtype used for raid10 volumes.])
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
dnl -- asynchronous volume replicator inclusion type
|
||||||
|
AC_MSG_CHECKING(whether to include replicators)
|
||||||
|
AC_ARG_WITH(replicators,
|
||||||
|
AC_HELP_STRING([--with-replicators=TYPE],
|
||||||
|
[replicator support: internal/shared/none [none]]),
|
||||||
|
REPLICATORS=$withval, REPLICATORS=none)
|
||||||
|
AC_MSG_RESULT($REPLICATORS)
|
||||||
|
|
||||||
|
case "$REPLICATORS" in
|
||||||
|
none|shared) ;;
|
||||||
|
internal) AC_DEFINE([REPLICATOR_INTERNAL], 1,
|
||||||
|
[Define to 1 to include built-in support for replicators.]) ;;
|
||||||
|
*) AC_MSG_ERROR([--with-replicators parameter invalid ($REPLICATORS)]) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
|
||||||
AC_ARG_WITH(default-sparse-segtype,
|
AC_ARG_WITH(default-sparse-segtype,
|
||||||
AC_HELP_STRING([--with-default-sparse-segtype=TYPE],
|
AC_HELP_STRING([--with-default-sparse-segtype=TYPE],
|
||||||
[default sparse segtype: thin/snapshot [thin]]),
|
[default sparse segtype: thin/snapshot [thin]]),
|
||||||
@@ -609,10 +612,6 @@ case "$CACHE" in
|
|||||||
CACHE_CHECK_VERSION_WARN=y
|
CACHE_CHECK_VERSION_WARN=y
|
||||||
CACHE_CHECK_NEEDS_CHECK=no
|
CACHE_CHECK_NEEDS_CHECK=no
|
||||||
fi
|
fi
|
||||||
if test "$CACHE_CHECK_VSN_MINOR" -lt 7 ; then
|
|
||||||
AC_MSG_WARN([$CACHE_CHECK_CMD: Old version "$CACHE_CHECK_VSN" does not support new cache format V2])
|
|
||||||
CACHE_CHECK_VERSION_WARN=y
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
# Empty means a config way to ignore cache dumping
|
# Empty means a config way to ignore cache dumping
|
||||||
@@ -666,9 +665,11 @@ AC_DEFINE_UNQUOTED([CACHE_RESTORE_CMD], ["$CACHE_RESTORE_CMD"],
|
|||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Disable readline
|
dnl -- Disable readline
|
||||||
|
AC_MSG_CHECKING(whether to enable readline)
|
||||||
AC_ARG_ENABLE([readline],
|
AC_ARG_ENABLE([readline],
|
||||||
AC_HELP_STRING([--disable-readline], [disable readline support]),
|
AC_HELP_STRING([--disable-readline], [disable readline support]),
|
||||||
READLINE=$enableval, READLINE=maybe)
|
READLINE=$enableval, READLINE=maybe)
|
||||||
|
AC_MSG_RESULT($READLINE)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Disable realtime clock support
|
dnl -- Disable realtime clock support
|
||||||
@@ -1128,8 +1129,9 @@ AC_ARG_ENABLE(lvmetad,
|
|||||||
AC_HELP_STRING([--enable-lvmetad],
|
AC_HELP_STRING([--enable-lvmetad],
|
||||||
[enable the LVM Metadata Daemon]),
|
[enable the LVM Metadata Daemon]),
|
||||||
LVMETAD=$enableval)
|
LVMETAD=$enableval)
|
||||||
test -n "$LVMETAD" && BUILD_LVMETAD=$LVMETAD
|
AC_MSG_RESULT($LVMETAD)
|
||||||
AC_MSG_RESULT($BUILD_LVMETAD)
|
|
||||||
|
BUILD_LVMETAD=$LVMETAD
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Build lvmpolld
|
dnl -- Build lvmpolld
|
||||||
@@ -1138,16 +1140,17 @@ AC_ARG_ENABLE(lvmpolld,
|
|||||||
AC_HELP_STRING([--enable-lvmpolld],
|
AC_HELP_STRING([--enable-lvmpolld],
|
||||||
[enable the LVM Polling Daemon]),
|
[enable the LVM Polling Daemon]),
|
||||||
LVMPOLLD=$enableval)
|
LVMPOLLD=$enableval)
|
||||||
test -n "$LVMPOLLD" && BUILD_LVMPOLLD=$LVMPOLLD
|
AC_MSG_RESULT($LVMPOLLD)
|
||||||
AC_MSG_RESULT($BUILD_LVMPOLLD)
|
|
||||||
|
BUILD_LVMPOLLD=$LVMPOLLD
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
BUILD_LVMLOCKD=no
|
BUILD_LVMLOCKD=no
|
||||||
|
|
||||||
dnl -- Build lvmlockdsanlock
|
dnl -- Build lockdsanlock
|
||||||
AC_MSG_CHECKING(whether to build lvmlockdsanlock)
|
AC_MSG_CHECKING(whether to build lockdsanlock)
|
||||||
AC_ARG_ENABLE(lvmlockd-sanlock,
|
AC_ARG_ENABLE(lockd-sanlock,
|
||||||
AC_HELP_STRING([--enable-lvmlockd-sanlock],
|
AC_HELP_STRING([--enable-lockd-sanlock],
|
||||||
[enable the LVM lock daemon using sanlock]),
|
[enable the LVM lock daemon using sanlock]),
|
||||||
LOCKDSANLOCK=$enableval)
|
LOCKDSANLOCK=$enableval)
|
||||||
AC_MSG_RESULT($LOCKDSANLOCK)
|
AC_MSG_RESULT($LOCKDSANLOCK)
|
||||||
@@ -1156,16 +1159,16 @@ BUILD_LOCKDSANLOCK=$LOCKDSANLOCK
|
|||||||
|
|
||||||
dnl -- Look for sanlock libraries
|
dnl -- Look for sanlock libraries
|
||||||
if test "$BUILD_LOCKDSANLOCK" = yes; then
|
if test "$BUILD_LOCKDSANLOCK" = yes; then
|
||||||
PKG_CHECK_MODULES(LOCKD_SANLOCK, libsanlock_client >= 3.3.0, [HAVE_LOCKD_SANLOCK=yes], $bailout)
|
PKG_CHECK_MODULES(LOCKD_SANLOCK, libsanlock_client, [HAVE_LOCKD_SANLOCK=yes], $bailout)
|
||||||
AC_DEFINE([LOCKDSANLOCK_SUPPORT], 1, [Define to 1 to include code that uses lvmlockd sanlock option.])
|
AC_DEFINE([LOCKDSANLOCK_SUPPORT], 1, [Define to 1 to include code that uses lvmlockd sanlock option.])
|
||||||
BUILD_LVMLOCKD=yes
|
BUILD_LVMLOCKD=yes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Build lvmlockddlm
|
dnl -- Build lockddlm
|
||||||
AC_MSG_CHECKING(whether to build lvmlockddlm)
|
AC_MSG_CHECKING(whether to build lockddlm)
|
||||||
AC_ARG_ENABLE(lvmlockd-dlm,
|
AC_ARG_ENABLE(lockd-dlm,
|
||||||
AC_HELP_STRING([--enable-lvmlockd-dlm],
|
AC_HELP_STRING([--enable-lockd-dlm],
|
||||||
[enable the LVM lock daemon using dlm]),
|
[enable the LVM lock daemon using dlm]),
|
||||||
LOCKDDLM=$enableval)
|
LOCKDDLM=$enableval)
|
||||||
AC_MSG_RESULT($LOCKDDLM)
|
AC_MSG_RESULT($LOCKDDLM)
|
||||||
@@ -1185,10 +1188,8 @@ AC_MSG_CHECKING(whether to build lvmlockd)
|
|||||||
AC_MSG_RESULT($BUILD_LVMLOCKD)
|
AC_MSG_RESULT($BUILD_LVMLOCKD)
|
||||||
|
|
||||||
if test "$BUILD_LVMLOCKD" = yes; then
|
if test "$BUILD_LVMLOCKD" = yes; then
|
||||||
AS_IF([test "$LVMPOLLD" = no], [AC_MSG_ERROR([cannot build lvmlockd with --disable-lvmpolld.])])
|
AS_IF([test -n "$BUILD_LVMPOLLD"], [BUILD_LVMPOLLD=yes; AC_MSG_WARN([Enabling lvmpolld - required by lvmlockd.])])
|
||||||
AS_IF([test "$LVMETAD" = no], [AC_MSG_ERROR([cannot build lvmlockd with --disable-lvmetad.])])
|
AS_IF([test -n "$BUILD_LVMETAD"], [BUILD_LVMETAD=yes; AC_MSG_WARN([Enabling lvmetad - required by lvmlockd.])])
|
||||||
AS_IF([test "$BUILD_LVMPOLLD" = no], [BUILD_LVMPOLLD=yes; AC_MSG_WARN([Enabling lvmpolld - required by lvmlockd.])])
|
|
||||||
AS_IF([test "$BUILD_LVMETAD" = no], [BUILD_LVMETAD=yes; AC_MSG_WARN([Enabling lvmetad - required by lvmlockd.])])
|
|
||||||
AC_MSG_CHECKING([defaults for use_lvmlockd])
|
AC_MSG_CHECKING([defaults for use_lvmlockd])
|
||||||
AC_ARG_ENABLE(use_lvmlockd,
|
AC_ARG_ENABLE(use_lvmlockd,
|
||||||
AC_HELP_STRING([--disable-use-lvmlockd],
|
AC_HELP_STRING([--disable-use-lvmlockd],
|
||||||
@@ -1267,80 +1268,53 @@ fi
|
|||||||
AC_DEFINE_UNQUOTED(DEFAULT_USE_LVMPOLLD, [$DEFAULT_USE_LVMPOLLD],
|
AC_DEFINE_UNQUOTED(DEFAULT_USE_LVMPOLLD, [$DEFAULT_USE_LVMPOLLD],
|
||||||
[Use lvmpolld by default.])
|
[Use lvmpolld by default.])
|
||||||
|
|
||||||
################################################################################
|
|
||||||
dnl -- Check dmfilemapd
|
|
||||||
AC_MSG_CHECKING(whether to build dmfilemapd)
|
|
||||||
AC_ARG_ENABLE(dmfilemapd, AC_HELP_STRING([--enable-dmfilemapd],
|
|
||||||
[enable the dmstats filemap daemon]),
|
|
||||||
BUILD_DMFILEMAPD=$enableval, BUILD_DMFILEMAPD=no)
|
|
||||||
AC_MSG_RESULT($BUILD_DMFILEMAPD)
|
|
||||||
AC_DEFINE([DMFILEMAPD], $BUILD_DMFILEMAPD, [Define to 1 to enable the device-mapper filemap daemon.])
|
|
||||||
|
|
||||||
dnl -- dmfilemapd requires FIEMAP
|
|
||||||
if test "$BUILD_DMFILEMAPD" = yes; then
|
|
||||||
AC_CHECK_HEADER([linux/fiemap.h], , [AC_MSG_ERROR(--enable-dmfilemapd requires fiemap.h)])
|
|
||||||
fi
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
dnl -- Build notifydbus
|
|
||||||
AC_MSG_CHECKING(whether to build notifydbus)
|
|
||||||
AC_ARG_ENABLE(notify-dbus,
|
|
||||||
AC_HELP_STRING([--enable-notify-dbus],
|
|
||||||
[enable LVM notification using dbus]),
|
|
||||||
NOTIFYDBUS_SUPPORT=$enableval, NOTIFYDBUS_SUPPORT=no)
|
|
||||||
AC_MSG_RESULT($NOTIFYDBUS_SUPPORT)
|
|
||||||
|
|
||||||
if test "$NOTIFYDBUS_SUPPORT" = yes; then
|
|
||||||
AC_DEFINE([NOTIFYDBUS_SUPPORT], 1, [Define to 1 to include code that uses dbus notification.])
|
|
||||||
SYSTEMD_LIBS="-lsystemd"
|
|
||||||
fi
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
dnl -- Look for dbus libraries
|
|
||||||
if test "$NOTIFYDBUS_SUPPORT" = yes; then
|
|
||||||
PKG_CHECK_MODULES(NOTIFY_DBUS, systemd >= 221, [HAVE_NOTIFY_DBUS=yes], $bailout)
|
|
||||||
fi
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
dnl -- Enable blkid wiping functionality
|
dnl -- Enable blkid wiping functionality
|
||||||
|
AC_MSG_CHECKING(whether to enable libblkid detection of signatures when wiping)
|
||||||
AC_ARG_ENABLE(blkid_wiping,
|
AC_ARG_ENABLE(blkid_wiping,
|
||||||
AC_HELP_STRING([--disable-blkid_wiping],
|
AC_HELP_STRING([--disable-blkid_wiping],
|
||||||
[disable libblkid detection of signatures when wiping and use native code instead]),
|
[disable libblkid detection of signatures when wiping and use native code instead]),
|
||||||
BLKID_WIPING=$enableval, BLKID_WIPING=maybe)
|
BLKID_WIPING=$enableval, BLKID_WIPING=maybe)
|
||||||
|
AC_MSG_RESULT($BLKID_WIPING)
|
||||||
|
|
||||||
DEFAULT_USE_BLKID_WIPING=0
|
|
||||||
if test "$BLKID_WIPING" != no; then
|
if test "$BLKID_WIPING" != no; then
|
||||||
pkg_config_init
|
pkg_config_init
|
||||||
PKG_CHECK_MODULES(BLKID, blkid >= 2.24,
|
PKG_CHECK_MODULES(BLKID, blkid >= 2.24,
|
||||||
[ BLKID_WIPING=yes
|
[test "$BLKID_WIPING" = maybe && BLKID_WIPING=yes],
|
||||||
BLKID_PC="blkid"
|
[if test "$BLKID_WIPING" = maybe; then
|
||||||
DEFAULT_USE_BLKID_WIPING=1
|
|
||||||
AC_DEFINE([BLKID_WIPING_SUPPORT], 1, [Define to 1 to use libblkid detection of signatures when wiping.])
|
|
||||||
], [if test "$BLKID_WIPING" = maybe; then
|
|
||||||
BLKID_WIPING=no
|
BLKID_WIPING=no
|
||||||
else
|
else
|
||||||
AC_MSG_ERROR([bailing out... blkid library >= 2.24 is required])
|
AC_MSG_ERROR([bailing out... blkid library >= 2.24 is required])
|
||||||
fi])
|
fi])
|
||||||
|
if test "$BLKID_WIPING" = yes; then
|
||||||
|
BLKID_PC="blkid"
|
||||||
|
DEFAULT_USE_BLKID_WIPING=1
|
||||||
|
AC_DEFINE([BLKID_WIPING_SUPPORT], 1, [Define to 1 to use libblkid detection of signatures when wiping.])
|
||||||
|
else
|
||||||
|
DEFAULT_USE_BLKID_WIPING=0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
DEFAULT_USE_BLKID_WIPING=0
|
||||||
fi
|
fi
|
||||||
AC_MSG_CHECKING([whether to enable libblkid detection of signatures when wiping])
|
|
||||||
AC_MSG_RESULT($BLKID_WIPING)
|
|
||||||
AC_DEFINE_UNQUOTED(DEFAULT_USE_BLKID_WIPING, [$DEFAULT_USE_BLKID_WIPING],
|
AC_DEFINE_UNQUOTED(DEFAULT_USE_BLKID_WIPING, [$DEFAULT_USE_BLKID_WIPING],
|
||||||
[Use blkid wiping by default.])
|
[Use blkid wiping by default.])
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Enable udev-systemd protocol to instantiate a service for background jobs
|
dnl -- Enable udev-systemd protocol to instantiate a service for background jobs
|
||||||
dnl -- Requires systemd version 205 at least (including support for systemd-run)
|
dnl -- Requires systemd version 205 at least (including support for systemd-run)
|
||||||
|
AC_MSG_CHECKING(whether to use udev-systemd protocol for jobs in background)
|
||||||
AC_ARG_ENABLE(udev-systemd-background-jobs,
|
AC_ARG_ENABLE(udev-systemd-background-jobs,
|
||||||
AC_HELP_STRING([--disable-udev-systemd-background-jobs],
|
AC_HELP_STRING([--disable-udev-systemd-background-jobs],
|
||||||
[disable udev-systemd protocol to instantiate a service for background job]),
|
[disable udev-systemd protocol to instantiate a service for background job]),
|
||||||
UDEV_SYSTEMD_BACKGROUND_JOBS=$enableval,
|
UDEV_SYSTEMD_BACKGROUND_JOBS=$enableval,
|
||||||
UDEV_SYSTEMD_BACKGROUND_JOBS=maybe)
|
UDEV_SYSTEMD_BACKGROUND_JOBS=maybe)
|
||||||
|
AC_MSG_RESULT($UDEV_SYSTEMD_BACKGROUND_JOBS)
|
||||||
|
|
||||||
if test "$UDEV_SYSTEMD_BACKGROUND_JOBS" != no; then
|
if test "$UDEV_SYSTEMD_BACKGROUND_JOBS" != no; then
|
||||||
pkg_config_init
|
pkg_config_init
|
||||||
PKG_CHECK_MODULES(SYSTEMD, systemd >= 205,
|
PKG_CHECK_MODULES(SYSTEMD, systemd >= 205,
|
||||||
[UDEV_SYSTEMD_BACKGROUND_JOBS=yes],
|
[test "$UDEV_SYSTEMD_BACKGROUND_JOBS" = maybe && UDEV_SYSTEMD_BACKGROUND_JOBS=yes],
|
||||||
[if test "$UDEV_SYSTEMD_BACKGROUND_JOBS" = maybe; then
|
[if test "$UDEV_SYSTEMD_BACKGROUND_JOBS" = maybe; then
|
||||||
UDEV_SYSTEMD_BACKGROUND_JOBS=no
|
UDEV_SYSTEMD_BACKGROUND_JOBS=no
|
||||||
else
|
else
|
||||||
@@ -1348,9 +1322,6 @@ if test "$UDEV_SYSTEMD_BACKGROUND_JOBS" != no; then
|
|||||||
fi])
|
fi])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_MSG_CHECKING(whether to use udev-systemd protocol for jobs in background)
|
|
||||||
AC_MSG_RESULT($UDEV_SYSTEMD_BACKGROUND_JOBS)
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Enable udev synchronisation
|
dnl -- Enable udev synchronisation
|
||||||
AC_MSG_CHECKING(whether to enable synchronisation with udev processing)
|
AC_MSG_CHECKING(whether to enable synchronisation with udev processing)
|
||||||
@@ -1454,8 +1425,6 @@ AC_SUBST([LVM2APP_LIB])
|
|||||||
test "$APPLIB" = yes \
|
test "$APPLIB" = yes \
|
||||||
&& LVM2APP_LIB=-llvm2app \
|
&& LVM2APP_LIB=-llvm2app \
|
||||||
|| LVM2APP_LIB=
|
|| LVM2APP_LIB=
|
||||||
AS_IF([test "$APPLIB"],
|
|
||||||
[AC_MSG_WARN([liblvm2app is deprecated. Use D-Bus API])])
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Enable cmdlib
|
dnl -- Enable cmdlib
|
||||||
@@ -1476,8 +1445,6 @@ AC_ARG_ENABLE(dbus-service,
|
|||||||
AC_HELP_STRING([--enable-dbus-service], [install D-Bus support]),
|
AC_HELP_STRING([--enable-dbus-service], [install D-Bus support]),
|
||||||
BUILD_LVMDBUSD=$enableval, BUILD_LVMDBUSD=no)
|
BUILD_LVMDBUSD=$enableval, BUILD_LVMDBUSD=no)
|
||||||
AC_MSG_RESULT($BUILD_LVMDBUSD)
|
AC_MSG_RESULT($BUILD_LVMDBUSD)
|
||||||
AS_IF([test "$NOTIFYDBUS_SUPPORT" = yes && test "BUILD_LVMDBUSD" = yes],
|
|
||||||
[AC_MSG_WARN([Building D-Bus support without D-Bus notifications.])])
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Enable Python liblvm2app bindings
|
dnl -- Enable Python liblvm2app bindings
|
||||||
@@ -1530,7 +1497,7 @@ if test "$PYTHON3_BINDINGS" = yes -o "$BUILD_LVMDBUSD" = yes; then
|
|||||||
PYTHON3_INCDIRS=`"$PYTHON3_CONFIG" --includes`
|
PYTHON3_INCDIRS=`"$PYTHON3_CONFIG" --includes`
|
||||||
PYTHON3_LIBDIRS=`"$PYTHON3_CONFIG" --libs`
|
PYTHON3_LIBDIRS=`"$PYTHON3_CONFIG" --libs`
|
||||||
PYTHON3DIR=$pythondir
|
PYTHON3DIR=$pythondir
|
||||||
test "$PYTHON3_BINDINGS" = yes && PYTHON_BINDINGS=yes
|
PYTHON_BINDINGS=yes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$BUILD_LVMDBUSD" = yes; then
|
if test "$BUILD_LVMDBUSD" = yes; then
|
||||||
@@ -1540,7 +1507,6 @@ if test "$BUILD_LVMDBUSD" = yes; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$PYTHON_BINDINGS" = yes -o "$PYTHON2_BINDINGS" = yes -o "$PYTHON3_BINDINGS" = yes; then
|
if test "$PYTHON_BINDINGS" = yes -o "$PYTHON2_BINDINGS" = yes -o "$PYTHON3_BINDINGS" = yes; then
|
||||||
AC_MSG_WARN([Python bindings are deprecated. Use D-Bus API])
|
|
||||||
test "$APPLIB" != yes && AC_MSG_ERROR([Python_bindings require --enable-applib])
|
test "$APPLIB" != yes && AC_MSG_ERROR([Python_bindings require --enable-applib])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1576,11 +1542,13 @@ dnl -- enable dmeventd handling
|
|||||||
AC_MSG_CHECKING(whether to use dmeventd)
|
AC_MSG_CHECKING(whether to use dmeventd)
|
||||||
AC_ARG_ENABLE(dmeventd, AC_HELP_STRING([--enable-dmeventd],
|
AC_ARG_ENABLE(dmeventd, AC_HELP_STRING([--enable-dmeventd],
|
||||||
[enable the device-mapper event daemon]),
|
[enable the device-mapper event daemon]),
|
||||||
BUILD_DMEVENTD=$enableval, BUILD_DMEVENTD=no)
|
DMEVENTD=$enableval)
|
||||||
AC_MSG_RESULT($BUILD_DMEVENTD)
|
AC_MSG_RESULT($DMEVENTD)
|
||||||
|
|
||||||
|
BUILD_DMEVENTD=$DMEVENTD
|
||||||
|
|
||||||
dnl -- dmeventd currently requires internal mirror support
|
dnl -- dmeventd currently requires internal mirror support
|
||||||
if test "$BUILD_DMEVENTD" = yes; then
|
if test "$DMEVENTD" = yes; then
|
||||||
if test "$MIRRORS" != internal; then
|
if test "$MIRRORS" != internal; then
|
||||||
AC_MSG_ERROR([--enable-dmeventd currently requires --with-mirrors=internal])
|
AC_MSG_ERROR([--enable-dmeventd currently requires --with-mirrors=internal])
|
||||||
fi
|
fi
|
||||||
@@ -1604,6 +1572,10 @@ AC_CHECK_LIB(c, canonicalize_file_name,
|
|||||||
AC_DEFINE([HAVE_CANONICALIZE_FILE_NAME], 1,
|
AC_DEFINE([HAVE_CANONICALIZE_FILE_NAME], 1,
|
||||||
[Define to 1 if canonicalize_file_name is available.]))
|
[Define to 1 if canonicalize_file_name is available.]))
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
dnl -- Clear default exec_prefix - install into /sbin rather than /usr/sbin
|
||||||
|
test "$exec_prefix" = NONE -a "$prefix" = NONE && exec_prefix=""
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Check for dlopen
|
dnl -- Check for dlopen
|
||||||
AC_CHECK_LIB(dl, dlopen,
|
AC_CHECK_LIB(dl, dlopen,
|
||||||
@@ -1660,16 +1632,13 @@ fi
|
|||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- Check for realtime clock support
|
dnl -- Check for realtime clock support
|
||||||
RT_LIBS=
|
|
||||||
HAVE_REALTIME=no
|
|
||||||
if test "$REALTIME" = yes; then
|
if test "$REALTIME" = yes; then
|
||||||
AC_CHECK_FUNCS([clock_gettime], HAVE_REALTIME=yes)
|
AC_CHECK_LIB(rt, clock_gettime, HAVE_REALTIME=yes, HAVE_REALTIME=no)
|
||||||
|
|
||||||
AS_IF([test "$HAVE_REALTIME" != yes], [ # try again with -lrt
|
|
||||||
AC_CHECK_LIB([rt], [clock_gettime], RT_LIBS="-lrt"; HAVE_REALTIME=yes)])
|
|
||||||
|
|
||||||
if test "$HAVE_REALTIME" = yes; then
|
if test "$HAVE_REALTIME" = yes; then
|
||||||
AC_DEFINE([HAVE_REALTIME], 1, [Define to 1 to include support for realtime clock.])
|
AC_DEFINE([HAVE_REALTIME], 1, [Define to 1 to include support for realtime clock.])
|
||||||
|
LIBS="-lrt $LIBS"
|
||||||
|
RT_LIB="-lrt"
|
||||||
else
|
else
|
||||||
AC_MSG_WARN(Disabling realtime clock)
|
AC_MSG_WARN(Disabling realtime clock)
|
||||||
fi
|
fi
|
||||||
@@ -1713,7 +1682,6 @@ Note: (n)curses also seems to work as a substitute for termcap. This was
|
|||||||
AC_DEFINE([READLINE_SUPPORT], 1,
|
AC_DEFINE([READLINE_SUPPORT], 1,
|
||||||
[Define to 1 to include the LVM readline shell.])
|
[Define to 1 to include the LVM readline shell.])
|
||||||
dnl -- Try only with -lreadline and check for different symbol
|
dnl -- Try only with -lreadline and check for different symbol
|
||||||
READLINE=yes
|
|
||||||
LIBS=$lvm_saved_libs
|
LIBS=$lvm_saved_libs
|
||||||
AC_CHECK_LIB([readline], [rl_line_buffer],
|
AC_CHECK_LIB([readline], [rl_line_buffer],
|
||||||
[ READLINE_LIBS="-lreadline" ], [
|
[ READLINE_LIBS="-lreadline" ], [
|
||||||
@@ -1820,16 +1788,13 @@ dnl -- Ensure additional headers required
|
|||||||
if test "$READLINE" = yes; then
|
if test "$READLINE" = yes; then
|
||||||
AC_CHECK_HEADERS(readline/readline.h readline/history.h,,hard_bailout)
|
AC_CHECK_HEADERS(readline/readline.h readline/history.h,,hard_bailout)
|
||||||
fi
|
fi
|
||||||
AC_MSG_CHECKING(whether to enable readline)
|
|
||||||
AC_MSG_RESULT($READLINE)
|
|
||||||
|
|
||||||
if test "$BUILD_CMIRRORD" = yes; then
|
if test "$BUILD_CMIRRORD" = yes; then
|
||||||
AC_CHECK_FUNCS(atexit,,hard_bailout)
|
AC_CHECK_FUNCS(atexit,,hard_bailout)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$BUILD_LVMLOCKD" = yes; then
|
if test "$BUILD_LVMLOCKD" = yes; then
|
||||||
AS_IF([test "$HAVE_REALTIME" != yes], [AC_MSG_ERROR([Realtime clock support is mandatory for lvmlockd.])])
|
AC_CHECK_FUNCS(clock_gettime strtoull,,hard_bailout)
|
||||||
AC_CHECK_FUNCS(strtoull,,hard_bailout)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$BUILD_LVMPOLLD" = yes; then
|
if test "$BUILD_LVMPOLLD" = yes; then
|
||||||
@@ -1849,7 +1814,7 @@ if test "$CLUSTER" != none; then
|
|||||||
AC_CHECK_FUNCS(socket,,hard_bailout)
|
AC_CHECK_FUNCS(socket,,hard_bailout)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$BUILD_DMEVENTD" = yes; then
|
if test "$DMEVENTD" = yes; then
|
||||||
AC_CHECK_HEADERS(arpa/inet.h,,hard_bailout)
|
AC_CHECK_HEADERS(arpa/inet.h,,hard_bailout)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1865,10 +1830,6 @@ if test "$UDEV_SYNC" = yes; then
|
|||||||
AC_CHECK_HEADERS(sys/ipc.h sys/sem.h,,hard_bailout)
|
AC_CHECK_HEADERS(sys/ipc.h sys/sem.h,,hard_bailout)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$BUILD_DMFILEMAPD" = yes; then
|
|
||||||
AC_CHECK_HEADERS([sys/inotify.h],,hard_bailout)
|
|
||||||
fi
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
AC_PATH_TOOL(MODPROBE_CMD, modprobe)
|
AC_PATH_TOOL(MODPROBE_CMD, modprobe)
|
||||||
|
|
||||||
@@ -1876,19 +1837,18 @@ if test -n "$MODPROBE_CMD"; then
|
|||||||
AC_DEFINE_UNQUOTED([MODPROBE_CMD], ["$MODPROBE_CMD"], [The path to 'modprobe', if available.])
|
AC_DEFINE_UNQUOTED([MODPROBE_CMD], ["$MODPROBE_CMD"], [The path to 'modprobe', if available.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
SYSCONFDIR="$(eval echo $(eval echo $sysconfdir))"
|
|
||||||
|
|
||||||
SBINDIR="$(eval echo $(eval echo $sbindir))"
|
lvm_exec_prefix=$exec_prefix
|
||||||
LVM_PATH="$SBINDIR/lvm"
|
test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$prefix
|
||||||
|
test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$ac_default_prefix
|
||||||
|
LVM_PATH="$lvm_exec_prefix/sbin/lvm"
|
||||||
AC_DEFINE_UNQUOTED(LVM_PATH, ["$LVM_PATH"], [Path to lvm binary.])
|
AC_DEFINE_UNQUOTED(LVM_PATH, ["$LVM_PATH"], [Path to lvm binary.])
|
||||||
|
|
||||||
USRSBINDIR="$(eval echo $(eval echo $usrsbindir))"
|
clvmd_prefix=$ac_default_prefix
|
||||||
CLVMD_PATH="$USRSBINDIR/clvmd"
|
test "$prefix" != NONE && clvmd_prefix=$prefix
|
||||||
|
CLVMD_PATH="$clvmd_prefix/sbin/clvmd"
|
||||||
AC_DEFINE_UNQUOTED(CLVMD_PATH, ["$CLVMD_PATH"], [Path to clvmd binary.])
|
AC_DEFINE_UNQUOTED(CLVMD_PATH, ["$CLVMD_PATH"], [Path to clvmd binary.])
|
||||||
|
|
||||||
FSADM_PATH="$SBINDIR/fsadm"
|
|
||||||
AC_DEFINE_UNQUOTED(FSADM_PATH, ["$FSADM_PATH"], [Path to fsadm binary.])
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
dnl -- dmeventd pidfile and executable path
|
dnl -- dmeventd pidfile and executable path
|
||||||
if test "$BUILD_DMEVENTD" = yes; then
|
if test "$BUILD_DMEVENTD" = yes; then
|
||||||
@@ -1906,7 +1866,7 @@ if test "$BUILD_DMEVENTD" = yes; then
|
|||||||
AC_HELP_STRING([--with-dmeventd-path=PATH],
|
AC_HELP_STRING([--with-dmeventd-path=PATH],
|
||||||
[dmeventd path [EPREFIX/sbin/dmeventd]]),
|
[dmeventd path [EPREFIX/sbin/dmeventd]]),
|
||||||
DMEVENTD_PATH=$withval,
|
DMEVENTD_PATH=$withval,
|
||||||
DMEVENTD_PATH="$SBINDIR/dmeventd")
|
DMEVENTD_PATH="$lvm_exec_prefix/sbin/dmeventd")
|
||||||
AC_DEFINE_UNQUOTED(DMEVENTD_PATH, ["$DMEVENTD_PATH"],
|
AC_DEFINE_UNQUOTED(DMEVENTD_PATH, ["$DMEVENTD_PATH"],
|
||||||
[Path to dmeventd binary.])
|
[Path to dmeventd binary.])
|
||||||
fi
|
fi
|
||||||
@@ -1949,18 +1909,10 @@ AC_ARG_WITH(default-cache-subdir,
|
|||||||
AC_DEFINE_UNQUOTED(DEFAULT_CACHE_SUBDIR, ["$DEFAULT_CACHE_SUBDIR"],
|
AC_DEFINE_UNQUOTED(DEFAULT_CACHE_SUBDIR, ["$DEFAULT_CACHE_SUBDIR"],
|
||||||
[Name of default metadata cache subdirectory.])
|
[Name of default metadata cache subdirectory.])
|
||||||
|
|
||||||
# Select default system locking dir, prefer /run/lock over /var/lock
|
|
||||||
DEFAULT_SYS_LOCK_DIR="$RUN_DIR/lock"
|
|
||||||
test -d "$DEFAULT_SYS_LOCK_DIR" || DEFAULT_SYS_LOCK_DIR="/var/lock"
|
|
||||||
|
|
||||||
# Support configurable locking subdir for lvm
|
|
||||||
AC_ARG_WITH(default-locking-dir,
|
AC_ARG_WITH(default-locking-dir,
|
||||||
AC_HELP_STRING([--with-default-locking-dir=DIR],
|
AC_HELP_STRING([--with-default-locking-dir=DIR],
|
||||||
[default locking directory [autodetect_lock_dir/lvm]]),
|
[default locking directory [/var/lock/lvm]]),
|
||||||
DEFAULT_LOCK_DIR=$withval,
|
DEFAULT_LOCK_DIR=$withval, DEFAULT_LOCK_DIR="/var/lock/lvm")
|
||||||
[AC_MSG_CHECKING(for default lock directory)
|
|
||||||
DEFAULT_LOCK_DIR="$DEFAULT_SYS_LOCK_DIR/lvm"
|
|
||||||
AC_MSG_RESULT($DEFAULT_LOCK_DIR)])
|
|
||||||
AC_DEFINE_UNQUOTED(DEFAULT_LOCK_DIR, ["$DEFAULT_LOCK_DIR"],
|
AC_DEFINE_UNQUOTED(DEFAULT_LOCK_DIR, ["$DEFAULT_LOCK_DIR"],
|
||||||
[Name of default locking directory.])
|
[Name of default locking directory.])
|
||||||
|
|
||||||
@@ -2001,8 +1953,6 @@ LVM_MINOR=`echo "$VER" | $AWK -F '.' '{print $2}'`
|
|||||||
LVM_PATCHLEVEL=`echo "$VER" | $AWK -F '[[(.]]' '{print $3}'`
|
LVM_PATCHLEVEL=`echo "$VER" | $AWK -F '[[(.]]' '{print $3}'`
|
||||||
LVM_LIBAPI=`echo "$VER" | $AWK -F '[[()]]' '{print $2}'`
|
LVM_LIBAPI=`echo "$VER" | $AWK -F '[[()]]' '{print $2}'`
|
||||||
|
|
||||||
AC_DEFINE_UNQUOTED(LVM_CONFIGURE_LINE, "$CONFIGURE_LINE", [configure command line used])
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
AC_SUBST(APPLIB)
|
AC_SUBST(APPLIB)
|
||||||
AC_SUBST(AWK)
|
AC_SUBST(AWK)
|
||||||
@@ -2015,7 +1965,6 @@ AC_SUBST(BUILD_LVMPOLLD)
|
|||||||
AC_SUBST(BUILD_LVMLOCKD)
|
AC_SUBST(BUILD_LVMLOCKD)
|
||||||
AC_SUBST(BUILD_LOCKDSANLOCK)
|
AC_SUBST(BUILD_LOCKDSANLOCK)
|
||||||
AC_SUBST(BUILD_LOCKDDLM)
|
AC_SUBST(BUILD_LOCKDDLM)
|
||||||
AC_SUBST(BUILD_DMFILEMAPD)
|
|
||||||
AC_SUBST(CACHE)
|
AC_SUBST(CACHE)
|
||||||
AC_SUBST(CFLAGS)
|
AC_SUBST(CFLAGS)
|
||||||
AC_SUBST(CFLOW_CMD)
|
AC_SUBST(CFLOW_CMD)
|
||||||
@@ -2054,7 +2003,6 @@ AC_SUBST(DEFAULT_RAID10_SEGTYPE)
|
|||||||
AC_SUBST(DEFAULT_RUN_DIR)
|
AC_SUBST(DEFAULT_RUN_DIR)
|
||||||
AC_SUBST(DEFAULT_SPARSE_SEGTYPE)
|
AC_SUBST(DEFAULT_SPARSE_SEGTYPE)
|
||||||
AC_SUBST(DEFAULT_SYS_DIR)
|
AC_SUBST(DEFAULT_SYS_DIR)
|
||||||
AC_SUBST(DEFAULT_SYS_LOCK_DIR)
|
|
||||||
AC_SUBST(DEFAULT_USE_BLKID_WIPING)
|
AC_SUBST(DEFAULT_USE_BLKID_WIPING)
|
||||||
AC_SUBST(DEFAULT_USE_LVMETAD)
|
AC_SUBST(DEFAULT_USE_LVMETAD)
|
||||||
AC_SUBST(DEFAULT_USE_LVMPOLLD)
|
AC_SUBST(DEFAULT_USE_LVMPOLLD)
|
||||||
@@ -2063,11 +2011,11 @@ AC_SUBST(DEVMAPPER)
|
|||||||
AC_SUBST(DLM_CFLAGS)
|
AC_SUBST(DLM_CFLAGS)
|
||||||
AC_SUBST(DLM_LIBS)
|
AC_SUBST(DLM_LIBS)
|
||||||
AC_SUBST(DL_LIBS)
|
AC_SUBST(DL_LIBS)
|
||||||
|
AC_SUBST(DMEVENTD)
|
||||||
AC_SUBST(DMEVENTD_PATH)
|
AC_SUBST(DMEVENTD_PATH)
|
||||||
AC_SUBST(DM_LIB_PATCHLEVEL)
|
AC_SUBST(DM_LIB_PATCHLEVEL)
|
||||||
AC_SUBST(ELDFLAGS)
|
AC_SUBST(ELDFLAGS)
|
||||||
AC_SUBST(FSADM)
|
AC_SUBST(FSADM)
|
||||||
AC_SUBST(FSADM_PATH)
|
|
||||||
AC_SUBST(BLKDEACTIVATE)
|
AC_SUBST(BLKDEACTIVATE)
|
||||||
AC_SUBST(HAVE_LIBDL)
|
AC_SUBST(HAVE_LIBDL)
|
||||||
AC_SUBST(HAVE_REALTIME)
|
AC_SUBST(HAVE_REALTIME)
|
||||||
@@ -2093,7 +2041,6 @@ AC_SUBST(MIRRORS)
|
|||||||
AC_SUBST(MSGFMT)
|
AC_SUBST(MSGFMT)
|
||||||
AC_SUBST(OCF)
|
AC_SUBST(OCF)
|
||||||
AC_SUBST(OCFDIR)
|
AC_SUBST(OCFDIR)
|
||||||
AC_SUBST(ODIRECT)
|
|
||||||
AC_SUBST(PKGCONFIG)
|
AC_SUBST(PKGCONFIG)
|
||||||
AC_SUBST(POOL)
|
AC_SUBST(POOL)
|
||||||
AC_SUBST(M_LIBS)
|
AC_SUBST(M_LIBS)
|
||||||
@@ -2112,18 +2059,15 @@ AC_SUBST(PYTHON3DIR)
|
|||||||
AC_SUBST(QUORUM_CFLAGS)
|
AC_SUBST(QUORUM_CFLAGS)
|
||||||
AC_SUBST(QUORUM_LIBS)
|
AC_SUBST(QUORUM_LIBS)
|
||||||
AC_SUBST(RAID)
|
AC_SUBST(RAID)
|
||||||
AC_SUBST(RT_LIBS)
|
AC_SUBST(RT_LIB)
|
||||||
AC_SUBST(READLINE_LIBS)
|
AC_SUBST(READLINE_LIBS)
|
||||||
AC_SUBST(REPLICATORS)
|
AC_SUBST(REPLICATORS)
|
||||||
AC_SUBST(SACKPT_CFLAGS)
|
AC_SUBST(SACKPT_CFLAGS)
|
||||||
AC_SUBST(SACKPT_LIBS)
|
AC_SUBST(SACKPT_LIBS)
|
||||||
AC_SUBST(SALCK_CFLAGS)
|
AC_SUBST(SALCK_CFLAGS)
|
||||||
AC_SUBST(SALCK_LIBS)
|
AC_SUBST(SALCK_LIBS)
|
||||||
AC_SUBST(SBINDIR)
|
|
||||||
AC_SUBST(SELINUX_LIBS)
|
AC_SUBST(SELINUX_LIBS)
|
||||||
AC_SUBST(SELINUX_PC)
|
AC_SUBST(SELINUX_PC)
|
||||||
AC_SUBST(SYSCONFDIR)
|
|
||||||
AC_SUBST(SYSTEMD_LIBS)
|
|
||||||
AC_SUBST(SNAPSHOTS)
|
AC_SUBST(SNAPSHOTS)
|
||||||
AC_SUBST(STATICDIR)
|
AC_SUBST(STATICDIR)
|
||||||
AC_SUBST(STATIC_LINK)
|
AC_SUBST(STATIC_LINK)
|
||||||
@@ -2145,7 +2089,6 @@ AC_SUBST(UDEV_SYSTEMD_BACKGROUND_JOBS)
|
|||||||
AC_SUBST(UDEV_RULE_EXEC_DETECTION)
|
AC_SUBST(UDEV_RULE_EXEC_DETECTION)
|
||||||
AC_SUBST(UDEV_HAS_BUILTIN_BLKID)
|
AC_SUBST(UDEV_HAS_BUILTIN_BLKID)
|
||||||
AC_SUBST(USE_TRACKING)
|
AC_SUBST(USE_TRACKING)
|
||||||
AC_SUBST(USRSBINDIR)
|
|
||||||
AC_SUBST(VALGRIND_POOL)
|
AC_SUBST(VALGRIND_POOL)
|
||||||
AC_SUBST(WRITE_INSTALL)
|
AC_SUBST(WRITE_INSTALL)
|
||||||
AC_SUBST(DMEVENTD_PIDFILE)
|
AC_SUBST(DMEVENTD_PIDFILE)
|
||||||
@@ -2184,11 +2127,7 @@ daemons/dmeventd/plugins/raid/Makefile
|
|||||||
daemons/dmeventd/plugins/mirror/Makefile
|
daemons/dmeventd/plugins/mirror/Makefile
|
||||||
daemons/dmeventd/plugins/snapshot/Makefile
|
daemons/dmeventd/plugins/snapshot/Makefile
|
||||||
daemons/dmeventd/plugins/thin/Makefile
|
daemons/dmeventd/plugins/thin/Makefile
|
||||||
daemons/dmfilemapd/Makefile
|
|
||||||
daemons/lvmdbusd/Makefile
|
daemons/lvmdbusd/Makefile
|
||||||
daemons/lvmdbusd/lvmdbusd
|
|
||||||
daemons/lvmdbusd/lvmdb.py
|
|
||||||
daemons/lvmdbusd/lvm_shell_proxy.py
|
|
||||||
daemons/lvmdbusd/path.py
|
daemons/lvmdbusd/path.py
|
||||||
daemons/lvmetad/Makefile
|
daemons/lvmetad/Makefile
|
||||||
daemons/lvmpolld/Makefile
|
daemons/lvmpolld/Makefile
|
||||||
@@ -2205,6 +2144,7 @@ lib/format1/Makefile
|
|||||||
lib/format_pool/Makefile
|
lib/format_pool/Makefile
|
||||||
lib/locking/Makefile
|
lib/locking/Makefile
|
||||||
lib/mirror/Makefile
|
lib/mirror/Makefile
|
||||||
|
lib/replicator/Makefile
|
||||||
include/lvm-version.h
|
include/lvm-version.h
|
||||||
lib/raid/Makefile
|
lib/raid/Makefile
|
||||||
lib/snapshot/Makefile
|
lib/snapshot/Makefile
|
||||||
@@ -2246,7 +2186,6 @@ scripts/lvm2_monitoring_init_red_hat
|
|||||||
scripts/lvm2_monitoring_systemd_red_hat.service
|
scripts/lvm2_monitoring_systemd_red_hat.service
|
||||||
scripts/lvm2_pvscan_systemd_red_hat@.service
|
scripts/lvm2_pvscan_systemd_red_hat@.service
|
||||||
scripts/lvm2_tmpfiles_red_hat.conf
|
scripts/lvm2_tmpfiles_red_hat.conf
|
||||||
scripts/lvmdump.sh
|
|
||||||
scripts/Makefile
|
scripts/Makefile
|
||||||
test/Makefile
|
test/Makefile
|
||||||
test/api/Makefile
|
test/api/Makefile
|
||||||
@@ -2263,14 +2202,10 @@ AS_IF([test -n "$THIN_CONFIGURE_WARN"],
|
|||||||
[AC_MSG_WARN([Support for thin provisioning is limited since some thin provisioning tools are missing!])])
|
[AC_MSG_WARN([Support for thin provisioning is limited since some thin provisioning tools are missing!])])
|
||||||
|
|
||||||
AS_IF([test -n "$THIN_CHECK_VERSION_WARN"],
|
AS_IF([test -n "$THIN_CHECK_VERSION_WARN"],
|
||||||
[AC_MSG_WARN([You should also install latest thin_check vsn 0.7.0 (or later) for lvm2 thin provisioning])])
|
[AC_MSG_WARN([You should also install thin_check vsn 0.3.2 (or later) to use lvm2 thin provisioning])])
|
||||||
|
|
||||||
AS_IF([test -n "$CACHE_CONFIGURE_WARN"],
|
AS_IF([test -n "$CACHE_CONFIGURE_WARN"],
|
||||||
[AC_MSG_WARN([Support for cache is limited since some cache tools are missing!])])
|
[AC_MSG_WARN([Support for cache is limited since some cache tools are missing!])])
|
||||||
|
|
||||||
AS_IF([test -n "$CACHE_CHECK_VERSION_WARN"],
|
|
||||||
[AC_MSG_WARN([You should install latest cache_check vsn 0.7.0 to use lvm2 cache metadata format 2])])
|
|
||||||
|
|
||||||
|
|
||||||
AS_IF([test "$ODIRECT" != yes],
|
AS_IF([test "$ODIRECT" != yes],
|
||||||
[AC_MSG_WARN([O_DIRECT disabled: low-memory pvmove may lock up])])
|
[AC_MSG_WARN([O_DIRECT disabled: low-memory pvmove may lock up])])
|
||||||
|
|||||||
@@ -41,19 +41,6 @@ struct lv_segment *last_seg(const struct logical_volume *lv)
|
|||||||
return ((struct lv_segment **)lv)[0];
|
return ((struct lv_segment **)lv)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile)
|
|
||||||
{
|
|
||||||
return "STRING";
|
|
||||||
}
|
|
||||||
|
|
||||||
struct logical_volume *origin_from_cow(const struct logical_volume *lv)
|
|
||||||
{
|
|
||||||
if (lv)
|
|
||||||
return lv;
|
|
||||||
|
|
||||||
__coverity_panic__();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simple_memccpy() from glibc */
|
/* simple_memccpy() from glibc */
|
||||||
void *memccpy(void *dest, const void *src, int c, size_t n)
|
void *memccpy(void *dest, const void *src, int c, size_t n)
|
||||||
{
|
{
|
||||||
@@ -84,17 +71,6 @@ void model_FD_ZERO(void *fdset)
|
|||||||
((long*)fdset)[i] = 0;
|
((long*)fdset)[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Resent Coverity reports quite weird errors... */
|
|
||||||
int *__errno_location(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
const unsigned short **__ctype_b_loc (void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Added extra pointer check to not need these models,
|
* Added extra pointer check to not need these models,
|
||||||
* for now just keep then in file
|
* for now just keep then in file
|
||||||
|
|||||||
@@ -48,12 +48,8 @@ ifeq ("@BUILD_LVMDBUSD@", "yes")
|
|||||||
SUBDIRS += lvmdbusd
|
SUBDIRS += lvmdbusd
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ("@BUILD_DMFILEMAPD@", "yes")
|
|
||||||
SUBDIRS += dmfilemapd
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(MAKECMDGOALS),distclean)
|
ifeq ($(MAKECMDGOALS),distclean)
|
||||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd dmfilemapd
|
SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
include $(top_builddir)/make.tmpl
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ SALCK_LIBS = @SALCK_LIBS@
|
|||||||
SALCK_CFLAGS = @SALCK_CFLAGS@
|
SALCK_CFLAGS = @SALCK_CFLAGS@
|
||||||
|
|
||||||
SOURCES = \
|
SOURCES = \
|
||||||
clvmd-command.c\
|
clvmd-command.c \
|
||||||
clvmd.c\
|
clvmd.c \
|
||||||
lvm-functions.c\
|
lvm-functions.c \
|
||||||
refresh_clvmd.c
|
refresh_clvmd.c
|
||||||
|
|
||||||
ifneq (,$(findstring cman,, "@CLVMD@,"))
|
ifneq (,$(findstring cman,, "@CLVMD@,"))
|
||||||
@@ -72,17 +72,26 @@ endif
|
|||||||
TARGETS = \
|
TARGETS = \
|
||||||
clvmd
|
clvmd
|
||||||
|
|
||||||
|
LVMLIBS = $(LVMINTERNAL_LIBS)
|
||||||
|
|
||||||
|
ifeq ("@DMEVENTD@", "yes")
|
||||||
|
LVMLIBS += -ldevmapper-event
|
||||||
|
endif
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
include $(top_builddir)/make.tmpl
|
||||||
|
|
||||||
LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS)
|
LVMLIBS += -ldevmapper
|
||||||
|
LIBS += $(PTHREAD_LIBS)
|
||||||
|
|
||||||
CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS)
|
CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS)
|
||||||
|
LDFLAGS += $(EXTRA_EXEC_LDFLAGS)
|
||||||
|
|
||||||
INSTALL_TARGETS = \
|
INSTALL_TARGETS = \
|
||||||
install_clvmd
|
install_clvmd
|
||||||
|
|
||||||
clvmd: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
|
clvmd: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
|
$(CC) $(CFLAGS) $(LDFLAGS) -o clvmd $(OBJECTS) \
|
||||||
-o clvmd $(OBJECTS) $(LMLIBS) $(LIBS)
|
$(LVMLIBS) $(LMLIBS) $(LIBS)
|
||||||
|
|
||||||
.PHONY: install_clvmd
|
.PHONY: install_clvmd
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ struct clvm_header {
|
|||||||
#define CLVMD_FLAG_REMOTE 8 /* Do this on all nodes except for the local node */
|
#define CLVMD_FLAG_REMOTE 8 /* Do this on all nodes except for the local node */
|
||||||
|
|
||||||
/* Name of the local socket to communicate between lvm and clvmd */
|
/* Name of the local socket to communicate between lvm and clvmd */
|
||||||
#define CLVMD_SOCKNAME DEFAULT_RUN_DIR "/clvmd.sock"
|
static const char CLVMD_SOCKNAME[]= DEFAULT_RUN_DIR "/clvmd.sock";
|
||||||
|
|
||||||
/* Internal commands & replies */
|
/* Internal commands & replies */
|
||||||
#define CLVMD_CMD_REPLY 1
|
#define CLVMD_CMD_REPLY 1
|
||||||
|
|||||||
@@ -171,10 +171,8 @@ int do_command(struct local_client *client, struct clvm_header *msg, int msglen,
|
|||||||
|
|
||||||
/* Check the status of the command and return the error text */
|
/* Check the status of the command and return the error text */
|
||||||
if (status) {
|
if (status) {
|
||||||
if (*buf)
|
*retlen = 1 + ((*buf) ? dm_snprintf(*buf, buflen, "%s",
|
||||||
*retlen = dm_snprintf(*buf, buflen, "%s", strerror(status)) + 1;
|
strerror(status)) : -1);
|
||||||
else
|
|
||||||
*retlen = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
@@ -208,7 +206,7 @@ static int lock_vg(struct local_client *client)
|
|||||||
lock_mode = ((int) lock_cmd & LCK_TYPE_MASK);
|
lock_mode = ((int) lock_cmd & LCK_TYPE_MASK);
|
||||||
/* lock_flags = args[1]; */
|
/* lock_flags = args[1]; */
|
||||||
lockname = &args[2];
|
lockname = &args[2];
|
||||||
DEBUGLOG("(%p) doing PRE command LOCK_VG '%s' at %x\n", client, lockname, lock_cmd);
|
DEBUGLOG("doing PRE command LOCK_VG '%s' at %x (client=%p)\n", lockname, lock_cmd, client);
|
||||||
|
|
||||||
if (lock_mode == LCK_UNLOCK) {
|
if (lock_mode == LCK_UNLOCK) {
|
||||||
if (!(lkid = (int) (long) dm_hash_lookup(lock_hash, lockname)))
|
if (!(lkid = (int) (long) dm_hash_lookup(lock_hash, lockname)))
|
||||||
@@ -325,7 +323,7 @@ void cmd_client_cleanup(struct local_client *client)
|
|||||||
int lkid;
|
int lkid;
|
||||||
char *lockname;
|
char *lockname;
|
||||||
|
|
||||||
DEBUGLOG("(%p) Client thread cleanup\n", client);
|
DEBUGLOG("Client thread cleanup (%p)\n", client);
|
||||||
if (!client->bits.localsock.private)
|
if (!client->bits.localsock.private)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -334,7 +332,7 @@ void cmd_client_cleanup(struct local_client *client)
|
|||||||
dm_hash_iterate(v, lock_hash) {
|
dm_hash_iterate(v, lock_hash) {
|
||||||
lkid = (int)(long)dm_hash_get_data(lock_hash, v);
|
lkid = (int)(long)dm_hash_get_data(lock_hash, v);
|
||||||
lockname = dm_hash_get_key(lock_hash, v);
|
lockname = dm_hash_get_key(lock_hash, v);
|
||||||
DEBUGLOG("(%p) Cleanup: Unlocking lock %s %x\n", client, lockname, lkid);
|
DEBUGLOG("Cleanup (%p): Unlocking lock %s %x\n", client, lockname, lkid);
|
||||||
(void) sync_unlock(lockname, lkid);
|
(void) sync_unlock(lockname, lkid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -532,7 +532,6 @@ static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
|
|||||||
static int _cluster_send_message(const void *buf, int msglen, const char *csid,
|
static int _cluster_send_message(const void *buf, int msglen, const char *csid,
|
||||||
const char *errtext)
|
const char *errtext)
|
||||||
{
|
{
|
||||||
static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
struct iovec iov[2];
|
struct iovec iov[2];
|
||||||
cs_error_t err;
|
cs_error_t err;
|
||||||
int target_node;
|
int target_node;
|
||||||
@@ -547,10 +546,7 @@ static int _cluster_send_message(const void *buf, int msglen, const char *csid,
|
|||||||
iov[1].iov_base = (char *)buf;
|
iov[1].iov_base = (char *)buf;
|
||||||
iov[1].iov_len = msglen;
|
iov[1].iov_len = msglen;
|
||||||
|
|
||||||
pthread_mutex_lock(&_mutex);
|
|
||||||
err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
|
err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
|
||||||
pthread_mutex_unlock(&_mutex);
|
|
||||||
|
|
||||||
return cs_to_errno(err);
|
return cs_to_errno(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -425,6 +425,8 @@ static void _add_up_node(const char *csid)
|
|||||||
DEBUGLOG("openais_add_up_node %d\n", ninfo->nodeid);
|
DEBUGLOG("openais_add_up_node %d\n", ninfo->nodeid);
|
||||||
|
|
||||||
ninfo->state = NODE_CLVMD;
|
ninfo->state = NODE_CLVMD;
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call a callback for each node, so the caller knows whether it's up or down */
|
/* Call a callback for each node, so the caller knows whether it's up or down */
|
||||||
|
|||||||
@@ -58,7 +58,6 @@
|
|||||||
/* Head of the fd list. Also contains
|
/* Head of the fd list. Also contains
|
||||||
the cluster_socket details */
|
the cluster_socket details */
|
||||||
static struct local_client local_client_head;
|
static struct local_client local_client_head;
|
||||||
static int _local_client_count = 0;
|
|
||||||
|
|
||||||
static unsigned short global_xid = 0; /* Last transaction ID issued */
|
static unsigned short global_xid = 0; /* Last transaction ID issued */
|
||||||
|
|
||||||
@@ -69,37 +68,6 @@ static unsigned max_csid_len;
|
|||||||
static unsigned max_cluster_message;
|
static unsigned max_cluster_message;
|
||||||
static unsigned max_cluster_member_name_len;
|
static unsigned max_cluster_member_name_len;
|
||||||
|
|
||||||
static void _add_client(struct local_client *new_client, struct local_client *existing_client)
|
|
||||||
{
|
|
||||||
_local_client_count++;
|
|
||||||
DEBUGLOG("(%p) Adding listener for fd %d. (Now %d monitored fds.)\n", new_client, new_client->fd, _local_client_count);
|
|
||||||
new_client->next = existing_client->next;
|
|
||||||
existing_client->next = new_client;
|
|
||||||
}
|
|
||||||
|
|
||||||
int add_client(struct local_client *new_client)
|
|
||||||
{
|
|
||||||
_add_client(new_client, &local_client_head);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns 0 if delfd is found and removed from list */
|
|
||||||
static int _del_client(struct local_client *delfd)
|
|
||||||
{
|
|
||||||
struct local_client *lastfd, *thisfd;
|
|
||||||
|
|
||||||
for (lastfd = &local_client_head; (thisfd = lastfd->next); lastfd = thisfd)
|
|
||||||
if (thisfd == delfd) {
|
|
||||||
DEBUGLOG("(%p) Removing listener for fd %d\n", thisfd, thisfd->fd);
|
|
||||||
lastfd->next = delfd->next;
|
|
||||||
_local_client_count--;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Structure of items on the LVM thread list */
|
/* Structure of items on the LVM thread list */
|
||||||
struct lvm_thread_cmd {
|
struct lvm_thread_cmd {
|
||||||
struct dm_list list;
|
struct dm_list list;
|
||||||
@@ -124,7 +92,6 @@ static const size_t STACK_SIZE = 128 * 1024;
|
|||||||
static pthread_attr_t stack_attr;
|
static pthread_attr_t stack_attr;
|
||||||
static int lvm_thread_exit = 0;
|
static int lvm_thread_exit = 0;
|
||||||
static pthread_mutex_t lvm_thread_mutex;
|
static pthread_mutex_t lvm_thread_mutex;
|
||||||
static pthread_mutex_t _debuglog_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static pthread_cond_t lvm_thread_cond;
|
static pthread_cond_t lvm_thread_cond;
|
||||||
static pthread_barrier_t lvm_start_barrier;
|
static pthread_barrier_t lvm_start_barrier;
|
||||||
static struct dm_list lvm_cmd_head;
|
static struct dm_list lvm_cmd_head;
|
||||||
@@ -251,17 +218,14 @@ void debuglog(const char *fmt, ...)
|
|||||||
|
|
||||||
switch (clvmd_get_debug()) {
|
switch (clvmd_get_debug()) {
|
||||||
case DEBUG_STDERR:
|
case DEBUG_STDERR:
|
||||||
pthread_mutex_lock(&_debuglog_mutex);
|
|
||||||
va_start(ap,fmt);
|
va_start(ap,fmt);
|
||||||
time(&P);
|
time(&P);
|
||||||
fprintf(stderr, "CLVMD[%x]: %.15s ", (int)pthread_self(), ctime_r(&P, buf_ctime) + 4);
|
fprintf(stderr, "CLVMD[%x]: %.15s ", (int)pthread_self(), ctime_r(&P, buf_ctime) + 4);
|
||||||
vfprintf(stderr, fmt, ap);
|
vfprintf(stderr, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
pthread_mutex_unlock(&_debuglog_mutex);
|
|
||||||
break;
|
break;
|
||||||
case DEBUG_SYSLOG:
|
case DEBUG_SYSLOG:
|
||||||
pthread_mutex_lock(&_debuglog_mutex);
|
|
||||||
if (!syslog_init) {
|
if (!syslog_init) {
|
||||||
openlog("clvmd", LOG_PID, LOG_DAEMON);
|
openlog("clvmd", LOG_PID, LOG_DAEMON);
|
||||||
syslog_init = 1;
|
syslog_init = 1;
|
||||||
@@ -270,7 +234,6 @@ void debuglog(const char *fmt, ...)
|
|||||||
va_start(ap,fmt);
|
va_start(ap,fmt);
|
||||||
vsyslog(LOG_DEBUG, fmt, ap);
|
vsyslog(LOG_DEBUG, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
pthread_mutex_unlock(&_debuglog_mutex);
|
|
||||||
break;
|
break;
|
||||||
case DEBUG_OFF:
|
case DEBUG_OFF:
|
||||||
break;
|
break;
|
||||||
@@ -554,7 +517,7 @@ int main(int argc, char *argv[])
|
|||||||
/* Initialise the LVM thread variables */
|
/* Initialise the LVM thread variables */
|
||||||
dm_list_init(&lvm_cmd_head);
|
dm_list_init(&lvm_cmd_head);
|
||||||
if (pthread_attr_init(&stack_attr) ||
|
if (pthread_attr_init(&stack_attr) ||
|
||||||
pthread_attr_setstacksize(&stack_attr, STACK_SIZE + getpagesize())) {
|
pthread_attr_setstacksize(&stack_attr, STACK_SIZE)) {
|
||||||
log_sys_error("pthread_attr_init", "");
|
log_sys_error("pthread_attr_init", "");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -621,7 +584,6 @@ int main(int argc, char *argv[])
|
|||||||
local_client_head.fd = clops->get_main_cluster_fd();
|
local_client_head.fd = clops->get_main_cluster_fd();
|
||||||
local_client_head.type = CLUSTER_MAIN_SOCK;
|
local_client_head.type = CLUSTER_MAIN_SOCK;
|
||||||
local_client_head.callback = clops->cluster_fd_callback;
|
local_client_head.callback = clops->cluster_fd_callback;
|
||||||
_local_client_count++;
|
|
||||||
|
|
||||||
/* Add the local socket to the list */
|
/* Add the local socket to the list */
|
||||||
if (!(newfd = dm_zalloc(sizeof(struct local_client)))) {
|
if (!(newfd = dm_zalloc(sizeof(struct local_client)))) {
|
||||||
@@ -632,14 +594,14 @@ int main(int argc, char *argv[])
|
|||||||
newfd->fd = local_sock;
|
newfd->fd = local_sock;
|
||||||
newfd->type = LOCAL_RENDEZVOUS;
|
newfd->type = LOCAL_RENDEZVOUS;
|
||||||
newfd->callback = local_rendezvous_callback;
|
newfd->callback = local_rendezvous_callback;
|
||||||
|
newfd->next = local_client_head.next;
|
||||||
(void) add_client(newfd);
|
local_client_head.next = newfd;
|
||||||
|
|
||||||
/* This needs to be started after cluster initialisation
|
/* This needs to be started after cluster initialisation
|
||||||
as it may need to take out locks */
|
as it may need to take out locks */
|
||||||
DEBUGLOG("Starting LVM thread\n");
|
DEBUGLOG("Starting LVM thread\n");
|
||||||
DEBUGLOG("(%p) Main cluster socket fd %d with local socket %d (%p)\n",
|
DEBUGLOG("Main cluster socket fd %d (%p) with local socket %d (%p)\n",
|
||||||
&local_client_head, local_client_head.fd, newfd->fd, newfd);
|
local_client_head.fd, &local_client_head, newfd->fd, newfd);
|
||||||
|
|
||||||
/* Don't let anyone else to do work until we are started */
|
/* Don't let anyone else to do work until we are started */
|
||||||
if (pthread_create(&lvm_thread, &stack_attr, lvm_thread_fn, &lvm_params)) {
|
if (pthread_create(&lvm_thread, &stack_attr, lvm_thread_fn, &lvm_params)) {
|
||||||
@@ -675,7 +637,6 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
while ((delfd = local_client_head.next)) {
|
while ((delfd = local_client_head.next)) {
|
||||||
local_client_head.next = delfd->next;
|
local_client_head.next = delfd->next;
|
||||||
_local_client_count--;
|
|
||||||
/* Failing cleanup_zombie leaks... */
|
/* Failing cleanup_zombie leaks... */
|
||||||
if (delfd->type == LOCAL_SOCK && !cleanup_zombie(delfd))
|
if (delfd->type == LOCAL_SOCK && !cleanup_zombie(delfd))
|
||||||
cmd_client_cleanup(delfd); /* calls sync_unlock */
|
cmd_client_cleanup(delfd); /* calls sync_unlock */
|
||||||
@@ -737,13 +698,13 @@ static int local_rendezvous_callback(struct local_client *thisfd, char *buf,
|
|||||||
pthread_mutex_init(&newfd->bits.localsock.mutex, NULL);
|
pthread_mutex_init(&newfd->bits.localsock.mutex, NULL);
|
||||||
|
|
||||||
if (fcntl(client_fd, F_SETFD, 1))
|
if (fcntl(client_fd, F_SETFD, 1))
|
||||||
DEBUGLOG("(%p) Setting CLOEXEC on client fd %d failed: %s\n", thisfd, client_fd, strerror(errno));
|
DEBUGLOG("Setting CLOEXEC on client fd failed: %s\n", strerror(errno));
|
||||||
|
|
||||||
newfd->fd = client_fd;
|
newfd->fd = client_fd;
|
||||||
newfd->type = LOCAL_SOCK;
|
newfd->type = LOCAL_SOCK;
|
||||||
newfd->callback = local_sock_callback;
|
newfd->callback = local_sock_callback;
|
||||||
newfd->bits.localsock.all_success = 1;
|
newfd->bits.localsock.all_success = 1;
|
||||||
DEBUGLOG("(%p) Got new connection on fd %d\n", newfd, newfd->fd);
|
DEBUGLOG("Got new connection on fd %d (%p)\n", newfd->fd, newfd);
|
||||||
*new_client = newfd;
|
*new_client = newfd;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@@ -765,8 +726,8 @@ static int local_pipe_callback(struct local_client *thisfd, char *buf,
|
|||||||
if (len == sizeof(int))
|
if (len == sizeof(int))
|
||||||
memcpy(&status, buffer, sizeof(int));
|
memcpy(&status, buffer, sizeof(int));
|
||||||
|
|
||||||
DEBUGLOG("(%p) Read on pipe %d, %d bytes, status %d\n",
|
DEBUGLOG("Read on pipe %d, %d bytes, status %d\n",
|
||||||
thisfd, thisfd->fd, len, status);
|
thisfd->fd, len, status);
|
||||||
|
|
||||||
/* EOF on pipe or an error, close it */
|
/* EOF on pipe or an error, close it */
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
@@ -789,11 +750,11 @@ static int local_pipe_callback(struct local_client *thisfd, char *buf,
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
DEBUGLOG("(%p) Background routine status was %d, sock_client %p\n",
|
DEBUGLOG("Background routine status was %d, sock_client (%p)\n",
|
||||||
thisfd, status, sock_client);
|
status, sock_client);
|
||||||
/* But has the client gone away ?? */
|
/* But has the client gone away ?? */
|
||||||
if (!sock_client) {
|
if (!sock_client) {
|
||||||
DEBUGLOG("(%p) Got pipe response for dead client, ignoring it\n", thisfd);
|
DEBUGLOG("Got pipe response for dead client, ignoring it\n");
|
||||||
} else {
|
} else {
|
||||||
/* If error then just return that code */
|
/* If error then just return that code */
|
||||||
if (status)
|
if (status)
|
||||||
@@ -833,7 +794,7 @@ static void timedout_callback(struct local_client *client, const char *csid,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
clops->name_from_csid(csid, nodename);
|
clops->name_from_csid(csid, nodename);
|
||||||
DEBUGLOG("(%p) Checking for a reply from %s\n", client, nodename);
|
DEBUGLOG("Checking for a reply from %s\n", nodename);
|
||||||
pthread_mutex_lock(&client->bits.localsock.mutex);
|
pthread_mutex_lock(&client->bits.localsock.mutex);
|
||||||
|
|
||||||
reply = client->bits.localsock.replies;
|
reply = client->bits.localsock.replies;
|
||||||
@@ -843,7 +804,7 @@ static void timedout_callback(struct local_client *client, const char *csid,
|
|||||||
pthread_mutex_unlock(&client->bits.localsock.mutex);
|
pthread_mutex_unlock(&client->bits.localsock.mutex);
|
||||||
|
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
DEBUGLOG("(%p) Node %s timed-out\n", client, nodename);
|
DEBUGLOG("Node %s timed-out\n", nodename);
|
||||||
add_reply_to_list(client, ETIMEDOUT, csid,
|
add_reply_to_list(client, ETIMEDOUT, csid,
|
||||||
"Command timed out", 18);
|
"Command timed out", 18);
|
||||||
}
|
}
|
||||||
@@ -858,7 +819,7 @@ static void timedout_callback(struct local_client *client, const char *csid,
|
|||||||
*/
|
*/
|
||||||
static void request_timed_out(struct local_client *client)
|
static void request_timed_out(struct local_client *client)
|
||||||
{
|
{
|
||||||
DEBUGLOG("(%p) Request timed-out. padding\n", client);
|
DEBUGLOG("Request timed-out. padding\n");
|
||||||
clops->cluster_do_node_callback(client, timedout_callback);
|
clops->cluster_do_node_callback(client, timedout_callback);
|
||||||
|
|
||||||
if (!client->bits.localsock.threadid)
|
if (!client->bits.localsock.threadid)
|
||||||
@@ -892,11 +853,13 @@ static void main_loop(int cmd_timeout)
|
|||||||
while (!quit) {
|
while (!quit) {
|
||||||
fd_set in;
|
fd_set in;
|
||||||
int select_status;
|
int select_status;
|
||||||
struct local_client *thisfd, *nextfd;
|
struct local_client *thisfd;
|
||||||
struct timeval tv = { cmd_timeout, 0 };
|
struct timeval tv = { cmd_timeout, 0 };
|
||||||
int quorate = clops->is_quorate();
|
int quorate = clops->is_quorate();
|
||||||
int client_count = 0;
|
int client_count = 0;
|
||||||
int max_fd = 0;
|
int max_fd = 0;
|
||||||
|
struct local_client *lastfd = &local_client_head;
|
||||||
|
struct local_client *nextfd = local_client_head.next;
|
||||||
|
|
||||||
/* Wait on the cluster FD and all local sockets/pipes */
|
/* Wait on the cluster FD and all local sockets/pipes */
|
||||||
local_client_head.fd = clops->get_main_cluster_fd();
|
local_client_head.fd = clops->get_main_cluster_fd();
|
||||||
@@ -912,22 +875,21 @@ static void main_loop(int cmd_timeout)
|
|||||||
fprintf(stderr, "WARNING: Your cluster may freeze up if the number of clvmd file descriptors (%d) exceeds %d.\n", max_fd + 1, FD_SETSIZE);
|
fprintf(stderr, "WARNING: Your cluster may freeze up if the number of clvmd file descriptors (%d) exceeds %d.\n", max_fd + 1, FD_SETSIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (thisfd = &local_client_head; thisfd; thisfd = nextfd) {
|
for (thisfd = &local_client_head; thisfd; thisfd = nextfd, nextfd = thisfd ? thisfd->next : NULL) {
|
||||||
nextfd = thisfd->next;
|
|
||||||
|
|
||||||
if (thisfd->removeme && !cleanup_zombie(thisfd)) {
|
if (thisfd->removeme && !cleanup_zombie(thisfd)) {
|
||||||
/* cleanup_zombie might have removed the next list element */
|
struct local_client *free_fd = thisfd;
|
||||||
nextfd = thisfd->next;
|
lastfd->next = nextfd;
|
||||||
|
DEBUGLOG("removeme set for %p with %d monitored fds remaining\n", free_fd, client_count - 1);
|
||||||
(void) _del_client(thisfd);
|
|
||||||
|
|
||||||
DEBUGLOG("(%p) removeme set with %d monitored fds remaining\n", thisfd, _local_client_count);
|
|
||||||
|
|
||||||
/* Queue cleanup, this also frees the client struct */
|
/* Queue cleanup, this also frees the client struct */
|
||||||
add_to_lvmqueue(thisfd, NULL, 0, NULL);
|
add_to_lvmqueue(free_fd, NULL, 0, NULL);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastfd = thisfd;
|
||||||
|
|
||||||
if (thisfd->removeme)
|
if (thisfd->removeme)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -977,15 +939,16 @@ static void main_loop(int cmd_timeout)
|
|||||||
type == CLUSTER_INTERNAL)
|
type == CLUSTER_INTERNAL)
|
||||||
goto closedown;
|
goto closedown;
|
||||||
|
|
||||||
DEBUGLOG("(%p) ret == %d, errno = %d. removing client\n",
|
DEBUGLOG("ret == %d, errno = %d. removing client\n",
|
||||||
thisfd, ret, errno);
|
ret, errno);
|
||||||
thisfd->removeme = 1;
|
thisfd->removeme = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* New client...simply add it to the list */
|
/* New client...simply add it to the list */
|
||||||
if (newfd) {
|
if (newfd) {
|
||||||
_add_client(newfd, thisfd);
|
newfd->next = thisfd->next;
|
||||||
|
thisfd->next = newfd;
|
||||||
thisfd = newfd;
|
thisfd = newfd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1003,8 +966,8 @@ static void main_loop(int cmd_timeout)
|
|||||||
thisfd->bits.localsock.expected_replies !=
|
thisfd->bits.localsock.expected_replies !=
|
||||||
thisfd->bits.localsock.num_replies) {
|
thisfd->bits.localsock.num_replies) {
|
||||||
/* Send timed out message + replies we already have */
|
/* Send timed out message + replies we already have */
|
||||||
DEBUGLOG("Request to client %p timed-out (send: %ld, now: %ld)\n",
|
DEBUGLOG("Request timed-out (send: %ld, now: %ld)\n",
|
||||||
thisfd, thisfd->bits.localsock.sent_time, the_time);
|
thisfd->bits.localsock.sent_time, the_time);
|
||||||
|
|
||||||
thisfd->bits.localsock.all_success = 0;
|
thisfd->bits.localsock.all_success = 0;
|
||||||
|
|
||||||
@@ -1105,31 +1068,31 @@ static void be_daemon(int timeout)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default: /* Parent */
|
default: /* Parent */
|
||||||
(void) close(devnull);
|
|
||||||
(void) close(child_pipe[1]);
|
(void) close(child_pipe[1]);
|
||||||
wait_for_child(child_pipe[0], timeout); /* noreturn */
|
wait_for_child(child_pipe[0], timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Detach ourself from the calling environment */
|
/* Detach ourself from the calling environment */
|
||||||
if ((dup2(devnull, STDIN_FILENO) == -1) ||
|
if (close(0) || close(1) || close(2)) {
|
||||||
(dup2(devnull, STDOUT_FILENO) == -1) ||
|
perror("Error closing terminal FDs");
|
||||||
(dup2(devnull, STDERR_FILENO) == -1)) {
|
exit(4);
|
||||||
|
}
|
||||||
|
setsid();
|
||||||
|
|
||||||
|
if (dup2(devnull, 0) < 0 || dup2(devnull, 1) < 0
|
||||||
|
|| dup2(devnull, 2) < 0) {
|
||||||
perror("Error setting terminal FDs to /dev/null");
|
perror("Error setting terminal FDs to /dev/null");
|
||||||
log_error("Error setting terminal FDs to /dev/null: %m");
|
log_error("Error setting terminal FDs to /dev/null: %m");
|
||||||
exit(5);
|
exit(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((devnull > STDERR_FILENO) && close(devnull)) {
|
if ((devnull > STDERR_FILENO) && close(devnull)) {
|
||||||
log_sys_error("close", "/dev/null");
|
log_sys_error("close", "/dev/null");
|
||||||
exit(7);
|
exit(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chdir("/")) {
|
if (chdir("/")) {
|
||||||
log_error("Error setting current directory to /: %m");
|
log_error("Error setting current directory to /: %m");
|
||||||
exit(6);
|
exit(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
setsid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int verify_message(char *buf, int len)
|
static int verify_message(char *buf, int len)
|
||||||
@@ -1216,8 +1179,8 @@ static int cleanup_zombie(struct local_client *thisfd)
|
|||||||
if (!thisfd->bits.localsock.cleanup_needed)
|
if (!thisfd->bits.localsock.cleanup_needed)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
DEBUGLOG("(%p) EOF on local socket %d: inprogress=%d\n",
|
DEBUGLOG("EOF on local socket: inprogress=%d\n",
|
||||||
thisfd, thisfd->fd, thisfd->bits.localsock.in_progress);
|
thisfd->bits.localsock.in_progress);
|
||||||
|
|
||||||
if ((pipe_client = thisfd->bits.localsock.pipe_client))
|
if ((pipe_client = thisfd->bits.localsock.pipe_client))
|
||||||
pipe_client = pipe_client->bits.pipe.client;
|
pipe_client = pipe_client->bits.pipe.client;
|
||||||
@@ -1239,7 +1202,7 @@ static int cleanup_zombie(struct local_client *thisfd)
|
|||||||
|
|
||||||
/* Kill the subthread & free resources */
|
/* Kill the subthread & free resources */
|
||||||
if (thisfd->bits.localsock.threadid) {
|
if (thisfd->bits.localsock.threadid) {
|
||||||
DEBUGLOG("(%p) Waiting for pre&post thread\n", pipe_client);
|
DEBUGLOG("Waiting for pre&post thread (%p)\n", pipe_client);
|
||||||
pthread_mutex_lock(&thisfd->bits.localsock.mutex);
|
pthread_mutex_lock(&thisfd->bits.localsock.mutex);
|
||||||
thisfd->bits.localsock.state = PRE_COMMAND;
|
thisfd->bits.localsock.state = PRE_COMMAND;
|
||||||
thisfd->bits.localsock.finished = 1;
|
thisfd->bits.localsock.finished = 1;
|
||||||
@@ -1250,21 +1213,25 @@ static int cleanup_zombie(struct local_client *thisfd)
|
|||||||
(void **) &status)))
|
(void **) &status)))
|
||||||
log_sys_error("pthread_join", "");
|
log_sys_error("pthread_join", "");
|
||||||
|
|
||||||
DEBUGLOG("(%p) Joined pre&post thread\n", pipe_client);
|
DEBUGLOG("Joined pre&post thread\n");
|
||||||
|
|
||||||
thisfd->bits.localsock.threadid = 0;
|
thisfd->bits.localsock.threadid = 0;
|
||||||
|
|
||||||
/* Remove the pipe client */
|
/* Remove the pipe client */
|
||||||
if (thisfd->bits.localsock.pipe_client) {
|
if (thisfd->bits.localsock.pipe_client) {
|
||||||
struct local_client *delfd = thisfd->bits.localsock.pipe_client;
|
struct local_client *delfd;
|
||||||
|
struct local_client *lastfd;
|
||||||
|
|
||||||
(void) close(delfd->fd); /* Close pipe */
|
(void) close(thisfd->bits.localsock.pipe_client->fd); /* Close pipe */
|
||||||
(void) close(thisfd->bits.localsock.pipe);
|
(void) close(thisfd->bits.localsock.pipe);
|
||||||
|
|
||||||
/* Remove pipe client */
|
/* Remove pipe client */
|
||||||
if (!_del_client(delfd)) {
|
for (lastfd = &local_client_head; (delfd = lastfd->next); lastfd = delfd)
|
||||||
dm_free(delfd);
|
if (thisfd->bits.localsock.pipe_client == delfd) {
|
||||||
thisfd->bits.localsock.pipe_client = NULL;
|
thisfd->bits.localsock.pipe_client = NULL;
|
||||||
|
lastfd->next = delfd->next;
|
||||||
|
dm_free(delfd);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1296,7 +1263,7 @@ static int read_from_local_sock(struct local_client *thisfd)
|
|||||||
if (len == -1 && errno == EINTR)
|
if (len == -1 && errno == EINTR)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
DEBUGLOG("(%p) Read on local socket %d, len = %d\n", thisfd, thisfd->fd, len);
|
DEBUGLOG("Read on local socket %d, len = %d\n", thisfd->fd, len);
|
||||||
|
|
||||||
if (len && verify_message(buffer, len) < 0) {
|
if (len && verify_message(buffer, len) < 0) {
|
||||||
log_error("read_from_local_sock from %d len %d bad verify.",
|
log_error("read_from_local_sock from %d len %d bad verify.",
|
||||||
@@ -1370,15 +1337,15 @@ static int read_from_local_sock(struct local_client *thisfd)
|
|||||||
char *argptr = inheader->node + strlen(inheader->node) + 1;
|
char *argptr = inheader->node + strlen(inheader->node) + 1;
|
||||||
|
|
||||||
while (missing_len > 0) {
|
while (missing_len > 0) {
|
||||||
DEBUGLOG("(%p) got %d bytes, need another %d (total %d)\n",
|
DEBUGLOG("got %d bytes, need another %d (total %d)\n",
|
||||||
thisfd, argslen, missing_len, inheader->arglen);
|
argslen, missing_len, inheader->arglen);
|
||||||
len = read(thisfd->fd, argptr + argslen, missing_len);
|
len = read(thisfd->fd, argptr + argslen, missing_len);
|
||||||
if (len == -1 && errno == EINTR)
|
if (len == -1 && errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
/* EOF or error on socket */
|
/* EOF or error on socket */
|
||||||
DEBUGLOG("(%p) EOF on local socket\n", thisfd);
|
DEBUGLOG("EOF on local socket\n");
|
||||||
dm_free(thisfd->bits.localsock.cmd);
|
dm_free(thisfd->bits.localsock.cmd);
|
||||||
thisfd->bits.localsock.cmd = NULL;
|
thisfd->bits.localsock.cmd = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1406,7 +1373,7 @@ static int read_from_local_sock(struct local_client *thisfd)
|
|||||||
.status = ENOENT
|
.status = ENOENT
|
||||||
};
|
};
|
||||||
|
|
||||||
DEBUGLOG("(%p) Unknown node: '%s'\n", thisfd, inheader->node);
|
DEBUGLOG("Unknown node: '%s'\n", inheader->node);
|
||||||
send_message(&reply, sizeof(reply), our_csid, thisfd->fd,
|
send_message(&reply, sizeof(reply), our_csid, thisfd->fd,
|
||||||
"Error sending ENOENT reply to local user");
|
"Error sending ENOENT reply to local user");
|
||||||
thisfd->bits.localsock.expected_replies = 0;
|
thisfd->bits.localsock.expected_replies = 0;
|
||||||
@@ -1432,7 +1399,7 @@ static int read_from_local_sock(struct local_client *thisfd)
|
|||||||
.status = EBUSY
|
.status = EBUSY
|
||||||
};
|
};
|
||||||
|
|
||||||
DEBUGLOG("(%p) Creating pipe failed: %s\n", thisfd, strerror(errno));
|
DEBUGLOG("Creating pipe failed: %s\n", strerror(errno));
|
||||||
send_message(&reply, sizeof(reply), our_csid, thisfd->fd,
|
send_message(&reply, sizeof(reply), our_csid, thisfd->fd,
|
||||||
"Error sending EBUSY reply to local user");
|
"Error sending EBUSY reply to local user");
|
||||||
return len;
|
return len;
|
||||||
@@ -1452,7 +1419,7 @@ static int read_from_local_sock(struct local_client *thisfd)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUGLOG("(%p) Creating pipe, [%d, %d]\n", thisfd, comms_pipe[0], comms_pipe[1]);
|
DEBUGLOG("Creating pipe, [%d, %d]\n", comms_pipe[0], comms_pipe[1]);
|
||||||
|
|
||||||
if (fcntl(comms_pipe[0], F_SETFD, 1))
|
if (fcntl(comms_pipe[0], F_SETFD, 1))
|
||||||
DEBUGLOG("setting CLOEXEC on pipe[0] failed: %s\n", strerror(errno));
|
DEBUGLOG("setting CLOEXEC on pipe[0] failed: %s\n", strerror(errno));
|
||||||
@@ -1463,8 +1430,8 @@ static int read_from_local_sock(struct local_client *thisfd)
|
|||||||
newfd->type = THREAD_PIPE;
|
newfd->type = THREAD_PIPE;
|
||||||
newfd->callback = local_pipe_callback;
|
newfd->callback = local_pipe_callback;
|
||||||
newfd->bits.pipe.client = thisfd;
|
newfd->bits.pipe.client = thisfd;
|
||||||
|
newfd->next = thisfd->next;
|
||||||
_add_client(newfd, thisfd);
|
thisfd->next = newfd;
|
||||||
|
|
||||||
/* Store a cross link to the pipe */
|
/* Store a cross link to the pipe */
|
||||||
thisfd->bits.localsock.pipe_client = newfd;
|
thisfd->bits.localsock.pipe_client = newfd;
|
||||||
@@ -1477,10 +1444,10 @@ static int read_from_local_sock(struct local_client *thisfd)
|
|||||||
thisfd->bits.localsock.in_progress = TRUE;
|
thisfd->bits.localsock.in_progress = TRUE;
|
||||||
thisfd->bits.localsock.state = PRE_COMMAND;
|
thisfd->bits.localsock.state = PRE_COMMAND;
|
||||||
thisfd->bits.localsock.cleanup_needed = 1;
|
thisfd->bits.localsock.cleanup_needed = 1;
|
||||||
DEBUGLOG("(%p) Creating pre&post thread for pipe fd %d\n", newfd, newfd->fd);
|
DEBUGLOG("Creating pre&post thread for pipe fd %d (%p)\n", newfd->fd, newfd);
|
||||||
status = pthread_create(&thisfd->bits.localsock.threadid,
|
status = pthread_create(&thisfd->bits.localsock.threadid,
|
||||||
&stack_attr, pre_and_post_thread, thisfd);
|
&stack_attr, pre_and_post_thread, thisfd);
|
||||||
DEBUGLOG("(%p) Created pre&post thread, state = %d\n", newfd, status);
|
DEBUGLOG("Created pre&post thread, state = %d\n", status);
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@@ -1488,6 +1455,13 @@ static int read_from_local_sock(struct local_client *thisfd)
|
|||||||
/* Add a file descriptor from the cluster or comms interface to
|
/* Add a file descriptor from the cluster or comms interface to
|
||||||
our list of FDs for select
|
our list of FDs for select
|
||||||
*/
|
*/
|
||||||
|
int add_client(struct local_client *new_client)
|
||||||
|
{
|
||||||
|
new_client->next = local_client_head.next;
|
||||||
|
local_client_head.next = new_client;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Called when the pre-command has completed successfully - we
|
/* Called when the pre-command has completed successfully - we
|
||||||
now execute the real command on all the requested nodes */
|
now execute the real command on all the requested nodes */
|
||||||
@@ -1498,8 +1472,8 @@ static int distribute_command(struct local_client *thisfd)
|
|||||||
int len = thisfd->bits.localsock.cmd_len;
|
int len = thisfd->bits.localsock.cmd_len;
|
||||||
|
|
||||||
thisfd->xid = global_xid++;
|
thisfd->xid = global_xid++;
|
||||||
DEBUGLOG("(%p) distribute command: XID = %d, flags=0x%x (%s%s)\n",
|
DEBUGLOG("distribute command: XID = %d, flags=0x%x (%s%s)\n",
|
||||||
thisfd, thisfd->xid, inheader->flags,
|
thisfd->xid, inheader->flags,
|
||||||
(inheader->flags & CLVMD_FLAG_LOCAL) ? "LOCAL" : "",
|
(inheader->flags & CLVMD_FLAG_LOCAL) ? "LOCAL" : "",
|
||||||
(inheader->flags & CLVMD_FLAG_REMOTE) ? "REMOTE" : "");
|
(inheader->flags & CLVMD_FLAG_REMOTE) ? "REMOTE" : "");
|
||||||
|
|
||||||
@@ -1521,7 +1495,7 @@ static int distribute_command(struct local_client *thisfd)
|
|||||||
*/
|
*/
|
||||||
add_to_lvmqueue(thisfd, inheader, len, NULL);
|
add_to_lvmqueue(thisfd, inheader, len, NULL);
|
||||||
|
|
||||||
DEBUGLOG("(%p) Sending message to all cluster nodes\n", thisfd);
|
DEBUGLOG("Sending message to all cluster nodes\n");
|
||||||
inheader->xid = thisfd->xid;
|
inheader->xid = thisfd->xid;
|
||||||
send_message(inheader, len, NULL, -1,
|
send_message(inheader, len, NULL, -1,
|
||||||
"Error forwarding message to cluster");
|
"Error forwarding message to cluster");
|
||||||
@@ -1540,11 +1514,11 @@ static int distribute_command(struct local_client *thisfd)
|
|||||||
|
|
||||||
/* Are we the requested node ?? */
|
/* Are we the requested node ?? */
|
||||||
if (memcmp(csid, our_csid, max_csid_len) == 0) {
|
if (memcmp(csid, our_csid, max_csid_len) == 0) {
|
||||||
DEBUGLOG("(%p) Doing command on local node only\n", thisfd);
|
DEBUGLOG("Doing command on local node only\n");
|
||||||
add_to_lvmqueue(thisfd, inheader, len, NULL);
|
add_to_lvmqueue(thisfd, inheader, len, NULL);
|
||||||
} else {
|
} else {
|
||||||
DEBUGLOG("(%p) Sending message to single node: %s\n",
|
DEBUGLOG("Sending message to single node: %s\n",
|
||||||
thisfd, inheader->node);
|
inheader->node);
|
||||||
inheader->xid = thisfd->xid;
|
inheader->xid = thisfd->xid;
|
||||||
send_message(inheader, len, csid, -1,
|
send_message(inheader, len, csid, -1,
|
||||||
"Error forwarding message to cluster node");
|
"Error forwarding message to cluster node");
|
||||||
@@ -1555,7 +1529,7 @@ static int distribute_command(struct local_client *thisfd)
|
|||||||
thisfd->bits.localsock.in_progress = TRUE;
|
thisfd->bits.localsock.in_progress = TRUE;
|
||||||
thisfd->bits.localsock.expected_replies = 1;
|
thisfd->bits.localsock.expected_replies = 1;
|
||||||
thisfd->bits.localsock.num_replies = 0;
|
thisfd->bits.localsock.num_replies = 0;
|
||||||
DEBUGLOG("(%p) Doing command explicitly on local node only\n", thisfd);
|
DEBUGLOG("Doing command explicitly on local node only\n");
|
||||||
add_to_lvmqueue(thisfd, inheader, len, NULL);
|
add_to_lvmqueue(thisfd, inheader, len, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1681,7 +1655,7 @@ static void add_reply_to_list(struct local_client *client, int status,
|
|||||||
|
|
||||||
reply->status = status;
|
reply->status = status;
|
||||||
clops->name_from_csid(csid, reply->node);
|
clops->name_from_csid(csid, reply->node);
|
||||||
DEBUGLOG("(%p) Reply from node %s: %d bytes\n", client, reply->node, len);
|
DEBUGLOG("Reply from node %s: %d bytes\n", reply->node, len);
|
||||||
|
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
if (!(reply->replymsg = dm_malloc(len)))
|
if (!(reply->replymsg = dm_malloc(len)))
|
||||||
@@ -1708,8 +1682,8 @@ static void add_reply_to_list(struct local_client *client, int status,
|
|||||||
client->bits.localsock.state = POST_COMMAND;
|
client->bits.localsock.state = POST_COMMAND;
|
||||||
pthread_cond_signal(&client->bits.localsock.cond);
|
pthread_cond_signal(&client->bits.localsock.cond);
|
||||||
}
|
}
|
||||||
DEBUGLOG("(%p) Got %d replies, expecting: %d\n",
|
DEBUGLOG("Got %d replies, expecting: %d\n",
|
||||||
client, client->bits.localsock.num_replies,
|
client->bits.localsock.num_replies,
|
||||||
client->bits.localsock.expected_replies);
|
client->bits.localsock.expected_replies);
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&client->bits.localsock.mutex);
|
pthread_mutex_unlock(&client->bits.localsock.mutex);
|
||||||
@@ -1724,7 +1698,7 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
|
|||||||
sigset_t ss;
|
sigset_t ss;
|
||||||
int pipe_fd = client->bits.localsock.pipe;
|
int pipe_fd = client->bits.localsock.pipe;
|
||||||
|
|
||||||
DEBUGLOG("(%p) Pre&post thread pipe fd %d\n", client, pipe_fd);
|
DEBUGLOG("Pre&post thread (%p), pipe fd %d\n", client, pipe_fd);
|
||||||
pthread_mutex_lock(&client->bits.localsock.mutex);
|
pthread_mutex_lock(&client->bits.localsock.mutex);
|
||||||
|
|
||||||
/* Ignore SIGUSR1 (handled by master process) but enable
|
/* Ignore SIGUSR1 (handled by master process) but enable
|
||||||
@@ -1744,7 +1718,7 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
|
|||||||
if ((status = do_pre_command(client)))
|
if ((status = do_pre_command(client)))
|
||||||
client->bits.localsock.all_success = 0;
|
client->bits.localsock.all_success = 0;
|
||||||
|
|
||||||
DEBUGLOG("(%p) Pre&post thread writes status %d down to pipe fd %d\n",
|
DEBUGLOG("Pre&post thread (%p) writes status %d down to pipe fd %d\n",
|
||||||
client, status, pipe_fd);
|
client, status, pipe_fd);
|
||||||
|
|
||||||
/* Tell the parent process we have finished this bit */
|
/* Tell the parent process we have finished this bit */
|
||||||
@@ -1762,13 +1736,13 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
|
|||||||
/* We may need to wait for the condition variable before running the post command */
|
/* We may need to wait for the condition variable before running the post command */
|
||||||
if (client->bits.localsock.state != POST_COMMAND &&
|
if (client->bits.localsock.state != POST_COMMAND &&
|
||||||
!client->bits.localsock.finished) {
|
!client->bits.localsock.finished) {
|
||||||
DEBUGLOG("(%p) Pre&post thread waiting to do post command, state = %d\n",
|
DEBUGLOG("Pre&post thread (%p) waiting to do post command, state = %d\n",
|
||||||
client, client->bits.localsock.state);
|
client, client->bits.localsock.state);
|
||||||
pthread_cond_wait(&client->bits.localsock.cond,
|
pthread_cond_wait(&client->bits.localsock.cond,
|
||||||
&client->bits.localsock.mutex);
|
&client->bits.localsock.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUGLOG("(%p) Pre&post thread got post command condition...\n", client);
|
DEBUGLOG("Pre&post thread (%p) got post command condition...\n", client);
|
||||||
|
|
||||||
/* POST function must always run, even if the client aborts */
|
/* POST function must always run, even if the client aborts */
|
||||||
status = 0;
|
status = 0;
|
||||||
@@ -1782,15 +1756,15 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
|
|||||||
next_pre:
|
next_pre:
|
||||||
if (client->bits.localsock.state != PRE_COMMAND &&
|
if (client->bits.localsock.state != PRE_COMMAND &&
|
||||||
!client->bits.localsock.finished) {
|
!client->bits.localsock.finished) {
|
||||||
DEBUGLOG("(%p) Pre&post thread waiting for next pre command\n", client);
|
DEBUGLOG("Pre&post thread (%p) waiting for next pre command\n", client);
|
||||||
pthread_cond_wait(&client->bits.localsock.cond,
|
pthread_cond_wait(&client->bits.localsock.cond,
|
||||||
&client->bits.localsock.mutex);
|
&client->bits.localsock.mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUGLOG("(%p) Pre&post thread got pre command condition...\n", client);
|
DEBUGLOG("Pre&post thread (%p) got pre command condition...\n", client);
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&client->bits.localsock.mutex);
|
pthread_mutex_unlock(&client->bits.localsock.mutex);
|
||||||
DEBUGLOG("(%p) Pre&post thread finished\n", client);
|
DEBUGLOG("Pre&post thread (%p) finished\n", client);
|
||||||
|
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
@@ -1808,8 +1782,8 @@ static int process_local_command(struct clvm_header *msg, int msglen,
|
|||||||
if (!(replybuf = dm_malloc(max_cluster_message)))
|
if (!(replybuf = dm_malloc(max_cluster_message)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
DEBUGLOG("(%p) process_local_command: %s msg=%p, msglen =%d\n",
|
DEBUGLOG("process_local_command: %s msg=%p, msglen =%d, client=%p\n",
|
||||||
client, decode_cmd(msg->cmd), msg, msglen);
|
decode_cmd(msg->cmd), msg, msglen, client);
|
||||||
|
|
||||||
/* If remote flag is set, just set a successful status code. */
|
/* If remote flag is set, just set a successful status code. */
|
||||||
if (msg->flags & CLVMD_FLAG_REMOTE)
|
if (msg->flags & CLVMD_FLAG_REMOTE)
|
||||||
@@ -1824,8 +1798,8 @@ static int process_local_command(struct clvm_header *msg, int msglen,
|
|||||||
if (xid == client->xid)
|
if (xid == client->xid)
|
||||||
add_reply_to_list(client, status, our_csid, replybuf, replylen);
|
add_reply_to_list(client, status, our_csid, replybuf, replylen);
|
||||||
else
|
else
|
||||||
DEBUGLOG("(%p) Local command took too long, discarding xid %d, current is %d\n",
|
DEBUGLOG("Local command took too long, discarding xid %d, current is %d\n",
|
||||||
client, xid, client->xid);
|
xid, client->xid);
|
||||||
|
|
||||||
dm_free(replybuf);
|
dm_free(replybuf);
|
||||||
|
|
||||||
@@ -1867,7 +1841,7 @@ static void send_local_reply(struct local_client *client, int status, int fd)
|
|||||||
char *ptr;
|
char *ptr;
|
||||||
int message_len = 0;
|
int message_len = 0;
|
||||||
|
|
||||||
DEBUGLOG("(%p) Send local reply\n", client);
|
DEBUGLOG("Send local reply\n");
|
||||||
|
|
||||||
/* Work out the total size of the reply */
|
/* Work out the total size of the reply */
|
||||||
while (thisreply) {
|
while (thisreply) {
|
||||||
@@ -1884,7 +1858,7 @@ static void send_local_reply(struct local_client *client, int status, int fd)
|
|||||||
/* Add in the size of our header */
|
/* Add in the size of our header */
|
||||||
message_len = message_len + sizeof(struct clvm_header);
|
message_len = message_len + sizeof(struct clvm_header);
|
||||||
if (!(replybuf = dm_malloc(message_len))) {
|
if (!(replybuf = dm_malloc(message_len))) {
|
||||||
DEBUGLOG("(%p) Memory allocation fails\n", client);
|
DEBUGLOG("Memory allocation fails\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2013,7 +1987,6 @@ static int send_message(void *buf, int msglen, const char *csid, int fd,
|
|||||||
(void) nanosleep (&delay, &remtime);
|
(void) nanosleep (&delay, &remtime);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DEBUGLOG("%s", errtext);
|
|
||||||
log_error("%s", errtext);
|
log_error("%s", errtext);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2027,7 +2000,7 @@ static int process_work_item(struct lvm_thread_cmd *cmd)
|
|||||||
{
|
{
|
||||||
/* If msg is NULL then this is a cleanup request */
|
/* If msg is NULL then this is a cleanup request */
|
||||||
if (cmd->msg == NULL) {
|
if (cmd->msg == NULL) {
|
||||||
DEBUGLOG("(%p) process_work_item: free\n", cmd->client);
|
DEBUGLOG("process_work_item: free %p\n", cmd->client);
|
||||||
cmd_client_cleanup(cmd->client);
|
cmd_client_cleanup(cmd->client);
|
||||||
pthread_mutex_destroy(&cmd->client->bits.localsock.mutex);
|
pthread_mutex_destroy(&cmd->client->bits.localsock.mutex);
|
||||||
pthread_cond_destroy(&cmd->client->bits.localsock.cond);
|
pthread_cond_destroy(&cmd->client->bits.localsock.cond);
|
||||||
@@ -2036,11 +2009,11 @@ static int process_work_item(struct lvm_thread_cmd *cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!cmd->remote) {
|
if (!cmd->remote) {
|
||||||
DEBUGLOG("(%p) process_work_item: local\n", cmd->client);
|
DEBUGLOG("process_work_item: local\n");
|
||||||
process_local_command(cmd->msg, cmd->msglen, cmd->client,
|
process_local_command(cmd->msg, cmd->msglen, cmd->client,
|
||||||
cmd->xid);
|
cmd->xid);
|
||||||
} else {
|
} else {
|
||||||
DEBUGLOG("(%p) process_work_item: remote\n", cmd->client);
|
DEBUGLOG("process_work_item: remote\n");
|
||||||
process_remote_command(cmd->msg, cmd->msglen, cmd->client->fd,
|
process_remote_command(cmd->msg, cmd->msglen, cmd->client->fd,
|
||||||
cmd->csid);
|
cmd->csid);
|
||||||
}
|
}
|
||||||
@@ -2134,8 +2107,8 @@ static int add_to_lvmqueue(struct local_client *client, struct clvm_header *msg,
|
|||||||
} else
|
} else
|
||||||
cmd->remote = 0;
|
cmd->remote = 0;
|
||||||
|
|
||||||
DEBUGLOG("(%p) add_to_lvmqueue: cmd=%p, msg=%p, len=%d, csid=%p, xid=%d\n",
|
DEBUGLOG("add_to_lvmqueue: cmd=%p. client=%p, msg=%p, len=%d, csid=%p, xid=%d\n",
|
||||||
client, cmd, msg, msglen, csid, cmd->xid);
|
cmd, client, msg, msglen, csid, cmd->xid);
|
||||||
pthread_mutex_lock(&lvm_thread_mutex);
|
pthread_mutex_lock(&lvm_thread_mutex);
|
||||||
if (lvm_thread_exit) {
|
if (lvm_thread_exit) {
|
||||||
pthread_mutex_unlock(&lvm_thread_mutex);
|
pthread_mutex_unlock(&lvm_thread_mutex);
|
||||||
@@ -2151,14 +2124,6 @@ static int add_to_lvmqueue(struct local_client *client, struct clvm_header *msg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Return 0 if we can talk to an existing clvmd */
|
/* Return 0 if we can talk to an existing clvmd */
|
||||||
/*
|
|
||||||
* FIXME:
|
|
||||||
*
|
|
||||||
* This function returns only -1 or 0, but there are
|
|
||||||
* different levels of errors, some of them should stop
|
|
||||||
* further execution of clvmd thus another state is needed
|
|
||||||
* and some error message need to be only informational.
|
|
||||||
*/
|
|
||||||
static int check_local_clvmd(void)
|
static int check_local_clvmd(void)
|
||||||
{
|
{
|
||||||
int local_socket;
|
int local_socket;
|
||||||
@@ -2178,10 +2143,6 @@ static int check_local_clvmd(void)
|
|||||||
|
|
||||||
if (connect(local_socket,(struct sockaddr *) &sockaddr,
|
if (connect(local_socket,(struct sockaddr *) &sockaddr,
|
||||||
sizeof(sockaddr))) {
|
sizeof(sockaddr))) {
|
||||||
/* connection failure is expected state */
|
|
||||||
if (errno == ENOENT)
|
|
||||||
log_sys_debug("connect", "local socket");
|
|
||||||
else
|
|
||||||
log_sys_error("connect", "local socket");
|
log_sys_error("connect", "local socket");
|
||||||
ret = -1;
|
ret = -1;
|
||||||
}
|
}
|
||||||
@@ -2283,8 +2244,7 @@ static void check_all_callback(struct local_client *client, const char *csid,
|
|||||||
If not, returns -1 and prints out a list of errant nodes */
|
If not, returns -1 and prints out a list of errant nodes */
|
||||||
static int check_all_clvmds_running(struct local_client *client)
|
static int check_all_clvmds_running(struct local_client *client)
|
||||||
{
|
{
|
||||||
DEBUGLOG("(%p) check_all_clvmds_running\n", client);
|
DEBUGLOG("check_all_clvmds_running\n");
|
||||||
|
|
||||||
return clops->cluster_do_node_callback(client, check_all_callback);
|
return clops->cluster_do_node_callback(client, check_all_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2323,11 +2283,13 @@ static void ntoh_clvm(struct clvm_header *hdr)
|
|||||||
static void sigusr2_handler(int sig)
|
static void sigusr2_handler(int sig)
|
||||||
{
|
{
|
||||||
DEBUGLOG("SIGUSR2 received\n");
|
DEBUGLOG("SIGUSR2 received\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sigterm_handler(int sig)
|
static void sigterm_handler(int sig)
|
||||||
{
|
{
|
||||||
quit = 1;
|
quit = 1;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sighup_handler(int sig)
|
static void sighup_handler(int sig)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ include $(top_builddir)/make.tmpl
|
|||||||
LIBS += -ldevmapper
|
LIBS += -ldevmapper
|
||||||
LMLIBS += $(CPG_LIBS) $(SACKPT_LIBS)
|
LMLIBS += $(CPG_LIBS) $(SACKPT_LIBS)
|
||||||
CFLAGS += $(CPG_CFLAGS) $(SACKPT_CFLAGS) $(EXTRA_EXEC_CFLAGS)
|
CFLAGS += $(CPG_CFLAGS) $(SACKPT_CFLAGS) $(EXTRA_EXEC_CFLAGS)
|
||||||
LDFLAGS += $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS)
|
LDFLAGS += $(EXTRA_EXEC_LDFLAGS)
|
||||||
|
|
||||||
cmirrord: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
|
cmirrord: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ int cluster_send(struct clog_request *rq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Once the request heads for the cluster, the luid loses
|
* Once the request heads for the cluster, the luid looses
|
||||||
* all its meaning.
|
* all its meaning.
|
||||||
*/
|
*/
|
||||||
rq->u_rq.luid = 0;
|
rq->u_rq.luid = 0;
|
||||||
@@ -1440,7 +1440,7 @@ static void cpg_leave_callback(struct clog_cpg *match,
|
|||||||
free(rq);
|
free(rq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0, j = 0; (int) i < match->checkpoints_needed; i++, j++) {
|
for (i = 0, j = 0; i < match->checkpoints_needed; i++, j++) {
|
||||||
match->checkpoint_requesters[j] = match->checkpoint_requesters[i];
|
match->checkpoint_requesters[j] = match->checkpoint_requesters[i];
|
||||||
if (match->checkpoint_requesters[i] == left->nodeid) {
|
if (match->checkpoint_requesters[i] == left->nodeid) {
|
||||||
LOG_ERROR("[%s] Removing pending ckpt from needed list (%u is leaving)",
|
LOG_ERROR("[%s] Removing pending ckpt from needed list (%u is leaving)",
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "functions.h"
|
#include "functions.h"
|
||||||
|
|
||||||
#include <sys/sysmacros.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -377,7 +376,7 @@ static int _clog_ctr(char *uuid, uint64_t luid,
|
|||||||
uint32_t block_on_error = 0;
|
uint32_t block_on_error = 0;
|
||||||
|
|
||||||
int disk_log;
|
int disk_log;
|
||||||
char disk_path[PATH_MAX];
|
char disk_path[128];
|
||||||
int unlink_path = 0;
|
int unlink_path = 0;
|
||||||
long page_size;
|
long page_size;
|
||||||
int pages;
|
int pages;
|
||||||
@@ -574,12 +573,6 @@ static int clog_ctr(struct dm_ulog_request *rq)
|
|||||||
for (argc = 0, p = rq->data; (p = strstr(p, " ")); p++, argc++)
|
for (argc = 0, p = rq->data; (p = strstr(p, " ")); p++, argc++)
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
if (!argc) {
|
|
||||||
LOG_ERROR("Received constructor request with bad data %s",
|
|
||||||
rq->data);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
argv = malloc(argc * sizeof(char *));
|
argv = malloc(argc * sizeof(char *));
|
||||||
if (!argv)
|
if (!argv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|||||||
@@ -56,16 +56,18 @@ include $(top_builddir)/make.tmpl
|
|||||||
all: device-mapper
|
all: device-mapper
|
||||||
device-mapper: $(TARGETS)
|
device-mapper: $(TARGETS)
|
||||||
|
|
||||||
|
LIBS += -ldevmapper
|
||||||
|
LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS)
|
||||||
|
|
||||||
CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS)
|
CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS)
|
||||||
LIBS += -ldevmapper $(PTHREAD_LIBS)
|
|
||||||
|
|
||||||
dmeventd: $(LIB_SHARED) dmeventd.o
|
dmeventd: $(LIB_SHARED) dmeventd.o
|
||||||
$(CC) $(CFLAGS) -L. $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) dmeventd.o \
|
$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) -L. -o $@ dmeventd.o \
|
||||||
-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS)
|
$(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic
|
||||||
|
|
||||||
dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a
|
dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) dmeventd.o \
|
$(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \
|
||||||
-o $@ $(DL_LIBS) $(DMEVENT_LIBS) $(LIBS) $(STATIC_LIBS)
|
dmeventd.o $(DL_LIBS) $(LVMLIBS) $(LIBS) $(STATIC_LIBS)
|
||||||
|
|
||||||
ifeq ("@PKGCONFIG@", "yes")
|
ifeq ("@PKGCONFIG@", "yes")
|
||||||
INSTALL_LIB_TARGETS += install_pkgconfig
|
INSTALL_LIB_TARGETS += install_pkgconfig
|
||||||
|
|||||||
@@ -62,8 +62,6 @@
|
|||||||
|
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
|
||||||
#define DM_SIGNALED_EXIT 1
|
|
||||||
#define DM_SCHEDULED_EXIT 2
|
|
||||||
static volatile sig_atomic_t _exit_now = 0; /* set to '1' when signal is given to exit */
|
static volatile sig_atomic_t _exit_now = 0; /* set to '1' when signal is given to exit */
|
||||||
|
|
||||||
/* List (un)link macros. */
|
/* List (un)link macros. */
|
||||||
@@ -101,28 +99,13 @@ static time_t _idle_since = 0;
|
|||||||
static char **_initial_registrations = 0;
|
static char **_initial_registrations = 0;
|
||||||
|
|
||||||
/* FIXME Make configurable at runtime */
|
/* FIXME Make configurable at runtime */
|
||||||
|
__attribute__((format(printf, 4, 5)))
|
||||||
/* 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, 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,
|
static void _dmeventd_log(int level, const char *file, int line,
|
||||||
int dm_errno_or_class, const char *format, ...)
|
const char *format, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
dm_event_log("dmeventd", level, file, line, dm_errno_or_class, format, ap);
|
dm_event_log("dm", level, file, line, 0, format, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,7 +453,7 @@ static int _pthread_create_smallstack(pthread_t *t, void *(*fun)(void *), void *
|
|||||||
/*
|
/*
|
||||||
* We use a smaller stack since it gets preallocated in its entirety
|
* We use a smaller stack since it gets preallocated in its entirety
|
||||||
*/
|
*/
|
||||||
pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE + getpagesize());
|
pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If no-one will be waiting, we need to detach.
|
* If no-one will be waiting, we need to detach.
|
||||||
@@ -846,6 +829,17 @@ static void _print_sigset(const char *prefix, const sigset_t *sigset)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static sigset_t _unblock_sigalrm(void)
|
||||||
|
{
|
||||||
|
sigset_t set, old;
|
||||||
|
|
||||||
|
sigemptyset(&set);
|
||||||
|
sigaddset(&set, SIGALRM);
|
||||||
|
pthread_sigmask(SIG_UNBLOCK, &set, &old);
|
||||||
|
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DM_WAIT_RETRY,
|
DM_WAIT_RETRY,
|
||||||
DM_WAIT_INTR,
|
DM_WAIT_INTR,
|
||||||
@@ -855,7 +849,7 @@ enum {
|
|||||||
/* Wait on a device until an event occurs. */
|
/* Wait on a device until an event occurs. */
|
||||||
static int _event_wait(struct thread_status *thread)
|
static int _event_wait(struct thread_status *thread)
|
||||||
{
|
{
|
||||||
sigset_t set, old;
|
sigset_t set;
|
||||||
int ret = DM_WAIT_RETRY;
|
int ret = DM_WAIT_RETRY;
|
||||||
struct dm_info info;
|
struct dm_info info;
|
||||||
|
|
||||||
@@ -865,13 +859,7 @@ static int _event_wait(struct thread_status *thread)
|
|||||||
* This is so that you can break out of waiting on an event,
|
* This is so that you can break out of waiting on an event,
|
||||||
* either for a timeout event, or to cancel the thread.
|
* either for a timeout event, or to cancel the thread.
|
||||||
*/
|
*/
|
||||||
sigemptyset(&old);
|
set = _unblock_sigalrm();
|
||||||
sigemptyset(&set);
|
|
||||||
sigaddset(&set, SIGALRM);
|
|
||||||
if (pthread_sigmask(SIG_UNBLOCK, &set, &old) != 0) {
|
|
||||||
log_sys_error("pthread_sigmask", "unblock alarm");
|
|
||||||
return ret; /* What better */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dm_task_run(thread->wait_task)) {
|
if (dm_task_run(thread->wait_task)) {
|
||||||
thread->current_events |= DM_EVENT_DEVICE_ERROR;
|
thread->current_events |= DM_EVENT_DEVICE_ERROR;
|
||||||
@@ -895,11 +883,10 @@ static int _event_wait(struct thread_status *thread)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pthread_sigmask(SIG_SETMASK, &old, NULL) != 0)
|
pthread_sigmask(SIG_SETMASK, &set, NULL);
|
||||||
log_sys_error("pthread_sigmask", "block alarm");
|
|
||||||
|
|
||||||
#ifdef DEBUG_SIGNALS
|
#ifdef DEBUG_SIGNALS
|
||||||
_print_sigset("dmeventd blocking ", &old);
|
_print_sigset("dmeventd blocking ", &set);
|
||||||
#endif
|
#endif
|
||||||
DEBUGLOG("Completed waitevent task for %s.", thread->device.name);
|
DEBUGLOG("Completed waitevent task for %s.", thread->device.name);
|
||||||
|
|
||||||
@@ -1753,7 +1740,7 @@ static void _init_thread_signals(void)
|
|||||||
*/
|
*/
|
||||||
static void _exit_handler(int sig __attribute__((unused)))
|
static void _exit_handler(int sig __attribute__((unused)))
|
||||||
{
|
{
|
||||||
_exit_now = DM_SIGNALED_EXIT;
|
_exit_now = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@@ -2209,7 +2196,7 @@ int main(int argc, char *argv[])
|
|||||||
openlog("dmeventd", LOG_PID, LOG_DAEMON);
|
openlog("dmeventd", LOG_PID, LOG_DAEMON);
|
||||||
|
|
||||||
dm_event_log_set(_debug_level, _use_syslog);
|
dm_event_log_set(_debug_level, _use_syslog);
|
||||||
dm_log_with_errno_init(_libdm_log);
|
dm_log_init(_dmeventd_log);
|
||||||
|
|
||||||
(void) dm_prepare_selinux_context(DMEVENTD_PIDFILE, S_IFREG);
|
(void) dm_prepare_selinux_context(DMEVENTD_PIDFILE, S_IFREG);
|
||||||
if (dm_create_lockfile(DMEVENTD_PIDFILE) == 0)
|
if (dm_create_lockfile(DMEVENTD_PIDFILE) == 0)
|
||||||
@@ -2251,14 +2238,11 @@ int main(int argc, char *argv[])
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
if (_idle_since) {
|
if (_idle_since) {
|
||||||
if (_exit_now) {
|
if (_exit_now) {
|
||||||
if (_exit_now == DM_SCHEDULED_EXIT)
|
|
||||||
break; /* Only prints shutdown message */
|
|
||||||
log_info("dmeventd detected break while being idle "
|
log_info("dmeventd detected break while being idle "
|
||||||
"for %ld second(s), exiting.",
|
"for %ld second(s), exiting.",
|
||||||
(long) (time(NULL) - _idle_since));
|
(long) (time(NULL) - _idle_since));
|
||||||
break;
|
break;
|
||||||
}
|
} else if (idle_exit_timeout) {
|
||||||
if (idle_exit_timeout) {
|
|
||||||
now = time(NULL);
|
now = time(NULL);
|
||||||
if (now < _idle_since)
|
if (now < _idle_since)
|
||||||
_idle_since = now; /* clock change? */
|
_idle_since = now; /* clock change? */
|
||||||
@@ -2269,14 +2253,15 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (_exit_now == DM_SIGNALED_EXIT) {
|
} else if (_exit_now) {
|
||||||
_exit_now = DM_SCHEDULED_EXIT;
|
_exit_now = 0;
|
||||||
/*
|
/*
|
||||||
* When '_exit_now' is set, signal has been received,
|
* When '_exit_now' is set, signal has been received,
|
||||||
* but can not simply exit unless all
|
* but can not simply exit unless all
|
||||||
* threads are done processing.
|
* threads are done processing.
|
||||||
*/
|
*/
|
||||||
log_info("dmeventd received break, scheduling exit.");
|
log_warn("WARNING: There are still devices being monitored.");
|
||||||
|
log_warn("WARNING: Refusing to exit.");
|
||||||
}
|
}
|
||||||
_process_request(&fifos);
|
_process_request(&fifos);
|
||||||
_cleanup_unused_threads();
|
_cleanup_unused_threads();
|
||||||
|
|||||||
@@ -250,10 +250,11 @@ static int _daemon_read(struct dm_event_fifos *fifos,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if ((errno == EINTR) || (errno == EAGAIN))
|
if ((errno == EINTR) || (errno == EAGAIN))
|
||||||
continue;
|
continue;
|
||||||
|
else {
|
||||||
log_error("Unable to read from event server.");
|
log_error("Unable to read from event server.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bytes += ret;
|
bytes += ret;
|
||||||
if (header && (bytes == 2 * sizeof(uint32_t))) {
|
if (header && (bytes == 2 * sizeof(uint32_t))) {
|
||||||
@@ -328,10 +329,11 @@ static int _daemon_write(struct dm_event_fifos *fifos,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if ((errno == EINTR) || (errno == EAGAIN))
|
if ((errno == EINTR) || (errno == EAGAIN))
|
||||||
continue;
|
continue;
|
||||||
|
else {
|
||||||
log_error("Unable to talk to event daemon.");
|
log_error("Unable to talk to event daemon.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bytes += ret;
|
bytes += ret;
|
||||||
}
|
}
|
||||||
@@ -452,8 +454,7 @@ static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos)
|
|||||||
if (close(fifos->client))
|
if (close(fifos->client))
|
||||||
log_sys_debug("close", fifos->client_path);
|
log_sys_debug("close", fifos->client_path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
} else if (errno != ENXIO && errno != ENOENT) {
|
||||||
if (errno != ENXIO && errno != ENOENT) {
|
|
||||||
/* problem */
|
/* problem */
|
||||||
log_sys_error("open", fifos->client_path);
|
log_sys_error("open", fifos->client_path);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -854,9 +855,9 @@ int dm_event_get_version(struct dm_event_fifos *fifos, int *version) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dm_event_log_set(int debug_log_level, int use_syslog)
|
void dm_event_log_set(int debug_level, int use_syslog)
|
||||||
{
|
{
|
||||||
_debug_level = debug_log_level;
|
_debug_level = debug_level;
|
||||||
_use_syslog = use_syslog;
|
_use_syslog = use_syslog;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,38 +865,28 @@ void dm_event_log(const char *subsys, int level, const char *file,
|
|||||||
int line, int dm_errno_or_class,
|
int line, int dm_errno_or_class,
|
||||||
const char *format, va_list ap)
|
const char *format, va_list ap)
|
||||||
{
|
{
|
||||||
static int _abort_on_internal_errors = -1;
|
|
||||||
static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static time_t start = 0;
|
static time_t start = 0;
|
||||||
const char *indent = "";
|
const char *indent = "";
|
||||||
FILE *stream = log_stderr(level) ? stderr : stdout;
|
FILE *stream = stdout;
|
||||||
int prio;
|
int prio;
|
||||||
time_t now;
|
time_t now;
|
||||||
int log_with_debug = 0;
|
|
||||||
|
|
||||||
if (subsys[0] == '#') {
|
switch (level & ~(_LOG_STDERR | _LOG_ONCE)) {
|
||||||
/* Subsystems starting with '#' are logged
|
|
||||||
* only when debugging is enabled. */
|
|
||||||
log_with_debug++;
|
|
||||||
subsys++;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (log_level(level)) {
|
|
||||||
case _LOG_DEBUG:
|
case _LOG_DEBUG:
|
||||||
/* Never shown without -ddd */
|
|
||||||
if (_debug_level < 3)
|
if (_debug_level < 3)
|
||||||
return;
|
return;
|
||||||
prio = LOG_DEBUG;
|
prio = LOG_DEBUG;
|
||||||
indent = " ";
|
indent = " ";
|
||||||
break;
|
break;
|
||||||
case _LOG_INFO:
|
case _LOG_INFO:
|
||||||
if (log_with_debug && _debug_level < 2)
|
if (_debug_level < 2)
|
||||||
return;
|
return;
|
||||||
prio = LOG_INFO;
|
prio = LOG_INFO;
|
||||||
indent = " ";
|
indent = " ";
|
||||||
break;
|
break;
|
||||||
case _LOG_NOTICE:
|
case _LOG_NOTICE:
|
||||||
if (log_with_debug && _debug_level < 1)
|
if (_debug_level < 1)
|
||||||
return;
|
return;
|
||||||
prio = LOG_NOTICE;
|
prio = LOG_NOTICE;
|
||||||
indent = " ";
|
indent = " ";
|
||||||
@@ -921,7 +912,6 @@ void dm_event_log(const char *subsys, int level, const char *file,
|
|||||||
if (!start)
|
if (!start)
|
||||||
start = now;
|
start = now;
|
||||||
now -= start;
|
now -= start;
|
||||||
if (_debug_level)
|
|
||||||
fprintf(stream, "[%2d:%02d] %8x:%-6s%s",
|
fprintf(stream, "[%2d:%02d] %8x:%-6s%s",
|
||||||
(int)now / 60, (int)now % 60,
|
(int)now / 60, (int)now % 60,
|
||||||
// TODO: Maybe use shorter ID
|
// TODO: Maybe use shorter ID
|
||||||
@@ -936,15 +926,6 @@ void dm_event_log(const char *subsys, int level, const char *file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&_log_mutex);
|
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 */
|
#if 0 /* left out for now */
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh);
|
|||||||
int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
|
int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
|
||||||
|
|
||||||
/* Set debug level for logging, and whether to log on stdout/stderr or syslog */
|
/* Set debug level for logging, and whether to log on stdout/stderr or syslog */
|
||||||
void dm_event_log_set(int debug_log_level, int use_syslog);
|
void dm_event_log_set(int debug_level, int use_syslog);
|
||||||
|
|
||||||
/* Log messages acroding to current debug level */
|
/* Log messages acroding to current debug level */
|
||||||
__attribute__((format(printf, 6, 0)))
|
__attribute__((format(printf, 6, 0)))
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ static int _register_count = 0;
|
|||||||
static struct dm_pool *_mem_pool = NULL;
|
static struct dm_pool *_mem_pool = NULL;
|
||||||
static void *_lvm_handle = 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,
|
static void _lvm2_print_log(int level, const char *file, int line,
|
||||||
int dm_errno_or_class, const char *msg)
|
int dm_errno_or_class, const char *msg)
|
||||||
@@ -121,7 +121,6 @@ int dmeventd_lvm2_run(const char *cmdline)
|
|||||||
int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
||||||
const char *cmd, const char *device)
|
const char *cmd, const char *device)
|
||||||
{
|
{
|
||||||
static char _internal_prefix[] = "_dmeventd_";
|
|
||||||
char *vg = NULL, *lv = NULL, *layer;
|
char *vg = NULL, *lv = NULL, *layer;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@@ -136,21 +135,6 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
|
|||||||
(layer = strstr(lv, "_mlog")))
|
(layer = strstr(lv, "_mlog")))
|
||||||
*layer = '\0';
|
*layer = '\0';
|
||||||
|
|
||||||
if (!strncmp(cmd, _internal_prefix, sizeof(_internal_prefix) - 1)) {
|
|
||||||
dmeventd_lvm2_lock();
|
|
||||||
/* output of internal command passed via env var */
|
|
||||||
if (!dmeventd_lvm2_run(cmd))
|
|
||||||
cmd = NULL;
|
|
||||||
else if ((cmd = getenv(cmd)))
|
|
||||||
cmd = dm_pool_strdup(mem, cmd); /* copy with lock */
|
|
||||||
dmeventd_lvm2_unlock();
|
|
||||||
|
|
||||||
if (!cmd) {
|
|
||||||
log_error("Unable to find configured command.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv);
|
r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv);
|
||||||
|
|
||||||
dm_pool_free(mem, vg);
|
dm_pool_free(mem, vg);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
|
* Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of LVM2.
|
* This file is part of LVM2.
|
||||||
*
|
*
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
#include "libdevmapper-event.h"
|
#include "libdevmapper-event.h"
|
||||||
#include "dmeventd_lvm.h"
|
#include "dmeventd_lvm.h"
|
||||||
#include "activate.h" /* For TARGET_NAME* */
|
#include "defaults.h"
|
||||||
|
|
||||||
/* FIXME Reformat to 80 char lines. */
|
/* FIXME Reformat to 80 char lines. */
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
struct dso_state {
|
struct dso_state {
|
||||||
struct dm_pool *mem;
|
struct dm_pool *mem;
|
||||||
|
char cmd_lvscan[512];
|
||||||
char cmd_lvconvert[512];
|
char cmd_lvconvert[512];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,10 +73,8 @@ static int _get_mirror_event(struct dso_state *state, char *params)
|
|||||||
unsigned i;
|
unsigned i;
|
||||||
struct dm_status_mirror *ms;
|
struct dm_status_mirror *ms;
|
||||||
|
|
||||||
if (!dm_get_status_mirror(state->mem, params, &ms)) {
|
if (!dm_get_status_mirror(state->mem, params, &ms))
|
||||||
log_error("Unable to parse mirror status string.");
|
goto_out;
|
||||||
return ME_IGNORE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for bad mirror devices */
|
/* Check for bad mirror devices */
|
||||||
for (i = 0; i < ms->dev_count; ++i)
|
for (i = 0; i < ms->dev_count; ++i)
|
||||||
@@ -96,19 +95,27 @@ static int _get_mirror_event(struct dso_state *state, char *params)
|
|||||||
dm_pool_free(state->mem, ms);
|
dm_pool_free(state->mem, ms);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
out:
|
||||||
|
log_error("Unable to parse mirror status string.");
|
||||||
|
|
||||||
|
return ME_IGNORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _remove_failed_devices(const char *cmd_lvconvert, const char *device)
|
static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert)
|
||||||
{
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (!dmeventd_lvm2_run_with_lock(cmd_lvscan))
|
||||||
|
log_info("Re-scan of mirrored device failed.");
|
||||||
|
|
||||||
/* if repair goes OK, report success even if lvscan has failed */
|
/* if repair goes OK, report success even if lvscan has failed */
|
||||||
if (!dmeventd_lvm2_run_with_lock(cmd_lvconvert)) {
|
r = 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 finished successfully.", device);
|
log_info("Repair of mirrored device %s.",
|
||||||
|
(r) ? "finished successfully" : "failed");
|
||||||
|
|
||||||
return 1;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_event(struct dm_task *dmt,
|
void process_event(struct dm_task *dmt,
|
||||||
@@ -131,7 +138,7 @@ void process_event(struct dm_task *dmt,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(target_type, TARGET_NAME_MIRROR)) {
|
if (strcmp(target_type, "mirror")) {
|
||||||
log_info("%s has unmirrored portion.", device);
|
log_info("%s has unmirrored portion.", device);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -146,7 +153,8 @@ void process_event(struct dm_task *dmt,
|
|||||||
break;
|
break;
|
||||||
case ME_FAILURE:
|
case ME_FAILURE:
|
||||||
log_error("Device failure in %s.", device);
|
log_error("Device failure in %s.", device);
|
||||||
if (!_remove_failed_devices(state->cmd_lvconvert, device))
|
if (!_remove_failed_devices(state->cmd_lvscan,
|
||||||
|
state->cmd_lvconvert))
|
||||||
/* FIXME Why are all the error return codes unused? Get rid of them? */
|
/* FIXME Why are all the error return codes unused? Get rid of them? */
|
||||||
log_error("Failed to remove faulty devices in %s.",
|
log_error("Failed to remove faulty devices in %s.",
|
||||||
device);
|
device);
|
||||||
@@ -160,7 +168,7 @@ void process_event(struct dm_task *dmt,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* FIXME Provide value then! */
|
/* FIXME Provide value then! */
|
||||||
log_warn("WARNING: %s received unknown event.", device);
|
log_info("Unknown event received.");
|
||||||
}
|
}
|
||||||
} while (next);
|
} while (next);
|
||||||
}
|
}
|
||||||
@@ -176,10 +184,17 @@ int register_device(const char *device,
|
|||||||
if (!dmeventd_lvm2_init_with_pool("mirror_state", state))
|
if (!dmeventd_lvm2_init_with_pool("mirror_state", state))
|
||||||
goto_bad;
|
goto_bad;
|
||||||
|
|
||||||
/* CANNOT use --config as this disables cached content */
|
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan),
|
||||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
|
"lvscan --cache", device)) {
|
||||||
"lvconvert --repair --use-policies", device))
|
dmeventd_lvm2_exit_with_pool(state);
|
||||||
goto_bad;
|
goto_bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
|
||||||
|
"lvconvert --repair --use-policies", device)) {
|
||||||
|
dmeventd_lvm2_exit_with_pool(state);
|
||||||
|
goto_bad;
|
||||||
|
}
|
||||||
|
|
||||||
*user = state;
|
*user = state;
|
||||||
|
|
||||||
@@ -189,9 +204,6 @@ int register_device(const char *device,
|
|||||||
bad:
|
bad:
|
||||||
log_error("Failed to monitor mirror %s.", device);
|
log_error("Failed to monitor mirror %s.", device);
|
||||||
|
|
||||||
if (state)
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved.
|
* Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of LVM2.
|
* This file is part of LVM2.
|
||||||
*
|
*
|
||||||
@@ -13,19 +13,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
#include "defaults.h"
|
|
||||||
#include "dmeventd_lvm.h"
|
#include "dmeventd_lvm.h"
|
||||||
#include "libdevmapper-event.h"
|
#include "libdevmapper-event.h"
|
||||||
|
|
||||||
/* Hold enough elements for the mximum number of RAID images */
|
|
||||||
#define RAID_DEVS_ELEMS ((DEFAULT_RAID_MAX_IMAGES + 63) / 64)
|
|
||||||
|
|
||||||
struct dso_state {
|
struct dso_state {
|
||||||
struct dm_pool *mem;
|
struct dm_pool *mem;
|
||||||
|
char cmd_lvscan[512];
|
||||||
char cmd_lvconvert[512];
|
char cmd_lvconvert[512];
|
||||||
uint64_t raid_devs[RAID_DEVS_ELEMS];
|
|
||||||
int failed;
|
int failed;
|
||||||
int warned;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DM_EVENT_LOG_FN("raid")
|
DM_EVENT_LOG_FN("raid")
|
||||||
@@ -36,70 +31,32 @@ static int _process_raid_event(struct dso_state *state, char *params, const char
|
|||||||
{
|
{
|
||||||
struct dm_status_raid *status;
|
struct dm_status_raid *status;
|
||||||
const char *d;
|
const char *d;
|
||||||
int dead = 0, r = 1;
|
|
||||||
uint32_t dev;
|
|
||||||
|
|
||||||
if (!dm_get_status_raid(state->mem, params, &status)) {
|
if (!dm_get_status_raid(state->mem, params, &status)) {
|
||||||
log_error("Failed to process status line for %s.", device);
|
log_error("Failed to process status line for %s.", device);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
d = status->dev_health;
|
if ((d = strchr(status->dev_health, 'D'))) {
|
||||||
while ((d = strchr(d, 'D'))) {
|
|
||||||
dev = (uint32_t)(d - status->dev_health);
|
|
||||||
|
|
||||||
if (!(state->raid_devs[dev / 64] & (UINT64_C(1) << (dev % 64)))) {
|
|
||||||
state->raid_devs[dev / 64] |= (UINT64_C(1) << (dev % 64));
|
|
||||||
log_warn("WARNING: Device #%u of %s array, %s, has failed.",
|
|
||||||
dev, status->raid_type, device);
|
|
||||||
}
|
|
||||||
|
|
||||||
d++;
|
|
||||||
dead = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if we are converting from non-RAID to RAID (e.g. linear -> raid1)
|
|
||||||
* and too many original devices die, such that we cannot continue
|
|
||||||
* the "recover" operation, the sync action will go to "idle", the
|
|
||||||
* unsynced devs will remain at 'a', and the original devices will
|
|
||||||
* NOT SWITCH TO 'D', but will remain at 'A' - hoping to be revived.
|
|
||||||
*
|
|
||||||
* This is simply the way the kernel works...
|
|
||||||
*/
|
|
||||||
if (!strcmp(status->sync_action, "idle") &&
|
|
||||||
(status->dev_health[0] == 'a') &&
|
|
||||||
(status->insync_regions < status->total_regions)) {
|
|
||||||
log_error("Primary sources for new RAID, %s, have failed.",
|
|
||||||
device);
|
|
||||||
dead = 1; /* run it through LVM repair */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dead) {
|
|
||||||
if (status->insync_regions < status->total_regions) {
|
|
||||||
if (!state->warned) {
|
|
||||||
state->warned = 1;
|
|
||||||
log_warn("WARNING: waiting for resynchronization to finish "
|
|
||||||
"before initiating repair on RAID device %s.", device);
|
|
||||||
}
|
|
||||||
|
|
||||||
goto out; /* Not yet done syncing with accessible devices */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->failed)
|
if (state->failed)
|
||||||
goto out; /* already reported */
|
goto out; /* already reported */
|
||||||
|
|
||||||
|
log_error("Device #%d of %s array, %s, has failed.",
|
||||||
|
(int)(d - status->dev_health),
|
||||||
|
status->raid_type, device);
|
||||||
|
|
||||||
state->failed = 1;
|
state->failed = 1;
|
||||||
|
if (!dmeventd_lvm2_run_with_lock(state->cmd_lvscan))
|
||||||
|
log_warn("WARNING: Re-scan of RAID device %s failed.", device);
|
||||||
|
|
||||||
/* if repair goes OK, report success even if lvscan has failed */
|
/* if repair goes OK, report success even if lvscan has failed */
|
||||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) {
|
if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) {
|
||||||
log_error("Repair of RAID device %s failed.", device);
|
log_info("Repair of RAID device %s failed.", device);
|
||||||
r = 0;
|
dm_pool_free(state->mem, status);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state->failed = 0;
|
state->failed = 0;
|
||||||
if (status->insync_regions == status->total_regions)
|
|
||||||
memset(&state->raid_devs, 0, sizeof(state->raid_devs));
|
|
||||||
log_info("%s array, %s, is %s in-sync.",
|
log_info("%s array, %s, is %s in-sync.",
|
||||||
status->raid_type, device,
|
status->raid_type, device,
|
||||||
(status->insync_regions == status->total_regions) ? "now" : "not");
|
(status->insync_regions == status->total_regions) ? "now" : "not");
|
||||||
@@ -107,7 +64,7 @@ static int _process_raid_event(struct dso_state *state, char *params, const char
|
|||||||
out:
|
out:
|
||||||
dm_pool_free(state->mem, status);
|
dm_pool_free(state->mem, status);
|
||||||
|
|
||||||
return r;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_event(struct dm_task *dmt,
|
void process_event(struct dm_task *dmt,
|
||||||
@@ -152,9 +109,14 @@ int register_device(const char *device,
|
|||||||
if (!dmeventd_lvm2_init_with_pool("raid_state", state))
|
if (!dmeventd_lvm2_init_with_pool("raid_state", state))
|
||||||
goto_bad;
|
goto_bad;
|
||||||
|
|
||||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
|
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan),
|
||||||
"lvconvert --repair --use-policies", device))
|
"lvscan --cache", device) ||
|
||||||
|
!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert),
|
||||||
|
"lvconvert --config devices{ignore_suspended_devices=1} "
|
||||||
|
"--repair --use-policies", device)) {
|
||||||
|
dmeventd_lvm2_exit_with_pool(state);
|
||||||
goto_bad;
|
goto_bad;
|
||||||
|
}
|
||||||
|
|
||||||
*user = state;
|
*user = state;
|
||||||
|
|
||||||
@@ -164,9 +126,6 @@ int register_device(const char *device,
|
|||||||
bad:
|
bad:
|
||||||
log_error("Failed to monitor RAID %s.", device);
|
log_error("Failed to monitor RAID %s.", device);
|
||||||
|
|
||||||
if (state)
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include "dmeventd_lvm.h"
|
#include "dmeventd_lvm.h"
|
||||||
#include "libdevmapper-event.h"
|
#include "libdevmapper-event.h"
|
||||||
|
|
||||||
#include <sys/sysmacros.h>
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
@@ -149,8 +148,8 @@ static void _umount(const char *device, int major, int minor)
|
|||||||
continue; /* can't stat, skip this one */
|
continue; /* can't stat, skip this one */
|
||||||
|
|
||||||
if (S_ISBLK(st.st_mode) &&
|
if (S_ISBLK(st.st_mode) &&
|
||||||
(int) major(st.st_rdev) == major &&
|
major(st.st_rdev) == major &&
|
||||||
(int) minor(st.st_rdev) == minor) {
|
minor(st.st_rdev) == minor) {
|
||||||
log_error("Unmounting invalid snapshot %s from %s.", device, words[1]);
|
log_error("Unmounting invalid snapshot %s from %s.", device, words[1]);
|
||||||
if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
|
if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
|
||||||
log_error("Failed to umount snapshot %s from %s: %s.",
|
log_error("Failed to umount snapshot %s from %s: %s.",
|
||||||
@@ -231,7 +230,7 @@ void process_event(struct dm_task *dmt,
|
|||||||
|
|
||||||
if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
|
if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
|
||||||
log_warn("WARNING: Snapshot %s is now %.2f%% full.",
|
log_warn("WARNING: Snapshot %s is now %.2f%% full.",
|
||||||
device, dm_percent_to_round_float(percent, 2));
|
device, dm_percent_to_float(percent));
|
||||||
|
|
||||||
/* Try to extend the snapshot, in accord with user-set policies */
|
/* Try to extend the snapshot, in accord with user-set policies */
|
||||||
if (!_extend(state->cmd_lvextend))
|
if (!_extend(state->cmd_lvextend))
|
||||||
@@ -254,8 +253,10 @@ int register_device(const char *device,
|
|||||||
|
|
||||||
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvextend,
|
if (!dmeventd_lvm2_command(state->mem, state->cmd_lvextend,
|
||||||
sizeof(state->cmd_lvextend),
|
sizeof(state->cmd_lvextend),
|
||||||
"lvextend --use-policies", device))
|
"lvextend --use-policies", device)) {
|
||||||
|
dmeventd_lvm2_exit_with_pool(state);
|
||||||
goto_bad;
|
goto_bad;
|
||||||
|
}
|
||||||
|
|
||||||
state->percent_check = CHECK_MINIMUM;
|
state->percent_check = CHECK_MINIMUM;
|
||||||
*user = state;
|
*user = state;
|
||||||
@@ -266,9 +267,6 @@ int register_device(const char *device,
|
|||||||
bad:
|
bad:
|
||||||
log_error("Failed to monitor snapshot %s.", device);
|
log_error("Failed to monitor snapshot %s.", device);
|
||||||
|
|
||||||
if (state)
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2017 Red Hat, Inc. All rights reserved.
|
* Copyright (C) 2011-2015 Red Hat, Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of LVM2.
|
* This file is part of LVM2.
|
||||||
*
|
*
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
/* TODO - move this mountinfo code into library to be reusable */
|
/* TODO - move this mountinfo code into library to be reusable */
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@@ -29,9 +30,6 @@
|
|||||||
|
|
||||||
/* First warning when thin data or metadata is 80% full. */
|
/* First warning when thin data or metadata is 80% full. */
|
||||||
#define WARNING_THRESH (DM_PERCENT_1 * 80)
|
#define WARNING_THRESH (DM_PERCENT_1 * 80)
|
||||||
/* Umount thin LVs when thin data or metadata LV is >=
|
|
||||||
* and lvextend --use-policies has failed. */
|
|
||||||
#define UMOUNT_THRESH (DM_PERCENT_1 * 95)
|
|
||||||
/* Run a check every 5%. */
|
/* Run a check every 5%. */
|
||||||
#define CHECK_STEP (DM_PERCENT_1 * 5)
|
#define CHECK_STEP (DM_PERCENT_1 * 5)
|
||||||
/* Do not bother checking thin data or metadata is less than 50% full. */
|
/* Do not bother checking thin data or metadata is less than 50% full. */
|
||||||
@@ -39,119 +37,221 @@
|
|||||||
|
|
||||||
#define UMOUNT_COMMAND "/bin/umount"
|
#define UMOUNT_COMMAND "/bin/umount"
|
||||||
|
|
||||||
#define MAX_FAILS (256) /* ~42 mins between cmd call retry with 10s delay */
|
#define MAX_FAILS (10)
|
||||||
|
|
||||||
#define THIN_DEBUG 0
|
#define THIN_DEBUG 0
|
||||||
|
|
||||||
struct dso_state {
|
struct dso_state {
|
||||||
struct dm_pool *mem;
|
struct dm_pool *mem;
|
||||||
int metadata_percent_check;
|
int metadata_percent_check;
|
||||||
int metadata_percent;
|
|
||||||
int data_percent_check;
|
int data_percent_check;
|
||||||
int data_percent;
|
|
||||||
uint64_t known_metadata_size;
|
uint64_t known_metadata_size;
|
||||||
uint64_t known_data_size;
|
uint64_t known_data_size;
|
||||||
unsigned fails;
|
unsigned fails;
|
||||||
unsigned max_fails;
|
char cmd_str[1024];
|
||||||
int restore_sigset;
|
|
||||||
sigset_t old_sigset;
|
|
||||||
pid_t pid;
|
|
||||||
char *argv[3];
|
|
||||||
char *cmd_str;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DM_EVENT_LOG_FN("thin")
|
DM_EVENT_LOG_FN("thin")
|
||||||
|
|
||||||
static int _run_command(struct dso_state *state)
|
/* Get dependencies for device, and try to find matching device */
|
||||||
|
static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_minor)
|
||||||
{
|
{
|
||||||
char val[3][36];
|
struct dm_task *dmt;
|
||||||
char *env[] = { val[0], val[1], val[2], NULL };
|
const struct dm_deps *deps;
|
||||||
int i;
|
struct dm_info info;
|
||||||
|
int major, minor;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
/* Mark for possible lvm2 command we are running from dmeventd
|
if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
|
||||||
* lvm2 will not try to talk back to dmeventd while processing it */
|
return 0;
|
||||||
(void) dm_snprintf(val[0], sizeof(val[0]), "LVM_RUN_BY_DMEVENTD=1");
|
|
||||||
|
|
||||||
if (state->data_percent) {
|
if (!dm_task_set_name(dmt, name))
|
||||||
/* Prepare some known data to env vars for easy use */
|
goto out;
|
||||||
(void) dm_snprintf(val[1], sizeof(val[1]), "DMEVENTD_THIN_POOL_DATA=%d",
|
|
||||||
state->data_percent / DM_PERCENT_1);
|
if (!dm_task_no_open_count(dmt))
|
||||||
(void) dm_snprintf(val[2], sizeof(val[2]), "DMEVENTD_THIN_POOL_METADATA=%d",
|
goto out;
|
||||||
state->metadata_percent / DM_PERCENT_1);
|
|
||||||
} else {
|
if (!dm_task_run(dmt))
|
||||||
/* For an error event it's for a user to check status and decide */
|
goto out;
|
||||||
env[1] = NULL;
|
|
||||||
log_debug("Error event processing.");
|
if (!dm_task_get_info(dmt, &info))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!(deps = dm_task_get_deps(dmt)))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!info.exists || deps->count != 1)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
major = (int) MAJOR(deps->device[0]);
|
||||||
|
minor = (int) MINOR(deps->device[0]);
|
||||||
|
if ((major != tp_major) || (minor != tp_minor))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*dev_minor = info.minor;
|
||||||
|
|
||||||
|
#if THIN_DEBUG
|
||||||
|
{
|
||||||
|
char dev_name[PATH_MAX];
|
||||||
|
if (dm_device_get_name(major, minor, 0, dev_name, sizeof(dev_name)))
|
||||||
|
log_debug("Found %s (%u:%u) depends on %s.",
|
||||||
|
name, major, *dev_minor, dev_name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
r = 1;
|
||||||
|
out:
|
||||||
|
dm_task_destroy(dmt);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get all active devices */
|
||||||
|
static int _find_all_devs(dm_bitset_t bs, int tp_major, int tp_minor)
|
||||||
|
{
|
||||||
|
struct dm_task *dmt;
|
||||||
|
struct dm_names *names;
|
||||||
|
unsigned next = 0;
|
||||||
|
int minor, r = 1;
|
||||||
|
|
||||||
|
if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!dm_task_run(dmt)) {
|
||||||
|
r = 0;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_verbose("Executing command: %s", state->cmd_str);
|
if (!(names = dm_task_get_names(dmt))) {
|
||||||
|
r = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO:
|
if (!names->dev)
|
||||||
* Support parallel run of 'task' and it's waitpid maintainence
|
goto out;
|
||||||
* ATM we can't handle signaling of SIGALRM
|
|
||||||
* as signalling is not allowed while 'process_event()' is running
|
do {
|
||||||
*/
|
names = (struct dm_names *)((char *) names + next);
|
||||||
if (!(state->pid = fork())) {
|
if (_has_deps(names->name, tp_major, tp_minor, &minor))
|
||||||
/* child */
|
dm_bit_set(bs, minor);
|
||||||
(void) close(0);
|
next = names->next;
|
||||||
for (i = 3; i < 255; ++i) (void) close(i);
|
} while (next);
|
||||||
execve(state->argv[0], state->argv, env);
|
|
||||||
_exit(errno);
|
out:
|
||||||
} else if (state->pid == -1) {
|
dm_task_destroy(dmt);
|
||||||
log_error("Can't fork command %s.", state->cmd_str);
|
|
||||||
state->fails = 1;
|
return r;
|
||||||
return 0;
|
}
|
||||||
|
|
||||||
|
static int _run(const char *cmd, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int argc = 1; /* for argv[0], i.e. cmd */
|
||||||
|
int i = 0;
|
||||||
|
const char **argv;
|
||||||
|
pid_t pid = fork();
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (pid == 0) { /* child */
|
||||||
|
va_start(ap, cmd);
|
||||||
|
while (va_arg(ap, const char *))
|
||||||
|
++argc;
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
/* + 1 for the terminating NULL */
|
||||||
|
argv = alloca(sizeof(const char *) * (argc + 1));
|
||||||
|
|
||||||
|
argv[0] = cmd;
|
||||||
|
va_start(ap, cmd);
|
||||||
|
while ((argv[++i] = va_arg(ap, const char *)));
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
execvp(cmd, (char **)argv);
|
||||||
|
log_sys_error("exec", cmd);
|
||||||
|
exit(127);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid > 0) { /* parent */
|
||||||
|
if (waitpid(pid, &status, 0) != pid)
|
||||||
|
return 0; /* waitpid failed */
|
||||||
|
if (!WIFEXITED(status) || WEXITSTATUS(status))
|
||||||
|
return 0; /* the child failed */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid < 0)
|
||||||
|
return 0; /* fork failed */
|
||||||
|
|
||||||
|
return 1; /* all good */
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mountinfo_s {
|
||||||
|
const char *device;
|
||||||
|
struct dm_info info;
|
||||||
|
dm_bitset_t minors; /* Bitset for active thin pool minors */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int _umount_device(char *buffer, unsigned major, unsigned minor,
|
||||||
|
char *target, void *cb_data)
|
||||||
|
{
|
||||||
|
struct mountinfo_s *data = cb_data;
|
||||||
|
|
||||||
|
if ((major == data->info.major) && dm_bit(data->minors, minor)) {
|
||||||
|
log_info("Unmounting thin volume %s from %s.",
|
||||||
|
data->device, target);
|
||||||
|
if (!_run(UMOUNT_COMMAND, "-fl", target, NULL))
|
||||||
|
log_error("Failed to umount thin %s from %s: %s.",
|
||||||
|
data->device, target, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
|
/*
|
||||||
|
* Find all thin pool users and try to umount them.
|
||||||
|
* TODO: work with read-only thin pool support
|
||||||
|
*/
|
||||||
|
static void _umount(struct dm_task *dmt)
|
||||||
|
{
|
||||||
|
/* TODO: Convert to use hash to reduce memory usage */
|
||||||
|
static const size_t MINORS = (1U << 20); /* 20 bit */
|
||||||
|
struct mountinfo_s data = { NULL };
|
||||||
|
|
||||||
|
if (!dm_task_get_info(dmt, &data.info))
|
||||||
|
return;
|
||||||
|
|
||||||
|
data.device = dm_task_get_name(dmt);
|
||||||
|
|
||||||
|
if (!(data.minors = dm_bitset_create(NULL, MINORS))) {
|
||||||
|
log_error("Failed to allocate bitset. Not unmounting %s.", data.device);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_find_all_devs(data.minors, data.info.major, data.info.minor)) {
|
||||||
|
log_error("Failed to detect mounted volumes for %s.", data.device);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dm_mountinfo_read(_umount_device, &data)) {
|
||||||
|
log_error("Could not parse mountinfo file.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (data.minors)
|
||||||
|
dm_bitset_destroy(data.minors);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _use_policy(struct dm_task *dmt, struct dso_state *state)
|
||||||
{
|
{
|
||||||
#if THIN_DEBUG
|
#if THIN_DEBUG
|
||||||
log_debug("dmeventd executes: %s.", state->cmd_str);
|
log_info("dmeventd executes: %s.", state->cmd_str);
|
||||||
#endif
|
#endif
|
||||||
if (state->argv[0])
|
|
||||||
return _run_command(state);
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) {
|
if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) {
|
||||||
log_error("Failed command for %s.", dm_task_get_name(dmt));
|
log_error("Failed to extend thin pool %s.",
|
||||||
state->fails = 1;
|
dm_task_get_name(dmt));
|
||||||
return 0;
|
_umount(dmt);
|
||||||
}
|
state->fails++;
|
||||||
|
} else
|
||||||
state->fails = 0;
|
state->fails = 0;
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if executed command has finished
|
|
||||||
* Only 1 command may run */
|
|
||||||
static int _wait_for_pid(struct dso_state *state)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
if (state->pid == -1)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (!waitpid(state->pid, &status, WNOHANG))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Wait for finish */
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
log_verbose("Child %d exited with status %d.",
|
|
||||||
state->pid, WEXITSTATUS(status));
|
|
||||||
state->fails = WEXITSTATUS(status) ? 1 : 0;
|
|
||||||
} else {
|
|
||||||
if (WIFSIGNALED(status))
|
|
||||||
log_verbose("Child %d was terminated with status %d.",
|
|
||||||
state->pid, WTERMSIG(status));
|
|
||||||
state->fails = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->pid = -1;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_event(struct dm_task *dmt,
|
void process_event(struct dm_task *dmt,
|
||||||
@@ -159,6 +259,7 @@ void process_event(struct dm_task *dmt,
|
|||||||
void **user)
|
void **user)
|
||||||
{
|
{
|
||||||
const char *device = dm_task_get_name(dmt);
|
const char *device = dm_task_get_name(dmt);
|
||||||
|
int percent;
|
||||||
struct dso_state *state = *user;
|
struct dso_state *state = *user;
|
||||||
struct dm_status_thin_pool *tps = NULL;
|
struct dm_status_thin_pool *tps = NULL;
|
||||||
void *next = NULL;
|
void *next = NULL;
|
||||||
@@ -166,48 +267,16 @@ void process_event(struct dm_task *dmt,
|
|||||||
char *target_type = NULL;
|
char *target_type = NULL;
|
||||||
char *params;
|
char *params;
|
||||||
int needs_policy = 0;
|
int needs_policy = 0;
|
||||||
struct dm_task *new_dmt = NULL;
|
|
||||||
|
|
||||||
#if THIN_DEBUG
|
#if 0
|
||||||
log_debug("Watch for tp-data:%.2f%% tp-metadata:%.2f%%.",
|
/* No longer monitoring, waiting for remove */
|
||||||
dm_percent_to_round_float(state->data_percent_check, 2),
|
if (!state->meta_percent_check && !state->data_percent_check)
|
||||||
dm_percent_to_round_float(state->metadata_percent_check, 2));
|
|
||||||
#endif
|
|
||||||
if (!_wait_for_pid(state)) {
|
|
||||||
log_warn("WARNING: Skipping event, child %d is still running (%s).",
|
|
||||||
state->pid, state->cmd_str);
|
|
||||||
return;
|
return;
|
||||||
}
|
#endif
|
||||||
|
|
||||||
if (event & DM_EVENT_DEVICE_ERROR) {
|
if (event & DM_EVENT_DEVICE_ERROR) {
|
||||||
/* Error -> no need to check and do instant resize */
|
/* Error -> no need to check and do instant resize */
|
||||||
state->data_percent = state->metadata_percent = 0;
|
_use_policy(dmt, state);
|
||||||
if (_use_policy(dmt, state))
|
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
stack;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Rather update oldish status
|
|
||||||
* since after 'command' processing
|
|
||||||
* percentage info could have changed a lot.
|
|
||||||
* If we would get above UMOUNT_THRESH
|
|
||||||
* we would wait for next sigalarm.
|
|
||||||
*/
|
|
||||||
if (!(new_dmt = dm_task_create(DM_DEVICE_STATUS)))
|
|
||||||
goto_out;
|
|
||||||
|
|
||||||
if (!dm_task_set_uuid(new_dmt, dm_task_get_uuid(dmt)))
|
|
||||||
goto_out;
|
|
||||||
|
|
||||||
/* Non-blocking status read */
|
|
||||||
if (!dm_task_no_flush(new_dmt))
|
|
||||||
log_warn("WARNING: Can't set no_flush for dm status.");
|
|
||||||
|
|
||||||
if (!dm_task_run(new_dmt))
|
|
||||||
goto_out;
|
|
||||||
|
|
||||||
dmt = new_dmt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||||
@@ -219,6 +288,7 @@ void process_event(struct dm_task *dmt,
|
|||||||
|
|
||||||
if (!dm_get_status_thin_pool(state->mem, params, &tps)) {
|
if (!dm_get_status_thin_pool(state->mem, params, &tps)) {
|
||||||
log_error("Failed to parse status.");
|
log_error("Failed to parse status.");
|
||||||
|
_umount(dmt);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,66 +303,41 @@ void process_event(struct dm_task *dmt,
|
|||||||
if (state->known_metadata_size != tps->total_metadata_blocks) {
|
if (state->known_metadata_size != tps->total_metadata_blocks) {
|
||||||
state->metadata_percent_check = CHECK_MINIMUM;
|
state->metadata_percent_check = CHECK_MINIMUM;
|
||||||
state->known_metadata_size = tps->total_metadata_blocks;
|
state->known_metadata_size = tps->total_metadata_blocks;
|
||||||
state->fails = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->known_data_size != tps->total_data_blocks) {
|
if (state->known_data_size != tps->total_data_blocks) {
|
||||||
state->data_percent_check = CHECK_MINIMUM;
|
state->data_percent_check = CHECK_MINIMUM;
|
||||||
state->known_data_size = tps->total_data_blocks;
|
state->known_data_size = tps->total_data_blocks;
|
||||||
state->fails = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks);
|
||||||
|
if (percent >= state->metadata_percent_check) {
|
||||||
/*
|
/*
|
||||||
* Trigger action when threshold boundary is exceeded.
|
* Usage has raised more than CHECK_STEP since the last
|
||||||
* Report 80% threshold warning when it's used above 80%.
|
* time. Run actions.
|
||||||
* Only 100% is exception as it cannot be surpased so policy
|
|
||||||
* action is called for: >50%, >55% ... >95%, 100%
|
|
||||||
*/
|
*/
|
||||||
state->metadata_percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks);
|
state->metadata_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
|
||||||
if ((state->metadata_percent > WARNING_THRESH) &&
|
|
||||||
(state->metadata_percent > state->metadata_percent_check))
|
/* FIXME: extension of metadata needs to be written! */
|
||||||
|
if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
|
||||||
log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.",
|
log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.",
|
||||||
device, dm_percent_to_round_float(state->metadata_percent, 2));
|
device, dm_percent_to_float(percent));
|
||||||
if (state->metadata_percent > CHECK_MINIMUM) {
|
needs_policy = 1;
|
||||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
}
|
||||||
if (state->metadata_percent > state->metadata_percent_check)
|
|
||||||
|
percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks);
|
||||||
|
if (percent >= state->data_percent_check) {
|
||||||
|
/*
|
||||||
|
* Usage has raised more than CHECK_STEP since
|
||||||
|
* the last time. Run actions.
|
||||||
|
*/
|
||||||
|
state->data_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
|
||||||
|
|
||||||
|
if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
|
||||||
|
log_warn("WARNING: Thin pool %s data is now %.2f%% full.",
|
||||||
|
device, dm_percent_to_float(percent));
|
||||||
needs_policy = 1;
|
needs_policy = 1;
|
||||||
state->metadata_percent_check = (state->metadata_percent / CHECK_STEP + 1) * CHECK_STEP;
|
|
||||||
if (state->metadata_percent_check == DM_PERCENT_100)
|
|
||||||
state->metadata_percent_check--; /* Can't get bigger then 100% */
|
|
||||||
} else
|
|
||||||
state->metadata_percent_check = CHECK_MINIMUM;
|
|
||||||
|
|
||||||
state->data_percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks);
|
|
||||||
if ((state->data_percent > WARNING_THRESH) &&
|
|
||||||
(state->data_percent > state->data_percent_check))
|
|
||||||
log_warn("WARNING: Thin pool %s data is now %.2f%% full.",
|
|
||||||
device, dm_percent_to_round_float(state->data_percent, 2));
|
|
||||||
if (state->data_percent > CHECK_MINIMUM) {
|
|
||||||
/* Run action when usage raised more than CHECK_STEP since the last time */
|
|
||||||
if (state->data_percent > state->data_percent_check)
|
|
||||||
needs_policy = 1;
|
|
||||||
state->data_percent_check = (state->data_percent / CHECK_STEP + 1) * CHECK_STEP;
|
|
||||||
if (state->data_percent_check == DM_PERCENT_100)
|
|
||||||
state->data_percent_check--; /* Can't get bigger then 100% */
|
|
||||||
} else
|
|
||||||
state->data_percent_check = CHECK_MINIMUM;
|
|
||||||
|
|
||||||
/* Reduce number of _use_policy() calls by power-of-2 factor till frequency of MAX_FAILS is reached.
|
|
||||||
* Avoids too high number of error retries, yet shows some status messages in log regularly.
|
|
||||||
* i.e. PV could have been pvmoved and VG/LV was locked for a while...
|
|
||||||
*/
|
|
||||||
if (state->fails) {
|
|
||||||
if (state->fails++ <= state->max_fails) {
|
|
||||||
log_debug("Postponing frequently failing policy (%u <= %u).",
|
|
||||||
state->fails - 1, state->max_fails);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (state->max_fails < MAX_FAILS)
|
|
||||||
state->max_fails <<= 1;
|
|
||||||
state->fails = needs_policy = 1; /* Retry failing command */
|
|
||||||
} else
|
|
||||||
state->max_fails = 1; /* Reset on success */
|
|
||||||
|
|
||||||
if (needs_policy)
|
if (needs_policy)
|
||||||
_use_policy(dmt, state);
|
_use_policy(dmt, state);
|
||||||
@@ -300,43 +345,12 @@ out:
|
|||||||
if (tps)
|
if (tps)
|
||||||
dm_pool_free(state->mem, tps);
|
dm_pool_free(state->mem, tps);
|
||||||
|
|
||||||
if (new_dmt)
|
if (state->fails >= MAX_FAILS) {
|
||||||
dm_task_destroy(new_dmt);
|
log_warn("WARNING: Dropping monitoring of %s. "
|
||||||
}
|
"lvm2 command fails too often (%u times in raw).",
|
||||||
|
device, state->fails);
|
||||||
/* Handle SIGCHLD for a thread */
|
pthread_kill(pthread_self(), SIGALRM);
|
||||||
static void _sig_child(int signum __attribute__((unused)))
|
}
|
||||||
{
|
|
||||||
/* empty SIG_IGN */;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup handler for SIGCHLD when executing external command
|
|
||||||
* to get quick 'waitpid()' reaction
|
|
||||||
* It will interrupt syscall just like SIGALRM and
|
|
||||||
* invoke process_event().
|
|
||||||
*/
|
|
||||||
static void _init_thread_signals(struct dso_state *state)
|
|
||||||
{
|
|
||||||
struct sigaction act = { .sa_handler = _sig_child };
|
|
||||||
sigset_t my_sigset;
|
|
||||||
|
|
||||||
sigemptyset(&my_sigset);
|
|
||||||
|
|
||||||
if (sigaction(SIGCHLD, &act, NULL))
|
|
||||||
log_warn("WARNING: Failed to set SIGCHLD action.");
|
|
||||||
else if (sigaddset(&my_sigset, SIGCHLD))
|
|
||||||
log_warn("WARNING: Failed to add SIGCHLD to set.");
|
|
||||||
else if (pthread_sigmask(SIG_UNBLOCK, &my_sigset, &state->old_sigset))
|
|
||||||
log_warn("WARNING: Failed to unblock SIGCHLD.");
|
|
||||||
else
|
|
||||||
state->restore_sigset = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _restore_thread_signals(struct dso_state *state)
|
|
||||||
{
|
|
||||||
if (state->restore_sigset &&
|
|
||||||
pthread_sigmask(SIG_SETMASK, &state->old_sigset, NULL))
|
|
||||||
log_warn("WARNING: Failed to block SIGCHLD.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int register_device(const char *device,
|
int register_device(const char *device,
|
||||||
@@ -346,55 +360,27 @@ int register_device(const char *device,
|
|||||||
void **user)
|
void **user)
|
||||||
{
|
{
|
||||||
struct dso_state *state;
|
struct dso_state *state;
|
||||||
char *str;
|
|
||||||
char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */
|
|
||||||
|
|
||||||
if (!dmeventd_lvm2_init_with_pool("thin_pool_state", state))
|
if (!dmeventd_lvm2_init_with_pool("thin_pool_state", state))
|
||||||
goto_bad;
|
goto_bad;
|
||||||
|
|
||||||
if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str),
|
if (!dmeventd_lvm2_command(state->mem, state->cmd_str,
|
||||||
"_dmeventd_thin_command", device))
|
sizeof(state->cmd_str),
|
||||||
|
"lvextend --use-policies",
|
||||||
|
device)) {
|
||||||
|
dmeventd_lvm2_exit_with_pool(state);
|
||||||
goto_bad;
|
goto_bad;
|
||||||
|
|
||||||
if (strncmp(cmd_str, "lvm ", 4) == 0) {
|
|
||||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) {
|
|
||||||
log_error("Failed to copy lvm command.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
} else if (cmd_str[0] == '/') {
|
|
||||||
if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) {
|
|
||||||
log_error("Failed to copy thin command.");
|
|
||||||
goto bad;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find last space before 'vg/lv' */
|
state->metadata_percent_check = CHECK_MINIMUM;
|
||||||
if (!(str = strrchr(state->cmd_str, ' ')))
|
state->data_percent_check = CHECK_MINIMUM;
|
||||||
goto inval;
|
|
||||||
|
|
||||||
if (!(state->argv[0] = dm_pool_strndup(state->mem, state->cmd_str,
|
|
||||||
str - state->cmd_str))) {
|
|
||||||
log_error("Failed to copy command.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->argv[1] = str + 1; /* 1 argument - vg/lv */
|
|
||||||
_init_thread_signals(state);
|
|
||||||
} else /* Unuspported command format */
|
|
||||||
goto inval;
|
|
||||||
|
|
||||||
state->pid = -1;
|
|
||||||
*user = state;
|
*user = state;
|
||||||
|
|
||||||
log_info("Monitoring thin pool %s.", device);
|
log_info("Monitoring thin %s.", device);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
inval:
|
|
||||||
log_error("Invalid command for monitoring: %s.", cmd_str);
|
|
||||||
bad:
|
bad:
|
||||||
log_error("Failed to monitor thin pool %s.", device);
|
log_error("Failed to monitor thin %s.", device);
|
||||||
|
|
||||||
if (state)
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -406,31 +392,9 @@ int unregister_device(const char *device,
|
|||||||
void **user)
|
void **user)
|
||||||
{
|
{
|
||||||
struct dso_state *state = *user;
|
struct dso_state *state = *user;
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; !_wait_for_pid(state) && (i < 6); ++i) {
|
|
||||||
if (i == 0)
|
|
||||||
/* Give it 2 seconds, then try to terminate & kill it */
|
|
||||||
log_verbose("Child %d still not finished (%s) waiting.",
|
|
||||||
state->pid, state->cmd_str);
|
|
||||||
else if (i == 3) {
|
|
||||||
log_warn("WARNING: Terminating child %d.", state->pid);
|
|
||||||
kill(state->pid, SIGINT);
|
|
||||||
kill(state->pid, SIGTERM);
|
|
||||||
} else if (i == 5) {
|
|
||||||
log_warn("WARNING: Killing child %d.", state->pid);
|
|
||||||
kill(state->pid, SIGKILL);
|
|
||||||
}
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->pid != -1)
|
|
||||||
log_warn("WARNING: Cannot kill child %d!", state->pid);
|
|
||||||
|
|
||||||
_restore_thread_signals(state);
|
|
||||||
|
|
||||||
dmeventd_lvm2_exit_with_pool(state);
|
dmeventd_lvm2_exit_with_pool(state);
|
||||||
log_info("No longer monitoring thin pool %s.", device);
|
log_info("No longer monitoring thin %s.", device);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
1
daemons/dmfilemapd/.gitignore
vendored
1
daemons/dmfilemapd/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
dmfilemapd
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This file is part of the device-mapper userspace tools.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU Lesser General Public License v.2.1.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
srcdir = @srcdir@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
top_builddir = @top_builddir@
|
|
||||||
|
|
||||||
SOURCES = dmfilemapd.c
|
|
||||||
|
|
||||||
TARGETS = dmfilemapd
|
|
||||||
|
|
||||||
.PHONY: install_dmfilemapd install_dmfilemapd_static
|
|
||||||
|
|
||||||
INSTALL_DMFILEMAPD_TARGETS = install_dmfilemapd_dynamic
|
|
||||||
|
|
||||||
CLEAN_TARGETS = dmfilemapd.static
|
|
||||||
|
|
||||||
CFLOW_LIST = $(SOURCES)
|
|
||||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
|
||||||
CFLOW_TARGET = dmfilemapd
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
|
||||||
|
|
||||||
all: device-mapper
|
|
||||||
device-mapper: $(TARGETS)
|
|
||||||
|
|
||||||
CFLAGS_dmfilemapd.o += $(EXTRA_EXEC_CFLAGS)
|
|
||||||
LIBS += -ldevmapper
|
|
||||||
|
|
||||||
dmfilemapd: $(LIB_SHARED) dmfilemapd.o
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
|
|
||||||
-o $@ dmfilemapd.o $(DL_LIBS) $(LIBS)
|
|
||||||
|
|
||||||
dmfilemapd.static: $(LIB_STATIC) dmfilemapd.o $(interfacebuilddir)/libdevmapper.a
|
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L$(interfacebuilddir) \
|
|
||||||
-o $@ dmfilemapd.o $(DL_LIBS) $(LIBS) $(STATIC_LIBS)
|
|
||||||
|
|
||||||
ifneq ("$(CFLOW_CMD)", "")
|
|
||||||
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
|
|
||||||
-include $(top_builddir)/libdm/libdevmapper.cflow
|
|
||||||
-include $(top_builddir)/lib/liblvm-internal.cflow
|
|
||||||
-include $(top_builddir)/lib/liblvm2cmd.cflow
|
|
||||||
-include $(top_builddir)/daemons/dmfilemapd/$(LIB_NAME).cflow
|
|
||||||
endif
|
|
||||||
|
|
||||||
install_dmfilemapd_dynamic: dmfilemapd
|
|
||||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
|
||||||
|
|
||||||
install_dmfilemapd_static: dmfilemapd.static
|
|
||||||
$(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
|
|
||||||
|
|
||||||
install_dmfilemapd: $(INSTALL_DMFILEMAPD_TARGETS)
|
|
||||||
|
|
||||||
install: install_dmfilemapd
|
|
||||||
|
|
||||||
install_device-mapper: install_dmfilemapd
|
|
||||||
@@ -1,834 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Red Hat, Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of the device-mapper userspace tools.
|
|
||||||
*
|
|
||||||
* It includes tree drawing code based on pstree: http://psmisc.sourceforge.net/
|
|
||||||
*
|
|
||||||
* This copyrighted material is made available to anyone wishing to use,
|
|
||||||
* modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
* of the GNU General Public License v.2.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "tool.h"
|
|
||||||
|
|
||||||
#include "dm-logging.h"
|
|
||||||
|
|
||||||
#include "defaults.h"
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/inotify.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
# include "kdev_t.h"
|
|
||||||
#else
|
|
||||||
# define MAJOR(x) major((x))
|
|
||||||
# define MINOR(x) minor((x))
|
|
||||||
# define MKDEV(x,y) makedev((x),(y))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* limit to two updates/sec */
|
|
||||||
#define FILEMAPD_WAIT_USECS 500000
|
|
||||||
|
|
||||||
/* how long to wait for unlinked files */
|
|
||||||
#define FILEMAPD_NOFILE_WAIT_USECS 100000
|
|
||||||
#define FILEMAPD_NOFILE_WAIT_TRIES 10
|
|
||||||
|
|
||||||
struct filemap_monitor {
|
|
||||||
dm_filemapd_mode_t mode;
|
|
||||||
const char *program_id;
|
|
||||||
uint64_t group_id;
|
|
||||||
char *path;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
int inotify_fd;
|
|
||||||
int inotify_watch_fd;
|
|
||||||
|
|
||||||
/* monitoring heuristics */
|
|
||||||
int64_t blocks; /* allocated blocks, from stat.st_blocks */
|
|
||||||
uint64_t nr_regions;
|
|
||||||
int deleted;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int _foreground;
|
|
||||||
static int _verbose;
|
|
||||||
|
|
||||||
const char *const _usage = "dmfilemapd <fd> <group_id> <abs_path> <mode> "
|
|
||||||
"[<foreground>[<log_level>]]";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Daemon logging. By default, all messages are thrown away: messages
|
|
||||||
* are only written to the terminal if the daemon is run in the foreground.
|
|
||||||
*/
|
|
||||||
__attribute__((format(printf, 5, 0)))
|
|
||||||
static void _dmfilemapd_log_line(int level,
|
|
||||||
const char *file __attribute__((unused)),
|
|
||||||
int line __attribute__((unused)),
|
|
||||||
int dm_errno_or_class,
|
|
||||||
const char *f, va_list ap)
|
|
||||||
{
|
|
||||||
static int _abort_on_internal_errors = -1;
|
|
||||||
FILE *out = log_stderr(level) ? stderr : stdout;
|
|
||||||
|
|
||||||
level = log_level(level);
|
|
||||||
|
|
||||||
if (level <= _LOG_WARN || _verbose) {
|
|
||||||
if (level < _LOG_WARN)
|
|
||||||
out = stderr;
|
|
||||||
vfprintf(out, f, ap);
|
|
||||||
fputc('\n', out);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(f, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1))
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((format(printf, 5, 6)))
|
|
||||||
static void _dmfilemapd_log_with_errno(int level,
|
|
||||||
const char *file, int line,
|
|
||||||
int dm_errno_or_class,
|
|
||||||
const char *f, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, f);
|
|
||||||
_dmfilemapd_log_line(level, file, line, dm_errno_or_class, f, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only used for reporting errors before daemonise().
|
|
||||||
*/
|
|
||||||
__attribute__((format(printf, 1, 2)))
|
|
||||||
static void _early_log(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
fputc('\n', stderr);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _setup_logging(void)
|
|
||||||
{
|
|
||||||
dm_log_init_verbose(_verbose - 1);
|
|
||||||
dm_log_with_errno_init(_dmfilemapd_log_with_errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PROC_FD_DELETED_STR "(deleted)"
|
|
||||||
/*
|
|
||||||
* Scan the /proc/<pid>/fd directory for pid and check for an fd
|
|
||||||
* symlink whose contents match path.
|
|
||||||
*/
|
|
||||||
static int _is_open_in_pid(pid_t pid, const char *path)
|
|
||||||
{
|
|
||||||
char deleted_path[PATH_MAX + sizeof(PROC_FD_DELETED_STR)];
|
|
||||||
struct dirent *pid_dp = NULL;
|
|
||||||
char path_buf[PATH_MAX];
|
|
||||||
char link_buf[PATH_MAX];
|
|
||||||
DIR *pid_d = NULL;
|
|
||||||
ssize_t len;
|
|
||||||
|
|
||||||
if (pid == getpid())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (dm_snprintf(path_buf, sizeof(path_buf),
|
|
||||||
DEFAULT_PROC_DIR "%d/fd", pid) < 0) {
|
|
||||||
log_error("Could not format pid path.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test for the kernel 'file (deleted)' form when scanning.
|
|
||||||
*/
|
|
||||||
if (dm_snprintf(deleted_path, sizeof(deleted_path), "%s %s",
|
|
||||||
path, PROC_FD_DELETED_STR) < 0) {
|
|
||||||
log_error("Could not format check path.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_d = opendir(path_buf);
|
|
||||||
if (!pid_d) {
|
|
||||||
log_error("Could not open proc path: %s.", path_buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((pid_dp = readdir(pid_d)) != NULL) {
|
|
||||||
if (pid_dp->d_name[0] == '.')
|
|
||||||
continue;
|
|
||||||
if ((len = readlinkat(dirfd(pid_d), pid_dp->d_name, link_buf,
|
|
||||||
sizeof(link_buf))) < 0) {
|
|
||||||
log_error("readlink failed for " DEFAULT_PROC_DIR
|
|
||||||
"/%d/fd/.", pid);
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
link_buf[len] = '\0';
|
|
||||||
if (!strcmp(deleted_path, link_buf)) {
|
|
||||||
if (closedir(pid_d))
|
|
||||||
log_sys_error("closedir", path_buf);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bad:
|
|
||||||
if (closedir(pid_d))
|
|
||||||
log_sys_error("closedir", path_buf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempt to determine whether a file is open by any process by
|
|
||||||
* scanning symbolic links in /proc/<pid>/fd.
|
|
||||||
*
|
|
||||||
* This is a heuristic since it cannot guarantee to detect brief
|
|
||||||
* access in all cases: a process that opens and then closes the
|
|
||||||
* file rapidly may never be seen by the scan.
|
|
||||||
*
|
|
||||||
* The method will also give false-positives if a process exists
|
|
||||||
* that has a deleted file open that had the same path, but a
|
|
||||||
* different inode number, to the file being monitored.
|
|
||||||
*
|
|
||||||
* For this reason the daemon only uses _is_open() for unlinked
|
|
||||||
* files when the mode is DM_FILEMAPD_FOLLOW_INODE, since these
|
|
||||||
* files can no longer be newly opened by processes.
|
|
||||||
*
|
|
||||||
* In this situation !is_open(path) provides an indication that
|
|
||||||
* the daemon should shut down: the file has been unlinked from
|
|
||||||
* the file system and we appear to hold the final reference.
|
|
||||||
*/
|
|
||||||
static int _is_open(const char *path)
|
|
||||||
{
|
|
||||||
struct dirent *proc_dp = NULL;
|
|
||||||
DIR *proc_d = NULL;
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
proc_d = opendir(DEFAULT_PROC_DIR);
|
|
||||||
if (!proc_d)
|
|
||||||
return 0;
|
|
||||||
while ((proc_dp = readdir(proc_d)) != NULL) {
|
|
||||||
if (!isdigit(proc_dp->d_name[0]))
|
|
||||||
continue;
|
|
||||||
errno = 0;
|
|
||||||
pid = (pid_t) strtol(proc_dp->d_name, NULL, 10);
|
|
||||||
if (errno || !pid)
|
|
||||||
continue;
|
|
||||||
if (_is_open_in_pid(pid, path)) {
|
|
||||||
if (closedir(proc_d))
|
|
||||||
log_sys_error("closedir", DEFAULT_PROC_DIR);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closedir(proc_d))
|
|
||||||
log_sys_error("closedir", DEFAULT_PROC_DIR);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _filemap_monitor_wait(uint64_t usecs)
|
|
||||||
{
|
|
||||||
if (_verbose) {
|
|
||||||
if (usecs == FILEMAPD_WAIT_USECS)
|
|
||||||
log_very_verbose("Waiting for check interval");
|
|
||||||
if (usecs == FILEMAPD_NOFILE_WAIT_USECS)
|
|
||||||
log_very_verbose("Waiting for unlinked path");
|
|
||||||
}
|
|
||||||
usleep((useconds_t) usecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _parse_args(int argc, char **argv, struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
char *endptr;
|
|
||||||
|
|
||||||
/* we don't care what is in argv[0]. */
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
if (argc < 5) {
|
|
||||||
_early_log("Wrong number of arguments.");
|
|
||||||
_early_log("usage: %s", _usage);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We don't know the true nr_regions at daemon start time,
|
|
||||||
* and it is not worth a dm_stats_list()/group walk to count:
|
|
||||||
* we can assume that there is at least one region or the
|
|
||||||
* daemon would not have been started.
|
|
||||||
*
|
|
||||||
* A correct value will be obtained following the first update
|
|
||||||
* of the group's regions.
|
|
||||||
*/
|
|
||||||
fm->nr_regions = 1;
|
|
||||||
|
|
||||||
/* parse <fd> */
|
|
||||||
errno = 0;
|
|
||||||
fm->fd = (int) strtol(argv[0], &endptr, 10);
|
|
||||||
if (errno || *endptr) {
|
|
||||||
_early_log("Could not parse file descriptor: %s", argv[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
/* parse <group_id> */
|
|
||||||
errno = 0;
|
|
||||||
fm->group_id = strtoull(argv[0], &endptr, 10);
|
|
||||||
if (*endptr || errno) {
|
|
||||||
_early_log("Could not parse group identifier: %s", argv[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
/* parse <path> */
|
|
||||||
if (!argv[0] || !strlen(argv[0])) {
|
|
||||||
_early_log("Path argument is required.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*argv[0] != '/') {
|
|
||||||
_early_log("Path argument must specify an absolute path.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fm->path = dm_strdup(argv[0]);
|
|
||||||
if (!fm->path) {
|
|
||||||
_early_log("Could not allocate memory for path argument.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
/* parse <mode> */
|
|
||||||
if (!argv[0] || !strlen(argv[0])) {
|
|
||||||
_early_log("Mode argument is required.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fm->mode = dm_filemapd_mode_from_string(argv[0]);
|
|
||||||
if (fm->mode == DM_FILEMAPD_FOLLOW_NONE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
|
|
||||||
/* parse [<foreground>[<verbose>]] */
|
|
||||||
if (argc) {
|
|
||||||
errno = 0;
|
|
||||||
_foreground = (int) strtol(argv[0], &endptr, 10);
|
|
||||||
if (errno || *endptr) {
|
|
||||||
_early_log("Could not parse debug argument: %s.",
|
|
||||||
argv[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
argc--;
|
|
||||||
argv++;
|
|
||||||
if (argc) {
|
|
||||||
errno = 0;
|
|
||||||
_verbose = (int) strtol(argv[0], &endptr, 10);
|
|
||||||
if (errno || *endptr) {
|
|
||||||
_early_log("Could not parse verbose "
|
|
||||||
"argument: %s", argv[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (_verbose < 0 || _verbose > 3) {
|
|
||||||
_early_log("Verbose argument out of range: %d.",
|
|
||||||
_verbose);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_fd_update_blocks(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
struct stat buf;
|
|
||||||
|
|
||||||
if (fm->fd < 0) {
|
|
||||||
log_error("Filemap fd is not open.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fstat(fm->fd, &buf)) {
|
|
||||||
log_error("Failed to fstat filemap file descriptor.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fm->blocks = buf.st_blocks;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_fd_check_changed(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
int64_t old_blocks;
|
|
||||||
|
|
||||||
old_blocks = fm->blocks;
|
|
||||||
|
|
||||||
if (!_filemap_fd_update_blocks(fm))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return (fm->blocks != old_blocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _filemap_monitor_close_fd(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
if (close(fm->fd))
|
|
||||||
log_error("Error closing file descriptor.");
|
|
||||||
fm->fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _filemap_monitor_end_notify(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
inotify_rm_watch(fm->inotify_fd, fm->inotify_watch_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_set_notify(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
int inotify_fd, watch_fd;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set IN_NONBLOCK since we do not want to block in event read()
|
|
||||||
* calls. Do not set IN_CLOEXEC as dmfilemapd is single-threaded
|
|
||||||
* and does not fork or exec.
|
|
||||||
*/
|
|
||||||
if ((inotify_fd = inotify_init1(IN_NONBLOCK)) < 0) {
|
|
||||||
log_sys_error("inotify_init1", "IN_NONBLOCK");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((watch_fd = inotify_add_watch(inotify_fd, fm->path,
|
|
||||||
IN_MODIFY | IN_DELETE_SELF)) < 0) {
|
|
||||||
log_sys_error("inotify_add_watch", fm->path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fm->inotify_fd = inotify_fd;
|
|
||||||
fm->inotify_watch_fd = watch_fd;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_reopen_fd(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
int tries = FILEMAPD_NOFILE_WAIT_TRIES;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In DM_FILEMAPD_FOLLOW_PATH mode, inotify watches must be
|
|
||||||
* re-established whenever the file at the watched path is
|
|
||||||
* changed.
|
|
||||||
*
|
|
||||||
* FIXME: stat file and skip if inode is unchanged.
|
|
||||||
*/
|
|
||||||
if (fm->fd > 0)
|
|
||||||
log_error("Filemap file descriptor already open.");
|
|
||||||
|
|
||||||
while ((fm->fd < 0) && --tries)
|
|
||||||
if (((fm->fd = open(fm->path, O_RDONLY)) < 0) && tries)
|
|
||||||
_filemap_monitor_wait(FILEMAPD_NOFILE_WAIT_USECS);
|
|
||||||
|
|
||||||
if (!tries && (fm->fd < 0)) {
|
|
||||||
log_error("Could not re-open file descriptor.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _filemap_monitor_set_notify(fm);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_get_events(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
/* alignment as per man(7) inotify */
|
|
||||||
char buf[sizeof(struct inotify_event) + NAME_MAX + 1]
|
|
||||||
__attribute__ ((aligned(__alignof__(struct inotify_event))));
|
|
||||||
|
|
||||||
struct inotify_event *event;
|
|
||||||
int check = 0;
|
|
||||||
ssize_t len;
|
|
||||||
char *ptr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Close the file descriptor for the file being monitored here
|
|
||||||
* when mode=path: this will allow the inode to be de-allocated,
|
|
||||||
* and an IN_DELETE_SELF event generated in the case that the
|
|
||||||
* daemon is holding the last open reference to the file.
|
|
||||||
*/
|
|
||||||
if (fm->mode == DM_FILEMAPD_FOLLOW_PATH) {
|
|
||||||
_filemap_monitor_end_notify(fm);
|
|
||||||
_filemap_monitor_close_fd(fm);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = read(fm->inotify_fd, (void *) &buf, sizeof(buf));
|
|
||||||
|
|
||||||
/* no events to read? */
|
|
||||||
if (len < 0 && (errno == EAGAIN))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* interrupted by signal? */
|
|
||||||
if (len < 0 && (errno == EINTR))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (len < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (!len)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
for (ptr = buf; ptr < buf + len; ptr += sizeof(*event) + event->len) {
|
|
||||||
event = (struct inotify_event *) ptr;
|
|
||||||
if (event->mask & IN_DELETE_SELF)
|
|
||||||
fm->deleted = 1;
|
|
||||||
if (event->mask & IN_MODIFY)
|
|
||||||
check = 1;
|
|
||||||
/*
|
|
||||||
* Event IN_IGNORED is generated when a file has been deleted
|
|
||||||
* and IN_DELETE_SELF generated, and indicates that the file
|
|
||||||
* watch has been automatically removed.
|
|
||||||
*
|
|
||||||
* This can only happen for the DM_FILEMAPD_FOLLOW_PATH mode,
|
|
||||||
* since inotify IN_DELETE events are generated at the time
|
|
||||||
* the inode is destroyed: DM_FILEMAPD_FOLLOW_INODE will hold
|
|
||||||
* the file descriptor open, meaning that the event will not
|
|
||||||
* be generated until after the daemon closes the file.
|
|
||||||
*
|
|
||||||
* The event is ignored here since inotify monitoring will
|
|
||||||
* be reestablished (or the daemon will terminate) following
|
|
||||||
* deletion of a DM_FILEMAPD_FOLLOW_PATH monitored file.
|
|
||||||
*/
|
|
||||||
if (event->mask & IN_IGNORED)
|
|
||||||
log_very_verbose("Inotify watch removed: IN_IGNORED "
|
|
||||||
"in event->mask");
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
/*
|
|
||||||
* Re-open file descriptor if required and log disposition.
|
|
||||||
*/
|
|
||||||
if (fm->mode == DM_FILEMAPD_FOLLOW_PATH)
|
|
||||||
if (!_filemap_monitor_reopen_fd(fm))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
log_very_verbose("exiting _filemap_monitor_get_events() with "
|
|
||||||
"deleted=%d, check=%d", fm->deleted, check);
|
|
||||||
return check;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _filemap_monitor_destroy(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
if (fm->fd > 0) {
|
|
||||||
_filemap_monitor_end_notify(fm);
|
|
||||||
_filemap_monitor_close_fd(fm);
|
|
||||||
}
|
|
||||||
dm_free((void *) fm->program_id);
|
|
||||||
dm_free(fm->path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_check_same_file(int fd1, int fd2)
|
|
||||||
{
|
|
||||||
struct stat buf1, buf2;
|
|
||||||
|
|
||||||
if ((fd1 < 0) || (fd2 < 0))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (fstat(fd1, &buf1)) {
|
|
||||||
log_error("Failed to fstat file descriptor %d", fd1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fstat(fd2, &buf2)) {
|
|
||||||
log_error("Failed to fstat file descriptor %d", fd2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ((buf1.st_dev == buf2.st_dev) && (buf1.st_ino == buf2.st_ino));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _filemap_monitor_check_file_unlinked(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
char path_buf[PATH_MAX];
|
|
||||||
char link_buf[PATH_MAX];
|
|
||||||
int same, fd;
|
|
||||||
ssize_t len;
|
|
||||||
|
|
||||||
fm->deleted = 0;
|
|
||||||
same = 0;
|
|
||||||
|
|
||||||
if ((fd = open(fm->path, O_RDONLY)) < 0)
|
|
||||||
goto check_unlinked;
|
|
||||||
|
|
||||||
same = _filemap_monitor_check_same_file(fm->fd, fd);
|
|
||||||
|
|
||||||
if (close(fd))
|
|
||||||
log_error("Error closing fd %d", fd);
|
|
||||||
|
|
||||||
if (same < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (same)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
check_unlinked:
|
|
||||||
/*
|
|
||||||
* The file has been unlinked from its original location: test
|
|
||||||
* whether it is still reachable in the filesystem, or if it is
|
|
||||||
* unlinked and anonymous.
|
|
||||||
*/
|
|
||||||
if (dm_snprintf(path_buf, sizeof(path_buf), DEFAULT_PROC_DIR
|
|
||||||
"/%d/fd/%d", getpid(), fm->fd) < 0) {
|
|
||||||
log_error("Could not format pid path.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if ((len = readlink(path_buf, link_buf, sizeof(link_buf) - 1)) < 0) {
|
|
||||||
log_error("readlink failed for " DEFAULT_PROC_DIR "/%d/fd/%d.",
|
|
||||||
getpid(), fm->fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
link_buf[len] = '\0';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to re-open the file, from the path now reported in /proc/pid/fd.
|
|
||||||
*/
|
|
||||||
if ((fd = open(link_buf, O_RDONLY)) < 0)
|
|
||||||
fm->deleted = 1;
|
|
||||||
else
|
|
||||||
same = _filemap_monitor_check_same_file(fm->fd, fd);
|
|
||||||
|
|
||||||
if ((fd >= 0) && close(fd))
|
|
||||||
log_error("Error closing fd %d", fd);
|
|
||||||
|
|
||||||
if (same < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Should not happen with normal /proc. */
|
|
||||||
if ((fd > 0) && !same) {
|
|
||||||
log_error("File descriptor mismatch: %d and %s (read from %s) "
|
|
||||||
"are not the same file!", fm->fd, link_buf, path_buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _daemonise(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
pid_t pid = 0, sid;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
if (!(sid = setsid())) {
|
|
||||||
_early_log("setsid failed.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((pid = fork()) < 0) {
|
|
||||||
_early_log("Failed to fork daemon process.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid > 0) {
|
|
||||||
if (_verbose)
|
|
||||||
_early_log("Started dmfilemapd with pid=%d", pid);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chdir("/")) {
|
|
||||||
_early_log("Failed to change directory.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_verbose) {
|
|
||||||
if (close(STDIN_FILENO))
|
|
||||||
_early_log("Error closing stdin");
|
|
||||||
if (close(STDOUT_FILENO))
|
|
||||||
_early_log("Error closing stdout");
|
|
||||||
if (close(STDERR_FILENO))
|
|
||||||
_early_log("Error closing stderr");
|
|
||||||
if ((open("/dev/null", O_RDONLY) < 0) ||
|
|
||||||
(open("/dev/null", O_WRONLY) < 0) ||
|
|
||||||
(open("/dev/null", O_WRONLY) < 0)) {
|
|
||||||
_early_log("Error opening stdio streams.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* TODO: Use libdaemon/server/daemon-server.c _daemonise() */
|
|
||||||
for (fd = (int) sysconf(_SC_OPEN_MAX) - 1; fd > STDERR_FILENO; fd--) {
|
|
||||||
if (fd == fm->fd)
|
|
||||||
continue;
|
|
||||||
(void) close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _update_regions(struct dm_stats *dms, struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
uint64_t *regions = NULL, *region, nr_regions = 0;
|
|
||||||
|
|
||||||
regions = dm_stats_update_regions_from_fd(dms, fm->fd, fm->group_id);
|
|
||||||
if (!regions) {
|
|
||||||
log_error("Failed to update filemap regions for group_id="
|
|
||||||
FMTu64 ".", fm->group_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
|
|
||||||
nr_regions++;
|
|
||||||
|
|
||||||
if (!nr_regions)
|
|
||||||
log_warn("File contains no extents: exiting.");
|
|
||||||
|
|
||||||
if (nr_regions && (regions[0] != fm->group_id)) {
|
|
||||||
log_warn("group_id changed from " FMTu64 " to " FMTu64,
|
|
||||||
fm->group_id, regions[0]);
|
|
||||||
fm->group_id = regions[0];
|
|
||||||
}
|
|
||||||
dm_free(regions);
|
|
||||||
fm->nr_regions = nr_regions;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _dmfilemapd(struct filemap_monitor *fm)
|
|
||||||
{
|
|
||||||
int running = 1, check = 0, open = 0;
|
|
||||||
const char *program_id;
|
|
||||||
struct dm_stats *dms;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The correct program_id is retrieved from the group leader
|
|
||||||
* following the call to dm_stats_list().
|
|
||||||
*/
|
|
||||||
if (!(dms = dm_stats_create(NULL)))
|
|
||||||
goto_bad;
|
|
||||||
|
|
||||||
if (!dm_stats_bind_from_fd(dms, fm->fd)) {
|
|
||||||
log_error("Could not bind dm_stats handle to file descriptor "
|
|
||||||
"%d", fm->fd);
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_filemap_monitor_set_notify(fm))
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
if (!_filemap_fd_update_blocks(fm))
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
if (!dm_stats_list(dms, DM_STATS_ALL_PROGRAMS)) {
|
|
||||||
log_error("Failed to list stats handle.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Take the program_id for new regions (created by calls to
|
|
||||||
* dm_stats_update_regions_from_fd()) from the value used by
|
|
||||||
* the group leader.
|
|
||||||
*/
|
|
||||||
program_id = dm_stats_get_region_program_id(dms, fm->group_id);
|
|
||||||
if (program_id)
|
|
||||||
fm->program_id = dm_strdup(program_id);
|
|
||||||
else
|
|
||||||
fm->program_id = NULL;
|
|
||||||
dm_stats_set_program_id(dms, 1, program_id);
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (!dm_stats_group_present(dms, fm->group_id)) {
|
|
||||||
log_info("Filemap group removed: exiting.");
|
|
||||||
running = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((check = _filemap_monitor_get_events(fm)) < 0)
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
if (!check)
|
|
||||||
goto wait;
|
|
||||||
|
|
||||||
if ((check = _filemap_fd_check_changed(fm)) < 0)
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
if (check && !_update_regions(dms, fm))
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
running = !!fm->nr_regions;
|
|
||||||
if (!running)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
wait:
|
|
||||||
_filemap_monitor_wait(FILEMAPD_WAIT_USECS);
|
|
||||||
|
|
||||||
/* mode=inode termination condions */
|
|
||||||
if (fm->mode == DM_FILEMAPD_FOLLOW_INODE) {
|
|
||||||
if (!_filemap_monitor_check_file_unlinked(fm))
|
|
||||||
goto bad;
|
|
||||||
if (fm->deleted && !(open = _is_open(fm->path))) {
|
|
||||||
log_info("File unlinked and closed: exiting.");
|
|
||||||
running = 0;
|
|
||||||
} else if (fm->deleted && open)
|
|
||||||
log_verbose("File unlinked and open: "
|
|
||||||
"continuing.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dm_stats_list(dms, NULL)) {
|
|
||||||
log_error("Failed to list stats handle.");
|
|
||||||
goto bad;
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (running);
|
|
||||||
|
|
||||||
_filemap_monitor_destroy(fm);
|
|
||||||
dm_stats_destroy(dms);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
bad:
|
|
||||||
_filemap_monitor_destroy(fm);
|
|
||||||
dm_stats_destroy(dms);
|
|
||||||
log_error("Exiting");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * _mode_names[] = {
|
|
||||||
"inode",
|
|
||||||
"path"
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* dmfilemapd <fd> <group_id> <path> <mode> [<foreground>[<log_level>]]
|
|
||||||
*/
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
struct filemap_monitor fm;
|
|
||||||
|
|
||||||
memset(&fm, 0, sizeof(fm));
|
|
||||||
|
|
||||||
if (!_parse_args(argc, argv, &fm)) {
|
|
||||||
dm_free(fm.path);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setup_logging();
|
|
||||||
|
|
||||||
log_info("Starting dmfilemapd with fd=%d, group_id=" FMTu64 " "
|
|
||||||
"mode=%s, path=%s", fm.fd, fm.group_id,
|
|
||||||
_mode_names[fm.mode], fm.path);
|
|
||||||
|
|
||||||
if (!_foreground && !_daemonise(&fm))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return _dmfilemapd(&fm);
|
|
||||||
}
|
|
||||||
4
daemons/lvmdbusd/.gitignore
vendored
4
daemons/lvmdbusd/.gitignore
vendored
@@ -1,4 +0,0 @@
|
|||||||
path.py
|
|
||||||
lvmdbusd
|
|
||||||
lvmdb.py
|
|
||||||
lvm_shell_proxy.py
|
|
||||||
@@ -26,11 +26,14 @@ LVMDBUS_SRCDIR_FILES = \
|
|||||||
__init__.py \
|
__init__.py \
|
||||||
job.py \
|
job.py \
|
||||||
loader.py \
|
loader.py \
|
||||||
|
lvmdb.py \
|
||||||
main.py \
|
main.py \
|
||||||
|
lvm_shell_proxy.py \
|
||||||
lv.py \
|
lv.py \
|
||||||
manager.py \
|
manager.py \
|
||||||
objectmanager.py \
|
objectmanager.py \
|
||||||
pv.py \
|
pv.py \
|
||||||
|
refresh.py \
|
||||||
request.py \
|
request.py \
|
||||||
state.py \
|
state.py \
|
||||||
udevwatch.py \
|
udevwatch.py \
|
||||||
@@ -38,19 +41,14 @@ LVMDBUS_SRCDIR_FILES = \
|
|||||||
vg.py
|
vg.py
|
||||||
|
|
||||||
LVMDBUS_BUILDDIR_FILES = \
|
LVMDBUS_BUILDDIR_FILES = \
|
||||||
lvmdb.py \
|
|
||||||
lvm_shell_proxy.py \
|
|
||||||
path.py
|
path.py
|
||||||
|
|
||||||
LVMDBUSD = lvmdbusd
|
LVMDBUSD = $(srcdir)/lvmdbusd
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
include $(top_builddir)/make.tmpl
|
||||||
|
|
||||||
.PHONY: install_lvmdbusd
|
.PHONY: install_lvmdbusd
|
||||||
|
|
||||||
all:
|
|
||||||
test -x $(LVMDBUSD) || chmod 755 $(LVMDBUSD)
|
|
||||||
|
|
||||||
install_lvmdbusd:
|
install_lvmdbusd:
|
||||||
$(INSTALL_DIR) $(sbindir)
|
$(INSTALL_DIR) $(sbindir)
|
||||||
$(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir)
|
$(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir)
|
||||||
@@ -59,12 +57,9 @@ install_lvmdbusd:
|
|||||||
$(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(DESTDIR)$(lvmdbusdir)
|
$(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(DESTDIR)$(lvmdbusdir)
|
||||||
PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbusdir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES)
|
PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbusdir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES)
|
||||||
$(CHMOD) 755 $(DESTDIR)$(lvmdbusdir)/__pycache__
|
$(CHMOD) 755 $(DESTDIR)$(lvmdbusdir)/__pycache__
|
||||||
$(CHMOD) 444 $(DESTDIR)$(lvmdbusdir)/__pycache__/*.py[co]
|
$(CHMOD) 444 $(DESTDIR)$(lvmdbusdir)/__pycache__/*.pyc $(DESTDIR)$(lvmdbusdir)/__pycache__/*.pyo
|
||||||
|
|
||||||
install_lvm2: install_lvmdbusd
|
install_lvm2: install_lvmdbusd
|
||||||
|
|
||||||
install: install_lvm2
|
install: install_lvm2
|
||||||
|
|
||||||
DISTCLEAN_TARGETS+= \
|
|
||||||
$(LVMDBUS_BUILDDIR_FILES) \
|
|
||||||
$(LVMDBUSD)
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import dbus
|
import dbus
|
||||||
import dbus.service
|
|
||||||
from . import cfg
|
from . import cfg
|
||||||
from .utils import get_properties, add_properties, get_object_property_diff, \
|
from .utils import get_properties, add_properties, get_object_property_diff, \
|
||||||
log_debug
|
log_debug
|
||||||
@@ -38,7 +37,7 @@ class AutomatedProperties(dbus.service.Object):
|
|||||||
props = {}
|
props = {}
|
||||||
|
|
||||||
for i in self.interface():
|
for i in self.interface():
|
||||||
props[i] = AutomatedProperties._get_all_prop(self, i)
|
props[i] = self.GetAll(i)
|
||||||
|
|
||||||
return self._ap_o_path, props
|
return self._ap_o_path, props
|
||||||
|
|
||||||
@@ -65,52 +64,31 @@ class AutomatedProperties(dbus.service.Object):
|
|||||||
|
|
||||||
return self._ap_interface
|
return self._ap_interface
|
||||||
|
|
||||||
@staticmethod
|
# Properties
|
||||||
def _get_prop(obj, interface_name, property_name):
|
# noinspection PyUnusedLocal
|
||||||
value = getattr(obj, property_name)
|
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||||
|
in_signature='ss', out_signature='v')
|
||||||
|
def Get(self, interface_name, property_name):
|
||||||
|
value = getattr(self, property_name)
|
||||||
# Note: If we get an exception in this handler we won't know about it,
|
# Note: If we get an exception in this handler we won't know about it,
|
||||||
# only the side effect of no returned value!
|
# only the side effect of no returned value!
|
||||||
log_debug('Get (%s), type (%s), value(%s)' %
|
log_debug('Get (%s), type (%s), value(%s)' %
|
||||||
(property_name, str(type(value)), str(value)))
|
(property_name, str(type(value)), str(value)))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# Properties
|
|
||||||
# noinspection PyUnusedLocal
|
|
||||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||||
in_signature='ss', out_signature='v',
|
in_signature='s', out_signature='a{sv}')
|
||||||
async_callbacks=('cb', 'cbe'))
|
def GetAll(self, interface_name):
|
||||||
def Get(self, interface_name, property_name, cb, cbe):
|
if interface_name in self.interface(True):
|
||||||
# Note: If we get an exception in this handler we won't know about it,
|
|
||||||
# only the side effect of no returned value!
|
|
||||||
r = cfg.create_request_entry(
|
|
||||||
-1, AutomatedProperties._get_prop,
|
|
||||||
(self, interface_name, property_name),
|
|
||||||
cb, cbe, False)
|
|
||||||
cfg.worker_q.put(r)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_all_prop(obj, interface_name):
|
|
||||||
if interface_name in obj.interface(True):
|
|
||||||
# Using introspection, lets build this dynamically
|
# Using introspection, lets build this dynamically
|
||||||
properties = get_properties(obj)
|
properties = get_properties(self)
|
||||||
if interface_name in properties:
|
if interface_name in properties:
|
||||||
return properties[interface_name][1]
|
return properties[interface_name][1]
|
||||||
return {}
|
return {}
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
obj._ap_interface,
|
self._ap_interface,
|
||||||
'The object %s does not implement the %s interface'
|
'The object %s does not implement the %s interface'
|
||||||
% (obj.__class__, interface_name))
|
% (self.__class__, interface_name))
|
||||||
|
|
||||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
|
||||||
in_signature='s', out_signature='a{sv}',
|
|
||||||
async_callbacks=('cb', 'cbe'))
|
|
||||||
def GetAll(self, interface_name, cb, cbe):
|
|
||||||
r = cfg.create_request_entry(
|
|
||||||
-1, AutomatedProperties._get_all_prop,
|
|
||||||
(self, interface_name),
|
|
||||||
cb, cbe, False)
|
|
||||||
cfg.worker_q.put(r)
|
|
||||||
|
|
||||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||||
in_signature='ssv')
|
in_signature='ssv')
|
||||||
@@ -180,7 +158,10 @@ class AutomatedProperties(dbus.service.Object):
|
|||||||
cfg.om.lookup_update(self, new_id[0], new_id[1])
|
cfg.om.lookup_update(self, new_id[0], new_id[1])
|
||||||
|
|
||||||
# Grab the properties values, then replace the state of the object
|
# Grab the properties values, then replace the state of the object
|
||||||
# and retrieve the new values.
|
# and retrieve the new values
|
||||||
|
# TODO: We need to add locking to prevent concurrent access to the
|
||||||
|
# properties so that a client is not accessing while we are
|
||||||
|
# replacing.
|
||||||
o_prop = get_properties(self)
|
o_prop = get_properties(self)
|
||||||
self.state = new_state
|
self.state = new_state
|
||||||
n_prop = get_properties(self)
|
n_prop = get_properties(self)
|
||||||
|
|||||||
@@ -7,15 +7,18 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import threading
|
||||||
import subprocess
|
import subprocess
|
||||||
from . import cfg
|
from . import cfg
|
||||||
from .cmdhandler import options_to_cli_args, LvmExecutionMeta
|
|
||||||
import dbus
|
|
||||||
from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug,\
|
|
||||||
add_no_notify
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
|
from .cmdhandler import options_to_cli_args
|
||||||
|
import dbus
|
||||||
|
from .job import Job, JobState
|
||||||
|
from .utils import pv_range_append, pv_dest_ranges
|
||||||
|
from .request import RequestEntry
|
||||||
|
|
||||||
|
_rlock = threading.RLock()
|
||||||
|
_thread_list = list()
|
||||||
|
|
||||||
|
|
||||||
def pv_move_lv_cmd(move_options, lv_full_name,
|
def pv_move_lv_cmd(move_options, lv_full_name,
|
||||||
@@ -39,65 +42,40 @@ def lv_merge_cmd(merge_options, lv_full_name):
|
|||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
def _move_merge(interface_name, command, job_state):
|
def _create_background_dbus_job(job_state):
|
||||||
# We need to execute these command stand alone by forking & exec'ing
|
job_obj = Job(None, job_state)
|
||||||
# the command always as we will be getting periodic output from them on
|
cfg.om.register_object(job_obj)
|
||||||
# the status of the long running operation.
|
return job_obj.dbus_object_path()
|
||||||
command.insert(0, cfg.LVM_CMD)
|
|
||||||
|
|
||||||
# Instruct lvm to not register an event with us
|
|
||||||
command = add_no_notify(command)
|
|
||||||
|
|
||||||
#(self, start, ended, cmd, ec, stdout_txt, stderr_txt)
|
def _move_merge(interface_name, cmd, time_out, skip_first_line=False):
|
||||||
meta = LvmExecutionMeta(time.time(), 0, command, -1000, None, None)
|
# Create job object to be used while running the command
|
||||||
|
rc = '/'
|
||||||
|
job_state = JobState(None)
|
||||||
|
add(cmd, job_state, skip_first_line)
|
||||||
|
|
||||||
cfg.blackbox.add(meta)
|
if time_out == -1:
|
||||||
|
# Waiting forever
|
||||||
process = subprocess.Popen(command, stdout=subprocess.PIPE,
|
done = job_state.Wait(time_out)
|
||||||
env=os.environ,
|
if not done:
|
||||||
stderr=subprocess.PIPE, close_fds=True)
|
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)
|
|
||||||
|
|
||||||
# While the move is in progress we need to periodically update
|
|
||||||
# the state to reflect where everything is at.
|
|
||||||
cfg.load()
|
|
||||||
except ValueError:
|
|
||||||
log_error("Trying to parse percentage which failed for %s" %
|
|
||||||
line_str)
|
|
||||||
|
|
||||||
out = process.communicate()
|
|
||||||
|
|
||||||
with meta.lock:
|
|
||||||
meta.ended = time.time()
|
|
||||||
meta.ec = process.returncode
|
|
||||||
meta.stderr_txt = out[1]
|
|
||||||
|
|
||||||
if process.returncode == 0:
|
|
||||||
job_state.Percent = 100
|
|
||||||
else:
|
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
interface_name,
|
interface_name,
|
||||||
'Exit code %s, stderr = %s' % (str(process.returncode), out[1]))
|
'Exit code %s, stderr = %s' % (str(ec), err_msg))
|
||||||
|
elif time_out == 0:
|
||||||
|
# Immediately create and return a job
|
||||||
|
rc = _create_background_dbus_job(job_state)
|
||||||
|
else:
|
||||||
|
# Willing to wait for a bit
|
||||||
|
done = job_state.Wait(time_out)
|
||||||
|
if not done:
|
||||||
|
rc = _create_background_dbus_job(job_state)
|
||||||
|
|
||||||
cfg.load()
|
return rc
|
||||||
return '/'
|
|
||||||
|
|
||||||
|
|
||||||
def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
||||||
pv_dests_and_ranges, move_options, job_state):
|
pv_dests_and_ranges, move_options, time_out):
|
||||||
"""
|
"""
|
||||||
Common code for the pvmove handling.
|
Common code for the pvmove handling.
|
||||||
:param interface_name: What dbus interface we are providing for
|
:param interface_name: What dbus interface we are providing for
|
||||||
@@ -106,8 +84,8 @@ def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
|||||||
:param pv_source_range: (0,0 to ignore, else start, end segments)
|
:param pv_source_range: (0,0 to ignore, else start, end segments)
|
||||||
:param pv_dests_and_ranges: Array of PV object paths and start/end segs
|
:param pv_dests_and_ranges: Array of PV object paths and start/end segs
|
||||||
:param move_options: Hash with optional arguments
|
:param move_options: Hash with optional arguments
|
||||||
:param job_state: Used to convey information about jobs between processes
|
:param time_out:
|
||||||
:return: '/' When complete, the empty object path
|
:return: Object path to job object
|
||||||
"""
|
"""
|
||||||
pv_dests = []
|
pv_dests = []
|
||||||
pv_src = cfg.om.get_object_by_path(pv_src_obj)
|
pv_src = cfg.om.get_object_by_path(pv_src_obj)
|
||||||
@@ -125,39 +103,93 @@ def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
|||||||
|
|
||||||
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
|
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,
|
cmd = pv_move_lv_cmd(move_options,
|
||||||
lv_name,
|
lv_name,
|
||||||
pv_src.lvm_id,
|
pv_src.lvm_id,
|
||||||
pv_source_range,
|
pv_source_range,
|
||||||
pv_dests)
|
pv_dests)
|
||||||
|
|
||||||
return _move_merge(interface_name, cmd, job_state)
|
return _move_merge(interface_name, cmd, time_out)
|
||||||
else:
|
else:
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
interface_name, 'pv_src_obj (%s) not found' % pv_src_obj)
|
interface_name, 'pv_src_obj (%s) not found' % pv_src_obj)
|
||||||
|
|
||||||
|
|
||||||
def merge(interface_name, lv_uuid, lv_name, merge_options, job_state):
|
def merge(interface_name, lv_uuid, lv_name, merge_options, time_out):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||||
if dbo:
|
if dbo:
|
||||||
cmd = lv_merge_cmd(merge_options, dbo.lvm_id)
|
cmd = lv_merge_cmd(merge_options, dbo.lvm_id)
|
||||||
return _move_merge(interface_name, cmd, job_state)
|
return _move_merge(interface_name, cmd, time_out, True)
|
||||||
else:
|
else:
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
interface_name,
|
interface_name,
|
||||||
'LV with uuid %s and name %s not present!' % (lv_uuid, lv_name))
|
'LV with uuid %s and name %s not present!' % (lv_uuid, lv_name))
|
||||||
|
|
||||||
|
|
||||||
def _run_cmd(req):
|
def background_reaper():
|
||||||
log_debug(
|
while cfg.run.value != 0:
|
||||||
"_run_cmd: Running method: %s with args %s" %
|
with _rlock:
|
||||||
(str(req.method), str(req.arguments)))
|
num_threads = len(_thread_list) - 1
|
||||||
req.run_cmd()
|
if num_threads >= 0:
|
||||||
log_debug("_run_cmd: complete!")
|
for i in range(num_threads, -1, -1):
|
||||||
|
_thread_list[i].join(0)
|
||||||
|
if not _thread_list[i].is_alive():
|
||||||
|
_thread_list.pop(i)
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
|
||||||
def cmd_runner(request):
|
def process_background_result(job_object, exit_code, error_msg):
|
||||||
t = threading.Thread(target=_run_cmd, args=(request,),
|
cfg.load()
|
||||||
name="cmd_runner %s" % str(request.method))
|
job_object.set_result(exit_code, error_msg)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnusedLocal
|
||||||
|
def empty_cb(disregard):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def background_execute(command, background_job, skip_first_line=False):
|
||||||
|
process = subprocess.Popen(command, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE, close_fds=True)
|
||||||
|
lines_iterator = iter(process.stdout.readline, b"")
|
||||||
|
for line in lines_iterator:
|
||||||
|
# Merge ouputs a line before updates, move does not
|
||||||
|
if skip_first_line:
|
||||||
|
skip_first_line = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
if len(line) > 10:
|
||||||
|
(device, ignore, percentage) = line.decode("utf-8").split(':')
|
||||||
|
background_job.Percent = round(float(percentage.strip()[:-1]), 1)
|
||||||
|
|
||||||
|
out = process.communicate()
|
||||||
|
|
||||||
|
# print "DEBUG: EC %d, STDOUT %s, STDERR %s" % \
|
||||||
|
# (process.returncode, out[0], out[1])
|
||||||
|
|
||||||
|
if process.returncode == 0:
|
||||||
|
background_job.Percent = 100
|
||||||
|
|
||||||
|
# Queue up the result so that it gets executed in same thread as others.
|
||||||
|
r = RequestEntry(
|
||||||
|
-1, process_background_result,
|
||||||
|
(background_job, process.returncode, out[1]),
|
||||||
|
empty_cb, empty_cb, False)
|
||||||
|
cfg.worker_q.put(r)
|
||||||
|
|
||||||
|
|
||||||
|
def add(command, reporting_job, skip_first_line=False):
|
||||||
|
# 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, skip_first_line))
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
|
with _rlock:
|
||||||
|
_thread_list.append(t)
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import os
|
|||||||
import multiprocessing
|
import multiprocessing
|
||||||
import queue
|
import queue
|
||||||
import itertools
|
import itertools
|
||||||
|
try:
|
||||||
from lvmdbusd import path
|
from . import path
|
||||||
|
except SystemError:
|
||||||
|
import path
|
||||||
|
|
||||||
LVM_CMD = os.getenv('LVM_BINARY', path.LVM_BINARY)
|
LVM_CMD = os.getenv('LVM_BINARY', path.LVM_BINARY)
|
||||||
|
|
||||||
@@ -22,28 +24,24 @@ om = None
|
|||||||
# This is the global bus connection
|
# This is the global bus connection
|
||||||
bus = None
|
bus = None
|
||||||
|
|
||||||
# Command line args
|
|
||||||
args = None
|
|
||||||
|
|
||||||
# Set to true if we are depending on external events for updates
|
|
||||||
got_external_event = False
|
|
||||||
|
|
||||||
# Shared state variable across all processes
|
# Shared state variable across all processes
|
||||||
run = multiprocessing.Value('i', 1)
|
run = multiprocessing.Value('i', 1)
|
||||||
|
|
||||||
# If this is set to true, the current setup support lvm shell and we are
|
# Debug
|
||||||
# running in that mode of operation
|
DEBUG = True
|
||||||
SHELL_IN_USE = None
|
|
||||||
|
# Use lvm shell
|
||||||
|
USE_SHELL = False
|
||||||
|
|
||||||
# Lock used by pprint
|
# Lock used by pprint
|
||||||
stdout_lock = multiprocessing.Lock()
|
stdout_lock = multiprocessing.Lock()
|
||||||
|
|
||||||
|
kick_q = multiprocessing.Queue()
|
||||||
worker_q = queue.Queue()
|
worker_q = queue.Queue()
|
||||||
|
|
||||||
# Main event loop
|
# Main event loop
|
||||||
loop = None
|
loop = None
|
||||||
|
|
||||||
BUS_NAME = os.getenv('LVM_DBUS_NAME', 'com.redhat.lvmdbus1')
|
|
||||||
BASE_INTERFACE = 'com.redhat.lvmdbus1'
|
BASE_INTERFACE = 'com.redhat.lvmdbus1'
|
||||||
PV_INTERFACE = BASE_INTERFACE + '.Pv'
|
PV_INTERFACE = BASE_INTERFACE + '.Pv'
|
||||||
VG_INTERFACE = BASE_INTERFACE + '.Vg'
|
VG_INTERFACE = BASE_INTERFACE + '.Vg'
|
||||||
@@ -77,13 +75,6 @@ hidden_lv = itertools.count()
|
|||||||
|
|
||||||
# Used to prevent circular imports...
|
# Used to prevent circular imports...
|
||||||
load = None
|
load = None
|
||||||
event = None
|
|
||||||
|
|
||||||
# Global cached state
|
# Global cached state
|
||||||
db = None
|
db = None
|
||||||
|
|
||||||
# lvm flight recorder
|
|
||||||
blackbox = None
|
|
||||||
|
|
||||||
# RequestEntry ctor
|
|
||||||
create_request_entry = None
|
|
||||||
|
|||||||
@@ -11,18 +11,15 @@ from subprocess import Popen, PIPE
|
|||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
import collections
|
|
||||||
import traceback
|
|
||||||
import os
|
|
||||||
|
|
||||||
from lvmdbusd import cfg
|
|
||||||
from lvmdbusd.utils import pv_dest_ranges, log_debug, log_error, add_no_notify
|
|
||||||
from lvmdbusd.lvm_shell_proxy import LVMShellProxy
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import simplejson as json
|
from . import cfg
|
||||||
except ImportError:
|
from .utils import pv_dest_ranges, log_debug, log_error
|
||||||
import json
|
from .lvm_shell_proxy import LVMShellProxy
|
||||||
|
except SystemError:
|
||||||
|
import cfg
|
||||||
|
from utils import pv_dest_ranges, log_debug, log_error
|
||||||
|
from lvm_shell_proxy import LVMShellProxy
|
||||||
|
|
||||||
SEP = '{|}'
|
SEP = '{|}'
|
||||||
|
|
||||||
@@ -31,48 +28,11 @@ total_count = 0
|
|||||||
|
|
||||||
# We need to prevent different threads from using the same lvm shell
|
# We need to prevent different threads from using the same lvm shell
|
||||||
# at the same time.
|
# at the same time.
|
||||||
cmd_lock = threading.RLock()
|
cmd_lock = threading.Lock()
|
||||||
|
|
||||||
|
# The actual method which gets called to invoke the lvm command, can vary
|
||||||
class LvmExecutionMeta(object):
|
# from forking a new process to using lvm shell
|
||||||
|
_t_call = None
|
||||||
def __init__(self, start, ended, cmd, ec, stdout_txt, stderr_txt):
|
|
||||||
self.lock = threading.RLock()
|
|
||||||
self.start = start
|
|
||||||
self.ended = ended
|
|
||||||
self.cmd = cmd
|
|
||||||
self.ec = ec
|
|
||||||
self.stdout_txt = stdout_txt
|
|
||||||
self.stderr_txt = stderr_txt
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
with self.lock:
|
|
||||||
return "EC= %d for %s\n" \
|
|
||||||
"STARTED: %f, ENDED: %f\n" \
|
|
||||||
"STDOUT=%s\n" \
|
|
||||||
"STDERR=%s\n" % \
|
|
||||||
(self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt,
|
|
||||||
self.stderr_txt)
|
|
||||||
|
|
||||||
|
|
||||||
class LvmFlightRecorder(object):
|
|
||||||
|
|
||||||
def __init__(self, size=16):
|
|
||||||
self.queue = collections.deque(maxlen=size)
|
|
||||||
|
|
||||||
def add(self, lvm_exec_meta):
|
|
||||||
self.queue.append(lvm_exec_meta)
|
|
||||||
|
|
||||||
def dump(self):
|
|
||||||
with cmd_lock:
|
|
||||||
if len(self.queue):
|
|
||||||
log_error("LVM dbus flight recorder START")
|
|
||||||
for c in self.queue:
|
|
||||||
log_error(str(c))
|
|
||||||
log_error("LVM dbus flight recorder END")
|
|
||||||
|
|
||||||
|
|
||||||
cfg.blackbox = LvmFlightRecorder()
|
|
||||||
|
|
||||||
|
|
||||||
def _debug_c(cmd, exit_code, out):
|
def _debug_c(cmd, exit_code, out):
|
||||||
@@ -95,10 +55,8 @@ def call_lvm(command, debug=False):
|
|||||||
# Prepend the full lvm executable so that we can run different versions
|
# Prepend the full lvm executable so that we can run different versions
|
||||||
# in different locations on the same box
|
# in different locations on the same box
|
||||||
command.insert(0, cfg.LVM_CMD)
|
command.insert(0, cfg.LVM_CMD)
|
||||||
command = add_no_notify(command)
|
|
||||||
|
|
||||||
process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True,
|
process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True)
|
||||||
env=os.environ)
|
|
||||||
out = process.communicate()
|
out = process.communicate()
|
||||||
|
|
||||||
stdout_text = bytes(out[0]).decode("utf-8")
|
stdout_text = bytes(out[0]).decode("utf-8")
|
||||||
@@ -107,48 +65,37 @@ def call_lvm(command, debug=False):
|
|||||||
if debug or process.returncode != 0:
|
if debug or process.returncode != 0:
|
||||||
_debug_c(command, process.returncode, (stdout_text, stderr_text))
|
_debug_c(command, process.returncode, (stdout_text, stderr_text))
|
||||||
|
|
||||||
return process.returncode, stdout_text, stderr_text
|
if process.returncode == 0:
|
||||||
|
if cfg.DEBUG and out[1] and len(out[1]):
|
||||||
|
log_error('WARNING: lvm is out-putting text to STDERR on success!')
|
||||||
|
_debug_c(command, process.returncode, (stdout_text, stderr_text))
|
||||||
|
|
||||||
# The actual method which gets called to invoke the lvm command, can vary
|
return process.returncode, stdout_text, stderr_text
|
||||||
# from forking a new process to using lvm shell
|
|
||||||
_t_call = call_lvm
|
|
||||||
|
|
||||||
|
|
||||||
def _shell_cfg():
|
def _shell_cfg():
|
||||||
global _t_call
|
global _t_call
|
||||||
# noinspection PyBroadException
|
log_debug('Using lvm shell!')
|
||||||
try:
|
|
||||||
lvm_shell = LVMShellProxy()
|
lvm_shell = LVMShellProxy()
|
||||||
_t_call = lvm_shell.call_lvm
|
_t_call = lvm_shell.call_lvm
|
||||||
cfg.SHELL_IN_USE = lvm_shell
|
|
||||||
return True
|
|
||||||
except Exception:
|
if cfg.USE_SHELL:
|
||||||
|
_shell_cfg()
|
||||||
|
else:
|
||||||
_t_call = call_lvm
|
_t_call = call_lvm
|
||||||
cfg.SHELL_IN_USE = None
|
|
||||||
log_error(traceback.format_exc())
|
|
||||||
log_error("Unable to utilize lvm shell, dropping back to fork & exec")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def set_execution(shell):
|
def set_execution(shell):
|
||||||
global _t_call
|
global _t_call
|
||||||
with cmd_lock:
|
with cmd_lock:
|
||||||
# If the user requested lvm shell and we are currently setup that
|
_t_call = None
|
||||||
# way, just return
|
|
||||||
if cfg.SHELL_IN_USE and shell:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
if not shell and cfg.SHELL_IN_USE:
|
|
||||||
cfg.SHELL_IN_USE.exit_shell()
|
|
||||||
cfg.SHELL_IN_USE = None
|
|
||||||
|
|
||||||
_t_call = call_lvm
|
|
||||||
if shell:
|
if shell:
|
||||||
if cfg.args.use_json:
|
log_debug('Using lvm shell!')
|
||||||
return _shell_cfg()
|
lvm_shell = LVMShellProxy()
|
||||||
|
_t_call = lvm_shell.call_lvm
|
||||||
else:
|
else:
|
||||||
return False
|
_t_call = call_lvm
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def time_wrapper(command, debug=False):
|
def time_wrapper(command, debug=False):
|
||||||
@@ -158,10 +105,9 @@ def time_wrapper(command, debug=False):
|
|||||||
with cmd_lock:
|
with cmd_lock:
|
||||||
start = time.time()
|
start = time.time()
|
||||||
results = _t_call(command, debug)
|
results = _t_call(command, debug)
|
||||||
ended = time.time()
|
total_time += (time.time() - start)
|
||||||
total_time += (ended - start)
|
|
||||||
total_count += 1
|
total_count += 1
|
||||||
cfg.blackbox.add(LvmExecutionMeta(start, ended, command, *results))
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@@ -228,10 +174,6 @@ def pv_remove(device, remove_options):
|
|||||||
return call(cmd)
|
return call(cmd)
|
||||||
|
|
||||||
|
|
||||||
def _qt(tag_name):
|
|
||||||
return '@%s' % tag_name
|
|
||||||
|
|
||||||
|
|
||||||
def _tag(operation, what, add, rm, tag_options):
|
def _tag(operation, what, add, rm, tag_options):
|
||||||
cmd = [operation]
|
cmd = [operation]
|
||||||
cmd.extend(options_to_cli_args(tag_options))
|
cmd.extend(options_to_cli_args(tag_options))
|
||||||
@@ -242,11 +184,9 @@ def _tag(operation, what, add, rm, tag_options):
|
|||||||
cmd.append(what)
|
cmd.append(what)
|
||||||
|
|
||||||
if add:
|
if add:
|
||||||
cmd.extend(list(chain.from_iterable(
|
cmd.extend(list(chain.from_iterable(('--addtag', x) for x in add)))
|
||||||
('--addtag', _qt(x)) for x in add)))
|
|
||||||
if rm:
|
if rm:
|
||||||
cmd.extend(list(chain.from_iterable(
|
cmd.extend(list(chain.from_iterable(('--deltag', x) for x in rm)))
|
||||||
('--deltag', _qt(x)) for x in rm)))
|
|
||||||
|
|
||||||
return call(cmd, False)
|
return call(cmd, False)
|
||||||
|
|
||||||
@@ -281,7 +221,7 @@ def vg_lv_create(vg_name, create_options, name, size_bytes, pv_dests):
|
|||||||
cmd = ['lvcreate']
|
cmd = ['lvcreate']
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
cmd.extend(options_to_cli_args(create_options))
|
||||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
cmd.extend(['--size', str(size_bytes) + 'B'])
|
||||||
cmd.extend(['--name', name, vg_name, '--yes'])
|
cmd.extend(['--name', name, vg_name])
|
||||||
pv_dest_ranges(cmd, pv_dests)
|
pv_dest_ranges(cmd, pv_dests)
|
||||||
return call(cmd)
|
return call(cmd)
|
||||||
|
|
||||||
@@ -298,7 +238,20 @@ def vg_lv_snapshot(vg_name, snapshot_options, name, size_bytes):
|
|||||||
return call(cmd)
|
return call(cmd)
|
||||||
|
|
||||||
|
|
||||||
def _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool):
|
def vg_lv_create_linear(vg_name, create_options, name, size_bytes, thin_pool):
|
||||||
|
cmd = ['lvcreate']
|
||||||
|
cmd.extend(options_to_cli_args(create_options))
|
||||||
|
|
||||||
|
if not thin_pool:
|
||||||
|
cmd.extend(['--size', str(size_bytes) + 'B'])
|
||||||
|
else:
|
||||||
|
cmd.extend(['--thin', '--size', str(size_bytes) + 'B'])
|
||||||
|
cmd.extend(['--name', name, vg_name])
|
||||||
|
return call(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def vg_lv_create_striped(vg_name, create_options, name, size_bytes,
|
||||||
|
num_stripes, stripe_size_kb, thin_pool):
|
||||||
cmd = ['lvcreate']
|
cmd = ['lvcreate']
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
cmd.extend(options_to_cli_args(create_options))
|
||||||
|
|
||||||
@@ -307,19 +260,6 @@ def _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool):
|
|||||||
else:
|
else:
|
||||||
cmd.extend(['--thin', '--size', str(size_bytes) + 'B'])
|
cmd.extend(['--thin', '--size', str(size_bytes) + 'B'])
|
||||||
|
|
||||||
cmd.extend(['--yes'])
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
|
|
||||||
def vg_lv_create_linear(vg_name, create_options, name, size_bytes, thin_pool):
|
|
||||||
cmd = _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool)
|
|
||||||
cmd.extend(['--name', name, vg_name])
|
|
||||||
return call(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def vg_lv_create_striped(vg_name, create_options, name, size_bytes,
|
|
||||||
num_stripes, stripe_size_kb, thin_pool):
|
|
||||||
cmd = _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool)
|
|
||||||
cmd.extend(['--stripes', str(num_stripes)])
|
cmd.extend(['--stripes', str(num_stripes)])
|
||||||
|
|
||||||
if stripe_size_kb != 0:
|
if stripe_size_kb != 0:
|
||||||
@@ -344,7 +284,7 @@ def _vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
|
|||||||
if stripe_size_kb != 0:
|
if stripe_size_kb != 0:
|
||||||
cmd.extend(['--stripesize', str(stripe_size_kb)])
|
cmd.extend(['--stripesize', str(stripe_size_kb)])
|
||||||
|
|
||||||
cmd.extend(['--name', name, vg_name, '--yes'])
|
cmd.extend(['--name', name, vg_name])
|
||||||
return call(cmd)
|
return call(cmd)
|
||||||
|
|
||||||
|
|
||||||
@@ -357,15 +297,14 @@ def vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
|
|||||||
size_bytes, num_stripes, stripe_size_kb)
|
size_bytes, num_stripes, stripe_size_kb)
|
||||||
|
|
||||||
|
|
||||||
def vg_lv_create_mirror(
|
def vg_lv_create_mirror(vg_name, create_options, name, size_bytes, num_copies):
|
||||||
vg_name, create_options, name, size_bytes, num_copies):
|
|
||||||
cmd = ['lvcreate']
|
cmd = ['lvcreate']
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
cmd.extend(options_to_cli_args(create_options))
|
||||||
|
|
||||||
cmd.extend(['--type', 'mirror'])
|
cmd.extend(['--type', 'mirror'])
|
||||||
cmd.extend(['--mirrors', str(num_copies)])
|
cmd.extend(['--mirrors', str(num_copies)])
|
||||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
cmd.extend(['--size', str(size_bytes) + 'B'])
|
||||||
cmd.extend(['--name', name, vg_name, '--yes'])
|
cmd.extend(['--name', name, vg_name])
|
||||||
return call(cmd)
|
return call(cmd)
|
||||||
|
|
||||||
|
|
||||||
@@ -419,7 +358,7 @@ def lv_lv_create(lv_full_name, create_options, name, size_bytes):
|
|||||||
cmd = ['lvcreate']
|
cmd = ['lvcreate']
|
||||||
cmd.extend(options_to_cli_args(create_options))
|
cmd.extend(options_to_cli_args(create_options))
|
||||||
cmd.extend(['--virtualsize', str(size_bytes) + 'B', '-T'])
|
cmd.extend(['--virtualsize', str(size_bytes) + 'B', '-T'])
|
||||||
cmd.extend(['--name', name, lv_full_name, '--yes'])
|
cmd.extend(['--name', name, lv_full_name])
|
||||||
return call(cmd)
|
return call(cmd)
|
||||||
|
|
||||||
|
|
||||||
@@ -427,7 +366,7 @@ def lv_cache_lv(cache_pool_full_name, lv_full_name, cache_options):
|
|||||||
# lvconvert --type cache --cachepool VG/CachePoolLV VG/OriginLV
|
# lvconvert --type cache --cachepool VG/CachePoolLV VG/OriginLV
|
||||||
cmd = ['lvconvert']
|
cmd = ['lvconvert']
|
||||||
cmd.extend(options_to_cli_args(cache_options))
|
cmd.extend(options_to_cli_args(cache_options))
|
||||||
cmd.extend(['-y', '--type', 'cache', '--cachepool',
|
cmd.extend(['--type', 'cache', '--cachepool',
|
||||||
cache_pool_full_name, lv_full_name])
|
cache_pool_full_name, lv_full_name])
|
||||||
return call(cmd)
|
return call(cmd)
|
||||||
|
|
||||||
@@ -447,84 +386,19 @@ def lv_detach_cache(lv_full_name, detach_options, destroy_cache):
|
|||||||
return call(cmd)
|
return call(cmd)
|
||||||
|
|
||||||
|
|
||||||
def supports_json():
|
|
||||||
cmd = ['help']
|
|
||||||
rc, out, err = call(cmd)
|
|
||||||
if rc == 0:
|
|
||||||
if cfg.SHELL_IN_USE:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
if 'fullreport' in err:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def lvm_full_report_json():
|
|
||||||
pv_columns = ['pv_name', 'pv_uuid', 'pv_fmt', 'pv_size', 'pv_free',
|
|
||||||
'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free',
|
|
||||||
'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
|
|
||||||
'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
|
|
||||||
'vg_uuid', 'pv_missing']
|
|
||||||
|
|
||||||
pv_seg_columns = ['pvseg_start', 'pvseg_size', 'segtype',
|
|
||||||
'pv_uuid', 'lv_uuid', 'pv_name']
|
|
||||||
|
|
||||||
vg_columns = ['vg_name', 'vg_uuid', 'vg_fmt', 'vg_size', 'vg_free',
|
|
||||||
'vg_sysid', 'vg_extent_size', 'vg_extent_count',
|
|
||||||
'vg_free_count', 'vg_profile', 'max_lv', 'max_pv',
|
|
||||||
'pv_count', 'lv_count', 'snap_count', 'vg_seqno',
|
|
||||||
'vg_mda_count', 'vg_mda_free', 'vg_mda_size',
|
|
||||||
'vg_mda_used_count', 'vg_attr', 'vg_tags']
|
|
||||||
|
|
||||||
lv_columns = ['lv_uuid', 'lv_name', 'lv_path', 'lv_size',
|
|
||||||
'vg_name', 'pool_lv_uuid', 'pool_lv', 'origin_uuid',
|
|
||||||
'origin', 'data_percent',
|
|
||||||
'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
|
|
||||||
'metadata_lv', 'lv_parent', 'lv_role', 'lv_layout',
|
|
||||||
'snap_percent', 'metadata_percent', 'copy_percent',
|
|
||||||
'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
|
|
||||||
|
|
||||||
lv_seg_columns = ['seg_pe_ranges', 'segtype', 'lv_uuid']
|
|
||||||
|
|
||||||
cmd = _dc('fullreport', [
|
|
||||||
'-a', # Need hidden too
|
|
||||||
'--configreport', 'pv', '-o', ','.join(pv_columns),
|
|
||||||
'--configreport', 'vg', '-o', ','.join(vg_columns),
|
|
||||||
'--configreport', 'lv', '-o', ','.join(lv_columns),
|
|
||||||
'--configreport', 'seg', '-o', ','.join(lv_seg_columns),
|
|
||||||
'--configreport', 'pvseg', '-o', ','.join(pv_seg_columns),
|
|
||||||
'--reportformat', 'json'
|
|
||||||
])
|
|
||||||
|
|
||||||
rc, out, err = call(cmd)
|
|
||||||
if rc == 0:
|
|
||||||
# With the current implementation, if we are using the shell then we
|
|
||||||
# are using JSON and JSON is returned back to us as it was parsed to
|
|
||||||
# figure out if we completed OK or not
|
|
||||||
if cfg.SHELL_IN_USE:
|
|
||||||
assert(type(out) == dict)
|
|
||||||
return out
|
|
||||||
else:
|
|
||||||
return json.loads(out)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def pv_retrieve_with_segs(device=None):
|
def pv_retrieve_with_segs(device=None):
|
||||||
d = []
|
d = []
|
||||||
err = ""
|
|
||||||
out = ""
|
|
||||||
rc = 0
|
|
||||||
|
|
||||||
columns = ['pv_name', 'pv_uuid', 'pv_fmt', 'pv_size', 'pv_free',
|
columns = ['pv_name', 'pv_uuid', 'pv_fmt', 'pv_size', 'pv_free',
|
||||||
'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free',
|
'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free',
|
||||||
'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
|
'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
|
||||||
'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
|
'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
|
||||||
'vg_uuid', 'pvseg_start', 'pvseg_size', 'segtype', 'pv_missing']
|
'vg_uuid', 'pv_seg_start', 'pvseg_size', 'segtype']
|
||||||
|
|
||||||
# Lvm has some issues where it returns failure when querying pvs when other
|
# Lvm has some issues where it returns failure when querying pvs when other
|
||||||
# operations are in process, see:
|
# operations are in process, see:
|
||||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1274085
|
# https://bugzilla.redhat.com/show_bug.cgi?id=1274085
|
||||||
for i in range(0, 10):
|
while True:
|
||||||
cmd = _dc('pvs', ['-o', ','.join(columns)])
|
cmd = _dc('pvs', ['-o', ','.join(columns)])
|
||||||
|
|
||||||
if device:
|
if device:
|
||||||
@@ -539,13 +413,6 @@ def pv_retrieve_with_segs(device=None):
|
|||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
log_debug("LVM Bug workaround, retrying pvs command...")
|
log_debug("LVM Bug workaround, retrying pvs command...")
|
||||||
|
|
||||||
if rc != 0:
|
|
||||||
msg = "We were unable to get pvs to return without error after " \
|
|
||||||
"trying 10 times, RC=%d, STDERR=(%s), STDOUT=(%s)" % \
|
|
||||||
(rc, err, out)
|
|
||||||
log_error(msg)
|
|
||||||
raise RuntimeError(msg)
|
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
@@ -555,7 +422,7 @@ def pv_resize(device, size_bytes, create_options):
|
|||||||
cmd.extend(options_to_cli_args(create_options))
|
cmd.extend(options_to_cli_args(create_options))
|
||||||
|
|
||||||
if size_bytes != 0:
|
if size_bytes != 0:
|
||||||
cmd.extend(['--yes', '--setphysicalvolumesize', str(size_bytes) + 'B'])
|
cmd.extend(['--setphysicalvolumesize', str(size_bytes) + 'B'])
|
||||||
|
|
||||||
cmd.extend([device])
|
cmd.extend([device])
|
||||||
return call(cmd)
|
return call(cmd)
|
||||||
@@ -620,10 +487,10 @@ def vg_reduce(vg_name, missing, pv_devices, reduce_options):
|
|||||||
cmd = ['vgreduce']
|
cmd = ['vgreduce']
|
||||||
cmd.extend(options_to_cli_args(reduce_options))
|
cmd.extend(options_to_cli_args(reduce_options))
|
||||||
|
|
||||||
|
if len(pv_devices) == 0:
|
||||||
|
cmd.append('--all')
|
||||||
if missing:
|
if missing:
|
||||||
cmd.append('--removemissing')
|
cmd.append('--removemissing')
|
||||||
elif len(pv_devices) == 0:
|
|
||||||
cmd.append('--all')
|
|
||||||
|
|
||||||
cmd.append(vg_name)
|
cmd.append(vg_name)
|
||||||
cmd.extend(pv_devices)
|
cmd.extend(pv_devices)
|
||||||
@@ -732,9 +599,7 @@ def lv_retrieve_with_segments():
|
|||||||
'origin', 'data_percent',
|
'origin', 'data_percent',
|
||||||
'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
|
'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
|
||||||
'metadata_lv', 'seg_pe_ranges', 'segtype', 'lv_parent',
|
'metadata_lv', 'seg_pe_ranges', 'segtype', 'lv_parent',
|
||||||
'lv_role', 'lv_layout',
|
'lv_role', 'lv_layout']
|
||||||
'snap_percent', 'metadata_percent', 'copy_percent',
|
|
||||||
'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
|
|
||||||
|
|
||||||
cmd = _dc('lvs', ['-a', '-o', ','.join(columns)])
|
cmd = _dc('lvs', ['-a', '-o', ','.join(columns)])
|
||||||
rc, out, err = call(cmd)
|
rc, out, err = call(cmd)
|
||||||
@@ -751,4 +616,4 @@ if __name__ == '__main__':
|
|||||||
pv_data = pv_retrieve_with_segs()
|
pv_data = pv_retrieve_with_segs()
|
||||||
|
|
||||||
for p in pv_data:
|
for p in pv_data:
|
||||||
print(str(p))
|
log_debug(str(p))
|
||||||
|
|||||||
@@ -11,158 +11,20 @@ from .pv import load_pvs
|
|||||||
from .vg import load_vgs
|
from .vg import load_vgs
|
||||||
from .lv import load_lvs
|
from .lv import load_lvs
|
||||||
from . import cfg
|
from . import cfg
|
||||||
from .utils import MThreadRunner, log_debug, log_error
|
|
||||||
import threading
|
|
||||||
import queue
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
|
|
||||||
def _main_thread_load(refresh=True, emit_signal=True):
|
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True):
|
||||||
num_total_changes = 0
|
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
|
# Go through and load all the PVs, VGs and LVs
|
||||||
if cache_refresh:
|
if cache_refresh:
|
||||||
cfg.db.refresh(log)
|
cfg.db.refresh(log)
|
||||||
|
|
||||||
if need_main_thread:
|
num_total_changes += load_pvs(refresh=refresh, emit_signal=emit_signal,
|
||||||
rc = MThreadRunner(_main_thread_load, refresh, emit_signal).done()
|
cache_refresh=False)[1]
|
||||||
else:
|
num_total_changes += load_vgs(refresh=refresh, emit_signal=emit_signal,
|
||||||
rc = _main_thread_load(refresh, emit_signal)
|
cache_refresh=False)[1]
|
||||||
|
num_total_changes += load_lvs(refresh=refresh, emit_signal=emit_signal,
|
||||||
|
cache_refresh=False)[1]
|
||||||
|
|
||||||
return rc
|
return num_total_changes
|
||||||
|
|
||||||
|
|
||||||
# Even though lvm can handle multiple changes concurrently it really doesn't
|
|
||||||
# make sense to make a 1-1 fetch of data for each change of lvm because when
|
|
||||||
# we fetch the data once all previous changes are reflected.
|
|
||||||
class StateUpdate(object):
|
|
||||||
|
|
||||||
class UpdateRequest(object):
|
|
||||||
|
|
||||||
def __init__(self, refresh, emit_signal, cache_refresh, log,
|
|
||||||
need_main_thread):
|
|
||||||
self.is_done = False
|
|
||||||
self.refresh = refresh
|
|
||||||
self.emit_signal = emit_signal
|
|
||||||
self.cache_refresh = cache_refresh
|
|
||||||
self.log = log
|
|
||||||
self.need_main_thread = need_main_thread
|
|
||||||
self.result = None
|
|
||||||
self.cond = threading.Condition(threading.Lock())
|
|
||||||
|
|
||||||
def done(self):
|
|
||||||
with self.cond:
|
|
||||||
if not self.is_done:
|
|
||||||
self.cond.wait()
|
|
||||||
return self.result
|
|
||||||
|
|
||||||
def set_result(self, result):
|
|
||||||
with self.cond:
|
|
||||||
self.result = result
|
|
||||||
self.is_done = True
|
|
||||||
self.cond.notify_all()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def update_thread(obj):
|
|
||||||
queued_requests = []
|
|
||||||
while cfg.run.value != 0:
|
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
|
||||||
refresh = True
|
|
||||||
emit_signal = True
|
|
||||||
cache_refresh = True
|
|
||||||
log = True
|
|
||||||
need_main_thread = True
|
|
||||||
|
|
||||||
with obj.lock:
|
|
||||||
wait = not obj.deferred
|
|
||||||
obj.deferred = False
|
|
||||||
|
|
||||||
if len(queued_requests) == 0 and wait:
|
|
||||||
queued_requests.append(obj.queue.get(True, 2))
|
|
||||||
|
|
||||||
# Ok we have one or the deferred queue has some,
|
|
||||||
# check if any others
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
queued_requests.append(obj.queue.get(False))
|
|
||||||
|
|
||||||
except queue.Empty:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if len(queued_requests) > 1:
|
|
||||||
log_debug("Processing %d updates!" % len(queued_requests),
|
|
||||||
'bg_black', 'fg_light_green')
|
|
||||||
|
|
||||||
# We have what we can, run the update with the needed options
|
|
||||||
for i in queued_requests:
|
|
||||||
if not i.refresh:
|
|
||||||
refresh = False
|
|
||||||
if not i.emit_signal:
|
|
||||||
emit_signal = False
|
|
||||||
if not i.cache_refresh:
|
|
||||||
cache_refresh = False
|
|
||||||
if not i.log:
|
|
||||||
log = False
|
|
||||||
if not i.need_main_thread:
|
|
||||||
need_main_thread = False
|
|
||||||
|
|
||||||
num_changes = load(refresh, emit_signal, cache_refresh, log,
|
|
||||||
need_main_thread)
|
|
||||||
# Update is done, let everyone know!
|
|
||||||
for i in queued_requests:
|
|
||||||
i.set_result(num_changes)
|
|
||||||
|
|
||||||
# Only clear out the requests after we have given them a result
|
|
||||||
# otherwise we can orphan the waiting threads and they never
|
|
||||||
# wake up if we get an exception
|
|
||||||
queued_requests = []
|
|
||||||
|
|
||||||
except queue.Empty:
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
st = traceback.format_exc()
|
|
||||||
log_error("update_thread exception: \n%s" % st)
|
|
||||||
cfg.blackbox.dump()
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.lock = threading.RLock()
|
|
||||||
self.queue = queue.Queue()
|
|
||||||
self.deferred = False
|
|
||||||
|
|
||||||
# Do initial load
|
|
||||||
load(refresh=False, emit_signal=False, need_main_thread=False)
|
|
||||||
|
|
||||||
self.thread = threading.Thread(target=StateUpdate.update_thread,
|
|
||||||
args=(self,),
|
|
||||||
name="StateUpdate.update_thread")
|
|
||||||
|
|
||||||
def load(self, refresh=True, emit_signal=True, cache_refresh=True,
|
|
||||||
log=True, need_main_thread=True):
|
|
||||||
# Place this request on the queue and wait for it to be completed
|
|
||||||
req = StateUpdate.UpdateRequest(refresh, emit_signal, cache_refresh,
|
|
||||||
log, need_main_thread)
|
|
||||||
self.queue.put(req)
|
|
||||||
return req.done()
|
|
||||||
|
|
||||||
def event(self):
|
|
||||||
with self.lock:
|
|
||||||
self.deferred = True
|
|
||||||
|
|||||||
@@ -8,68 +8,24 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from .automatedproperties import AutomatedProperties
|
from .automatedproperties import AutomatedProperties
|
||||||
from .utils import job_obj_path_generate, mt_async_call
|
from .utils import job_obj_path_generate
|
||||||
from . import cfg
|
from . import cfg
|
||||||
from .cfg import JOB_INTERFACE
|
from .cfg import JOB_INTERFACE
|
||||||
import dbus
|
import dbus
|
||||||
import threading
|
import threading
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
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_call(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_call(self.cb, self.job_state.Complete)
|
|
||||||
self.job_state = None
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
class JobState(object):
|
class JobState(object):
|
||||||
def __init__(self, request=None):
|
def __init__(self, request):
|
||||||
self.rlock = threading.RLock()
|
self.rlock = threading.RLock()
|
||||||
|
|
||||||
self._percent = 0
|
self._percent = 0
|
||||||
self._complete = False
|
self._complete = False
|
||||||
self._request = request
|
self._request = request
|
||||||
|
self._cond = threading.Condition(self.rlock)
|
||||||
self._ec = 0
|
self._ec = 0
|
||||||
self._stderr = ''
|
self._stderr = ''
|
||||||
self._waiting_clients = []
|
|
||||||
|
|
||||||
# This is an lvm command that is just taking too long and doesn't
|
# This is an lvm command that is just taking too long and doesn't
|
||||||
# support background operation
|
# support background operation
|
||||||
@@ -92,6 +48,8 @@ class JobState(object):
|
|||||||
with self.rlock:
|
with self.rlock:
|
||||||
if self._request:
|
if self._request:
|
||||||
self._complete = self._request.is_done()
|
self._complete = self._request.is_done()
|
||||||
|
if self._complete:
|
||||||
|
self._percent = 100
|
||||||
|
|
||||||
return self._complete
|
return self._complete
|
||||||
|
|
||||||
@@ -99,8 +57,7 @@ class JobState(object):
|
|||||||
def Complete(self, value):
|
def Complete(self, value):
|
||||||
with self.rlock:
|
with self.rlock:
|
||||||
self._complete = value
|
self._complete = value
|
||||||
self._percent = 100
|
self._cond.notify_all()
|
||||||
self.notify_waiting_clients()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def GetError(self):
|
def GetError(self):
|
||||||
@@ -114,10 +71,29 @@ class JobState(object):
|
|||||||
else:
|
else:
|
||||||
return (-1, 'Job is not complete!')
|
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):
|
def dtor(self):
|
||||||
with self.rlock:
|
with self.rlock:
|
||||||
self._request = None
|
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
|
@property
|
||||||
def Result(self):
|
def Result(self):
|
||||||
with self.rlock:
|
with self.rlock:
|
||||||
@@ -125,40 +101,10 @@ class JobState(object):
|
|||||||
return self._request.result()
|
return self._request.result()
|
||||||
return '/'
|
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
|
# noinspection PyPep8Naming
|
||||||
class Job(AutomatedProperties):
|
class Job(AutomatedProperties):
|
||||||
_Percent_meta = ('d', JOB_INTERFACE)
|
_Percent_meta = ('y', JOB_INTERFACE)
|
||||||
_Complete_meta = ('b', JOB_INTERFACE)
|
_Complete_meta = ('b', JOB_INTERFACE)
|
||||||
_Result_meta = ('o', JOB_INTERFACE)
|
_Result_meta = ('o', JOB_INTERFACE)
|
||||||
_GetError_meta = ('(is)', JOB_INTERFACE)
|
_GetError_meta = ('(is)', JOB_INTERFACE)
|
||||||
@@ -174,25 +120,26 @@ class Job(AutomatedProperties):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def Percent(self):
|
def Percent(self):
|
||||||
return dbus.Double(float(self.state.Percent))
|
return self.state.Percent
|
||||||
|
|
||||||
|
@Percent.setter
|
||||||
|
def Percent(self, value):
|
||||||
|
self.state.Percent = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Complete(self):
|
def Complete(self):
|
||||||
return dbus.Boolean(self.state.Complete)
|
return self.state.Complete
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _signal_complete(obj):
|
|
||||||
obj.PropertiesChanged(
|
|
||||||
JOB_INTERFACE, dict(Complete=dbus.Boolean(obj.state.Complete)), [])
|
|
||||||
|
|
||||||
@Complete.setter
|
@Complete.setter
|
||||||
def Complete(self, value):
|
def Complete(self, value):
|
||||||
self.state.Complete = value
|
self.state.Complete = value
|
||||||
mt_async_call(Job._signal_complete, self)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def GetError(self):
|
def GetError(self):
|
||||||
return dbus.Struct(self.state.GetError, signature="(is)")
|
return self.state.GetError
|
||||||
|
|
||||||
|
def set_result(self, ec, msg):
|
||||||
|
self.state.set_result(ec, msg)
|
||||||
|
|
||||||
@dbus.service.method(dbus_interface=JOB_INTERFACE)
|
@dbus.service.method(dbus_interface=JOB_INTERFACE)
|
||||||
def Remove(self):
|
def Remove(self):
|
||||||
@@ -205,18 +152,13 @@ class Job(AutomatedProperties):
|
|||||||
|
|
||||||
@dbus.service.method(dbus_interface=JOB_INTERFACE,
|
@dbus.service.method(dbus_interface=JOB_INTERFACE,
|
||||||
in_signature='i',
|
in_signature='i',
|
||||||
out_signature='b',
|
out_signature='b')
|
||||||
async_callbacks=('cb', 'cbe'))
|
def Wait(self, timeout):
|
||||||
def Wait(self, timeout, cb, cbe):
|
return self.state.Wait(timeout)
|
||||||
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
|
@property
|
||||||
def Result(self):
|
def Result(self):
|
||||||
return dbus.ObjectPath(self.state.Result)
|
return self.state.Result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lvm_id(self):
|
def lvm_id(self):
|
||||||
|
|||||||
@@ -21,41 +21,7 @@ from .utils import n, n32
|
|||||||
from .loader import common
|
from .loader import common
|
||||||
from .state import State
|
from .state import State
|
||||||
from . import background
|
from . import background
|
||||||
from .utils import round_size, mt_remove_dbus_objects
|
from .utils import round_size
|
||||||
from .job import JobState
|
|
||||||
|
|
||||||
|
|
||||||
# Try and build a key for a LV, so that we sort the LVs with least dependencies
|
|
||||||
# first. This may be error prone because of the flexibility LVM
|
|
||||||
# provides and what you can stack.
|
|
||||||
def get_key(i):
|
|
||||||
|
|
||||||
name = i['lv_name']
|
|
||||||
parent = i['lv_parent']
|
|
||||||
pool = i['pool_lv']
|
|
||||||
a1 = ""
|
|
||||||
a2 = ""
|
|
||||||
|
|
||||||
if name[0] == '[':
|
|
||||||
a1 = '#'
|
|
||||||
|
|
||||||
# We have a parent
|
|
||||||
if parent:
|
|
||||||
# Check if parent is hidden
|
|
||||||
if parent[0] == '[':
|
|
||||||
a2 = '##'
|
|
||||||
else:
|
|
||||||
a2 = '#'
|
|
||||||
|
|
||||||
# If a LV has a pool, then it should be sorted/loaded after the pool
|
|
||||||
# lv, unless it's a hidden too, then after other hidden, but before visible
|
|
||||||
if pool:
|
|
||||||
if pool[0] != '[':
|
|
||||||
a2 += '~'
|
|
||||||
else:
|
|
||||||
a1 = '$' + a1
|
|
||||||
|
|
||||||
return "%s%s%s" % (a1, a2, name)
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
@@ -65,13 +31,7 @@ def lvs_state_retrieve(selection, cache_refresh=True):
|
|||||||
if cache_refresh:
|
if cache_refresh:
|
||||||
cfg.db.refresh()
|
cfg.db.refresh()
|
||||||
|
|
||||||
# When building up the model, it's best to process LVs with the least
|
for l in cfg.db.fetch_lvs(selection):
|
||||||
# dependencies to those that are dependant upon other LVs. Otherwise, when
|
|
||||||
# we are trying to gather information we could be in a position where we
|
|
||||||
# don't have information available yet.
|
|
||||||
lvs = sorted(cfg.db.fetch_lvs(selection), key=get_key)
|
|
||||||
|
|
||||||
for l in lvs:
|
|
||||||
rc.append(LvState(
|
rc.append(LvState(
|
||||||
l['lv_uuid'], l['lv_name'],
|
l['lv_uuid'], l['lv_name'],
|
||||||
l['lv_path'], n(l['lv_size']),
|
l['lv_path'], n(l['lv_size']),
|
||||||
@@ -81,14 +41,7 @@ def lvs_state_retrieve(selection, cache_refresh=True):
|
|||||||
n32(l['data_percent']), l['lv_attr'],
|
n32(l['data_percent']), l['lv_attr'],
|
||||||
l['lv_tags'], l['lv_active'], l['data_lv'],
|
l['lv_tags'], l['lv_active'], l['data_lv'],
|
||||||
l['metadata_lv'], l['segtype'], l['lv_role'],
|
l['metadata_lv'], l['segtype'], l['lv_role'],
|
||||||
l['lv_layout'],
|
l['lv_layout']))
|
||||||
n32(l['snap_percent']),
|
|
||||||
n32(l['metadata_percent']),
|
|
||||||
n32(l['copy_percent']),
|
|
||||||
n32(l['sync_percent']),
|
|
||||||
n(l['lv_metadata_size']),
|
|
||||||
l['move_pv'],
|
|
||||||
l['move_pv_uuid']))
|
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
|
|
||||||
@@ -108,15 +61,9 @@ class LvState(State):
|
|||||||
rc = []
|
rc = []
|
||||||
for pv in sorted(cfg.db.lv_contained_pv(uuid)):
|
for pv in sorted(cfg.db.lv_contained_pv(uuid)):
|
||||||
(pv_uuid, pv_name, pv_segs) = pv
|
(pv_uuid, pv_name, pv_segs) = pv
|
||||||
pv_obj = cfg.om.get_object_path_by_uuid_lvm_id(pv_uuid, pv_name)
|
pv_obj = cfg.om.get_object_path_by_lvm_id(
|
||||||
|
pv_uuid, pv_name, gen_new=False)
|
||||||
segs_decorate = []
|
rc.append((pv_obj, pv_segs))
|
||||||
for i in pv_segs:
|
|
||||||
segs_decorate.append((dbus.UInt64(i[0]),
|
|
||||||
dbus.UInt64(i[1]),
|
|
||||||
dbus.String(i[2])))
|
|
||||||
|
|
||||||
rc.append((dbus.ObjectPath(pv_obj), segs_decorate))
|
|
||||||
|
|
||||||
return dbus.Array(rc, signature="(oa(tts))")
|
return dbus.Array(rc, signature="(oa(tts))")
|
||||||
|
|
||||||
@@ -137,28 +84,27 @@ class LvState(State):
|
|||||||
|
|
||||||
for l in cfg.db.hidden_lvs(self.Uuid):
|
for l in cfg.db.hidden_lvs(self.Uuid):
|
||||||
full_name = "%s/%s" % (vg_name, l[1])
|
full_name = "%s/%s" % (vg_name, l[1])
|
||||||
op = cfg.om.get_object_path_by_uuid_lvm_id(l[0], full_name)
|
op = cfg.om.get_object_path_by_lvm_id(
|
||||||
|
l[0], full_name, gen_new=False)
|
||||||
assert op
|
assert op
|
||||||
rc.append(dbus.ObjectPath(op))
|
rc.append(op)
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
def __init__(self, Uuid, Name, Path, SizeBytes,
|
def __init__(self, Uuid, Name, Path, SizeBytes,
|
||||||
vg_name, vg_uuid, pool_lv_uuid, PoolLv,
|
vg_name, vg_uuid, pool_lv_uuid, PoolLv,
|
||||||
origin_uuid, OriginLv, DataPercent, Attr, Tags, active,
|
origin_uuid, OriginLv, DataPercent, Attr, Tags, active,
|
||||||
data_lv, metadata_lv, segtypes, role, layout, SnapPercent,
|
data_lv, metadata_lv, segtypes, role, layout):
|
||||||
MetaDataPercent, CopyPercent, SyncPercent, MetaDataSizeBytes,
|
|
||||||
move_pv, move_pv_uuid):
|
|
||||||
utils.init_class_from_arguments(self)
|
utils.init_class_from_arguments(self)
|
||||||
|
|
||||||
# The segtypes is possibly an array with potentially dupes or a single
|
# The segtypes is possibly an array with potentially dupes or a single
|
||||||
# value
|
# value
|
||||||
self._segs = dbus.Array([], signature='s')
|
self._segs = dbus.Array([], signature='s')
|
||||||
if not isinstance(segtypes, list):
|
if not isinstance(segtypes, list):
|
||||||
self._segs.append(dbus.String(segtypes))
|
self._segs.append(segtypes)
|
||||||
else:
|
else:
|
||||||
self._segs.extend([dbus.String(x) for x in set(segtypes)])
|
self._segs.extend(set(segtypes))
|
||||||
|
|
||||||
self.Vg = cfg.om.get_object_path_by_uuid_lvm_id(
|
self.Vg = cfg.om.get_object_path_by_lvm_id(
|
||||||
vg_uuid, vg_name, vg_obj_path_generate)
|
vg_uuid, vg_name, vg_obj_path_generate)
|
||||||
|
|
||||||
self.Devices = LvState._pv_devices(self.Uuid)
|
self.Devices = LvState._pv_devices(self.Uuid)
|
||||||
@@ -166,14 +112,15 @@ class LvState(State):
|
|||||||
if PoolLv:
|
if PoolLv:
|
||||||
gen = utils.lv_object_path_method(Name, (Attr, layout, role))
|
gen = utils.lv_object_path_method(Name, (Attr, layout, role))
|
||||||
|
|
||||||
self.PoolLv = cfg.om.get_object_path_by_uuid_lvm_id(
|
self.PoolLv = cfg.om.get_object_path_by_lvm_id(
|
||||||
pool_lv_uuid, '%s/%s' % (vg_name, PoolLv), gen)
|
pool_lv_uuid, '%s/%s' % (vg_name, PoolLv),
|
||||||
|
gen)
|
||||||
else:
|
else:
|
||||||
self.PoolLv = '/'
|
self.PoolLv = '/'
|
||||||
|
|
||||||
if OriginLv:
|
if OriginLv:
|
||||||
self.OriginLv = \
|
self.OriginLv = \
|
||||||
cfg.om.get_object_path_by_uuid_lvm_id(
|
cfg.om.get_object_path_by_lvm_id(
|
||||||
origin_uuid, '%s/%s' % (vg_name, OriginLv),
|
origin_uuid, '%s/%s' % (vg_name, OriginLv),
|
||||||
vg_obj_path_generate)
|
vg_obj_path_generate)
|
||||||
else:
|
else:
|
||||||
@@ -190,6 +137,8 @@ class LvState(State):
|
|||||||
self.Name, (self.Attr, self.layout, self.role))
|
self.Name, (self.Attr, self.layout, self.role))
|
||||||
|
|
||||||
def _object_type_create(self):
|
def _object_type_create(self):
|
||||||
|
if self.Name[0] == '[':
|
||||||
|
return LvCommon
|
||||||
if self.Attr[0] == 't':
|
if self.Attr[0] == 't':
|
||||||
return LvThinPool
|
return LvThinPool
|
||||||
elif self.Attr[0] == 'C':
|
elif self.Attr[0] == 'C':
|
||||||
@@ -197,8 +146,6 @@ class LvState(State):
|
|||||||
return LvCachePool
|
return LvCachePool
|
||||||
else:
|
else:
|
||||||
return LvCacheLv
|
return LvCacheLv
|
||||||
elif self.Name[0] == '[':
|
|
||||||
return LvCommon
|
|
||||||
elif self.OriginLv != '/':
|
elif self.OriginLv != '/':
|
||||||
return LvSnapShot
|
return LvSnapShot
|
||||||
else:
|
else:
|
||||||
@@ -206,7 +153,7 @@ class LvState(State):
|
|||||||
|
|
||||||
def create_dbus_object(self, path):
|
def create_dbus_object(self, path):
|
||||||
if not path:
|
if not path:
|
||||||
path = cfg.om.get_object_path_by_uuid_lvm_id(
|
path = cfg.om.get_object_path_by_lvm_id(
|
||||||
self.Uuid, self.lvm_id, self._object_path_create())
|
self.Uuid, self.lvm_id, self._object_path_create())
|
||||||
|
|
||||||
obj_ctor = self._object_type_create()
|
obj_ctor = self._object_type_create()
|
||||||
@@ -223,23 +170,15 @@ class LvState(State):
|
|||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Name', 's')
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Name', 's')
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Path', 's')
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Path', 's')
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SizeBytes', 't')
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'SizeBytes', 't')
|
||||||
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u')
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SegType', 'as')
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'SegType', 'as')
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Vg', 'o')
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Vg', 'o')
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'OriginLv', 'o')
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'OriginLv', 'o')
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'PoolLv', 'o')
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'PoolLv', 'o')
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Devices', "a(oa(tts))")
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'Devices', "a(oa(tts))")
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'HiddenLvs', "ao")
|
@utils.dbus_property(LV_COMMON_INTERFACE, 'HiddenLvs', "ao")
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Attr', 's')
|
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u')
|
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SnapPercent', 'u')
|
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u')
|
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'MetaDataPercent', 'u')
|
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'CopyPercent', 'u')
|
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SyncPercent', 'u')
|
|
||||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'MetaDataSizeBytes', 't')
|
|
||||||
class LvCommon(AutomatedProperties):
|
class LvCommon(AutomatedProperties):
|
||||||
_Tags_meta = ("as", LV_COMMON_INTERFACE)
|
_Tags_meta = ("as", LV_COMMON_INTERFACE)
|
||||||
_Roles_meta = ("as", LV_COMMON_INTERFACE)
|
|
||||||
_IsThinVolume_meta = ("b", LV_COMMON_INTERFACE)
|
_IsThinVolume_meta = ("b", LV_COMMON_INTERFACE)
|
||||||
_IsThinPool_meta = ("b", LV_COMMON_INTERFACE)
|
_IsThinPool_meta = ("b", LV_COMMON_INTERFACE)
|
||||||
_Active_meta = ("b", LV_COMMON_INTERFACE)
|
_Active_meta = ("b", LV_COMMON_INTERFACE)
|
||||||
@@ -252,45 +191,12 @@ class LvCommon(AutomatedProperties):
|
|||||||
_FixedMinor_meta = ('b', LV_COMMON_INTERFACE)
|
_FixedMinor_meta = ('b', LV_COMMON_INTERFACE)
|
||||||
_ZeroBlocks_meta = ('b', LV_COMMON_INTERFACE)
|
_ZeroBlocks_meta = ('b', LV_COMMON_INTERFACE)
|
||||||
_SkipActivation_meta = ('b', LV_COMMON_INTERFACE)
|
_SkipActivation_meta = ('b', LV_COMMON_INTERFACE)
|
||||||
_MovePv_meta = ('o', LV_COMMON_INTERFACE)
|
|
||||||
|
|
||||||
def _get_move_pv(self):
|
|
||||||
path = None
|
|
||||||
|
|
||||||
# It's likely that the move_pv is empty
|
|
||||||
if self.state.move_pv_uuid and self.state.move_pv:
|
|
||||||
path = cfg.om.get_object_path_by_uuid_lvm_id(
|
|
||||||
self.state.move_pv_uuid, self.state.move_pv)
|
|
||||||
if not path:
|
|
||||||
path = '/'
|
|
||||||
return path
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal,PyPep8Naming
|
# noinspection PyUnusedLocal,PyPep8Naming
|
||||||
def __init__(self, object_path, object_state):
|
def __init__(self, object_path, object_state):
|
||||||
super(LvCommon, self).__init__(object_path, lvs_state_retrieve)
|
super(LvCommon, self).__init__(object_path, lvs_state_retrieve)
|
||||||
self.set_interface(LV_COMMON_INTERFACE)
|
self.set_interface(LV_COMMON_INTERFACE)
|
||||||
self.state = object_state
|
self.state = object_state
|
||||||
self._move_pv = self._get_move_pv()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def handle_execute(rc, out, err):
|
|
||||||
if rc == 0:
|
|
||||||
cfg.load()
|
|
||||||
else:
|
|
||||||
# Need to work on error handling, need consistent
|
|
||||||
raise dbus.exceptions.DBusException(
|
|
||||||
LV_INTERFACE,
|
|
||||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def validate_dbus_object(lv_uuid, lv_name):
|
|
||||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
|
||||||
if not dbo:
|
|
||||||
raise dbus.exceptions.DBusException(
|
|
||||||
LV_INTERFACE,
|
|
||||||
'LV with uuid %s and name %s not present!' %
|
|
||||||
(lv_uuid, lv_name))
|
|
||||||
return dbo
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def VolumeType(self):
|
def VolumeType(self):
|
||||||
@@ -305,16 +211,14 @@ class LvCommon(AutomatedProperties):
|
|||||||
'V': 'thin Volume', 't': 'thin pool', 'T': 'Thin pool data',
|
'V': 'thin Volume', 't': 'thin pool', 'T': 'Thin pool data',
|
||||||
'e': 'raid or pool metadata or pool metadata spare',
|
'e': 'raid or pool metadata or pool metadata spare',
|
||||||
'-': 'Unspecified'}
|
'-': 'Unspecified'}
|
||||||
return dbus.Struct((self.state.Attr[0], type_map[self.state.Attr[0]]),
|
return (self.state.Attr[0], type_map[self.state.Attr[0]])
|
||||||
signature="as")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Permissions(self):
|
def Permissions(self):
|
||||||
type_map = {'w': 'writable', 'r': 'read-only',
|
type_map = {'w': 'writable', 'r': 'read-only',
|
||||||
'R': 'Read-only activation of non-read-only volume',
|
'R': 'Read-only activation of non-read-only volume',
|
||||||
'-': 'Unspecified'}
|
'-': 'Unspecified'}
|
||||||
return dbus.Struct((self.state.Attr[1], type_map[self.state.Attr[1]]),
|
return (self.state.Attr[1], type_map[self.state.Attr[1]])
|
||||||
signature="(ss)")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def AllocationPolicy(self):
|
def AllocationPolicy(self):
|
||||||
@@ -323,12 +227,11 @@ class LvCommon(AutomatedProperties):
|
|||||||
'i': 'inherited', 'I': 'inherited locked',
|
'i': 'inherited', 'I': 'inherited locked',
|
||||||
'l': 'cling', 'L': 'cling locked',
|
'l': 'cling', 'L': 'cling locked',
|
||||||
'n': 'normal', 'N': 'normal locked', '-': 'Unspecified'}
|
'n': 'normal', 'N': 'normal locked', '-': 'Unspecified'}
|
||||||
return dbus.Struct((self.state.Attr[2], type_map[self.state.Attr[2]]),
|
return (self.state.Attr[2], type_map[self.state.Attr[2]])
|
||||||
signature="(ss)")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def FixedMinor(self):
|
def FixedMinor(self):
|
||||||
return dbus.Boolean(self.state.Attr[3] == 'm')
|
return self.state.Attr[3] == 'm'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def State(self):
|
def State(self):
|
||||||
@@ -339,32 +242,29 @@ class LvCommon(AutomatedProperties):
|
|||||||
'd': 'mapped device present without tables',
|
'd': 'mapped device present without tables',
|
||||||
'i': 'mapped device present with inactive table',
|
'i': 'mapped device present with inactive table',
|
||||||
'X': 'unknown', '-': 'Unspecified'}
|
'X': 'unknown', '-': 'Unspecified'}
|
||||||
return dbus.Struct((self.state.Attr[4], type_map[self.state.Attr[4]]),
|
return (self.state.Attr[4], type_map[self.state.Attr[4]])
|
||||||
signature="(ss)")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def TargetType(self):
|
def TargetType(self):
|
||||||
type_map = {'C': 'Cache', 'm': 'mirror', 'r': 'raid',
|
type_map = {'C': 'Cache', 'm': 'mirror', 'r': 'raid',
|
||||||
's': 'snapshot', 't': 'thin', 'u': 'unknown',
|
's': 'snapshot', 't': 'thin', 'u': 'unknown',
|
||||||
'v': 'virtual', '-': 'Unspecified'}
|
'v': 'virtual', '-': 'Unspecified'}
|
||||||
return dbus.Struct((self.state.Attr[6], type_map[self.state.Attr[6]]),
|
return (self.state.Attr[6], type_map[self.state.Attr[6]])
|
||||||
signature="(ss)")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ZeroBlocks(self):
|
def ZeroBlocks(self):
|
||||||
return dbus.Boolean(self.state.Attr[7] == 'z')
|
return self.state.Attr[7] == 'z'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Health(self):
|
def Health(self):
|
||||||
type_map = {'p': 'partial', 'r': 'refresh',
|
type_map = {'p': 'partial', 'r': 'refresh',
|
||||||
'm': 'mismatches', 'w': 'writemostly',
|
'm': 'mismatches', 'w': 'writemostly',
|
||||||
'X': 'X unknown', '-': 'Unspecified'}
|
'X': 'X unknown', '-': 'Unspecified'}
|
||||||
return dbus.Struct((self.state.Attr[8], type_map[self.state.Attr[8]]),
|
return (self.state.Attr[8], type_map[self.state.Attr[8]])
|
||||||
signature="(ss)")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def SkipActivation(self):
|
def SkipActivation(self):
|
||||||
return dbus.Boolean(self.state.Attr[9] == 'k')
|
return self.state.Attr[9] == 'k'
|
||||||
|
|
||||||
def vg_name_lookup(self):
|
def vg_name_lookup(self):
|
||||||
return self.state.vg_name_lookup()
|
return self.state.vg_name_lookup()
|
||||||
@@ -380,45 +280,32 @@ class LvCommon(AutomatedProperties):
|
|||||||
def Tags(self):
|
def Tags(self):
|
||||||
return utils.parse_tags(self.state.Tags)
|
return utils.parse_tags(self.state.Tags)
|
||||||
|
|
||||||
@property
|
|
||||||
def Roles(self):
|
|
||||||
return utils.parse_tags(self.state.role)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lvm_id(self):
|
def lvm_id(self):
|
||||||
return self.state.lvm_id
|
return self.state.lvm_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def IsThinVolume(self):
|
def IsThinVolume(self):
|
||||||
return dbus.Boolean(self.state.Attr[0] == 'V')
|
return self.state.Attr[0] == 'V'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def IsThinPool(self):
|
def IsThinPool(self):
|
||||||
return dbus.Boolean(self.state.Attr[0] == 't')
|
return self.state.Attr[0] == 't'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Active(self):
|
def Active(self):
|
||||||
return dbus.Boolean(self.state.active == "active")
|
return self.state.active == "active"
|
||||||
|
|
||||||
@property
|
@dbus.service.method(
|
||||||
def MovePv(self):
|
dbus_interface=LV_COMMON_INTERFACE,
|
||||||
return dbus.ObjectPath(self._move_pv)
|
in_signature='ia{sv}',
|
||||||
|
out_signature='o')
|
||||||
|
def _Future(self, tmo, open_options):
|
||||||
|
raise dbus.exceptions.DBusException(LV_COMMON_INTERFACE, 'Do not use!')
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
class Lv(LvCommon):
|
class Lv(LvCommon):
|
||||||
def _fetch_hidden(self, name):
|
|
||||||
|
|
||||||
# The name is vg/name
|
|
||||||
full_name = "%s/%s" % (self.vg_name_lookup(), name)
|
|
||||||
return cfg.om.get_object_path_by_lvm_id(full_name)
|
|
||||||
|
|
||||||
def _get_data_meta(self):
|
|
||||||
|
|
||||||
# Get the data
|
|
||||||
return (self._fetch_hidden(self.state.data_lv),
|
|
||||||
self._fetch_hidden(self.state.metadata_lv))
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal,PyPep8Naming
|
# noinspection PyUnusedLocal,PyPep8Naming
|
||||||
def __init__(self, object_path, object_state):
|
def __init__(self, object_path, object_state):
|
||||||
super(Lv, self).__init__(object_path, object_state)
|
super(Lv, self).__init__(object_path, object_state)
|
||||||
@@ -428,10 +315,25 @@ class Lv(LvCommon):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _remove(lv_uuid, lv_name, remove_options):
|
def _remove(lv_uuid, lv_name, remove_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
# Remove the LV, if successful then remove from the model
|
# Remove the LV, if successful then remove from the model
|
||||||
rc, out, err = cmdhandler.lv_remove(lv_name, remove_options)
|
rc, out, err = cmdhandler.lv_remove(lv_name, remove_options)
|
||||||
LvCommon.handle_execute(rc, out, err)
|
|
||||||
|
if rc == 0:
|
||||||
|
cfg.om.remove_object(dbo, True)
|
||||||
|
cfg.load()
|
||||||
|
else:
|
||||||
|
# Need to work on error handling, need consistent
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'LV with uuid %s and name %s not present!' %
|
||||||
|
(lv_uuid, lv_name))
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -449,11 +351,24 @@ class Lv(LvCommon):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _rename(lv_uuid, lv_name, new_name, rename_options):
|
def _rename(lv_uuid, lv_name, new_name, rename_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
# Rename the logical volume
|
# Rename the logical volume
|
||||||
rc, out, err = cmdhandler.lv_rename(lv_name, new_name,
|
rc, out, err = cmdhandler.lv_rename(lv_name, new_name,
|
||||||
rename_options)
|
rename_options)
|
||||||
LvCommon.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
cfg.load()
|
||||||
|
else:
|
||||||
|
# Need to work on error handling, need consistent
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'LV with uuid %s and name %s not present!' %
|
||||||
|
(lv_uuid, lv_name))
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -473,41 +388,53 @@ class Lv(LvCommon):
|
|||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=LV_INTERFACE,
|
dbus_interface=LV_INTERFACE,
|
||||||
in_signature='o(tt)a(ott)ia{sv}',
|
in_signature='o(tt)a(ott)ia{sv}',
|
||||||
out_signature='o',
|
out_signature='o')
|
||||||
async_callbacks=('cb', 'cbe'))
|
|
||||||
def Move(self, pv_src_obj, pv_source_range,
|
def Move(self, pv_src_obj, pv_source_range,
|
||||||
pv_dests_and_ranges,
|
pv_dests_and_ranges,
|
||||||
tmo, move_options, cb, cbe):
|
tmo, move_options):
|
||||||
|
return background.move(
|
||||||
job_state = JobState()
|
LV_INTERFACE, self.lvm_id, pv_src_obj,
|
||||||
|
pv_source_range, pv_dests_and_ranges,
|
||||||
r = RequestEntry(
|
move_options, tmo)
|
||||||
tmo, background.move,
|
|
||||||
(LV_INTERFACE, self.lvm_id, pv_src_obj, pv_source_range,
|
|
||||||
pv_dests_and_ranges, move_options, job_state), cb, cbe, False,
|
|
||||||
job_state)
|
|
||||||
|
|
||||||
background.cmd_runner(r)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _snap_shot(lv_uuid, lv_name, name, optional_size,
|
def _snap_shot(lv_uuid, lv_name, name, optional_size,
|
||||||
snapshot_options):
|
snapshot_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
# If you specify a size you get a 'thick' snapshot even if
|
# If you specify a size you get a 'thick' snapshot even if
|
||||||
# it is a thin lv
|
# it is a thin lv
|
||||||
if not dbo.IsThinVolume:
|
if not dbo.IsThinVolume:
|
||||||
if optional_size == 0:
|
if optional_size == 0:
|
||||||
|
# TODO: Should we pick a sane default or force user to
|
||||||
|
# make a decision?
|
||||||
space = dbo.SizeBytes / 80
|
space = dbo.SizeBytes / 80
|
||||||
remainder = space % 512
|
remainder = space % 512
|
||||||
optional_size = space + 512 - remainder
|
optional_size = space + 512 - remainder
|
||||||
|
|
||||||
rc, out, err = cmdhandler.vg_lv_snapshot(
|
rc, out, err = cmdhandler.vg_lv_snapshot(
|
||||||
lv_name, snapshot_options, name, optional_size)
|
lv_name, snapshot_options, name, optional_size)
|
||||||
LvCommon.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
return_path = '/'
|
||||||
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
||||||
return cfg.om.get_object_path_by_lvm_id(full_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
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'LV with uuid %s and name %s not present!' %
|
||||||
|
(lv_uuid, lv_name))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=LV_INTERFACE,
|
dbus_interface=LV_INTERFACE,
|
||||||
@@ -530,8 +457,9 @@ class Lv(LvCommon):
|
|||||||
resize_options):
|
resize_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
pv_dests = []
|
pv_dests = []
|
||||||
dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
# If we have PVs, verify them
|
# If we have PVs, verify them
|
||||||
if len(pv_dests_and_ranges):
|
if len(pv_dests_and_ranges):
|
||||||
for pr in pv_dests_and_ranges:
|
for pr in pv_dests_and_ranges:
|
||||||
@@ -544,10 +472,23 @@ class Lv(LvCommon):
|
|||||||
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
|
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
|
||||||
|
|
||||||
size_change = new_size_bytes - dbo.SizeBytes
|
size_change = new_size_bytes - dbo.SizeBytes
|
||||||
|
|
||||||
rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change,
|
rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change,
|
||||||
pv_dests, resize_options)
|
pv_dests, resize_options)
|
||||||
LvCommon.handle_execute(rc, out, err)
|
|
||||||
|
if rc == 0:
|
||||||
|
# Refresh what's changed
|
||||||
|
cfg.load()
|
||||||
return "/"
|
return "/"
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'LV with uuid %s and name %s not present!' %
|
||||||
|
(lv_uuid, lv_name))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=LV_INTERFACE,
|
dbus_interface=LV_INTERFACE,
|
||||||
@@ -580,11 +521,23 @@ class Lv(LvCommon):
|
|||||||
def _lv_activate_deactivate(uuid, lv_name, activate, control_flags,
|
def _lv_activate_deactivate(uuid, lv_name, activate, control_flags,
|
||||||
options):
|
options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
LvCommon.validate_dbus_object(uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.activate_deactivate(
|
rc, out, err = cmdhandler.activate_deactivate(
|
||||||
'lvchange', lv_name, activate, control_flags, options)
|
'lvchange', lv_name, activate, control_flags, options)
|
||||||
LvCommon.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
dbo.refresh()
|
||||||
return '/'
|
return '/'
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'LV with uuid %s and name %s not present!' %
|
||||||
|
(uuid, lv_name))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=LV_INTERFACE,
|
dbus_interface=LV_INTERFACE,
|
||||||
@@ -616,11 +569,25 @@ class Lv(LvCommon):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _add_rm_tags(uuid, lv_name, tags_add, tags_del, tag_options):
|
def _add_rm_tags(uuid, lv_name, tags_add, tags_del, tag_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
LvCommon.validate_dbus_object(uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
|
|
||||||
rc, out, err = cmdhandler.lv_tag(
|
rc, out, err = cmdhandler.lv_tag(
|
||||||
lv_name, tags_add, tags_del, tag_options)
|
lv_name, tags_add, tags_del, tag_options)
|
||||||
LvCommon.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
dbo.refresh()
|
||||||
return '/'
|
return '/'
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'LV with uuid %s and name %s not present!' %
|
||||||
|
(uuid, lv_name))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=LV_INTERFACE,
|
dbus_interface=LV_INTERFACE,
|
||||||
@@ -662,6 +629,23 @@ class LvThinPool(Lv):
|
|||||||
_DataLv_meta = ("o", THIN_POOL_INTERFACE)
|
_DataLv_meta = ("o", THIN_POOL_INTERFACE)
|
||||||
_MetaDataLv_meta = ("o", THIN_POOL_INTERFACE)
|
_MetaDataLv_meta = ("o", THIN_POOL_INTERFACE)
|
||||||
|
|
||||||
|
def _fetch_hidden(self, name):
|
||||||
|
|
||||||
|
# The name is vg/name
|
||||||
|
full_name = "%s/%s" % (self.vg_name_lookup(), name)
|
||||||
|
|
||||||
|
o = cfg.om.get_object_by_lvm_id(full_name)
|
||||||
|
if o:
|
||||||
|
return o.dbus_object_path()
|
||||||
|
|
||||||
|
return '/'
|
||||||
|
|
||||||
|
def _get_data_meta(self):
|
||||||
|
|
||||||
|
# Get the data
|
||||||
|
return (self._fetch_hidden(self.state.data_lv),
|
||||||
|
self._fetch_hidden(self.state.metadata_lv))
|
||||||
|
|
||||||
def __init__(self, object_path, object_state):
|
def __init__(self, object_path, object_state):
|
||||||
super(LvThinPool, self).__init__(object_path, object_state)
|
super(LvThinPool, self).__init__(object_path, object_state)
|
||||||
self.set_interface(THIN_POOL_INTERFACE)
|
self.set_interface(THIN_POOL_INTERFACE)
|
||||||
@@ -669,22 +653,37 @@ class LvThinPool(Lv):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def DataLv(self):
|
def DataLv(self):
|
||||||
return dbus.ObjectPath(self._data_lv)
|
return self._data_lv
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def MetaDataLv(self):
|
def MetaDataLv(self):
|
||||||
return dbus.ObjectPath(self._metadata_lv)
|
return self._metadata_lv
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _lv_create(lv_uuid, lv_name, name, size_bytes, create_options):
|
def _lv_create(lv_uuid, lv_name, name, size_bytes, create_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||||
|
|
||||||
|
lv_created = '/'
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.lv_lv_create(
|
rc, out, err = cmdhandler.lv_lv_create(
|
||||||
lv_name, create_options, name, size_bytes)
|
lv_name, create_options, name, size_bytes)
|
||||||
LvCommon.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
||||||
return cfg.om.get_object_path_by_lvm_id(full_name)
|
lvs = load_lvs([full_name], emit_signal=True)[0]
|
||||||
|
for l in lvs:
|
||||||
|
lv_created = l.dbus_object_path()
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'LV with uuid %s and name %s not present!' %
|
||||||
|
(lv_uuid, lv_name))
|
||||||
|
return lv_created
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=THIN_POOL_INTERFACE,
|
dbus_interface=THIN_POOL_INTERFACE,
|
||||||
@@ -703,31 +702,20 @@ class LvThinPool(Lv):
|
|||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
class LvCachePool(Lv):
|
class LvCachePool(Lv):
|
||||||
_DataLv_meta = ("o", CACHE_POOL_INTERFACE)
|
|
||||||
_MetaDataLv_meta = ("o", CACHE_POOL_INTERFACE)
|
|
||||||
|
|
||||||
def __init__(self, object_path, object_state):
|
def __init__(self, object_path, object_state):
|
||||||
super(LvCachePool, self).__init__(object_path, object_state)
|
super(LvCachePool, self).__init__(object_path, object_state)
|
||||||
self.set_interface(CACHE_POOL_INTERFACE)
|
self.set_interface(CACHE_POOL_INTERFACE)
|
||||||
self._data_lv, self._metadata_lv = self._get_data_meta()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def DataLv(self):
|
|
||||||
return dbus.ObjectPath(self._data_lv)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def MetaDataLv(self):
|
|
||||||
return dbus.ObjectPath(self._metadata_lv)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _cache_lv(lv_uuid, lv_name, lv_object_path, cache_options):
|
def _cache_lv(lv_uuid, lv_name, lv_object_path, cache_options):
|
||||||
|
|
||||||
# Make sure we have a dbus object representing cache pool
|
# Make sure we have a dbus object representing cache pool
|
||||||
dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||||
|
|
||||||
# Make sure we have dbus object representing lv to cache
|
# Make sure we have dbus object representing lv to cache
|
||||||
lv_to_cache = cfg.om.get_object_by_path(lv_object_path)
|
lv_to_cache = cfg.om.get_object_by_path(lv_object_path)
|
||||||
|
|
||||||
if lv_to_cache:
|
if dbo and lv_to_cache:
|
||||||
fcn = lv_to_cache.lv_full_name()
|
fcn = lv_to_cache.lv_full_name()
|
||||||
rc, out, err = cmdhandler.lv_cache_lv(
|
rc, out, err = cmdhandler.lv_cache_lv(
|
||||||
dbo.lv_full_name(), fcn, cache_options)
|
dbo.lv_full_name(), fcn, cache_options)
|
||||||
@@ -735,18 +723,28 @@ class LvCachePool(Lv):
|
|||||||
# When we cache an LV, the cache pool and the lv that is getting
|
# When we cache an LV, the cache pool and the lv that is getting
|
||||||
# cached need to be removed from the object manager and
|
# cached need to be removed from the object manager and
|
||||||
# re-created as their interfaces have changed!
|
# re-created as their interfaces have changed!
|
||||||
mt_remove_dbus_objects((dbo, lv_to_cache))
|
cfg.om.remove_object(dbo, emit_signal=True)
|
||||||
|
cfg.om.remove_object(lv_to_cache, emit_signal=True)
|
||||||
cfg.load()
|
cfg.load()
|
||||||
|
|
||||||
lv_converted = cfg.om.get_object_path_by_lvm_id(fcn)
|
lv_converted = \
|
||||||
|
cfg.om.get_object_by_lvm_id(fcn).dbus_object_path()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
LV_INTERFACE,
|
LV_INTERFACE,
|
||||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
else:
|
else:
|
||||||
raise dbus.exceptions.DBusException(
|
msg = ""
|
||||||
LV_INTERFACE, 'LV to cache with object path %s not present!' %
|
if not dbo:
|
||||||
lv_object_path)
|
dbo += 'CachePool LV with uuid %s and name %s not present!' % \
|
||||||
|
(lv_uuid, lv_name)
|
||||||
|
|
||||||
|
if not lv_to_cache:
|
||||||
|
dbo += 'LV to cache with object path %s not present!' % \
|
||||||
|
(lv_object_path)
|
||||||
|
|
||||||
|
raise dbus.exceptions.DBusException(LV_INTERFACE, msg)
|
||||||
return lv_converted
|
return lv_converted
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -772,12 +770,14 @@ class LvCacheLv(Lv):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def CachePool(self):
|
def CachePool(self):
|
||||||
return dbus.ObjectPath(self.state.PoolLv)
|
return self.state.PoolLv
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _detach_lv(lv_uuid, lv_name, detach_options, destroy_cache):
|
def _detach_lv(lv_uuid, lv_name, detach_options, destroy_cache):
|
||||||
# Make sure we have a dbus object representing cache pool
|
# Make sure we have a dbus object representing cache pool
|
||||||
dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
|
|
||||||
# Get current cache name
|
# Get current cache name
|
||||||
cache_pool = cfg.om.get_object_by_path(dbo.CachePool)
|
cache_pool = cfg.om.get_object_by_path(dbo.CachePool)
|
||||||
@@ -787,15 +787,22 @@ class LvCacheLv(Lv):
|
|||||||
if rc == 0:
|
if rc == 0:
|
||||||
# The cache pool gets removed as hidden and put back to
|
# The cache pool gets removed as hidden and put back to
|
||||||
# visible, so lets delete
|
# visible, so lets delete
|
||||||
mt_remove_dbus_objects((cache_pool, dbo))
|
cfg.om.remove_object(cache_pool, emit_signal=True)
|
||||||
|
cfg.om.remove_object(dbo, emit_signal=True)
|
||||||
cfg.load()
|
cfg.load()
|
||||||
|
|
||||||
uncached_lv_path = cfg.om.get_object_path_by_lvm_id(lv_name)
|
uncached_lv_path = \
|
||||||
|
cfg.om.get_object_by_lvm_id(lv_name).dbus_object_path()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
LV_INTERFACE,
|
LV_INTERFACE,
|
||||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
LV_INTERFACE,
|
||||||
|
'LV with uuid %s and name %s not present!' %
|
||||||
|
(lv_uuid, lv_name))
|
||||||
return uncached_lv_path
|
return uncached_lv_path
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -820,13 +827,7 @@ class LvSnapShot(Lv):
|
|||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=SNAPSHOT_INTERFACE,
|
dbus_interface=SNAPSHOT_INTERFACE,
|
||||||
in_signature='ia{sv}',
|
in_signature='ia{sv}',
|
||||||
out_signature='o',
|
out_signature='o')
|
||||||
async_callbacks=('cb', 'cbe'))
|
def Merge(self, tmo, merge_options):
|
||||||
def Merge(self, tmo, merge_options, cb, cbe):
|
return background.merge(SNAPSHOT_INTERFACE, self.Uuid, self.lvm_id,
|
||||||
job_state = JobState()
|
merge_options, tmo)
|
||||||
|
|
||||||
r = RequestEntry(tmo, background.merge,
|
|
||||||
(SNAPSHOT_INTERFACE, self.Uuid, self.lvm_id,
|
|
||||||
merge_options, job_state), cb, cbe, False,
|
|
||||||
job_state)
|
|
||||||
background.cmd_runner(r)
|
|
||||||
|
|||||||
184
daemons/lvmdbusd/lvm_shell_proxy.py
Executable file
184
daemons/lvmdbusd/lvm_shell_proxy.py
Executable file
@@ -0,0 +1,184 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# This copyrighted material is made available to anyone wishing to use,
|
||||||
|
# modify, copy, or redistribute it subject to the terms and conditions
|
||||||
|
# of the GNU General Public License v.2.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Copyright 2015-2016, Vratislav Podzimek <vpodzime@redhat.com>
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import shlex
|
||||||
|
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||||
|
from os import O_NONBLOCK
|
||||||
|
import traceback
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
try:
|
||||||
|
from .cfg import LVM_CMD
|
||||||
|
from .utils import log_debug, log_error
|
||||||
|
except:
|
||||||
|
from cfg import LVM_CMD
|
||||||
|
from utils import log_debug, log_error
|
||||||
|
|
||||||
|
SHELL_PROMPT = "lvm> "
|
||||||
|
|
||||||
|
|
||||||
|
def _quote_arg(arg):
|
||||||
|
if len(shlex.split(arg)) > 1:
|
||||||
|
return '"%s"' % arg
|
||||||
|
else:
|
||||||
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
class LVMShellProxy(object):
|
||||||
|
def _read_until_prompt(self):
|
||||||
|
prev_ec = None
|
||||||
|
stdout = ""
|
||||||
|
while not stdout.endswith(SHELL_PROMPT):
|
||||||
|
try:
|
||||||
|
tmp = self.lvm_shell.stdout.read()
|
||||||
|
if tmp:
|
||||||
|
stdout += tmp.decode("utf-8")
|
||||||
|
except IOError:
|
||||||
|
# nothing written yet
|
||||||
|
pass
|
||||||
|
|
||||||
|
# strip the prompt from the STDOUT before returning and grab the exit
|
||||||
|
# code if it's available
|
||||||
|
m = self.re.match(stdout)
|
||||||
|
if m:
|
||||||
|
prev_ec = int(m.group(2))
|
||||||
|
strip_idx = -1 * len(m.group(1))
|
||||||
|
else:
|
||||||
|
strip_idx = -1 * len(SHELL_PROMPT)
|
||||||
|
|
||||||
|
return stdout[:strip_idx], prev_ec
|
||||||
|
|
||||||
|
def _read_line(self):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
tmp = self.lvm_shell.stdout.readline()
|
||||||
|
if tmp:
|
||||||
|
return tmp.decode("utf-8")
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _discard_echo(self, expected):
|
||||||
|
line = ""
|
||||||
|
while line != expected:
|
||||||
|
# GNU readline inserts some interesting characters at times...
|
||||||
|
line += self._read_line().replace(' \r', '')
|
||||||
|
|
||||||
|
def _write_cmd(self, cmd):
|
||||||
|
cmd_bytes = bytes(cmd, "utf-8")
|
||||||
|
num_written = self.lvm_shell.stdin.write(cmd_bytes)
|
||||||
|
assert (num_written == len(cmd_bytes))
|
||||||
|
self.lvm_shell.stdin.flush()
|
||||||
|
|
||||||
|
def _lvm_echos(self):
|
||||||
|
echo = False
|
||||||
|
cmd = "version\n"
|
||||||
|
self._write_cmd(cmd)
|
||||||
|
line = self._read_line()
|
||||||
|
|
||||||
|
if line == cmd:
|
||||||
|
echo = True
|
||||||
|
|
||||||
|
self._read_until_prompt()
|
||||||
|
|
||||||
|
return echo
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.re = re.compile(".*(\[(-?[0-9]+)\] lvm> $)", re.DOTALL)
|
||||||
|
|
||||||
|
# run the lvm shell
|
||||||
|
self.lvm_shell = subprocess.Popen(
|
||||||
|
[LVM_CMD], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE, close_fds=True)
|
||||||
|
flags = fcntl(self.lvm_shell.stdout, F_GETFL)
|
||||||
|
fcntl(self.lvm_shell.stdout, F_SETFL, flags | O_NONBLOCK)
|
||||||
|
flags = fcntl(self.lvm_shell.stderr, F_GETFL)
|
||||||
|
fcntl(self.lvm_shell.stderr, F_SETFL, flags | O_NONBLOCK)
|
||||||
|
|
||||||
|
# wait for the first prompt
|
||||||
|
self._read_until_prompt()
|
||||||
|
|
||||||
|
# Check to see if the version of LVM we are using is running with
|
||||||
|
# gnu readline which will echo our writes from stdin to stdout
|
||||||
|
self.echo = self._lvm_echos()
|
||||||
|
|
||||||
|
def call_lvm(self, argv, debug=False):
|
||||||
|
# create the command string
|
||||||
|
cmd = " ".join(_quote_arg(arg) for arg in argv)
|
||||||
|
cmd += "\n"
|
||||||
|
|
||||||
|
# run the command by writing it to the shell's STDIN
|
||||||
|
self._write_cmd(cmd)
|
||||||
|
|
||||||
|
# If lvm is utilizing gnu readline, it echos stdin to stdout
|
||||||
|
if self.echo:
|
||||||
|
self._discard_echo(cmd)
|
||||||
|
|
||||||
|
# read everything from the STDOUT to the next prompt
|
||||||
|
stdout, exit_code = self._read_until_prompt()
|
||||||
|
|
||||||
|
# read everything from STDERR if there's something (we waited for the
|
||||||
|
# prompt on STDOUT so there should be all or nothing at this point on
|
||||||
|
# STDERR)
|
||||||
|
stderr = None
|
||||||
|
try:
|
||||||
|
t_error = self.lvm_shell.stderr.read()
|
||||||
|
if t_error:
|
||||||
|
stderr = t_error.decode("utf-8")
|
||||||
|
except IOError:
|
||||||
|
# nothing on STDERR
|
||||||
|
pass
|
||||||
|
|
||||||
|
if exit_code is not None:
|
||||||
|
rc = exit_code
|
||||||
|
else:
|
||||||
|
# LVM does write to stderr even when it did complete successfully,
|
||||||
|
# so without having the exit code in the prompt we can never be
|
||||||
|
# sure.
|
||||||
|
if stderr:
|
||||||
|
rc = 1
|
||||||
|
else:
|
||||||
|
rc = 0
|
||||||
|
|
||||||
|
if debug or rc != 0:
|
||||||
|
log_error(('CMD: %s' % cmd))
|
||||||
|
log_error(("EC = %d" % rc))
|
||||||
|
log_error(("STDOUT=\n %s\n" % stdout))
|
||||||
|
log_error(("STDERR=\n %s\n" % stderr))
|
||||||
|
|
||||||
|
return (rc, stdout, stderr)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.lvm_shell.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
shell = LVMShellProxy()
|
||||||
|
in_line = "start"
|
||||||
|
try:
|
||||||
|
while in_line:
|
||||||
|
in_line = input("lvm> ")
|
||||||
|
if in_line:
|
||||||
|
ret, out, err, = shell.call_lvm(in_line.split())
|
||||||
|
print(("RET: %d" % ret))
|
||||||
|
print(("OUT:\n%s" % out))
|
||||||
|
print(("ERR:\n%s" % err))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc(file=sys.stdout)
|
||||||
|
finally:
|
||||||
|
print()
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
#!@PYTHON3@
|
|
||||||
|
|
||||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
|
||||||
#
|
|
||||||
# This copyrighted material is made available to anyone wishing to use,
|
|
||||||
# modify, copy, or redistribute it subject to the terms and conditions
|
|
||||||
# of the GNU General Public License v.2.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
# Copyright 2015-2016, Vratislav Podzimek <vpodzime@redhat.com>
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import shlex
|
|
||||||
from fcntl import fcntl, F_GETFL, F_SETFL
|
|
||||||
import os
|
|
||||||
import traceback
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
import select
|
|
||||||
import copy
|
|
||||||
|
|
||||||
try:
|
|
||||||
import simplejson as json
|
|
||||||
except ImportError:
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
from lvmdbusd.cfg import LVM_CMD
|
|
||||||
from lvmdbusd.utils import log_debug, log_error, add_no_notify
|
|
||||||
|
|
||||||
SHELL_PROMPT = "lvm> "
|
|
||||||
|
|
||||||
|
|
||||||
def _quote_arg(arg):
|
|
||||||
if len(shlex.split(arg)) > 1:
|
|
||||||
return '"%s"' % arg
|
|
||||||
else:
|
|
||||||
return arg
|
|
||||||
|
|
||||||
|
|
||||||
class LVMShellProxy(object):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _read(stream):
|
|
||||||
tmp = stream.read()
|
|
||||||
if tmp:
|
|
||||||
return tmp.decode("utf-8")
|
|
||||||
return ''
|
|
||||||
|
|
||||||
# Read until we get prompt back and a result
|
|
||||||
# @param: no_output Caller expects no output to report FD
|
|
||||||
# Returns stdout, report, stderr (report is JSON!)
|
|
||||||
def _read_until_prompt(self, no_output=False):
|
|
||||||
stdout = ""
|
|
||||||
report = ""
|
|
||||||
stderr = ""
|
|
||||||
keep_reading = True
|
|
||||||
extra_passes = 3
|
|
||||||
report_json = {}
|
|
||||||
prev_report_len = 0
|
|
||||||
|
|
||||||
# Try reading from all FDs to prevent one from filling up and causing
|
|
||||||
# a hang. Keep reading until we get the prompt back and the report
|
|
||||||
# FD does not contain valid JSON
|
|
||||||
while keep_reading:
|
|
||||||
try:
|
|
||||||
rd_fd = [
|
|
||||||
self.lvm_shell.stdout.fileno(),
|
|
||||||
self.report_stream.fileno(),
|
|
||||||
self.lvm_shell.stderr.fileno()]
|
|
||||||
ready = select.select(rd_fd, [], [], 2)
|
|
||||||
|
|
||||||
for r in ready[0]:
|
|
||||||
if r == self.lvm_shell.stdout.fileno():
|
|
||||||
stdout += LVMShellProxy._read(self.lvm_shell.stdout)
|
|
||||||
elif r == self.report_stream.fileno():
|
|
||||||
report += LVMShellProxy._read(self.report_stream)
|
|
||||||
elif r == self.lvm_shell.stderr.fileno():
|
|
||||||
stderr += LVMShellProxy._read(self.lvm_shell.stderr)
|
|
||||||
|
|
||||||
# Check to see if the lvm process died on us
|
|
||||||
if self.lvm_shell.poll():
|
|
||||||
raise Exception(self.lvm_shell.returncode, "%s" % stderr)
|
|
||||||
|
|
||||||
if stdout.endswith(SHELL_PROMPT):
|
|
||||||
if no_output:
|
|
||||||
keep_reading = False
|
|
||||||
else:
|
|
||||||
cur_report_len = len(report)
|
|
||||||
if cur_report_len != 0:
|
|
||||||
# Only bother to parse if we have more data
|
|
||||||
if prev_report_len != cur_report_len:
|
|
||||||
prev_report_len = cur_report_len
|
|
||||||
# Parse the JSON if it's good we are done,
|
|
||||||
# if not we will try to read some more.
|
|
||||||
try:
|
|
||||||
report_json = json.loads(report)
|
|
||||||
keep_reading = False
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if keep_reading:
|
|
||||||
extra_passes -= 1
|
|
||||||
if extra_passes <= 0:
|
|
||||||
if len(report):
|
|
||||||
raise ValueError("Invalid json: %s" %
|
|
||||||
report)
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
"lvm returned no JSON output!")
|
|
||||||
|
|
||||||
except IOError as ioe:
|
|
||||||
log_debug(str(ioe))
|
|
||||||
pass
|
|
||||||
|
|
||||||
return stdout, report_json, stderr
|
|
||||||
|
|
||||||
def _write_cmd(self, cmd):
|
|
||||||
cmd_bytes = bytes(cmd, "utf-8")
|
|
||||||
num_written = self.lvm_shell.stdin.write(cmd_bytes)
|
|
||||||
assert (num_written == len(cmd_bytes))
|
|
||||||
self.lvm_shell.stdin.flush()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _make_non_block(stream):
|
|
||||||
flags = fcntl(stream, F_GETFL)
|
|
||||||
fcntl(stream, F_SETFL, flags | os.O_NONBLOCK)
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
|
|
||||||
# Create a temp directory
|
|
||||||
tmp_dir = tempfile.mkdtemp(prefix="lvmdbus_")
|
|
||||||
tmp_file = "%s/lvmdbus_report" % (tmp_dir)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Lets create fifo for the report output
|
|
||||||
os.mkfifo(tmp_file, 0o600)
|
|
||||||
except FileExistsError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# We have to open non-blocking as the other side isn't open until
|
|
||||||
# we actually fork the process.
|
|
||||||
self.report_fd = os.open(tmp_file, os.O_NONBLOCK)
|
|
||||||
self.report_stream = os.fdopen(self.report_fd, 'rb', 0)
|
|
||||||
|
|
||||||
# Setup the environment for using our own socket for reporting
|
|
||||||
local_env = copy.deepcopy(os.environ)
|
|
||||||
local_env["LVM_REPORT_FD"] = "32"
|
|
||||||
local_env["LVM_COMMAND_PROFILE"] = "lvmdbusd"
|
|
||||||
|
|
||||||
# Disable the abort logic if lvm logs too much, which easily happens
|
|
||||||
# when utilizing the lvm shell.
|
|
||||||
local_env["LVM_LOG_FILE_MAX_LINES"] = "0"
|
|
||||||
|
|
||||||
# run the lvm shell
|
|
||||||
self.lvm_shell = subprocess.Popen(
|
|
||||||
[LVM_CMD + " 32>%s" % tmp_file],
|
|
||||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=local_env,
|
|
||||||
stderr=subprocess.PIPE, close_fds=True, shell=True)
|
|
||||||
|
|
||||||
try:
|
|
||||||
LVMShellProxy._make_non_block(self.lvm_shell.stdout)
|
|
||||||
LVMShellProxy._make_non_block(self.lvm_shell.stderr)
|
|
||||||
|
|
||||||
# wait for the first prompt
|
|
||||||
errors = self._read_until_prompt(no_output=True)[2]
|
|
||||||
if errors and len(errors):
|
|
||||||
raise RuntimeError(errors)
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
# These will get deleted when the FD count goes to zero so we
|
|
||||||
# can be sure to clean up correctly no matter how we finish
|
|
||||||
os.unlink(tmp_file)
|
|
||||||
os.rmdir(tmp_dir)
|
|
||||||
|
|
||||||
def get_error_msg(self):
|
|
||||||
# We got an error, lets go fetch the error message
|
|
||||||
self._write_cmd('lastlog\n')
|
|
||||||
|
|
||||||
# read everything from the STDOUT to the next prompt
|
|
||||||
stdout, report_json, stderr = self._read_until_prompt()
|
|
||||||
if 'log' in report_json:
|
|
||||||
error_msg = ""
|
|
||||||
# Walk the entire log array and build an error string
|
|
||||||
for log_entry in report_json['log']:
|
|
||||||
if log_entry['log_type'] == "error":
|
|
||||||
if error_msg:
|
|
||||||
error_msg += ', ' + log_entry['log_message']
|
|
||||||
else:
|
|
||||||
error_msg = log_entry['log_message']
|
|
||||||
|
|
||||||
return error_msg
|
|
||||||
|
|
||||||
return 'No error reason provided! (missing "log" section)'
|
|
||||||
|
|
||||||
def call_lvm(self, argv, debug=False):
|
|
||||||
rc = 1
|
|
||||||
error_msg = ""
|
|
||||||
|
|
||||||
if self.lvm_shell.poll():
|
|
||||||
raise Exception(
|
|
||||||
self.lvm_shell.returncode,
|
|
||||||
"Underlying lvm shell process is not present!")
|
|
||||||
|
|
||||||
argv = add_no_notify(argv)
|
|
||||||
|
|
||||||
# create the command string
|
|
||||||
cmd = " ".join(_quote_arg(arg) for arg in argv)
|
|
||||||
cmd += "\n"
|
|
||||||
|
|
||||||
# run the command by writing it to the shell's STDIN
|
|
||||||
self._write_cmd(cmd)
|
|
||||||
|
|
||||||
# read everything from the STDOUT to the next prompt
|
|
||||||
stdout, report_json, stderr = self._read_until_prompt()
|
|
||||||
|
|
||||||
# Parse the report to see what happened
|
|
||||||
if 'log' in report_json:
|
|
||||||
if report_json['log'][-1:][0]['log_ret_code'] == '1':
|
|
||||||
rc = 0
|
|
||||||
else:
|
|
||||||
error_msg = self.get_error_msg()
|
|
||||||
|
|
||||||
if debug or rc != 0:
|
|
||||||
log_error(('CMD: %s' % cmd))
|
|
||||||
log_error(("EC = %d" % rc))
|
|
||||||
log_error(("ERROR_MSG=\n %s\n" % error_msg))
|
|
||||||
|
|
||||||
return rc, report_json, error_msg
|
|
||||||
|
|
||||||
def exit_shell(self):
|
|
||||||
try:
|
|
||||||
self._write_cmd('exit\n')
|
|
||||||
except Exception as e:
|
|
||||||
log_error(str(e))
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
try:
|
|
||||||
self.lvm_shell.terminate()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
shell = LVMShellProxy()
|
|
||||||
in_line = "start"
|
|
||||||
try:
|
|
||||||
while in_line:
|
|
||||||
in_line = input("lvm> ")
|
|
||||||
if in_line:
|
|
||||||
start = time.time()
|
|
||||||
ret, out, err = shell.call_lvm(in_line.split())
|
|
||||||
end = time.time()
|
|
||||||
|
|
||||||
print(("RC: %d" % ret))
|
|
||||||
print(("OUT:\n%s" % out))
|
|
||||||
print(("ERR:\n%s" % err))
|
|
||||||
|
|
||||||
print("Command = %f seconds" % (end - start))
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
except EOFError:
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
traceback.print_exc(file=sys.stdout)
|
|
||||||
210
daemons/lvmdbusd/lvmdb.py.in → daemons/lvmdbusd/lvmdb.py
Normal file → Executable file
210
daemons/lvmdbusd/lvmdb.py.in → daemons/lvmdbusd/lvmdb.py
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
#!@PYTHON3@
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||||
#
|
#
|
||||||
@@ -12,15 +12,17 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
import pprint as prettyprint
|
import pprint as prettyprint
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from lvmdbusd import cmdhandler
|
try:
|
||||||
from lvmdbusd.utils import log_debug, log_error
|
from . import cmdhandler
|
||||||
|
from .utils import log_debug
|
||||||
|
except SystemError:
|
||||||
|
import cmdhandler
|
||||||
|
from utils import log_debug
|
||||||
|
|
||||||
|
|
||||||
class DataStore(object):
|
class DataStore(object):
|
||||||
def __init__(self, usejson=True):
|
def __init__(self):
|
||||||
self.pvs = {}
|
self.pvs = {}
|
||||||
self.vgs = {}
|
self.vgs = {}
|
||||||
self.lvs = {}
|
self.lvs = {}
|
||||||
@@ -38,11 +40,6 @@ class DataStore(object):
|
|||||||
# self.refresh()
|
# self.refresh()
|
||||||
self.num_refreshes = 0
|
self.num_refreshes = 0
|
||||||
|
|
||||||
if usejson:
|
|
||||||
self.json = cmdhandler.supports_json()
|
|
||||||
else:
|
|
||||||
self.json = usejson
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _insert_record(table, key, record, allowed_multiple):
|
def _insert_record(table, key, record, allowed_multiple):
|
||||||
if key in table:
|
if key in table:
|
||||||
@@ -69,7 +66,18 @@ class DataStore(object):
|
|||||||
table[key] = record
|
table[key] = record
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup):
|
def _parse_pvs(_pvs):
|
||||||
|
pvs = sorted(_pvs, key=lambda pk: pk['pv_name'])
|
||||||
|
|
||||||
|
c_pvs = OrderedDict()
|
||||||
|
c_lookup = {}
|
||||||
|
c_pvs_in_vgs = {}
|
||||||
|
|
||||||
|
for p in pvs:
|
||||||
|
DataStore._insert_record(
|
||||||
|
c_pvs, p['pv_uuid'], p,
|
||||||
|
['pv_seg_start', 'pvseg_size', 'segtype'])
|
||||||
|
|
||||||
for p in c_pvs.values():
|
for p in c_pvs.values():
|
||||||
# Capture which PVs are associated with which VG
|
# Capture which PVs are associated with which VG
|
||||||
if p['vg_uuid'] not in c_pvs_in_vgs:
|
if p['vg_uuid'] not in c_pvs_in_vgs:
|
||||||
@@ -82,61 +90,6 @@ class DataStore(object):
|
|||||||
# Lookup for translating between /dev/<name> and pv uuid
|
# Lookup for translating between /dev/<name> and pv uuid
|
||||||
c_lookup[p['pv_name']] = p['pv_uuid']
|
c_lookup[p['pv_name']] = p['pv_uuid']
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse_pvs(_pvs):
|
|
||||||
pvs = sorted(_pvs, key=lambda pk: pk['pv_name'])
|
|
||||||
|
|
||||||
c_pvs = OrderedDict()
|
|
||||||
c_lookup = {}
|
|
||||||
c_pvs_in_vgs = {}
|
|
||||||
|
|
||||||
for p in pvs:
|
|
||||||
DataStore._insert_record(
|
|
||||||
c_pvs, p['pv_uuid'], p,
|
|
||||||
['pvseg_start', 'pvseg_size', 'segtype'])
|
|
||||||
|
|
||||||
DataStore._pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup)
|
|
||||||
return c_pvs, c_lookup, c_pvs_in_vgs
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse_pvs_json(_all):
|
|
||||||
|
|
||||||
c_pvs = OrderedDict()
|
|
||||||
c_lookup = {}
|
|
||||||
c_pvs_in_vgs = {}
|
|
||||||
|
|
||||||
# Each item item in the report is a collection of information pertaining
|
|
||||||
# to the vg
|
|
||||||
for r in _all['report']:
|
|
||||||
tmp_pv = []
|
|
||||||
|
|
||||||
# Get the pv data for this VG.
|
|
||||||
if 'pv' in r:
|
|
||||||
tmp_pv.extend(r['pv'])
|
|
||||||
|
|
||||||
# Sort them
|
|
||||||
sorted_tmp_pv = sorted(tmp_pv, key=lambda pk: pk['pv_name'])
|
|
||||||
|
|
||||||
# Add them to result set
|
|
||||||
for p in sorted_tmp_pv:
|
|
||||||
c_pvs[p['pv_uuid']] = p
|
|
||||||
|
|
||||||
if 'pvseg' in r:
|
|
||||||
for s in r['pvseg']:
|
|
||||||
r = c_pvs[s['pv_uuid']]
|
|
||||||
r.setdefault('pvseg_start', []).append(s['pvseg_start'])
|
|
||||||
r.setdefault('pvseg_size', []).append(s['pvseg_size'])
|
|
||||||
r.setdefault('segtype', []).append(s['segtype'])
|
|
||||||
|
|
||||||
# TODO: Remove this bug work around when we have orphan segs.
|
|
||||||
for i in c_pvs.values():
|
|
||||||
if 'pvseg_start' not in i:
|
|
||||||
i['pvseg_start'] = '0'
|
|
||||||
i['pvseg_size'] = i['pv_pe_count']
|
|
||||||
i['segtype'] = 'free'
|
|
||||||
|
|
||||||
DataStore._pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup)
|
|
||||||
|
|
||||||
return c_pvs, c_lookup, c_pvs_in_vgs
|
return c_pvs, c_lookup, c_pvs_in_vgs
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -153,31 +106,20 @@ class DataStore(object):
|
|||||||
return c_vgs, c_lookup
|
return c_vgs, c_lookup
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_vgs_json(_all):
|
def _parse_lvs(_lvs):
|
||||||
|
lvs = sorted(_lvs, key=lambda vk: vk['lv_name'])
|
||||||
|
|
||||||
tmp_vg = []
|
c_lvs = OrderedDict()
|
||||||
for r in _all['report']:
|
c_lvs_in_vgs = {}
|
||||||
# Get the pv data for this VG.
|
c_lvs_hidden = {}
|
||||||
if 'vg' in r:
|
c_lv_full_lookup = {}
|
||||||
tmp_vg.extend(r['vg'])
|
|
||||||
|
|
||||||
# Sort for consistent output, however this is optional
|
for i in lvs:
|
||||||
vgs = sorted(tmp_vg, key=lambda vk: vk['vg_name'])
|
full_name = "%s/%s" % (i['vg_name'], i['lv_name'])
|
||||||
|
c_lv_full_lookup[full_name] = i['lv_uuid']
|
||||||
c_vgs = OrderedDict()
|
DataStore._insert_record(
|
||||||
c_lookup = {}
|
c_lvs, i['lv_uuid'], i,
|
||||||
|
['seg_pe_ranges', 'segtype'])
|
||||||
for i in vgs:
|
|
||||||
c_lookup[i['vg_name']] = i['vg_uuid']
|
|
||||||
c_vgs[i['vg_uuid']] = i
|
|
||||||
|
|
||||||
return c_vgs, c_lookup
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse_lvs_common(c_lvs, c_lv_full_lookup):
|
|
||||||
|
|
||||||
c_lvs_in_vgs = OrderedDict()
|
|
||||||
c_lvs_hidden = OrderedDict()
|
|
||||||
|
|
||||||
for i in c_lvs.values():
|
for i in c_lvs.values():
|
||||||
if i['vg_uuid'] not in c_lvs_in_vgs:
|
if i['vg_uuid'] not in c_lvs_in_vgs:
|
||||||
@@ -207,48 +149,6 @@ class DataStore(object):
|
|||||||
|
|
||||||
return c_lvs, c_lvs_in_vgs, c_lvs_hidden, c_lv_full_lookup
|
return c_lvs, c_lvs_in_vgs, c_lvs_hidden, c_lv_full_lookup
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse_lvs(_lvs):
|
|
||||||
lvs = sorted(_lvs, key=lambda vk: vk['lv_name'])
|
|
||||||
|
|
||||||
c_lvs = OrderedDict()
|
|
||||||
c_lv_full_lookup = OrderedDict()
|
|
||||||
|
|
||||||
for i in lvs:
|
|
||||||
full_name = "%s/%s" % (i['vg_name'], i['lv_name'])
|
|
||||||
c_lv_full_lookup[full_name] = i['lv_uuid']
|
|
||||||
DataStore._insert_record(
|
|
||||||
c_lvs, i['lv_uuid'], i,
|
|
||||||
['seg_pe_ranges', 'segtype'])
|
|
||||||
|
|
||||||
return DataStore._parse_lvs_common(c_lvs, c_lv_full_lookup)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse_lvs_json(_all):
|
|
||||||
|
|
||||||
c_lvs = OrderedDict()
|
|
||||||
c_lv_full_lookup = {}
|
|
||||||
|
|
||||||
# Each item item in the report is a collection of information pertaining
|
|
||||||
# to the vg
|
|
||||||
for r in _all['report']:
|
|
||||||
# Get the lv data for this VG.
|
|
||||||
if 'lv' in r:
|
|
||||||
# Add them to result set
|
|
||||||
for i in r['lv']:
|
|
||||||
full_name = "%s/%s" % (i['vg_name'], i['lv_name'])
|
|
||||||
c_lv_full_lookup[full_name] = i['lv_uuid']
|
|
||||||
c_lvs[i['lv_uuid']] = i
|
|
||||||
|
|
||||||
# Add in the segment data
|
|
||||||
if 'seg' in r:
|
|
||||||
for s in r['seg']:
|
|
||||||
r = c_lvs[s['lv_uuid']]
|
|
||||||
r.setdefault('seg_pe_ranges', []).append(s['seg_pe_ranges'])
|
|
||||||
r.setdefault('segtype', []).append(s['segtype'])
|
|
||||||
|
|
||||||
return DataStore._parse_lvs_common(c_lvs, c_lv_full_lookup)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _make_list(l):
|
def _make_list(l):
|
||||||
if not isinstance(l, list):
|
if not isinstance(l, list):
|
||||||
@@ -372,20 +272,12 @@ class DataStore(object):
|
|||||||
:param log Add debug log entry/exit messages
|
:param log Add debug log entry/exit messages
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self.num_refreshes += 1
|
|
||||||
if log:
|
if log:
|
||||||
log_debug("lvmdb - refresh entry")
|
log_debug("lvmdb - refresh entry")
|
||||||
|
self.num_refreshes += 1
|
||||||
|
|
||||||
# Grab everything first then parse it
|
# Grab everything first then parse it
|
||||||
if self.json:
|
|
||||||
# Do a single lvm retrieve for everything in json
|
|
||||||
a = cmdhandler.lvm_full_report_json()
|
|
||||||
|
|
||||||
_pvs, _pvs_lookup, _pvs_in_vgs = self._parse_pvs_json(a)
|
|
||||||
_vgs, _vgs_lookup = self._parse_vgs_json(a)
|
|
||||||
_lvs, _lvs_in_vgs, _lvs_hidden, _lvs_lookup = self._parse_lvs_json(a)
|
|
||||||
|
|
||||||
else:
|
|
||||||
_raw_pvs = cmdhandler.pv_retrieve_with_segs()
|
_raw_pvs = cmdhandler.pv_retrieve_with_segs()
|
||||||
_raw_vgs = cmdhandler.vg_retrieve(None)
|
_raw_vgs = cmdhandler.vg_retrieve(None)
|
||||||
_raw_lvs = cmdhandler.lv_retrieve_with_segments()
|
_raw_lvs = cmdhandler.lv_retrieve_with_segments()
|
||||||
@@ -418,20 +310,9 @@ class DataStore(object):
|
|||||||
else:
|
else:
|
||||||
rc = []
|
rc = []
|
||||||
for s in pv_name:
|
for s in pv_name:
|
||||||
# Ths user could be using a symlink instead of the actual
|
|
||||||
# block device, make sure we are using actual block device file
|
|
||||||
# if the pv name isn't in the lookup
|
|
||||||
if s not in self.pv_path_to_uuid:
|
|
||||||
s = os.path.realpath(s)
|
|
||||||
rc.append(self.pvs[self.pv_path_to_uuid[s]])
|
rc.append(self.pvs[self.pv_path_to_uuid[s]])
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
def pv_missing(self, pv_uuid):
|
|
||||||
if pv_uuid in self.pvs:
|
|
||||||
if self.pvs[pv_uuid]['pv_missing'] == '':
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def fetch_vgs(self, vg_name):
|
def fetch_vgs(self, vg_name):
|
||||||
if not vg_name:
|
if not vg_name:
|
||||||
return self.vgs.values()
|
return self.vgs.values()
|
||||||
@@ -451,18 +332,18 @@ class DataStore(object):
|
|||||||
rc.append(self.lvs[self.lv_full_name_to_uuid[s]])
|
rc.append(self.lvs[self.lv_full_name_to_uuid[s]])
|
||||||
return rc
|
return rc
|
||||||
except KeyError as ke:
|
except KeyError as ke:
|
||||||
log_error("Key %s not found!" % (str(lv_names)))
|
print("Key %s not found!" % (str(lv_names)))
|
||||||
log_error("lv name to uuid lookup")
|
print("lv name to uuid lookup")
|
||||||
for keys in sorted(self.lv_full_name_to_uuid.keys()):
|
for keys in sorted(self.lv_full_name_to_uuid.keys()):
|
||||||
log_error("%s" % (keys))
|
print("%s" % (keys))
|
||||||
log_error("lvs entries by uuid")
|
print("lvs entries by uuid")
|
||||||
for keys in sorted(self.lvs.keys()):
|
for keys in sorted(self.lvs.keys()):
|
||||||
log_error("%s" % (keys))
|
print("%s" % (keys))
|
||||||
raise ke
|
raise ke
|
||||||
|
|
||||||
def pv_pe_segments(self, pv_uuid):
|
def pv_pe_segments(self, pv_uuid):
|
||||||
pv = self.pvs[pv_uuid]
|
pv = self.pvs[pv_uuid]
|
||||||
return list(zip(pv['pvseg_start'], pv['pvseg_size']))
|
return list(zip(pv['pv_seg_start'], pv['pvseg_size']))
|
||||||
|
|
||||||
def pv_contained_lv(self, pv_device):
|
def pv_contained_lv(self, pv_device):
|
||||||
rc = []
|
rc = []
|
||||||
@@ -503,21 +384,12 @@ class DataStore(object):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pp = prettyprint.PrettyPrinter(indent=4)
|
pp = prettyprint.PrettyPrinter(indent=4)
|
||||||
|
|
||||||
use_json = False
|
ds = DataStore()
|
||||||
|
|
||||||
if len(sys.argv) != 1:
|
|
||||||
print(len(sys.argv))
|
|
||||||
use_json = True
|
|
||||||
|
|
||||||
ds = DataStore(use_json)
|
|
||||||
ds.refresh()
|
ds.refresh()
|
||||||
|
|
||||||
print("PVS")
|
|
||||||
for v in ds.pvs.values():
|
for v in ds.pvs.values():
|
||||||
pp.pprint(v)
|
pp.pprint(v)
|
||||||
print('PV missing is %s' % ds.pv_missing(v['pv_uuid']))
|
|
||||||
|
|
||||||
print("VGS")
|
|
||||||
for v in ds.vgs.values():
|
for v in ds.vgs.values():
|
||||||
pp.pprint(v)
|
pp.pprint(v)
|
||||||
|
|
||||||
6
daemons/lvmdbusd/lvmdbusd.in → daemons/lvmdbusd/lvmdbusd
Normal file → Executable file
6
daemons/lvmdbusd/lvmdbusd.in → daemons/lvmdbusd/lvmdbusd
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
#!@PYTHON3@
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||||
#
|
#
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from lvmdbusd import main
|
import lvmdbusd
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main())
|
sys.exit(lvmdbusd.main())
|
||||||
@@ -10,27 +10,24 @@
|
|||||||
from . import cfg
|
from . import cfg
|
||||||
from . import objectmanager
|
from . import objectmanager
|
||||||
from . import utils
|
from . import utils
|
||||||
from .cfg import BUS_NAME, BASE_INTERFACE, BASE_OBJ_PATH, MANAGER_OBJ_PATH
|
from .cfg import BASE_INTERFACE, BASE_OBJ_PATH, MANAGER_OBJ_PATH
|
||||||
import threading
|
import threading
|
||||||
from . import cmdhandler
|
from . import cmdhandler
|
||||||
import time
|
import time
|
||||||
import signal
|
import signal
|
||||||
import dbus
|
import dbus
|
||||||
import dbus.mainloop.glib
|
|
||||||
from . import lvmdb
|
from . import lvmdb
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from gi.repository import GLib
|
from gi.repository import GObject
|
||||||
from .fetch import StateUpdate
|
from .fetch import load
|
||||||
from .manager import Manager
|
from .manager import Manager
|
||||||
|
from .background import background_reaper
|
||||||
import traceback
|
import traceback
|
||||||
import queue
|
import queue
|
||||||
from . import udevwatch
|
|
||||||
from .utils import log_debug, log_error
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
from .cmdhandler import LvmFlightRecorder
|
from . import udevwatch
|
||||||
from .request import RequestEntry
|
from .utils import log_debug
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
class Lvm(objectmanager.ObjectManager):
|
class Lvm(objectmanager.ObjectManager):
|
||||||
@@ -40,157 +37,104 @@ class Lvm(objectmanager.ObjectManager):
|
|||||||
|
|
||||||
def process_request():
|
def process_request():
|
||||||
while cfg.run.value != 0:
|
while cfg.run.value != 0:
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
req = cfg.worker_q.get(True, 5)
|
req = cfg.worker_q.get(True, 5)
|
||||||
|
|
||||||
|
start = cfg.db.num_refreshes
|
||||||
|
|
||||||
log_debug(
|
log_debug(
|
||||||
"Running method: %s with args %s" %
|
"Running method: %s with args %s" %
|
||||||
(str(req.method), str(req.arguments)))
|
(str(req.method), str(req.arguments)))
|
||||||
req.run_cmd()
|
req.run_cmd()
|
||||||
log_debug("Method complete ")
|
|
||||||
|
end = cfg.db.num_refreshes
|
||||||
|
|
||||||
|
if end - start > 1:
|
||||||
|
log_debug(
|
||||||
|
"Inspect method %s for too many refreshes" %
|
||||||
|
(str(req.method)))
|
||||||
|
log_debug("Complete ")
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
st = traceback.format_exc()
|
traceback.print_exc(file=sys.stdout)
|
||||||
utils.log_error("process_request exception: \n%s" % st)
|
pass
|
||||||
|
|
||||||
|
|
||||||
def check_bb_size(value):
|
|
||||||
v = int(value)
|
|
||||||
if v < 0:
|
|
||||||
raise argparse.ArgumentTypeError(
|
|
||||||
"positive integers only ('%s' invalid)" % value)
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
def install_signal_handlers():
|
|
||||||
# Because of the glib main loop stuff the python signal handler code is
|
|
||||||
# apparently not usable and we need to use the glib calls instead
|
|
||||||
signal_add = None
|
|
||||||
|
|
||||||
if hasattr(GLib, 'unix_signal_add'):
|
|
||||||
signal_add = GLib.unix_signal_add
|
|
||||||
elif hasattr(GLib, 'unix_signal_add_full'):
|
|
||||||
signal_add = GLib.unix_signal_add_full
|
|
||||||
|
|
||||||
if signal_add:
|
|
||||||
signal_add(GLib.PRIORITY_HIGH, signal.SIGHUP, utils.handler, signal.SIGHUP)
|
|
||||||
signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, utils.handler, signal.SIGINT)
|
|
||||||
signal_add(GLib.PRIORITY_HIGH, signal.SIGUSR1, utils.handler, signal.SIGUSR1)
|
|
||||||
else:
|
|
||||||
log_error("GLib.unix_signal_[add|add_full] are NOT available!")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
start = time.time()
|
|
||||||
# Add simple command line handling
|
# Add simple command line handling
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument("--udev", action='store_true',
|
||||||
"--udev", action='store_true',
|
help="Use udev for updating state", default=False,
|
||||||
help="Use udev for updating state",
|
|
||||||
default=False,
|
|
||||||
dest='use_udev')
|
dest='use_udev')
|
||||||
parser.add_argument(
|
parser.add_argument("--debug", action='store_true',
|
||||||
"--debug", action='store_true',
|
|
||||||
help="Dump debug messages", default=False,
|
help="Dump debug messages", default=False,
|
||||||
dest='debug')
|
dest='debug')
|
||||||
parser.add_argument(
|
|
||||||
"--nojson", action='store_false',
|
|
||||||
help="Do not use LVM JSON output (disables lvmshell)", default=True,
|
|
||||||
dest='use_json')
|
|
||||||
parser.add_argument(
|
|
||||||
"--lvmshell", action='store_true',
|
|
||||||
help="Use the lvm shell, not fork & exec lvm",
|
|
||||||
default=False,
|
|
||||||
dest='use_lvm_shell')
|
|
||||||
parser.add_argument(
|
|
||||||
"--blackboxsize",
|
|
||||||
help="Size of the black box flight recorder, 0 to disable",
|
|
||||||
default=10,
|
|
||||||
type=check_bb_size,
|
|
||||||
dest='bb_size')
|
|
||||||
|
|
||||||
use_session = os.getenv('LVMDBUSD_USE_SESSION', False)
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Ensure that we get consistent output for parsing stdout/stderr
|
cfg.DEBUG = args.debug
|
||||||
os.environ["LC_ALL"] = "C"
|
|
||||||
|
|
||||||
cfg.args = parser.parse_args()
|
|
||||||
cfg.create_request_entry = RequestEntry
|
|
||||||
|
|
||||||
# We create a flight recorder in cmdhandler too, but we replace it here
|
|
||||||
# as the user may be specifying a different size. The default one in
|
|
||||||
# cmdhandler is for when we are running other code with a different main.
|
|
||||||
cfg.blackbox = LvmFlightRecorder(cfg.args.bb_size)
|
|
||||||
|
|
||||||
if cfg.args.use_lvm_shell and not cfg.args.use_json:
|
|
||||||
log_error("You cannot specify --lvmshell and --nojson")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# List of threads that we start up
|
# List of threads that we start up
|
||||||
thread_list = []
|
thread_list = []
|
||||||
|
|
||||||
install_signal_handlers()
|
start = time.time()
|
||||||
|
|
||||||
|
# Install signal handlers
|
||||||
|
for s in [signal.SIGHUP, signal.SIGINT]:
|
||||||
|
try:
|
||||||
|
signal.signal(s, utils.handler)
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||||
|
GObject.threads_init()
|
||||||
dbus.mainloop.glib.threads_init()
|
dbus.mainloop.glib.threads_init()
|
||||||
|
|
||||||
cmdhandler.set_execution(cfg.args.use_lvm_shell)
|
|
||||||
|
|
||||||
if use_session:
|
|
||||||
cfg.bus = dbus.SessionBus()
|
|
||||||
else:
|
|
||||||
cfg.bus = dbus.SystemBus()
|
cfg.bus = dbus.SystemBus()
|
||||||
# The base name variable needs to exist for things to work.
|
# The base name variable needs to exist for things to work.
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
base_name = dbus.service.BusName(BUS_NAME, cfg.bus)
|
base_name = dbus.service.BusName(BASE_INTERFACE, cfg.bus)
|
||||||
cfg.om = Lvm(BASE_OBJ_PATH)
|
cfg.om = Lvm(BASE_OBJ_PATH)
|
||||||
cfg.om.register_object(Manager(MANAGER_OBJ_PATH))
|
cfg.om.register_object(Manager(MANAGER_OBJ_PATH))
|
||||||
|
|
||||||
cfg.db = lvmdb.DataStore(cfg.args.use_json)
|
cfg.load = load
|
||||||
|
|
||||||
# Using a thread to process requests, we cannot hang the dbus library
|
cfg.db = lvmdb.DataStore()
|
||||||
# thread that is handling the dbus interface
|
|
||||||
thread_list.append(threading.Thread(target=process_request,
|
|
||||||
name='process_request'))
|
|
||||||
|
|
||||||
# Have a single thread handling updating lvm and the dbus model so we
|
# Start up thread to monitor pv moves
|
||||||
# don't have multiple threads doing this as the same time
|
thread_list.append(
|
||||||
updater = StateUpdate()
|
threading.Thread(target=background_reaper, name="pv_move_reaper"))
|
||||||
thread_list.append(updater.thread)
|
|
||||||
|
|
||||||
cfg.load = updater.load
|
# Using a thread to process requests.
|
||||||
|
thread_list.append(threading.Thread(target=process_request))
|
||||||
|
|
||||||
cfg.loop = GLib.MainLoop()
|
cfg.load(refresh=False, emit_signal=False)
|
||||||
|
cfg.loop = GObject.MainLoop()
|
||||||
|
|
||||||
for thread in thread_list:
|
for process in thread_list:
|
||||||
thread.damon = True
|
process.damon = True
|
||||||
thread.start()
|
process.start()
|
||||||
|
|
||||||
# Add udev watching
|
|
||||||
if cfg.args.use_udev:
|
|
||||||
log_debug('Utilizing udev to trigger updates')
|
|
||||||
|
|
||||||
# In all cases we are going to monitor for udev until we get an
|
|
||||||
# ExternalEvent. In the case where we get an external event and the user
|
|
||||||
# didn't specify --udev we will stop monitoring udev
|
|
||||||
udevwatch.add()
|
|
||||||
|
|
||||||
end = time.time()
|
end = time.time()
|
||||||
log_debug(
|
log_debug(
|
||||||
'Service ready! total time= %.4f, lvm time= %.4f count= %d' %
|
'Service ready! total time= %.2f, lvm time= %.2f count= %d' %
|
||||||
(end - start, cmdhandler.total_time, cmdhandler.total_count),
|
(end - start, cmdhandler.total_time, cmdhandler.total_count),
|
||||||
'bg_black', 'fg_light_green')
|
'bg_black', 'fg_light_green')
|
||||||
|
|
||||||
|
# Add udev watching
|
||||||
|
if args.use_udev:
|
||||||
|
log_debug('Utilizing udev to trigger updates')
|
||||||
|
udevwatch.add()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if cfg.run.value != 0:
|
if cfg.run.value != 0:
|
||||||
cfg.loop.run()
|
cfg.loop.run()
|
||||||
|
|
||||||
|
if args.use_udev:
|
||||||
udevwatch.remove()
|
udevwatch.remove()
|
||||||
|
|
||||||
for thread in thread_list:
|
for process in thread_list:
|
||||||
thread.join()
|
process.join()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
# If we are unable to register signal handler, we will end up here when
|
utils.handler(signal.SIGINT, None)
|
||||||
# the service gets a ^C or a kill -2 <parent pid>
|
|
||||||
utils.handler(signal.SIGINT)
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from .automatedproperties import AutomatedProperties
|
from .automatedproperties import AutomatedProperties
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
@@ -13,13 +14,14 @@ from .cfg import MANAGER_INTERFACE
|
|||||||
import dbus
|
import dbus
|
||||||
from . import cfg
|
from . import cfg
|
||||||
from . import cmdhandler
|
from . import cmdhandler
|
||||||
|
from .fetch import load_pvs, load_vgs
|
||||||
from .request import RequestEntry
|
from .request import RequestEntry
|
||||||
from . import udevwatch
|
from .refresh import event_add
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
class Manager(AutomatedProperties):
|
class Manager(AutomatedProperties):
|
||||||
_Version_meta = ("s", MANAGER_INTERFACE)
|
_Version_meta = ("t", MANAGER_INTERFACE)
|
||||||
|
|
||||||
def __init__(self, object_path):
|
def __init__(self, object_path):
|
||||||
super(Manager, self).__init__(object_path)
|
super(Manager, self).__init__(object_path)
|
||||||
@@ -27,31 +29,31 @@ class Manager(AutomatedProperties):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def Version(self):
|
def Version(self):
|
||||||
return dbus.String('1.0.0')
|
return '1.0.0'
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def handle_execute(rc, out, err):
|
|
||||||
if rc == 0:
|
|
||||||
cfg.load()
|
|
||||||
else:
|
|
||||||
# Need to work on error handling, need consistent
|
|
||||||
raise dbus.exceptions.DBusException(
|
|
||||||
MANAGER_INTERFACE,
|
|
||||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _pv_create(device, create_options):
|
def _pv_create(device, create_options):
|
||||||
|
|
||||||
# Check to see if we are already trying to create a PV for an existing
|
# Check to see if we are already trying to create a PV for an existing
|
||||||
# PV
|
# PV
|
||||||
pv = cfg.om.get_object_path_by_uuid_lvm_id(device, device)
|
pv = cfg.om.get_object_path_by_lvm_id(
|
||||||
|
device, device, None, False)
|
||||||
if pv:
|
if pv:
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
MANAGER_INTERFACE, "PV %s Already exists!" % device)
|
MANAGER_INTERFACE, "PV Already exists!")
|
||||||
|
|
||||||
|
created_pv = []
|
||||||
rc, out, err = cmdhandler.pv_create(create_options, [device])
|
rc, out, err = cmdhandler.pv_create(create_options, [device])
|
||||||
Manager.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
return cfg.om.get_object_path_by_lvm_id(device)
|
pvs = load_pvs([device], emit_signal=True)[0]
|
||||||
|
for p in pvs:
|
||||||
|
created_pv = p.dbus_object_path()
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
MANAGER_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
|
||||||
|
return created_pv
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=MANAGER_INTERFACE,
|
dbus_interface=MANAGER_INTERFACE,
|
||||||
@@ -78,8 +80,20 @@ class Manager(AutomatedProperties):
|
|||||||
MANAGER_INTERFACE, 'object path = %s not found' % p)
|
MANAGER_INTERFACE, 'object path = %s not found' % p)
|
||||||
|
|
||||||
rc, out, err = cmdhandler.vg_create(create_options, pv_devices, name)
|
rc, out, err = cmdhandler.vg_create(create_options, pv_devices, name)
|
||||||
Manager.handle_execute(rc, out, err)
|
created_vg = "/"
|
||||||
return cfg.om.get_object_path_by_lvm_id(name)
|
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
MANAGER_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
return created_vg
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=MANAGER_INTERFACE,
|
dbus_interface=MANAGER_INTERFACE,
|
||||||
@@ -100,10 +114,6 @@ class Manager(AutomatedProperties):
|
|||||||
|
|
||||||
# This is a diagnostic and should not be run in normal operation, so
|
# This is a diagnostic and should not be run in normal operation, so
|
||||||
# lets remove the log entries for refresh as it's implied.
|
# lets remove the log entries for refresh as it's implied.
|
||||||
|
|
||||||
# Run an internal diagnostic on the object manager look up tables
|
|
||||||
lc = cfg.om.validate_lookups()
|
|
||||||
|
|
||||||
rc = cfg.load(log=False)
|
rc = cfg.load(log=False)
|
||||||
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
@@ -111,7 +121,7 @@ class Manager(AutomatedProperties):
|
|||||||
'bg_black', 'fg_light_red')
|
'bg_black', 'fg_light_red')
|
||||||
else:
|
else:
|
||||||
utils.log_debug('Manager.Refresh - exit %d' % (rc))
|
utils.log_debug('Manager.Refresh - exit %d' % (rc))
|
||||||
return rc + lc
|
return rc
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=MANAGER_INTERFACE,
|
dbus_interface=MANAGER_INTERFACE,
|
||||||
@@ -131,28 +141,11 @@ class Manager(AutomatedProperties):
|
|||||||
r = RequestEntry(-1, Manager._refresh, (), cb, cbe, False)
|
r = RequestEntry(-1, Manager._refresh, (), cb, cbe, False)
|
||||||
cfg.worker_q.put(r)
|
cfg.worker_q.put(r)
|
||||||
|
|
||||||
@dbus.service.method(
|
|
||||||
dbus_interface=MANAGER_INTERFACE)
|
|
||||||
def FlightRecorderDump(self):
|
|
||||||
"""
|
|
||||||
Dump the flight recorder to syslog
|
|
||||||
"""
|
|
||||||
cfg.blackbox.dump()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _lookup_by_lvm_id(key):
|
|
||||||
p = cfg.om.get_object_path_by_uuid_lvm_id(key, key)
|
|
||||||
if not p:
|
|
||||||
p = '/'
|
|
||||||
utils.log_debug('LookUpByLvmId: key = %s, result = %s' % (key, p))
|
|
||||||
return p
|
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=MANAGER_INTERFACE,
|
dbus_interface=MANAGER_INTERFACE,
|
||||||
in_signature='s',
|
in_signature='s',
|
||||||
out_signature='o',
|
out_signature='o')
|
||||||
async_callbacks=('cb', 'cbe'))
|
def LookUpByLvmId(self, key):
|
||||||
def LookUpByLvmId(self, key, cb, cbe):
|
|
||||||
"""
|
"""
|
||||||
Given a lvm id in one of the forms:
|
Given a lvm id in one of the forms:
|
||||||
|
|
||||||
@@ -166,50 +159,29 @@ class Manager(AutomatedProperties):
|
|||||||
:param key: The lookup value
|
:param key: The lookup value
|
||||||
:return: Return the object path. If object not found you will get '/'
|
:return: Return the object path. If object not found you will get '/'
|
||||||
"""
|
"""
|
||||||
r = RequestEntry(-1, Manager._lookup_by_lvm_id, (key,), cb, cbe, False)
|
p = cfg.om.get_object_path_by_lvm_id(
|
||||||
cfg.worker_q.put(r)
|
key, key, gen_new=False)
|
||||||
|
if p:
|
||||||
@staticmethod
|
return p
|
||||||
def _use_lvm_shell(yes_no):
|
return '/'
|
||||||
return dbus.Boolean(cmdhandler.set_execution(yes_no))
|
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=MANAGER_INTERFACE,
|
dbus_interface=MANAGER_INTERFACE,
|
||||||
in_signature='b', out_signature='b',
|
in_signature='b')
|
||||||
async_callbacks=('cb', 'cbe'))
|
def UseLvmShell(self, yes_no):
|
||||||
def UseLvmShell(self, yes_no, cb, cbe):
|
|
||||||
"""
|
"""
|
||||||
Allow the client to enable/disable lvm shell, used for testing
|
Allow the client to enable/disable lvm shell, used for testing
|
||||||
:param yes_no:
|
:param yes_no:
|
||||||
:param cb: dbus python call back parameter, not client visible
|
:return: Nothing
|
||||||
:param cbe: dbus python error call back parameter, not client visible
|
|
||||||
:return: Boolean
|
|
||||||
"""
|
"""
|
||||||
r = RequestEntry(-1, Manager._use_lvm_shell, (yes_no,), cb, cbe, False)
|
cmdhandler.set_execution(yes_no)
|
||||||
cfg.worker_q.put(r)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _external_event(command):
|
|
||||||
utils.log_debug("Processing _external_event= %s" % command,
|
|
||||||
'bg_black', 'fg_orange')
|
|
||||||
cfg.load()
|
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=MANAGER_INTERFACE,
|
dbus_interface=MANAGER_INTERFACE,
|
||||||
in_signature='s', out_signature='i')
|
in_signature='s', out_signature='i')
|
||||||
def ExternalEvent(self, command):
|
def ExternalEvent(self, command):
|
||||||
utils.log_debug("ExternalEvent %s" % command)
|
|
||||||
# If a user didn't explicitly specify udev, we will turn it off now.
|
|
||||||
if not cfg.args.use_udev:
|
|
||||||
if udevwatch.remove():
|
|
||||||
utils.log_debug("ExternalEvent received, disabling "
|
|
||||||
"udev monitoring")
|
|
||||||
# We are dependent on external events now to stay current!
|
|
||||||
cfg.got_external_event = True
|
|
||||||
|
|
||||||
r = RequestEntry(
|
event_add((command,))
|
||||||
-1, Manager._external_event, (command,), None, None, False)
|
|
||||||
cfg.worker_q.put(r)
|
|
||||||
return dbus.Int32(0)
|
return dbus.Int32(0)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -219,8 +191,15 @@ class Manager(AutomatedProperties):
|
|||||||
activate, cache, device_path,
|
activate, cache, device_path,
|
||||||
major_minor, scan_options)
|
major_minor, scan_options)
|
||||||
|
|
||||||
Manager.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
# This could potentially change the state quite a bit, so lets
|
||||||
|
# update everything to be safe
|
||||||
|
cfg.load()
|
||||||
return '/'
|
return '/'
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
MANAGER_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=MANAGER_INTERFACE,
|
dbus_interface=MANAGER_INTERFACE,
|
||||||
|
|||||||
@@ -11,10 +11,8 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
import dbus
|
import dbus
|
||||||
import os
|
|
||||||
import copy
|
|
||||||
from . import cfg
|
from . import cfg
|
||||||
from .utils import log_debug, pv_obj_path_generate, log_error
|
from .utils import log_debug
|
||||||
from .automatedproperties import AutomatedProperties
|
from .automatedproperties import AutomatedProperties
|
||||||
|
|
||||||
|
|
||||||
@@ -32,12 +30,14 @@ class ObjectManager(AutomatedProperties):
|
|||||||
self._id_to_object_path = {}
|
self._id_to_object_path = {}
|
||||||
self.rlock = threading.RLock()
|
self.rlock = threading.RLock()
|
||||||
|
|
||||||
@staticmethod
|
@dbus.service.method(
|
||||||
def _get_managed_objects(obj):
|
dbus_interface="org.freedesktop.DBus.ObjectManager",
|
||||||
with obj.rlock:
|
out_signature='a{oa{sa{sv}}}')
|
||||||
|
def GetManagedObjects(self):
|
||||||
|
with self.rlock:
|
||||||
rc = {}
|
rc = {}
|
||||||
try:
|
try:
|
||||||
for k, v in list(obj._objects.items()):
|
for k, v in list(self._objects.items()):
|
||||||
path, props = v[0].emit_data()
|
path, props = v[0].emit_data()
|
||||||
rc[path] = props
|
rc[path] = props
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -45,14 +45,6 @@ class ObjectManager(AutomatedProperties):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
@dbus.service.method(
|
|
||||||
dbus_interface="org.freedesktop.DBus.ObjectManager",
|
|
||||||
out_signature='a{oa{sa{sv}}}', async_callbacks=('cb', 'cbe'))
|
|
||||||
def GetManagedObjects(self, cb, cbe):
|
|
||||||
r = cfg.create_request_entry(-1, ObjectManager._get_managed_objects,
|
|
||||||
(self, ), cb, cbe, False)
|
|
||||||
cfg.worker_q.put(r)
|
|
||||||
|
|
||||||
def locked(self):
|
def locked(self):
|
||||||
"""
|
"""
|
||||||
If some external code need to run across a number of different
|
If some external code need to run across a number of different
|
||||||
@@ -77,37 +69,12 @@ class ObjectManager(AutomatedProperties):
|
|||||||
log_debug(('SIGNAL: InterfacesRemoved(%s, %s)' %
|
log_debug(('SIGNAL: InterfacesRemoved(%s, %s)' %
|
||||||
(str(object_path), str(interface_list))))
|
(str(object_path), str(interface_list))))
|
||||||
|
|
||||||
def validate_lookups(self):
|
|
||||||
with self.rlock:
|
|
||||||
tmp_lookups = copy.deepcopy(self._id_to_object_path)
|
|
||||||
|
|
||||||
# iterate over all we know, removing from the copy. If all is well
|
|
||||||
# we will have zero items left over
|
|
||||||
for path, md in self._objects.items():
|
|
||||||
obj, lvm_id, uuid = md
|
|
||||||
|
|
||||||
if lvm_id:
|
|
||||||
assert path == tmp_lookups[lvm_id]
|
|
||||||
del tmp_lookups[lvm_id]
|
|
||||||
|
|
||||||
if uuid:
|
|
||||||
assert path == tmp_lookups[uuid]
|
|
||||||
del tmp_lookups[uuid]
|
|
||||||
|
|
||||||
rc = len(tmp_lookups)
|
|
||||||
if rc:
|
|
||||||
# Error condition
|
|
||||||
log_error("_id_to_object_path has extraneous lookups!")
|
|
||||||
for key, path in tmp_lookups.items():
|
|
||||||
log_error("Key= %s, path= %s" % (key, path))
|
|
||||||
return rc
|
|
||||||
|
|
||||||
def _lookup_add(self, obj, path, lvm_id, uuid):
|
def _lookup_add(self, obj, path, lvm_id, uuid):
|
||||||
"""
|
"""
|
||||||
Store information about what we added to the caches so that we
|
Store information about what we added to the caches so that we
|
||||||
can remove it cleanly
|
can remove it cleanly
|
||||||
:param obj: The dbus object we are storing
|
:param obj: The dbus object we are storing
|
||||||
:param lvm_id: The lvm id for the asset
|
:param lvm_id: The user name for the asset
|
||||||
:param uuid: The uuid for the asset
|
:param uuid: The uuid for the asset
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
@@ -117,11 +84,6 @@ class ObjectManager(AutomatedProperties):
|
|||||||
self._lookup_remove(path)
|
self._lookup_remove(path)
|
||||||
|
|
||||||
self._objects[path] = (obj, lvm_id, uuid)
|
self._objects[path] = (obj, lvm_id, uuid)
|
||||||
|
|
||||||
# Make sure we have one or the other
|
|
||||||
assert lvm_id or uuid
|
|
||||||
|
|
||||||
if lvm_id:
|
|
||||||
self._id_to_object_path[lvm_id] = path
|
self._id_to_object_path[lvm_id] = path
|
||||||
|
|
||||||
if uuid:
|
if uuid:
|
||||||
@@ -131,13 +93,8 @@ class ObjectManager(AutomatedProperties):
|
|||||||
# Note: Only called internally, lock implied
|
# Note: Only called internally, lock implied
|
||||||
if obj_path in self._objects:
|
if obj_path in self._objects:
|
||||||
(obj, lvm_id, uuid) = self._objects[obj_path]
|
(obj, lvm_id, uuid) = self._objects[obj_path]
|
||||||
|
|
||||||
if lvm_id in self._id_to_object_path:
|
|
||||||
del self._id_to_object_path[lvm_id]
|
del self._id_to_object_path[lvm_id]
|
||||||
|
|
||||||
if uuid in self._id_to_object_path:
|
|
||||||
del self._id_to_object_path[uuid]
|
del self._id_to_object_path[uuid]
|
||||||
|
|
||||||
del self._objects[obj_path]
|
del self._objects[obj_path]
|
||||||
|
|
||||||
def lookup_update(self, dbus_obj, new_uuid, new_lvm_id):
|
def lookup_update(self, dbus_obj, new_uuid, new_lvm_id):
|
||||||
@@ -166,8 +123,8 @@ class ObjectManager(AutomatedProperties):
|
|||||||
with self.rlock:
|
with self.rlock:
|
||||||
path, props = dbus_object.emit_data()
|
path, props = dbus_object.emit_data()
|
||||||
|
|
||||||
# print('Registering object path %s for %s' %
|
# print 'Registering object path %s for %s' %
|
||||||
# (path, dbus_object.lvm_id))
|
# (path, dbus_object.lvm_id)
|
||||||
|
|
||||||
# We want fast access to the object by a number of different ways
|
# We want fast access to the object by a number of different ways
|
||||||
# so we use multiple hashs with different keys
|
# so we use multiple hashs with different keys
|
||||||
@@ -215,7 +172,7 @@ class ObjectManager(AutomatedProperties):
|
|||||||
def get_object_by_uuid_lvm_id(self, uuid, lvm_id):
|
def get_object_by_uuid_lvm_id(self, uuid, lvm_id):
|
||||||
with self.rlock:
|
with self.rlock:
|
||||||
return self.get_object_by_path(
|
return self.get_object_by_path(
|
||||||
self.get_object_path_by_uuid_lvm_id(uuid, lvm_id))
|
self.get_object_path_by_lvm_id(uuid, lvm_id, None, False))
|
||||||
|
|
||||||
def get_object_by_lvm_id(self, lvm_id):
|
def get_object_by_lvm_id(self, lvm_id):
|
||||||
"""
|
"""
|
||||||
@@ -223,137 +180,77 @@ class ObjectManager(AutomatedProperties):
|
|||||||
:param lvm_id: The lvm identifier
|
:param lvm_id: The lvm identifier
|
||||||
"""
|
"""
|
||||||
with self.rlock:
|
with self.rlock:
|
||||||
lookup_rc = self._id_lookup(lvm_id)
|
if lvm_id in self._id_to_object_path:
|
||||||
if lookup_rc:
|
return self.get_object_by_path(self._id_to_object_path[lvm_id])
|
||||||
return self.get_object_by_path(lookup_rc)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_object_path_by_lvm_id(self, lvm_id):
|
def _uuid_verify(self, path, lvm_id, uuid):
|
||||||
"""
|
|
||||||
Given an lvm identifier, return the object path for it
|
|
||||||
:param lvm_id: The lvm identifier
|
|
||||||
:return: Object path or '/' if not found
|
|
||||||
"""
|
|
||||||
with self.rlock:
|
|
||||||
lookup_rc = self._id_lookup(lvm_id)
|
|
||||||
if lookup_rc:
|
|
||||||
return lookup_rc
|
|
||||||
return '/'
|
|
||||||
|
|
||||||
def _uuid_verify(self, path, uuid, lvm_id):
|
|
||||||
"""
|
"""
|
||||||
Ensure uuid is present for a successful lvm_id lookup
|
Ensure uuid is present for a successful lvm_id lookup
|
||||||
NOTE: Internal call, assumes under object manager lock
|
NOTE: Internal call, assumes under object manager lock
|
||||||
:param path: Path to object we looked up
|
:param path: Path to object we looked up
|
||||||
:param uuid: lvm uuid to verify
|
|
||||||
:param lvm_id: lvm_id used to find object
|
:param lvm_id: lvm_id used to find object
|
||||||
|
:param uuid: lvm uuid to verify
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
# This gets called when we found an object based on lvm_id, ensure
|
# This gets called when we found an object based on lvm_id, ensure
|
||||||
# uuid is correct too, as they can change. There is no durable
|
# uuid is correct too, as they can change
|
||||||
# non-changeable name in lvm
|
|
||||||
if lvm_id != uuid:
|
if lvm_id != uuid:
|
||||||
if uuid and uuid not in self._id_to_object_path:
|
if uuid not in self._id_to_object_path:
|
||||||
obj = self.get_object_by_path(path)
|
obj = self.get_object_by_path(path)
|
||||||
self._lookup_add(obj, path, lvm_id, uuid)
|
self._lookup_add(obj, path, lvm_id, uuid)
|
||||||
|
|
||||||
def _lvm_id_verify(self, path, uuid, lvm_id):
|
def get_object_path_by_lvm_id(self, uuid, lvm_id, path_create=None,
|
||||||
|
gen_new=True):
|
||||||
"""
|
"""
|
||||||
Ensure lvm_id is present for a successful uuid lookup
|
For a given lvm asset return the dbus object registered to it. If the
|
||||||
NOTE: Internal call, assumes under object manager lock
|
object is not found and gen_new == True and path_create is a valid
|
||||||
:param path: Path to object we looked up
|
function we will create a new path, register it and return it.
|
||||||
:param uuid: uuid used to find object
|
:param uuid: The uuid for the lvm object
|
||||||
:param lvm_id: lvm_id to verify
|
:param lvm_id: The lvm name
|
||||||
:return: None
|
:param path_create: If true create an object path if not found
|
||||||
"""
|
:param gen_new: The function used to create the new path
|
||||||
# This gets called when we found an object based on uuid, ensure
|
|
||||||
# lvm_id is correct too, as they can change. There is no durable
|
|
||||||
# non-changeable name in lvm
|
|
||||||
if lvm_id != uuid:
|
|
||||||
if lvm_id and lvm_id not in self._id_to_object_path:
|
|
||||||
obj = self.get_object_by_path(path)
|
|
||||||
self._lookup_add(obj, path, lvm_id, uuid)
|
|
||||||
|
|
||||||
def _id_lookup(self, the_id):
|
|
||||||
path = None
|
|
||||||
|
|
||||||
if the_id:
|
|
||||||
# The _id_to_object_path contains hash keys for everything, so
|
|
||||||
# uuid and lvm_id
|
|
||||||
if the_id in self._id_to_object_path:
|
|
||||||
path = self._id_to_object_path[the_id]
|
|
||||||
else:
|
|
||||||
if "/" in the_id:
|
|
||||||
if the_id.startswith('/'):
|
|
||||||
# We could have a pv device path lookup that failed,
|
|
||||||
# lets try canonical form and try again.
|
|
||||||
canonical = os.path.realpath(the_id)
|
|
||||||
if canonical in self._id_to_object_path:
|
|
||||||
path = self._id_to_object_path[canonical]
|
|
||||||
else:
|
|
||||||
vg, lv = the_id.split("/", 1)
|
|
||||||
int_lvm_id = vg + "/" + ("[%s]" % lv)
|
|
||||||
if int_lvm_id in self._id_to_object_path:
|
|
||||||
path = self._id_to_object_path[int_lvm_id]
|
|
||||||
return path
|
|
||||||
|
|
||||||
def get_object_path_by_uuid_lvm_id(self, uuid, lvm_id, path_create=None):
|
|
||||||
"""
|
|
||||||
For a given lvm asset return the dbus object path registered for it.
|
|
||||||
This method first looks up by uuid and then by lvm_id. You
|
|
||||||
can search by just one by setting uuid == lvm_id (uuid or lvm_id).
|
|
||||||
If the object is not found and path_create is a not None, the
|
|
||||||
path_create function will be called to create a new object path and
|
|
||||||
register it with the object manager for the specified uuid & lvm_id.
|
|
||||||
Note: If path create is not None, uuid and lvm_id cannot be equal
|
|
||||||
:param uuid: The uuid for the lvm object we are searching for
|
|
||||||
:param lvm_id: The lvm name (eg. pv device path, vg name, lv full name)
|
|
||||||
:param path_create: If not None, create the path using this function if
|
|
||||||
we fail to find the object by uuid or lvm_id.
|
|
||||||
:returns None if lvm asset not found and path_create == None otherwise
|
|
||||||
a valid dbus object path
|
|
||||||
"""
|
"""
|
||||||
with self.rlock:
|
with self.rlock:
|
||||||
assert lvm_id
|
assert lvm_id
|
||||||
assert uuid
|
assert uuid
|
||||||
|
|
||||||
if path_create:
|
if gen_new:
|
||||||
assert uuid != lvm_id
|
assert path_create
|
||||||
|
|
||||||
# Check for Manager.LookUpByLvmId query, we cannot
|
path = None
|
||||||
# check/verify/update the uuid and lvm_id lookups so don't!
|
|
||||||
if uuid == lvm_id:
|
|
||||||
path = self._id_lookup(lvm_id)
|
|
||||||
else:
|
|
||||||
# We have a uuid and a lvm_id we can do sanity checks to ensure
|
|
||||||
# that they are consistent
|
|
||||||
|
|
||||||
# If a PV is missing it's device path is '[unknown]' or some
|
if lvm_id in self._id_to_object_path:
|
||||||
# other text derivation of unknown. When we find that a PV is
|
path = self._id_to_object_path[lvm_id]
|
||||||
# missing we will clear out the lvm_id as it's likely not unique
|
self._uuid_verify(path, lvm_id, uuid)
|
||||||
# and thus not useful and potentially harmful for lookups.
|
return path
|
||||||
if path_create == pv_obj_path_generate and \
|
if "/" in lvm_id:
|
||||||
cfg.db.pv_missing(uuid):
|
vg, lv = lvm_id.split("/", 1)
|
||||||
lvm_id = None
|
int_lvm_id = vg + "/" + ("[%s]" % lv)
|
||||||
|
if int_lvm_id in self._id_to_object_path:
|
||||||
|
path = self._id_to_object_path[int_lvm_id]
|
||||||
|
self._uuid_verify(path, int_lvm_id, uuid)
|
||||||
|
return path
|
||||||
|
|
||||||
# Lets check for the uuid first
|
if uuid and uuid in self._id_to_object_path:
|
||||||
path = self._id_lookup(uuid)
|
# If we get here it indicates that we found the object, but
|
||||||
if path:
|
# the lvm_id lookup failed. In the case of a rename, the uuid
|
||||||
# Verify the lvm_id is sane
|
# will be correct, but the lvm_id will be wrong and vise versa.
|
||||||
self._lvm_id_verify(path, uuid, lvm_id)
|
# If the lvm_id does not equal the uuid, lets fix up the table
|
||||||
|
# so that lookups will be handled correctly.
|
||||||
|
path = self._id_to_object_path[uuid]
|
||||||
|
|
||||||
|
# In some cases we are looking up by one or the other, don't
|
||||||
|
# update when they are the same.
|
||||||
|
if uuid != lvm_id:
|
||||||
|
obj = self.get_object_by_path(path)
|
||||||
|
self._lookup_add(obj, path, lvm_id, uuid)
|
||||||
else:
|
else:
|
||||||
# Unable to find by UUID, lets lookup by lvm_id
|
if gen_new:
|
||||||
path = self._id_lookup(lvm_id)
|
|
||||||
if path:
|
|
||||||
# Verify the uuid is sane
|
|
||||||
self._uuid_verify(path, uuid, lvm_id)
|
|
||||||
else:
|
|
||||||
# We have exhausted all lookups, let's create if we can
|
|
||||||
if path_create:
|
|
||||||
path = path_create()
|
path = path_create()
|
||||||
self._lookup_add(None, path, lvm_id, uuid)
|
self._lookup_add(None, path, lvm_id, uuid)
|
||||||
|
|
||||||
# print('get_object_path_by_lvm_id(%s, %s, %s, %s: return %s' %
|
# pprint('get_object_path_by_lvm_id(%s, %s, %s, %s: return %s' %
|
||||||
# (uuid, lvm_id, str(path_create), str(gen_new), path))
|
# (uuid, lvm_id, str(path_create), str(gen_new), path))
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ class PvState(State):
|
|||||||
return self.lvm_path
|
return self.lvm_path
|
||||||
|
|
||||||
def _lv_object_list(self, vg_name):
|
def _lv_object_list(self, vg_name):
|
||||||
|
|
||||||
|
# Note we are returning "a(oa(tts))"
|
||||||
|
|
||||||
rc = []
|
rc = []
|
||||||
if vg_name:
|
if vg_name:
|
||||||
for lv in sorted(cfg.db.pv_contained_lv(self.lvm_id)):
|
for lv in sorted(cfg.db.pv_contained_lv(self.lvm_id)):
|
||||||
@@ -62,11 +65,11 @@ class PvState(State):
|
|||||||
full_name = "%s/%s" % (vg_name, lv_name)
|
full_name = "%s/%s" % (vg_name, lv_name)
|
||||||
|
|
||||||
path_create = lv_object_path_method(lv_name, meta)
|
path_create = lv_object_path_method(lv_name, meta)
|
||||||
lv_path = cfg.om.get_object_path_by_uuid_lvm_id(
|
lv_path = cfg.om.get_object_path_by_lvm_id(
|
||||||
lv_uuid, full_name, path_create)
|
lv_uuid, full_name, path_create)
|
||||||
|
|
||||||
rc.append((lv_path, segs))
|
rc.append((lv_path, segs))
|
||||||
return rc
|
return dbus.Array(rc, signature="(oa(tts))")
|
||||||
|
|
||||||
# noinspection PyUnusedLocal,PyPep8Naming
|
# noinspection PyUnusedLocal,PyPep8Naming
|
||||||
def __init__(self, lvm_path, Uuid, Name,
|
def __init__(self, lvm_path, Uuid, Name,
|
||||||
@@ -79,10 +82,8 @@ class PvState(State):
|
|||||||
|
|
||||||
self.lv = self._lv_object_list(vg_name)
|
self.lv = self._lv_object_list(vg_name)
|
||||||
|
|
||||||
# It's possible to have a vg_name and no uuid with the main example
|
if vg_name:
|
||||||
# being when the vg_name == '[unknown]'
|
self.vg_path = cfg.om.get_object_path_by_lvm_id(
|
||||||
if vg_uuid and vg_name:
|
|
||||||
self.vg_path = cfg.om.get_object_path_by_uuid_lvm_id(
|
|
||||||
vg_uuid, vg_name, vg_obj_path_generate)
|
vg_uuid, vg_name, vg_obj_path_generate)
|
||||||
else:
|
else:
|
||||||
self.vg_path = '/'
|
self.vg_path = '/'
|
||||||
@@ -92,7 +93,7 @@ class PvState(State):
|
|||||||
|
|
||||||
def create_dbus_object(self, path):
|
def create_dbus_object(self, path):
|
||||||
if not path:
|
if not path:
|
||||||
path = cfg.om.get_object_path_by_uuid_lvm_id(self.Uuid, self.Name,
|
path = cfg.om.get_object_path_by_lvm_id(self.Uuid, self.Name,
|
||||||
pv_obj_path_generate)
|
pv_obj_path_generate)
|
||||||
return Pv(path, self)
|
return Pv(path, self)
|
||||||
|
|
||||||
@@ -137,30 +138,23 @@ class Pv(AutomatedProperties):
|
|||||||
def _remove(pv_uuid, pv_name, remove_options):
|
def _remove(pv_uuid, pv_name, remove_options):
|
||||||
# Remove the PV, if successful then remove from the model
|
# Remove the PV, if successful then remove from the model
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Pv.validate_dbus_object(pv_uuid, pv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
|
||||||
rc, out, err = cmdhandler.pv_remove(pv_name, remove_options)
|
|
||||||
Pv.handle_execute(rc, out, err)
|
|
||||||
return '/'
|
|
||||||
|
|
||||||
@staticmethod
|
if dbo:
|
||||||
def handle_execute(rc, out, err):
|
rc, out, err = cmdhandler.pv_remove(pv_name, remove_options)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
cfg.load()
|
cfg.om.remove_object(dbo, True)
|
||||||
else:
|
else:
|
||||||
# Need to work on error handling, need consistent
|
# Need to work on error handling, need consistent
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
PV_INTERFACE,
|
PV_INTERFACE,
|
||||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
@staticmethod
|
|
||||||
def validate_dbus_object(pv_uuid, pv_name):
|
|
||||||
dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
|
|
||||||
if not dbo:
|
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
PV_INTERFACE,
|
PV_INTERFACE,
|
||||||
'PV with uuid %s and name %s not present!' %
|
'PV with uuid %s and name %s not present!' %
|
||||||
(pv_uuid, pv_name))
|
(pv_uuid, pv_name))
|
||||||
return dbo
|
return '/'
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=PV_INTERFACE,
|
dbus_interface=PV_INTERFACE,
|
||||||
@@ -177,11 +171,22 @@ class Pv(AutomatedProperties):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _resize(pv_uuid, pv_name, new_size_bytes, resize_options):
|
def _resize(pv_uuid, pv_name, new_size_bytes, resize_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Pv.validate_dbus_object(pv_uuid, pv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes,
|
rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes,
|
||||||
resize_options)
|
resize_options)
|
||||||
Pv.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
dbo.refresh()
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
PV_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
PV_INTERFACE,
|
||||||
|
'PV with uuid %s and name %s not present!' %
|
||||||
|
(pv_uuid, pv_name))
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -199,10 +204,21 @@ class Pv(AutomatedProperties):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _allocation_enabled(pv_uuid, pv_name, yes_no, allocation_options):
|
def _allocation_enabled(pv_uuid, pv_name, yes_no, allocation_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Pv.validate_dbus_object(pv_uuid, pv_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.pv_allocatable(
|
rc, out, err = cmdhandler.pv_allocatable(
|
||||||
pv_name, yes_no, allocation_options)
|
pv_name, yes_no, allocation_options)
|
||||||
Pv.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
cfg.load()
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
PV_INTERFACE, 'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
PV_INTERFACE,
|
||||||
|
'PV with uuid %s and name %s not present!' %
|
||||||
|
(pv_uuid, pv_name))
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -225,20 +241,26 @@ class Pv(AutomatedProperties):
|
|||||||
@property
|
@property
|
||||||
def PeSegments(self):
|
def PeSegments(self):
|
||||||
if len(self.state.pe_segments):
|
if len(self.state.pe_segments):
|
||||||
return dbus.Array(self.state.pe_segments, signature='(tt)')
|
return self.state.pe_segments
|
||||||
return dbus.Array([], '(tt)')
|
return dbus.Array([], '(tt)')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Exportable(self):
|
def Exportable(self):
|
||||||
return dbus.Boolean(self.state.attr[1] == 'x')
|
if self.state.attr[1] == 'x':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Allocatable(self):
|
def Allocatable(self):
|
||||||
return dbus.Boolean(self.state.attr[0] == 'a')
|
if self.state.attr[0] == 'a':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Missing(self):
|
def Missing(self):
|
||||||
return dbus.Boolean(self.state.attr[2] == 'm')
|
if self.state.attr[2] == 'm':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def object_path(self):
|
def object_path(self):
|
||||||
return self._object_path
|
return self._object_path
|
||||||
@@ -253,8 +275,8 @@ class Pv(AutomatedProperties):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def Lv(self):
|
def Lv(self):
|
||||||
return dbus.Array(self.state.lv, signature="(oa(tts))")
|
return self.state.lv
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Vg(self):
|
def Vg(self):
|
||||||
return dbus.ObjectPath(self.state.vg_path)
|
return self.state.vg_path
|
||||||
|
|||||||
45
daemons/lvmdbusd/refresh.py
Normal file
45
daemons/lvmdbusd/refresh.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# This copyrighted material is made available to anyone wishing to use,
|
||||||
|
# modify, copy, or redistribute it subject to the terms and conditions
|
||||||
|
# of the GNU General Public License v.2.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Try and minimize the refreshes we do.
|
||||||
|
|
||||||
|
import threading
|
||||||
|
from .request import RequestEntry
|
||||||
|
from . import cfg
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
_rlock = threading.RLock()
|
||||||
|
_count = 0
|
||||||
|
|
||||||
|
|
||||||
|
def handle_external_event(command):
|
||||||
|
utils.log_debug("External event: '%s'" % command)
|
||||||
|
event_complete()
|
||||||
|
cfg.load()
|
||||||
|
|
||||||
|
|
||||||
|
def event_add(params):
|
||||||
|
global _rlock
|
||||||
|
global _count
|
||||||
|
with _rlock:
|
||||||
|
if _count == 0:
|
||||||
|
_count += 1
|
||||||
|
r = RequestEntry(
|
||||||
|
-1, handle_external_event,
|
||||||
|
params, None, None, False)
|
||||||
|
cfg.worker_q.put(r)
|
||||||
|
|
||||||
|
|
||||||
|
def event_complete():
|
||||||
|
global _rlock
|
||||||
|
global _count
|
||||||
|
with _rlock:
|
||||||
|
if _count > 0:
|
||||||
|
_count -= 1
|
||||||
|
return _count
|
||||||
@@ -9,16 +9,17 @@
|
|||||||
|
|
||||||
import threading
|
import threading
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from gi.repository import GLib
|
from gi.repository import GObject
|
||||||
from .job import Job
|
from .job import Job
|
||||||
from . import cfg
|
from . import cfg
|
||||||
import traceback
|
import traceback
|
||||||
from .utils import log_error, mt_async_call
|
from .utils import log_error
|
||||||
|
|
||||||
|
|
||||||
class RequestEntry(object):
|
class RequestEntry(object):
|
||||||
def __init__(self, tmo, method, arguments, cb, cb_error,
|
def __init__(self, tmo, method, arguments, cb, cb_error,
|
||||||
return_tuple=True, job_state=None):
|
return_tuple=True):
|
||||||
|
self.tmo = tmo
|
||||||
self.method = method
|
self.method = method
|
||||||
self.arguments = arguments
|
self.arguments = arguments
|
||||||
self.cb = cb
|
self.cb = cb
|
||||||
@@ -28,39 +29,31 @@ class RequestEntry(object):
|
|||||||
self.lock = threading.RLock()
|
self.lock = threading.RLock()
|
||||||
self.done = False
|
self.done = False
|
||||||
self._result = None
|
self._result = None
|
||||||
self._job = None
|
self._job = False
|
||||||
self._rc = 0
|
self._rc = 0
|
||||||
self._rc_error = None
|
self._rc_error = None
|
||||||
self._return_tuple = return_tuple
|
self._return_tuple = return_tuple
|
||||||
self._job_state = job_state
|
|
||||||
|
|
||||||
if tmo < 0:
|
if self.tmo < 0:
|
||||||
# Client is willing to block forever
|
# Client is willing to block forever
|
||||||
pass
|
pass
|
||||||
elif tmo == 0:
|
elif tmo == 0:
|
||||||
self._return_job()
|
self._return_job()
|
||||||
else:
|
else:
|
||||||
# Note: using 990 instead of 1000 for second to ms conversion to
|
self.timer_id = GObject.timeout_add_seconds(
|
||||||
# account for overhead. Goal is to return just before the
|
tmo, RequestEntry._request_timeout, self)
|
||||||
# timeout amount has expired. Better to be a little early than
|
|
||||||
# late.
|
|
||||||
self.timer_id = GLib.timeout_add(
|
|
||||||
tmo * 990, RequestEntry._request_timeout, self)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _request_timeout(r):
|
def _request_timeout(r):
|
||||||
"""
|
"""
|
||||||
Method which gets called when the timer runs out!
|
Method which gets called when the timer runs out!
|
||||||
:param r: RequestEntry which timed out
|
:param r: RequestEntry which timed out
|
||||||
:return: Result of timer_expired
|
:return: Nothing
|
||||||
"""
|
"""
|
||||||
return r.timer_expired()
|
r.timer_expired()
|
||||||
|
|
||||||
def _return_job(self):
|
def _return_job(self):
|
||||||
# Return job is only called when we create a request object or when
|
self._job = Job(self)
|
||||||
# we pop a timer. In both cases we are running in the correct context
|
|
||||||
# and do not need to schedule the call back in main context.
|
|
||||||
self._job = Job(self, self._job_state)
|
|
||||||
cfg.om.register_object(self._job, True)
|
cfg.om.register_object(self._job, True)
|
||||||
if self._return_tuple:
|
if self._return_tuple:
|
||||||
self.cb(('/', self._job.dbus_object_path()))
|
self.cb(('/', self._job.dbus_object_path()))
|
||||||
@@ -71,14 +64,13 @@ class RequestEntry(object):
|
|||||||
try:
|
try:
|
||||||
result = self.method(*self.arguments)
|
result = self.method(*self.arguments)
|
||||||
self.register_result(result)
|
self.register_result(result)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
# Use the request entry to return the result as the client may
|
# Use the request entry to return the result as the client may
|
||||||
# have gotten a job by the time we hit an error
|
# have gotten a job by the time we hit an error
|
||||||
# Lets get the stacktrace and set that to the error message
|
# Lets get the stacktrace and set that to the error message
|
||||||
st = traceback.format_exc()
|
st = traceback.format_exc()
|
||||||
cfg.blackbox.dump()
|
|
||||||
log_error("Exception returned to client: \n%s" % st)
|
log_error("Exception returned to client: \n%s" % st)
|
||||||
self.register_error(-1, str(e), e)
|
self.register_error(-1, st)
|
||||||
|
|
||||||
def is_done(self):
|
def is_done(self):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
@@ -95,17 +87,16 @@ class RequestEntry(object):
|
|||||||
return self._result
|
return self._result
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
def _reg_ending(self, result, error_rc=0, error_msg=None,
|
def _reg_ending(self, result, error_rc=0, error=None):
|
||||||
error_exception=None):
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.done = True
|
self.done = True
|
||||||
if self.timer_id != -1:
|
if self.timer_id != -1:
|
||||||
# Try to prevent the timer from firing
|
# Try to prevent the timer from firing
|
||||||
GLib.source_remove(self.timer_id)
|
GObject.source_remove(self.timer_id)
|
||||||
|
|
||||||
self._result = result
|
self._result = result
|
||||||
self._rc = error_rc
|
self._rc = error_rc
|
||||||
self._rc_error = error_msg
|
self._rc_error = error
|
||||||
|
|
||||||
if not self._job:
|
if not self._job:
|
||||||
# We finished and there is no job, so return result or error
|
# We finished and there is no job, so return result or error
|
||||||
@@ -116,27 +107,20 @@ class RequestEntry(object):
|
|||||||
if error_rc == 0:
|
if error_rc == 0:
|
||||||
if self.cb:
|
if self.cb:
|
||||||
if self._return_tuple:
|
if self._return_tuple:
|
||||||
mt_async_call(self.cb, (result, '/'))
|
self.cb((result, '/'))
|
||||||
else:
|
else:
|
||||||
mt_async_call(self.cb, result)
|
self.cb(result)
|
||||||
else:
|
else:
|
||||||
if self.cb_error:
|
if self.cb_error:
|
||||||
if not error_exception:
|
self.cb_error(self._rc_error)
|
||||||
if not error_msg:
|
|
||||||
error_exception = Exception(
|
|
||||||
"An error occurred, but no reason was "
|
|
||||||
"given, see service logs!")
|
|
||||||
else:
|
|
||||||
error_exception = Exception(error_msg)
|
|
||||||
|
|
||||||
mt_async_call(self.cb_error, error_exception)
|
|
||||||
else:
|
else:
|
||||||
# We have a job and it's complete, indicate that it's done.
|
# We have a job and it's complete, indicate that it's done.
|
||||||
|
# TODO: We need to signal the job is done too.
|
||||||
self._job.Complete = True
|
self._job.Complete = True
|
||||||
self._job = None
|
self._job = None
|
||||||
|
|
||||||
def register_error(self, error_rc, error_message, error_exception):
|
def register_error(self, error_rc, error):
|
||||||
self._reg_ending('/', error_rc, error_message, error_exception)
|
self._reg_ending(None, error_rc, error)
|
||||||
|
|
||||||
def register_result(self, result):
|
def register_result(self, result):
|
||||||
self._reg_ending(result)
|
self._reg_ending(result)
|
||||||
|
|||||||
@@ -8,42 +8,10 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import pyudev
|
import pyudev
|
||||||
import threading
|
from .refresh import event_add
|
||||||
from . import cfg
|
from . import cfg
|
||||||
from .request import RequestEntry
|
|
||||||
from . import utils
|
|
||||||
|
|
||||||
observer = None
|
observer = None
|
||||||
observer_lock = threading.RLock()
|
|
||||||
|
|
||||||
_udev_lock = threading.RLock()
|
|
||||||
_udev_count = 0
|
|
||||||
|
|
||||||
|
|
||||||
def udev_add():
|
|
||||||
global _udev_count
|
|
||||||
with _udev_lock:
|
|
||||||
if _udev_count == 0:
|
|
||||||
_udev_count += 1
|
|
||||||
|
|
||||||
# Place this on the queue so any other operations will sequence
|
|
||||||
# behind it
|
|
||||||
r = RequestEntry(
|
|
||||||
-1, _udev_event, (), None, None, False)
|
|
||||||
cfg.worker_q.put(r)
|
|
||||||
|
|
||||||
|
|
||||||
def udev_complete():
|
|
||||||
global _udev_count
|
|
||||||
with _udev_lock:
|
|
||||||
if _udev_count > 0:
|
|
||||||
_udev_count -= 1
|
|
||||||
|
|
||||||
|
|
||||||
def _udev_event():
|
|
||||||
utils.log_debug("Processing udev event")
|
|
||||||
udev_complete()
|
|
||||||
cfg.load()
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
@@ -68,11 +36,10 @@ def filter_event(action, device):
|
|||||||
refresh = True
|
refresh = True
|
||||||
|
|
||||||
if refresh:
|
if refresh:
|
||||||
udev_add()
|
event_add(('udev',))
|
||||||
|
|
||||||
|
|
||||||
def add():
|
def add():
|
||||||
with observer_lock:
|
|
||||||
global observer
|
global observer
|
||||||
context = pyudev.Context()
|
context = pyudev.Context()
|
||||||
monitor = pyudev.Monitor.from_netlink(context)
|
monitor = pyudev.Monitor.from_netlink(context)
|
||||||
@@ -82,10 +49,6 @@ def add():
|
|||||||
|
|
||||||
|
|
||||||
def remove():
|
def remove():
|
||||||
with observer_lock:
|
|
||||||
global observer
|
global observer
|
||||||
if observer:
|
|
||||||
observer.stop()
|
observer.stop()
|
||||||
observer = None
|
observer = None
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ import inspect
|
|||||||
import ctypes
|
import ctypes
|
||||||
import os
|
import os
|
||||||
import string
|
import string
|
||||||
import datetime
|
|
||||||
|
|
||||||
import dbus
|
import dbus
|
||||||
from lvmdbusd import cfg
|
import dbus.service
|
||||||
# noinspection PyUnresolvedReferences
|
import dbus.mainloop.glib
|
||||||
from gi.repository import GLib
|
|
||||||
import threading
|
try:
|
||||||
import traceback
|
from . import cfg
|
||||||
import signal
|
except SystemError:
|
||||||
|
import cfg
|
||||||
|
|
||||||
STDOUT_TTY = os.isatty(sys.stdout.fileno())
|
STDOUT_TTY = os.isatty(sys.stdout.fileno())
|
||||||
|
|
||||||
@@ -149,27 +149,17 @@ def add_properties(xml, interface, props):
|
|||||||
:param props: Output from get_properties
|
:param props: Output from get_properties
|
||||||
:return: updated XML string
|
:return: updated XML string
|
||||||
"""
|
"""
|
||||||
if props:
|
|
||||||
root = Et.fromstring(xml)
|
root = Et.fromstring(xml)
|
||||||
interface_element = None
|
|
||||||
|
|
||||||
# Check to see if interface is present
|
if props:
|
||||||
|
|
||||||
for c in root:
|
for c in root:
|
||||||
|
# print c.attrib['name']
|
||||||
if c.attrib['name'] == interface:
|
if c.attrib['name'] == interface:
|
||||||
interface_element = c
|
|
||||||
break
|
|
||||||
|
|
||||||
# Interface is not present, lets create it so we have something to
|
|
||||||
# attach the properties too
|
|
||||||
if interface_element is None:
|
|
||||||
interface_element = Et.Element("interface", name=interface)
|
|
||||||
root.append(interface_element)
|
|
||||||
|
|
||||||
# Add the properties
|
|
||||||
for p in props:
|
for p in props:
|
||||||
temp = '<property type="%s" name="%s" access="%s"/>\n' % \
|
temp = '<property type="%s" name="%s" access="%s"/>\n' % \
|
||||||
(p['p_t'], p['p_name'], p['p_access'])
|
(p['p_t'], p['p_name'], p['p_access'])
|
||||||
interface_element.append(Et.fromstring(temp))
|
c.append(Et.fromstring(temp))
|
||||||
|
|
||||||
return Et.tostring(root, encoding='utf8')
|
return Et.tostring(root, encoding='utf8')
|
||||||
return xml
|
return xml
|
||||||
@@ -245,7 +235,7 @@ def parse_tags(tags):
|
|||||||
if len(tags):
|
if len(tags):
|
||||||
if ',' in tags:
|
if ',' in tags:
|
||||||
return tags.split(',')
|
return tags.split(',')
|
||||||
return dbus.Array(sorted([tags]), signature='s')
|
return sorted([tags])
|
||||||
return dbus.Array([], signature='s')
|
return dbus.Array([], signature='s')
|
||||||
|
|
||||||
|
|
||||||
@@ -253,12 +243,6 @@ def _common_log(msg, *attributes):
|
|||||||
cfg.stdout_lock.acquire()
|
cfg.stdout_lock.acquire()
|
||||||
tid = ctypes.CDLL('libc.so.6').syscall(186)
|
tid = ctypes.CDLL('libc.so.6').syscall(186)
|
||||||
|
|
||||||
if STDOUT_TTY:
|
|
||||||
msg = "%s: %d:%d - %s" % \
|
|
||||||
(datetime.datetime.now().strftime("%b %d %H:%M:%S.%f"),
|
|
||||||
os.getpid(), tid, msg)
|
|
||||||
|
|
||||||
else:
|
|
||||||
msg = "%d:%d - %s" % (os.getpid(), tid, msg)
|
msg = "%d:%d - %s" % (os.getpid(), tid, msg)
|
||||||
|
|
||||||
if STDOUT_TTY and attributes:
|
if STDOUT_TTY and attributes:
|
||||||
@@ -274,7 +258,7 @@ def _common_log(msg, *attributes):
|
|||||||
# @param msg Message to output to stdout
|
# @param msg Message to output to stdout
|
||||||
# @return None
|
# @return None
|
||||||
def log_debug(msg, *attributes):
|
def log_debug(msg, *attributes):
|
||||||
if cfg.args and cfg.args.debug:
|
if cfg.DEBUG:
|
||||||
_common_log(msg, *attributes)
|
_common_log(msg, *attributes)
|
||||||
|
|
||||||
|
|
||||||
@@ -282,47 +266,12 @@ def log_error(msg, *attributes):
|
|||||||
_common_log(msg, *attributes)
|
_common_log(msg, *attributes)
|
||||||
|
|
||||||
|
|
||||||
def dump_threads_stackframe():
|
|
||||||
ident_to_name = {}
|
|
||||||
|
|
||||||
for thread_object in threading.enumerate():
|
|
||||||
ident_to_name[thread_object.ident] = thread_object
|
|
||||||
|
|
||||||
stacks = []
|
|
||||||
for thread_ident, frame in sys._current_frames().items():
|
|
||||||
stack = traceback.format_list(traceback.extract_stack(frame))
|
|
||||||
|
|
||||||
# There is a possibility that a thread gets created after we have
|
|
||||||
# enumerated all threads, so this lookup table may be incomplete, so
|
|
||||||
# account for this
|
|
||||||
if thread_ident in ident_to_name:
|
|
||||||
thread_name = ident_to_name[thread_ident].name
|
|
||||||
else:
|
|
||||||
thread_name = "unknown"
|
|
||||||
|
|
||||||
stacks.append("Thread: %s" % (thread_name))
|
|
||||||
stacks.append("".join(stack))
|
|
||||||
|
|
||||||
log_error("Dumping thread stack frames!\n" + "\n".join(stacks))
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
def handler(signum):
|
def handler(signum, frame):
|
||||||
try:
|
|
||||||
if signum == signal.SIGUSR1:
|
|
||||||
dump_threads_stackframe()
|
|
||||||
else:
|
|
||||||
cfg.run.value = 0
|
cfg.run.value = 0
|
||||||
log_debug('Exiting daemon with signal %d' % signum)
|
log_debug('Signal handler called with signal %d' % signum)
|
||||||
if cfg.loop is not None:
|
if cfg.loop is not None:
|
||||||
cfg.loop.quit()
|
cfg.loop.quit()
|
||||||
except:
|
|
||||||
st = traceback.format_exc()
|
|
||||||
log_error("signal handler: exception (logged, not reported!) \n %s" % st)
|
|
||||||
|
|
||||||
# It's important we report that we handled the exception for the exception
|
|
||||||
# handler to continue to work, especially for signal 10 (SIGUSR1)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def pv_obj_path_generate():
|
def pv_obj_path_generate():
|
||||||
@@ -440,7 +389,7 @@ def round_size(size_bytes):
|
|||||||
return size_bytes + bs - remainder
|
return size_bytes + bs - remainder
|
||||||
|
|
||||||
|
|
||||||
_ALLOWABLE_CH = string.ascii_letters + string.digits + '#+-.:=@_\/%'
|
_ALLOWABLE_CH = string.ascii_letters + string.digits + '#+.:=@_\/%'
|
||||||
_ALLOWABLE_CH_SET = set(_ALLOWABLE_CH)
|
_ALLOWABLE_CH_SET = set(_ALLOWABLE_CH)
|
||||||
|
|
||||||
_ALLOWABLE_VG_LV_CH = string.ascii_letters + string.digits + '.-_+'
|
_ALLOWABLE_VG_LV_CH = string.ascii_letters + string.digits + '.-_+'
|
||||||
@@ -533,115 +482,3 @@ def validate_tag(interface, tag):
|
|||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
interface, 'tag (%s) contains invalid character, allowable set(%s)'
|
interface, 'tag (%s) contains invalid character, allowable set(%s)'
|
||||||
% (tag, _ALLOWABLE_TAG_CH))
|
% (tag, _ALLOWABLE_TAG_CH))
|
||||||
|
|
||||||
|
|
||||||
def add_no_notify(cmdline):
|
|
||||||
"""
|
|
||||||
Given a command line to execute we will see if `--config` is present, if it
|
|
||||||
is we will add the global/notify_dbus=0 to it, otherwise we will append it
|
|
||||||
to the end of the list.
|
|
||||||
:param: cmdline: The command line to inspect
|
|
||||||
:type: cmdline: list
|
|
||||||
:return: cmdline with notify_dbus config option present
|
|
||||||
:rtype: list
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Only after we have seen an external event will be disable lvm from sending
|
|
||||||
# us one when we call lvm
|
|
||||||
if cfg.got_external_event:
|
|
||||||
if 'help' in cmdline:
|
|
||||||
return cmdline
|
|
||||||
|
|
||||||
if '--config' in cmdline:
|
|
||||||
for i, arg in enumerate(cmdline):
|
|
||||||
if arg == '--config':
|
|
||||||
if len(cmdline) <= i+1:
|
|
||||||
raise dbus.exceptions.DBusException("Missing value for --config option.")
|
|
||||||
cmdline[i+1] += " global/notify_dbus=0"
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
cmdline.extend(['--config', 'global/notify_dbus=0'])
|
|
||||||
return cmdline
|
|
||||||
|
|
||||||
|
|
||||||
# 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_handler(call_back, parameters):
|
|
||||||
params_str = ", ".join(str(x) for x in parameters)
|
|
||||||
log_debug('Main thread execution, callback = %s, parameters = (%s)' %
|
|
||||||
(str(call_back), params_str))
|
|
||||||
|
|
||||||
try:
|
|
||||||
if parameters:
|
|
||||||
call_back(*parameters)
|
|
||||||
else:
|
|
||||||
call_back()
|
|
||||||
except:
|
|
||||||
st = traceback.format_exc()
|
|
||||||
log_error("mt_async_call: exception (logged, not reported!) \n %s" % st)
|
|
||||||
|
|
||||||
|
|
||||||
# Execute the function on the main thread with the provided parameters, do
|
|
||||||
# not return *any* value or wait for the execution to complete!
|
|
||||||
def mt_async_call(function_call_back, *parameters):
|
|
||||||
GLib.idle_add(_async_handler, function_call_back, parameters)
|
|
||||||
|
|
||||||
|
|
||||||
# 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):
|
|
||||||
# noinspection PyProtectedMember
|
|
||||||
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.exception = 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()
|
|
||||||
if self.exception:
|
|
||||||
raise self.exception
|
|
||||||
return self.rc
|
|
||||||
|
|
||||||
def _run(self):
|
|
||||||
try:
|
|
||||||
if self.args:
|
|
||||||
self.rc = self.f(*self.args)
|
|
||||||
else:
|
|
||||||
self.rc = self.f()
|
|
||||||
except BaseException as be:
|
|
||||||
self.exception = be
|
|
||||||
st = traceback.format_exc()
|
|
||||||
log_error("MThreadRunner: exception \n %s" % st)
|
|
||||||
log_error("Exception will be raised in calling thread!")
|
|
||||||
|
|
||||||
|
|
||||||
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,8 +19,7 @@ from .request import RequestEntry
|
|||||||
from .loader import common
|
from .loader import common
|
||||||
from .state import State
|
from .state import State
|
||||||
from . import background
|
from . import background
|
||||||
from .utils import round_size, mt_remove_dbus_objects
|
from .utils import round_size
|
||||||
from .job import JobState
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
@@ -67,7 +66,7 @@ class VgState(State):
|
|||||||
|
|
||||||
gen = utils.lv_object_path_method(lv_name, meta)
|
gen = utils.lv_object_path_method(lv_name, meta)
|
||||||
|
|
||||||
lv_path = cfg.om.get_object_path_by_uuid_lvm_id(
|
lv_path = cfg.om.get_object_path_by_lvm_id(
|
||||||
lv_uuid, full_name, gen)
|
lv_uuid, full_name, gen)
|
||||||
rc.append(lv_path)
|
rc.append(lv_path)
|
||||||
return dbus.Array(rc, signature='o')
|
return dbus.Array(rc, signature='o')
|
||||||
@@ -76,9 +75,9 @@ class VgState(State):
|
|||||||
rc = []
|
rc = []
|
||||||
for p in cfg.db.pvs_in_vg(self.Uuid):
|
for p in cfg.db.pvs_in_vg(self.Uuid):
|
||||||
(pv_name, pv_uuid) = p
|
(pv_name, pv_uuid) = p
|
||||||
rc.append(cfg.om.get_object_path_by_uuid_lvm_id(
|
rc.append(cfg.om.get_object_path_by_lvm_id(
|
||||||
pv_uuid, pv_name, pv_obj_path_generate))
|
pv_uuid, pv_name, pv_obj_path_generate))
|
||||||
return rc
|
return dbus.Array(rc, signature='o')
|
||||||
|
|
||||||
def __init__(self, Uuid, Name, Fmt,
|
def __init__(self, Uuid, Name, Fmt,
|
||||||
SizeBytes, FreeBytes, SysId, ExtentSizeBytes,
|
SizeBytes, FreeBytes, SysId, ExtentSizeBytes,
|
||||||
@@ -91,7 +90,7 @@ class VgState(State):
|
|||||||
|
|
||||||
def create_dbus_object(self, path):
|
def create_dbus_object(self, path):
|
||||||
if not path:
|
if not path:
|
||||||
path = cfg.om.get_object_path_by_uuid_lvm_id(
|
path = cfg.om.get_object_path_by_lvm_id(
|
||||||
self.Uuid, self.Name, vg_obj_path_generate)
|
self.Uuid, self.Name, vg_obj_path_generate)
|
||||||
return Vg(path, self)
|
return Vg(path, self)
|
||||||
|
|
||||||
@@ -145,10 +144,22 @@ class Vg(AutomatedProperties):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fetch_new_lv(vg_name, lv_name):
|
def fetch_new_lv(vg_name, lv_name):
|
||||||
return cfg.om.get_object_path_by_lvm_id("%s/%s" % (vg_name, lv_name))
|
full_name = "%s/%s" % (vg_name, lv_name)
|
||||||
|
|
||||||
|
cfg.load()
|
||||||
|
l = cfg.om.get_object_by_lvm_id(full_name)
|
||||||
|
created_lv = l.dbus_object_path()
|
||||||
|
|
||||||
|
return created_lv
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_execute(rc, out, err):
|
def _rename(uuid, vg_name, new_name, rename_options):
|
||||||
|
# Make sure we have a dbus object representing it
|
||||||
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
|
rc, out, err = cmdhandler.vg_rename(vg_name, new_name,
|
||||||
|
rename_options)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
cfg.load()
|
cfg.load()
|
||||||
else:
|
else:
|
||||||
@@ -156,24 +167,11 @@ class Vg(AutomatedProperties):
|
|||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
VG_INTERFACE,
|
VG_INTERFACE,
|
||||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
@staticmethod
|
|
||||||
def validate_dbus_object(vg_uuid, vg_name):
|
|
||||||
dbo = cfg.om.get_object_by_uuid_lvm_id(vg_uuid, vg_name)
|
|
||||||
if not dbo:
|
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
VG_INTERFACE,
|
VG_INTERFACE,
|
||||||
'VG with uuid %s and name %s not present!' %
|
'VG with uuid %s and name %s not present!' %
|
||||||
(vg_uuid, vg_name))
|
(uuid, vg_name))
|
||||||
return dbo
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _rename(uuid, vg_name, new_name, rename_options):
|
|
||||||
# Make sure we have a dbus object representing it
|
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
|
||||||
rc, out, err = cmdhandler.vg_rename(
|
|
||||||
vg_name, new_name, rename_options)
|
|
||||||
Vg.handle_execute(rc, out, err)
|
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -190,10 +188,31 @@ class Vg(AutomatedProperties):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _remove(uuid, vg_name, remove_options):
|
def _remove(uuid, vg_name, remove_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
# Remove the VG, if successful then remove from the model
|
# Remove the VG, if successful then remove from the model
|
||||||
rc, out, err = cmdhandler.vg_remove(vg_name, remove_options)
|
rc, out, err = cmdhandler.vg_remove(vg_name, remove_options)
|
||||||
Vg.handle_execute(rc, out, err)
|
|
||||||
|
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(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -208,9 +227,29 @@ class Vg(AutomatedProperties):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _change(uuid, vg_name, change_options):
|
def _change(uuid, vg_name, change_options):
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.vg_change(change_options, vg_name)
|
rc, out, err = cmdhandler.vg_change(change_options, vg_name)
|
||||||
Vg.handle_execute(rc, out, err)
|
|
||||||
|
# To use an example with d-feet (Method input)
|
||||||
|
# {"activate": __import__('gi.repository.GLib', globals(),
|
||||||
|
# locals(), ['Variant']).Variant("s", "n")}
|
||||||
|
|
||||||
|
if rc == 0:
|
||||||
|
dbo.refresh()
|
||||||
|
|
||||||
|
if (('activate' in change_options) or ('-a' in change_options)):
|
||||||
|
cfg.load()
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
# TODO: This should be broken into a number of different methods
|
# TODO: This should be broken into a number of different methods
|
||||||
@@ -231,8 +270,9 @@ class Vg(AutomatedProperties):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _reduce(uuid, vg_name, missing, pv_object_paths, reduce_options):
|
def _reduce(uuid, vg_name, missing, pv_object_paths, reduce_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
pv_devices = []
|
pv_devices = []
|
||||||
|
|
||||||
# If pv_object_paths is not empty, then get the device paths
|
# If pv_object_paths is not empty, then get the device paths
|
||||||
@@ -248,7 +288,16 @@ class Vg(AutomatedProperties):
|
|||||||
|
|
||||||
rc, out, err = cmdhandler.vg_reduce(vg_name, missing, pv_devices,
|
rc, out, err = cmdhandler.vg_reduce(vg_name, missing, pv_devices,
|
||||||
reduce_options)
|
reduce_options)
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
cfg.load()
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE, 'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -265,8 +314,9 @@ class Vg(AutomatedProperties):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _extend(uuid, vg_name, pv_object_paths, extend_options):
|
def _extend(uuid, vg_name, pv_object_paths, extend_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
extend_devices = []
|
extend_devices = []
|
||||||
|
|
||||||
for i in pv_object_paths:
|
for i in pv_object_paths:
|
||||||
@@ -280,11 +330,20 @@ class Vg(AutomatedProperties):
|
|||||||
if len(extend_devices):
|
if len(extend_devices):
|
||||||
rc, out, err = cmdhandler.vg_extend(vg_name, extend_devices,
|
rc, out, err = cmdhandler.vg_extend(vg_name, extend_devices,
|
||||||
extend_options)
|
extend_options)
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
cfg.load()
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
else:
|
else:
|
||||||
raise dbus.exceptions.DBusException(
|
raise dbus.exceptions.DBusException(
|
||||||
VG_INTERFACE, 'No pv_object_paths provided!')
|
VG_INTERFACE, 'No pv_object_paths provided!')
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
return '/'
|
return '/'
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
@@ -301,29 +360,21 @@ class Vg(AutomatedProperties):
|
|||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
in_signature='o(tt)a(ott)ia{sv}',
|
in_signature='o(tt)a(ott)ia{sv}',
|
||||||
out_signature='o',
|
out_signature='o')
|
||||||
async_callbacks=('cb', 'cbe'))
|
|
||||||
def Move(self, pv_src_obj, pv_source_range, pv_dests_and_ranges,
|
def Move(self, pv_src_obj, pv_source_range, pv_dests_and_ranges,
|
||||||
tmo, move_options, cb, cbe):
|
tmo, move_options):
|
||||||
|
return background.move(
|
||||||
job_state = JobState()
|
VG_INTERFACE, None, pv_src_obj, pv_source_range,
|
||||||
|
pv_dests_and_ranges, move_options, tmo)
|
||||||
r = RequestEntry(
|
|
||||||
tmo, background.move,
|
|
||||||
(VG_INTERFACE, None, pv_src_obj, pv_source_range,
|
|
||||||
pv_dests_and_ranges, move_options, job_state), cb, cbe, False,
|
|
||||||
job_state)
|
|
||||||
|
|
||||||
cfg.worker_q.put(r)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _lv_create(uuid, vg_name, name, size_bytes, pv_dests_and_ranges,
|
def _lv_create(uuid, vg_name, name, size_bytes, pv_dests_and_ranges,
|
||||||
create_options):
|
create_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
pv_dests = []
|
pv_dests = []
|
||||||
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
if dbo:
|
||||||
|
|
||||||
if len(pv_dests_and_ranges):
|
if len(pv_dests_and_ranges):
|
||||||
for pr in pv_dests_and_ranges:
|
for pr in pv_dests_and_ranges:
|
||||||
pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
|
pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
|
||||||
@@ -337,8 +388,17 @@ class Vg(AutomatedProperties):
|
|||||||
rc, out, err = cmdhandler.vg_lv_create(
|
rc, out, err = cmdhandler.vg_lv_create(
|
||||||
vg_name, create_options, name, size_bytes, pv_dests)
|
vg_name, create_options, name, size_bytes, pv_dests)
|
||||||
|
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
return Vg.fetch_new_lv(vg_name, name)
|
return Vg.fetch_new_lv(vg_name, name)
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -374,13 +434,25 @@ class Vg(AutomatedProperties):
|
|||||||
def _lv_create_linear(uuid, vg_name, name, size_bytes,
|
def _lv_create_linear(uuid, vg_name, name, size_bytes,
|
||||||
thin_pool, create_options):
|
thin_pool, create_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.vg_lv_create_linear(
|
rc, out, err = cmdhandler.vg_lv_create_linear(
|
||||||
vg_name, create_options, name, size_bytes, thin_pool)
|
vg_name, create_options, name, size_bytes, thin_pool)
|
||||||
|
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
return Vg.fetch_new_lv(vg_name, name)
|
created_lv = Vg.fetch_new_lv(vg_name, name)
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
|
|
||||||
|
return created_lv
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -400,12 +472,24 @@ class Vg(AutomatedProperties):
|
|||||||
def _lv_create_striped(uuid, vg_name, name, size_bytes, num_stripes,
|
def _lv_create_striped(uuid, vg_name, name, size_bytes, num_stripes,
|
||||||
stripe_size_kb, thin_pool, create_options):
|
stripe_size_kb, thin_pool, create_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.vg_lv_create_striped(
|
rc, out, err = cmdhandler.vg_lv_create_striped(
|
||||||
vg_name, create_options, name, size_bytes,
|
vg_name, create_options, name, size_bytes,
|
||||||
num_stripes, stripe_size_kb, thin_pool)
|
num_stripes, stripe_size_kb, thin_pool)
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
return Vg.fetch_new_lv(vg_name, name)
|
created_lv = Vg.fetch_new_lv(vg_name, name)
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE, 'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
|
|
||||||
|
return created_lv
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -428,11 +512,25 @@ class Vg(AutomatedProperties):
|
|||||||
def _lv_create_mirror(uuid, vg_name, name, size_bytes,
|
def _lv_create_mirror(uuid, vg_name, name, size_bytes,
|
||||||
num_copies, create_options):
|
num_copies, create_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.vg_lv_create_mirror(
|
rc, out, err = cmdhandler.vg_lv_create_mirror(
|
||||||
vg_name, create_options, name, size_bytes, num_copies)
|
vg_name, create_options, name, size_bytes, num_copies)
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
return Vg.fetch_new_lv(vg_name, name)
|
created_lv = Vg.fetch_new_lv(vg_name, name)
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
|
|
||||||
|
return created_lv
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -453,12 +551,26 @@ class Vg(AutomatedProperties):
|
|||||||
def _lv_create_raid(uuid, vg_name, name, raid_type, size_bytes,
|
def _lv_create_raid(uuid, vg_name, name, raid_type, size_bytes,
|
||||||
num_stripes, stripe_size_kb, create_options):
|
num_stripes, stripe_size_kb, create_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.vg_lv_create_raid(
|
rc, out, err = cmdhandler.vg_lv_create_raid(
|
||||||
vg_name, create_options, name, raid_type, size_bytes,
|
vg_name, create_options, name, raid_type, size_bytes,
|
||||||
num_stripes, stripe_size_kb)
|
num_stripes, stripe_size_kb)
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
return Vg.fetch_new_lv(vg_name, name)
|
created_lv = Vg.fetch_new_lv(vg_name, name)
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
|
|
||||||
|
return created_lv
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -479,27 +591,35 @@ class Vg(AutomatedProperties):
|
|||||||
def _create_pool(uuid, vg_name, meta_data_lv, data_lv,
|
def _create_pool(uuid, vg_name, meta_data_lv, data_lv,
|
||||||
create_options, create_method):
|
create_options, create_method):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
# Retrieve the full names for the metadata and data lv
|
# Retrieve the full names for the metadata and data lv
|
||||||
md = cfg.om.get_object_by_path(meta_data_lv)
|
md = cfg.om.get_object_by_path(meta_data_lv)
|
||||||
data = cfg.om.get_object_by_path(data_lv)
|
data = cfg.om.get_object_by_path(data_lv)
|
||||||
|
|
||||||
if md and data:
|
if dbo and md and data:
|
||||||
|
|
||||||
new_name = data.Name
|
new_name = data.Name
|
||||||
|
|
||||||
rc, out, err = create_method(
|
rc, out, err = create_method(
|
||||||
md.lv_full_name(), data.lv_full_name(), create_options)
|
md.lv_full_name(), data.lv_full_name(), create_options)
|
||||||
|
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
mt_remove_dbus_objects((md, data))
|
cfg.om.remove_object(md, emit_signal=True)
|
||||||
|
cfg.om.remove_object(data, emit_signal=True)
|
||||||
|
|
||||||
Vg.handle_execute(rc, out, err)
|
cache_pool_lv = Vg.fetch_new_lv(vg_name, new_name)
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg = ""
|
msg = ""
|
||||||
|
|
||||||
|
if not dbo:
|
||||||
|
msg += 'VG with uuid %s and name %s not present!' % \
|
||||||
|
(uuid, vg_name)
|
||||||
|
|
||||||
if not md:
|
if not md:
|
||||||
msg += 'Meta data LV with object path %s not present!' % \
|
msg += 'Meta data LV with object path %s not present!' % \
|
||||||
(meta_data_lv)
|
(meta_data_lv)
|
||||||
@@ -510,7 +630,7 @@ class Vg(AutomatedProperties):
|
|||||||
|
|
||||||
raise dbus.exceptions.DBusException(VG_INTERFACE, msg)
|
raise dbus.exceptions.DBusException(VG_INTERFACE, msg)
|
||||||
|
|
||||||
return Vg.fetch_new_lv(vg_name, new_name)
|
return cache_pool_lv
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -544,8 +664,9 @@ class Vg(AutomatedProperties):
|
|||||||
pv_devices = []
|
pv_devices = []
|
||||||
|
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
# Check for existence of pv object paths
|
# Check for existence of pv object paths
|
||||||
for p in pv_object_paths:
|
for p in pv_object_paths:
|
||||||
pv = cfg.om.get_object_by_path(p)
|
pv = cfg.om.get_object_by_path(p)
|
||||||
@@ -557,8 +678,19 @@ class Vg(AutomatedProperties):
|
|||||||
|
|
||||||
rc, out, err = cmdhandler.pv_tag(
|
rc, out, err = cmdhandler.pv_tag(
|
||||||
pv_devices, tags_add, tags_del, tag_options)
|
pv_devices, tags_add, tags_del, tag_options)
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
cfg.load()
|
||||||
return '/'
|
return '/'
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -596,12 +728,25 @@ class Vg(AutomatedProperties):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _vg_add_rm_tags(uuid, vg_name, tags_add, tags_del, tag_options):
|
def _vg_add_rm_tags(uuid, vg_name, tags_add, tags_del, tag_options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
|
|
||||||
rc, out, err = cmdhandler.vg_tag(
|
rc, out, err = cmdhandler.vg_tag(
|
||||||
vg_name, tags_add, tags_del, tag_options)
|
vg_name, tags_add, tags_del, tag_options)
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
dbo.refresh()
|
||||||
return '/'
|
return '/'
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -638,10 +783,23 @@ class Vg(AutomatedProperties):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _vg_change_set(uuid, vg_name, method, value, options):
|
def _vg_change_set(uuid, vg_name, method, value, options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = method(vg_name, value, options)
|
rc, out, err = method(vg_name, value, options)
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
dbo.refresh()
|
||||||
return '/'
|
return '/'
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -681,7 +839,9 @@ class Vg(AutomatedProperties):
|
|||||||
cfg.worker_q.put(r)
|
cfg.worker_q.put(r)
|
||||||
|
|
||||||
def _attribute(self, pos, ch):
|
def _attribute(self, pos, ch):
|
||||||
return dbus.Boolean(self.state.attr[pos] == ch)
|
if self.state.attr[pos] == ch:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -699,11 +859,23 @@ class Vg(AutomatedProperties):
|
|||||||
def _vg_activate_deactivate(uuid, vg_name, activate, control_flags,
|
def _vg_activate_deactivate(uuid, vg_name, activate, control_flags,
|
||||||
options):
|
options):
|
||||||
# Make sure we have a dbus object representing it
|
# Make sure we have a dbus object representing it
|
||||||
Vg.validate_dbus_object(uuid, vg_name)
|
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||||
|
|
||||||
|
if dbo:
|
||||||
rc, out, err = cmdhandler.activate_deactivate(
|
rc, out, err = cmdhandler.activate_deactivate(
|
||||||
'vgchange', vg_name, activate, control_flags, options)
|
'vgchange', vg_name, activate, control_flags, options)
|
||||||
Vg.handle_execute(rc, out, err)
|
if rc == 0:
|
||||||
|
cfg.load()
|
||||||
return '/'
|
return '/'
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||||
|
else:
|
||||||
|
raise dbus.exceptions.DBusException(
|
||||||
|
VG_INTERFACE,
|
||||||
|
'VG with uuid %s and name %s not present!' %
|
||||||
|
(uuid, vg_name))
|
||||||
|
|
||||||
@dbus.service.method(
|
@dbus.service.method(
|
||||||
dbus_interface=VG_INTERFACE,
|
dbus_interface=VG_INTERFACE,
|
||||||
@@ -735,11 +907,11 @@ class Vg(AutomatedProperties):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def Pvs(self):
|
def Pvs(self):
|
||||||
return dbus.Array(self.state.Pvs, signature='o')
|
return self.state.Pvs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Lvs(self):
|
def Lvs(self):
|
||||||
return dbus.Array(self.state.Lvs, signature='o')
|
return self.state.Lvs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lvm_id(self):
|
def lvm_id(self):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ top_srcdir = @top_srcdir@
|
|||||||
top_builddir = @top_builddir@
|
top_builddir = @top_builddir@
|
||||||
|
|
||||||
SOURCES = lvmetad-core.c
|
SOURCES = lvmetad-core.c
|
||||||
SOURCES2 = lvmetactl.c
|
SOURCES2 = testclient.c
|
||||||
|
|
||||||
TARGETS = lvmetad lvmetactl
|
TARGETS = lvmetad lvmetactl
|
||||||
|
|
||||||
@@ -28,19 +28,22 @@ CFLOW_TARGET = lvmetad
|
|||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
include $(top_builddir)/make.tmpl
|
||||||
|
|
||||||
CFLAGS_lvmetactl.o += $(EXTRA_EXEC_CFLAGS)
|
|
||||||
CFLAGS_lvmetad-core.o += $(EXTRA_EXEC_CFLAGS)
|
|
||||||
INCLUDES += -I$(top_srcdir)/libdaemon/server
|
INCLUDES += -I$(top_srcdir)/libdaemon/server
|
||||||
LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS)
|
LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper
|
||||||
LIBS += $(RT_LIBS) $(DAEMON_LIBS) -ldevmapper $(PTHREAD_LIBS)
|
|
||||||
|
LIBS += $(PTHREAD_LIBS)
|
||||||
|
|
||||||
|
LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS)
|
||||||
|
CLDFLAGS += -L$(top_builddir)/libdaemon/server
|
||||||
|
CFLAGS += $(EXTRA_EXEC_CFLAGS)
|
||||||
|
|
||||||
lvmetad: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
lvmetad: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) -ldaemonserver $(LIBS)
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS)
|
||||||
|
|
||||||
lvmetactl: lvmetactl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
lvmetactl: lvmetactl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmetactl.o $(LIBS)
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmetactl.o $(LVMLIBS)
|
||||||
|
|
||||||
CLEAN_TARGETS += lvmetactl.o
|
CLEAN_TARGETS += lvmetactl.o
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,11 @@ int main(int argc, char **argv)
|
|||||||
printf("lvmetactl dump\n");
|
printf("lvmetactl dump\n");
|
||||||
printf("lvmetactl pv_list\n");
|
printf("lvmetactl pv_list\n");
|
||||||
printf("lvmetactl vg_list\n");
|
printf("lvmetactl vg_list\n");
|
||||||
printf("lvmetactl get_global_info\n");
|
|
||||||
printf("lvmetactl vg_lookup_name <name>\n");
|
printf("lvmetactl vg_lookup_name <name>\n");
|
||||||
printf("lvmetactl vg_lookup_uuid <uuid>\n");
|
printf("lvmetactl vg_lookup_uuid <uuid>\n");
|
||||||
printf("lvmetactl pv_lookup_uuid <uuid>\n");
|
printf("lvmetactl pv_lookup_uuid <uuid>\n");
|
||||||
printf("lvmetactl set_global_invalid 0|1\n");
|
printf("lvmetactl set_global_invalid 0|1\n");
|
||||||
printf("lvmetactl set_global_disable 0|1\n");
|
printf("lvmetactl get_global_invalid\n");
|
||||||
printf("lvmetactl set_vg_version <uuid> <name> <version>\n");
|
printf("lvmetactl set_vg_version <uuid> <name> <version>\n");
|
||||||
printf("lvmetactl vg_lock_type <uuid>\n");
|
printf("lvmetactl vg_lock_type <uuid>\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -55,32 +54,18 @@ int main(int argc, char **argv)
|
|||||||
if (!strcmp(cmd, "dump")) {
|
if (!strcmp(cmd, "dump")) {
|
||||||
reply = daemon_send_simple(h, "dump",
|
reply = daemon_send_simple(h, "dump",
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
printf("%s\n", reply.buffer.mem);
|
printf("%s\n", reply.buffer.mem);
|
||||||
|
|
||||||
} else if (!strcmp(cmd, "pv_list")) {
|
} else if (!strcmp(cmd, "pv_list")) {
|
||||||
reply = daemon_send_simple(h, "pv_list",
|
reply = daemon_send_simple(h, "pv_list",
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
printf("%s\n", reply.buffer.mem);
|
printf("%s\n", reply.buffer.mem);
|
||||||
|
|
||||||
} else if (!strcmp(cmd, "vg_list")) {
|
} else if (!strcmp(cmd, "vg_list")) {
|
||||||
reply = daemon_send_simple(h, "vg_list",
|
reply = daemon_send_simple(h, "vg_list",
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
|
||||||
printf("%s\n", reply.buffer.mem);
|
|
||||||
|
|
||||||
} else if (!strcmp(cmd, "get_global_info")) {
|
|
||||||
reply = daemon_send_simple(h, "get_global_info",
|
|
||||||
"token = %s", "skip",
|
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
printf("%s\n", reply.buffer.mem);
|
printf("%s\n", reply.buffer.mem);
|
||||||
|
|
||||||
@@ -94,26 +79,14 @@ int main(int argc, char **argv)
|
|||||||
reply = daemon_send_simple(h, "set_global_info",
|
reply = daemon_send_simple(h, "set_global_info",
|
||||||
"global_invalid = " FMTd64, (int64_t) val,
|
"global_invalid = " FMTd64, (int64_t) val,
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
print_reply(reply);
|
print_reply(reply);
|
||||||
|
|
||||||
} else if (!strcmp(cmd, "set_global_disable")) {
|
} else if (!strcmp(cmd, "get_global_invalid")) {
|
||||||
if (argc < 3) {
|
reply = daemon_send_simple(h, "get_global_info",
|
||||||
printf("set_global_disable 0|1\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
val = atoi(argv[2]);
|
|
||||||
|
|
||||||
reply = daemon_send_simple(h, "set_global_info",
|
|
||||||
"global_disable = " FMTd64, (int64_t) val,
|
|
||||||
"disable_reason = %s", LVMETAD_DISABLE_REASON_DIRECT,
|
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
print_reply(reply);
|
printf("%s\n", reply.buffer.mem);
|
||||||
|
|
||||||
} else if (!strcmp(cmd, "set_vg_version")) {
|
} else if (!strcmp(cmd, "set_vg_version")) {
|
||||||
if (argc < 5) {
|
if (argc < 5) {
|
||||||
@@ -135,24 +108,18 @@ int main(int argc, char **argv)
|
|||||||
"name = %s", name,
|
"name = %s", name,
|
||||||
"version = " FMTd64, (int64_t) ver,
|
"version = " FMTd64, (int64_t) ver,
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
} else if (uuid) {
|
} else if (uuid) {
|
||||||
reply = daemon_send_simple(h, "set_vg_info",
|
reply = daemon_send_simple(h, "set_vg_info",
|
||||||
"uuid = %s", uuid,
|
"uuid = %s", uuid,
|
||||||
"version = " FMTd64, (int64_t) ver,
|
"version = " FMTd64, (int64_t) ver,
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
} else if (name) {
|
} else if (name) {
|
||||||
reply = daemon_send_simple(h, "set_vg_info",
|
reply = daemon_send_simple(h, "set_vg_info",
|
||||||
"name = %s", name,
|
"name = %s", name,
|
||||||
"version = " FMTd64, (int64_t) ver,
|
"version = " FMTd64, (int64_t) ver,
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
} else {
|
} else {
|
||||||
printf("name or uuid required\n");
|
printf("name or uuid required\n");
|
||||||
@@ -171,8 +138,6 @@ int main(int argc, char **argv)
|
|||||||
reply = daemon_send_simple(h, "vg_lookup",
|
reply = daemon_send_simple(h, "vg_lookup",
|
||||||
"name = %s", name,
|
"name = %s", name,
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
printf("%s\n", reply.buffer.mem);
|
printf("%s\n", reply.buffer.mem);
|
||||||
|
|
||||||
@@ -186,8 +151,6 @@ int main(int argc, char **argv)
|
|||||||
reply = daemon_send_simple(h, "vg_lookup",
|
reply = daemon_send_simple(h, "vg_lookup",
|
||||||
"uuid = %s", uuid,
|
"uuid = %s", uuid,
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
printf("%s\n", reply.buffer.mem);
|
printf("%s\n", reply.buffer.mem);
|
||||||
|
|
||||||
@@ -204,8 +167,6 @@ int main(int argc, char **argv)
|
|||||||
reply = daemon_send_simple(h, "vg_lookup",
|
reply = daemon_send_simple(h, "vg_lookup",
|
||||||
"uuid = %s", uuid,
|
"uuid = %s", uuid,
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
/* printf("%s\n", reply.buffer.mem); */
|
/* printf("%s\n", reply.buffer.mem); */
|
||||||
|
|
||||||
@@ -232,8 +193,6 @@ int main(int argc, char **argv)
|
|||||||
reply = daemon_send_simple(h, "pv_lookup",
|
reply = daemon_send_simple(h, "pv_lookup",
|
||||||
"uuid = %s", uuid,
|
"uuid = %s", uuid,
|
||||||
"token = %s", "skip",
|
"token = %s", "skip",
|
||||||
"pid = " FMTd64, (int64_t)getpid(),
|
|
||||||
"cmd = %s", "lvmetactl",
|
|
||||||
NULL);
|
NULL);
|
||||||
printf("%s\n", reply.buffer.mem);
|
printf("%s\n", reply.buffer.mem);
|
||||||
|
|
||||||
|
|||||||
@@ -19,14 +19,6 @@
|
|||||||
|
|
||||||
#define LVMETAD_SOCKET DEFAULT_RUN_DIR "/lvmetad.socket"
|
#define LVMETAD_SOCKET DEFAULT_RUN_DIR "/lvmetad.socket"
|
||||||
|
|
||||||
#define LVMETAD_TOKEN_UPDATE_IN_PROGRESS "update in progress"
|
|
||||||
|
|
||||||
#define LVMETAD_DISABLE_REASON_DIRECT "DIRECT"
|
|
||||||
#define LVMETAD_DISABLE_REASON_LVM1 "LVM1"
|
|
||||||
#define LVMETAD_DISABLE_REASON_DUPLICATES "DUPLICATES"
|
|
||||||
#define LVMETAD_DISABLE_REASON_VGRESTORE "VGRESTORE"
|
|
||||||
#define LVMETAD_DISABLE_REASON_REPAIR "REPAIR"
|
|
||||||
|
|
||||||
struct volume_group;
|
struct volume_group;
|
||||||
|
|
||||||
/* Different types of replies we may get from lvmetad. */
|
/* Different types of replies we may get from lvmetad. */
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -75,10 +75,7 @@ int scan(daemon_handle h, char *fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char uuid[64];
|
char uuid[64];
|
||||||
if (!id_write_format(dev->pvid, uuid, 64)) {
|
id_write_format(dev->pvid, uuid, 64);
|
||||||
fprintf(stderr, "[C] Failed to format PV UUID for %s", dev_name(dev));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fprintf(stderr, "[C] found PV: %s\n", uuid);
|
fprintf(stderr, "[C] found PV: %s\n", uuid);
|
||||||
struct lvmcache_info *info = (struct lvmcache_info *) label->info;
|
struct lvmcache_info *info = (struct lvmcache_info *) label->info;
|
||||||
struct physical_volume pv = { 0, };
|
struct physical_volume pv = { 0, };
|
||||||
|
|||||||
@@ -19,12 +19,10 @@ SOURCES = lvmlockd-core.c
|
|||||||
|
|
||||||
ifeq ("@BUILD_LOCKDSANLOCK@", "yes")
|
ifeq ("@BUILD_LOCKDSANLOCK@", "yes")
|
||||||
SOURCES += lvmlockd-sanlock.c
|
SOURCES += lvmlockd-sanlock.c
|
||||||
LOCK_LIBS += -lsanlock_client
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ("@BUILD_LOCKDDLM@", "yes")
|
ifeq ("@BUILD_LOCKDDLM@", "yes")
|
||||||
SOURCES += lvmlockd-dlm.c
|
SOURCES += lvmlockd-dlm.c
|
||||||
LOCK_LIBS += -ldlm_lt
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
TARGETS = lvmlockd lvmlockctl
|
TARGETS = lvmlockd lvmlockctl
|
||||||
@@ -33,17 +31,29 @@ TARGETS = lvmlockd lvmlockctl
|
|||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
include $(top_builddir)/make.tmpl
|
||||||
|
|
||||||
CFLAGS += $(EXTRA_EXEC_CFLAGS)
|
|
||||||
INCLUDES += -I$(top_srcdir)/libdaemon/server
|
INCLUDES += -I$(top_srcdir)/libdaemon/server
|
||||||
LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS)
|
LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper
|
||||||
LIBS += $(RT_LIBS) $(DAEMON_LIBS) -ldevmapper $(PTHREAD_LIBS)
|
|
||||||
|
LIBS += $(PTHREAD_LIBS)
|
||||||
|
|
||||||
|
ifeq ("@BUILD_LOCKDSANLOCK@", "yes")
|
||||||
|
LIBS += -lsanlock_client
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ("@BUILD_LOCKDDLM@", "yes")
|
||||||
|
LIBS += -ldlm_lt
|
||||||
|
endif
|
||||||
|
|
||||||
|
LDFLAGS += -L$(top_builddir)/libdaemon/server
|
||||||
|
CLDFLAGS += -L$(top_builddir)/libdaemon/server
|
||||||
|
|
||||||
lvmlockd: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
lvmlockd: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LOCK_LIBS) -ldaemonserver $(LIBS)
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS)
|
||||||
|
|
||||||
lvmlockctl: lvmlockctl.o $(top_builddir)/libdaemon/client/libdaemonclient.a
|
lvmlockctl: lvmlockctl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmlockctl.o $(LIBS)
|
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmlockctl.o $(LVMLIBS)
|
||||||
|
|
||||||
install_lvmlockd: lvmlockd
|
install_lvmlockd: lvmlockd
|
||||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ static void save_client_info(char *line)
|
|||||||
uint32_t client_id = 0;
|
uint32_t client_id = 0;
|
||||||
char name[MAX_NAME+1] = { 0 };
|
char name[MAX_NAME+1] = { 0 };
|
||||||
|
|
||||||
(void) sscanf(line, "info=client pid=%u fd=%d pi=%d id=%u name=%s",
|
sscanf(line, "info=client pid=%u fd=%d pi=%d id=%u name=%s",
|
||||||
&pid, &fd, &pi, &client_id, name);
|
&pid, &fd, &pi, &client_id, name);
|
||||||
|
|
||||||
clients[num_clients].client_id = client_id;
|
clients[num_clients].client_id = client_id;
|
||||||
@@ -110,7 +110,7 @@ static void format_info_ls(char *line)
|
|||||||
char lock_args[MAX_ARGS+1] = { 0 };
|
char lock_args[MAX_ARGS+1] = { 0 };
|
||||||
char lock_type[MAX_NAME+1] = { 0 };
|
char lock_type[MAX_NAME+1] = { 0 };
|
||||||
|
|
||||||
(void) sscanf(line, "info=ls ls_name=%s vg_name=%s vg_uuid=%s vg_sysid=%s vg_args=%s lm_type=%s",
|
sscanf(line, "info=ls ls_name=%s vg_name=%s vg_uuid=%s vg_sysid=%s vg_args=%s lm_type=%s",
|
||||||
ls_name, vg_name, vg_uuid, vg_sysid, lock_args, lock_type);
|
ls_name, vg_name, vg_uuid, vg_sysid, lock_args, lock_type);
|
||||||
|
|
||||||
if (!first_ls)
|
if (!first_ls)
|
||||||
@@ -131,7 +131,7 @@ static void format_info_ls_action(char *line)
|
|||||||
uint32_t pid = 0;
|
uint32_t pid = 0;
|
||||||
char cl_name[MAX_NAME+1] = { 0 };
|
char cl_name[MAX_NAME+1] = { 0 };
|
||||||
|
|
||||||
(void) sscanf(line, "info=ls_action client_id=%u %s %s op=%s",
|
sscanf(line, "info=ls_action client_id=%u %s %s op=%s",
|
||||||
&client_id, flags, version, op);
|
&client_id, flags, version, op);
|
||||||
|
|
||||||
find_client_info(client_id, &pid, cl_name);
|
find_client_info(client_id, &pid, cl_name);
|
||||||
@@ -147,7 +147,7 @@ static void format_info_r(char *line, char *r_name_out, char *r_type_out)
|
|||||||
char sh_count[MAX_NAME+1] = { 0 };
|
char sh_count[MAX_NAME+1] = { 0 };
|
||||||
uint32_t ver = 0;
|
uint32_t ver = 0;
|
||||||
|
|
||||||
(void) sscanf(line, "info=r name=%s type=%s mode=%s %s version=%u",
|
sscanf(line, "info=r name=%s type=%s mode=%s %s version=%u",
|
||||||
r_name, r_type, mode, sh_count, &ver);
|
r_name, r_type, mode, sh_count, &ver);
|
||||||
|
|
||||||
strcpy(r_name_out, r_name);
|
strcpy(r_name_out, r_name);
|
||||||
@@ -185,7 +185,7 @@ static void format_info_lk(char *line, char *r_name, char *r_type)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) sscanf(line, "info=lk mode=%s version=%u %s client_id=%u",
|
sscanf(line, "info=lk mode=%s version=%u %s client_id=%u",
|
||||||
mode, &ver, flags, &client_id);
|
mode, &ver, flags, &client_id);
|
||||||
|
|
||||||
find_client_info(client_id, &pid, cl_name);
|
find_client_info(client_id, &pid, cl_name);
|
||||||
@@ -221,7 +221,7 @@ static void format_info_r_action(char *line, char *r_name, char *r_type)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) sscanf(line, "info=r_action client_id=%u %s %s op=%s rt=%s mode=%s %s %s %s",
|
sscanf(line, "info=r_action client_id=%u %s %s op=%s rt=%s mode=%s %s %s %s",
|
||||||
&client_id, flags, version, op, rt, mode, lm, result, lm_rv);
|
&client_id, flags, version, op, rt, mode, lm, result, lm_rv);
|
||||||
|
|
||||||
find_client_info(client_id, &pid, cl_name);
|
find_client_info(client_id, &pid, cl_name);
|
||||||
@@ -379,7 +379,7 @@ static int setup_dump_socket(void)
|
|||||||
rv = bind(s, (struct sockaddr *) &dump_addr, dump_addrlen);
|
rv = bind(s, (struct sockaddr *) &dump_addr, dump_addrlen);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
rv = -errno;
|
rv = -errno;
|
||||||
if (close(s))
|
if (!close(s))
|
||||||
log_error("failed to close dump socket");
|
log_error("failed to close dump socket");
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,5 @@ static inline void lvmlockd_close(daemon_handle h)
|
|||||||
#define EVGKILLED 217 /* sanlock lost access to leases and VG is killed. */
|
#define EVGKILLED 217 /* sanlock lost access to leases and VG is killed. */
|
||||||
#define ELOCKIO 218 /* sanlock io errors during lock op, may be transient. */
|
#define ELOCKIO 218 /* sanlock io errors during lock op, may be transient. */
|
||||||
#define EREMOVED 219
|
#define EREMOVED 219
|
||||||
#define EDEVOPEN 220 /* sanlock failed to open lvmlock LV */
|
|
||||||
#define ELMERR 221
|
|
||||||
|
|
||||||
#endif /* _LVM_LVMLOCKD_CLIENT_H */
|
#endif /* _LVM_LVMLOCKD_CLIENT_H */
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
#include "lvm-version.h"
|
#include "lvm-version.h"
|
||||||
#include "lvmetad-client.h"
|
#include "lvmetad-client.h"
|
||||||
#include "lvmlockd-client.h"
|
#include "lvmlockd-client.h"
|
||||||
#include "dm-ioctl.h" /* for DM_UUID_LEN */
|
|
||||||
|
|
||||||
/* #include <assert.h> */
|
/* #include <assert.h> */
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -307,13 +306,7 @@ static const char *_syslog_num_to_name(int num)
|
|||||||
static uint64_t monotime(void)
|
static uint64_t monotime(void)
|
||||||
{
|
{
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
|
|
||||||
log_error("clock_gettime failed to get timestamp %s.",
|
|
||||||
strerror(errno));
|
|
||||||
ts.tv_sec = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ts.tv_sec;
|
return ts.tv_sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,7 +317,7 @@ static void log_save_line(int len, char *line,
|
|||||||
unsigned int w = *wrap;
|
unsigned int w = *wrap;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (len < (int) (LOG_DUMP_SIZE - p)) {
|
if (len < LOG_DUMP_SIZE - p) {
|
||||||
memcpy(log_buf + p, line, len);
|
memcpy(log_buf + p, line, len);
|
||||||
p += len;
|
p += len;
|
||||||
|
|
||||||
@@ -1014,7 +1007,6 @@ static daemon_reply send_lvmetad(const char *id, ...)
|
|||||||
daemon_reply reply;
|
daemon_reply reply;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
int retries = 0;
|
int retries = 0;
|
||||||
int err;
|
|
||||||
|
|
||||||
va_start(ap, id);
|
va_start(ap, id);
|
||||||
|
|
||||||
@@ -1024,28 +1016,20 @@ static daemon_reply send_lvmetad(const char *id, ...)
|
|||||||
*/
|
*/
|
||||||
pthread_mutex_lock(&lvmetad_mutex);
|
pthread_mutex_lock(&lvmetad_mutex);
|
||||||
retry:
|
retry:
|
||||||
if (!lvmetad_connected) {
|
|
||||||
lvmetad_handle = lvmetad_open(NULL);
|
|
||||||
if (lvmetad_handle.error || lvmetad_handle.socket_fd < 0) {
|
|
||||||
err = lvmetad_handle.error ?: lvmetad_handle.socket_fd;
|
|
||||||
pthread_mutex_unlock(&lvmetad_mutex);
|
|
||||||
log_error("lvmetad_open reconnect error %d", err);
|
|
||||||
memset(&reply, 0, sizeof(reply));
|
|
||||||
reply.error = err;
|
|
||||||
va_end(ap);
|
|
||||||
return reply;
|
|
||||||
} else {
|
|
||||||
log_debug("lvmetad reconnected");
|
|
||||||
lvmetad_connected = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reply = daemon_send_simple_v(lvmetad_handle, id, ap);
|
reply = daemon_send_simple_v(lvmetad_handle, id, ap);
|
||||||
|
|
||||||
/* lvmetad may have been restarted */
|
/* lvmetad may have been restarted */
|
||||||
if ((reply.error == ECONNRESET) && (retries < 2)) {
|
if ((reply.error == ECONNRESET) && (retries < 2)) {
|
||||||
daemon_close(lvmetad_handle);
|
daemon_close(lvmetad_handle);
|
||||||
lvmetad_connected = 0;
|
lvmetad_connected = 0;
|
||||||
|
|
||||||
|
lvmetad_handle = lvmetad_open(NULL);
|
||||||
|
if (lvmetad_handle.error || lvmetad_handle.socket_fd < 0) {
|
||||||
|
log_error("lvmetad_open reconnect error %d", lvmetad_handle.error);
|
||||||
|
} else {
|
||||||
|
log_debug("lvmetad reconnected");
|
||||||
|
lvmetad_connected = 1;
|
||||||
|
}
|
||||||
retries++;
|
retries++;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
@@ -1250,6 +1234,10 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
|
|||||||
rv = -EREMOVED;
|
rv = -EREMOVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!lvmetad_connected && inval_meta)
|
||||||
|
log_debug("S %s R %s res_lock no lvmetad connection to invalidate",
|
||||||
|
ls->name, r->name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* r is vglk: tell lvmetad to set the vg invalid
|
* r is vglk: tell lvmetad to set the vg invalid
|
||||||
* flag, and provide the new r_version. If lvmetad finds
|
* flag, and provide the new r_version. If lvmetad finds
|
||||||
@@ -1265,7 +1253,7 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
|
|||||||
* caches, and tell lvmetad to set global invalid to 0.
|
* caches, and tell lvmetad to set global invalid to 0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (inval_meta && (r->type == LD_RT_VG)) {
|
if (lvmetad_connected && inval_meta && (r->type == LD_RT_VG)) {
|
||||||
daemon_reply reply;
|
daemon_reply reply;
|
||||||
char *uuid;
|
char *uuid;
|
||||||
|
|
||||||
@@ -1289,7 +1277,7 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
|
|||||||
daemon_reply_destroy(reply);
|
daemon_reply_destroy(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inval_meta && (r->type == LD_RT_GL)) {
|
if (lvmetad_connected && inval_meta && (r->type == LD_RT_GL)) {
|
||||||
daemon_reply reply;
|
daemon_reply reply;
|
||||||
|
|
||||||
log_debug("S %s R %s res_lock set lvmetad global invalid",
|
log_debug("S %s R %s res_lock set lvmetad global invalid",
|
||||||
@@ -1389,11 +1377,12 @@ static int res_convert(struct lockspace *ls, struct resource *r,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rv = lm_convert(ls, r, act->mode, act, r_version);
|
rv = lm_convert(ls, r, act->mode, act, r_version);
|
||||||
|
if (rv < 0) {
|
||||||
log_debug("S %s R %s res_convert rv %d", ls->name, r->name, rv);
|
log_error("S %s R %s res_convert lm error %d", ls->name, r->name, rv);
|
||||||
|
|
||||||
if (rv < 0)
|
|
||||||
return rv;
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("S %s R %s res_convert lm done", ls->name, r->name);
|
||||||
|
|
||||||
if (lk->mode == LD_LK_EX && act->mode == LD_LK_SH) {
|
if (lk->mode == LD_LK_EX && act->mode == LD_LK_SH) {
|
||||||
r->sh_count = 1;
|
r->sh_count = 1;
|
||||||
@@ -1654,9 +1643,6 @@ static int res_able(struct lockspace *ls, struct resource *r,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rv = lm_able_gl_sanlock(ls, act->op == LD_OP_ENABLE);
|
rv = lm_able_gl_sanlock(ls, act->op == LD_OP_ENABLE);
|
||||||
|
|
||||||
if (!rv && (act->op == LD_OP_ENABLE))
|
|
||||||
gl_vg_removed = 0;
|
|
||||||
out:
|
out:
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@@ -2283,7 +2269,6 @@ static void *lockspace_thread_main(void *arg_in)
|
|||||||
struct action *act_op_free = NULL;
|
struct action *act_op_free = NULL;
|
||||||
struct list_head tmp_act;
|
struct list_head tmp_act;
|
||||||
struct list_head act_close;
|
struct list_head act_close;
|
||||||
char tmp_name[MAX_NAME+1];
|
|
||||||
int free_vg = 0;
|
int free_vg = 0;
|
||||||
int drop_vg = 0;
|
int drop_vg = 0;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
@@ -2630,10 +2615,8 @@ out_act:
|
|||||||
|
|
||||||
if (free_vg && ls->sanlock_gl_enabled && act_op_free) {
|
if (free_vg && ls->sanlock_gl_enabled && act_op_free) {
|
||||||
pthread_mutex_lock(&lockspaces_mutex);
|
pthread_mutex_lock(&lockspaces_mutex);
|
||||||
if (other_sanlock_vgs_exist(ls)) {
|
if (other_sanlock_vgs_exist(ls))
|
||||||
act_op_free->flags |= LD_AF_WARN_GL_REMOVED;
|
act_op_free->flags |= LD_AF_WARN_GL_REMOVED;
|
||||||
gl_vg_removed = 1;
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&lockspaces_mutex);
|
pthread_mutex_unlock(&lockspaces_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2651,16 +2634,6 @@ out_act:
|
|||||||
ls->drop_vg = drop_vg;
|
ls->drop_vg = drop_vg;
|
||||||
if (ls->lm_type == LD_LM_DLM && !strcmp(ls->name, gl_lsname_dlm))
|
if (ls->lm_type == LD_LM_DLM && !strcmp(ls->name, gl_lsname_dlm))
|
||||||
global_dlm_lockspace_exists = 0;
|
global_dlm_lockspace_exists = 0;
|
||||||
|
|
||||||
/*
|
|
||||||
* Avoid a name collision of the same lockspace is added again before
|
|
||||||
* this thread is cleaned up. We just set ls->name to a "junk" value
|
|
||||||
* for the short period until the struct is freed. We could make it
|
|
||||||
* blank or fill it with garbage, but instead set it to REM:<name>
|
|
||||||
* to make it easier to follow progress of freeing is via log_debug.
|
|
||||||
*/
|
|
||||||
dm_strncpy(tmp_name, ls->name, sizeof(tmp_name));
|
|
||||||
snprintf(ls->name, sizeof(ls->name), "REM:%s", tmp_name);
|
|
||||||
pthread_mutex_unlock(&lockspaces_mutex);
|
pthread_mutex_unlock(&lockspaces_mutex);
|
||||||
|
|
||||||
/* worker_thread will join this thread, and free the ls */
|
/* worker_thread will join this thread, and free the ls */
|
||||||
@@ -3140,8 +3113,6 @@ static int for_each_lockspace(int do_stop, int do_free, int do_force)
|
|||||||
|
|
||||||
/* FIXME: will free_vg ever not be set? */
|
/* FIXME: will free_vg ever not be set? */
|
||||||
|
|
||||||
log_debug("free ls %s", ls->name);
|
|
||||||
|
|
||||||
if (ls->free_vg) {
|
if (ls->free_vg) {
|
||||||
/* In future we may need to free ls->actions here */
|
/* In future we may need to free ls->actions here */
|
||||||
free_ls_resources(ls);
|
free_ls_resources(ls);
|
||||||
@@ -3309,6 +3280,7 @@ static int work_init_lv(struct action *act)
|
|||||||
lm_type = ls->lm_type;
|
lm_type = ls->lm_type;
|
||||||
memcpy(vg_args, ls->vg_args, MAX_ARGS);
|
memcpy(vg_args, ls->vg_args, MAX_ARGS);
|
||||||
free_offset = ls->free_lock_offset;
|
free_offset = ls->free_lock_offset;
|
||||||
|
ls->free_lock_offset = 0;
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&lockspaces_mutex);
|
pthread_mutex_unlock(&lockspaces_mutex);
|
||||||
|
|
||||||
@@ -3361,10 +3333,7 @@ static void *worker_thread_main(void *arg_in)
|
|||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
pthread_mutex_lock(&worker_mutex);
|
pthread_mutex_lock(&worker_mutex);
|
||||||
if (clock_gettime(CLOCK_REALTIME, &ts)) {
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
log_error("clock_gettime failed.");
|
|
||||||
ts.tv_sec = ts.tv_nsec = 0;
|
|
||||||
}
|
|
||||||
ts.tv_sec += delay_sec;
|
ts.tv_sec += delay_sec;
|
||||||
rv = 0;
|
rv = 0;
|
||||||
act = NULL;
|
act = NULL;
|
||||||
@@ -3396,11 +3365,6 @@ static void *worker_thread_main(void *arg_in)
|
|||||||
int run_sanlock = lm_is_running_sanlock();
|
int run_sanlock = lm_is_running_sanlock();
|
||||||
int run_dlm = lm_is_running_dlm();
|
int run_dlm = lm_is_running_dlm();
|
||||||
|
|
||||||
if (daemon_test) {
|
|
||||||
run_sanlock = gl_use_sanlock;
|
|
||||||
run_dlm = gl_use_dlm;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (run_sanlock && run_dlm)
|
if (run_sanlock && run_dlm)
|
||||||
act->result = -EXFULL;
|
act->result = -EXFULL;
|
||||||
else if (!run_sanlock && !run_dlm)
|
else if (!run_sanlock && !run_dlm)
|
||||||
@@ -3538,15 +3502,11 @@ static int setup_worker_thread(void)
|
|||||||
|
|
||||||
static void close_worker_thread(void)
|
static void close_worker_thread(void)
|
||||||
{
|
{
|
||||||
int perrno;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&worker_mutex);
|
pthread_mutex_lock(&worker_mutex);
|
||||||
worker_stop = 1;
|
worker_stop = 1;
|
||||||
pthread_cond_signal(&worker_cond);
|
pthread_cond_signal(&worker_cond);
|
||||||
pthread_mutex_unlock(&worker_mutex);
|
pthread_mutex_unlock(&worker_mutex);
|
||||||
|
pthread_join(worker_thread, NULL);
|
||||||
if ((perrno = pthread_join(worker_thread, NULL)))
|
|
||||||
log_error("pthread_join worker_thread error %d", perrno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* client_mutex is locked */
|
/* client_mutex is locked */
|
||||||
@@ -3675,24 +3635,14 @@ static int client_send_result(struct client *cl, struct action *act)
|
|||||||
if (!gl_lsname_dlm[0])
|
if (!gl_lsname_dlm[0])
|
||||||
strcat(result_flags, "NO_GL_LS,");
|
strcat(result_flags, "NO_GL_LS,");
|
||||||
} else {
|
} else {
|
||||||
int found_lm = 0;
|
strcat(result_flags, "NO_GL_LS,");
|
||||||
|
|
||||||
if (lm_support_dlm() && lm_is_running_dlm())
|
|
||||||
found_lm++;
|
|
||||||
if (lm_support_sanlock() && lm_is_running_sanlock())
|
|
||||||
found_lm++;
|
|
||||||
|
|
||||||
if (!found_lm)
|
|
||||||
strcat(result_flags, "NO_GL_LS,NO_LM");
|
|
||||||
else
|
|
||||||
strcat(result_flags, "NO_GL_LS");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (act->flags & LD_AF_DUP_GL_LS)
|
if (act->flags & LD_AF_DUP_GL_LS)
|
||||||
strcat(result_flags, "DUP_GL_LS,");
|
strcat(result_flags, "DUP_GL_LS,");
|
||||||
|
|
||||||
if ((act->flags & LD_AF_WARN_GL_REMOVED) || gl_vg_removed)
|
if (act->flags & LD_AF_WARN_GL_REMOVED)
|
||||||
strcat(result_flags, "WARN_GL_REMOVED,");
|
strcat(result_flags, "WARN_GL_REMOVED,");
|
||||||
|
|
||||||
if (act->op == LD_OP_INIT) {
|
if (act->op == LD_OP_INIT) {
|
||||||
@@ -3782,8 +3732,7 @@ static int client_send_result(struct client *cl, struct action *act)
|
|||||||
if (dump_fd >= 0) {
|
if (dump_fd >= 0) {
|
||||||
/* To avoid deadlock, send data here after the reply. */
|
/* To avoid deadlock, send data here after the reply. */
|
||||||
send_dump_buf(dump_fd, dump_len);
|
send_dump_buf(dump_fd, dump_len);
|
||||||
if (close(dump_fd))
|
close(dump_fd);
|
||||||
log_error("failed to close dump socket %d", dump_fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
@@ -3856,9 +3805,8 @@ static int add_lock_action(struct action *act)
|
|||||||
pthread_mutex_lock(&lockspaces_mutex);
|
pthread_mutex_lock(&lockspaces_mutex);
|
||||||
if (ls_name[0])
|
if (ls_name[0])
|
||||||
ls = find_lockspace_name(ls_name);
|
ls = find_lockspace_name(ls_name);
|
||||||
if (!ls) {
|
|
||||||
pthread_mutex_unlock(&lockspaces_mutex);
|
pthread_mutex_unlock(&lockspaces_mutex);
|
||||||
|
if (!ls) {
|
||||||
if (act->op == LD_OP_UPDATE && act->rt == LD_RT_VG) {
|
if (act->op == LD_OP_UPDATE && act->rt == LD_RT_VG) {
|
||||||
log_debug("lockspace \"%s\" not found ignored for vg update", ls_name);
|
log_debug("lockspace \"%s\" not found ignored for vg update", ls_name);
|
||||||
return -ENOLS;
|
return -ENOLS;
|
||||||
@@ -4452,7 +4400,7 @@ static void client_recv_action(struct client *cl)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
req.cft = config_tree_from_string_without_dup_node_check(req.buffer.mem);
|
req.cft = dm_config_from_string(req.buffer.mem);
|
||||||
if (!req.cft) {
|
if (!req.cft) {
|
||||||
log_error("client recv %u config_from_string error", cl->id);
|
log_error("client recv %u config_from_string error", cl->id);
|
||||||
buffer_destroy(&req.buffer);
|
buffer_destroy(&req.buffer);
|
||||||
@@ -4775,7 +4723,7 @@ static void *client_thread_main(void *arg_in)
|
|||||||
} else {
|
} else {
|
||||||
pthread_mutex_unlock(&cl->mutex);
|
pthread_mutex_unlock(&cl->mutex);
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
pthread_mutex_unlock(&client_mutex);
|
pthread_mutex_unlock(&client_mutex);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
@@ -4800,15 +4748,11 @@ static int setup_client_thread(void)
|
|||||||
|
|
||||||
static void close_client_thread(void)
|
static void close_client_thread(void)
|
||||||
{
|
{
|
||||||
int perrno;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&client_mutex);
|
pthread_mutex_lock(&client_mutex);
|
||||||
client_stop = 1;
|
client_stop = 1;
|
||||||
pthread_cond_signal(&client_cond);
|
pthread_cond_signal(&client_cond);
|
||||||
pthread_mutex_unlock(&client_mutex);
|
pthread_mutex_unlock(&client_mutex);
|
||||||
|
pthread_join(client_thread, NULL);
|
||||||
if ((perrno = pthread_join(client_thread, NULL)))
|
|
||||||
log_error("pthread_join client_thread error %d", perrno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4932,10 +4876,14 @@ static int get_lockd_vgs(struct list_head *vg_lockd)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (lv_cn = md_cn->child; lv_cn; lv_cn = lv_cn->sib) {
|
for (lv_cn = md_cn->child; lv_cn; lv_cn = lv_cn->sib) {
|
||||||
|
snprintf(find_str_path, PATH_MAX, "%s/lock_type", lv_cn->key);
|
||||||
|
lock_type = dm_config_find_str(lv_cn, find_str_path, NULL);
|
||||||
|
|
||||||
|
if (!lock_type)
|
||||||
|
continue;
|
||||||
|
|
||||||
snprintf(find_str_path, PATH_MAX, "%s/lock_args", lv_cn->key);
|
snprintf(find_str_path, PATH_MAX, "%s/lock_args", lv_cn->key);
|
||||||
lock_args = dm_config_find_str(lv_cn, find_str_path, NULL);
|
lock_args = dm_config_find_str(lv_cn, find_str_path, NULL);
|
||||||
if (!lock_args)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
snprintf(find_str_path, PATH_MAX, "%s/id", lv_cn->key);
|
snprintf(find_str_path, PATH_MAX, "%s/id", lv_cn->key);
|
||||||
lv_uuid = dm_config_find_str(lv_cn, find_str_path, NULL);
|
lv_uuid = dm_config_find_str(lv_cn, find_str_path, NULL);
|
||||||
@@ -4981,7 +4929,7 @@ out:
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char _dm_uuid[DM_UUID_LEN];
|
static char _dm_uuid[64];
|
||||||
|
|
||||||
static char *get_dm_uuid(char *dm_name)
|
static char *get_dm_uuid(char *dm_name)
|
||||||
{
|
{
|
||||||
@@ -5200,17 +5148,20 @@ static void adopt_locks(void)
|
|||||||
* Get list of lockspaces from lock managers.
|
* Get list of lockspaces from lock managers.
|
||||||
* Get list of VGs from lvmetad with a lockd type.
|
* Get list of VGs from lvmetad with a lockd type.
|
||||||
* Get list of active lockd type LVs from /dev.
|
* Get list of active lockd type LVs from /dev.
|
||||||
|
*
|
||||||
|
* ECONNREFUSED means the lock manager is not running.
|
||||||
|
* This is expected for at least one of them.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (lm_support_dlm() && lm_is_running_dlm()) {
|
if (lm_support_dlm()) {
|
||||||
rv = lm_get_lockspaces_dlm(&ls_found);
|
rv = lm_get_lockspaces_dlm(&ls_found);
|
||||||
if (rv < 0)
|
if ((rv < 0) && (rv != -ECONNREFUSED))
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lm_support_sanlock() && lm_is_running_sanlock()) {
|
if (lm_support_sanlock()) {
|
||||||
rv = lm_get_lockspaces_sanlock(&ls_found);
|
rv = lm_get_lockspaces_sanlock(&ls_found);
|
||||||
if (rv < 0)
|
if ((rv < 0) && (rv != -ECONNREFUSED))
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5287,7 +5238,7 @@ static void adopt_locks(void)
|
|||||||
list_for_each_entry_safe(ls1, l1safe, &ls_found, list) {
|
list_for_each_entry_safe(ls1, l1safe, &ls_found, list) {
|
||||||
|
|
||||||
/* The dlm global lockspace is special and doesn't match a VG. */
|
/* The dlm global lockspace is special and doesn't match a VG. */
|
||||||
if ((ls1->lm_type == LD_LM_DLM) && !strcmp(ls1->name, gl_lsname_dlm)) {
|
if (!strcmp(ls1->name, gl_lsname_dlm)) {
|
||||||
list_del(&ls1->list);
|
list_del(&ls1->list);
|
||||||
free(ls1);
|
free(ls1);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -508,7 +508,7 @@ lockrv:
|
|||||||
}
|
}
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
log_error("S %s R %s lock_dlm acquire error %d errno %d", ls->name, r->name, rv, errno);
|
log_error("S %s R %s lock_dlm acquire error %d errno %d", ls->name, r->name, rv, errno);
|
||||||
return -ELMERR;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rdd->vb) {
|
if (rdd->vb) {
|
||||||
@@ -581,7 +581,6 @@ int lm_convert_dlm(struct lockspace *ls, struct resource *r,
|
|||||||
}
|
}
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
log_error("S %s R %s convert_dlm error %d", ls->name, r->name, rv);
|
log_error("S %s R %s convert_dlm error %d", ls->name, r->name, rv);
|
||||||
rv = -ELMERR;
|
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@@ -655,7 +654,6 @@ int lm_unlock_dlm(struct lockspace *ls, struct resource *r,
|
|||||||
0, NULL, NULL, NULL);
|
0, NULL, NULL, NULL);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
log_error("S %s R %s unlock_dlm error %d", ls->name, r->name, rv);
|
log_error("S %s R %s unlock_dlm error %d", ls->name, r->name, rv);
|
||||||
rv = -ELMERR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
|
|||||||
@@ -320,7 +320,6 @@ static inline int list_empty(const struct list_head *head)
|
|||||||
EXTERN int gl_type_static;
|
EXTERN int gl_type_static;
|
||||||
EXTERN int gl_use_dlm;
|
EXTERN int gl_use_dlm;
|
||||||
EXTERN int gl_use_sanlock;
|
EXTERN int gl_use_sanlock;
|
||||||
EXTERN int gl_vg_removed;
|
|
||||||
EXTERN char gl_lsname_dlm[MAX_NAME+1];
|
EXTERN char gl_lsname_dlm[MAX_NAME+1];
|
||||||
EXTERN char gl_lsname_sanlock[MAX_NAME+1];
|
EXTERN char gl_lsname_sanlock[MAX_NAME+1];
|
||||||
EXTERN int global_dlm_lockspace_exists;
|
EXTERN int global_dlm_lockspace_exists;
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ int lm_data_size_sanlock(void)
|
|||||||
#define VG_LOCK_BEGIN UINT64_C(66)
|
#define VG_LOCK_BEGIN UINT64_C(66)
|
||||||
#define LV_LOCK_BEGIN UINT64_C(67)
|
#define LV_LOCK_BEGIN UINT64_C(67)
|
||||||
|
|
||||||
static uint64_t daemon_test_lv_count;
|
static unsigned int daemon_test_lv_count;
|
||||||
|
|
||||||
static int lock_lv_name_from_args(char *vg_args, char *lock_lv_name)
|
static int lock_lv_name_from_args(char *vg_args, char *lock_lv_name)
|
||||||
{
|
{
|
||||||
@@ -224,10 +224,7 @@ static int lock_lv_offset_from_args(char *lv_args, uint64_t *lock_lv_offset)
|
|||||||
if (rv < 0)
|
if (rv < 0)
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
*lock_lv_offset = strtoull(offset_str, NULL, 10);
|
*lock_lv_offset = strtoull(offset_str, NULL, 10);
|
||||||
if (errno)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,8 +276,8 @@ static int read_host_id_file(void)
|
|||||||
*sep = '\0';
|
*sep = '\0';
|
||||||
memset(key_str, 0, sizeof(key_str));
|
memset(key_str, 0, sizeof(key_str));
|
||||||
memset(val_str, 0, sizeof(val_str));
|
memset(val_str, 0, sizeof(val_str));
|
||||||
(void) sscanf(key, "%s", key_str);
|
sscanf(key, "%s", key_str);
|
||||||
(void) sscanf(val, "%s", val_str);
|
sscanf(val, "%s", val_str);
|
||||||
|
|
||||||
if (!strcmp(key_str, "host_id")) {
|
if (!strcmp(key_str, "host_id")) {
|
||||||
host_id = atoi(val_str);
|
host_id = atoi(val_str);
|
||||||
@@ -356,19 +353,12 @@ int lm_init_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_ar
|
|||||||
log_debug("sanlock daemon version %08x proto %08x",
|
log_debug("sanlock daemon version %08x proto %08x",
|
||||||
daemon_version, daemon_proto);
|
daemon_version, daemon_proto);
|
||||||
|
|
||||||
rv = sanlock_align(&disk);
|
align_size = sanlock_align(&disk);
|
||||||
if (rv <= 0) {
|
if (align_size <= 0) {
|
||||||
if (rv == -EACCES) {
|
log_error("S %s init_vg_san bad disk align size %d %s",
|
||||||
log_error("S %s init_vg_san sanlock error -EACCES: no permission to access %s",
|
ls_name, align_size, disk.path);
|
||||||
ls_name, disk.path);
|
|
||||||
return -EDEVOPEN;
|
|
||||||
} else {
|
|
||||||
log_error("S %s init_vg_san sanlock error %d trying to get align size of %s",
|
|
||||||
ls_name, rv, disk.path);
|
|
||||||
return -EARGS;
|
return -EARGS;
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
align_size = rv;
|
|
||||||
|
|
||||||
strncpy(ss.name, ls_name, SANLK_NAME_LEN);
|
strncpy(ss.name, ls_name, SANLK_NAME_LEN);
|
||||||
memcpy(ss.host_id_disk.path, disk.path, SANLK_PATH_LEN);
|
memcpy(ss.host_id_disk.path, disk.path, SANLK_PATH_LEN);
|
||||||
@@ -945,9 +935,7 @@ int lm_find_free_lock_sanlock(struct lockspace *ls, uint64_t *free_offset)
|
|||||||
struct lm_sanlock *lms = (struct lm_sanlock *)ls->lm_data;
|
struct lm_sanlock *lms = (struct lm_sanlock *)ls->lm_data;
|
||||||
struct sanlk_resourced rd;
|
struct sanlk_resourced rd;
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
uint64_t start_offset;
|
|
||||||
int rv;
|
int rv;
|
||||||
int round = 0;
|
|
||||||
|
|
||||||
if (daemon_test) {
|
if (daemon_test) {
|
||||||
*free_offset = (1048576 * LV_LOCK_BEGIN) + (1048576 * (daemon_test_lv_count + 1));
|
*free_offset = (1048576 * LV_LOCK_BEGIN) + (1048576 * (daemon_test_lv_count + 1));
|
||||||
@@ -960,22 +948,9 @@ int lm_find_free_lock_sanlock(struct lockspace *ls, uint64_t *free_offset)
|
|||||||
rd.rs.num_disks = 1;
|
rd.rs.num_disks = 1;
|
||||||
strncpy(rd.rs.disks[0].path, lms->ss.host_id_disk.path, SANLK_PATH_LEN-1);
|
strncpy(rd.rs.disks[0].path, lms->ss.host_id_disk.path, SANLK_PATH_LEN-1);
|
||||||
|
|
||||||
if (ls->free_lock_offset)
|
|
||||||
offset = ls->free_lock_offset;
|
|
||||||
else
|
|
||||||
offset = lms->align_size * LV_LOCK_BEGIN;
|
offset = lms->align_size * LV_LOCK_BEGIN;
|
||||||
|
|
||||||
start_offset = offset;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (offset >= start_offset && round) {
|
|
||||||
/* This indicates the all space are allocated. */
|
|
||||||
log_debug("S %s init_lv_san read back to start offset %llu",
|
|
||||||
ls->name, (unsigned long long)offset);
|
|
||||||
rv = -EMSGSIZE;
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
rd.rs.disks[0].offset = offset;
|
rd.rs.disks[0].offset = offset;
|
||||||
|
|
||||||
memset(rd.rs.name, 0, SANLK_NAME_LEN);
|
memset(rd.rs.name, 0, SANLK_NAME_LEN);
|
||||||
@@ -985,14 +960,7 @@ int lm_find_free_lock_sanlock(struct lockspace *ls, uint64_t *free_offset)
|
|||||||
/* This indicates the end of the device is reached. */
|
/* This indicates the end of the device is reached. */
|
||||||
log_debug("S %s find_free_lock_san read limit offset %llu",
|
log_debug("S %s find_free_lock_san read limit offset %llu",
|
||||||
ls->name, (unsigned long long)offset);
|
ls->name, (unsigned long long)offset);
|
||||||
|
return -EMSGSIZE;
|
||||||
/* remember the NO SPACE offset, if no free area left,
|
|
||||||
* search from this offset after extend */
|
|
||||||
*free_offset = offset;
|
|
||||||
|
|
||||||
offset = lms->align_size * LV_LOCK_BEGIN;
|
|
||||||
round = 1;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1448,24 +1416,20 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
|
|||||||
if (adopt)
|
if (adopt)
|
||||||
flags |= SANLK_ACQUIRE_ORPHAN_ONLY;
|
flags |= SANLK_ACQUIRE_ORPHAN_ONLY;
|
||||||
|
|
||||||
|
#ifdef SANLOCK_HAS_ACQUIRE_OWNER_NOWAIT
|
||||||
/*
|
/*
|
||||||
* Don't block waiting for a failed lease to expire since it causes
|
* Don't block waiting for a failed lease to expire since it causes
|
||||||
* sanlock_acquire to block for a long time, which would prevent this
|
* sanlock_acquire to block for a long time, which would prevent this
|
||||||
* thread from processing other lock requests.
|
* thread from processing other lock requests.
|
||||||
*/
|
*/
|
||||||
flags |= SANLK_ACQUIRE_OWNER_NOWAIT;
|
flags |= SANLK_ACQUIRE_OWNER_NOWAIT;
|
||||||
|
#endif
|
||||||
|
|
||||||
memset(&opt, 0, sizeof(opt));
|
memset(&opt, 0, sizeof(opt));
|
||||||
sprintf(opt.owner_name, "%s", "lvmlockd");
|
sprintf(opt.owner_name, "%s", "lvmlockd");
|
||||||
|
|
||||||
rv = sanlock_acquire(lms->sock, -1, flags, 1, &rs, &opt);
|
rv = sanlock_acquire(lms->sock, -1, flags, 1, &rs, &opt);
|
||||||
|
|
||||||
/*
|
|
||||||
* errors: translate the sanlock error number to an lvmlockd error.
|
|
||||||
* We don't want to return an sanlock-specific error number from
|
|
||||||
* this function to code that doesn't recognize sanlock error numbers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (rv == -EAGAIN) {
|
if (rv == -EAGAIN) {
|
||||||
/*
|
/*
|
||||||
* It appears that sanlock_acquire returns EAGAIN when we request
|
* It appears that sanlock_acquire returns EAGAIN when we request
|
||||||
@@ -1534,26 +1498,7 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
|
|||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rv == SANLK_AIO_TIMEOUT) {
|
#ifdef SANLOCK_HAS_ACQUIRE_OWNER_NOWAIT
|
||||||
/*
|
|
||||||
* sanlock got an i/o timeout when trying to acquire the
|
|
||||||
* lease on disk.
|
|
||||||
*/
|
|
||||||
log_debug("S %s R %s lock_san acquire mode %d rv %d", ls->name, r->name, ld_mode, rv);
|
|
||||||
*retry = 0;
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rv == SANLK_DBLOCK_LVER || rv == SANLK_DBLOCK_MBAL) {
|
|
||||||
/*
|
|
||||||
* There was contention with another host for the lease,
|
|
||||||
* and we lost.
|
|
||||||
*/
|
|
||||||
log_debug("S %s R %s lock_san acquire mode %d rv %d", ls->name, r->name, ld_mode, rv);
|
|
||||||
*retry = 0;
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rv == SANLK_ACQUIRE_OWNED_RETRY) {
|
if (rv == SANLK_ACQUIRE_OWNED_RETRY) {
|
||||||
/*
|
/*
|
||||||
* The lock is held by a failed host, and will eventually
|
* The lock is held by a failed host, and will eventually
|
||||||
@@ -1572,7 +1517,7 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
|
|||||||
*retry = 0;
|
*retry = 0;
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
log_error("S %s R %s lock_san acquire error %d",
|
log_error("S %s R %s lock_san acquire error %d",
|
||||||
ls->name, r->name, rv);
|
ls->name, r->name, rv);
|
||||||
@@ -1604,25 +1549,15 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
|
|||||||
if (rv == -ENOSPC)
|
if (rv == -ENOSPC)
|
||||||
rv = -ELOCKIO;
|
rv = -ELOCKIO;
|
||||||
|
|
||||||
/*
|
return rv;
|
||||||
* generic error number for sanlock errors that we are not
|
|
||||||
* catching above.
|
|
||||||
*/
|
|
||||||
return -ELMERR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* sanlock acquire success (rv 0)
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (rds->vb) {
|
if (rds->vb) {
|
||||||
rv = sanlock_get_lvb(0, rs, (char *)&vb, sizeof(vb));
|
rv = sanlock_get_lvb(0, rs, (char *)&vb, sizeof(vb));
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
log_error("S %s R %s lock_san get_lvb error %d", ls->name, r->name, rv);
|
log_error("S %s R %s lock_san get_lvb error %d", ls->name, r->name, rv);
|
||||||
memset(rds->vb, 0, sizeof(struct val_blk));
|
memset(rds->vb, 0, sizeof(struct val_blk));
|
||||||
memset(vb_out, 0, sizeof(struct val_blk));
|
memset(vb_out, 0, sizeof(struct val_blk));
|
||||||
/* the lock is still acquired, the vb values considered invalid */
|
|
||||||
rv = 0;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1675,7 +1610,6 @@ int lm_convert_sanlock(struct lockspace *ls, struct resource *r,
|
|||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
log_error("S %s R %s convert_san set_lvb error %d",
|
log_error("S %s R %s convert_san set_lvb error %d",
|
||||||
ls->name, r->name, rv);
|
ls->name, r->name, rv);
|
||||||
return -ELMERR;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1688,35 +1622,14 @@ int lm_convert_sanlock(struct lockspace *ls, struct resource *r,
|
|||||||
if (daemon_test)
|
if (daemon_test)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
|
||||||
* Don't block waiting for a failed lease to expire since it causes
|
|
||||||
* sanlock_convert to block for a long time, which would prevent this
|
|
||||||
* thread from processing other lock requests.
|
|
||||||
*
|
|
||||||
* FIXME: SANLK_CONVERT_OWNER_NOWAIT is the same as SANLK_ACQUIRE_OWNER_NOWAIT.
|
|
||||||
* Change to use the CONVERT define when the latest sanlock version has it.
|
|
||||||
*/
|
|
||||||
flags |= SANLK_ACQUIRE_OWNER_NOWAIT;
|
|
||||||
|
|
||||||
rv = sanlock_convert(lms->sock, -1, flags, rs);
|
rv = sanlock_convert(lms->sock, -1, flags, rs);
|
||||||
if (!rv)
|
if (rv == -EAGAIN) {
|
||||||
return 0;
|
/* FIXME: When could this happen? Should something different be done? */
|
||||||
|
log_error("S %s R %s convert_san EAGAIN", ls->name, r->name);
|
||||||
switch (rv) {
|
|
||||||
case -EAGAIN:
|
|
||||||
case SANLK_ACQUIRE_IDLIVE:
|
|
||||||
case SANLK_ACQUIRE_OWNED:
|
|
||||||
case SANLK_ACQUIRE_OWNED_RETRY:
|
|
||||||
case SANLK_ACQUIRE_OTHER:
|
|
||||||
case SANLK_AIO_TIMEOUT:
|
|
||||||
case SANLK_DBLOCK_LVER:
|
|
||||||
case SANLK_DBLOCK_MBAL:
|
|
||||||
/* expected errors from known/normal cases like lock contention or io timeouts */
|
|
||||||
log_debug("S %s R %s convert_san error %d", ls->name, r->name, rv);
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
default:
|
}
|
||||||
|
if (rv < 0) {
|
||||||
log_error("S %s R %s convert_san convert error %d", ls->name, r->name, rv);
|
log_error("S %s R %s convert_san convert error %d", ls->name, r->name, rv);
|
||||||
rv = -ELMERR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
@@ -1753,7 +1666,6 @@ static int release_rename(struct lockspace *ls, struct resource *r)
|
|||||||
rv = sanlock_release(lms->sock, -1, SANLK_REL_RENAME, 2, res_args);
|
rv = sanlock_release(lms->sock, -1, SANLK_REL_RENAME, 2, res_args);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
log_error("S %s R %s unlock_san release rename error %d", ls->name, r->name, rv);
|
log_error("S %s R %s unlock_san release rename error %d", ls->name, r->name, rv);
|
||||||
rv = -ELMERR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(res_args);
|
free(res_args);
|
||||||
@@ -1810,7 +1722,6 @@ int lm_unlock_sanlock(struct lockspace *ls, struct resource *r,
|
|||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
log_error("S %s R %s unlock_san set_lvb error %d",
|
log_error("S %s R %s unlock_san set_lvb error %d",
|
||||||
ls->name, r->name, rv);
|
ls->name, r->name, rv);
|
||||||
return -ELMERR;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1829,8 +1740,6 @@ int lm_unlock_sanlock(struct lockspace *ls, struct resource *r,
|
|||||||
|
|
||||||
if (rv == -EIO)
|
if (rv == -EIO)
|
||||||
rv = -ELOCKIO;
|
rv = -ELOCKIO;
|
||||||
else if (rv < 0)
|
|
||||||
rv = -ELMERR;
|
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,14 +27,18 @@ CFLOW_TARGET = lvmpolld
|
|||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
include $(top_builddir)/make.tmpl
|
||||||
|
|
||||||
CFLAGS += $(EXTRA_EXEC_CFLAGS)
|
|
||||||
INCLUDES += -I$(top_srcdir)/libdaemon/server
|
INCLUDES += -I$(top_srcdir)/libdaemon/server
|
||||||
LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS)
|
LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper
|
||||||
LIBS += $(DAEMON_LIBS) -ldaemonserver -ldevmapper $(PTHREAD_LIBS)
|
|
||||||
|
LIBS += $(PTHREAD_LIBS)
|
||||||
|
|
||||||
|
LDFLAGS += -L$(top_builddir)/libdaemon/server $(DAEMON_LDFLAGS)
|
||||||
|
CLDFLAGS += -L$(top_builddir)/libdaemon/server
|
||||||
|
CFLAGS += $(DAEMON_CFLAGS)
|
||||||
|
|
||||||
lvmpolld: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
lvmpolld: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \
|
||||||
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
$(top_builddir)/libdaemon/server/libdaemonserver.a
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS)
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS)
|
||||||
|
|
||||||
install_lvmpolld: lvmpolld
|
install_lvmpolld: lvmpolld
|
||||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
||||||
|
|||||||
@@ -19,12 +19,10 @@
|
|||||||
|
|
||||||
#define MIN_ARGV_SIZE 8
|
#define MIN_ARGV_SIZE 8
|
||||||
|
|
||||||
static const char *const polling_ops[] = {
|
static const char *const const polling_ops[] = { [PVMOVE] = LVMPD_REQ_PVMOVE,
|
||||||
[PVMOVE] = LVMPD_REQ_PVMOVE,
|
|
||||||
[CONVERT] = LVMPD_REQ_CONVERT,
|
[CONVERT] = LVMPD_REQ_CONVERT,
|
||||||
[MERGE] = LVMPD_REQ_MERGE,
|
[MERGE] = LVMPD_REQ_MERGE,
|
||||||
[MERGE_THIN] = LVMPD_REQ_MERGE_THIN
|
[MERGE_THIN] = LVMPD_REQ_MERGE_THIN };
|
||||||
};
|
|
||||||
|
|
||||||
const char *polling_op(enum poll_type type)
|
const char *polling_op(enum poll_type type)
|
||||||
{
|
{
|
||||||
@@ -33,7 +31,7 @@ const char *polling_op(enum poll_type type)
|
|||||||
|
|
||||||
static int add_to_cmd_arr(const char ***cmdargv, const char *str, unsigned *ind)
|
static int add_to_cmd_arr(const char ***cmdargv, const char *str, unsigned *ind)
|
||||||
{
|
{
|
||||||
const char **newargv;
|
const char **newargv = *cmdargv;
|
||||||
|
|
||||||
if (*ind && !(*ind % MIN_ARGV_SIZE)) {
|
if (*ind && !(*ind % MIN_ARGV_SIZE)) {
|
||||||
newargv = dm_realloc(*cmdargv, (*ind / MIN_ARGV_SIZE + 1) * MIN_ARGV_SIZE * sizeof(char *));
|
newargv = dm_realloc(*cmdargv, (*ind / MIN_ARGV_SIZE + 1) * MIN_ARGV_SIZE * sizeof(char *));
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
static const char LVM_SYSTEM_DIR[] = "LVM_SYSTEM_DIR=";
|
|
||||||
|
|
||||||
static char *_construct_full_lvname(const char *vgname, const char *lvname)
|
static char *_construct_full_lvname(const char *vgname, const char *lvname)
|
||||||
{
|
{
|
||||||
char *name;
|
char *name;
|
||||||
@@ -54,7 +52,7 @@ static char *_construct_lvm_system_dir_env(const char *sysdir)
|
|||||||
|
|
||||||
*env = '\0';
|
*env = '\0';
|
||||||
|
|
||||||
if (sysdir && dm_snprintf(env, l, "%s%s", LVM_SYSTEM_DIR, sysdir) < 0) {
|
if (sysdir && dm_snprintf(env, l, "LVM_SYSTEM_DIR=%s", sysdir) < 0) {
|
||||||
dm_free(env);
|
dm_free(env);
|
||||||
env = NULL;
|
env = NULL;
|
||||||
}
|
}
|
||||||
@@ -261,8 +259,8 @@ static void _pdlv_locked_dump(struct buffer *buff, const struct lvmpolld_lv *pdl
|
|||||||
buffer_append(buff, tmp);
|
buffer_append(buff, tmp);
|
||||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvm_command_interval=\"%s\"\n", pdlv->sinterval ?: "<undefined>") > 0)
|
if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvm_command_interval=\"%s\"\n", pdlv->sinterval ?: "<undefined>") > 0)
|
||||||
buffer_append(buff, tmp);
|
buffer_append(buff, tmp);
|
||||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\t%s\"%s\"\n", LVM_SYSTEM_DIR,
|
if (dm_snprintf(tmp, sizeof(tmp), "\t\tLVM_SYSTEM_DIR=\"%s\"\n",
|
||||||
(*pdlv->lvm_system_dir_env ? (pdlv->lvm_system_dir_env + (sizeof(LVM_SYSTEM_DIR) - 1)) : "<undefined>")) > 0)
|
(*pdlv->lvm_system_dir_env ? (pdlv->lvm_system_dir_env + strlen("LVM_SYSTEM_DIR=")) : "<undefined>")) > 0)
|
||||||
buffer_append(buff, tmp);
|
buffer_append(buff, tmp);
|
||||||
if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvm_command_pid=%d\n", pdlv->cmd_pid) > 0)
|
if (dm_snprintf(tmp, sizeof(tmp), "\t\tlvm_command_pid=%d\n", pdlv->cmd_pid) > 0)
|
||||||
buffer_append(buff, tmp);
|
buffer_append(buff, tmp);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Every bio that is mapped by the target is referred to the policy.
|
|||||||
The policy can return a simple HIT or MISS or issue a migration.
|
The policy can return a simple HIT or MISS or issue a migration.
|
||||||
|
|
||||||
Currently there's no way for the policy to issue background work,
|
Currently there's no way for the policy to issue background work,
|
||||||
e.g. to start writing back dirty blocks that are going to be evicted
|
e.g. to start writing back dirty blocks that are going to be evicte
|
||||||
soon.
|
soon.
|
||||||
|
|
||||||
Because we map bios, rather than requests it's easy for the policy
|
Because we map bios, rather than requests it's easy for the policy
|
||||||
@@ -25,77 +25,53 @@ trying to see when the io scheduler has let the ios run.
|
|||||||
Overview of supplied cache replacement policies
|
Overview of supplied cache replacement policies
|
||||||
===============================================
|
===============================================
|
||||||
|
|
||||||
multiqueue (mq)
|
multiqueue
|
||||||
---------------
|
----------
|
||||||
|
|
||||||
This policy is now an alias for smq (see below).
|
This policy is the default.
|
||||||
|
|
||||||
The following tunables are accepted, but have no effect:
|
The multiqueue policy has three sets of 16 queues: one set for entries
|
||||||
|
waiting for the cache and another two for those in the cache (a set for
|
||||||
|
clean entries and a set for dirty entries).
|
||||||
|
|
||||||
|
Cache entries in the queues are aged based on logical time. Entry into
|
||||||
|
the cache is based on variable thresholds and queue selection is based
|
||||||
|
on hit count on entry. The policy aims to take different cache miss
|
||||||
|
costs into account and to adjust to varying load patterns automatically.
|
||||||
|
|
||||||
|
Message and constructor argument pairs are:
|
||||||
'sequential_threshold <#nr_sequential_ios>'
|
'sequential_threshold <#nr_sequential_ios>'
|
||||||
'random_threshold <#nr_random_ios>'
|
'random_threshold <#nr_random_ios>'
|
||||||
'read_promote_adjustment <value>'
|
'read_promote_adjustment <value>'
|
||||||
'write_promote_adjustment <value>'
|
'write_promote_adjustment <value>'
|
||||||
'discard_promote_adjustment <value>'
|
'discard_promote_adjustment <value>'
|
||||||
|
|
||||||
Stochastic multiqueue (smq)
|
The sequential threshold indicates the number of contiguous I/Os
|
||||||
---------------------------
|
required before a stream is treated as sequential. Once a stream is
|
||||||
|
considered sequential it will bypass the cache. The random threshold
|
||||||
|
is the number of intervening non-contiguous I/Os that must be seen
|
||||||
|
before the stream is treated as random again.
|
||||||
|
|
||||||
This policy is the default.
|
The sequential and random thresholds default to 512 and 4 respectively.
|
||||||
|
|
||||||
The stochastic multi-queue (smq) policy addresses some of the problems
|
Large, sequential I/Os are probably better left on the origin device
|
||||||
with the multiqueue (mq) policy.
|
since spindles tend to have good sequential I/O bandwidth. The
|
||||||
|
io_tracker counts contiguous I/Os to try to spot when the I/O is in one
|
||||||
|
of these sequential modes. But there are use-cases for wanting to
|
||||||
|
promote sequential blocks to the cache (e.g. fast application startup).
|
||||||
|
If sequential threshold is set to 0 the sequential I/O detection is
|
||||||
|
disabled and sequential I/O will no longer implicitly bypass the cache.
|
||||||
|
Setting the random threshold to 0 does _not_ disable the random I/O
|
||||||
|
stream detection.
|
||||||
|
|
||||||
The smq policy (vs mq) offers the promise of less memory utilization,
|
Internally the mq policy determines a promotion threshold. If the hit
|
||||||
improved performance and increased adaptability in the face of changing
|
count of a block not in the cache goes above this threshold it gets
|
||||||
workloads. smq also does not have any cumbersome tuning knobs.
|
promoted to the cache. The read, write and discard promote adjustment
|
||||||
|
tunables allow you to tweak the promotion threshold by adding a small
|
||||||
Users may switch from "mq" to "smq" simply by appropriately reloading a
|
value based on the io type. They default to 4, 8 and 1 respectively.
|
||||||
DM table that is using the cache target. Doing so will cause all of the
|
If you're trying to quickly warm a new cache device you may wish to
|
||||||
mq policy's hints to be dropped. Also, performance of the cache may
|
reduce these to encourage promotion. Remember to switch them back to
|
||||||
degrade slightly until smq recalculates the origin device's hotspots
|
their defaults after the cache fills though.
|
||||||
that should be cached.
|
|
||||||
|
|
||||||
Memory usage:
|
|
||||||
The mq policy used a lot of memory; 88 bytes per cache block on a 64
|
|
||||||
bit machine.
|
|
||||||
|
|
||||||
smq uses 28bit indexes to implement it's data structures rather than
|
|
||||||
pointers. It avoids storing an explicit hit count for each block. It
|
|
||||||
has a 'hotspot' queue, rather than a pre-cache, which uses a quarter of
|
|
||||||
the entries (each hotspot block covers a larger area than a single
|
|
||||||
cache block).
|
|
||||||
|
|
||||||
All this means smq uses ~25bytes per cache block. Still a lot of
|
|
||||||
memory, but a substantial improvement nontheless.
|
|
||||||
|
|
||||||
Level balancing:
|
|
||||||
mq placed entries in different levels of the multiqueue structures
|
|
||||||
based on their hit count (~ln(hit count)). This meant the bottom
|
|
||||||
levels generally had the most entries, and the top ones had very
|
|
||||||
few. Having unbalanced levels like this reduced the efficacy of the
|
|
||||||
multiqueue.
|
|
||||||
|
|
||||||
smq does not maintain a hit count, instead it swaps hit entries with
|
|
||||||
the least recently used entry from the level above. The overall
|
|
||||||
ordering being a side effect of this stochastic process. With this
|
|
||||||
scheme we can decide how many entries occupy each multiqueue level,
|
|
||||||
resulting in better promotion/demotion decisions.
|
|
||||||
|
|
||||||
Adaptability:
|
|
||||||
The mq policy maintained a hit count for each cache block. For a
|
|
||||||
different block to get promoted to the cache it's hit count has to
|
|
||||||
exceed the lowest currently in the cache. This meant it could take a
|
|
||||||
long time for the cache to adapt between varying IO patterns.
|
|
||||||
|
|
||||||
smq doesn't maintain hit counts, so a lot of this problem just goes
|
|
||||||
away. In addition it tracks performance of the hotspot queue, which
|
|
||||||
is used to decide which blocks to promote. If the hotspot queue is
|
|
||||||
performing badly then it starts moving entries more quickly between
|
|
||||||
levels. This lets it adapt to new IO patterns very quickly.
|
|
||||||
|
|
||||||
Performance:
|
|
||||||
Testing smq shows substantially better performance than mq.
|
|
||||||
|
|
||||||
cleaner
|
cleaner
|
||||||
-------
|
-------
|
||||||
|
|||||||
@@ -207,10 +207,6 @@ Optional feature arguments are:
|
|||||||
block, then the cache block is invalidated.
|
block, then the cache block is invalidated.
|
||||||
To enable passthrough mode the cache must be clean.
|
To enable passthrough mode the cache must be clean.
|
||||||
|
|
||||||
metadata2 : use version 2 of the metadata. This stores the dirty bits
|
|
||||||
in a separate btree, which improves speed of shutting
|
|
||||||
down the cache.
|
|
||||||
|
|
||||||
A policy called 'default' is always registered. This is an alias for
|
A policy called 'default' is always registered. This is an alias for
|
||||||
the policy we currently think is giving best all round performance.
|
the policy we currently think is giving best all round performance.
|
||||||
|
|
||||||
@@ -225,7 +221,6 @@ Status
|
|||||||
<#read hits> <#read misses> <#write hits> <#write misses>
|
<#read hits> <#read misses> <#write hits> <#write misses>
|
||||||
<#demotions> <#promotions> <#dirty> <#features> <features>*
|
<#demotions> <#promotions> <#dirty> <#features> <features>*
|
||||||
<#core args> <core args>* <policy name> <#policy args> <policy args>*
|
<#core args> <core args>* <policy name> <#policy args> <policy args>*
|
||||||
<cache metadata mode>
|
|
||||||
|
|
||||||
metadata block size : Fixed block size for each metadata block in
|
metadata block size : Fixed block size for each metadata block in
|
||||||
sectors
|
sectors
|
||||||
@@ -256,18 +251,8 @@ core args : Key/value pairs for tuning the core
|
|||||||
e.g. migration_threshold
|
e.g. migration_threshold
|
||||||
policy name : Name of the policy
|
policy name : Name of the policy
|
||||||
#policy args : Number of policy arguments to follow (must be even)
|
#policy args : Number of policy arguments to follow (must be even)
|
||||||
policy args : Key/value pairs e.g. sequential_threshold
|
policy args : Key/value pairs
|
||||||
cache metadata mode : ro if read-only, rw if read-write
|
e.g. sequential_threshold
|
||||||
In serious cases where even a read-only mode is deemed unsafe
|
|
||||||
no further I/O will be permitted and the status will just
|
|
||||||
contain the string 'Fail'. The userspace recovery tools
|
|
||||||
should then be used.
|
|
||||||
needs_check : 'needs_check' if set, '-' if not set
|
|
||||||
A metadata operation has failed, resulting in the needs_check
|
|
||||||
flag being set in the metadata's superblock. The metadata
|
|
||||||
device must be deactivated and checked/repaired before the
|
|
||||||
cache can be made fully operational again. '-' indicates
|
|
||||||
needs_check is not set.
|
|
||||||
|
|
||||||
Messages
|
Messages
|
||||||
--------
|
--------
|
||||||
@@ -290,7 +275,7 @@ message, which takes an arbitrary number of cblock ranges. Each cblock
|
|||||||
range's end value is "one past the end", meaning 5-10 expresses a range
|
range's end value is "one past the end", meaning 5-10 expresses a range
|
||||||
of values from 5 to 9. Each cblock must be expressed as a decimal
|
of values from 5 to 9. Each cblock must be expressed as a decimal
|
||||||
value, in the future a variant message that takes cblock ranges
|
value, in the future a variant message that takes cblock ranges
|
||||||
expressed in hexadecimal may be needed to better support efficient
|
expressed in hexidecimal may be needed to better support efficient
|
||||||
invalidation of larger caches. The cache must be in passthrough mode
|
invalidation of larger caches. The cache must be in passthrough mode
|
||||||
when invalidate_cblocks is used.
|
when invalidate_cblocks is used.
|
||||||
|
|
||||||
|
|||||||
@@ -11,57 +11,23 @@ Parameters: <cipher> <key> <iv_offset> <device path> \
|
|||||||
<offset> [<#opt_params> <opt_params>]
|
<offset> [<#opt_params> <opt_params>]
|
||||||
|
|
||||||
<cipher>
|
<cipher>
|
||||||
Encryption cipher, encryption mode and Initial Vector (IV) generator.
|
Encryption cipher and an optional IV generation mode.
|
||||||
|
(In format cipher[:keycount]-chainmode-ivmode[:ivopts]).
|
||||||
The cipher specifications format is:
|
|
||||||
cipher[:keycount]-chainmode-ivmode[:ivopts]
|
|
||||||
Examples:
|
Examples:
|
||||||
|
des
|
||||||
aes-cbc-essiv:sha256
|
aes-cbc-essiv:sha256
|
||||||
aes-xts-plain64
|
twofish-ecb
|
||||||
serpent-xts-plain64
|
|
||||||
|
|
||||||
Cipher format also supports direct specification with kernel crypt API
|
/proc/crypto contains supported crypto modes
|
||||||
format (selected by capi: prefix). The IV specification is the same
|
|
||||||
as for the first format type.
|
|
||||||
This format is mainly used for specification of authenticated modes.
|
|
||||||
|
|
||||||
The crypto API cipher specifications format is:
|
|
||||||
capi:cipher_api_spec-ivmode[:ivopts]
|
|
||||||
Examples:
|
|
||||||
capi:cbc(aes)-essiv:sha256
|
|
||||||
capi:xts(aes)-plain64
|
|
||||||
Examples of authenticated modes:
|
|
||||||
capi:gcm(aes)-random
|
|
||||||
capi:authenc(hmac(sha256),xts(aes))-random
|
|
||||||
capi:rfc7539(chacha20,poly1305)-random
|
|
||||||
|
|
||||||
The /proc/crypto contains a list of curently loaded crypto modes.
|
|
||||||
|
|
||||||
<key>
|
<key>
|
||||||
Key used for encryption. It is encoded either as a hexadecimal number
|
Key used for encryption. It is encoded as a hexadecimal number.
|
||||||
or it can be passed as <key_string> prefixed with single colon
|
|
||||||
character (':') for keys residing in kernel keyring service.
|
|
||||||
You can only use key sizes that are valid for the selected cipher
|
You can only use key sizes that are valid for the selected cipher
|
||||||
in combination with the selected iv mode.
|
in combination with the selected iv mode.
|
||||||
Note that for some iv modes the key string can contain additional
|
Note that for some iv modes the key string can contain additional
|
||||||
keys (for example IV seed) so the key contains more parts concatenated
|
keys (for example IV seed) so the key contains more parts concatenated
|
||||||
into a single string.
|
into a single string.
|
||||||
|
|
||||||
<key_string>
|
|
||||||
The kernel keyring key is identified by string in following format:
|
|
||||||
<key_size>:<key_type>:<key_description>.
|
|
||||||
|
|
||||||
<key_size>
|
|
||||||
The encryption key size in bytes. The kernel key payload size must match
|
|
||||||
the value passed in <key_size>.
|
|
||||||
|
|
||||||
<key_type>
|
|
||||||
Either 'logon' or 'user' kernel key type.
|
|
||||||
|
|
||||||
<key_description>
|
|
||||||
The kernel keyring key description crypt target should look for
|
|
||||||
when loading key of <key_type>.
|
|
||||||
|
|
||||||
<keycount>
|
<keycount>
|
||||||
Multi-key compatibility mode. You can define <keycount> keys and
|
Multi-key compatibility mode. You can define <keycount> keys and
|
||||||
then sectors are encrypted according to their offsets (sector 0 uses key0;
|
then sectors are encrypted according to their offsets (sector 0 uses key0;
|
||||||
@@ -110,32 +76,6 @@ submit_from_crypt_cpus
|
|||||||
thread because it benefits CFQ to have writes submitted using the
|
thread because it benefits CFQ to have writes submitted using the
|
||||||
same context.
|
same context.
|
||||||
|
|
||||||
integrity:<bytes>:<type>
|
|
||||||
The device requires additional <bytes> metadata per-sector stored
|
|
||||||
in per-bio integrity structure. This metadata must by provided
|
|
||||||
by underlying dm-integrity target.
|
|
||||||
|
|
||||||
The <type> can be "none" if metadata is used only for persistent IV.
|
|
||||||
|
|
||||||
For Authenticated Encryption with Additional Data (AEAD)
|
|
||||||
the <type> is "aead". An AEAD mode additionally calculates and verifies
|
|
||||||
integrity for the encrypted device. The additional space is then
|
|
||||||
used for storing authentication tag (and persistent IV if needed).
|
|
||||||
|
|
||||||
sector_size:<bytes>
|
|
||||||
Use <bytes> as the encryption unit instead of 512 bytes sectors.
|
|
||||||
This option can be in range 512 - 4096 bytes and must be power of two.
|
|
||||||
Virtual device will announce this size as a minimal IO and logical sector.
|
|
||||||
|
|
||||||
iv_large_sectors
|
|
||||||
IV generators will use sector number counted in <sector_size> units
|
|
||||||
instead of default 512 bytes sectors.
|
|
||||||
|
|
||||||
For example, if <sector_size> is 4096 bytes, plain64 IV for the second
|
|
||||||
sector will be 8 (without flag) and 1 if iv_large_sectors is present.
|
|
||||||
The <iv_offset> must be multiple of <sector_size> (in 512 bytes units)
|
|
||||||
if this flag is specified.
|
|
||||||
|
|
||||||
Example scripts
|
Example scripts
|
||||||
===============
|
===============
|
||||||
LUKS (Linux Unified Key Setup) is now the preferred way to set up disk
|
LUKS (Linux Unified Key Setup) is now the preferred way to set up disk
|
||||||
@@ -145,13 +85,7 @@ https://gitlab.com/cryptsetup/cryptsetup
|
|||||||
[[
|
[[
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Create a crypt device using dmsetup
|
# Create a crypt device using dmsetup
|
||||||
dmsetup create crypt1 --table "0 `blockdev --getsz $1` crypt aes-cbc-essiv:sha256 babebabebabebabebabebabebabebabe 0 $1 0"
|
dmsetup create crypt1 --table "0 `blockdev --getsize $1` crypt aes-cbc-essiv:sha256 babebabebabebabebabebabebabebabe 0 $1 0"
|
||||||
]]
|
|
||||||
|
|
||||||
[[
|
|
||||||
#!/bin/sh
|
|
||||||
# Create a crypt device using dmsetup when encryption key is stored in keyring service
|
|
||||||
dmsetup create crypt2 --table "0 `blockdev --getsize $1` crypt aes-cbc-essiv:sha256 :32:logon:my_prefix:my_key 0 $1 0"
|
|
||||||
]]
|
]]
|
||||||
|
|
||||||
[[
|
[[
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ Parameters:
|
|||||||
<device> <offset> <delay> [<write_device> <write_offset> <write_delay>]
|
<device> <offset> <delay> [<write_device> <write_offset> <write_delay>]
|
||||||
|
|
||||||
With separate write parameters, the first set is only used for reads.
|
With separate write parameters, the first set is only used for reads.
|
||||||
Offsets are specified in sectors.
|
|
||||||
Delays are specified in milliseconds.
|
Delays are specified in milliseconds.
|
||||||
|
|
||||||
Example scripts
|
Example scripts
|
||||||
@@ -16,12 +15,12 @@ Example scripts
|
|||||||
[[
|
[[
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Create device delaying rw operation for 500ms
|
# Create device delaying rw operation for 500ms
|
||||||
echo "0 `blockdev --getsz $1` delay $1 0 500" | dmsetup create delayed
|
echo "0 `blockdev --getsize $1` delay $1 0 500" | dmsetup create delayed
|
||||||
]]
|
]]
|
||||||
|
|
||||||
[[
|
[[
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Create device delaying only write operation for 500ms and
|
# Create device delaying only write operation for 500ms and
|
||||||
# splitting reads and writes to different devices $1 $2
|
# splitting reads and writes to different devices $1 $2
|
||||||
echo "0 `blockdev --getsz $1` delay $1 0 0 $2 0 500" | dmsetup create delayed
|
echo "0 `blockdev --getsize $1` delay $1 0 0 $2 0 500" | dmsetup create delayed
|
||||||
]]
|
]]
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ Optional feature parameters:
|
|||||||
<direction>: Either 'r' to corrupt reads or 'w' to corrupt writes.
|
<direction>: Either 'r' to corrupt reads or 'w' to corrupt writes.
|
||||||
'w' is incompatible with drop_writes.
|
'w' is incompatible with drop_writes.
|
||||||
<value>: The value (from 0-255) to write.
|
<value>: The value (from 0-255) to write.
|
||||||
<flags>: Perform the replacement only if bio->bi_opf has all the
|
<flags>: Perform the replacement only if bio->bi_rw has all the
|
||||||
selected flags set.
|
selected flags set.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|||||||
@@ -1,199 +0,0 @@
|
|||||||
The dm-integrity target emulates a block device that has additional
|
|
||||||
per-sector tags that can be used for storing integrity information.
|
|
||||||
|
|
||||||
A general problem with storing integrity tags with every sector is that
|
|
||||||
writing the sector and the integrity tag must be atomic - i.e. in case of
|
|
||||||
crash, either both sector and integrity tag or none of them is written.
|
|
||||||
|
|
||||||
To guarantee write atomicity, the dm-integrity target uses journal, it
|
|
||||||
writes sector data and integrity tags into a journal, commits the journal
|
|
||||||
and then copies the data and integrity tags to their respective location.
|
|
||||||
|
|
||||||
The dm-integrity target can be used with the dm-crypt target - in this
|
|
||||||
situation the dm-crypt target creates the integrity data and passes them
|
|
||||||
to the dm-integrity target via bio_integrity_payload attached to the bio.
|
|
||||||
In this mode, the dm-crypt and dm-integrity targets provide authenticated
|
|
||||||
disk encryption - if the attacker modifies the encrypted device, an I/O
|
|
||||||
error is returned instead of random data.
|
|
||||||
|
|
||||||
The dm-integrity target can also be used as a standalone target, in this
|
|
||||||
mode it calculates and verifies the integrity tag internally. In this
|
|
||||||
mode, the dm-integrity target can be used to detect silent data
|
|
||||||
corruption on the disk or in the I/O path.
|
|
||||||
|
|
||||||
|
|
||||||
When loading the target for the first time, the kernel driver will format
|
|
||||||
the device. But it will only format the device if the superblock contains
|
|
||||||
zeroes. If the superblock is neither valid nor zeroed, the dm-integrity
|
|
||||||
target can't be loaded.
|
|
||||||
|
|
||||||
To use the target for the first time:
|
|
||||||
1. overwrite the superblock with zeroes
|
|
||||||
2. load the dm-integrity target with one-sector size, the kernel driver
|
|
||||||
will format the device
|
|
||||||
3. unload the dm-integrity target
|
|
||||||
4. read the "provided_data_sectors" value from the superblock
|
|
||||||
5. load the dm-integrity target with the the target size
|
|
||||||
"provided_data_sectors"
|
|
||||||
6. if you want to use dm-integrity with dm-crypt, load the dm-crypt target
|
|
||||||
with the size "provided_data_sectors"
|
|
||||||
|
|
||||||
|
|
||||||
Target arguments:
|
|
||||||
|
|
||||||
1. the underlying block device
|
|
||||||
|
|
||||||
2. the number of reserved sector at the beginning of the device - the
|
|
||||||
dm-integrity won't read of write these sectors
|
|
||||||
|
|
||||||
3. the size of the integrity tag (if "-" is used, the size is taken from
|
|
||||||
the internal-hash algorithm)
|
|
||||||
|
|
||||||
4. mode:
|
|
||||||
D - direct writes (without journal) - in this mode, journaling is
|
|
||||||
not used and data sectors and integrity tags are written
|
|
||||||
separately. In case of crash, it is possible that the data
|
|
||||||
and integrity tag doesn't match.
|
|
||||||
J - journaled writes - data and integrity tags are written to the
|
|
||||||
journal and atomicity is guaranteed. In case of crash,
|
|
||||||
either both data and tag or none of them are written. The
|
|
||||||
journaled mode degrades write throughput twice because the
|
|
||||||
data have to be written twice.
|
|
||||||
R - recovery mode - in this mode, journal is not replayed,
|
|
||||||
checksums are not checked and writes to the device are not
|
|
||||||
allowed. This mode is useful for data recovery if the
|
|
||||||
device cannot be activated in any of the other standard
|
|
||||||
modes.
|
|
||||||
|
|
||||||
5. the number of additional arguments
|
|
||||||
|
|
||||||
Additional arguments:
|
|
||||||
|
|
||||||
journal_sectors:number
|
|
||||||
The size of journal, this argument is used only if formatting the
|
|
||||||
device. If the device is already formatted, the value from the
|
|
||||||
superblock is used.
|
|
||||||
|
|
||||||
interleave_sectors:number
|
|
||||||
The number of interleaved sectors. This values is rounded down to
|
|
||||||
a power of two. If the device is already formatted, the value from
|
|
||||||
the superblock is used.
|
|
||||||
|
|
||||||
buffer_sectors:number
|
|
||||||
The number of sectors in one buffer. The value is rounded down to
|
|
||||||
a power of two.
|
|
||||||
|
|
||||||
The tag area is accessed using buffers, the buffer size is
|
|
||||||
configurable. The large buffer size means that the I/O size will
|
|
||||||
be larger, but there could be less I/Os issued.
|
|
||||||
|
|
||||||
journal_watermark:number
|
|
||||||
The journal watermark in percents. When the size of the journal
|
|
||||||
exceeds this watermark, the thread that flushes the journal will
|
|
||||||
be started.
|
|
||||||
|
|
||||||
commit_time:number
|
|
||||||
Commit time in milliseconds. When this time passes, the journal is
|
|
||||||
written. The journal is also written immediatelly if the FLUSH
|
|
||||||
request is received.
|
|
||||||
|
|
||||||
internal_hash:algorithm(:key) (the key is optional)
|
|
||||||
Use internal hash or crc.
|
|
||||||
When this argument is used, the dm-integrity target won't accept
|
|
||||||
integrity tags from the upper target, but it will automatically
|
|
||||||
generate and verify the integrity tags.
|
|
||||||
|
|
||||||
You can use a crc algorithm (such as crc32), then integrity target
|
|
||||||
will protect the data against accidental corruption.
|
|
||||||
You can also use a hmac algorithm (for example
|
|
||||||
"hmac(sha256):0123456789abcdef"), in this mode it will provide
|
|
||||||
cryptographic authentication of the data without encryption.
|
|
||||||
|
|
||||||
When this argument is not used, the integrity tags are accepted
|
|
||||||
from an upper layer target, such as dm-crypt. The upper layer
|
|
||||||
target should check the validity of the integrity tags.
|
|
||||||
|
|
||||||
journal_crypt:algorithm(:key) (the key is optional)
|
|
||||||
Encrypt the journal using given algorithm to make sure that the
|
|
||||||
attacker can't read the journal. You can use a block cipher here
|
|
||||||
(such as "cbc(aes)") or a stream cipher (for example "chacha20",
|
|
||||||
"salsa20", "ctr(aes)" or "ecb(arc4)").
|
|
||||||
|
|
||||||
The journal contains history of last writes to the block device,
|
|
||||||
an attacker reading the journal could see the last sector nubmers
|
|
||||||
that were written. From the sector numbers, the attacker can infer
|
|
||||||
the size of files that were written. To protect against this
|
|
||||||
situation, you can encrypt the journal.
|
|
||||||
|
|
||||||
journal_mac:algorithm(:key) (the key is optional)
|
|
||||||
Protect sector numbers in the journal from accidental or malicious
|
|
||||||
modification. To protect against accidental modification, use a
|
|
||||||
crc algorithm, to protect against malicious modification, use a
|
|
||||||
hmac algorithm with a key.
|
|
||||||
|
|
||||||
This option is not needed when using internal-hash because in this
|
|
||||||
mode, the integrity of journal entries is checked when replaying
|
|
||||||
the journal. Thus, modified sector number would be detected at
|
|
||||||
this stage.
|
|
||||||
|
|
||||||
block_size:number
|
|
||||||
The size of a data block in bytes. The larger the block size the
|
|
||||||
less overhead there is for per-block integrity metadata.
|
|
||||||
Supported values are 512, 1024, 2048 and 4096 bytes. If not
|
|
||||||
specified the default block size is 512 bytes.
|
|
||||||
|
|
||||||
The journal mode (D/J), buffer_sectors, journal_watermark, commit_time can
|
|
||||||
be changed when reloading the target (load an inactive table and swap the
|
|
||||||
tables with suspend and resume). The other arguments should not be changed
|
|
||||||
when reloading the target because the layout of disk data depend on them
|
|
||||||
and the reloaded target would be non-functional.
|
|
||||||
|
|
||||||
|
|
||||||
The layout of the formatted block device:
|
|
||||||
* reserved sectors (they are not used by this target, they can be used for
|
|
||||||
storing LUKS metadata or for other purpose), the size of the reserved
|
|
||||||
area is specified in the target arguments
|
|
||||||
* superblock (4kiB)
|
|
||||||
* magic string - identifies that the device was formatted
|
|
||||||
* version
|
|
||||||
* log2(interleave sectors)
|
|
||||||
* integrity tag size
|
|
||||||
* the number of journal sections
|
|
||||||
* provided data sectors - the number of sectors that this target
|
|
||||||
provides (i.e. the size of the device minus the size of all
|
|
||||||
metadata and padding). The user of this target should not send
|
|
||||||
bios that access data beyond the "provided data sectors" limit.
|
|
||||||
* flags - a flag is set if journal_mac is used
|
|
||||||
* journal
|
|
||||||
The journal is divided into sections, each section contains:
|
|
||||||
* metadata area (4kiB), it contains journal entries
|
|
||||||
every journal entry contains:
|
|
||||||
* logical sector (specifies where the data and tag should
|
|
||||||
be written)
|
|
||||||
* last 8 bytes of data
|
|
||||||
* integrity tag (the size is specified in the superblock)
|
|
||||||
every metadata sector ends with
|
|
||||||
* mac (8-bytes), all the macs in 8 metadata sectors form a
|
|
||||||
64-byte value. It is used to store hmac of sector
|
|
||||||
numbers in the journal section, to protect against a
|
|
||||||
possibility that the attacker tampers with sector
|
|
||||||
numbers in the journal.
|
|
||||||
* commit id
|
|
||||||
* data area (the size is variable; it depends on how many journal
|
|
||||||
entries fit into the metadata area)
|
|
||||||
every sector in the data area contains:
|
|
||||||
* data (504 bytes of data, the last 8 bytes are stored in
|
|
||||||
the journal entry)
|
|
||||||
* commit id
|
|
||||||
To test if the whole journal section was written correctly, every
|
|
||||||
512-byte sector of the journal ends with 8-byte commit id. If the
|
|
||||||
commit id matches on all sectors in a journal section, then it is
|
|
||||||
assumed that the section was written correctly. If the commit id
|
|
||||||
doesn't match, the section was written partially and it should not
|
|
||||||
be replayed.
|
|
||||||
* one or more runs of interleaved tags and data. Each run contains:
|
|
||||||
* tag area - it contains integrity tags. There is one tag for each
|
|
||||||
sector in the data area
|
|
||||||
* data area - it contains data sectors. The number of data sectors
|
|
||||||
in one run must be a power of two. log2 of this value is stored
|
|
||||||
in the superblock.
|
|
||||||
@@ -16,15 +16,15 @@ Example scripts
|
|||||||
[[
|
[[
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Create an identity mapping for a device
|
# Create an identity mapping for a device
|
||||||
echo "0 `blockdev --getsz $1` linear $1 0" | dmsetup create identity
|
echo "0 `blockdev --getsize $1` linear $1 0" | dmsetup create identity
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
|
||||||
[[
|
[[
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Join 2 devices together
|
# Join 2 devices together
|
||||||
size1=`blockdev --getsz $1`
|
size1=`blockdev --getsize $1`
|
||||||
size2=`blockdev --getsz $2`
|
size2=`blockdev --getsize $2`
|
||||||
echo "0 $size1 linear $1 0
|
echo "0 $size1 linear $1 0
|
||||||
$size1 $size2 linear $2 0" | dmsetup create joined
|
$size1 $size2 linear $2 0" | dmsetup create joined
|
||||||
]]
|
]]
|
||||||
@@ -44,7 +44,7 @@ if (!defined($dev)) {
|
|||||||
die("Please specify a device.\n");
|
die("Please specify a device.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
my $dev_size = `blockdev --getsz $dev`;
|
my $dev_size = `blockdev --getsize $dev`;
|
||||||
my $extents = int($dev_size / $extent_size) -
|
my $extents = int($dev_size / $extent_size) -
|
||||||
(($dev_size % $extent_size) ? 1 : 0);
|
(($dev_size % $extent_size) ? 1 : 0);
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ Log Ordering
|
|||||||
|
|
||||||
We log things in order of completion once we are sure the write is no longer in
|
We log things in order of completion once we are sure the write is no longer in
|
||||||
cache. This means that normal WRITE requests are not actually logged until the
|
cache. This means that normal WRITE requests are not actually logged until the
|
||||||
next REQ_PREFLUSH request. This is to make it easier for userspace to replay
|
next REQ_FLUSH request. This is to make it easier for userspace to replay the
|
||||||
the log in a way that correlates to what is on disk and not what is in cache,
|
log in a way that correlates to what is on disk and not what is in cache, to
|
||||||
to make it easier to detect improper waiting/flushing.
|
make it easier to detect improper waiting/flushing.
|
||||||
|
|
||||||
This works by attaching all WRITE requests to a list once the write completes.
|
This works by attaching all WRITE requests to a list once the write completes.
|
||||||
Once we see a REQ_PREFLUSH request we splice this list onto the request and once
|
Once we see a REQ_FLUSH request we splice this list onto the request and once
|
||||||
the FLUSH request completes we log all of the WRITEs and then the FLUSH. Only
|
the FLUSH request completes we log all of the WRITEs and then the FLUSH. Only
|
||||||
completed WRITEs, at the time the REQ_PREFLUSH is issued, are added in order to
|
completed WRITEs, at the time the REQ_FLUSH is issued, are added in order to
|
||||||
simulate the worst case scenario with regard to power failures. Consider the
|
simulate the worst case scenario with regard to power failures. Consider the
|
||||||
following example (W means write, C means complete):
|
following example (W means write, C means complete):
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,8 @@ The target is named "raid" and it accepts the following parameters:
|
|||||||
<#raid_devs> <metadata_dev0> <dev0> [.. <metadata_devN> <devN>]
|
<#raid_devs> <metadata_dev0> <dev0> [.. <metadata_devN> <devN>]
|
||||||
|
|
||||||
<raid_type>:
|
<raid_type>:
|
||||||
raid0 RAID0 striping (no resilience)
|
|
||||||
raid1 RAID1 mirroring
|
raid1 RAID1 mirroring
|
||||||
raid4 RAID4 with dedicated last parity disk
|
raid4 RAID4 dedicated parity disk
|
||||||
raid5_n RAID5 with dedicated last parity disk supporting takeover
|
|
||||||
Same as raid4
|
|
||||||
-Transitory layout
|
|
||||||
raid5_la RAID5 left asymmetric
|
raid5_la RAID5 left asymmetric
|
||||||
- rotating parity 0 with data continuation
|
- rotating parity 0 with data continuation
|
||||||
raid5_ra RAID5 right asymmetric
|
raid5_ra RAID5 right asymmetric
|
||||||
@@ -34,19 +30,7 @@ The target is named "raid" and it accepts the following parameters:
|
|||||||
- rotating parity N (right-to-left) with data restart
|
- rotating parity N (right-to-left) with data restart
|
||||||
raid6_nc RAID6 N continue
|
raid6_nc RAID6 N continue
|
||||||
- rotating parity N (right-to-left) with data continuation
|
- rotating parity N (right-to-left) with data continuation
|
||||||
raid6_n_6 RAID6 with dedicate parity disks
|
|
||||||
- parity and Q-syndrome on the last 2 disks;
|
|
||||||
layout for takeover from/to raid4/raid5_n
|
|
||||||
raid6_la_6 Same as "raid_la" plus dedicated last Q-syndrome disk
|
|
||||||
- layout for takeover from raid5_la from/to raid6
|
|
||||||
raid6_ra_6 Same as "raid5_ra" dedicated last Q-syndrome disk
|
|
||||||
- layout for takeover from raid5_ra from/to raid6
|
|
||||||
raid6_ls_6 Same as "raid5_ls" dedicated last Q-syndrome disk
|
|
||||||
- layout for takeover from raid5_ls from/to raid6
|
|
||||||
raid6_rs_6 Same as "raid5_rs" dedicated last Q-syndrome disk
|
|
||||||
- layout for takeover from raid5_rs from/to raid6
|
|
||||||
raid10 Various RAID10 inspired algorithms chosen by additional params
|
raid10 Various RAID10 inspired algorithms chosen by additional params
|
||||||
(see raid10_format and raid10_copies below)
|
|
||||||
- RAID10: Striped Mirrors (aka 'Striping on top of mirrors')
|
- RAID10: Striped Mirrors (aka 'Striping on top of mirrors')
|
||||||
- RAID1E: Integrated Adjacent Stripe Mirroring
|
- RAID1E: Integrated Adjacent Stripe Mirroring
|
||||||
- RAID1E: Integrated Offset Stripe Mirroring
|
- RAID1E: Integrated Offset Stripe Mirroring
|
||||||
@@ -132,57 +116,10 @@ The target is named "raid" and it accepts the following parameters:
|
|||||||
Here we see layouts closely akin to 'RAID1E - Integrated
|
Here we see layouts closely akin to 'RAID1E - Integrated
|
||||||
Offset Stripe Mirroring'.
|
Offset Stripe Mirroring'.
|
||||||
|
|
||||||
[delta_disks <N>]
|
|
||||||
The delta_disks option value (-251 < N < +251) triggers
|
|
||||||
device removal (negative value) or device addition (positive
|
|
||||||
value) to any reshape supporting raid levels 4/5/6 and 10.
|
|
||||||
RAID levels 4/5/6 allow for addition of devices (metadata
|
|
||||||
and data device tuple), raid10_near and raid10_offset only
|
|
||||||
allow for device addition. raid10_far does not support any
|
|
||||||
reshaping at all.
|
|
||||||
A minimum of devices have to be kept to enforce resilience,
|
|
||||||
which is 3 devices for raid4/5 and 4 devices for raid6.
|
|
||||||
|
|
||||||
[data_offset <sectors>]
|
|
||||||
This option value defines the offset into each data device
|
|
||||||
where the data starts. This is used to provide out-of-place
|
|
||||||
reshaping space to avoid writing over data whilst
|
|
||||||
changing the layout of stripes, hence an interruption/crash
|
|
||||||
may happen at any time without the risk of losing data.
|
|
||||||
E.g. when adding devices to an existing raid set during
|
|
||||||
forward reshaping, the out-of-place space will be allocated
|
|
||||||
at the beginning of each raid device. The kernel raid4/5/6/10
|
|
||||||
MD personalities supporting such device addition will read the data from
|
|
||||||
the existing first stripes (those with smaller number of stripes)
|
|
||||||
starting at data_offset to fill up a new stripe with the larger
|
|
||||||
number of stripes, calculate the redundancy blocks (CRC/Q-syndrome)
|
|
||||||
and write that new stripe to offset 0. Same will be applied to all
|
|
||||||
N-1 other new stripes. This out-of-place scheme is used to change
|
|
||||||
the RAID type (i.e. the allocation algorithm) as well, e.g.
|
|
||||||
changing from raid5_ls to raid5_n.
|
|
||||||
|
|
||||||
[journal_dev <dev>]
|
|
||||||
This option adds a journal device to raid4/5/6 raid sets and
|
|
||||||
uses it to close the 'write hole' caused by the non-atomic updates
|
|
||||||
to the component devices which can cause data loss during recovery.
|
|
||||||
The journal device is used as writethrough thus causing writes to
|
|
||||||
be throttled versus non-journaled raid4/5/6 sets.
|
|
||||||
Takeover/reshape is not possible with a raid4/5/6 journal device;
|
|
||||||
it has to be deconfigured before requesting these.
|
|
||||||
|
|
||||||
[journal_mode <mode>]
|
|
||||||
This option sets the caching mode on journaled raid4/5/6 raid sets
|
|
||||||
(see 'journal_dev <dev>' above) to 'writethrough' or 'writeback'.
|
|
||||||
If 'writeback' is selected the journal device has to be resilient
|
|
||||||
and must not suffer from the 'write hole' problem itself (e.g. use
|
|
||||||
raid1 or raid10) to avoid a single point of failure.
|
|
||||||
|
|
||||||
<#raid_devs>: The number of devices composing the array.
|
<#raid_devs>: The number of devices composing the array.
|
||||||
Each device consists of two entries. The first is the device
|
Each device consists of two entries. The first is the device
|
||||||
containing the metadata (if any); the second is the one containing the
|
containing the metadata (if any); the second is the one containing the
|
||||||
data. A Maximum of 64 metadata/data device entries are supported
|
data.
|
||||||
up to target version 1.8.0.
|
|
||||||
1.9.0 supports up to 253 which is enforced by the used MD kernel runtime.
|
|
||||||
|
|
||||||
If a drive has failed or is missing at creation time, a '-' can be
|
If a drive has failed or is missing at creation time, a '-' can be
|
||||||
given for both the metadata and data drives for a given position.
|
given for both the metadata and data drives for a given position.
|
||||||
@@ -258,14 +195,6 @@ recovery. Here is a fuller description of the individual fields:
|
|||||||
in RAID1/10 or wrong parity values found in RAID4/5/6.
|
in RAID1/10 or wrong parity values found in RAID4/5/6.
|
||||||
This value is valid only after a "check" of the array
|
This value is valid only after a "check" of the array
|
||||||
is performed. A healthy array has a 'mismatch_cnt' of 0.
|
is performed. A healthy array has a 'mismatch_cnt' of 0.
|
||||||
<data_offset> The current data offset to the start of the user data on
|
|
||||||
each component device of a raid set (see the respective
|
|
||||||
raid parameter to support out-of-place reshaping).
|
|
||||||
<journal_char> 'A' - active write-through journal device.
|
|
||||||
'a' - active write-back journal device.
|
|
||||||
'D' - dead journal device.
|
|
||||||
'-' - no journal device.
|
|
||||||
|
|
||||||
|
|
||||||
Message Interface
|
Message Interface
|
||||||
-----------------
|
-----------------
|
||||||
@@ -278,37 +207,7 @@ include:
|
|||||||
"recover"- Initiate/continue a recover process.
|
"recover"- Initiate/continue a recover process.
|
||||||
"check" - Initiate a check (i.e. a "scrub") of the array.
|
"check" - Initiate a check (i.e. a "scrub") of the array.
|
||||||
"repair" - Initiate a repair of the array.
|
"repair" - Initiate a repair of the array.
|
||||||
|
"reshape"- Currently unsupported (-EINVAL).
|
||||||
|
|
||||||
Discard Support
|
|
||||||
---------------
|
|
||||||
The implementation of discard support among hardware vendors varies.
|
|
||||||
When a block is discarded, some storage devices will return zeroes when
|
|
||||||
the block is read. These devices set the 'discard_zeroes_data'
|
|
||||||
attribute. Other devices will return random data. Confusingly, some
|
|
||||||
devices that advertise 'discard_zeroes_data' will not reliably return
|
|
||||||
zeroes when discarded blocks are read! Since RAID 4/5/6 uses blocks
|
|
||||||
from a number of devices to calculate parity blocks and (for performance
|
|
||||||
reasons) relies on 'discard_zeroes_data' being reliable, it is important
|
|
||||||
that the devices be consistent. Blocks may be discarded in the middle
|
|
||||||
of a RAID 4/5/6 stripe and if subsequent read results are not
|
|
||||||
consistent, the parity blocks may be calculated differently at any time;
|
|
||||||
making the parity blocks useless for redundancy. It is important to
|
|
||||||
understand how your hardware behaves with discards if you are going to
|
|
||||||
enable discards with RAID 4/5/6.
|
|
||||||
|
|
||||||
Since the behavior of storage devices is unreliable in this respect,
|
|
||||||
even when reporting 'discard_zeroes_data', by default RAID 4/5/6
|
|
||||||
discard support is disabled -- this ensures data integrity at the
|
|
||||||
expense of losing some performance.
|
|
||||||
|
|
||||||
Storage devices that properly support 'discard_zeroes_data' are
|
|
||||||
increasingly whitelisted in the kernel and can thus be trusted.
|
|
||||||
|
|
||||||
For trusted devices, the following dm-raid module parameter can be set
|
|
||||||
to safely enable discard support for RAID 4/5/6:
|
|
||||||
'devices_handle_discards_safely'
|
|
||||||
|
|
||||||
|
|
||||||
Version History
|
Version History
|
||||||
---------------
|
---------------
|
||||||
@@ -325,21 +224,3 @@ Version History
|
|||||||
New status (STATUSTYPE_INFO) fields: sync_action and mismatch_cnt.
|
New status (STATUSTYPE_INFO) fields: sync_action and mismatch_cnt.
|
||||||
1.5.1 Add ability to restore transiently failed devices on resume.
|
1.5.1 Add ability to restore transiently failed devices on resume.
|
||||||
1.5.2 'mismatch_cnt' is zero unless [last_]sync_action is "check".
|
1.5.2 'mismatch_cnt' is zero unless [last_]sync_action is "check".
|
||||||
1.6.0 Add discard support (and devices_handle_discard_safely module param).
|
|
||||||
1.7.0 Add support for MD RAID0 mappings.
|
|
||||||
1.8.0 Explicitly check for compatible flags in the superblock metadata
|
|
||||||
and reject to start the raid set if any are set by a newer
|
|
||||||
target version, thus avoiding data corruption on a raid set
|
|
||||||
with a reshape in progress.
|
|
||||||
1.9.0 Add support for RAID level takeover/reshape/region size
|
|
||||||
and set size reduction.
|
|
||||||
1.9.1 Fix activation of existing RAID 4/10 mapped devices
|
|
||||||
1.9.2 Don't emit '- -' on the status table line in case the constructor
|
|
||||||
fails reading a superblock. Correctly emit 'maj:min1 maj:min2' and
|
|
||||||
'D' on the status line. If '- -' is passed into the constructor, emit
|
|
||||||
'- -' on the table line and '-' as the status line health character.
|
|
||||||
1.10.0 Add support for raid4/5/6 journal device
|
|
||||||
1.10.1 Fix data corruption on reshape request
|
|
||||||
1.11.0 Fix table line argument order
|
|
||||||
(wrong raid10_copies/raid10_format sequence)
|
|
||||||
1.11.1 Add raid4/5/6 journal write-back support via journal_mode option
|
|
||||||
|
|||||||
@@ -41,13 +41,9 @@ useless and be disabled, returning errors. So it is important to monitor
|
|||||||
the amount of free space and expand the <COW device> before it fills up.
|
the amount of free space and expand the <COW device> before it fills up.
|
||||||
|
|
||||||
<persistent?> is P (Persistent) or N (Not persistent - will not survive
|
<persistent?> is P (Persistent) or N (Not persistent - will not survive
|
||||||
after reboot). O (Overflow) can be added as a persistent store option
|
after reboot).
|
||||||
to allow userspace to advertise its support for seeing "Overflow" in the
|
The difference is that for transient snapshots less metadata must be
|
||||||
snapshot status. So supported store types are "P", "PO" and "N".
|
saved on disk - they can be kept in memory by the kernel.
|
||||||
|
|
||||||
The difference between persistent and transient is with transient
|
|
||||||
snapshots less metadata must be saved on disk - they can be kept in
|
|
||||||
memory by the kernel.
|
|
||||||
|
|
||||||
|
|
||||||
* snapshot-merge <origin> <COW device> <persistent> <chunksize>
|
* snapshot-merge <origin> <COW device> <persistent> <chunksize>
|
||||||
|
|||||||
@@ -13,14 +13,9 @@ the range specified.
|
|||||||
The I/O statistics counters for each step-sized area of a region are
|
The I/O statistics counters for each step-sized area of a region are
|
||||||
in the same format as /sys/block/*/stat or /proc/diskstats (see:
|
in the same format as /sys/block/*/stat or /proc/diskstats (see:
|
||||||
Documentation/iostats.txt). But two extra counters (12 and 13) are
|
Documentation/iostats.txt). But two extra counters (12 and 13) are
|
||||||
provided: total time spent reading and writing. When the histogram
|
provided: total time spent reading and writing in milliseconds. All
|
||||||
argument is used, the 14th parameter is reported that represents the
|
these counters may be accessed by sending the @stats_print message to
|
||||||
histogram of latencies. All these counters may be accessed by sending
|
the appropriate DM device via dmsetup.
|
||||||
the @stats_print message to the appropriate DM device via dmsetup.
|
|
||||||
|
|
||||||
The reported times are in milliseconds and the granularity depends on
|
|
||||||
the kernel ticks. When the option precise_timestamps is used, the
|
|
||||||
reported times are in nanoseconds.
|
|
||||||
|
|
||||||
Each region has a corresponding unique identifier, which we call a
|
Each region has a corresponding unique identifier, which we call a
|
||||||
region_id, that is assigned when the region is created. The region_id
|
region_id, that is assigned when the region is created. The region_id
|
||||||
@@ -38,9 +33,7 @@ memory is used by reading
|
|||||||
Messages
|
Messages
|
||||||
========
|
========
|
||||||
|
|
||||||
@stats_create <range> <step>
|
@stats_create <range> <step> [<program_id> [<aux_data>]]
|
||||||
[<number_of_optional_arguments> <optional_arguments>...]
|
|
||||||
[<program_id> [<aux_data>]]
|
|
||||||
|
|
||||||
Create a new region and return the region_id.
|
Create a new region and return the region_id.
|
||||||
|
|
||||||
@@ -55,29 +48,6 @@ Messages
|
|||||||
"/<number_of_areas>" - the range is subdivided into the specified
|
"/<number_of_areas>" - the range is subdivided into the specified
|
||||||
number of areas.
|
number of areas.
|
||||||
|
|
||||||
<number_of_optional_arguments>
|
|
||||||
The number of optional arguments
|
|
||||||
|
|
||||||
<optional_arguments>
|
|
||||||
The following optional arguments are supported
|
|
||||||
precise_timestamps - use precise timer with nanosecond resolution
|
|
||||||
instead of the "jiffies" variable. When this argument is
|
|
||||||
used, the resulting times are in nanoseconds instead of
|
|
||||||
milliseconds. Precise timestamps are a little bit slower
|
|
||||||
to obtain than jiffies-based timestamps.
|
|
||||||
histogram:n1,n2,n3,n4,... - collect histogram of latencies. The
|
|
||||||
numbers n1, n2, etc are times that represent the boundaries
|
|
||||||
of the histogram. If precise_timestamps is not used, the
|
|
||||||
times are in milliseconds, otherwise they are in
|
|
||||||
nanoseconds. For each range, the kernel will report the
|
|
||||||
number of requests that completed within this range. For
|
|
||||||
example, if we use "histogram:10,20,30", the kernel will
|
|
||||||
report four numbers a:b:c:d. a is the number of requests
|
|
||||||
that took 0-10 ms to complete, b is the number of requests
|
|
||||||
that took 10-20 ms to complete, c is the number of requests
|
|
||||||
that took 20-30 ms to complete and d is the number of
|
|
||||||
requests that took more than 30 ms to complete.
|
|
||||||
|
|
||||||
<program_id>
|
<program_id>
|
||||||
An optional parameter. A name that uniquely identifies
|
An optional parameter. A name that uniquely identifies
|
||||||
the userspace owner of the range. This groups ranges together
|
the userspace owner of the range. This groups ranges together
|
||||||
@@ -85,9 +55,6 @@ Messages
|
|||||||
created and ignore those created by others.
|
created and ignore those created by others.
|
||||||
The kernel returns this string back in the output of
|
The kernel returns this string back in the output of
|
||||||
@stats_list message, but it doesn't use it for anything else.
|
@stats_list message, but it doesn't use it for anything else.
|
||||||
If we omit the number of optional arguments, program id must not
|
|
||||||
be a number, otherwise it would be interpreted as the number of
|
|
||||||
optional arguments.
|
|
||||||
|
|
||||||
<aux_data>
|
<aux_data>
|
||||||
An optional parameter. A word that provides auxiliary data
|
An optional parameter. A word that provides auxiliary data
|
||||||
@@ -121,10 +88,6 @@ Messages
|
|||||||
|
|
||||||
Output format:
|
Output format:
|
||||||
<region_id>: <start_sector>+<length> <step> <program_id> <aux_data>
|
<region_id>: <start_sector>+<length> <step> <program_id> <aux_data>
|
||||||
precise_timestamps histogram:n1,n2,n3,...
|
|
||||||
|
|
||||||
The strings "precise_timestamps" and "histogram" are printed only
|
|
||||||
if they were specified when creating the region.
|
|
||||||
|
|
||||||
@stats_print <region_id> [<starting_line> <number_of_lines>]
|
@stats_print <region_id> [<starting_line> <number_of_lines>]
|
||||||
|
|
||||||
@@ -205,7 +168,7 @@ statistics on them:
|
|||||||
|
|
||||||
dmsetup message vol 0 @stats_create - /100
|
dmsetup message vol 0 @stats_create - /100
|
||||||
|
|
||||||
Set the auxiliary data string to "foo bar baz" (the escape for each
|
Set the auxillary data string to "foo bar baz" (the escape for each
|
||||||
space must also be escaped, otherwise the shell will consume them):
|
space must also be escaped, otherwise the shell will consume them):
|
||||||
|
|
||||||
dmsetup message vol 0 @stats_set_aux 0 foo\\ bar\\ baz
|
dmsetup message vol 0 @stats_set_aux 0 foo\\ bar\\ baz
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ if (!$num_devs) {
|
|||||||
die("Specify at least one device\n");
|
die("Specify at least one device\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
$min_dev_size = `blockdev --getsz $devs[0]`;
|
$min_dev_size = `blockdev --getsize $devs[0]`;
|
||||||
for ($i = 1; $i < $num_devs; $i++) {
|
for ($i = 1; $i < $num_devs; $i++) {
|
||||||
my $this_size = `blockdev --getsz $devs[$i]`;
|
my $this_size = `blockdev --getsize $devs[$i]`;
|
||||||
$min_dev_size = ($min_dev_size < $this_size) ?
|
$min_dev_size = ($min_dev_size < $this_size) ?
|
||||||
$min_dev_size : $this_size;
|
$min_dev_size : $this_size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ Assume that you have volumes vg1/switch0 vg1/switch1 vg1/switch2 with
|
|||||||
the same size.
|
the same size.
|
||||||
|
|
||||||
Create a switch device with 64kB region size:
|
Create a switch device with 64kB region size:
|
||||||
dmsetup create switch --table "0 `blockdev --getsz /dev/vg1/switch0`
|
dmsetup create switch --table "0 `blockdev --getsize /dev/vg1/switch0`
|
||||||
switch 3 128 0 /dev/vg1/switch0 0 /dev/vg1/switch1 0 /dev/vg1/switch2 0"
|
switch 3 128 0 /dev/vg1/switch0 0 /dev/vg1/switch1 0 /dev/vg1/switch2 0"
|
||||||
|
|
||||||
Set mappings for the first 7 entries to point to devices switch0, switch1,
|
Set mappings for the first 7 entries to point to devices switch0, switch1,
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ ii) Status
|
|||||||
underlying device. When this is enabled when loading the table,
|
underlying device. When this is enabled when loading the table,
|
||||||
it can get disabled if the underlying device doesn't support it.
|
it can get disabled if the underlying device doesn't support it.
|
||||||
|
|
||||||
ro|rw|out_of_data_space
|
ro|rw
|
||||||
If the pool encounters certain types of device failures it will
|
If the pool encounters certain types of device failures it will
|
||||||
drop into a read-only metadata mode in which no changes to
|
drop into a read-only metadata mode in which no changes to
|
||||||
the pool metadata (like allocating new blocks) are permitted.
|
the pool metadata (like allocating new blocks) are permitted.
|
||||||
@@ -314,13 +314,6 @@ ii) Status
|
|||||||
module parameter can be used to change this timeout -- it
|
module parameter can be used to change this timeout -- it
|
||||||
defaults to 60 seconds but may be disabled using a value of 0.
|
defaults to 60 seconds but may be disabled using a value of 0.
|
||||||
|
|
||||||
needs_check
|
|
||||||
A metadata operation has failed, resulting in the needs_check
|
|
||||||
flag being set in the metadata's superblock. The metadata
|
|
||||||
device must be deactivated and checked/repaired before the
|
|
||||||
thin-pool can be made fully operational again. '-' indicates
|
|
||||||
needs_check is not set.
|
|
||||||
|
|
||||||
iii) Messages
|
iii) Messages
|
||||||
|
|
||||||
create_thin <dev id>
|
create_thin <dev id>
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ Construction Parameters
|
|||||||
|
|
||||||
0 is the original format used in the Chromium OS.
|
0 is the original format used in the Chromium OS.
|
||||||
The salt is appended when hashing, digests are stored continuously and
|
The salt is appended when hashing, digests are stored continuously and
|
||||||
the rest of the block is padded with zeroes.
|
the rest of the block is padded with zeros.
|
||||||
|
|
||||||
1 is the current format that should be used for new devices.
|
1 is the current format that should be used for new devices.
|
||||||
The salt is prepended when hashing and each digest is
|
The salt is prepended when hashing and each digest is
|
||||||
padded with zeroes to the power of two.
|
padded with zeros to the power of two.
|
||||||
|
|
||||||
<dev>
|
<dev>
|
||||||
This is the device containing data, the integrity of which needs to be
|
This is the device containing data, the integrity of which needs to be
|
||||||
@@ -79,37 +79,6 @@ restart_on_corruption
|
|||||||
not compatible with ignore_corruption and requires user space support to
|
not compatible with ignore_corruption and requires user space support to
|
||||||
avoid restart loops.
|
avoid restart loops.
|
||||||
|
|
||||||
ignore_zero_blocks
|
|
||||||
Do not verify blocks that are expected to contain zeroes and always return
|
|
||||||
zeroes instead. This may be useful if the partition contains unused blocks
|
|
||||||
that are not guaranteed to contain zeroes.
|
|
||||||
|
|
||||||
use_fec_from_device <fec_dev>
|
|
||||||
Use forward error correction (FEC) to recover from corruption if hash
|
|
||||||
verification fails. Use encoding data from the specified device. This
|
|
||||||
may be the same device where data and hash blocks reside, in which case
|
|
||||||
fec_start must be outside data and hash areas.
|
|
||||||
|
|
||||||
If the encoding data covers additional metadata, it must be accessible
|
|
||||||
on the hash device after the hash blocks.
|
|
||||||
|
|
||||||
Note: block sizes for data and hash devices must match. Also, if the
|
|
||||||
verity <dev> is encrypted the <fec_dev> should be too.
|
|
||||||
|
|
||||||
fec_roots <num>
|
|
||||||
Number of generator roots. This equals to the number of parity bytes in
|
|
||||||
the encoding data. For example, in RS(M, N) encoding, the number of roots
|
|
||||||
is M-N.
|
|
||||||
|
|
||||||
fec_blocks <num>
|
|
||||||
The number of encoding data blocks on the FEC device. The block size for
|
|
||||||
the FEC device is <data_block_size>.
|
|
||||||
|
|
||||||
fec_start <offset>
|
|
||||||
This is the offset, in <data_block_size> blocks, from the start of the
|
|
||||||
FEC device to the beginning of the encoding data.
|
|
||||||
|
|
||||||
|
|
||||||
Theory of operation
|
Theory of operation
|
||||||
===================
|
===================
|
||||||
|
|
||||||
@@ -129,11 +98,6 @@ per-block basis. This allows for a lightweight hash computation on first read
|
|||||||
into the page cache. Block hashes are stored linearly, aligned to the nearest
|
into the page cache. Block hashes are stored linearly, aligned to the nearest
|
||||||
block size.
|
block size.
|
||||||
|
|
||||||
If forward error correction (FEC) support is enabled any recovery of
|
|
||||||
corrupted data will be verified using the cryptographic hash of the
|
|
||||||
corresponding data. This is why combining error correction with
|
|
||||||
integrity checking is essential.
|
|
||||||
|
|
||||||
Hash Tree
|
Hash Tree
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
dm-zoned
|
|
||||||
========
|
|
||||||
|
|
||||||
The dm-zoned device mapper target exposes a zoned block device (ZBC and
|
|
||||||
ZAC compliant devices) as a regular block device without any write
|
|
||||||
pattern constraints. In effect, it implements a drive-managed zoned
|
|
||||||
block device which hides from the user (a file system or an application
|
|
||||||
doing raw block device accesses) the sequential write constraints of
|
|
||||||
host-managed zoned block devices and can mitigate the potential
|
|
||||||
device-side performance degradation due to excessive random writes on
|
|
||||||
host-aware zoned block devices.
|
|
||||||
|
|
||||||
For a more detailed description of the zoned block device models and
|
|
||||||
their constraints see (for SCSI devices):
|
|
||||||
|
|
||||||
http://www.t10.org/drafts.htm#ZBC_Family
|
|
||||||
|
|
||||||
and (for ATA devices):
|
|
||||||
|
|
||||||
http://www.t13.org/Documents/UploadedDocuments/docs2015/di537r05-Zoned_Device_ATA_Command_Set_ZAC.pdf
|
|
||||||
|
|
||||||
The dm-zoned implementation is simple and minimizes system overhead (CPU
|
|
||||||
and memory usage as well as storage capacity loss). For a 10TB
|
|
||||||
host-managed disk with 256 MB zones, dm-zoned memory usage per disk
|
|
||||||
instance is at most 4.5 MB and as little as 5 zones will be used
|
|
||||||
internally for storing metadata and performaing reclaim operations.
|
|
||||||
|
|
||||||
dm-zoned target devices are formatted and checked using the dmzadm
|
|
||||||
utility available at:
|
|
||||||
|
|
||||||
https://github.com/hgst/dm-zoned-tools
|
|
||||||
|
|
||||||
Algorithm
|
|
||||||
=========
|
|
||||||
|
|
||||||
dm-zoned implements an on-disk buffering scheme to handle non-sequential
|
|
||||||
write accesses to the sequential zones of a zoned block device.
|
|
||||||
Conventional zones are used for caching as well as for storing internal
|
|
||||||
metadata.
|
|
||||||
|
|
||||||
The zones of the device are separated into 2 types:
|
|
||||||
|
|
||||||
1) Metadata zones: these are conventional zones used to store metadata.
|
|
||||||
Metadata zones are not reported as useable capacity to the user.
|
|
||||||
|
|
||||||
2) Data zones: all remaining zones, the vast majority of which will be
|
|
||||||
sequential zones used exclusively to store user data. The conventional
|
|
||||||
zones of the device may be used also for buffering user random writes.
|
|
||||||
Data in these zones may be directly mapped to the conventional zone, but
|
|
||||||
later moved to a sequential zone so that the conventional zone can be
|
|
||||||
reused for buffering incoming random writes.
|
|
||||||
|
|
||||||
dm-zoned exposes a logical device with a sector size of 4096 bytes,
|
|
||||||
irrespective of the physical sector size of the backend zoned block
|
|
||||||
device being used. This allows reducing the amount of metadata needed to
|
|
||||||
manage valid blocks (blocks written).
|
|
||||||
|
|
||||||
The on-disk metadata format is as follows:
|
|
||||||
|
|
||||||
1) The first block of the first conventional zone found contains the
|
|
||||||
super block which describes the on disk amount and position of metadata
|
|
||||||
blocks.
|
|
||||||
|
|
||||||
2) Following the super block, a set of blocks is used to describe the
|
|
||||||
mapping of the logical device blocks. The mapping is done per chunk of
|
|
||||||
blocks, with the chunk size equal to the zoned block device size. The
|
|
||||||
mapping table is indexed by chunk number and each mapping entry
|
|
||||||
indicates the zone number of the device storing the chunk of data. Each
|
|
||||||
mapping entry may also indicate if the zone number of a conventional
|
|
||||||
zone used to buffer random modification to the data zone.
|
|
||||||
|
|
||||||
3) A set of blocks used to store bitmaps indicating the validity of
|
|
||||||
blocks in the data zones follows the mapping table. A valid block is
|
|
||||||
defined as a block that was written and not discarded. For a buffered
|
|
||||||
data chunk, a block is always valid only in the data zone mapping the
|
|
||||||
chunk or in the buffer zone of the chunk.
|
|
||||||
|
|
||||||
For a logical chunk mapped to a conventional zone, all write operations
|
|
||||||
are processed by directly writing to the zone. If the mapping zone is a
|
|
||||||
sequential zone, the write operation is processed directly only if the
|
|
||||||
write offset within the logical chunk is equal to the write pointer
|
|
||||||
offset within of the sequential data zone (i.e. the write operation is
|
|
||||||
aligned on the zone write pointer). Otherwise, write operations are
|
|
||||||
processed indirectly using a buffer zone. In that case, an unused
|
|
||||||
conventional zone is allocated and assigned to the chunk being
|
|
||||||
accessed. Writing a block to the buffer zone of a chunk will
|
|
||||||
automatically invalidate the same block in the sequential zone mapping
|
|
||||||
the chunk. If all blocks of the sequential zone become invalid, the zone
|
|
||||||
is freed and the chunk buffer zone becomes the primary zone mapping the
|
|
||||||
chunk, resulting in native random write performance similar to a regular
|
|
||||||
block device.
|
|
||||||
|
|
||||||
Read operations are processed according to the block validity
|
|
||||||
information provided by the bitmaps. Valid blocks are read either from
|
|
||||||
the sequential zone mapping a chunk, or if the chunk is buffered, from
|
|
||||||
the buffer zone assigned. If the accessed chunk has no mapping, or the
|
|
||||||
accessed blocks are invalid, the read buffer is zeroed and the read
|
|
||||||
operation terminated.
|
|
||||||
|
|
||||||
After some time, the limited number of convnetional zones available may
|
|
||||||
be exhausted (all used to map chunks or buffer sequential zones) and
|
|
||||||
unaligned writes to unbuffered chunks become impossible. To avoid this
|
|
||||||
situation, a reclaim process regularly scans used conventional zones and
|
|
||||||
tries to reclaim the least recently used zones by copying the valid
|
|
||||||
blocks of the buffer zone to a free sequential zone. Once the copy
|
|
||||||
completes, the chunk mapping is updated to point to the sequential zone
|
|
||||||
and the buffer zone freed for reuse.
|
|
||||||
|
|
||||||
Metadata Protection
|
|
||||||
===================
|
|
||||||
|
|
||||||
To protect metadata against corruption in case of sudden power loss or
|
|
||||||
system crash, 2 sets of metadata zones are used. One set, the primary
|
|
||||||
set, is used as the main metadata region, while the secondary set is
|
|
||||||
used as a staging area. Modified metadata is first written to the
|
|
||||||
secondary set and validated by updating the super block in the secondary
|
|
||||||
set, a generation counter is used to indicate that this set contains the
|
|
||||||
newest metadata. Once this operation completes, in place of metadata
|
|
||||||
block updates can be done in the primary metadata set. This ensures that
|
|
||||||
one of the set is always consistent (all modifications committed or none
|
|
||||||
at all). Flush operations are used as a commit point. Upon reception of
|
|
||||||
a flush request, metadata modification activity is temporarily blocked
|
|
||||||
(for both incoming BIO processing and reclaim process) and all dirty
|
|
||||||
metadata blocks are staged and updated. Normal operation is then
|
|
||||||
resumed. Flushing metadata thus only temporarily delays write and
|
|
||||||
discard requests. Read requests can be processed concurrently while
|
|
||||||
metadata flush is being executed.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
=====
|
|
||||||
|
|
||||||
A zoned block device must first be formatted using the dmzadm tool. This
|
|
||||||
will analyze the device zone configuration, determine where to place the
|
|
||||||
metadata sets on the device and initialize the metadata sets.
|
|
||||||
|
|
||||||
Ex:
|
|
||||||
|
|
||||||
dmzadm --format /dev/sdxx
|
|
||||||
|
|
||||||
For a formatted device, the target can be created normally with the
|
|
||||||
dmsetup utility. The only parameter that dm-zoned requires is the
|
|
||||||
underlying zoned block device name. Ex:
|
|
||||||
|
|
||||||
echo "0 `blockdev --getsize ${dev}` zoned ${dev}" | dmsetup create dmz-`basename ${dev}`
|
|
||||||
@@ -50,7 +50,6 @@
|
|||||||
@top_srcdir@/lib/misc/lvm-file.h
|
@top_srcdir@/lib/misc/lvm-file.h
|
||||||
@top_srcdir@/lib/misc/lvm-flock.h
|
@top_srcdir@/lib/misc/lvm-flock.h
|
||||||
@top_srcdir@/lib/misc/lvm-globals.h
|
@top_srcdir@/lib/misc/lvm-globals.h
|
||||||
@top_srcdir@/lib/misc/lvm-maths.h
|
|
||||||
@top_srcdir@/lib/misc/lvm-percent.h
|
@top_srcdir@/lib/misc/lvm-percent.h
|
||||||
@top_srcdir@/lib/misc/lvm-signal.h
|
@top_srcdir@/lib/misc/lvm-signal.h
|
||||||
@top_srcdir@/lib/misc/lvm-string.h
|
@top_srcdir@/lib/misc/lvm-string.h
|
||||||
@@ -59,7 +58,6 @@
|
|||||||
@top_srcdir@/lib/misc/util.h
|
@top_srcdir@/lib/misc/util.h
|
||||||
@top_srcdir@/lib/mm/memlock.h
|
@top_srcdir@/lib/mm/memlock.h
|
||||||
@top_srcdir@/lib/mm/xlate.h
|
@top_srcdir@/lib/mm/xlate.h
|
||||||
@top_srcdir@/lib/notify/lvmnotify.h
|
|
||||||
@top_srcdir@/lib/properties/prop_common.h
|
@top_srcdir@/lib/properties/prop_common.h
|
||||||
@top_srcdir@/lib/report/properties.h
|
@top_srcdir@/lib/report/properties.h
|
||||||
@top_srcdir@/lib/report/report.h
|
@top_srcdir@/lib/report/report.h
|
||||||
|
|||||||
@@ -127,9 +127,6 @@
|
|||||||
/* Path to dmeventd pidfile. */
|
/* Path to dmeventd pidfile. */
|
||||||
#undef DMEVENTD_PIDFILE
|
#undef DMEVENTD_PIDFILE
|
||||||
|
|
||||||
/* Define to 1 to enable the device-mapper filemap daemon. */
|
|
||||||
#undef DMFILEMAPD
|
|
||||||
|
|
||||||
/* Define to enable compat protocol */
|
/* Define to enable compat protocol */
|
||||||
#undef DM_COMPAT
|
#undef DM_COMPAT
|
||||||
|
|
||||||
@@ -148,9 +145,6 @@
|
|||||||
/* Library version */
|
/* Library version */
|
||||||
#undef DM_LIB_VERSION
|
#undef DM_LIB_VERSION
|
||||||
|
|
||||||
/* Path to fsadm binary. */
|
|
||||||
#undef FSADM_PATH
|
|
||||||
|
|
||||||
/* Define to 1 if you have the `alarm' function. */
|
/* Define to 1 if you have the `alarm' function. */
|
||||||
#undef HAVE_ALARM
|
#undef HAVE_ALARM
|
||||||
|
|
||||||
@@ -270,15 +264,9 @@
|
|||||||
/* Define to 1 if you have the <limits.h> header file. */
|
/* Define to 1 if you have the <limits.h> header file. */
|
||||||
#undef HAVE_LIMITS_H
|
#undef HAVE_LIMITS_H
|
||||||
|
|
||||||
/* Define to 1 if you have the <linux/fiemap.h> header file. */
|
|
||||||
#undef HAVE_LINUX_FIEMAP_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <linux/fs.h> header file. */
|
/* Define to 1 if you have the <linux/fs.h> header file. */
|
||||||
#undef HAVE_LINUX_FS_H
|
#undef HAVE_LINUX_FS_H
|
||||||
|
|
||||||
/* Define to 1 if you have the <linux/magic.h> header file. */
|
|
||||||
#undef HAVE_LINUX_MAGIC_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <locale.h> header file. */
|
/* Define to 1 if you have the <locale.h> header file. */
|
||||||
#undef HAVE_LOCALE_H
|
#undef HAVE_LOCALE_H
|
||||||
|
|
||||||
@@ -347,9 +335,6 @@
|
|||||||
/* Define to 1 if the system has the type `ptrdiff_t'. */
|
/* Define to 1 if the system has the type `ptrdiff_t'. */
|
||||||
#undef HAVE_PTRDIFF_T
|
#undef HAVE_PTRDIFF_T
|
||||||
|
|
||||||
/* Define to 1 if the compiler has the `__builtin_clz` builtin. */
|
|
||||||
#undef HAVE___BUILTIN_CLZ
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <readline/history.h> header file. */
|
/* Define to 1 if you have the <readline/history.h> header file. */
|
||||||
#undef HAVE_READLINE_HISTORY_H
|
#undef HAVE_READLINE_HISTORY_H
|
||||||
|
|
||||||
@@ -494,9 +479,6 @@
|
|||||||
/* Define to 1 if you have the <sys/file.h> header file. */
|
/* Define to 1 if you have the <sys/file.h> header file. */
|
||||||
#undef HAVE_SYS_FILE_H
|
#undef HAVE_SYS_FILE_H
|
||||||
|
|
||||||
/* Define to 1 if you have the <sys/inotify.h> header file. */
|
|
||||||
#undef HAVE_SYS_INOTIFY_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||||
#undef HAVE_SYS_IOCTL_H
|
#undef HAVE_SYS_IOCTL_H
|
||||||
|
|
||||||
@@ -632,9 +614,6 @@
|
|||||||
/* Define to 1 to include code that uses lvmpolld. */
|
/* Define to 1 to include code that uses lvmpolld. */
|
||||||
#undef LVMPOLLD_SUPPORT
|
#undef LVMPOLLD_SUPPORT
|
||||||
|
|
||||||
/* configure command line used */
|
|
||||||
#undef LVM_CONFIGURE_LINE
|
|
||||||
|
|
||||||
/* Path to lvm binary. */
|
/* Path to lvm binary. */
|
||||||
#undef LVM_PATH
|
#undef LVM_PATH
|
||||||
|
|
||||||
@@ -652,9 +631,6 @@
|
|||||||
/* The path to 'modprobe', if available. */
|
/* The path to 'modprobe', if available. */
|
||||||
#undef MODPROBE_CMD
|
#undef MODPROBE_CMD
|
||||||
|
|
||||||
/* Define to 1 to include code that uses dbus notification. */
|
|
||||||
#undef NOTIFYDBUS_SUPPORT
|
|
||||||
|
|
||||||
/* Define to 1 to enable O_DIRECT support. */
|
/* Define to 1 to enable O_DIRECT support. */
|
||||||
#undef O_DIRECT_SUPPORT
|
#undef O_DIRECT_SUPPORT
|
||||||
|
|
||||||
@@ -685,6 +661,9 @@
|
|||||||
/* Define to 1 to include the LVM readline shell. */
|
/* Define to 1 to include the LVM readline shell. */
|
||||||
#undef READLINE_SUPPORT
|
#undef READLINE_SUPPORT
|
||||||
|
|
||||||
|
/* Define to 1 to include built-in support for replicators. */
|
||||||
|
#undef REPLICATOR_INTERNAL
|
||||||
|
|
||||||
/* Define as the return type of signal handlers (`int' or `void'). */
|
/* Define as the return type of signal handlers (`int' or `void'). */
|
||||||
#undef RETSIGTYPE
|
#undef RETSIGTYPE
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ ifeq ("@RAID@", "shared")
|
|||||||
SUBDIRS += raid
|
SUBDIRS += raid
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ("@REPLICATORS@", "shared")
|
||||||
|
SUBDIRS += replicator
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ("@THIN@", "shared")
|
ifeq ("@THIN@", "shared")
|
||||||
SUBDIRS += thin
|
SUBDIRS += thin
|
||||||
endif
|
endif
|
||||||
@@ -44,10 +48,6 @@ ifeq ("@CACHE@", "shared")
|
|||||||
SUBDIRS += cache_segtype
|
SUBDIRS += cache_segtype
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ("@CLUSTER@", "shared")
|
|
||||||
SUBDIRS += locking
|
|
||||||
endif
|
|
||||||
|
|
||||||
SOURCES =\
|
SOURCES =\
|
||||||
activate/activate.c \
|
activate/activate.c \
|
||||||
cache/lvmcache.c \
|
cache/lvmcache.c \
|
||||||
@@ -76,7 +76,6 @@ SOURCES =\
|
|||||||
filters/filter-partitioned.c \
|
filters/filter-partitioned.c \
|
||||||
filters/filter-type.c \
|
filters/filter-type.c \
|
||||||
filters/filter-usable.c \
|
filters/filter-usable.c \
|
||||||
filters/filter-internal.c \
|
|
||||||
format_text/archive.c \
|
format_text/archive.c \
|
||||||
format_text/archiver.c \
|
format_text/archiver.c \
|
||||||
format_text/export.c \
|
format_text/export.c \
|
||||||
@@ -96,13 +95,13 @@ SOURCES =\
|
|||||||
metadata/lv_manip.c \
|
metadata/lv_manip.c \
|
||||||
metadata/merge.c \
|
metadata/merge.c \
|
||||||
metadata/metadata.c \
|
metadata/metadata.c \
|
||||||
metadata/metadata-liblvm.c \
|
|
||||||
metadata/mirror.c \
|
metadata/mirror.c \
|
||||||
metadata/pool_manip.c \
|
metadata/pool_manip.c \
|
||||||
metadata/pv.c \
|
metadata/pv.c \
|
||||||
metadata/pv_manip.c \
|
metadata/pv_manip.c \
|
||||||
metadata/pv_map.c \
|
metadata/pv_map.c \
|
||||||
metadata/raid_manip.c \
|
metadata/raid_manip.c \
|
||||||
|
metadata/replicator_manip.c \
|
||||||
metadata/segtype.c \
|
metadata/segtype.c \
|
||||||
metadata/snapshot_manip.c \
|
metadata/snapshot_manip.c \
|
||||||
metadata/thin_manip.c \
|
metadata/thin_manip.c \
|
||||||
@@ -112,13 +111,11 @@ SOURCES =\
|
|||||||
misc/lvm-file.c \
|
misc/lvm-file.c \
|
||||||
misc/lvm-flock.c \
|
misc/lvm-flock.c \
|
||||||
misc/lvm-globals.c \
|
misc/lvm-globals.c \
|
||||||
misc/lvm-maths.c \
|
|
||||||
misc/lvm-signal.c \
|
misc/lvm-signal.c \
|
||||||
misc/lvm-string.c \
|
misc/lvm-string.c \
|
||||||
misc/lvm-wrappers.c \
|
misc/lvm-wrappers.c \
|
||||||
misc/lvm-percent.c \
|
misc/lvm-percent.c \
|
||||||
mm/memlock.c \
|
mm/memlock.c \
|
||||||
notify/lvmnotify.c \
|
|
||||||
properties/prop_common.c \
|
properties/prop_common.c \
|
||||||
report/properties.c \
|
report/properties.c \
|
||||||
report/report.c \
|
report/report.c \
|
||||||
@@ -149,6 +146,10 @@ ifeq ("@CLUSTER@", "internal")
|
|||||||
SOURCES += locking/cluster_locking.c
|
SOURCES += locking/cluster_locking.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ("@CLUSTER@", "shared")
|
||||||
|
SUBDIRS += locking
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ("@SNAPSHOTS@", "internal")
|
ifeq ("@SNAPSHOTS@", "internal")
|
||||||
SOURCES += snapshot/snapshot.c
|
SOURCES += snapshot/snapshot.c
|
||||||
endif
|
endif
|
||||||
@@ -161,6 +162,10 @@ ifeq ("@RAID@", "internal")
|
|||||||
SOURCES += raid/raid.c
|
SOURCES += raid/raid.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ("@REPLICATORS@", "internal")
|
||||||
|
SOURCES += replicator/replicator.c
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ("@THIN@", "internal")
|
ifeq ("@THIN@", "internal")
|
||||||
SOURCES += thin/thin.c
|
SOURCES += thin/thin.c
|
||||||
endif
|
endif
|
||||||
@@ -196,6 +201,11 @@ ifeq ("@BUILD_LVMLOCKD@", "yes")
|
|||||||
locking/lvmlockd.c
|
locking/lvmlockd.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ("@DMEVENTD@", "yes")
|
||||||
|
CLDFLAGS += -L$(top_builddir)/daemons/dmeventd
|
||||||
|
LIBS += -ldevmapper-event
|
||||||
|
endif
|
||||||
|
|
||||||
LIB_NAME = liblvm-internal
|
LIB_NAME = liblvm-internal
|
||||||
LIB_STATIC = $(LIB_NAME).a
|
LIB_STATIC = $(LIB_NAME).a
|
||||||
|
|
||||||
@@ -205,8 +215,8 @@ ifeq ($(MAKECMDGOALS),distclean)
|
|||||||
format_pool \
|
format_pool \
|
||||||
snapshot \
|
snapshot \
|
||||||
mirror \
|
mirror \
|
||||||
notify \
|
|
||||||
raid \
|
raid \
|
||||||
|
replicator \
|
||||||
thin \
|
thin \
|
||||||
cache_segtype \
|
cache_segtype \
|
||||||
locking
|
locking
|
||||||
@@ -215,10 +225,10 @@ endif
|
|||||||
CFLOW_LIST = $(SOURCES)
|
CFLOW_LIST = $(SOURCES)
|
||||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
||||||
|
|
||||||
PROGS_CFLAGS = $(BLKID_CFLAGS) $(UDEV_CFLAGS)
|
|
||||||
|
|
||||||
include $(top_builddir)/make.tmpl
|
include $(top_builddir)/make.tmpl
|
||||||
|
|
||||||
|
CFLAGS += $(BLKID_CFLAGS) $(UDEV_CFLAGS) $(VALGRIND_CFLAGS)
|
||||||
|
|
||||||
$(SUBDIRS): $(LIB_STATIC)
|
$(SUBDIRS): $(LIB_STATIC)
|
||||||
|
|
||||||
CLEAN_TARGETS += misc/configure.h misc/lvm-version.h
|
CLEAN_TARGETS += misc/configure.h misc/lvm-version.h
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||||
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
|
* Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of LVM2.
|
* This file is part of LVM2.
|
||||||
*
|
*
|
||||||
@@ -54,12 +54,11 @@ struct lv_seg_status {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct lv_with_info_and_seg_status {
|
struct lv_with_info_and_seg_status {
|
||||||
|
const struct logical_volume *lv; /* input */
|
||||||
int info_ok;
|
int info_ok;
|
||||||
const struct logical_volume *lv; /* output */
|
|
||||||
struct lvinfo info; /* output */
|
struct lvinfo info; /* output */
|
||||||
int seg_part_of_lv; /* output */
|
int seg_part_of_lv; /* output */
|
||||||
struct lv_seg_status seg_status; /* output, see lv_seg_status */
|
struct lv_seg_status seg_status; /* input/output, see lv_seg_status */
|
||||||
/* TODO: add extra status for snapshot origin */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lv_activate_opts {
|
struct lv_activate_opts {
|
||||||
@@ -82,7 +81,6 @@ struct lv_activate_opts {
|
|||||||
* set of flags to avoid any scanning in udev. These udev
|
* set of flags to avoid any scanning in udev. These udev
|
||||||
* flags are persistent in udev db for any spurious event
|
* flags are persistent in udev db for any spurious event
|
||||||
* that follows. */
|
* that follows. */
|
||||||
unsigned resuming; /* Set when resuming after a suspend. */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void set_activation(int activation, int silent);
|
void set_activation(int activation, int silent);
|
||||||
@@ -93,14 +91,10 @@ int library_version(char *version, size_t size);
|
|||||||
int lvm1_present(struct cmd_context *cmd);
|
int lvm1_present(struct cmd_context *cmd);
|
||||||
|
|
||||||
int module_present(struct cmd_context *cmd, const char *target_name);
|
int module_present(struct cmd_context *cmd, const char *target_name);
|
||||||
int target_present_version(struct cmd_context *cmd, const char *target_name,
|
|
||||||
int use_modprobe, uint32_t *maj,
|
|
||||||
uint32_t *min, uint32_t *patchlevel);
|
|
||||||
int target_present(struct cmd_context *cmd, const char *target_name,
|
int target_present(struct cmd_context *cmd, const char *target_name,
|
||||||
int use_modprobe);
|
int use_modprobe);
|
||||||
int target_version(const char *target_name, uint32_t *maj,
|
int target_version(const char *target_name, uint32_t *maj,
|
||||||
uint32_t *min, uint32_t *patchlevel);
|
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 lvm_dm_prefix_check(int major, int minor, const char *prefix);
|
||||||
int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
|
int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
|
||||||
struct dm_list *modules);
|
struct dm_list *modules);
|
||||||
@@ -124,8 +118,6 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi
|
|||||||
|
|
||||||
int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv);
|
int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv);
|
||||||
|
|
||||||
int lv_deactivate_any_missing_subdevs(const struct logical_volume *lv);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns 1 if info structure has been populated, else 0 on failure.
|
* Returns 1 if info structure has been populated, else 0 on failure.
|
||||||
* When lvinfo* is NULL, it returns 1 if the device is locally active, 0 otherwise.
|
* When lvinfo* is NULL, it returns 1 if the device is locally active, 0 otherwise.
|
||||||
@@ -135,6 +127,13 @@ int lv_info(struct cmd_context *cmd, const struct logical_volume *lv, int use_la
|
|||||||
int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, int use_layer,
|
int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, int use_layer,
|
||||||
struct lvinfo *info, int with_open_count, int with_read_ahead);
|
struct lvinfo *info, int with_open_count, int with_read_ahead);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns 1 if lv_seg_status structure has been populated,
|
||||||
|
* else 0 on failure or if device not active locally.
|
||||||
|
*/
|
||||||
|
int lv_status(struct cmd_context *cmd, const struct lv_segment *lv_seg,
|
||||||
|
int use_layer, struct lv_seg_status *lv_seg_status);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns 1 if lv_info_and_seg_status structure has been populated,
|
* Returns 1 if lv_info_and_seg_status structure has been populated,
|
||||||
* else 0 on failure or if device not active locally.
|
* else 0 on failure or if device not active locally.
|
||||||
@@ -142,12 +141,12 @@ int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, int use_layer,
|
|||||||
* lv_info_with_seg_status is the same as calling lv_info and then lv_status,
|
* lv_info_with_seg_status is the same as calling lv_info and then lv_status,
|
||||||
* but this fn tries to do that with one ioctl if possible.
|
* but this fn tries to do that with one ioctl if possible.
|
||||||
*/
|
*/
|
||||||
int lv_info_with_seg_status(struct cmd_context *cmd,
|
int lv_info_with_seg_status(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||||
const struct lv_segment *lv_seg,
|
const struct lv_segment *lv_seg, int use_layer,
|
||||||
struct lv_with_info_and_seg_status *status,
|
struct lv_with_info_and_seg_status *status,
|
||||||
int with_open_count, int with_read_ahead);
|
int with_open_count, int with_read_ahead);
|
||||||
|
|
||||||
int lv_check_not_in_use(const struct logical_volume *lv, int error_if_used);
|
int lv_check_not_in_use(const struct logical_volume *lv);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns 1 if activate_lv has been set: 1 = activate; 0 = don't.
|
* Returns 1 if activate_lv has been set: 1 = activate; 0 = don't.
|
||||||
@@ -168,13 +167,11 @@ int lv_snapshot_percent(const struct logical_volume *lv, dm_percent_t *percent);
|
|||||||
int lv_mirror_percent(struct cmd_context *cmd, const struct logical_volume *lv,
|
int lv_mirror_percent(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||||
int wait, dm_percent_t *percent, uint32_t *event_nr);
|
int wait, dm_percent_t *percent, uint32_t *event_nr);
|
||||||
int lv_raid_percent(const struct logical_volume *lv, dm_percent_t *percent);
|
int lv_raid_percent(const struct logical_volume *lv, dm_percent_t *percent);
|
||||||
int lv_raid_dev_count(const struct logical_volume *lv, uint32_t *dev_cnt);
|
|
||||||
int lv_raid_data_offset(const struct logical_volume *lv, uint64_t *data_offset);
|
|
||||||
int lv_raid_dev_health(const struct logical_volume *lv, char **dev_health);
|
int lv_raid_dev_health(const struct logical_volume *lv, char **dev_health);
|
||||||
int lv_raid_mismatch_count(const struct logical_volume *lv, uint64_t *cnt);
|
int lv_raid_mismatch_count(const struct logical_volume *lv, uint64_t *cnt);
|
||||||
int lv_raid_sync_action(const struct logical_volume *lv, char **sync_action);
|
int lv_raid_sync_action(const struct logical_volume *lv, char **sync_action);
|
||||||
int lv_raid_message(const struct logical_volume *lv, const char *msg);
|
int lv_raid_message(const struct logical_volume *lv, const char *msg);
|
||||||
int lv_cache_status(const struct logical_volume *cache_lv,
|
int lv_cache_status(const struct logical_volume *lv,
|
||||||
struct lv_status_cache **status);
|
struct lv_status_cache **status);
|
||||||
int lv_thin_pool_percent(const struct logical_volume *lv, int metadata,
|
int lv_thin_pool_percent(const struct logical_volume *lv, int metadata,
|
||||||
dm_percent_t *percent);
|
dm_percent_t *percent);
|
||||||
@@ -202,12 +199,12 @@ int lv_has_target_type(struct dm_pool *mem, const struct logical_volume *lv,
|
|||||||
const char *layer, const char *target_type);
|
const char *layer, const char *target_type);
|
||||||
|
|
||||||
int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume *lv,
|
int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||||
const struct lv_activate_opts *laopts, int monitor);
|
const struct lv_activate_opts *laopts, int do_reg);
|
||||||
|
|
||||||
#ifdef DMEVENTD
|
#ifdef DMEVENTD
|
||||||
# include "libdevmapper-event.h"
|
# include "libdevmapper-event.h"
|
||||||
char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath);
|
char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath);
|
||||||
int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
int target_registered_with_dmeventd(struct cmd_context *cmd, const char *libpath,
|
||||||
const struct logical_volume *lv, int *pending);
|
const struct logical_volume *lv, int *pending);
|
||||||
int target_register_events(struct cmd_context *cmd, const char *dso, const struct logical_volume *lv,
|
int target_register_events(struct cmd_context *cmd, const char *dso, const struct logical_volume *lv,
|
||||||
int evmask __attribute__((unused)), int set, int timeout);
|
int evmask __attribute__((unused)), int set, int timeout);
|
||||||
@@ -242,28 +239,4 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check);
|
|||||||
*/
|
*/
|
||||||
void fs_unlock(void);
|
void fs_unlock(void);
|
||||||
|
|
||||||
#define TARGET_NAME_CACHE "cache"
|
|
||||||
#define TARGET_NAME_ERROR "error"
|
|
||||||
#define TARGET_NAME_ERROR_OLD "erro" /* Truncated in older kernels */
|
|
||||||
#define TARGET_NAME_LINEAR "linear"
|
|
||||||
#define TARGET_NAME_MIRROR "mirror"
|
|
||||||
#define TARGET_NAME_RAID "raid"
|
|
||||||
#define TARGET_NAME_SNAPSHOT "snapshot"
|
|
||||||
#define TARGET_NAME_SNAPSHOT_MERGE "snapshot-merge"
|
|
||||||
#define TARGET_NAME_SNAPSHOT_ORIGIN "snapshot-origin"
|
|
||||||
#define TARGET_NAME_STRIPED "striped"
|
|
||||||
#define TARGET_NAME_THIN "thin"
|
|
||||||
#define TARGET_NAME_THIN_POOL "thin-pool"
|
|
||||||
#define TARGET_NAME_ZERO "zero"
|
|
||||||
|
|
||||||
#define MODULE_NAME_CLUSTERED_MIRROR "clog"
|
|
||||||
#define MODULE_NAME_CACHE TARGET_NAME_CACHE
|
|
||||||
#define MODULE_NAME_ERROR TARGET_NAME_ERROR
|
|
||||||
#define MODULE_NAME_LOG_CLUSTERED "log-clustered"
|
|
||||||
#define MODULE_NAME_LOG_USERSPACE "log-userspace"
|
|
||||||
#define MODULE_NAME_MIRROR TARGET_NAME_MIRROR
|
|
||||||
#define MODULE_NAME_SNAPSHOT TARGET_NAME_SNAPSHOT
|
|
||||||
#define MODULE_NAME_RAID TARGET_NAME_RAID
|
|
||||||
#define MODULE_NAME_ZERO TARGET_NAME_ZERO
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user