mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-10-30 20:23:49 +03:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			dev-dct-cm
			...
			dev-mcsont
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 05716c2d8a | 
							
								
								
									
										13
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,18 +1,13 @@ | ||||
| *.5 | ||||
| *.7 | ||||
| *.8 | ||||
| *.a | ||||
| *.d | ||||
| *.o | ||||
| *.orig | ||||
| *.pc | ||||
| *.pot | ||||
| *.pyc | ||||
| *.pyo | ||||
| *.rej | ||||
| *.so | ||||
| *.so.* | ||||
| *.sw* | ||||
| *.swp | ||||
| *~ | ||||
|  | ||||
| .export.sym | ||||
| @@ -22,11 +17,11 @@ | ||||
| Makefile | ||||
| make.tmpl | ||||
|  | ||||
| configure.h | ||||
| version.h | ||||
|  | ||||
| /autom4te.cache/ | ||||
| /autoscan.log | ||||
| /config.log | ||||
| /config.status | ||||
| /configure.scan | ||||
| /cscope.out | ||||
| /tags | ||||
| /tmp/ | ||||
|   | ||||
							
								
								
									
										4
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								COPYING
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
| 		       Version 2, June 1991 | ||||
|  | ||||
|  Copyright (C) 1989, 1991 Free Software Foundation, Inc. | ||||
|  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  Everyone is permitted to copy and distribute verbatim copies | ||||
|  of this license document, but changing it is not allowed. | ||||
|  | ||||
| @@ -305,7 +305,7 @@ the "copyright" line and a pointer to where the full notice is found. | ||||
|  | ||||
|     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 | ||||
|     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
|  | ||||
| Also add information on how to contact you by electronic and paper mail. | ||||
|   | ||||
							
								
								
									
										69
									
								
								Makefile.in
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								Makefile.in
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| # | ||||
| # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| @@ -10,13 +10,11 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
| abs_top_builddir = @abs_top_builddir@ | ||||
| abs_top_srcdir = @abs_top_srcdir@ | ||||
|  | ||||
| SUBDIRS = conf daemons include lib libdaemon libdm man scripts tools | ||||
|  | ||||
| @@ -93,47 +91,10 @@ cscope.out: | ||||
| all: cscope.out | ||||
| endif | ||||
| DISTCLEAN_TARGETS += cscope.out | ||||
| 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 unit: all | ||||
| 	$(MAKE) -C test $(@) | ||||
|  | ||||
| conf.generate: tools | ||||
|  | ||||
| # how to use parenthesis in makefiles | ||||
| leftparen:=( | ||||
| LVM_VER := $(firstword $(subst $(leftparen), ,$(LVM_VERSION))) | ||||
| VER := LVM2.$(LVM_VER) | ||||
| # release file name | ||||
| FILE_VER := $(VER).tgz | ||||
| CLEAN_TARGETS += $(FILE_VER) | ||||
| CLEAN_DIRS += $(rpmbuilddir) | ||||
|  | ||||
| dist: | ||||
| 	@echo "Generating $(FILE_VER)";\ | ||||
| 	(cd $(top_srcdir); git ls-tree -r HEAD --name-only | xargs tar --transform "s,^,$(VER)/," -c) | gzip >$(FILE_VER) | ||||
|  | ||||
| rpm: dist | ||||
| 	$(RM) -r $(rpmbuilddir)/SOURCES | ||||
| 	$(MKDIR_P) $(rpmbuilddir)/SOURCES | ||||
| 	$(LN_S) -f $(abs_top_builddir)/$(FILE_VER) $(rpmbuilddir)/SOURCES | ||||
| 	$(LN_S) -f $(abs_top_srcdir)/spec/build.inc $(rpmbuilddir)/SOURCES | ||||
| 	$(LN_S) -f $(abs_top_srcdir)/spec/macros.inc $(rpmbuilddir)/SOURCES | ||||
| 	$(LN_S) -f $(abs_top_srcdir)/spec/packages.inc $(rpmbuilddir)/SOURCES | ||||
| 	DM_VER=$$(cut -d- -f1 $(top_srcdir)/VERSION_DM);\ | ||||
| 	GIT_VER=$$(cd $(top_srcdir); git describe | cut -d- --output-delimiter=. -f2,3 || echo 0);\ | ||||
| 	sed -e "s,\(device_mapper_version\) [0-9.]*$$,\1 $$DM_VER," \ | ||||
| 	    -e "s,^\(Version:[^0-9%]*\)[0-9.]*$$,\1 $(LVM_VER)," \ | ||||
| 	    -e "s,^\(Release:[^0-9%]*\)[0-9.]\+,\1 $$GIT_VER," \ | ||||
| 	    $(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc | ||||
| 	rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec | ||||
|  | ||||
| generate: conf.generate | ||||
| 	$(MAKE) -C conf generate | ||||
|  | ||||
| all_man: | ||||
| 	$(MAKE) -C man all_man | ||||
|  | ||||
| install_system_dirs: | ||||
| 	$(INSTALL_DIR) $(DESTDIR)$(DEFAULT_SYS_DIR) | ||||
| 	$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_ARCHIVE_DIR) | ||||
| @@ -153,9 +114,6 @@ install_systemd_generators: | ||||
| install_systemd_units: | ||||
| 	$(MAKE) -C scripts install_systemd_units | ||||
|  | ||||
| install_all_man: | ||||
| 	$(MAKE) -C man install_all_man | ||||
|  | ||||
| ifeq ("@PYTHON_BINDINGS@", "yes") | ||||
| install_python_bindings: | ||||
| 	$(MAKE) -C liblvm/python install_python_bindings | ||||
| @@ -164,14 +122,8 @@ endif | ||||
| install_tmpfiles_configuration: | ||||
| 	$(MAKE) -C scripts install_tmpfiles_configuration | ||||
|  | ||||
| LCOV_TRACES = libdm.info lib.info liblvm.info tools.info \ | ||||
| 	libdaemon/client.info libdaemon/server.info \ | ||||
| 	daemons/clvmd.info \ | ||||
| 	daemons/dmeventd.info \ | ||||
| 	daemons/lvmetad.info \ | ||||
| 	daemons/lvmlockd.info \ | ||||
| 	daemons/lvmpolld.info | ||||
|  | ||||
| LCOV_TRACES = libdm.info lib.info tools.info \ | ||||
| 	daemons/dmeventd.info daemons/clvmd.info | ||||
| CLEAN_TARGETS += $(LCOV_TRACES) | ||||
|  | ||||
| ifneq ("$(LCOV)", "") | ||||
| @@ -200,7 +152,7 @@ lcov: $(LCOV_TRACES) | ||||
| 	$(RM) -r $(LCOV_REPORTS_DIR) | ||||
| 	$(MKDIR_P) $(LCOV_REPORTS_DIR) | ||||
| 	for i in $(LCOV_TRACES); do \ | ||||
| 		test -s $$i -a $$(wc -w <$$i) -ge 100 && lc="$$lc $$i"; \ | ||||
| 		test -s $$i && lc="$$lc $$i"; \ | ||||
| 	done; \ | ||||
| 	test -z "$$lc" || $(GENHTML) -p @abs_top_builddir@ \ | ||||
| 		-o $(LCOV_REPORTS_DIR) $$lc | ||||
| @@ -232,12 +184,3 @@ memcheck: test-programs | ||||
| ruby-test: | ||||
| 	$(RUBY) report-generators/test/ts.rb | ||||
| endif | ||||
|  | ||||
| ifneq ($(shell which ctags),) | ||||
| .PHONY: tags | ||||
| tags: | ||||
| 	test -z "$(shell find $(top_srcdir) -type f -name '*.[ch]' -newer tags 2>/dev/null | head -1)" || $(RM) tags | ||||
| 	test -f tags || find $(top_srcdir) -maxdepth 5 -type f -name '*.[ch]' -exec ctags -a '{}' + | ||||
|  | ||||
| CLEAN_TARGETS += tags | ||||
| endif | ||||
|   | ||||
							
								
								
									
										2
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								README
									
									
									
									
									
								
							| @@ -18,7 +18,7 @@ Mailing list for general discussion related to LVM2: | ||||
|  | ||||
| Mailing lists for LVM2 development, patches and commits: | ||||
|   lvm-devel@redhat.com | ||||
|   Subscribe from https://www.redhat.com/mailman/listinfo/lvm-devel | ||||
|   Subscribe from https://www.redhat.com/mailman/listinfo/linux-lvm | ||||
|  | ||||
|   lvm2-commits@lists.fedorahosted.org (Read-only archive of commits) | ||||
|   Subscribe from https://fedorahosted.org/mailman/listinfo/lvm2-commits | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| 1.02.136-git (2016-09-26) | ||||
| 1.02.91-git (2014-09-01) | ||||
|   | ||||
							
								
								
									
										870
									
								
								WHATS_NEW
									
									
									
									
									
								
							
							
						
						
									
										870
									
								
								WHATS_NEW
									
									
									
									
									
								
							| @@ -1,863 +1,6 @@ | ||||
| Version 2.02.167 -  | ||||
| ====================================== | ||||
|   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 | ||||
| Version 2.02.112 -  | ||||
| ===================================== | ||||
|   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. | ||||
|   Add configure --enable-dbus-service for an LVM D-Bus service. | ||||
|   Replace configure --enable-python_bindings with python2 and python3 versions. | ||||
|   If PV belongs to some VG and metadata missing, skip it if system ID is used. | ||||
|   Automatically change PV header extension to latest version if writing PV/VG. | ||||
|   Identify used PVs in pv_attr field by new 'u' character. | ||||
|   Add pv_in_use reporting field to report if PV is used or not. | ||||
|   Add pv_ext_vsn reporting field to report PV header extension version. | ||||
|   Add protective flag marking PVs as used even if no metadata available. | ||||
|  | ||||
| Version 2.02.142 - 15th February 2016 | ||||
| ===================================== | ||||
|   Fix memory pool corruption in pvmove (2.02.141). | ||||
|   Support control of spare metadata creation when repairing thin-pool. | ||||
|   Fix config type of 'log/verbose' from bool to int (2.02.99). | ||||
|   Fix inverted data LV thinp watermark calc for dmeventd response (2.02.133). | ||||
|   Use use_blkid_wiping=0 if not defined in lvm.conf and support not compiled in. | ||||
|   Do not check for suspended devices if scanning for lvmetad update. | ||||
|   Clear cached bootloader areas when PV format changed. | ||||
|   Fix partn table filter with external_device_info_source="udev" and blkid<2.20. | ||||
|  | ||||
| Version 2.02.141 - 25th January 2016 | ||||
| ==================================== | ||||
|   Add metadata/check_pv_device_sizes switch to lvm.conf for device size checks. | ||||
|   Warn if device size is less than corresponding PV size in metadata. | ||||
|   Cache device sizes internally. | ||||
|   Restore support for command breaking in process_each_lv_in_vg() (2.02.118). | ||||
|   Use correct mempool when process_each_lv_in_vg() (2.02.118). | ||||
|   Fix lvm.8 man to show again prohibited suffixes. | ||||
|   Fix configure to set proper use_blkid_wiping if autodetected as disabled. | ||||
|   Initialise udev in clvmd for use in device scanning. (2.02.116) | ||||
|   Add seg_le_ranges report field for common format when displaying seg devices. | ||||
|   Honour report/list_item_separator for seg_metadata_le_ranges report field. | ||||
|   Don't mark hidden devs in -o devices,metadata_devices,seg_pe_ranges.(2.02.140) | ||||
|   Change LV sizes in seg_pe_ranges report field to match underlying devices. | ||||
|   Add kernel_cache_settings report field for cache LV settings used in kernel. | ||||
|  | ||||
| Version 2.02.140 - 16th January 2016 | ||||
| ==================================== | ||||
|   Fix lvm2app to return either 0 or 1 for lvm_vg_is_{clustered,exported}. | ||||
|   Add kernel_discards report field to display thin pool discard used in kernel. | ||||
|   Correct checking of target presence when driver access is disabled. | ||||
|   Eval poolmetadatasize arg earlier in lvresize. | ||||
|   Fix vgcfgrestore to respect allocatable attribute of PVs. | ||||
|   Add report/mark_hidden_devices to lvm.conf. | ||||
|   Use brackets consistently in report fields to mark hidden devices. | ||||
|   Restore background polling processing during auto-activation (2.02.119). | ||||
|   Fix invalid memory read when reporting cache LV policy_name (2.02.126). | ||||
|  | ||||
| Version 2.02.139 - 8th January 2016 | ||||
| =================================== | ||||
|   Update lvmlockd with the new VG seqno before devices are suspended. | ||||
|   Rework vgrename to use the common processing code in toollib. | ||||
|   Make pvs show new devices on the system since the last .cache update. | ||||
|   Document F,D and M thin pool health status chars for lv_attr in lvs man page. | ||||
|   Also add lvm2-activation{-early,-net}.service systemd status for lvmdump -s. | ||||
|  | ||||
| Version 2.02.138 - 14th December 2015 | ||||
| ===================================== | ||||
|   Support lvrename for hidden (used) cache pools. | ||||
|   Fix lvrename for stacked cache pools. | ||||
|  | ||||
| Version 2.02.137 - 5th December 2015 | ||||
| ==================================== | ||||
|   Restore archiving before changing metadata in vgextend (2.02.117). | ||||
|   Dropped internal usage of log_suppress(2). | ||||
|   Cleaned logging code for buffer size usage. | ||||
|   Added internal id_read_format_try() function to check and read valid UUID. | ||||
|   Change lvcreate, lvrename, lvresize to use process_each_vg. | ||||
|   Change process_each_vg to handle single VG as separate arg. | ||||
|   Issue error if ambiguous VG name is supplied in most commands. | ||||
|   Make process_each fns always work through full list of known VG names. | ||||
|   Use dm_get_status_mirror() instead of individual parsers. | ||||
|   Add mem pool arg for check_transient_status() target function. | ||||
|   Avoid misleading error with -m is omitted with lvconvert to raid types. | ||||
|   Add system_id to vginfo cache. | ||||
|  | ||||
| Version 2.02.136 - 28th November 2015 | ||||
| ===================================== | ||||
|   Add new --sinceversion option for lvmconfig --type new. | ||||
|   Fix inactive table loaded for wrapping thin-pool when resizing it. | ||||
|   Extend the list of ignored libraries when locking memory. | ||||
|  | ||||
| Version 2.02.135 - 23rd November 2015 | ||||
| ===================================== | ||||
|   Add a model file for Coverity. | ||||
|   Show correct error message for unsupported yet cache pool repair. | ||||
|   Allow lvconvert cache pools' data and metadata LV to raid. | ||||
|   Fix reading of old metadata with missing cache policy or mode settings. | ||||
|   Issue error if external_device_info_source=udev and udev db record incomplete. | ||||
|   Update lvmetad duplicate VG name handling to use hash function extensions. | ||||
|   Detect invalid vgrenames by vgid where the name is unchanged. | ||||
|   Fix passing of 32bit values through daemons (mostly lvmlockd). | ||||
|   Use local memory pool for whole alloc_handle manipulation. | ||||
|   Add missing pointer validation after dm_get_next_target(). | ||||
|   Do not deref NULL pointer in debug message for _match_pv_tags(). | ||||
|   Drop unneeded stat() call when checking for sysfs file. | ||||
|   Fix memory leak on error path of failing thin-pool percentage check. | ||||
|   Add missing test for failing node allocation in lvmetad. | ||||
|   Correct configure messages when enabling/disabling lvmlockd. | ||||
|  | ||||
| Version 2.02.134 - 9th November 2015 | ||||
| ==================================== | ||||
|   Refactor some lvmetad code and adjust some duplicate PV messages. | ||||
|   No longer repair/wipe VG/PVs if inaccessible because foreign or shared. | ||||
|   Pass correct data size to mirror log calc so log can be bigger than 1 extent. | ||||
|  | ||||
| Version 2.02.133 - 30th October 2015 | ||||
| ==================================== | ||||
|   Support repeated -o|--options for reporting commands. | ||||
|   Support -o- and -o# for reporting commands to remove and compact fields. | ||||
|   Fix missing PVs from pvs output if vgremove is run concurrently. | ||||
|   Remove unwanted error message when running pvs/vgs/lvs and vgremove at once. | ||||
|   Check newly created VG's metadata do not overlap in metadata ring buffer. | ||||
|   Check metadata area size is at least the minimum size defined for the format. | ||||
|   Thin pool targets uses low_water_mark from profile. | ||||
|   Dropping 'yet' from error of unsupported thick snapshot of snapshots. | ||||
|   Do not support unpartitioned DASD devices with CDL formatted with pvcreate. | ||||
|   For thins use flush for suspend only when volume size is reduced. | ||||
|   Enable code which detects the need of flush during suspend. | ||||
|   Ensure --use-policy will resize volume to fit below threshold. | ||||
|   Correct percentage evaluation when checking thin-pool over threshold. | ||||
|   Fix lvmcache to move PV from VG to orphans if VG is removed and lvmetad used. | ||||
|   Fix lvmcache to not cache even invalid info about PV which got removed. | ||||
|   Support checking of memlock daemon counter. | ||||
|   Allow all log levels to be used with the lvmetad -l option. | ||||
|   Add optional shutdown when idle support for lvmetad. | ||||
|   Fix missing in-sync progress info while lvconvert used with lvmpolld. | ||||
|   Add report/compact_output_cols to lvm.conf to define report cols to compact. | ||||
|   Do not change logging in lvm2 library when it's already set. | ||||
|   Check for enough space in thin-pool in command before creating new thin. | ||||
|   Make libblkid detect all copies of the same signature if use_blkid_wiping=1. | ||||
|   Fix vgimportclone with -n to not add number unnecessarily to base VG name. | ||||
|   Cleanup vgimportclone script and remove dependency on awk, grep, cut and tr. | ||||
|   Add vg_missing_pv_count report field to report number of missing PVs in a VG. | ||||
|   Properly identify internal LV holding sanlock locks within lv_role field. | ||||
|   Add metadata_devices and seg_metadata_le_ranges report fields for raid vols. | ||||
|   Fix lvm2-{activation,clvmd,cmirrord,monitor} service to exec before mounting. | ||||
|  | ||||
| Version 2.02.132 - 22nd September 2015 | ||||
| ====================================== | ||||
|   Fix lvmconf to set locking_type=2 if external locking library is requested. | ||||
|   Remove verbose message when rescanning an unchanged device. (2.02.119) | ||||
|   Add origin_uuid, mirror_log_uuid, move_pv_uuid, convert_lv_uuid report fields. | ||||
|   Add pool_lv_uuid, metadata_lv_uuid, data_lv_uuid reporting fields. | ||||
|   Fix PV label processing failure after pvcreate in lvm shell with lvmetad. | ||||
|  | ||||
| Version 2.02.131 - 15th September 2015 | ||||
| ====================================== | ||||
|   Rename 'make install_full_man' to install_all_man and add all_man target. | ||||
|   Fix vgimportclone cache_dir path name (2.02.115). | ||||
|   Swapping of LV identifiers handles more complex LVs. | ||||
|   Use passed list of PVS when allocating space in lvconvert --thinpool. | ||||
|   Disallow usage of --stripe and --stripesize when creating cache pool. | ||||
|   Warn user when caching raid or thin pool data LV. | ||||
|   When layering LV, move LV flags with segments. | ||||
|   Ignore persistent cache if configuration changed. (2.02.127) | ||||
|   Fix devices/filter to be applied before disk-accessing filters. (2.02.112) | ||||
|   Make tags only when requested via 'make tags'. | ||||
|   Configure supports --disable-dependency-tracking for one-time builds. | ||||
|   Fix usage of configure.h when building in srcdir != builddir. | ||||
|  | ||||
| Version 2.02.130 - 5th September 2015 | ||||
| ===================================== | ||||
|   Fix use of uninitialized device status if reading outdated .cache record. | ||||
|   Restore support for --monitor option in lvcreate (2.02.112). | ||||
|   Read thin-pool data and metadata percent without flush. | ||||
|   Detect blocked thin-pool and avoid scanning their thin volumes. | ||||
|   Check if dm device is usable before checking its size (2.02.116). | ||||
|   Extend parsing of cache_check version in configure. | ||||
|   Make lvpoll error messages visible in lvmpolld's stderr and in syslog. | ||||
|   Add 'make install_full_man' to install all man pages regardless of config. | ||||
|  | ||||
| Version 2.02.129 - 26th August 2015 | ||||
| =================================== | ||||
|   Drop error message when vgdisplay encounters an exported VG. (2.02.27) | ||||
|   Fix shared library generation to stop exporting internal functions.(2.02.120) | ||||
|   Accept --cachemode with lvconvert. | ||||
|   Fix and improve reporting properties of cache-pool. | ||||
|   Enable usage of --cachepolicy and --cachesetting with lvconvert. | ||||
|   Don't allow to reduce size of thin-pool metadata. | ||||
|   Fix debug buffer overflows in cmirrord logging. | ||||
|   Add --foreground and --help to cmirrord. | ||||
|  | ||||
| Version 2.02.128 - 17th August 2015 | ||||
| =================================== | ||||
|   Allocation setting cache_pool_cachemode is replaced by cache_mode. | ||||
|   Don't attempt to close config file that couldn't be opened. | ||||
|   Check for valid cache mode in validation of cache segment. | ||||
|   Change internal interface handling cache mode and policy. | ||||
|   When no cache policy specified, prefer smq (if available) over mq. | ||||
|   Add demo cache-mq and cache-smq profiles. | ||||
|   Add cmd profilable allocation/cache_policy,cache_settings,cache_mode. | ||||
|   Require cache_check 0.5.4 for use of --clear-needs-check-flag. | ||||
|   Fix lvmetad udev rules to not override SYSTEMD_WANTS, add the service instead. | ||||
|  | ||||
| Version 2.02.127 - 10th August 2015 | ||||
| =================================== | ||||
|   Do not init filters, locking, lvmetad, lvmpolld if command doesn't use it. | ||||
|   Order fields in struct cmd_context more logically. | ||||
|   Add lock_type to lvmcache VG summary and info structs. | ||||
|   Fix regression in cache causing some PVs to bypass filters (2.02.105). | ||||
|   Make configure --enable-realtime the default now. | ||||
|   Update .gitignore and configure.in files to reflect usage of current tree. | ||||
|  | ||||
| Version 2.02.126 - 24th July 2015 | ||||
| ================================= | ||||
|   Fix long option hyphen removal. (2.02.122) | ||||
|   Fix clvmd freeze if client disappears without first releasing its locks. | ||||
|   Fix lvconvert segfaults while performing snapshots merge. | ||||
|   Ignore errors during detection if use_blkid_wiping=1 and --force is used. | ||||
|   Recognise DM_ABORT_ON_INTERNAL_ERRORS env var override in lvm logging fn. | ||||
|   Fix alloc segfault when extending LV with fewer stripes than in first seg. | ||||
|   Fix handling of cache policy name. | ||||
|   Set cache policy before with the first lvm2 cache pool metadata commit. | ||||
|   Fix detection of thin-pool overprovisioning (2.02.124). | ||||
|   Fix lvmpolld segfaults on 32 bit architectures. | ||||
|   Add lvmlockd lock_args validation to vg_validate. | ||||
|   Fix ignored --startstopservices option if running lvmconf with systemd. | ||||
|   Hide sanlock LVs when processing LVs in VG unless named or --all used. | ||||
|  | ||||
| Version 2.02.125 - 7th July 2015 | ||||
| ================================ | ||||
|   Fix getline memory usage in lvmpolld. | ||||
|   Add support --clear-needs-check-flag for cache_check of cache pool metadata. | ||||
|   Add lvmetactl for developer use only. | ||||
|   Rename global/lock_retries to lvmlockd_retries. | ||||
|   Replace --enable-lvmlockd by --enable-lockd-sanlock and --enable-lockd-dlm. | ||||
|  | ||||
| Version 2.02.124 - 3rd July 2015 | ||||
| ================================ | ||||
|   Move sending thin pool messages from resume to suspend phase. | ||||
|   Report warning when pool is overprovisioned and not auto resized. | ||||
|   Recognize free-form date/time values for lv_time field in selection criteria. | ||||
|   Added experimental lvmlockd with configure --enable-lvmlockd. | ||||
|   Fix regression in select to match string fields if using synonyms (2.02.123). | ||||
|   Fix regression when printing more lv names via display_lvname (2.02.122). | ||||
|   Add missing error logging to unlock_vg and sync_local_dev_names callers. | ||||
|  | ||||
| Version 2.02.123 - 30th June 2015 | ||||
| ================================= | ||||
|   Add report/time_format lvm.conf option to define time format for report. | ||||
|   Fix makefile shell compare == when building lvmetad lvmpolld (2.02.120). | ||||
|   Add --type full to lvmconfig for full configuration tree view. | ||||
|   Add undocumented environment variables to lvm man page. (2.02.119) | ||||
|   Add device synchronization point before activating a new snapshot. | ||||
|   Add --withspaces to lvmconfig to add spaces in output for better readability. | ||||
|   Add custom main function to libdaemon. | ||||
|   Use lvmetad to track out-of-date metadata discovered. | ||||
|  | ||||
| Version 2.02.122 - 20th June 2015 | ||||
| ================================= | ||||
|   Flush stdout before printing to stderr. | ||||
|   Use pre-allocated buffer for printed LV names in display_lvname. | ||||
|   Support thins with size of external origin unaligned with thin pool chunk. | ||||
|   Allow extension of reduced thin volumes with external origins. | ||||
|   Consider snapshot and origin LV as unusable if component devices suspended. | ||||
|   Fix lvmconfig segfault on settings with undefined default value (2.02.120). | ||||
|   Add explicit 's' (shared) LV activation mode. | ||||
|   Ignore hyphens in long options names (i.e. --long-option == --longoption). | ||||
|  | ||||
| Version 2.02.121 - 12th June 2015 | ||||
| ================================= | ||||
|   Distinguish between on-disk and lvmetad versions of text metadata. | ||||
|   Remove DL_LIBS from Makefiles for daemons that don't need them. | ||||
|   Zero errno in before strtoul call in dmsetup if tested after the call. | ||||
|   Zero errno in before strtoul call in lvmpolld. | ||||
|   Fix a segfault in pvscan --cache --background command. | ||||
|   Fix test for AREA_PV when checking for failed mirrors. | ||||
|   Do not use --sysinit in lvm2-activation{-early,-net}.service if lvmpolld used. | ||||
|   Maintain outdated PV info in lvmetad till all old metadata is gone from disk. | ||||
|   Do not fail polling when poll LV not found (already finished or removed). | ||||
|   Replace poll_get_copy_vg/lv fns with vg_read() and find_lv() in polldaemon. | ||||
|   Close all device fds only in before sleep call in polldaemon. | ||||
|   Simplify Makefile targets that generate exported symbols. | ||||
|   Move various -D settings from Makefiles to configure.h. | ||||
|  | ||||
| Version 2.02.120 - 15th May 2015 | ||||
| ================================ | ||||
|   Make various adjustments to Makefile compilation flags. | ||||
|   Add lvmpolld debug message class. | ||||
|   Add lvmpolld client mode for querying running server instance for status info. | ||||
|   Fix some libdaemon socket creation and reuse error paths. | ||||
|   Daemons (libdaemon) support exit on idle also in non-systemd environment. | ||||
|   Provide make dist and make rpm targets | ||||
|   Configure lvm.conf for use_lvmetad and use_lvmpolld. | ||||
|   Add lvpoll for cmdline communication with lvmpolld. | ||||
|   Add lvmpolld acting as a free-standing version of polldaemon. | ||||
|   Avoid repeated identical lvmetad VG lookups in commands processing all VGs. | ||||
|   Handle switches to alternative duplicate PVs efficiently with lvmetad. | ||||
|   Properly validate PV size for pvcreate --restorefile. | ||||
|   Fix check if pvcreate wiped device (2.02.117). | ||||
|   Fix storing of vgid when caching metadata (2.02.118). | ||||
|   Fix recursive lvm-config man page. (2.02.119) | ||||
|   Refactor polldaemon interfaces to poll every operation by VG/LV couple | ||||
|   Skip wait after testing in _wait_for_single_lv when polling finished | ||||
|   Return 'None' in python for empty string properties instead of crashing. | ||||
|   Distinguish signed numerical property type in reports for lvm2app library. | ||||
|   Reread raid completion status immediately when progress appears to be zero. | ||||
|   lvm2app closes locking on lvm_quit(). | ||||
|   Configure detects /run or /var/run. | ||||
|   Add missing newline in clvmd --help output. | ||||
|  | ||||
| Version 2.02.119 - 2nd May 2015 | ||||
| =============================== | ||||
|   New LVM_LOG_FILE_EPOCH, LVM_EXPECTED_EXIT_STATUS env vars. Man page to follow. | ||||
|   Remove detailed content from lvm.conf man page: use lvmconfig instead. | ||||
|   Generate complete config files with lvmconfig or 'make generate'. | ||||
|   Also display info on deprecated config with lvmconfig --withcomments. | ||||
|   Display version since which config is deprecated in lvmconfig --withversions. | ||||
|   Add --showdeprecated to lvmconfig to also display deprecated settings. | ||||
|   Hide deprecated settings in lvmconfig output for all types but current,diff. | ||||
|   Introduce support for exit on idle feature in libdaemon | ||||
|   Add --showunsupported to lvmconfig to also display unsupported settings. | ||||
|   Display unsupported settings for lvmconfig --type current,diff only by default | ||||
|   Honour lvmconfig --ignoreunsupported and --ignoreadvanced for all --type. | ||||
|   Make python bindings usable with python3 (and compatible with 2.6 & 2.7). | ||||
|   Add lvmconfig -l|--list as shortcut for lvmconfig --type list --withsummary. | ||||
|   Add lvmconfig --type list to display plain list of configuration settings. | ||||
|   Introduce lvmconfig as the preferred form of 'lvm dumpconfig'. | ||||
|   Add lv_ancestors and lv_descendants reporting fields. | ||||
|   Add --ignorelocal option to dumpconfig to ignore the local section. | ||||
|   Close connection to lvmetad after fork. | ||||
|   Make lvchange able to resume background pvmove polling again. | ||||
|   Split pvmove update metadata fn in an initial one and a subsequent one. | ||||
|   Refactor shared pvmove and lvconvert code into new _poll files. | ||||
|   Add --unconfigured option to dumpconfig to print strings unconfigured. | ||||
|   Add --withsummary option to dumpconfig to print first line - summary comment. | ||||
|   Use number of device holders to help choose between duplicate PVs. | ||||
|   Try to make lvmetad and non-lvmetad duplicate PV handling as similar as poss. | ||||
|   Issue warnings about duplicate PVs discovered by lvmetad. | ||||
|   Track alternative devices with matching PVIDs in lvmetad. | ||||
|   Check for lvm binary in blkdeactivate and skip LVM processing if not present. | ||||
|   Add --enable-halvm and --disable-halvm options to lvmconf script. | ||||
|   Add --services, --mirrorservice and --startstopservices option to lvmconf. | ||||
|   Use proper default value of global/use_lvmetad when processing lvmconf script. | ||||
|   Respect allocation/cling_tag_list during intial contiguous allocation. | ||||
|   Add A_PARTITION_BY_TAGS set when allocated areas should not share tags. | ||||
|   Make changes persist with python addTag/removeTag. | ||||
|   Set correct vgid when updating cache when writing PV metadata. | ||||
|   More efficient clvmd singlenode locking emulation. | ||||
|   Reject lvcreate -m with raid4/5/6 to avoid unexpected layout. | ||||
|   Don't skip invalidation of cached orphans if vg write lck is held (2.02.118). | ||||
|   Log relevant PV tags when using cling allocation. | ||||
|   Add str_list_add_list() to combine two lists. | ||||
|   Fix LV processing with selection to always do the selection on initial state. | ||||
|   Add internal LV_REMOVED LV status flag. | ||||
|  | ||||
| Version 2.02.118 - 23rd March 2015 | ||||
| ================================== | ||||
|   Store metadata size + checksum in lvmcache and add struct lvmcache_vgsummary. | ||||
|   Remove inaccessible clustered PVs from 'pvs -a'. | ||||
|   Don't invalidate cached orphan information while global lock is held. | ||||
|   Avoid rescan of all devices when requested pvscan for removed device. | ||||
|   Measure configuration timestamps with nanoseconds when available. | ||||
|   Disable lvchange of major and minor of pool LVs. | ||||
|   Fix pvscan --cache to not scan and read ignored metadata areas on PVs. | ||||
|   Add After=iscsi-shutdown.service to blk-availability.service systemd unit. | ||||
|   Disallow vgconvert from changing metadata format when lvmetad is used. | ||||
|   Don't do a full read of VG when creating a new VG with an existing name. | ||||
|   Reduce amount of VG metadata parsing when looking for vgname on a PV. | ||||
|   Avoid reparsing same metadata when reading same metadata from multiple PVs. | ||||
|   Save extra device open/close when scanning device for size. | ||||
|   Fix seg_monitor field to report status also for mirrors and thick snapshots. | ||||
|   Replace LVM_WRITE with LVM_WRITE_LOCKED flags in metadata if system ID is set. | ||||
|   Remove ACCESS_NEEDS_SYSTEM_ID VG status flag. (2.02.117) | ||||
|   Enable system ID features. | ||||
|  | ||||
| Version 2.02.117 - 4th March 2015 | ||||
| ================================= | ||||
|   Add CFG_DISABLED for new system ID config settings that must not yet be used. | ||||
|   Preserve original format type field when processing backup files. | ||||
|   Implement status action for lvm2-monitor initscript to display monitored LVs. | ||||
|   Allow lvchange -p to change kernel state only if metadata state differs. | ||||
|   Fix incorrect persistent .cache after report with label fields only (2.02.106). | ||||
|   Reinstate PV tag recognition for pvs if reporting label fields only (2.02.105). | ||||
|   Rescan devices before vgimport with lvmetad so exported VG is seen. | ||||
|   Fix hang by adjusting cluster mirror regionsize, avoiding CPG msg limit. | ||||
|   Do not crash when --cachepolicy is given without --cachesettings. | ||||
|   Add NEEDS_FOREIGN_VGS flag to vgimport so --foreign is always supplied. | ||||
|   Add --foreign to the 6 display and reporting tools and vgcfgbackup. | ||||
|   Install /etc/lvm/lvmlocal.conf template with local section for systemid. | ||||
|   Record creation_host_system_id in lvm2 metadata (never set yet). | ||||
|   Reinstate recursive config file tag section processing. (2.02.99) | ||||
|   Add 'lvm systemid' to display the current system ID (never set yet). | ||||
|   Fix configure to properly recognize --with-default-raid10-segtype option. | ||||
|   Do not refresh filters/rescan if no signature is wiped during pvcreate. | ||||
|   Enforce none external dev info for wiping during pvcreate to avoid races. | ||||
|   Add global/system_id_source and system_id_file to lvm.conf (disabled). | ||||
|   Add support for VG system_id to control host access to VGs. | ||||
|   Update vgextend to use process_each_vg. | ||||
|   Add --ignoreskippedcluster to pvchange. | ||||
|   Allow pvchange to modify several properties at once. | ||||
|   Update pvchange to use process_each_pv. | ||||
|   Fix pvs -a used with lvmetad to filter out devices unsuitable for PVs. | ||||
|   Fix selection to recognize units for ba_start, vg_free and seg_start fields. | ||||
|   Add support for -S/--select to vgexport and vgimport. | ||||
|   Add support for -S/--select to vgdisplay, lvdisplay and pvdisplay without -C. | ||||
|   Add support for -S/--select to vgremove and lvremove. | ||||
|   Add support for -S/--select to vgchange,lvchange and pvchange. | ||||
|   Add infrastructure to support selection for non-reporting tools. | ||||
|   Add LVM_COMMAND_PROFILE env var to set default command profile name to use. | ||||
|   Set CLOEXEC flag on file descriptors originating in libdaemon. | ||||
|  | ||||
| Version 2.02.116 - 30th January 2015 | ||||
| ==================================== | ||||
|   Deactivate unused thin pools activated with lvm2 pre-2.02.112 versions. | ||||
|   Check lock holding LV when lvconverting stacked raid LV in cluster. | ||||
|   Support udev external dev info for filters: PV min size, mpath, md, partition. | ||||
|   Add fw_raid_component_detection lvm.conf option to enable FW raid detection. | ||||
|   Add devices/external_device_info_source lvm.conf option ("none" by default). | ||||
|   Scan pools in for_each_sub_lv() and add for_each_sub_lv_except_pools(). | ||||
|   Fix lvm2app lvm_lv_get_property return value for fields with info/status ioctl. | ||||
|   Fix lvm2app regression in lvm_lv_get_attr causing unknown values (2.02.115). | ||||
|   Set default cache_mode to writehrough when missing in metadata. | ||||
|   Preserve chunk size with repair and metadata swap of a thin pool. | ||||
|   Fix raid --splitmirror 1 functionality (2.02.112). | ||||
|   Fix tree preload to handle splitting raid images. | ||||
|   Do not support unpartitioned DASD devices. | ||||
|   Improve config validation to check if setting with string value can be empty. | ||||
|  | ||||
| Version 2.02.115 - 21st January 2015 | ||||
| ==================================== | ||||
|   Report segment types without monitoring support as undefined. | ||||
|   Support lvchange --errorwhenfull for thin pools. | ||||
|   Improve the processing and reporting of duplicate PVs. | ||||
|   Report lv_health_status and health attribute also for thin pool. | ||||
|   Add lv_when_full reporting field. | ||||
|   Add support for lvcreate --errorwhenfull y|n for thin pools. | ||||
|   Fix lvconvert --repair to honour resilience requirement for segmented RAID LV. | ||||
|   Filter out partitioned device-mapper devices as unsuitable for use as PVs. | ||||
|   Also notify lvmetad about filtered device if using pvscan --cache DevicePath. | ||||
|   Use LVM's own selection instead of awk expressions in clvmd startup scripts. | ||||
|   Do not filter out snapshot origin LVs as unusable devices for an LVM stack. | ||||
|   Fix incorrect rimage names when converting from mirror to raid1 LV (2.02.112). | ||||
|   Introduce pvremove_many to avoid excessive metadata re-reading and messages. | ||||
|   Check for cmirror availability during cluster mirror creation and activation. | ||||
|   Add cache_policy and cache_settings reporting fields. | ||||
|   Add missing recognition for --binary option with {pv,vg,lv}display -C. | ||||
|   Fix vgimportclone to notify lvmetad about changes done if lvmetad is used. | ||||
|   Fix vgimportclone to properly override config if it is missing in lvm.conf. | ||||
|   Fix automatic use of configure --enable-udev-systemd-background-jobs. | ||||
|   Correctly rename active split LV with -splitmirrors for raid1. | ||||
|   Add report/compact_output to lvm.conf to enable/disable compact report output. | ||||
|   Still restrict mirror region size to power of 2 when VG extent size is not. | ||||
|  | ||||
| Version 2.02.114 - 28th November 2014 | ||||
| ===================================== | ||||
|   Release socket in daemon_close and protocol string in daemon_open error path. | ||||
|   Add --cachepolicy and --cachesettings to lvcreate. | ||||
|   Fix regression when parsing /dev/mapper dir (2.02.112). | ||||
|   Fix missing rounding to 64KB when estimating optimal thin pool chunk size. | ||||
|   Fix typo in clvmd initscript causing CLVMD_STOP_TIMEOUT var to be ignored. | ||||
|   Fix size in pvresize "Resizing to ..." verbose msg to show proper result size. | ||||
|  | ||||
| Version 2.02.113 - 24th November 2014 | ||||
| ===================================== | ||||
|   Add --cachepolicy and --cachesettings options to lvchange. | ||||
|   Validate that converted volume and specified pool volume differ in lvconvert. | ||||
|   Fix regression in vgscan --mknodes usage (2.02.112). | ||||
|   Respect --prefix when setting CLMVD_PATH configure (2.02.89). | ||||
|   Default to configure --enable-udev-systemd-background-jobs for systemd>=205. | ||||
|   Fix ignore_vg() to properly react on various vg_read errors (2.02.112). | ||||
|   Failed recovery returns FAILED_RECOVERY status flag for vg_read(). | ||||
|   Exit with non-zero status code when pvck encounters a problem. | ||||
|   Fix clean_tree after activation/resume for cache target (2.02.112). | ||||
|  | ||||
| Version 2.02.112 - 11th November 2014 | ||||
| ===================================== | ||||
|   Add cache_{read,write}_{hits,misses} reporting fields. | ||||
|   Add cache_{total,used,dirty}_blocks reporting fields. | ||||
|   Add _corig as reserved suffix. | ||||
|   Reduce number of VG writes and commits when creating spare volumes. | ||||
|   When remove_layer_from_lv() removes layer, restore subLV names. | ||||
|   Cache-pool in use becomes invisible LV. | ||||
|   Don't prompt for removal of _pmspare in VG without pool metadata LV. | ||||
|   Deactivation of snapshot origin detects and deactivates left-over snapshots. | ||||
|   Properly report error when taking snapshot of any cache type LV. | ||||
|   Add basic thread debugging messages to dmeventd. | ||||
|   Include threads being shutdown in dmeventd device registration responses. | ||||
|   Inital support for external users of thin pools based on transaction_id. | ||||
|   Report some basic percentage info for cache pools. | ||||
|   Introduce size_mb_arg_with_percent() for advanced size arg reading. | ||||
|   Add extra support for '.' as decimal point in size args. | ||||
|   Add configure parameters for default segment type choices. | ||||
|   Add global/sparse_segtype_default setting to use thin for --type sparse. | ||||
|   Update and correct lvcreate and lvcovert man pages. | ||||
|   Mark pools and snapshots as unzeroable volumes. | ||||
|   Check for zeroing of volume after segment type is fully detected. | ||||
|   Better support for persistent major and minor options with lvcreate. | ||||
|   Refactor lvcreate towards more complete validation of all supported options. | ||||
|   Support lvcreate --type linear. | ||||
|   Improve _should_wipe_lv() to warn with message. | ||||
|   Inform about temporarily created volumes only in verbose mode. | ||||
|   Better support for --test mode with pool creation. | ||||
|   Query lock holding LV when replacing and converting raid volumes. | ||||
|   Add extra validate for locked lv within validate_lv_cache_create(). | ||||
|   Add internal lvseg_name() function. | ||||
|   Skip use of lock files for virtual internal VG names. | ||||
|   Fix selection on {vg,lv}_permissions fields to properly match selection criteria. | ||||
|   Fix lv_permissions reporting to display read-only{-override} instead of blank. | ||||
|   Fix liblvm2cmd and lvm shell to respect quotes around args in cmd line string. | ||||
|   Permit extent sizes > 128KB that are not power of 2 with lvm2 format. | ||||
|   Remove workaround for lvm2-monitor.service hang on stop if lvmetad stopped. | ||||
|   Change vgremove to use process_each_lv_in_vg. | ||||
|   Allow lvconvert --repair and --splitmirrors on internal LVs. | ||||
|   Introduce WARN_ flags to control some metadata warning messages. | ||||
|   Use process_each_pv in vgreduce. | ||||
|   Refactor process_each_pv in toollib. | ||||
|   Introduce single validation routine for pool chunk size. | ||||
|   Support --yes like --force in vg/lvremove to skip y|n prompt. | ||||
|   Support --yes with lvconvert --splitsnapshot. | ||||
|   Fix detection of unsupported thin external lvconversions. | ||||
|   Fix detection of unsupported cache and thin pool lvconversions. | ||||
|   Fix detection of unsupported lvconversion of cache to snapshot. | ||||
|   Improve code for creation of cache and cache pool volumes. | ||||
|   Check cluster-wide (not local) active status before removing LV. | ||||
|   Properly check if activation of removed cached LV really activated. | ||||
|   lvremove cached LV removes cachepool (keep with lvconvert --splitcache). | ||||
|   Always remove spare LV with last removed pool volume. | ||||
|   Support lvconvert --splitcache and --uncache of cached LV. | ||||
|   Option --cache has also shortcut -H (i.e. lvcreate -H). | ||||
|   Refactor lvcreate code and better preserve --type argument. | ||||
|   Refactor filter processing around lvmetad. | ||||
|   Refactor process_each_lv in toollib. | ||||
|   Refactor process_each_vg in toollib. | ||||
|   Pools cannot be used as external origin. | ||||
|   Use lv_update_and_reload() for snapshot reload. | ||||
|   Don't print message in adjusted_mirror_region_size() in activation. | ||||
|   Improve lv_update_and_reload() to find out proper lock holding LV. | ||||
|   Improve search of LV in lv_ondisk(). | ||||
|   Do not scan sysfs in lv_check_not_in_use() when device is closed. | ||||
|   Backup final metadata after resync of mirror/raid. | ||||
|   Unify handling of --persistent option for lvcreate and lvchange. | ||||
|   Validate major and minor numbers stored in metadata. | ||||
|   Use -fPIE when linking -pie executables. | ||||
|   Support DEBUG_MEMLOCK to trap unsupported mmap usage. | ||||
|   Enable cache segment type by default. | ||||
|   Ensure only supported volume types are used with cache segments. | ||||
|   Fix inablility to specify cachemode when 'lvconvert'ing to cache-pool. | ||||
|   Grab cluster lock for active LVs when setting clustered attribute. | ||||
|   Disable vgchange of clustered attribute with any active LV in VG. | ||||
|   Use va_copy to properly pass va_list through functions. | ||||
|   Add function to detect rotational devices. | ||||
|   Review internal checks for mirror/raid/pvmove volumes. | ||||
| @@ -865,17 +8,16 @@ Version 2.02.112 - 11th November 2014 | ||||
|   Fix cmirror endian conversions. | ||||
|   Introduce lv_is_pvmove/locked/converting/merging macros. | ||||
|   Avoid leaving linear logical volume when thin pool creation fails. | ||||
|   Demote an error to a warning when devices known to lvmetad are filtered out. | ||||
|   Re-order filter evaluation, making component filters global. | ||||
|   Don't leak alloc_handle on raid target error path. | ||||
|   Properly validate raid leg names. | ||||
|   Archive metadata before starting their modification in raid target. | ||||
|   Add missing vg_revert() in suspend_lv() raid and snapshot error path. | ||||
|   Add missing vg_revert in suspend_lv() error path in raid target. | ||||
|   Add missing backup of lvm2 metadata after some raid modifications. | ||||
|   Use vg memory pool for extent allocation. | ||||
|   Add allocation/physical_extent_size config option for default PE size of VGs. | ||||
|   Demote an error to a warning when devices known to lvmetad are filtered out. | ||||
|   Re-order filter evaluation, making component filters global. | ||||
|   Fix logic that checks for full scan before iterating through devices. | ||||
|   Introduce common code to modify metadata and reload updated LV. | ||||
|   Introduce common code to modify metadate and reload updated LV. | ||||
|   Fix rename of active snapshot volume in cluster. | ||||
|   Make sure shared libraries are built with RELRO option. | ||||
|  | ||||
|   | ||||
							
								
								
									
										342
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							
							
						
						
									
										342
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							| @@ -1,345 +1,5 @@ | ||||
| Version 1.02.136 -  | ||||
| ====================================== | ||||
|   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 | ||||
| ===================================== | ||||
|   Use fully aligned allocations for dm_pool_strdup/strndup() (1.02.64). | ||||
|   Fix thin-pool table parameter feature order to match kernel output. | ||||
|  | ||||
| Version 1.02.115 - 25th January 2016 | ||||
| Version 1.02.91 -  | ||||
| ==================================== | ||||
|   Fix man page for dmsetup udevcreatecookie. | ||||
|  | ||||
| Version 1.02.114 - 14th December 2015 | ||||
| ===================================== | ||||
|   Better support for dmsetup static linkage. | ||||
|   Extend validity checks on dmeventd client socket. | ||||
|  | ||||
| Version 1.02.113 - 5th December 2015 | ||||
| ==================================== | ||||
|   Mirror plugin in dmeventd uses dm_get_status_mirror(). | ||||
|   Add dm_get_status_mirror() for parsing mirror status line. | ||||
|  | ||||
| Version 1.02.112 - 28th November 2015 | ||||
| ===================================== | ||||
|   Show error message when trying to create unsupported raid type. | ||||
|   Improve preloading sequence of an active thin-pool target. | ||||
|   Drop extra space from cache target line to fix unneded table reloads. | ||||
|  | ||||
| Version 1.02.111 - 23rd November 2015 | ||||
| ===================================== | ||||
|   Extend dm_hash to support multiple values with the same key. | ||||
|   Add missing check for allocation inside dm_split_lvm_name(). | ||||
|   Test dm_task_get_message_response for !NULL in dm_stats_print_region(). | ||||
|   Add checks for failing dm_stats_create() in dmsetup. | ||||
|   Add missing fifo close when failed to initialize client connection. | ||||
|  | ||||
| Version 1.02.110 - 30th October 2015 | ||||
| ==================================== | ||||
|   Disable thin monitoring plugin when it fails too often (>10 times). | ||||
|   Fix/restore parsing of empty field '-' when processing dmeventd event. | ||||
|   Enhance dm_tree_node_size_changed() to recognize size reduction. | ||||
|   Support exit on idle for dmenventd (1 hour). | ||||
|   Add support to allow unmonitor device from plugin itself. | ||||
|   New design for thread co-operation in dmeventd. | ||||
|   Dmeventd read device status with 'noflush'. | ||||
|   Dmeventd closes control device when no device is monitored. | ||||
|   Thin plugin for dmeventd improved percentage usage. | ||||
|   Snapshot plugin for dmeventd improved percentage usage. | ||||
|   Add dm_hold_control_dev to allow holding of control device open. | ||||
|   Add dm_report_compact_given_fields to remove given empty fields from report. | ||||
|   Use libdm status parsing and local mem raid dmeventd plugin. | ||||
|   Use local mem pool and lock only lvm2 execution for mirror dmeventd plugin. | ||||
|   Lock protect only lvm2 execution for snapshot and thin dmeventd plugin. | ||||
|   Use local mempool for raid and mirror plugins. | ||||
|   Reworked thread initialization for dmeventd plugins. | ||||
|   Dmeventd handles snapshot overflow for now equally as invalid. | ||||
|   Convert dmeventd to use common logging macro system from libdm. | ||||
|   Return -ENOMEM when device registration fails instead of 0 (=success). | ||||
|   Enforce writethrough mode for cleaner policy. | ||||
|   Add support for recognition and deactivation of MD devices to blkdeactivate. | ||||
|   Move target status functions out of libdm-deptree. | ||||
|   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. | ||||
|  | ||||
| Version 1.02.109 - 22nd September 2015 | ||||
| ====================================== | ||||
|   Update man pages for dmsetup and dmstats. | ||||
|   Improve help text for dmsetup. | ||||
|   Use --noflush and --nolockfs when removing device with --force. | ||||
|   Parse new Overflow status string for snapshot target. | ||||
|   Check dir path components are valid if using dm_create_dir, error out if not. | ||||
|   Fix /dev/mapper handling to remove dangling entries if symlinks are found. | ||||
|   Make it possible to use blank value as selection for string list report field. | ||||
|  | ||||
| Version 1.02.108 - 15th September 2015 | ||||
| ====================================== | ||||
|   Do not check for full thin pool when activating without messages (1.02.107). | ||||
|  | ||||
| Version 1.02.107 - 5th September 2015 | ||||
| ===================================== | ||||
|   Parse thin-pool status with one single routine internally. | ||||
|   Add --histogram to select default histogram fields for list and report. | ||||
|   Add report fields for displaying latency histogram configuration and data. | ||||
|   Add dmstats --bounds to specify histogram boundaries for a new region. | ||||
|   Add dm_histogram_to_string() to format histogram data in string form. | ||||
|   Add public methods to libdm to access numerical histogram config and data. | ||||
|   Parse and store histogram data in dm_stats_list() and dm_stats_populate(). | ||||
|   Add an argument to specify histogram bounds to dm_stats_create_region(). | ||||
|   Add dm_histogram_bounds_from_{string,uint64_t}() to parse histogram bounds. | ||||
|   Add dm_histogram handle type to represent a latency histogram and its bounds. | ||||
|   Fix devmapper.pc pkgconfig file to not reference non-existent rt.pc file. | ||||
|   Reinstate dm_task_get_info@Base to libdevmapper exports. (1.02.106) | ||||
|  | ||||
| Version 1.02.106 - 26th August 2015 | ||||
| =================================== | ||||
|   Add 'precise' column to statistics reports. | ||||
|   Add --precise switch to 'dmstats create' to request nanosecond counters. | ||||
|   Add precise argument to dm_stats_create_region(). | ||||
|   Add support to libdm-stats for precise_timestamps | ||||
|  | ||||
| Version 1.02.105 - 17th August 2015 | ||||
| =================================== | ||||
|   Fix 'dmstats list -o all' segfault. | ||||
|   Separate dmstats statistics fields from region information fields. | ||||
|   Add interval and interval_ns fields to dmstats reports. | ||||
|   Do not include internal glibc headers in libdm-timestamp.c (1.02.104) | ||||
|   Exit immediately if no device is supplied to dmsetup wipe_table. | ||||
|   Suppress dmsetup report headings when no data is output. (1.02.104) | ||||
|   Adjust dmsetup usage/help output selection to match command invoked. | ||||
|   Fix dmsetup -o all to select correct fields in splitname report. | ||||
|   Restructure internal dmsetup argument handling across all commands. | ||||
|   Add dm_report_is_empty() to indicate there is no data awaiting output. | ||||
|   Add more arg validation for dm_tree_node_add_cache_target(). | ||||
|   Add --alldevices switch to replace use of --force for stats create / delete. | ||||
|  | ||||
| Version 1.02.104 - 10th August 2015 | ||||
| =================================== | ||||
|   Add dmstats.8 man page | ||||
|   Add dmstats --segments switch to create one region per device segment. | ||||
|   Add dmstats --regionid, --allregions to specify a single / all stats regions. | ||||
|   Add dmstats --allprograms for stats commands that filter by program ID. | ||||
|   Add dmstats --auxdata and --programid args to specify aux data and program ID. | ||||
|   Add report stats sub-command to provide repeating stats reports. | ||||
|   Add clear, delete, list, and print stats sub-commands. | ||||
|   Add create stats sub-command and --start, --length, --areas and --areasize. | ||||
|   Recognize 'dmstats' as an alias for 'dmsetup stats' when run with this name. | ||||
|   Add a 'stats' command to dmsetup to configure, manage and report stats data. | ||||
|   Add statistics fields to dmsetup -o. | ||||
|   Add libdm-stats library to allow management of device-mapper statistics. | ||||
|   Add --nosuffix to suppress dmsetup unit suffixes in report output. | ||||
|   Add --units to control dmsetup report field output units. | ||||
|   Add support to redisplay column headings for repeating column reports. | ||||
|   Fix report header and row resource leaks. | ||||
|   Report timestamps of ioctls with dmsetup -vvv. | ||||
|   Recognize report field name variants without any underscores too. | ||||
|   Add dmsetup --interval and --count to repeat reports at specified intervals. | ||||
|   Add dm_timestamp functions to libdevmapper. | ||||
|   Recognise vg/lv name format in dmsetup. | ||||
|   Move size display code to libdevmapper as dm_size_to_string. | ||||
|  | ||||
| Version 1.02.103 - 24th July 2015 | ||||
| ================================= | ||||
|   Introduce libdevmapper wrappers for all malloc-related functions. | ||||
|  | ||||
| Version 1.02.102 - 7th July 2015 | ||||
| ================================ | ||||
|   Include tool.h for default non-library use. | ||||
|   Introduce format macros with embedded % such as FMTu64. | ||||
|  | ||||
| Version 1.02.101 - 3rd July 2015 | ||||
| ================================ | ||||
|   Add experimental support to passing messages in suspend tree. | ||||
|   Add dm_report_value_cache_{set,get} to support caching during report/select. | ||||
|   Add dm_report_reserved_handler to handle report reserved value actions. | ||||
|   Support dynamic value in select: DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE. | ||||
|   Support fuzzy names in select: DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES. | ||||
|   Thin pool trace messages show a device name and major:minor. | ||||
|  | ||||
| Version 1.02.100 - 30th June 2015 | ||||
| ================================= | ||||
|   Add since, after, until and before time operators to be used in selection. | ||||
|   Add support for time in reports and selection: DM_REPORT_FIELD_TYPE_TIME. | ||||
|   Support report reserved value ranges: DM_REPORT_FIELD_RESERVED_VALUE_RANGE. | ||||
|   Support report reserved value names: DM_REPORT_FIELD_RESERVED_VALUE_NAMED. | ||||
|   Add DM_CONFIG_VALUE_FMT_{INT_OCTAL,STRING_NO_QUOTES} config value format flag. | ||||
|   Add DM_CONFIG_VALUE_FMT_COMMON_{ARRAY,EXTRA_SPACE} config value format flag. | ||||
|   Add dm_config_value_{get,set}_format_flags to get and set config value format. | ||||
|  | ||||
| Version 1.02.99 - 20th June 2015 | ||||
| ================================ | ||||
|   New dm_tree_node_set_thin_pool_read_only(DM_1_02_99) for read-only thin pool. | ||||
|   Enhance error message when thin-pool message fails. | ||||
|   Fix dmeventd logging to avoid threaded use of static variable. | ||||
|   Remove redundant dmeventd SIGALRM coded. | ||||
|  | ||||
| Version 1.02.98 - 12th June 2015 | ||||
| ================================ | ||||
|   Add dm_task_get_errno() to return any unexpected errno from a dm ioctl call. | ||||
|   Use copy of errno made after each dm ioctl call in case errno changes later. | ||||
|  | ||||
| Version 1.02.97 - 15th May 2015 | ||||
| =============================== | ||||
|   New dm_task_get_info(DM_1_02_97) supports internal_suspend state. | ||||
|   New symbols are versioned and comes with versioned symbol name (DM_1_02_97). | ||||
|  | ||||
| Version 1.02.96 - 2nd May 2015 | ||||
| ============================== | ||||
|   Fix selection to not match if using reserved value in criteria with >,<,>=,<. | ||||
|   Fix selection to not match reserved values for size fields if using >,<,>=,<. | ||||
|   Include uuid or device number in log message after ioctl failure. | ||||
|   Add DM_INTERNAL_SUSPEND_FLAG to dm-ioctl.h. | ||||
|   Install blkdeactivate script and its man page with make install_device-mapper. | ||||
|  | ||||
| Version 1.02.95 - 15th March 2015 | ||||
| ================================= | ||||
|   Makefile regenerated. | ||||
|  | ||||
| Version 1.02.94 - 4th March 2015 | ||||
| ================================ | ||||
|   Add dm_report_object_is_selected for generalized interface for report/select. | ||||
|  | ||||
| Version 1.02.93 - 21st January 2015 | ||||
| =================================== | ||||
|   Reduce severity of ioctl error message when dmeventd waitevent is interrupted. | ||||
|   Report 'unknown version' when incompatible version numbers were not obtained. | ||||
|   Report more info from thin pool status (out of data, metadata-ro, fail). | ||||
|   Support error_if_no_space for thin pool target. | ||||
|   Fix segfault while using selection with regex and unbuffered reporting. | ||||
|   Add dm_report_compact_fields to remove empty fields from report output. | ||||
|   Remove unimplemented dm_report_set_output_selection from libdevmapper.h. | ||||
|  | ||||
| Version 1.02.92 - 24th November 2014 | ||||
| ==================================== | ||||
|   Fix memory corruption with sorting empty string lists (1.02.86). | ||||
|   Fix man dmsetup.8 syntax warning of Groff | ||||
|   Accept unquoted strings and / in place of {} when parsing configs. | ||||
|  | ||||
| Version 1.02.91 - 11th November 2014 | ||||
| ==================================== | ||||
|   Update cache creation and dm_config_node to pass policy. | ||||
|   Allow activation of any thin-pool if transaction_id supplied is 0. | ||||
|   Don't print uninitialized stack bytes when non-root uses dm_check_version(). | ||||
|   Fix selection criteria to not match reserved values when using >, <, >=, <. | ||||
|   Add DM_LIST_HEAD_INIT macro to libdevmapper.h. | ||||
|   Fix dm_is_dm_major to not issue error about missing /proc lines for dm module. | ||||
|  | ||||
| Version 1.02.90 - 1st September 2014 | ||||
|   | ||||
| @@ -37,10 +37,6 @@ AC_DEFUN([AC_TRY_CCFLAG], | ||||
|     fi | ||||
| ]) | ||||
|  | ||||
| dnl AC_IF_YES([TEST-FOR-YES], [ACTION-IF-TRUE], [ACTION-IF-FALSE]) | ||||
| dnl AS_IF() abstraction, checks shell variable for 'yes' | ||||
| AC_DEFUN([AC_IF_YES], [AS_IF([test $$1 = yes], [$2], [$3])]) | ||||
|  | ||||
| dnl AC_TRY_LDFLAGS([LDFLAGS], [VAR], [ACTION-IF-WORKS], [ACTION-IF-FAILS]) | ||||
| dnl check if $CC supports given ld flags | ||||
|  | ||||
|   | ||||
							
								
								
									
										313
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										313
									
								
								aclocal.m4
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| # generated automatically by aclocal 1.15 -*- Autoconf -*- | ||||
| # generated automatically by aclocal 1.13.4 -*- Autoconf -*- | ||||
|  | ||||
| # Copyright (C) 1996-2014 Free Software Foundation, Inc. | ||||
| # Copyright (C) 1996-2013 Free Software Foundation, Inc. | ||||
|  | ||||
| # This file is free software; the Free Software Foundation | ||||
| # gives unlimited permission to copy and/or distribute it, | ||||
| @@ -12,63 +12,6 @@ | ||||
| # PARTICULAR PURPOSE. | ||||
|  | ||||
| m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) | ||||
| # =========================================================================== | ||||
| #     http://www.gnu.org/software/autoconf-archive/ax_python_module.html | ||||
| # =========================================================================== | ||||
| # | ||||
| # SYNOPSIS | ||||
| # | ||||
| #   AX_PYTHON_MODULE(modname[, fatal, python]) | ||||
| # | ||||
| # DESCRIPTION | ||||
| # | ||||
| #   Checks for Python module. | ||||
| # | ||||
| #   If fatal is non-empty then absence of a module will trigger an error. | ||||
| #   The third parameter can either be "python" for Python 2 or "python3" for | ||||
| #   Python 3; defaults to Python 3. | ||||
| # | ||||
| # LICENSE | ||||
| # | ||||
| #   Copyright (c) 2008 Andrew Collier | ||||
| # | ||||
| #   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 8 | ||||
|  | ||||
| AU_ALIAS([AC_PYTHON_MODULE], [AX_PYTHON_MODULE]) | ||||
| AC_DEFUN([AX_PYTHON_MODULE],[ | ||||
|     if test -z $PYTHON; | ||||
|     then | ||||
|         if test -z "$3"; | ||||
|         then | ||||
|             PYTHON="python3" | ||||
|         else | ||||
|             PYTHON="$3" | ||||
|         fi | ||||
|     fi | ||||
|     PYTHON_NAME=`basename $PYTHON` | ||||
|     AC_MSG_CHECKING($PYTHON_NAME module: $1) | ||||
|     $PYTHON -c "import $1" 2>/dev/null | ||||
|     if test $? -eq 0; | ||||
|     then | ||||
|         AC_MSG_RESULT(yes) | ||||
|         eval AS_TR_CPP(HAVE_PYMOD_$1)=yes | ||||
|     else | ||||
|         AC_MSG_RESULT(no) | ||||
|         eval AS_TR_CPP(HAVE_PYMOD_$1)=no | ||||
|         # | ||||
|         if test -n "$2" | ||||
|         then | ||||
|             AC_MSG_ERROR(failed to find required module $1) | ||||
|             exit 1 | ||||
|         fi | ||||
|     fi | ||||
| ]) | ||||
|  | ||||
| # pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*- | ||||
| # serial 1 (pkg-config-0.24) | ||||
| #  | ||||
| @@ -284,256 +227,4 @@ AS_VAR_COPY([$1], [pkg_cv_][$1]) | ||||
| AS_VAR_IF([$1], [""], [$5], [$4])dnl | ||||
| ])# PKG_CHECK_VAR | ||||
|  | ||||
| # Copyright (C) 1999-2014 Free Software Foundation, Inc. | ||||
| # | ||||
| # This file is free software; the Free Software Foundation | ||||
| # gives unlimited permission to copy and/or distribute it, | ||||
| # with or without modifications, as long as this notice is preserved. | ||||
|  | ||||
|  | ||||
| # AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) | ||||
| # --------------------------------------------------------------------------- | ||||
| # Adds support for distributing Python modules and packages.  To | ||||
| # install modules, copy them to $(pythondir), using the python_PYTHON | ||||
| # automake variable.  To install a package with the same name as the | ||||
| # automake package, install to $(pkgpythondir), or use the | ||||
| # pkgpython_PYTHON automake variable. | ||||
| # | ||||
| # The variables $(pyexecdir) and $(pkgpyexecdir) are provided as | ||||
| # locations to install python extension modules (shared libraries). | ||||
| # Another macro is required to find the appropriate flags to compile | ||||
| # extension modules. | ||||
| # | ||||
| # If your package is configured with a different prefix to python, | ||||
| # users will have to add the install directory to the PYTHONPATH | ||||
| # environment variable, or create a .pth file (see the python | ||||
| # documentation for details). | ||||
| # | ||||
| # If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will | ||||
| # cause an error if the version of python installed on the system | ||||
| # doesn't meet the requirement.  MINIMUM-VERSION should consist of | ||||
| # numbers and dots only. | ||||
| AC_DEFUN([AM_PATH_PYTHON], | ||||
|  [ | ||||
|   dnl Find a Python interpreter.  Python versions prior to 2.0 are not | ||||
|   dnl supported. (2.0 was released on October 16, 2000). | ||||
|   m4_define_default([_AM_PYTHON_INTERPRETER_LIST], | ||||
| [python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl | ||||
|  python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0]) | ||||
|  | ||||
|   AC_ARG_VAR([PYTHON], [the Python interpreter]) | ||||
|  | ||||
|   m4_if([$1],[],[ | ||||
|     dnl No version check is needed. | ||||
|     # Find any Python interpreter. | ||||
|     if test -z "$PYTHON"; then | ||||
|       AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) | ||||
|     fi | ||||
|     am_display_PYTHON=python | ||||
|   ], [ | ||||
|     dnl A version check is needed. | ||||
|     if test -n "$PYTHON"; then | ||||
|       # If the user set $PYTHON, use it and don't search something else. | ||||
|       AC_MSG_CHECKING([whether $PYTHON version is >= $1]) | ||||
|       AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], | ||||
| 			      [AC_MSG_RESULT([yes])], | ||||
| 			      [AC_MSG_RESULT([no]) | ||||
| 			       AC_MSG_ERROR([Python interpreter is too old])]) | ||||
|       am_display_PYTHON=$PYTHON | ||||
|     else | ||||
|       # Otherwise, try each interpreter until we find one that satisfies | ||||
|       # VERSION. | ||||
|       AC_CACHE_CHECK([for a Python interpreter with version >= $1], | ||||
| 	[am_cv_pathless_PYTHON],[ | ||||
| 	for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do | ||||
| 	  test "$am_cv_pathless_PYTHON" = none && break | ||||
| 	  AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) | ||||
| 	done]) | ||||
|       # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. | ||||
|       if test "$am_cv_pathless_PYTHON" = none; then | ||||
| 	PYTHON=: | ||||
|       else | ||||
|         AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) | ||||
|       fi | ||||
|       am_display_PYTHON=$am_cv_pathless_PYTHON | ||||
|     fi | ||||
|   ]) | ||||
|  | ||||
|   if test "$PYTHON" = :; then | ||||
|   dnl Run any user-specified action, or abort. | ||||
|     m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) | ||||
|   else | ||||
|  | ||||
|   dnl Query Python for its version number.  Getting [:3] seems to be | ||||
|   dnl the best way to do this; it's what "site.py" does in the standard | ||||
|   dnl library. | ||||
|  | ||||
|   AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], | ||||
|     [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) | ||||
|   AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) | ||||
|  | ||||
|   dnl Use the values of $prefix and $exec_prefix for the corresponding | ||||
|   dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX.  These are made | ||||
|   dnl distinct variables so they can be overridden if need be.  However, | ||||
|   dnl general consensus is that you shouldn't need this ability. | ||||
|  | ||||
|   AC_SUBST([PYTHON_PREFIX], ['${prefix}']) | ||||
|   AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) | ||||
|  | ||||
|   dnl At times (like when building shared libraries) you may want | ||||
|   dnl to know which OS platform Python thinks this is. | ||||
|  | ||||
|   AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], | ||||
|     [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) | ||||
|   AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) | ||||
|  | ||||
|   # Just factor out some code duplication. | ||||
|   am_python_setup_sysconfig="\ | ||||
| import sys | ||||
| # Prefer sysconfig over distutils.sysconfig, for better compatibility | ||||
| # with python 3.x.  See automake bug#10227. | ||||
| try: | ||||
|     import sysconfig | ||||
| except ImportError: | ||||
|     can_use_sysconfig = 0 | ||||
| else: | ||||
|     can_use_sysconfig = 1 | ||||
| # Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: | ||||
| # <https://github.com/pypa/virtualenv/issues/118> | ||||
| try: | ||||
|     from platform import python_implementation | ||||
|     if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7': | ||||
|         can_use_sysconfig = 0 | ||||
| except ImportError: | ||||
|     pass" | ||||
|  | ||||
|   dnl Set up 4 directories: | ||||
|  | ||||
|   dnl pythondir -- where to install python scripts.  This is the | ||||
|   dnl   site-packages directory, not the python standard library | ||||
|   dnl   directory like in previous automake betas.  This behavior | ||||
|   dnl   is more consistent with lispdir.m4 for example. | ||||
|   dnl Query distutils for this directory. | ||||
|   AC_CACHE_CHECK([for $am_display_PYTHON script directory], | ||||
|     [am_cv_python_pythondir], | ||||
|     [if test "x$prefix" = xNONE | ||||
|      then | ||||
|        am_py_prefix=$ac_default_prefix | ||||
|      else | ||||
|        am_py_prefix=$prefix | ||||
|      fi | ||||
|      am_cv_python_pythondir=`$PYTHON -c " | ||||
| $am_python_setup_sysconfig | ||||
| if can_use_sysconfig: | ||||
|     sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) | ||||
| else: | ||||
|     from distutils import sysconfig | ||||
|     sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') | ||||
| sys.stdout.write(sitedir)"` | ||||
|      case $am_cv_python_pythondir in | ||||
|      $am_py_prefix*) | ||||
|        am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` | ||||
|        am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` | ||||
|        ;; | ||||
|      *) | ||||
|        case $am_py_prefix in | ||||
|          /usr|/System*) ;; | ||||
|          *) | ||||
| 	  am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages | ||||
| 	  ;; | ||||
|        esac | ||||
|        ;; | ||||
|      esac | ||||
|     ]) | ||||
|   AC_SUBST([pythondir], [$am_cv_python_pythondir]) | ||||
|  | ||||
|   dnl pkgpythondir -- $PACKAGE directory under pythondir.  Was | ||||
|   dnl   PYTHON_SITE_PACKAGE in previous betas, but this naming is | ||||
|   dnl   more consistent with the rest of automake. | ||||
|  | ||||
|   AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) | ||||
|  | ||||
|   dnl pyexecdir -- directory for installing python extension modules | ||||
|   dnl   (shared libraries) | ||||
|   dnl Query distutils for this directory. | ||||
|   AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], | ||||
|     [am_cv_python_pyexecdir], | ||||
|     [if test "x$exec_prefix" = xNONE | ||||
|      then | ||||
|        am_py_exec_prefix=$am_py_prefix | ||||
|      else | ||||
|        am_py_exec_prefix=$exec_prefix | ||||
|      fi | ||||
|      am_cv_python_pyexecdir=`$PYTHON -c " | ||||
| $am_python_setup_sysconfig | ||||
| if can_use_sysconfig: | ||||
|     sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) | ||||
| else: | ||||
|     from distutils import sysconfig | ||||
|     sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') | ||||
| sys.stdout.write(sitedir)"` | ||||
|      case $am_cv_python_pyexecdir in | ||||
|      $am_py_exec_prefix*) | ||||
|        am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` | ||||
|        am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` | ||||
|        ;; | ||||
|      *) | ||||
|        case $am_py_exec_prefix in | ||||
|          /usr|/System*) ;; | ||||
|          *) | ||||
| 	   am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages | ||||
| 	   ;; | ||||
|        esac | ||||
|        ;; | ||||
|      esac | ||||
|     ]) | ||||
|   AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) | ||||
|  | ||||
|   dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) | ||||
|  | ||||
|   AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) | ||||
|  | ||||
|   dnl Run any user-specified action. | ||||
|   $2 | ||||
|   fi | ||||
|  | ||||
| ]) | ||||
|  | ||||
|  | ||||
| # AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) | ||||
| # --------------------------------------------------------------------------- | ||||
| # Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. | ||||
| # Run ACTION-IF-FALSE otherwise. | ||||
| # This test uses sys.hexversion instead of the string equivalent (first | ||||
| # word of sys.version), in order to cope with versions such as 2.2c1. | ||||
| # This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). | ||||
| AC_DEFUN([AM_PYTHON_CHECK_VERSION], | ||||
|  [prog="import sys | ||||
| # split strings by '.' and convert to numeric.  Append some zeros | ||||
| # because we need at least 4 digits for the hex conversion. | ||||
| # map returns an iterator in Python 3.0 and a list in 2.x | ||||
| minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] | ||||
| minverhex = 0 | ||||
| # xrange is not present in Python 3.0 and range returns an iterator | ||||
| for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] | ||||
| sys.exit(sys.hexversion < minverhex)" | ||||
|   AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) | ||||
|  | ||||
| # Copyright (C) 2001-2014 Free Software Foundation, Inc. | ||||
| # | ||||
| # This file is free software; the Free Software Foundation | ||||
| # gives unlimited permission to copy and/or distribute it, | ||||
| # with or without modifications, as long as this notice is preserved. | ||||
|  | ||||
| # AM_RUN_LOG(COMMAND) | ||||
| # ------------------- | ||||
| # Run COMMAND, save the exit status in ac_status, and log it. | ||||
| # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) | ||||
| AC_DEFUN([AM_RUN_LOG], | ||||
| [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD | ||||
|    ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD | ||||
|    ac_status=$? | ||||
|    echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD | ||||
|    (exit $ac_status); }]) | ||||
|  | ||||
| m4_include([acinclude.m4]) | ||||
|   | ||||
| @@ -1,170 +0,0 @@ | ||||
| #!/bin/sh | ||||
| # py-compile - Compile a Python program | ||||
|  | ||||
| scriptversion=2011-06-08.12; # UTC | ||||
|  | ||||
| # Copyright (C) 2000-2014 Free Software Foundation, Inc. | ||||
|  | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2, or (at your option) | ||||
| # any later version. | ||||
|  | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
|  | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| # As a special exception to the GNU General Public License, if you | ||||
| # distribute this file as part of a program that contains a | ||||
| # configuration script generated by Autoconf, you may include it under | ||||
| # the same distribution terms that you use for the rest of that program. | ||||
|  | ||||
| # This file is maintained in Automake, please report | ||||
| # bugs to <bug-automake@gnu.org> or send patches to | ||||
| # <automake-patches@gnu.org>. | ||||
|  | ||||
| if [ -z "$PYTHON" ]; then | ||||
|   PYTHON=python | ||||
| fi | ||||
|  | ||||
| me=py-compile | ||||
|  | ||||
| usage_error () | ||||
| { | ||||
|   echo "$me: $*" >&2 | ||||
|   echo "Try '$me --help' for more information." >&2 | ||||
|   exit 1 | ||||
| } | ||||
|  | ||||
| basedir= | ||||
| destdir= | ||||
| while test $# -ne 0; do | ||||
|   case "$1" in | ||||
|     --basedir) | ||||
|       if test $# -lt 2; then | ||||
|         usage_error "option '--basedir' requires an argument" | ||||
|       else | ||||
|         basedir=$2 | ||||
|       fi | ||||
|       shift | ||||
|       ;; | ||||
|     --destdir) | ||||
|       if test $# -lt 2; then | ||||
|         usage_error "option '--destdir' requires an argument" | ||||
|       else | ||||
|         destdir=$2 | ||||
|       fi | ||||
|       shift | ||||
|       ;; | ||||
|     -h|--help) | ||||
|       cat <<\EOF | ||||
| Usage: py-compile [--help] [--version] [--basedir DIR] [--destdir DIR] FILES..." | ||||
|  | ||||
| Byte compile some python scripts FILES.  Use --destdir to specify any | ||||
| leading directory path to the FILES that you don't want to include in the | ||||
| byte compiled file.  Specify --basedir for any additional path information you | ||||
| do want to be shown in the byte compiled file. | ||||
|  | ||||
| Example: | ||||
|   py-compile --destdir /tmp/pkg-root --basedir /usr/share/test test.py test2.py | ||||
|  | ||||
| Report bugs to <bug-automake@gnu.org>. | ||||
| EOF | ||||
|       exit $? | ||||
|       ;; | ||||
|     -v|--version) | ||||
|       echo "$me $scriptversion" | ||||
|       exit $? | ||||
|       ;; | ||||
|     --) | ||||
|       shift | ||||
|       break | ||||
|       ;; | ||||
|     -*) | ||||
|       usage_error "unrecognized option '$1'" | ||||
|       ;; | ||||
|     *) | ||||
|       break | ||||
|       ;; | ||||
|   esac | ||||
|   shift | ||||
| done | ||||
|  | ||||
| files=$* | ||||
| if test -z "$files"; then | ||||
|     usage_error "no files given" | ||||
| fi | ||||
|  | ||||
| # if basedir was given, then it should be prepended to filenames before | ||||
| # byte compilation. | ||||
| if [ -z "$basedir" ]; then | ||||
|     pathtrans="path = file" | ||||
| else | ||||
|     pathtrans="path = os.path.join('$basedir', file)" | ||||
| fi | ||||
|  | ||||
| # if destdir was given, then it needs to be prepended to the filename to | ||||
| # byte compile but not go into the compiled file. | ||||
| if [ -z "$destdir" ]; then | ||||
|     filetrans="filepath = path" | ||||
| else | ||||
|     filetrans="filepath = os.path.normpath('$destdir' + os.sep + path)" | ||||
| fi | ||||
|  | ||||
| $PYTHON -c " | ||||
| import sys, os, py_compile, imp | ||||
|  | ||||
| files = '''$files''' | ||||
|  | ||||
| sys.stdout.write('Byte-compiling python modules...\n') | ||||
| for file in files.split(): | ||||
|     $pathtrans | ||||
|     $filetrans | ||||
|     if not os.path.exists(filepath) or not (len(filepath) >= 3 | ||||
|                                             and filepath[-3:] == '.py'): | ||||
| 	    continue | ||||
|     sys.stdout.write(file) | ||||
|     sys.stdout.flush() | ||||
|     if hasattr(imp, 'get_tag'): | ||||
|         py_compile.compile(filepath, imp.cache_from_source(filepath), path) | ||||
|     else: | ||||
|         py_compile.compile(filepath, filepath + 'c', path) | ||||
| sys.stdout.write('\n')" || exit $? | ||||
|  | ||||
| # this will fail for python < 1.5, but that doesn't matter ... | ||||
| $PYTHON -O -c " | ||||
| import sys, os, py_compile, imp | ||||
|  | ||||
| # pypy does not use .pyo optimization | ||||
| if hasattr(sys, 'pypy_translation_info'): | ||||
|     sys.exit(0) | ||||
|  | ||||
| files = '''$files''' | ||||
| sys.stdout.write('Byte-compiling python modules (optimized versions) ...\n') | ||||
| for file in files.split(): | ||||
|     $pathtrans | ||||
|     $filetrans | ||||
|     if not os.path.exists(filepath) or not (len(filepath) >= 3 | ||||
|                                             and filepath[-3:] == '.py'): | ||||
| 	    continue | ||||
|     sys.stdout.write(file) | ||||
|     sys.stdout.flush() | ||||
|     if hasattr(imp, 'get_tag'): | ||||
|         py_compile.compile(filepath, imp.cache_from_source(filepath, False), path) | ||||
|     else: | ||||
|         py_compile.compile(filepath, filepath + 'o', path) | ||||
| sys.stdout.write('\n')" 2>/dev/null || : | ||||
|  | ||||
| # Local Variables: | ||||
| # mode: shell-script | ||||
| # sh-indentation: 2 | ||||
| # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||
| # time-stamp-start: "scriptversion=" | ||||
| # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||
| # time-stamp-time-zone: "UTC" | ||||
| # time-stamp-end: "; # UTC" | ||||
| # End: | ||||
							
								
								
									
										6
									
								
								conf/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								conf/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +0,0 @@ | ||||
| command_profile_template.profile | ||||
| example.conf | ||||
| lvmlocal.conf | ||||
| metadata_profile_template.profile | ||||
| configure.h | ||||
| lvm-version.h | ||||
| @@ -1,5 +1,5 @@ | ||||
| # | ||||
| # Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| @@ -9,7 +9,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| @@ -17,42 +17,24 @@ top_builddir = @top_builddir@ | ||||
|  | ||||
| CONFSRC=example.conf | ||||
| CONFDEST=lvm.conf | ||||
| CONFLOCAL=lvmlocal.conf | ||||
|  | ||||
| PROFILE_TEMPLATES=command_profile_template.profile metadata_profile_template.profile | ||||
| PROFILES=$(PROFILE_TEMPLATES) \ | ||||
| 	$(srcdir)/cache-mq.profile \ | ||||
| 	$(srcdir)/cache-smq.profile \ | ||||
| 	$(srcdir)/thin-generic.profile \ | ||||
| 	$(srcdir)/thin-performance.profile \ | ||||
| 	$(srcdir)/lvmdbusd.profile | ||||
| PROFILES=$(PROFILE_TEMPLATES) $(srcdir)/thin-generic.profile $(srcdir)/thin-performance.profile | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| .PHONY: install_conf install_localconf install_profiles | ||||
|  | ||||
| generate: | ||||
| 	(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 | ||||
| 	(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) | ||||
| 	@if [ ! -e $(confdir)/$(CONFDEST) ]; then \ | ||||
| 		echo "$(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST)"; \ | ||||
| 		$(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST); \ | ||||
| 	fi | ||||
|  | ||||
| install_localconf: $(CONFLOCAL) | ||||
| 	@if [ ! -e $(confdir)/$(CONFLOCAL) ]; then \ | ||||
| 		echo "$(INSTALL_WDATA) -D $< $(confdir)/$(CONFLOCAL)"; \ | ||||
| 		$(INSTALL_WDATA) -D $< $(confdir)/$(CONFLOCAL); \ | ||||
| 	fi | ||||
|  | ||||
| install_profiles: $(PROFILES) | ||||
| 	$(INSTALL_DIR) $(DESTDIR)$(DEFAULT_PROFILE_DIR) | ||||
| 	$(INSTALL_DATA) $(PROFILES) $(DESTDIR)$(DEFAULT_PROFILE_DIR)/ | ||||
|  | ||||
| install_lvm2: install_conf install_localconf install_profiles | ||||
| install_lvm2: install_conf install_profiles | ||||
|  | ||||
| install: install_lvm2 | ||||
|  | ||||
| DISTCLEAN_TARGETS += $(CONFSRC) $(CONFLOCAL) $(PROFILE_TEMPLATES) | ||||
| DISTCLEAN_TARGETS += $(CONFSRC) $(PROFILE_TEMPLATES) | ||||
|   | ||||
| @@ -1,20 +0,0 @@ | ||||
| # Demo configuration 'mq' cache policy | ||||
| # | ||||
| # Note: This policy has been deprecated in favor of the smq policy | ||||
| # keyword "default" means, setting is left with kernel defaults. | ||||
| # | ||||
|  | ||||
| allocation { | ||||
| 	cache_pool_chunk_size = 64 | ||||
| 	cache_mode = "writethrough" | ||||
| 	cache_policy = "mq" | ||||
| 	cache_settings { | ||||
| 		mq { | ||||
| 			sequential_threshold = "default"	#  #nr_sequential_ios | ||||
| 			random_threshold = "default"		#  #nr_random_ios | ||||
| 			read_promote_adjustment = "default" | ||||
| 			write_promote_adjustment = "default" | ||||
| 			discard_promote_adjustment = "default" | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,14 +0,0 @@ | ||||
| # Demo configuration 'smq' cache policy | ||||
| # | ||||
| # The stochastic multi-queue (smq) policy addresses some of the problems | ||||
| # with the multiqueue (mq) policy and uses less memory. | ||||
| # | ||||
|  | ||||
| allocation { | ||||
| 	cache_pool_chunk_size = 64 | ||||
| 	cache_mode = "writethrough" | ||||
| 	cache_policy = "smq" | ||||
| 	cache_settings { | ||||
| 	        # currently no settins for "smq" policy | ||||
| 	} | ||||
| } | ||||
| @@ -11,17 +11,6 @@ | ||||
| # Refer to 'man lvm.conf' for further information about profiles and | ||||
| # general configuration file layout. | ||||
| # | ||||
| allocation { | ||||
| 	cache_mode="writethrough" | ||||
| 	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 { | ||||
| 	units="h" | ||||
| 	si_unit_consistency=1 | ||||
| @@ -29,9 +18,6 @@ global { | ||||
| 	lvdisplay_shows_full_device_path=0 | ||||
| } | ||||
| report { | ||||
| 	output_format="basic" | ||||
| 	compact_output=0 | ||||
| 	compact_output_cols="" | ||||
| 	aligned=1 | ||||
| 	buffered=1 | ||||
| 	headings=1 | ||||
| @@ -39,9 +25,8 @@ report { | ||||
| 	list_item_separator="," | ||||
| 	prefixes=0 | ||||
| 	quoted=1 | ||||
| 	columns_as_rows=0 | ||||
| 	colums_as_rows=0 | ||||
| 	binary_values_as_numeric=0 | ||||
| 	time_format="%Y-%m-%d %T %z" | ||||
| 	devtypes_sort="devtype_name" | ||||
| 	devtypes_cols="devtype_name,devtype_max_partitions,devtype_description" | ||||
| 	devtypes_cols_verbose="devtype_name,devtype_max_partitions,devtype_description" | ||||
| @@ -60,15 +45,4 @@ report { | ||||
| 	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_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 | ||||
| } | ||||
|   | ||||
| @@ -1,23 +0,0 @@ | ||||
| # 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. | ||||
|  | ||||
							
								
								
									
										2942
									
								
								conf/example.conf.in
									
									
									
									
									
								
							
							
						
						
									
										2942
									
								
								conf/example.conf.in
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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" | ||||
| } | ||||
| @@ -1,19 +0,0 @@ | ||||
| # 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. | ||||
|  | ||||
| @@ -1,57 +0,0 @@ | ||||
| # 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. | ||||
|  | ||||
|  | ||||
| # Configuration section local. | ||||
| # LVM settings that are specific to the local host. | ||||
| local { | ||||
|  | ||||
| 	# Configuration option local/system_id. | ||||
| 	# Defines the local system ID for lvmlocal mode. | ||||
| 	# This is used when global/system_id_source is set to 'lvmlocal' in the | ||||
| 	# main configuration file, e.g. lvm.conf. When used, it must be set to | ||||
| 	# a unique value among all hosts sharing access to the storage, | ||||
| 	# e.g. a host name. | ||||
| 	#  | ||||
| 	# Example | ||||
| 	# Set no system ID: | ||||
| 	# system_id = "" | ||||
| 	# Set the system_id to a specific name: | ||||
| 	# system_id = "host1" | ||||
| 	#  | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# system_id = "" | ||||
|  | ||||
| 	# Configuration option local/extra_system_ids. | ||||
| 	# A list of extra VG system IDs the local host can access. | ||||
| 	# VGs with the system IDs listed here (in addition to the host's own | ||||
| 	# system ID) can be fully accessed by the local host. (These are | ||||
| 	# system IDs that the host sees in VGs, not system IDs that identify | ||||
| 	# the local host, which is determined by system_id_source.) | ||||
| 	# Use this only after consulting 'man lvmsystemid' to be certain of | ||||
| 	# correct usage and possible dangers. | ||||
| 	# This configuration option does not have a default value defined. | ||||
|  | ||||
| 	# Configuration option local/host_id. | ||||
| 	# The lvmlockd sanlock host_id. | ||||
| 	# 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. | ||||
| 	# host_id = 0 | ||||
| } | ||||
| @@ -16,7 +16,7 @@ allocation { | ||||
| 	thin_pool_zero=1 | ||||
| 	thin_pool_discards="passdown" | ||||
| 	thin_pool_chunk_size_policy="generic" | ||||
| #	thin_pool_chunk_size=128 | ||||
| #	thin_pool_chunk_size=64 | ||||
| } | ||||
| activation { | ||||
| 	thin_pool_autoextend_threshold=100 | ||||
|   | ||||
							
								
								
									
										704
									
								
								configure.in
									
									
									
									
									
								
							
							
						
						
									
										704
									
								
								configure.in
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,122 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2015 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
|  * 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 | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Coverity usage: | ||||
|  * | ||||
|  * translate model into xml | ||||
|  * cov-make-library -of coverity_model.xml coverity_model.c | ||||
|  * | ||||
|  * compile (using outdir 'cov'): | ||||
|  * cov-build --dir=cov make CC=gcc | ||||
|  * | ||||
|  * analyze (agressively, using 'cov') | ||||
|  * cov-analyze --dir cov --wait-for-license --hfa --concurrency --enable-fnptr --enable-constraint-fpp --security --all --aggressiveness-level=high --field-offset-escape --user-model-file=coverity/coverity_model.xml | ||||
|  * | ||||
|  * generate html output (to 'html' from 'cov'): | ||||
|  * cov-format-errors --dir cov  --html-output html | ||||
|  */ | ||||
|  | ||||
| struct lv_segment; | ||||
| struct logical_volume; | ||||
|  | ||||
| struct lv_segment *first_seg(const struct logical_volume *lv) | ||||
| { | ||||
| 	return ((struct lv_segment **)lv)[0]; | ||||
| } | ||||
|  | ||||
| struct lv_segment *last_seg(const struct logical_volume *lv) | ||||
| { | ||||
| 	return ((struct lv_segment **)lv)[0]; | ||||
| } | ||||
|  | ||||
| /* simple_memccpy() from glibc */ | ||||
| void *memccpy(void *dest, const void *src, int c, size_t n) | ||||
| { | ||||
| 	const char *s = src; | ||||
| 	char *d = dest; | ||||
|  | ||||
| 	while (n-- > 0) | ||||
| 		if ((*d++ = *s++) == (char) c) | ||||
| 			return d; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 2 lines bellow needs to be placed in coverity/config/user_nodefs.h | ||||
|  * Not sure about any other way. | ||||
|  * Without them, coverity shows warning since x86 system header files | ||||
|  * are using inline assembly to reset fdset | ||||
|  */ | ||||
| //#nodef FD_ZERO model_FD_ZERO | ||||
| //void model_FD_ZERO(void *fdset); | ||||
|  | ||||
| void model_FD_ZERO(void *fdset) | ||||
| { | ||||
| 	unsigned i; | ||||
|  | ||||
| 	for (i = 0; i < 1024 / 8 / sizeof(long); ++i) | ||||
| 		((long*)fdset)[i] = 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Added extra pointer check to not need these models, | ||||
|  * for now just keep then in file | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| struct cmd_context; | ||||
| struct profile; | ||||
|  | ||||
| const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile) | ||||
| { | ||||
|         return "text"; | ||||
| } | ||||
|  | ||||
| const char *find_config_tree_str_allow_empty(struct cmd_context *cmd, int id, struct profile *profile) | ||||
| { | ||||
|         return "text"; | ||||
| } | ||||
| */ | ||||
|  | ||||
| /* | ||||
|  * Until fixed coverity case# 00531860: | ||||
|  *   A FORWARD_NULL false positive on a recursive function call | ||||
|  * | ||||
|  * model also these functions: | ||||
|  */ | ||||
| /* | ||||
| const struct dm_config_node; | ||||
| const struct dm_config_node *find_config_tree_array(struct cmd_context *cmd, int id, struct profile *profile) | ||||
| { | ||||
| 	const struct dm_config_node *cn; | ||||
|  | ||||
| 	return cn; | ||||
| } | ||||
|  | ||||
| const struct dm_config_node *find_config_tree_node(struct cmd_context *cmd, int id, struct profile *profile) | ||||
| { | ||||
| 	const struct dm_config_node *cn; | ||||
|  | ||||
| 	return cn; | ||||
| } | ||||
|  | ||||
| int find_config_tree_bool(struct cmd_context *cmd, int id, struct profile *profile) | ||||
| { | ||||
| 	int b; | ||||
|  | ||||
| 	return b; | ||||
| } | ||||
| */ | ||||
| @@ -1,5 +1,5 @@ | ||||
| # | ||||
| # Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. | ||||
| # Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| @@ -9,13 +9,13 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| .PHONY: dmeventd clvmd cmirrord lvmetad lvmpolld lvmlockd | ||||
| .PHONY: dmeventd clvmd cmirrord lvmetad | ||||
|  | ||||
| ifneq ("@CLVMD@", "none") | ||||
|   SUBDIRS += clvmd | ||||
| @@ -36,20 +36,8 @@ ifeq ("@BUILD_LVMETAD@", "yes") | ||||
|   SUBDIRS += lvmetad | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_LVMPOLLD@", "yes") | ||||
|   SUBDIRS += lvmpolld | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_LVMLOCKD@", "yes") | ||||
|   SUBDIRS += lvmlockd  | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_LVMDBUSD@", "yes") | ||||
|   SUBDIRS += lvmdbusd  | ||||
| endif | ||||
|  | ||||
| ifeq ($(MAKECMDGOALS),distclean) | ||||
|   SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd | ||||
|   SUBDIRS = clvmd cmirrord dmeventd lvmetad | ||||
| endif | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|   | ||||
							
								
								
									
										1
									
								
								daemons/clvmd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								daemons/clvmd/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | ||||
| clvmd | ||||
| @@ -9,7 +9,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| @@ -36,6 +36,10 @@ SOURCES = \ | ||||
| 	lvm-functions.c  \ | ||||
| 	refresh_clvmd.c | ||||
|  | ||||
| ifeq ("@DEBUG@", "yes") | ||||
| 	DEFS += -DDEBUG | ||||
| endif | ||||
|  | ||||
| ifneq (,$(findstring cman,, "@CLVMD@,")) | ||||
| 	SOURCES += clvmd-cman.c | ||||
| 	LMLIBS += $(CMAN_LIBS) $(CONFDB_LIBS) $(DLM_LIBS) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* Definitions for CLVMD server and clients */ | ||||
| @@ -50,7 +50,7 @@ struct clvm_header { | ||||
| #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 */ | ||||
| #define CLVMD_SOCKNAME		DEFAULT_RUN_DIR "/clvmd.sock" | ||||
| static const char CLVMD_SOCKNAME[]= DEFAULT_RUN_DIR "/clvmd.sock"; | ||||
|  | ||||
| /* Internal commands & replies */ | ||||
| #define CLVMD_CMD_REPLY    1 | ||||
| @@ -76,10 +76,8 @@ struct clvm_header { | ||||
| #define CLVMD_CMD_SYNC_NAMES	    45 | ||||
|  | ||||
| /* Used internally by some callers, but not part of the protocol.*/ | ||||
| #ifndef NODE_ALL | ||||
| #  define NODE_ALL	"*" | ||||
| #  define NODE_LOCAL	"." | ||||
| #  define NODE_REMOTE	"^" | ||||
| #endif | ||||
| #define NODE_ALL	"*" | ||||
| #define NODE_LOCAL	"." | ||||
| #define NODE_REMOTE	"^" | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| @@ -323,7 +323,6 @@ void cmd_client_cleanup(struct local_client *client) | ||||
| 	int lkid; | ||||
| 	char *lockname; | ||||
|  | ||||
| 	DEBUGLOG("Client thread cleanup (%p)\n", client); | ||||
| 	if (!client->bits.localsock.private) | ||||
| 		return; | ||||
|  | ||||
| @@ -332,7 +331,7 @@ void cmd_client_cleanup(struct local_client *client) | ||||
| 	dm_hash_iterate(v, lock_hash) { | ||||
| 		lkid = (int)(long)dm_hash_get_data(lock_hash, v); | ||||
| 		lockname = dm_hash_get_key(lock_hash, v); | ||||
| 		DEBUGLOG("Cleanup (%p): Unlocking lock %s %x\n", client, lockname, lkid); | ||||
| 		DEBUGLOG("cleanup: Unlocking lock %s %x\n", lockname, lkid); | ||||
| 		(void) sync_unlock(lockname, lkid); | ||||
| 	} | ||||
|  | ||||
| @@ -340,6 +339,7 @@ void cmd_client_cleanup(struct local_client *client) | ||||
| 	client->bits.localsock.private = NULL; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int restart_clvmd(void) | ||||
| { | ||||
| 	const char **argv; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| @@ -18,10 +18,15 @@ | ||||
| #ifndef _LVM_CLVMD_COMMON_H | ||||
| #define _LVM_CLVMD_COMMON_H | ||||
|  | ||||
| #include "configure.h" | ||||
|  | ||||
| #define _REENTRANT | ||||
| #define _GNU_SOURCE | ||||
| #define _FILE_OFFSET_BITS 64 | ||||
|  | ||||
| #include "tool.h" | ||||
|  | ||||
| #include "libdevmapper.h" | ||||
| #include "lvm-logging.h" | ||||
|  | ||||
| #include <unistd.h> | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| @@ -243,7 +243,7 @@ static void openais_cpg_confchg_callback(cpg_handle_t handle, | ||||
| 	struct node_info *ninfo; | ||||
|  | ||||
| 	DEBUGLOG("confchg callback. %" PRIsize_t " joined, " | ||||
| 		 FMTsize_t " left, %" PRIsize_t " members\n", | ||||
| 		 "%" PRIsize_t " left, %" PRIsize_t " members\n", | ||||
| 		 joined_list_entries, left_list_entries, member_list_entries); | ||||
|  | ||||
| 	for (i=0; i<joined_list_entries; i++) { | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "clvmd-common.h" | ||||
| @@ -208,6 +208,8 @@ static int _lock_resource(const char *resource, int mode, int flags, int *lockid | ||||
| 	pthread_mutex_lock(&_lock_mutex); | ||||
|  | ||||
| retry: | ||||
| 	pthread_cond_broadcast(&_lock_cond); /* to wakeup waiters */ | ||||
|  | ||||
| 	if (!(head = dm_hash_lookup(_locks, resource))) { | ||||
| 		if (flags & LCKF_CONVERT) { | ||||
| 			/* In real DLM, lock is identified only by lockid, resource is not used */ | ||||
| @@ -267,14 +269,12 @@ retry: | ||||
| 		dm_list_add(head, &lck->list); | ||||
| 	} | ||||
| out: | ||||
| 	pthread_cond_broadcast(&_lock_cond); /* to wakeup waiters */ | ||||
| 	pthread_mutex_unlock(&_lock_mutex); | ||||
| 	DEBUGLOG("Locked resource %s, lockid=%d, mode=%s\n", | ||||
| 		 resource, lck->lockid, _get_mode(lck->mode)); | ||||
|  | ||||
| 	return 0; | ||||
| bad: | ||||
| 	pthread_cond_broadcast(&_lock_cond); /* to wakeup waiters */ | ||||
| 	pthread_mutex_unlock(&_lock_mutex); | ||||
| 	DEBUGLOG("Failed to lock resource %s\n", resource); | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include "clvmd.h" | ||||
| #include "lvm-functions.h" | ||||
| #include "lvm-version.h" | ||||
| #include "lvm-wrappers.h" | ||||
| #include "refresh_clvmd.h" | ||||
|  | ||||
| #ifdef HAVE_COROSYNC_CONFDB_H | ||||
| @@ -88,7 +89,7 @@ static debug_t debug = DEBUG_OFF; | ||||
| static int foreground_mode = 0; | ||||
| static pthread_t lvm_thread; | ||||
| /* Stack size 128KiB for thread, must be bigger then DEFAULT_RESERVED_STACK */ | ||||
| static const size_t STACK_SIZE = 128 * 1024; | ||||
| static const size_t MIN_STACK_SIZE = 128 * 1024; | ||||
| static pthread_attr_t stack_attr; | ||||
| static int lvm_thread_exit = 0; | ||||
| static pthread_mutex_t lvm_thread_mutex; | ||||
| @@ -153,11 +154,16 @@ static if_type_t get_cluster_type(void); | ||||
| static void usage(const char *prog, FILE *file) | ||||
| { | ||||
| 	fprintf(file, "Usage: %s [options]\n" | ||||
| 		"   -C       Sets debug level (from -d) on all clvmd instances clusterwide\n" | ||||
| 		"   -d[<n>]  Set debug logging (0:none, 1:stderr (implies -f option), 2:syslog)\n" | ||||
| 		"   -E<uuid> Take this lock uuid as exclusively locked resource (for restart)\n" | ||||
| 		"   -f       Don't fork, run in the foreground\n" | ||||
| 		"   -V       Show version of clvmd\n" | ||||
| 		"   -h       Show this help information\n" | ||||
| 		"   -d[n]    Set debug logging (0:none, 1:stderr (implies -f option), 2:syslog)\n" | ||||
| 		"   -f       Don't fork, run in the foreground\n" | ||||
| 		"   -E<lockuuid> Take this lock uuid as exclusively locked resource (for restart)\n" | ||||
| 		"   -R       Tell all running clvmds in the cluster to reload their device cache\n" | ||||
| 		"   -S       Restart clvmd, preserving exclusive locks\n" | ||||
| 		"   -C       Sets debug level (from -d) on all clvmd instances clusterwide\n" | ||||
| 		"   -t<secs> Command timeout (default 60 seconds)\n" | ||||
| 		"   -T<secs> Startup timeout (default none)\n" | ||||
| 		"   -I<cmgr> Cluster manager (default: auto)\n" | ||||
| 		"            Available cluster managers: " | ||||
| #ifdef USE_COROSYNC | ||||
| @@ -172,12 +178,6 @@ static void usage(const char *prog, FILE *file) | ||||
| #ifdef USE_SINGLENODE | ||||
| 		"singlenode " | ||||
| #endif | ||||
| 		"\n" | ||||
| 		"   -R       Tell all running clvmds in the cluster to reload their device cache\n" | ||||
| 		"   -S       Restart clvmd, preserving exclusive locks\n" | ||||
| 		"   -t<secs> Command timeout (default: 60 seconds)\n" | ||||
| 		"   -T<secs> Startup timeout (default:  0 seconds)\n" | ||||
| 		"   -V       Show version of clvmd\n" | ||||
| 		"\n", prog); | ||||
| } | ||||
|  | ||||
| @@ -223,7 +223,6 @@ void debuglog(const char *fmt, ...) | ||||
| 		fprintf(stderr, "CLVMD[%x]: %.15s ", (int)pthread_self(), ctime_r(&P, buf_ctime) + 4); | ||||
| 		vfprintf(stderr, fmt, ap); | ||||
| 		va_end(ap); | ||||
| 		fflush(stderr); | ||||
| 		break; | ||||
| 	case DEBUG_SYSLOG: | ||||
| 		if (!syslog_init) { | ||||
| @@ -360,6 +359,7 @@ int main(int argc, char *argv[]) | ||||
| 	int clusterwide_opt = 0; | ||||
| 	mode_t old_mask; | ||||
| 	int ret = 1; | ||||
| 	size_t stack_size; | ||||
|  | ||||
| 	struct option longopts[] = { | ||||
| 		{ "help", 0, 0, 'h' }, | ||||
| @@ -374,7 +374,7 @@ int main(int argc, char *argv[]) | ||||
| 	/* Deal with command-line arguments */ | ||||
| 	opterr = 0; | ||||
| 	optind = 0; | ||||
| 	while ((opt = getopt_long(argc, argv, "Vhfd:t:RST:CI:E:", | ||||
| 	while ((opt = getopt_long(argc, argv, "vVhfd:t:RST:CI:E:", | ||||
| 				  longopts, NULL)) != -1) { | ||||
| 		switch (opt) { | ||||
| 		case 'h': | ||||
| @@ -516,8 +516,10 @@ int main(int argc, char *argv[]) | ||||
|  | ||||
| 	/* Initialise the LVM thread variables */ | ||||
| 	dm_list_init(&lvm_cmd_head); | ||||
| 	stack_size = 3 * lvm_getpagesize(); | ||||
| 	stack_size = stack_size < MIN_STACK_SIZE ? MIN_STACK_SIZE : stack_size; | ||||
| 	if (pthread_attr_init(&stack_attr) || | ||||
| 	    pthread_attr_setstacksize(&stack_attr, STACK_SIZE)) { | ||||
| 	    pthread_attr_setstacksize(&stack_attr, stack_size)) { | ||||
| 		log_sys_error("pthread_attr_init", ""); | ||||
| 		exit(1); | ||||
| 	} | ||||
| @@ -599,15 +601,10 @@ int main(int argc, char *argv[]) | ||||
|  | ||||
| 	/* This needs to be started after cluster initialisation | ||||
| 	   as it may need to take out locks */ | ||||
| 	DEBUGLOG("Starting LVM thread\n"); | ||||
| 	DEBUGLOG("Main cluster socket fd %d (%p) with local socket %d (%p)\n", | ||||
| 		 local_client_head.fd, &local_client_head, newfd->fd, newfd); | ||||
| 	DEBUGLOG("starting LVM thread\n"); | ||||
|  | ||||
| 	/* Don't let anyone else to do work until we are started */ | ||||
| 	if (pthread_create(&lvm_thread, &stack_attr, lvm_thread_fn, &lvm_params)) { | ||||
| 		log_sys_error("pthread_create", ""); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	pthread_create(&lvm_thread, &stack_attr, lvm_thread_fn, &lvm_params); | ||||
|  | ||||
| 	/* Don't start until the LVM thread is ready */ | ||||
| 	pthread_barrier_wait(&lvm_start_barrier); | ||||
| @@ -704,7 +701,7 @@ static int local_rendezvous_callback(struct local_client *thisfd, char *buf, | ||||
| 		newfd->type = LOCAL_SOCK; | ||||
| 		newfd->callback = local_sock_callback; | ||||
| 		newfd->bits.localsock.all_success = 1; | ||||
| 		DEBUGLOG("Got new connection on fd %d (%p)\n", newfd->fd, newfd); | ||||
| 		DEBUGLOG("Got new connection on fd %d\n", newfd->fd); | ||||
| 		*new_client = newfd; | ||||
| 	} | ||||
| 	return 1; | ||||
| @@ -856,48 +853,18 @@ static void main_loop(int cmd_timeout) | ||||
| 		struct local_client *thisfd; | ||||
| 		struct timeval tv = { cmd_timeout, 0 }; | ||||
| 		int quorate = clops->is_quorate(); | ||||
| 		int client_count = 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 */ | ||||
| 		local_client_head.fd = clops->get_main_cluster_fd(); | ||||
| 		FD_ZERO(&in); | ||||
|  | ||||
| 		for (thisfd = &local_client_head; thisfd; thisfd = thisfd->next) { | ||||
| 			client_count++; | ||||
| 			max_fd = max(max_fd, thisfd->fd); | ||||
| 		} | ||||
|  | ||||
| 		if (max_fd > FD_SETSIZE - 32) { | ||||
| 			fprintf(stderr, "WARNING: There are too many connections to clvmd.  Investigate and take action now!\n"); | ||||
|  			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, nextfd = thisfd ? thisfd->next : NULL) { | ||||
|  | ||||
| 			if (thisfd->removeme && !cleanup_zombie(thisfd)) { | ||||
| 				struct local_client *free_fd = thisfd; | ||||
| 				lastfd->next = nextfd; | ||||
| 				DEBUGLOG("removeme set for %p with %d monitored fds remaining\n", free_fd, client_count - 1); | ||||
|  | ||||
| 				/* Queue cleanup, this also frees the client struct */ | ||||
| 				add_to_lvmqueue(free_fd, NULL, 0, NULL); | ||||
|  | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 			lastfd = thisfd; | ||||
|  | ||||
| 			if (thisfd->removeme) | ||||
| 				continue; | ||||
|  | ||||
| 			/* if the cluster is not quorate then don't listen for new requests */ | ||||
| 			if ((thisfd->type != LOCAL_RENDEZVOUS && | ||||
| 			     thisfd->type != LOCAL_SOCK) || quorate) | ||||
| 				if (thisfd->fd < FD_SETSIZE) | ||||
| 					FD_SET(thisfd->fd, &in); | ||||
| 				FD_SET(thisfd->fd, &in); | ||||
| 		} | ||||
|  | ||||
| 		select_status = select(FD_SETSIZE, &in, NULL, NULL, &tv); | ||||
| @@ -913,22 +880,31 @@ static void main_loop(int cmd_timeout) | ||||
| 		} | ||||
|  | ||||
| 		if (select_status > 0) { | ||||
| 			struct local_client *lastfd = NULL; | ||||
| 			char csid[MAX_CSID_LEN]; | ||||
| 			char buf[max_cluster_message]; | ||||
|  | ||||
| 			for (thisfd = &local_client_head; thisfd; thisfd = thisfd->next) { | ||||
| 				if (thisfd->fd < FD_SETSIZE && FD_ISSET(thisfd->fd, &in)) { | ||||
| 				if (thisfd->removeme && !cleanup_zombie(thisfd)) { | ||||
| 					struct local_client *free_fd = thisfd; | ||||
| 					lastfd->next = thisfd->next; | ||||
| 					DEBUGLOG("removeme set for fd %d\n", free_fd->fd); | ||||
|  | ||||
| 					/* Queue cleanup, this also frees the client struct */ | ||||
| 					add_to_lvmqueue(free_fd, NULL, 0, NULL); | ||||
| 					break; | ||||
| 				} | ||||
|  | ||||
| 				if (FD_ISSET(thisfd->fd, &in)) { | ||||
| 					struct local_client *newfd = NULL; | ||||
| 					int ret; | ||||
|  | ||||
| 					/* FIXME Remove from main thread in case it blocks! */ | ||||
| 					/* Do callback */ | ||||
| 					ret = thisfd->callback(thisfd, buf, sizeof(buf), | ||||
| 							       csid, &newfd); | ||||
| 					/* Ignore EAGAIN */ | ||||
| 					if (ret < 0 && (errno == EAGAIN || errno == EINTR)) { | ||||
| 					if (ret < 0 && (errno == EAGAIN || errno == EINTR)) | ||||
| 						continue; | ||||
|                                         } | ||||
|  | ||||
| 					/* Got error or EOF: Remove it from the list safely */ | ||||
| 					if (ret <= 0) { | ||||
| @@ -942,16 +918,17 @@ static void main_loop(int cmd_timeout) | ||||
| 						DEBUGLOG("ret == %d, errno = %d. removing client\n", | ||||
| 							 ret, errno); | ||||
| 						thisfd->removeme = 1; | ||||
| 						continue; | ||||
| 						break; | ||||
| 					} | ||||
|  | ||||
| 					/* New client...simply add it to the list */ | ||||
| 					if (newfd) { | ||||
| 						newfd->next = thisfd->next; | ||||
| 						thisfd->next = newfd; | ||||
| 						thisfd = newfd; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				lastfd = thisfd; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -1155,7 +1132,7 @@ static void dump_message(char *buf, int len) | ||||
| 		row[j] = buf[i]; | ||||
| 		str[j] = (isprint(buf[i])) ? buf[i] : ' '; | ||||
|  | ||||
| 		if (i + 1 == len) { | ||||
| 		if ((j == 8) || (i + 1 == len)) { | ||||
| 			for (;j < 8; ++j) { | ||||
| 				row[j] = 0; | ||||
| 				str[j] = ' '; | ||||
| @@ -1444,7 +1421,7 @@ static int read_from_local_sock(struct local_client *thisfd) | ||||
| 	thisfd->bits.localsock.in_progress = TRUE; | ||||
| 	thisfd->bits.localsock.state = PRE_COMMAND; | ||||
| 	thisfd->bits.localsock.cleanup_needed = 1; | ||||
| 	DEBUGLOG("Creating pre&post thread for pipe fd %d (%p)\n", newfd->fd, newfd); | ||||
| 	DEBUGLOG("Creating pre&post thread\n"); | ||||
| 	status = pthread_create(&thisfd->bits.localsock.threadid, | ||||
| 				&stack_attr, pre_and_post_thread, thisfd); | ||||
| 	DEBUGLOG("Created pre&post thread, state = %d\n", status); | ||||
| @@ -1698,7 +1675,7 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg) | ||||
| 	sigset_t ss; | ||||
| 	int pipe_fd = client->bits.localsock.pipe; | ||||
|  | ||||
| 	DEBUGLOG("Pre&post thread (%p), pipe fd %d\n", client, pipe_fd); | ||||
| 	DEBUGLOG("Pre&post thread (%p), pipe %d\n", client, pipe_fd); | ||||
| 	pthread_mutex_lock(&client->bits.localsock.mutex); | ||||
|  | ||||
| 	/* Ignore SIGUSR1 (handled by master process) but enable | ||||
| @@ -1718,7 +1695,7 @@ static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg) | ||||
| 		if ((status = do_pre_command(client))) | ||||
| 			client->bits.localsock.all_success = 0; | ||||
|  | ||||
| 		DEBUGLOG("Pre&post thread (%p) writes status %d down to pipe fd %d\n", | ||||
| 		DEBUGLOG("Pre&post thread (%p) writes status %d down to pipe %d\n", | ||||
| 			 client, status, pipe_fd); | ||||
|  | ||||
| 		/* Tell the parent process we have finished this bit */ | ||||
| @@ -2000,7 +1977,7 @@ static int process_work_item(struct lvm_thread_cmd *cmd) | ||||
| { | ||||
| 	/* If msg is NULL then this is a cleanup request */ | ||||
| 	if (cmd->msg == NULL) { | ||||
| 		DEBUGLOG("process_work_item: free %p\n", cmd->client); | ||||
| 		DEBUGLOG("process_work_item: free fd %d\n", cmd->client->fd); | ||||
| 		cmd_client_cleanup(cmd->client); | ||||
| 		pthread_mutex_destroy(&cmd->client->bits.localsock.mutex); | ||||
| 		pthread_cond_destroy(&cmd->client->bits.localsock.cond); | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #ifndef _CLVMD_H | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "clvmd-common.h" | ||||
| @@ -136,7 +136,7 @@ static const char *decode_flags(unsigned char flags) | ||||
| 		flags & LCK_DMEVENTD_MONITOR_MODE ? "DMEVENTD_MONITOR|" : "", | ||||
| 		flags & LCK_ORIGIN_ONLY_MODE ? "ORIGIN_ONLY|" : "", | ||||
| 		flags & LCK_TEST_MODE ? "TEST|" : "", | ||||
| 		flags & LCK_CONVERT_MODE ? "CONVERT|" : "", | ||||
| 		flags & LCK_CONVERT ? "CONVERT|" : "", | ||||
| 		flags & LCK_DMEVENTD_MONITOR_IGNORE ? "DMEVENTD_MONITOR_IGNORE|" : "", | ||||
| 		flags & LCK_REVERT_MODE ? "REVERT|" : ""); | ||||
|  | ||||
| @@ -291,7 +291,6 @@ static int hold_lock(char *resource, int mode, int flags) | ||||
| 		} | ||||
|  | ||||
| 		lvi->lock_mode = mode; | ||||
| 		lvi->lock_id = 0; | ||||
| 		status = sync_lock(resource, mode, flags & ~LCKF_CONVERT, &lvi->lock_id); | ||||
| 		saved_errno = errno; | ||||
| 		if (status) { | ||||
| @@ -376,7 +375,7 @@ static int do_activate_lv(char *resource, unsigned char command, unsigned char l | ||||
| 	 * of exclusive lock to shared one during activation. | ||||
| 	 */ | ||||
| 	if (!test_mode() && command & LCK_CLUSTER_VG) { | ||||
| 		status = hold_lock(resource, mode, LCKF_NOQUEUE | ((lock_flags & LCK_CONVERT_MODE) ? LCKF_CONVERT:0)); | ||||
| 		status = hold_lock(resource, mode, LCKF_NOQUEUE | (lock_flags & LCK_CONVERT ? LCKF_CONVERT:0)); | ||||
| 		if (status) { | ||||
| 			/* Return an LVM-sensible error for this. | ||||
| 			 * Forcing EIO makes the upper level return this text | ||||
| @@ -511,7 +510,7 @@ int do_lock_lv(unsigned char command, unsigned char lock_flags, char *resource) | ||||
| 	DEBUGLOG("do_lock_lv: resource '%s', cmd = %s, flags = %s, critical_section = %d\n", | ||||
| 		 resource, decode_locking_cmd(command), decode_flags(lock_flags), critical_section()); | ||||
|  | ||||
| 	if (!cmd->initialized.config || config_files_changed(cmd)) { | ||||
| 	if (!cmd->config_initialized || config_files_changed(cmd)) { | ||||
| 		/* Reinitialise various settings inc. logging, filters */ | ||||
| 		if (do_refresh_cache()) { | ||||
| 			log_error("Updated config file invalid. Aborting."); | ||||
| @@ -663,8 +662,7 @@ int do_refresh_cache(void) | ||||
|  | ||||
| 	init_full_scan_done(0); | ||||
| 	init_ignore_suspended_devices(1); | ||||
| 	lvmcache_force_next_label_scan(); | ||||
| 	lvmcache_label_scan(cmd); | ||||
| 	lvmcache_label_scan(cmd, 2); | ||||
| 	dm_pool_empty(cmd->mem); | ||||
|  | ||||
| 	pthread_mutex_unlock(&lvm_lock); | ||||
| @@ -844,7 +842,7 @@ void lvm_do_backup(const char *vgname) | ||||
|  | ||||
| 	pthread_mutex_lock(&lvm_lock); | ||||
|  | ||||
| 	vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, WARN_PV_READ, &consistent); | ||||
| 	vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, 1, &consistent); | ||||
|  | ||||
| 	if (vg && consistent) | ||||
| 		check_current_backup(vg); | ||||
| @@ -901,12 +899,8 @@ int init_clvm(struct dm_hash_table *excl_uuid) | ||||
| 	if (!get_initial_state(excl_uuid)) | ||||
| 		log_error("Cannot load initial lock states."); | ||||
|  | ||||
| 	if (!udev_init_library_context()) | ||||
| 		stack; | ||||
|  | ||||
| 	if (!(cmd = create_toolcontext(1, NULL, 0, 1, 1, 1))) { | ||||
| 	if (!(cmd = create_toolcontext(1, NULL, 0, 1))) { | ||||
| 		log_error("Failed to allocate command context"); | ||||
| 		udev_fin_library_context(); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| @@ -933,7 +927,6 @@ void destroy_lvm(void) | ||||
| 	if (cmd) { | ||||
| 		memlock_dec_daemon(cmd); | ||||
| 		destroy_toolcontext(cmd); | ||||
| 		udev_fin_library_context(); | ||||
| 		cmd = NULL; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* Functions in lvm-functions.c */ | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* FIXME Remove duplicated functions from this file. */ | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								daemons/cmirrord/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								daemons/cmirrord/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | ||||
| cmirrord | ||||
| @@ -9,7 +9,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #include "logging.h" | ||||
| #include "common.h" | ||||
| @@ -15,7 +15,6 @@ | ||||
| #include "link_mon.h" | ||||
| #include "local.h" | ||||
|  | ||||
| #include <getopt.h> | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <sys/socket.h> | ||||
| @@ -33,49 +32,14 @@ static void daemonize(void); | ||||
| static void init_all(void); | ||||
| static void cleanup_all(void); | ||||
|  | ||||
| static void usage (FILE *dest) | ||||
| int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) | ||||
| { | ||||
| 	fprintf (dest, "Usage: cmirrord [options]\n" | ||||
| 		 "   -f, --foreground    stay in the foreground, log to the terminal\n" | ||||
| 		 "   -h, --help          print this help\n"); | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	int foreground_mode = 0; | ||||
| 	struct option longopts[] = { | ||||
| 		{ "foreground", no_argument, NULL, 'f' }, | ||||
| 		{ "help"      , no_argument, NULL, 'h' }, | ||||
| 		{ 0, 0, 0, 0 } | ||||
| 	}; | ||||
| 	int opt; | ||||
|  | ||||
| 	while ((opt = getopt_long (argc, argv, "fh", longopts, NULL)) != -1) { | ||||
| 		switch (opt) { | ||||
| 		case 'f': | ||||
| 			foreground_mode = 1; | ||||
| 			break; | ||||
| 		case 'h': | ||||
| 			usage (stdout); | ||||
| 			exit (0); | ||||
| 		default: | ||||
| 			usage (stderr); | ||||
| 			exit (2); | ||||
| 		} | ||||
| 	} | ||||
| 	if (optind < argc) { | ||||
| 		usage (stderr); | ||||
| 		exit (2); | ||||
| 	} | ||||
|  | ||||
| 	if (!foreground_mode) | ||||
| 		daemonize(); | ||||
| 	daemonize(); | ||||
|  | ||||
| 	init_all(); | ||||
|  | ||||
| 	/* Parent can now exit, we're ready to handle requests */ | ||||
| 	if (!foreground_mode) | ||||
| 		kill(getppid(), SIGTERM); | ||||
| 	kill(getppid(), SIGTERM); | ||||
|  | ||||
| 	LOG_PRINT("Starting cmirrord:"); | ||||
| 	LOG_PRINT(" Built: "__DATE__" "__TIME__"\n"); | ||||
| @@ -245,16 +209,6 @@ static void daemonize(void) | ||||
| 	} | ||||
|  | ||||
| 	LOG_OPEN("cmirrord", LOG_PID, LOG_DAEMON); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * init_all | ||||
|  * | ||||
|  * Initialize modules.  Exit on failure. | ||||
|  */ | ||||
| static void init_all(void) | ||||
| { | ||||
| 	int r; | ||||
|  | ||||
| 	(void) dm_prepare_selinux_context(CMIRRORD_PIDFILE, S_IFREG); | ||||
| 	if (dm_create_lockfile(CMIRRORD_PIDFILE) == 0) | ||||
| @@ -273,6 +227,16 @@ static void init_all(void) | ||||
| 	signal(SIGUSR2, &sig_handler); | ||||
| 	sigemptyset(&signal_mask); | ||||
| 	signal_received = 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * init_all | ||||
|  * | ||||
|  * Initialize modules.  Exit on failure. | ||||
|  */ | ||||
| static void init_all(void) | ||||
| { | ||||
| 	int r; | ||||
|  | ||||
| 	if ((r = init_local()) || | ||||
| 	    (r = init_cluster())) { | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #include "logging.h" | ||||
| #include "cluster.h" | ||||
| @@ -104,11 +104,10 @@ static SaVersionT version = { 'B', 1, 1 }; | ||||
| #endif | ||||
|  | ||||
| #define DEBUGGING_HISTORY 100 | ||||
| #define DEBUGGING_BUFLEN 128 | ||||
| #define LOG_SPRINT(cc, f, arg...) do {				\ | ||||
| 		cc->idx++;					\ | ||||
| 		cc->idx = cc->idx % DEBUGGING_HISTORY;		\ | ||||
| 		snprintf(cc->debugging[cc->idx], DEBUGGING_BUFLEN, f, ## arg); \ | ||||
| 		sprintf(cc->debugging[cc->idx], f, ## arg);	\ | ||||
| 	} while (0) | ||||
|  | ||||
| static int log_resp_rec = 0; | ||||
| @@ -151,7 +150,7 @@ struct clog_cpg { | ||||
| 	uint32_t checkpoint_requesters[MAX_CHECKPOINT_REQUESTERS]; | ||||
| 	struct checkpoint_data *checkpoint_list; | ||||
| 	int idx; | ||||
| 	char debugging[DEBUGGING_HISTORY][DEBUGGING_BUFLEN]; | ||||
| 	char debugging[DEBUGGING_HISTORY][128]; | ||||
| }; | ||||
|  | ||||
| static struct dm_list clog_cpg_list; | ||||
| @@ -1295,9 +1294,7 @@ static void cpg_join_callback(struct clog_cpg *match, | ||||
| 	uint32_t my_pid = (uint32_t)getpid(); | ||||
| 	uint32_t lowest = match->lowest_id; | ||||
| 	struct clog_request *rq; | ||||
| 	char dbuf[64] = { 0 }; | ||||
| 	char *dbuf_p = dbuf; | ||||
| 	size_t dbuf_rem = sizeof dbuf; | ||||
| 	char dbuf[32] = { 0 }; | ||||
|  | ||||
| 	/* Assign my_cluster_id */ | ||||
| 	if ((my_cluster_id == 0xDEAD) && (joined->pid == my_pid)) | ||||
| @@ -1313,17 +1310,9 @@ static void cpg_join_callback(struct clog_cpg *match, | ||||
| 	if (joined->nodeid == my_cluster_id) | ||||
| 		goto out; | ||||
|  | ||||
| 	for (i = 0; i < member_list_entries - 1; i++) { | ||||
| 		int written = snprintf(dbuf_p, dbuf_rem, "%u-", member_list[i].nodeid); | ||||
| 		if (written < 0) continue; /* impossible */ | ||||
| 		if ((unsigned)written >= dbuf_rem) { | ||||
| 			dbuf_rem = 0; | ||||
| 			break; | ||||
| 		} | ||||
| 		dbuf_rem -= written; | ||||
| 		dbuf_p += written; | ||||
| 	} | ||||
| 	snprintf(dbuf_p, dbuf_rem, "(%u)", joined->nodeid); | ||||
| 	for (i = 0; i < member_list_entries - 1; i++) | ||||
| 		sprintf(dbuf+strlen(dbuf), "%u-", member_list[i].nodeid); | ||||
| 	sprintf(dbuf+strlen(dbuf), "(%u)", joined->nodeid); | ||||
| 	LOG_COND(log_checkpoint, "[%s] Joining node, %u needs checkpoint [%s]", | ||||
| 		 SHORT_UUID(match->name.value), joined->nodeid, dbuf); | ||||
|  | ||||
| @@ -1440,7 +1429,7 @@ static void cpg_leave_callback(struct clog_cpg *match, | ||||
| 			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]; | ||||
| 		if (match->checkpoint_requesters[i] == left->nodeid) { | ||||
| 			LOG_ERROR("[%s] Removing pending ckpt from needed list (%u is leaving)", | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #ifndef _LVM_CLOG_CLUSTER_H | ||||
| #define _LVM_CLOG_CLUSTER_H | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #ifndef _LVM_CLOG_COMMON_H | ||||
| #define _LVM_CLOG_COMMON_H | ||||
|   | ||||
| @@ -183,6 +183,7 @@ int clog_request_from_network(void *data, size_t data_len) | ||||
| { | ||||
| 	uint64_t *vp = data; | ||||
| 	uint64_t version = xlate64(vp[0]); | ||||
| 	uint64_t unconverted_version = vp[1]; | ||||
| 	struct clog_request *rq = data; | ||||
|  | ||||
| 	switch (version) { | ||||
|   | ||||
| @@ -7,12 +7,11 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #include "logging.h" | ||||
| #include "functions.h" | ||||
|  | ||||
| #include <sys/sysmacros.h> | ||||
| #include <dirent.h> | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| @@ -33,13 +32,12 @@ | ||||
| #define LOG_OFFSET 2 | ||||
|  | ||||
| #define RESYNC_HISTORY 50 | ||||
| #define RESYNC_BUFLEN 128 | ||||
| //static char resync_history[RESYNC_HISTORY][128]; | ||||
| //static int idx = 0; | ||||
| #define LOG_SPRINT(_lc, f, arg...) do {					\ | ||||
| 		lc->idx++;						\ | ||||
| 		lc->idx = lc->idx % RESYNC_HISTORY;			\ | ||||
| 		snprintf(lc->resync_history[lc->idx], RESYNC_BUFLEN, f, ## arg); \ | ||||
| 		sprintf(lc->resync_history[lc->idx], f, ## arg);	\ | ||||
| 	} while (0) | ||||
|  | ||||
| struct log_header { | ||||
| @@ -90,7 +88,7 @@ struct log_c { | ||||
| 	size_t disk_size;       /* size of disk_buffer in bytes */ | ||||
| 	void *disk_buffer;      /* aligned memory for O_DIRECT */ | ||||
| 	int idx; | ||||
| 	char resync_history[RESYNC_HISTORY][RESYNC_BUFLEN]; | ||||
| 	char resync_history[RESYNC_HISTORY][128]; | ||||
| }; | ||||
|  | ||||
| struct mark_entry { | ||||
| @@ -574,12 +572,6 @@ static int clog_ctr(struct dm_ulog_request *rq) | ||||
| 	for (argc = 0, p = rq->data; (p = strstr(p, " ")); p++, argc++) | ||||
| 		*p = '\0'; | ||||
|  | ||||
| 	if (!argc) { | ||||
| 		LOG_ERROR("Received constructor request with bad data %s", | ||||
| 			  rq->data); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	argv = malloc(argc * sizeof(char *)); | ||||
| 	if (!argv) | ||||
| 		return -ENOMEM; | ||||
| @@ -1452,7 +1444,7 @@ static int disk_status_info(struct log_c *lc, struct dm_ulog_request *rq) | ||||
| 	char *data = (char *)rq->data; | ||||
| 	struct stat statbuf; | ||||
|  | ||||
| 	if (fstat(lc->disk_fd, &statbuf)) { | ||||
| 	if(fstat(lc->disk_fd, &statbuf)) { | ||||
| 		rq->error = -errno; | ||||
| 		return -errno; | ||||
| 	} | ||||
| @@ -1515,7 +1507,7 @@ static int disk_status_table(struct log_c *lc, struct dm_ulog_request *rq) | ||||
| 	char *data = (char *)rq->data; | ||||
| 	struct stat statbuf; | ||||
|  | ||||
| 	if (fstat(lc->disk_fd, &statbuf)) { | ||||
| 	if(fstat(lc->disk_fd, &statbuf)) { | ||||
| 		rq->error = -errno; | ||||
| 		return -errno; | ||||
| 	} | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #ifndef _LVM_CLOG_FUNCTIONS_H | ||||
| #define _LVM_CLOG_FUNCTIONS_H | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #include "logging.h" | ||||
| #include "link_mon.h" | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #ifndef _LVM_CLOG_LINK_MON_H | ||||
| #define _LVM_CLOG_LINK_MON_H | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #include "logging.h" | ||||
| #include "common.h" | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #ifndef _LVM_CLOG_LOCAL_H | ||||
| #define _LVM_CLOG_LOCAL_H | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
| #include "logging.h" | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #ifndef _LVM_CLOG_LOGGING_H | ||||
|   | ||||
							
								
								
									
										1
									
								
								daemons/dmeventd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								daemons/dmeventd/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | ||||
| dmeventd | ||||
| @@ -9,7 +9,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -9,7 +9,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #ifndef __DMEVENTD_DOT_H__ | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of the device-mapper userspace tools. | ||||
|  * | ||||
| @@ -9,25 +9,26 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "dm-logging.h" | ||||
| #include "dmlib.h" | ||||
| #include "libdevmapper-event.h" | ||||
| //#include "libmultilog.h" | ||||
| #include "dmeventd.h" | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/file.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/wait.h> | ||||
| #include <arpa/inet.h>		/* for htonl, ntohl */ | ||||
| #include <pthread.h> | ||||
| #include <syslog.h> | ||||
|  | ||||
| static int _debug_level = 0; | ||||
| static int _use_syslog = 0; | ||||
| static int _sequence_nr = 0; | ||||
|  | ||||
| struct dm_event_handler { | ||||
| @@ -198,7 +199,7 @@ static int _check_message_id(struct dm_event_daemon_message *msg) | ||||
| 	if ((sscanf(msg->data, "%d:%d", &pid, &seq_nr) != 2) || | ||||
| 	    (pid != getpid()) || (seq_nr != _sequence_nr)) { | ||||
| 		log_error("Ignoring out-of-sequence reply from dmeventd. " | ||||
| 			  "Expected %d:%d but received %s.", getpid(), | ||||
| 			  "Expected %d:%d but received %s", getpid(), | ||||
| 			  _sequence_nr, msg->data); | ||||
| 		return 0; | ||||
| 	} | ||||
| @@ -233,7 +234,7 @@ static int _daemon_read(struct dm_event_fifos *fifos, | ||||
| 			FD_SET(fifos->server, &fds); | ||||
| 			ret = select(fifos->server + 1, &fds, NULL, NULL, &tval); | ||||
| 			if (ret < 0 && errno != EINTR) { | ||||
| 				log_error("Unable to read from event server."); | ||||
| 				log_error("Unable to read from event server"); | ||||
| 				return 0; | ||||
| 			} | ||||
| 			if ((ret == 0) && (i > 4) && !bytes) { | ||||
| @@ -299,7 +300,7 @@ static int _daemon_write(struct dm_event_fifos *fifos, | ||||
| 		if (ret < 0) { | ||||
| 			if (errno == EINTR) | ||||
| 				continue; | ||||
| 			log_error("Unable to talk to event daemon."); | ||||
| 			log_error("Unable to talk to event daemon"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		if (ret == 0) | ||||
| @@ -308,7 +309,7 @@ static int _daemon_write(struct dm_event_fifos *fifos, | ||||
| 		if (ret < 0) { | ||||
| 			if ((errno == EINTR) || (errno == EAGAIN)) | ||||
| 				continue; | ||||
| 			log_error("Unable to talk to event daemon."); | ||||
| 			log_error("Unable to talk to event daemon"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| @@ -320,7 +321,7 @@ static int _daemon_write(struct dm_event_fifos *fifos, | ||||
| 			FD_SET(fifos->client, &fds); | ||||
| 			ret = select(fifos->client + 1, NULL, &fds, NULL, NULL); | ||||
| 			if ((ret < 0) && (errno != EINTR)) { | ||||
| 				log_error("Unable to talk to event daemon."); | ||||
| 				log_error("Unable to talk to event daemon"); | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} while (ret < 1); | ||||
| @@ -330,7 +331,7 @@ static int _daemon_write(struct dm_event_fifos *fifos, | ||||
| 			if ((errno == EINTR) || (errno == EAGAIN)) | ||||
| 				continue; | ||||
| 			else { | ||||
| 				log_error("Unable to talk to event daemon."); | ||||
| 				log_error("Unable to talk to event daemon"); | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
| @@ -360,7 +361,7 @@ int daemon_talk(struct dm_event_fifos *fifos, | ||||
| 			  getpid(), _sequence_nr, | ||||
| 			  dso_name ? : "-", dev_name ? : "-", evmask, timeout))) | ||||
| 	    < 0) { | ||||
| 		log_error("_daemon_talk: message allocation failed."); | ||||
| 		log_error("_daemon_talk: message allocation failed"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	msg->cmd = cmd; | ||||
| @@ -412,55 +413,28 @@ static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos) | ||||
| 	char default_dmeventd_path[] = DMEVENTD_PATH; | ||||
| 	char *args[] = { dmeventd_path ? : default_dmeventd_path, NULL }; | ||||
|  | ||||
| 	/* | ||||
| 	 * FIXME Explicitly verify the code's requirement that client_path is secure: | ||||
| 	 * - All parent directories owned by root without group/other write access unless sticky. | ||||
| 	 */ | ||||
| 	if (stat(fifos->client_path, &statbuf)) | ||||
| 		goto start_server; | ||||
|  | ||||
| 	/* If client fifo path exists, only use it if it is root-owned fifo mode 0600 */ | ||||
| 	if ((lstat(fifos->client_path, &statbuf) < 0)) { | ||||
| 		if (errno == ENOENT) | ||||
| 			/* Jump ahead if fifo does not already exist. */ | ||||
| 			goto start_server; | ||||
| 		else { | ||||
| 			log_sys_error("stat", fifos->client_path); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} else if (!S_ISFIFO(statbuf.st_mode)) { | ||||
| 		log_error("%s must be a fifo.", fifos->client_path); | ||||
| 		return 0; | ||||
| 	} else if (statbuf.st_uid) { | ||||
| 		log_error("%s must be owned by uid 0.", fifos->client_path); | ||||
| 		return 0; | ||||
| 	} else if (statbuf.st_mode & (S_IEXEC | S_IRWXG | S_IRWXO)) { | ||||
| 		log_error("%s must have mode 0600.", fifos->client_path); | ||||
| 	if (!S_ISFIFO(statbuf.st_mode)) { | ||||
| 		log_error("%s is not a fifo.", fifos->client_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Anyone listening?  If not, errno will be ENXIO */ | ||||
| 	fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK); | ||||
| 	if (fifos->client >= 0) { | ||||
| 		/* Should never happen if all the above checks passed. */ | ||||
| 		if ((fstat(fifos->client, &statbuf) < 0) || | ||||
| 		    !S_ISFIFO(statbuf.st_mode) || statbuf.st_uid || | ||||
| 		    (statbuf.st_mode & (S_IEXEC | S_IRWXG | S_IRWXO))) { | ||||
| 			log_error("%s is no longer a secure root-owned fifo with mode 0600.", fifos->client_path); | ||||
| 			if (close(fifos->client)) | ||||
| 				log_sys_debug("close", fifos->client_path); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		/* server is running and listening */ | ||||
| 		if (close(fifos->client)) | ||||
| 			log_sys_debug("close", fifos->client_path); | ||||
| 		return 1; | ||||
| 	} else if (errno != ENXIO && errno != ENOENT)  { | ||||
| 	} else if (errno != ENXIO) { | ||||
| 		/* problem */ | ||||
| 		log_sys_error("open", fifos->client_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| start_server: | ||||
|       start_server: | ||||
| 	/* server is not running */ | ||||
|  | ||||
| 	if ((args[0][0] == '/') && stat(args[0], &statbuf)) { | ||||
| @@ -475,11 +449,11 @@ start_server: | ||||
|  | ||||
| 	else if (!pid) { | ||||
| 		execvp(args[0], args); | ||||
| 		log_error("Unable to exec dmeventd: %s.", strerror(errno)); | ||||
| 		log_error("Unable to exec dmeventd: %s", strerror(errno)); | ||||
| 		_exit(EXIT_FAILURE); | ||||
| 	} else { | ||||
| 		if (waitpid(pid, &status, 0) < 0) | ||||
| 			log_error("Unable to start dmeventd: %s.", | ||||
| 			log_error("Unable to start dmeventd: %s", | ||||
| 				  strerror(errno)); | ||||
| 		else if (WEXITSTATUS(status)) | ||||
| 			log_error("Unable to start dmeventd."); | ||||
| @@ -552,7 +526,7 @@ static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh) | ||||
| 	struct dm_info info; | ||||
|  | ||||
| 	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) { | ||||
| 		log_error("_get_device_info: dm_task creation for info failed."); | ||||
| 		log_error("_get_device_info: dm_task creation for info failed"); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| @@ -570,17 +544,17 @@ static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh) | ||||
|  | ||||
| 	/* FIXME Add name or uuid or devno to messages */ | ||||
| 	if (!dm_task_run(dmt)) { | ||||
| 		log_error("_get_device_info: dm_task_run() failed."); | ||||
| 		log_error("_get_device_info: dm_task_run() failed"); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!dm_task_get_info(dmt, &info)) { | ||||
| 		log_error("_get_device_info: failed to get info for device."); | ||||
| 		log_error("_get_device_info: failed to get info for device"); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!info.exists) { | ||||
| 		log_error("_get_device_info: %s%s%s%.0d%s%.0d%s%s: device not found.", | ||||
| 		log_error("_get_device_info: %s%s%s%.0d%s%.0d%s%s: device not found", | ||||
| 			  dmevh->uuid ? : "", | ||||
| 			  (!dmevh->uuid && dmevh->dev_name) ? dmevh->dev_name : "", | ||||
| 			  (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? "(" : "", | ||||
| @@ -614,8 +588,8 @@ static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_messag | ||||
| 	}; | ||||
|  | ||||
| 	if (!_init_client(dmeventd_path, &fifos)) { | ||||
| 		ret = -ESRCH; | ||||
| 		goto_out; | ||||
| 		stack; | ||||
| 		return -ESRCH; | ||||
| 	} | ||||
|  | ||||
| 	ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0); | ||||
| @@ -625,7 +599,7 @@ static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_messag | ||||
|  | ||||
| 	if (!ret) | ||||
| 		ret = daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout); | ||||
| out: | ||||
|  | ||||
| 	/* what is the opposite of init? */ | ||||
| 	fini_fifos(&fifos); | ||||
|  | ||||
| @@ -649,12 +623,12 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh) | ||||
| 	    !strstr(dmevh->dso, "libdevmapper-event-lvm2snapshot.so") && | ||||
| 	    !strstr(dmevh->dso, "libdevmapper-event-lvm2mirror.so") && | ||||
| 	    !strstr(dmevh->dso, "libdevmapper-event-lvm2raid.so")) | ||||
| 		log_warn("WARNING: %s: dmeventd plugins are deprecated.", dmevh->dso); | ||||
| 		log_warn("WARNING: %s: dmeventd plugins are deprecated", dmevh->dso); | ||||
|  | ||||
|  | ||||
| 	if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg, | ||||
| 			     dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) { | ||||
| 		log_error("%s: event registration failed: %s.", | ||||
| 		log_error("%s: event registration failed: %s", | ||||
| 			  dm_task_get_name(dmt), | ||||
| 			  msg.data ? msg.data : strerror(-err)); | ||||
| 		ret = 0; | ||||
| @@ -681,7 +655,7 @@ int dm_event_unregister_handler(const struct dm_event_handler *dmevh) | ||||
|  | ||||
| 	if ((err = _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg, | ||||
| 			    dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) { | ||||
| 		log_error("%s: event deregistration failed: %s.", | ||||
| 		log_error("%s: event deregistration failed: %s", | ||||
| 			  dm_task_get_name(dmt), | ||||
| 			  msg.data ? msg.data : strerror(-err)); | ||||
| 		ret = 0; | ||||
| @@ -754,7 +728,6 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next) | ||||
|  | ||||
| 	uuid = dm_task_get_uuid(dmt); | ||||
|  | ||||
| 	/* FIXME Distinguish errors connecting to daemon */ | ||||
| 	if (_do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE : | ||||
| 		      DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path, | ||||
| 		      &msg, dmevh->dso, uuid, dmevh->mask, 0)) { | ||||
| @@ -855,79 +828,6 @@ int dm_event_get_version(struct dm_event_fifos *fifos, int *version) { | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| void dm_event_log_set(int debug_log_level, int use_syslog) | ||||
| { | ||||
| 	_debug_level = debug_log_level; | ||||
| 	_use_syslog = use_syslog; | ||||
| } | ||||
|  | ||||
| void dm_event_log(const char *subsys, int level, const char *file, | ||||
| 		  int line, int dm_errno_or_class, | ||||
| 		  const char *format, va_list ap) | ||||
| { | ||||
| 	static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER; | ||||
| 	static time_t start = 0; | ||||
| 	const char *indent = ""; | ||||
| 	FILE *stream = stdout; | ||||
| 	int prio; | ||||
| 	time_t now; | ||||
|  | ||||
| 	switch (level & ~(_LOG_STDERR | _LOG_ONCE)) { | ||||
| 	case _LOG_DEBUG: | ||||
| 		if (_debug_level < 3) | ||||
| 			return; | ||||
| 		prio = LOG_DEBUG; | ||||
| 		indent = "      "; | ||||
| 		break; | ||||
| 	case _LOG_INFO: | ||||
| 		if (_debug_level < 2) | ||||
| 			return; | ||||
| 		prio = LOG_INFO; | ||||
| 		indent = "    "; | ||||
| 		break; | ||||
| 	case _LOG_NOTICE: | ||||
| 		if (_debug_level < 1) | ||||
| 			return; | ||||
| 		prio = LOG_NOTICE; | ||||
| 		indent = "  "; | ||||
| 		break; | ||||
| 	case _LOG_WARN: | ||||
| 		prio = LOG_WARNING; | ||||
| 		break; | ||||
| 	case _LOG_ERR: | ||||
| 		prio = LOG_ERR; | ||||
| 		stream = stderr; | ||||
| 		break; | ||||
| 	default: | ||||
| 		prio = LOG_CRIT; | ||||
| 	} | ||||
|  | ||||
| 	/* Serialize to keep lines readable */ | ||||
| 	pthread_mutex_lock(&_log_mutex); | ||||
|  | ||||
| 	if (_use_syslog) { | ||||
| 		vsyslog(prio, format, ap); | ||||
| 	} else { | ||||
| 		now = time(NULL); | ||||
| 		if (!start) | ||||
| 			start = now; | ||||
| 		now -= start; | ||||
| 		fprintf(stream, "[%2d:%02d] %8x:%-6s%s", | ||||
| 			(int)now / 60, (int)now % 60, | ||||
| 			// TODO: Maybe use shorter ID | ||||
| 			// ((int)(pthread_self()) >> 6) & 0xffff, | ||||
| 			(int)pthread_self(), subsys, | ||||
| 			(_debug_level > 3) ? "" : indent); | ||||
| 		if (_debug_level > 3) | ||||
| 			fprintf(stream, "%28s:%4d %s", file, line, indent); | ||||
| 		vfprintf(stream, _(format), ap); | ||||
| 		fputc('\n', stream); | ||||
| 		fflush(stream); | ||||
| 	} | ||||
|  | ||||
| 	pthread_mutex_unlock(&_log_mutex); | ||||
| } | ||||
|  | ||||
| #if 0				/* left out for now */ | ||||
|  | ||||
| static char *_skip_string(char *src, const int delimiter) | ||||
| @@ -961,7 +861,7 @@ int dm_event_get_timeout(const char *device_path, uint32_t *timeout) | ||||
| 			     0, 0))) { | ||||
| 		char *p = _skip_string(msg.data, ' '); | ||||
| 		if (!p) { | ||||
| 			log_error("Malformed reply from dmeventd '%s'.", | ||||
| 			log_error("malformed reply from dmeventd '%s'\n", | ||||
| 				  msg.data); | ||||
| 			dm_free(msg.data); | ||||
| 			return -EIO; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of the device-mapper userspace tools. | ||||
|  * | ||||
| @@ -9,7 +9,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| @@ -105,25 +105,6 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next); | ||||
| int dm_event_register_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 */ | ||||
| void dm_event_log_set(int debug_log_level, int use_syslog); | ||||
|  | ||||
| /* Log messages acroding to current debug level  */ | ||||
| __attribute__((format(printf, 6, 0))) | ||||
| void dm_event_log(const char *subsys, int level, const char *file, | ||||
| 		  int line, int dm_errno_or_class, | ||||
| 		  const char *format, va_list ap); | ||||
| /* Macro to route print_log do dm_event_log() */ | ||||
| #define DM_EVENT_LOG_FN(subsys) \ | ||||
| void print_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(subsys, level, file, line, dm_errno_or_class, format, ap);\ | ||||
| 	va_end(ap);\ | ||||
| } | ||||
|  | ||||
| /* Prototypes for DSO interface, see dmeventd.c, struct dso_data for | ||||
|    detailed descriptions. */ | ||||
| // FIXME  misuse of bitmask as enum | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2010 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -9,15 +9,19 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "libdevmapper-event.h" | ||||
| #include "log.h" | ||||
|  | ||||
| #include "lvm2cmd.h" | ||||
| #include "dmeventd_lvm.h" | ||||
|  | ||||
| #include <pthread.h> | ||||
| #include <syslog.h> | ||||
|  | ||||
| extern int dmeventd_debug; | ||||
|  | ||||
| /* | ||||
|  * register_device() is called first and performs initialisation. | ||||
| @@ -32,19 +36,48 @@ static int _register_count = 0; | ||||
| static struct dm_pool *_mem_pool = NULL; | ||||
| static void *_lvm_handle = NULL; | ||||
|  | ||||
| DM_EVENT_LOG_FN("lvm") | ||||
|  | ||||
| static void _lvm2_print_log(int level, const char *file, int line, | ||||
| 			    int dm_errno_or_class, const char *msg) | ||||
| { | ||||
| 	print_log(level, file, line, dm_errno_or_class, "%s", msg); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Currently only one event can be processed at a time. | ||||
|  */ | ||||
| static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER; | ||||
|  | ||||
| /* | ||||
|  * FIXME Do not pass things directly to syslog, rather use the existing logging | ||||
|  * facilities to sort logging ... however that mechanism needs to be somehow | ||||
|  * configurable and we don't have that option yet | ||||
|  */ | ||||
| static void _temporary_log_fn(int level, | ||||
| 			      const char *file __attribute__((unused)), | ||||
| 			      int line __attribute__((unused)), | ||||
| 			      int dm_errno __attribute__((unused)), | ||||
| 			      const char *message) | ||||
| { | ||||
| 	level &= ~(_LOG_STDERR | _LOG_ONCE); | ||||
|  | ||||
| 	switch (level) { | ||||
| 	case _LOG_DEBUG: | ||||
| 		if (dmeventd_debug >= 3) | ||||
| 			syslog(LOG_DEBUG, "%s", message); | ||||
| 		break; | ||||
| 	case _LOG_INFO: | ||||
| 		if (dmeventd_debug >= 2) | ||||
| 			syslog(LOG_INFO, "%s", message); | ||||
| 		break; | ||||
| 	case _LOG_NOTICE: | ||||
| 		if (dmeventd_debug >= 1) | ||||
| 			syslog(LOG_NOTICE, "%s", message); | ||||
| 		break; | ||||
| 	case _LOG_WARN: | ||||
| 		syslog(LOG_WARNING, "%s", message); | ||||
| 		break; | ||||
| 	case _LOG_ERR: | ||||
| 		syslog(LOG_ERR, "%s", message); | ||||
| 		break; | ||||
| 	default: | ||||
| 		syslog(LOG_CRIT, "%s", message); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void dmeventd_lvm2_lock(void) | ||||
| { | ||||
| 	pthread_mutex_lock(&_event_mutex); | ||||
| @@ -61,26 +94,23 @@ int dmeventd_lvm2_init(void) | ||||
|  | ||||
| 	pthread_mutex_lock(&_register_mutex); | ||||
|  | ||||
| 	/* | ||||
| 	 * Need some space for allocations.  1024 should be more | ||||
| 	 * than enough for what we need (device mapper name splitting) | ||||
| 	 */ | ||||
| 	if (!_mem_pool && !(_mem_pool = dm_pool_create("mirror_dso", 1024))) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!_lvm_handle) { | ||||
| 		lvm2_log_fn(_lvm2_print_log); | ||||
|  | ||||
| 		if (!(_lvm_handle = lvm2_init())) | ||||
| 			goto out; | ||||
|  | ||||
| 		/* | ||||
| 		 * Need some space for allocations.  1024 should be more | ||||
| 		 * than enough for what we need (device mapper name splitting) | ||||
| 		 */ | ||||
| 		if (!_mem_pool && !(_mem_pool = dm_pool_create("mirror_dso", 1024))) { | ||||
| 			lvm2_exit(_lvm_handle); | ||||
| 			_lvm_handle = NULL; | ||||
| 		lvm2_log_fn(_temporary_log_fn); | ||||
| 		if (!(_lvm_handle = lvm2_init())) { | ||||
| 			dm_pool_destroy(_mem_pool); | ||||
| 			_mem_pool = NULL; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| 		lvm2_disable_dmeventd_monitoring(_lvm_handle); | ||||
| 		/* FIXME Temporary: move to dmeventd core */ | ||||
| 		lvm2_run(_lvm_handle, "_memlock_inc"); | ||||
| 		log_debug("lvm plugin initilized."); | ||||
| 	} | ||||
|  | ||||
| 	_register_count++; | ||||
| @@ -96,13 +126,11 @@ void dmeventd_lvm2_exit(void) | ||||
| 	pthread_mutex_lock(&_register_mutex); | ||||
|  | ||||
| 	if (!--_register_count) { | ||||
| 		log_debug("lvm plugin shuting down."); | ||||
| 		lvm2_run(_lvm_handle, "_memlock_dec"); | ||||
| 		dm_pool_destroy(_mem_pool); | ||||
| 		_mem_pool = NULL; | ||||
| 		lvm2_exit(_lvm_handle); | ||||
| 		_lvm_handle = NULL; | ||||
| 		log_debug("lvm plugin exited."); | ||||
| 	} | ||||
|  | ||||
| 	pthread_mutex_unlock(&_register_mutex); | ||||
| @@ -125,8 +153,8 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size, | ||||
| 	int r; | ||||
|  | ||||
| 	if (!dm_split_lvm_name(mem, device, &vg, &lv, &layer)) { | ||||
| 		log_error("Unable to determine VG name from %s.", | ||||
| 			  device); | ||||
| 		syslog(LOG_ERR, "Unable to determine VG name from %s.\n", | ||||
| 		       device); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| @@ -140,7 +168,7 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size, | ||||
| 	dm_pool_free(mem, vg); | ||||
|  | ||||
| 	if (r < 0) { | ||||
| 		log_error("Unable to form LVM command. (too long)."); | ||||
| 		syslog(LOG_ERR, "Unable to form LVM command. (too long).\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2010 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -9,7 +9,7 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| @@ -39,36 +39,4 @@ struct dm_pool *dmeventd_lvm2_pool(void); | ||||
| int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size, | ||||
| 			  const char *cmd, const char *device); | ||||
|  | ||||
| #define dmeventd_lvm2_run_with_lock(cmdline) \ | ||||
| 	({\ | ||||
| 		int rc;\ | ||||
| 		dmeventd_lvm2_lock();\ | ||||
| 		rc = dmeventd_lvm2_run(cmdline);\ | ||||
| 		dmeventd_lvm2_unlock();\ | ||||
| 		rc;\ | ||||
| 	}) | ||||
|  | ||||
| #define dmeventd_lvm2_init_with_pool(name, st) \ | ||||
| 	({\ | ||||
| 		struct dm_pool *mem;\ | ||||
| 		st = NULL;\ | ||||
| 		if (dmeventd_lvm2_init()) {\ | ||||
| 			if ((mem = dm_pool_create(name, 2048)) &&\ | ||||
| 			    (st = dm_pool_zalloc(mem, sizeof(*st))))\ | ||||
| 				st->mem = mem;\ | ||||
| 			else {\ | ||||
| 				if (mem)\ | ||||
| 					dm_pool_destroy(mem);\ | ||||
| 				dmeventd_lvm2_exit();\ | ||||
| 			}\ | ||||
| 		}\ | ||||
| 		st;\ | ||||
| 	}) | ||||
|  | ||||
| #define dmeventd_lvm2_exit_with_pool(pool) \ | ||||
| 	do {\ | ||||
| 		dm_pool_destroy(pool->mem);\ | ||||
| 		dmeventd_lvm2_exit();\ | ||||
| 	} while(0) | ||||
|  | ||||
| #endif /* _DMEVENTD_LVMWRAP_H */ | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2005-2012 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -9,31 +9,26 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
|  | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "activate.h"	/* For TARGET_NAME* */ | ||||
| #include "defaults.h" | ||||
|  | ||||
| #include <syslog.h> /* FIXME Replace syslog with multilog */ | ||||
| /* FIXME Missing openlog? */ | ||||
| /* FIXME Replace most syslogs with log_error() style messages and add complete context. */ | ||||
| /* FIXME Reformat to 80 char lines. */ | ||||
|  | ||||
| #define ME_IGNORE    0 | ||||
| #define ME_INSYNC    1 | ||||
| #define ME_FAILURE   2 | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	char cmd_lvscan[512]; | ||||
| 	char cmd_lvconvert[512]; | ||||
| }; | ||||
|  | ||||
| DM_EVENT_LOG_FN("mirr") | ||||
|  | ||||
| static void _process_status_code(dm_status_mirror_health_t health, | ||||
| 				 uint32_t major, uint32_t minor, | ||||
| 				 const char *dev_type, int *r) | ||||
| static int _process_status_code(const char status_code, const char *dev_name, | ||||
| 				const char *dev_type, int r) | ||||
| { | ||||
| 	/* | ||||
| 	 *    A => Alive - No failures | ||||
| @@ -43,181 +38,196 @@ static void _process_status_code(dm_status_mirror_health_t health, | ||||
| 	 *    R => Read - A read failure occurred, mirror data unaffected | ||||
| 	 *    U => Unclassified failure (bug) | ||||
| 	 */  | ||||
| 	switch (health) { | ||||
| 	case DM_STATUS_MIRROR_ALIVE: | ||||
| 		return; | ||||
| 	case DM_STATUS_MIRROR_FLUSH_FAILED: | ||||
| 		log_error("%s device %u:%u flush failed.", | ||||
| 			  dev_type, major, minor); | ||||
| 		*r = ME_FAILURE; | ||||
| 		break; | ||||
| 	case DM_STATUS_MIRROR_SYNC_FAILED: | ||||
| 		log_error("%s device %u:%u sync failed.", | ||||
| 			  dev_type, major, minor); | ||||
| 		break; | ||||
| 	case DM_STATUS_MIRROR_READ_FAILED: | ||||
| 		log_error("%s device %u:%u read failed.", | ||||
| 			  dev_type, major, minor); | ||||
| 		break; | ||||
| 	default: | ||||
| 		log_error("%s device %u:%u has failed (%c).", | ||||
| 			  dev_type, major, minor, (char)health); | ||||
| 		*r = ME_FAILURE; | ||||
| 		break; | ||||
| 	if (status_code == 'F') { | ||||
| 		syslog(LOG_ERR, "%s device %s flush failed.", | ||||
| 		       dev_type, dev_name); | ||||
| 		r = ME_FAILURE; | ||||
| 	} else if (status_code == 'S') | ||||
| 		syslog(LOG_ERR, "%s device %s sync failed.", | ||||
| 		       dev_type, dev_name); | ||||
| 	else if (status_code == 'R') | ||||
| 		syslog(LOG_ERR, "%s device %s read failed.", | ||||
| 		       dev_type, dev_name); | ||||
| 	else if (status_code != 'A') { | ||||
| 		syslog(LOG_ERR, "%s device %s has failed (%c).", | ||||
| 		       dev_type, dev_name, status_code); | ||||
| 		r = ME_FAILURE; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int _get_mirror_event(struct dso_state *state, char *params) | ||||
| { | ||||
| 	int r = ME_INSYNC; | ||||
| 	unsigned i; | ||||
| 	struct dm_status_mirror *ms; | ||||
|  | ||||
| 	if (!dm_get_status_mirror(state->mem, params, &ms)) | ||||
| 		goto_out; | ||||
|  | ||||
| 	/* Check for bad mirror devices */ | ||||
| 	for (i = 0; i < ms->dev_count; ++i) | ||||
| 		_process_status_code(ms->devs[i].health, | ||||
| 				     ms->devs[i].major, ms->devs[i].minor, | ||||
| 				     i ? "Secondary mirror" : "Primary mirror", &r); | ||||
|  | ||||
| 	/* Check for bad disk log device */ | ||||
| 	for (i = 0; i < ms->log_count; ++i) | ||||
| 		_process_status_code(ms->logs[i].health, | ||||
| 				     ms->logs[i].major, ms->logs[i].minor, | ||||
| 				     "Log", &r); | ||||
|  | ||||
| 	/* Ignore if not in-sync */ | ||||
| 	if ((r == ME_INSYNC) && (ms->insync_regions != ms->total_regions)) | ||||
| 		r = ME_IGNORE; | ||||
|  | ||||
| 	dm_pool_free(state->mem, ms); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _get_mirror_event(char *params) | ||||
| { | ||||
| 	int i, r = ME_INSYNC; | ||||
| 	char **args = NULL; | ||||
| 	char *dev_status_str; | ||||
| 	char *log_status_str; | ||||
| 	char *sync_str; | ||||
| 	char *p = NULL; | ||||
| 	int log_argc, num_devs; | ||||
|  | ||||
| 	/* | ||||
| 	 * dm core parms:	     0 409600 mirror | ||||
| 	 * Mirror core parms:	     2 253:4 253:5 400/400 | ||||
| 	 * New-style failure params: 1 AA | ||||
| 	 * New-style log params:     3 cluster 253:3 A | ||||
| 	 *			 or  3 disk 253:3 A | ||||
| 	 *			 or  1 core | ||||
| 	 */ | ||||
|  | ||||
| 	/* number of devices */ | ||||
| 	if (!dm_split_words(params, 1, 0, &p)) | ||||
| 		goto out_parse; | ||||
|  | ||||
| 	if (!(num_devs = atoi(p)) || | ||||
| 	    (num_devs > DEFAULT_MIRROR_MAX_IMAGES) || (num_devs < 0)) | ||||
| 		goto out_parse; | ||||
| 	p += strlen(p) + 1; | ||||
|  | ||||
| 	/* devices names + "400/400" + "1 AA" + 1 or 3 log parms + NULL */ | ||||
| 	args = dm_malloc((num_devs + 7) * sizeof(char *)); | ||||
| 	if (!args || dm_split_words(p, num_devs + 7, 0, args) < num_devs + 5) | ||||
| 		goto out_parse; | ||||
|  | ||||
| 	/* FIXME: Code differs from lib/mirror/mirrored.c */ | ||||
| 	dev_status_str = args[2 + num_devs]; | ||||
| 	log_argc = atoi(args[3 + num_devs]); | ||||
| 	log_status_str = args[3 + num_devs + log_argc]; | ||||
| 	sync_str = args[num_devs]; | ||||
|  | ||||
| 	/* Check for bad mirror devices */ | ||||
| 	for (i = 0; i < num_devs; i++) | ||||
| 		r = _process_status_code(dev_status_str[i], args[i], | ||||
| 			i ? "Secondary mirror" : "Primary mirror", r); | ||||
|  | ||||
| 	/* Check for bad disk log device */ | ||||
| 	if (log_argc > 1) | ||||
| 		r = _process_status_code(log_status_str[0], | ||||
| 					 args[2 + num_devs + log_argc], | ||||
| 					 "Log", r); | ||||
|  | ||||
| 	if (r == ME_FAILURE) | ||||
| 		goto out; | ||||
|  | ||||
| 	p = strstr(sync_str, "/"); | ||||
| 	if (p) { | ||||
| 		p[0] = '\0'; | ||||
| 		if (strcmp(sync_str, p+1)) | ||||
| 			r = ME_IGNORE; | ||||
| 		p[0] = '/'; | ||||
| 	} else | ||||
| 		goto out_parse; | ||||
|  | ||||
| out: | ||||
| 	log_error("Unable to parse mirror status string."); | ||||
| 	dm_free(args); | ||||
| 	return r; | ||||
|  | ||||
| out_parse: | ||||
| 	dm_free(args); | ||||
| 	syslog(LOG_ERR, "Unable to parse mirror status string."); | ||||
| 	return ME_IGNORE; | ||||
| } | ||||
|  | ||||
| static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert) | ||||
| static int _remove_failed_devices(const char *device) | ||||
| { | ||||
| 	int r; | ||||
| #define CMD_SIZE 256	/* FIXME Use system restriction */ | ||||
| 	char cmd_str[CMD_SIZE]; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_run_with_lock(cmd_lvscan)) | ||||
| 		log_info("Re-scan of mirrored device failed."); | ||||
| 	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str), | ||||
| 				  "lvconvert --config devices{ignore_suspended_devices=1} " | ||||
| 				  "--repair --use-policies", device)) | ||||
| 		return -ENAMETOOLONG; /* FIXME Replace with generic error return - reason for failure has already got logged */ | ||||
|  | ||||
| 	/* if repair goes OK, report success even if lvscan has failed */ | ||||
| 	r = dmeventd_lvm2_run_with_lock(cmd_lvconvert); | ||||
| 	r = dmeventd_lvm2_run(cmd_str); | ||||
|  | ||||
| 	log_info("Repair of mirrored device %s.", | ||||
| 		 (r) ? "finished successfully" : "failed"); | ||||
| 	syslog(LOG_INFO, "Repair of mirrored device %s %s.", device, | ||||
| 	       (r) ? "finished successfully" : "failed"); | ||||
|  | ||||
| 	return r; | ||||
| 	return (r) ? 0 : -1; | ||||
| } | ||||
|  | ||||
| void process_event(struct dm_task *dmt, | ||||
| 		   enum dm_event_mask event __attribute__((unused)), | ||||
| 		   void **user) | ||||
| 		   void **unused __attribute__((unused))) | ||||
| { | ||||
| 	struct dso_state *state = *user; | ||||
| 	void *next = NULL; | ||||
| 	uint64_t start, length; | ||||
| 	char *target_type = NULL; | ||||
| 	char *params; | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
|  | ||||
| 	dmeventd_lvm2_lock(); | ||||
|  | ||||
| 	do { | ||||
| 		next = dm_get_next_target(dmt, next, &start, &length, | ||||
| 					  &target_type, ¶ms); | ||||
|  | ||||
| 		if (!target_type) { | ||||
| 			log_info("%s mapping lost.", device); | ||||
| 			syslog(LOG_INFO, "%s mapping lost.", device); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (strcmp(target_type, TARGET_NAME_MIRROR)) { | ||||
| 			log_info("%s has unmirrored portion.", device); | ||||
| 		if (strcmp(target_type, "mirror")) { | ||||
| 			syslog(LOG_INFO, "%s has unmirrored portion.", device); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		switch(_get_mirror_event(state, params)) { | ||||
| 		switch(_get_mirror_event(params)) { | ||||
| 		case ME_INSYNC: | ||||
| 			/* FIXME: all we really know is that this | ||||
| 			   _part_ of the device is in sync | ||||
| 			   Also, this is not an error | ||||
| 			*/ | ||||
| 			log_notice("%s is now in-sync.", device); | ||||
| 			syslog(LOG_NOTICE, "%s is now in-sync.", device); | ||||
| 			break; | ||||
| 		case ME_FAILURE: | ||||
| 			log_error("Device failure in %s.", device); | ||||
| 			if (!_remove_failed_devices(state->cmd_lvscan, | ||||
| 						    state->cmd_lvconvert)) | ||||
| 			syslog(LOG_ERR, "Device failure in %s.", device); | ||||
| 			if (_remove_failed_devices(device)) | ||||
| 				/* FIXME Why are all the error return codes unused? Get rid of them? */ | ||||
| 				log_error("Failed to remove faulty devices in %s.", | ||||
| 					  device); | ||||
| 				syslog(LOG_ERR, "Failed to remove faulty devices in %s.", | ||||
| 				       device); | ||||
| 			/* Should check before warning user that device is now linear | ||||
| 			else | ||||
| 				log_notice("%s is now a linear device.", | ||||
| 					   device); | ||||
| 				syslog(LOG_NOTICE, "%s is now a linear device.\n", | ||||
| 					device); | ||||
| 			*/ | ||||
| 			break; | ||||
| 		case ME_IGNORE: | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* FIXME Provide value then! */ | ||||
| 			log_info("Unknown event received."); | ||||
| 			syslog(LOG_INFO, "Unknown event received."); | ||||
| 		} | ||||
| 	} while (next); | ||||
|  | ||||
| 	dmeventd_lvm2_unlock(); | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| 		    const char *uuid __attribute__((unused)), | ||||
| 		    int major __attribute__((unused)), | ||||
| 		    int minor __attribute__((unused)), | ||||
| 		    void **user) | ||||
| 		    void **unused __attribute__((unused))) | ||||
| { | ||||
| 	struct dso_state *state; | ||||
| 	if (!dmeventd_lvm2_init()) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_init_with_pool("mirror_state", state)) | ||||
| 		goto_bad; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan), | ||||
| 				   "lvscan --cache", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		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; | ||||
|  | ||||
| 	log_info("Monitoring mirror device %s for events.", device); | ||||
| 	syslog(LOG_INFO, "Monitoring mirror device %s for events.", device); | ||||
|  | ||||
| 	return 1; | ||||
| bad: | ||||
| 	log_error("Failed to monitor mirror %s.", device); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int unregister_device(const char *device, | ||||
| 		      const char *uuid __attribute__((unused)), | ||||
| 		      int major __attribute__((unused)), | ||||
| 		      int minor __attribute__((unused)), | ||||
| 		      void **user) | ||||
| 		      void **unused __attribute__((unused))) | ||||
| { | ||||
| 	struct dso_state *state = *user; | ||||
|  | ||||
| 	dmeventd_lvm2_exit_with_pool(state); | ||||
| 	log_info("No longer monitoring mirror device %s for events.", | ||||
| 		 device); | ||||
| 	syslog(LOG_INFO, "No longer monitoring mirror device %s for events.", | ||||
| 	       device); | ||||
| 	dmeventd_lvm2_exit(); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2005-2016 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2005-2011 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -9,161 +9,172 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "defaults.h" | ||||
| #include "dmeventd_lvm.h" | ||||
|  | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd_lvm.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 dm_pool *mem; | ||||
| 	char cmd_lvscan[512]; | ||||
| 	char cmd_lvconvert[512]; | ||||
| 	uint64_t raid_devs[RAID_DEVS_ELEMS]; | ||||
| 	int failed; | ||||
| 	int warned; | ||||
| }; | ||||
|  | ||||
| DM_EVENT_LOG_FN("raid") | ||||
|  | ||||
| #include <syslog.h> /* FIXME Replace syslog with multilog */ | ||||
| /* FIXME Missing openlog? */ | ||||
| /* FIXME Replace most syslogs with log_error() style messages and add complete context. */ | ||||
| /* FIXME Reformat to 80 char lines. */ | ||||
|  | ||||
| static int _process_raid_event(struct dso_state *state, char *params, const char *device) | ||||
| /* | ||||
|  * run_repair is a close copy to | ||||
|  * plugins/mirror/dmeventd_mirror.c:_remove_failed_devices() | ||||
|  */ | ||||
| static int run_repair(const char *device) | ||||
| { | ||||
| 	struct dm_status_raid *status; | ||||
| 	const char *d; | ||||
| 	int dead = 0, r = 1; | ||||
| 	int r; | ||||
| #define CMD_SIZE 256	/* FIXME Use system restriction */ | ||||
| 	char cmd_str[CMD_SIZE]; | ||||
|  | ||||
| 	if (!dm_get_status_raid(state->mem, params, &status)) { | ||||
| 		log_error("Failed to process status line for %s.", device); | ||||
| 		return 0; | ||||
| 	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str), | ||||
| 				  "lvscan --cache", device)) | ||||
| 		return -1; | ||||
|  | ||||
| 	r = dmeventd_lvm2_run(cmd_str); | ||||
|  | ||||
| 	if (!r) | ||||
| 		syslog(LOG_INFO, "Re-scan of RAID device %s failed.", device); | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str), | ||||
| 				  "lvconvert --config devices{ignore_suspended_devices=1} " | ||||
| 				  "--repair --use-policies", device)) | ||||
| 		return -1; | ||||
|  | ||||
| 	/* if repair goes OK, report success even if lvscan has failed */ | ||||
| 	r = dmeventd_lvm2_run(cmd_str); | ||||
|  | ||||
| 	if (!r) | ||||
| 		syslog(LOG_INFO, "Repair of RAID device %s failed.", device); | ||||
|  | ||||
| 	return (r) ? 0 : -1; | ||||
| } | ||||
|  | ||||
| static int _process_raid_event(char *params, const char *device) | ||||
| { | ||||
| 	int i, n, failure = 0; | ||||
| 	char *p, *a[4]; | ||||
| 	char *raid_type; | ||||
| 	char *num_devices; | ||||
| 	char *health_chars; | ||||
| 	char *resync_ratio; | ||||
|  | ||||
| 	/* | ||||
| 	 * RAID parms:     <raid_type> <#raid_disks> \ | ||||
| 	 *                 <health chars> <resync ratio> | ||||
| 	 */ | ||||
| 	if (!dm_split_words(params, 4, 0, a)) { | ||||
| 		syslog(LOG_ERR, "Failed to process status line for %s\n", | ||||
| 		       device); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	raid_type = a[0]; | ||||
| 	num_devices = a[1]; | ||||
| 	health_chars = a[2]; | ||||
| 	resync_ratio = a[3]; | ||||
|  | ||||
| 	if (!(n = atoi(num_devices))) { | ||||
| 		syslog(LOG_ERR, "Failed to parse number of devices for %s: %s", | ||||
| 		       device, num_devices); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	d = status->dev_health; | ||||
| 	while ((d = strchr(d, 'D'))) { | ||||
| 		uint32_t dev = (uint32_t)(d - status->dev_health); | ||||
|  | ||||
| 		if (!(state->raid_devs[dev / 64] & (UINT64_C(1) << (dev % 64)))) | ||||
| 			log_error("Device #%u of %s array, %s, has failed.", | ||||
| 				  dev, status->raid_type, device); | ||||
|  | ||||
| 		state->raid_devs[dev / 64] |= (UINT64_C(1) << (dev % 64)); | ||||
| 		d++; | ||||
| 		dead = 1; | ||||
| 	} | ||||
|  | ||||
| 	if (dead) { | ||||
| 		if (status->insync_regions < status->total_regions) { | ||||
| 			if (!state->warned) | ||||
| 				log_warn("WARNING: waiting for resynchronization to finish " | ||||
| 					 "before initiating repair on RAID device %s", device); | ||||
|  | ||||
| 			state->warned = 1; | ||||
| 			goto out; /* Not yet done syncing with accessible devices */ | ||||
| 	for (i = 0; i < n; i++) { | ||||
| 		switch (health_chars[i]) { | ||||
| 		case 'A': | ||||
| 			/* Device is 'A'live and well */ | ||||
| 		case 'a': | ||||
| 			/* Device is 'a'live, but not yet in-sync */ | ||||
| 			break; | ||||
| 		case 'D': | ||||
| 			syslog(LOG_ERR, | ||||
| 			       "Device #%d of %s array, %s, has failed.", | ||||
| 			       i, raid_type, device); | ||||
| 			failure++; | ||||
| 			break; | ||||
| 		default: | ||||
| 			/* Unhandled character returned from kernel */ | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		if (state->failed) | ||||
| 			goto out; /* already reported */ | ||||
|  | ||||
| 		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 (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) { | ||||
| 			log_info("Repair of RAID device %s failed.", device); | ||||
| 			r = 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		state->failed = 0; | ||||
| 		log_info("%s array, %s, is %s in-sync.", | ||||
| 			 status->raid_type, device, | ||||
| 			 (status->insync_regions == status->total_regions) ? "now" : "not"); | ||||
| 		if (failure) | ||||
| 			return run_repair(device); | ||||
| 	} | ||||
| out: | ||||
| 	dm_pool_free(state->mem, status); | ||||
|  | ||||
| 	return r; | ||||
| 	p = strstr(resync_ratio, "/"); | ||||
| 	if (!p) { | ||||
| 		syslog(LOG_ERR, "Failed to parse resync_ratio for %s: %s", | ||||
| 		       device, resync_ratio); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	p[0] = '\0'; | ||||
| 	syslog(LOG_INFO, "%s array, %s, is %s in-sync.", | ||||
| 	       raid_type, device, strcmp(resync_ratio, p+1) ? "not" : "now"); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void process_event(struct dm_task *dmt, | ||||
| 		   enum dm_event_mask event __attribute__((unused)), | ||||
| 		   void **user) | ||||
| 		   void **unused __attribute__((unused))) | ||||
| { | ||||
| 	struct dso_state *state = *user; | ||||
| 	void *next = NULL; | ||||
| 	uint64_t start, length; | ||||
| 	char *target_type = NULL; | ||||
| 	char *params; | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
|  | ||||
| 	dmeventd_lvm2_lock(); | ||||
|  | ||||
| 	do { | ||||
| 		next = dm_get_next_target(dmt, next, &start, &length, | ||||
| 					  &target_type, ¶ms); | ||||
|  | ||||
| 		if (!target_type) { | ||||
| 			log_info("%s mapping lost.", device); | ||||
| 			syslog(LOG_INFO, "%s mapping lost.", device); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (strcmp(target_type, "raid")) { | ||||
| 			log_info("%s has non-raid portion.", device); | ||||
| 			syslog(LOG_INFO, "%s has non-raid portion.", device); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if (!_process_raid_event(state, params, device)) | ||||
| 			log_error("Failed to process event for %s.", | ||||
| 				  device); | ||||
| 		if (_process_raid_event(params, device)) | ||||
| 			syslog(LOG_ERR, "Failed to process event for %s", | ||||
| 			       device); | ||||
| 	} while (next); | ||||
|  | ||||
| 	dmeventd_lvm2_unlock(); | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| 		    const char *uuid __attribute__((unused)), | ||||
| 		    int major __attribute__((unused)), | ||||
| 		    int minor __attribute__((unused)), | ||||
| 		    void **user) | ||||
| 		    void **unused __attribute__((unused))) | ||||
| { | ||||
| 	struct dso_state *state; | ||||
| 	if (!dmeventd_lvm2_init()) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_init_with_pool("raid_state", state)) | ||||
| 		goto_bad; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan), | ||||
| 				   "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; | ||||
| 	} | ||||
|  | ||||
| 	*user = state; | ||||
|  | ||||
| 	log_info("Monitoring RAID device %s for events.", device); | ||||
| 	syslog(LOG_INFO, "Monitoring RAID device %s for events.", device); | ||||
|  | ||||
| 	return 1; | ||||
| bad: | ||||
| 	log_error("Failed to monitor RAID %s.", device); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int unregister_device(const char *device, | ||||
| 		      const char *uuid __attribute__((unused)), | ||||
| 		      int major __attribute__((unused)), | ||||
| 		      int minor __attribute__((unused)), | ||||
| 		      void **user) | ||||
| 		      void **unused __attribute__((unused))) | ||||
| { | ||||
| 	struct dso_state *state = *user; | ||||
|  | ||||
| 	dmeventd_lvm2_exit_with_pool(state); | ||||
| 	log_info("No longer monitoring RAID device %s for events.", | ||||
| 		 device); | ||||
| 	syslog(LOG_INFO, "No longer monitoring RAID device %s for events.", | ||||
| 	       device); | ||||
| 	dmeventd_lvm2_exit(); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2007-2015 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2007-2011 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -9,36 +9,35 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h" | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "libdevmapper-event.h" | ||||
|  | ||||
| #include <sys/sysmacros.h> | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd_lvm.h" | ||||
|  | ||||
| #include <sys/wait.h> | ||||
| #include <syslog.h> /* FIXME Replace syslog with multilog */ | ||||
| #include <stdarg.h> | ||||
| #include <pthread.h> | ||||
| /* FIXME Missing openlog? */ | ||||
|  | ||||
| /* First warning when snapshot is 80% full. */ | ||||
| #define WARNING_THRESH	(DM_PERCENT_1 * 80) | ||||
| #define WARNING_THRESH 80 | ||||
| /* Run a check every 5%. */ | ||||
| #define CHECK_STEP	(DM_PERCENT_1 *  5) | ||||
| #define CHECK_STEP 5 | ||||
| /* Do not bother checking snapshots less than 50% full. */ | ||||
| #define CHECK_MINIMUM	(DM_PERCENT_1 * 50) | ||||
| #define CHECK_MINIMUM 50 | ||||
|  | ||||
| #define UMOUNT_COMMAND "/bin/umount" | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	dm_percent_t percent_check; | ||||
| 	int percent_check; | ||||
| 	uint64_t known_size; | ||||
| 	char cmd_lvextend[512]; | ||||
| 	char cmd_str[1024]; | ||||
| }; | ||||
|  | ||||
| DM_EVENT_LOG_FN("snap") | ||||
|  | ||||
| static int _run(const char *cmd, ...) | ||||
| { | ||||
|         va_list ap; | ||||
| @@ -63,7 +62,7 @@ static int _run(const char *cmd, ...) | ||||
|                 va_end(ap); | ||||
|  | ||||
|                 execvp(cmd, (char **)argv); | ||||
|                 log_sys_error("exec", cmd); | ||||
|                 syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno)); | ||||
|                 exit(127); | ||||
|         } | ||||
|  | ||||
| @@ -82,56 +81,18 @@ static int _run(const char *cmd, ...) | ||||
|  | ||||
| static int _extend(const char *cmd) | ||||
| { | ||||
| 	log_debug("Extending snapshot via %s.", cmd); | ||||
| 	return dmeventd_lvm2_run_with_lock(cmd); | ||||
| 	return dmeventd_lvm2_run(cmd); | ||||
| } | ||||
|  | ||||
| #ifdef SNAPSHOT_REMOVE | ||||
| /* Remove invalid snapshot from dm-table */ | ||||
| /* Experimental for now and not used by default */ | ||||
| static int _remove(const char *uuid) | ||||
| { | ||||
| 	int r = 1; | ||||
| 	uint32_t cookie = 0; | ||||
| 	struct dm_task *dmt; | ||||
|  | ||||
| 	if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!dm_task_set_uuid(dmt, uuid)) { | ||||
| 		r = 0; | ||||
| 		goto_out; | ||||
| 	} | ||||
|  | ||||
| 	dm_task_retry_remove(dmt); | ||||
|  | ||||
| 	if (!dm_task_set_cookie(dmt, &cookie, 0)) { | ||||
| 		r = 0; | ||||
| 		goto_out; | ||||
| 	} | ||||
|  | ||||
| 	if (!dm_task_run(dmt)) { | ||||
| 		r = 0; | ||||
| 		goto_out; | ||||
| 	} | ||||
| out: | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
| #endif /* SNAPSHOT_REMOVE */ | ||||
|  | ||||
| static void _umount(const char *device, int major, int minor) | ||||
| { | ||||
| 	FILE *mounts; | ||||
| 	char buffer[4096]; | ||||
| 	char *words[3]; | ||||
| 	struct stat st; | ||||
| 	const char procmounts[] = "/proc/mounts"; | ||||
|  | ||||
| 	if (!(mounts = fopen(procmounts, "r"))) { | ||||
| 		log_sys_error("fopen", procmounts); | ||||
| 		log_error("Not umounting %s.", device); | ||||
| 	if (!(mounts = fopen("/proc/mounts", "r"))) { | ||||
| 		syslog(LOG_ERR, "Could not read /proc/mounts. Not umounting %s.\n", device); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| @@ -149,24 +110,23 @@ static void _umount(const char *device, int major, int minor) | ||||
| 			continue; /* can't stat, skip this one */ | ||||
|  | ||||
| 		if (S_ISBLK(st.st_mode) && | ||||
| 		    (int) major(st.st_rdev) == major && | ||||
| 		    (int) minor(st.st_rdev) == minor) { | ||||
| 			log_error("Unmounting invalid snapshot %s from %s.", device, words[1]); | ||||
| 			if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL)) | ||||
| 				log_error("Failed to umount snapshot %s from %s: %s.", | ||||
| 					  device, words[1], strerror(errno)); | ||||
| 		    major(st.st_rdev) == major && | ||||
| 		    minor(st.st_rdev) == minor) { | ||||
| 			syslog(LOG_ERR, "Unmounting invalid snapshot %s from %s.\n", device, words[1]); | ||||
|                         if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL)) | ||||
|                                 syslog(LOG_ERR, "Failed to umount snapshot %s from %s: %s.\n", | ||||
|                                        device, words[1], strerror(errno)); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (fclose(mounts)) | ||||
| 		log_sys_error("close", procmounts); | ||||
| 		syslog(LOG_ERR, "Failed to close /proc/mounts.\n"); | ||||
| } | ||||
|  | ||||
| void process_event(struct dm_task *dmt, | ||||
| 		   enum dm_event_mask event __attribute__((unused)), | ||||
| 		   void **user) | ||||
| 		   void **private) | ||||
| { | ||||
| 	struct dso_state *state = *user; | ||||
| 	void *next = NULL; | ||||
| 	uint64_t start, length; | ||||
| 	char *target_type = NULL; | ||||
| @@ -174,47 +134,28 @@ void process_event(struct dm_task *dmt, | ||||
| 	struct dm_status_snapshot *status = NULL; | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
| 	int percent; | ||||
| 	struct dm_info info; | ||||
| 	struct dso_state *state = *private; | ||||
|  | ||||
| 	/* No longer monitoring, waiting for remove */ | ||||
| 	if (!state->percent_check) | ||||
| 		return; | ||||
|  | ||||
| 	dmeventd_lvm2_lock(); | ||||
|  | ||||
| 	dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); | ||||
| 	if (!target_type || strcmp(target_type, "snapshot")) { | ||||
| 		log_error("Target %s is not snapshot.", target_type); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (!target_type) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_get_status_snapshot(state->mem, params, &status)) { | ||||
| 		log_error("Cannot parse snapshot %s state: %s.", device, params); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (!dm_get_status_snapshot(state->mem, params, &status)) | ||||
| 		goto out; | ||||
|  | ||||
| 	/* | ||||
| 	 * If the snapshot has been invalidated or we failed to parse | ||||
| 	 * the status string. Report the full status string to syslog. | ||||
| 	 */ | ||||
| 	if (status->invalid || status->overflow || !status->total_sectors) { | ||||
| 		log_warn("WARNING: Snapshot %s changed state to: %s and should be removed.", | ||||
| 			 device, params); | ||||
| 		state->percent_check = 0; | ||||
| 		if (dm_task_get_info(dmt, &info)) | ||||
| 	if (status->invalid) { | ||||
| 		struct dm_info info; | ||||
| 		if (dm_task_get_info(dmt, &info)) { | ||||
| 			dmeventd_lvm2_unlock(); | ||||
| 			_umount(device, info.major, info.minor); | ||||
| #ifdef SNAPSHOT_REMOVE | ||||
| 		/* Maybe configurable ? */ | ||||
| 		_remove(dm_task_get_uuid(dmt)); | ||||
| #endif | ||||
| 		pthread_kill(pthread_self(), SIGALRM); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (length <= (status->used_sectors - status->metadata_sectors)) { | ||||
| 		/* TODO eventually recognize earlier when room is enough */ | ||||
| 		log_info("Dropping monitoring of fully provisioned snapshot %s.", | ||||
| 			 device); | ||||
| 		pthread_kill(pthread_self(), SIGALRM); | ||||
| 		goto out; | ||||
| 			return; | ||||
| 		} /* else; too bad, but this is best-effort thing... */ | ||||
| 	} | ||||
|  | ||||
| 	/* Snapshot size had changed. Clear the threshold. */ | ||||
| @@ -223,50 +164,69 @@ void process_event(struct dm_task *dmt, | ||||
| 		state->known_size = status->total_sectors; | ||||
| 	} | ||||
|  | ||||
| 	percent = dm_make_percent(status->used_sectors, status->total_sectors); | ||||
| 	/* | ||||
| 	 * If the snapshot has been invalidated or we failed to parse | ||||
| 	 * the status string. Report the full status string to syslog. | ||||
| 	 */ | ||||
| 	if (status->invalid || !status->total_sectors) { | ||||
| 		syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params); | ||||
| 		state->percent_check = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	percent = (int) (100 * status->used_sectors / status->total_sectors); | ||||
| 	if (percent >= state->percent_check) { | ||||
| 		/* Usage has raised more than CHECK_STEP since the last | ||||
| 		   time. Run actions. */ | ||||
| 		state->percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP; | ||||
|  | ||||
| 		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ | ||||
| 			log_warn("WARNING: Snapshot %s is now %.2f%% full.", | ||||
| 				 device, dm_percent_to_float(percent)); | ||||
|  | ||||
| 			syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent); | ||||
| 		/* Try to extend the snapshot, in accord with user-set policies */ | ||||
| 		if (!_extend(state->cmd_lvextend)) | ||||
| 			log_error("Failed to extend snapshot %s.", device); | ||||
| 		if (!_extend(state->cmd_str)) | ||||
| 			syslog(LOG_ERR, "Failed to extend snapshot %s.\n", device); | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	dm_pool_free(state->mem, status); | ||||
| 	if (status) | ||||
| 		dm_pool_free(state->mem, status); | ||||
| 	dmeventd_lvm2_unlock(); | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| 		    const char *uuid __attribute__((unused)), | ||||
| 		    int major __attribute__((unused)), | ||||
| 		    int minor __attribute__((unused)), | ||||
| 		    void **user) | ||||
| 		    void **private) | ||||
| { | ||||
| 	struct dm_pool *statemem = NULL; | ||||
| 	struct dso_state *state; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_init_with_pool("snapshot_state", state)) | ||||
| 		goto_bad; | ||||
| 	if (!dmeventd_lvm2_init()) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvextend, | ||||
| 				   sizeof(state->cmd_lvextend), | ||||
| 				   "lvextend --use-policies", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
| 	if (!(statemem = dm_pool_create("snapshot_state", 512)) || | ||||
| 	    !(state = dm_pool_zalloc(statemem, sizeof(*state)))) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(statemem, state->cmd_str, | ||||
| 				   sizeof(state->cmd_str), | ||||
| 				   "lvextend --use-policies", device)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	state->mem = statemem; | ||||
| 	state->percent_check = CHECK_MINIMUM; | ||||
| 	*user = state; | ||||
| 	*private = state; | ||||
|  | ||||
| 	log_info("Monitoring snapshot %s.", device); | ||||
| 	syslog(LOG_INFO, "Monitoring snapshot %s\n", device); | ||||
|  | ||||
| 	return 1; | ||||
| bad: | ||||
| 	log_error("Failed to monitor snapshot %s.", device); | ||||
| 	if (statemem) | ||||
| 		dm_pool_destroy(statemem); | ||||
| 	dmeventd_lvm2_exit(); | ||||
| out: | ||||
| 	syslog(LOG_ERR, "Failed to monitor snapshot %s.\n", device); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| @@ -275,12 +235,13 @@ int unregister_device(const char *device, | ||||
| 		      const char *uuid __attribute__((unused)), | ||||
| 		      int major __attribute__((unused)), | ||||
| 		      int minor __attribute__((unused)), | ||||
| 		      void **user) | ||||
| 		      void **private) | ||||
| { | ||||
| 	struct dso_state *state = *user; | ||||
| 	struct dso_state *state = *private; | ||||
|  | ||||
| 	dmeventd_lvm2_exit_with_pool(state); | ||||
| 	log_info("No longer monitoring snapshot %s.", device); | ||||
| 	syslog(LOG_INFO, "No longer monitoring snapshot %s\n", device); | ||||
| 	dm_pool_destroy(state->mem); | ||||
| 	dmeventd_lvm2_exit(); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| # | ||||
| # 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 | ||||
| # Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2011-2016 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2011-2013 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -9,39 +9,28 @@ | ||||
|  * | ||||
|  * 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 | ||||
|  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  */ | ||||
|  | ||||
| #include "lib.h"	/* using here lvm log */ | ||||
| #include "dmeventd_lvm.h" | ||||
| #include "lib.h" | ||||
|  | ||||
| #include "libdevmapper-event.h" | ||||
| #include "dmeventd_lvm.h" | ||||
|  | ||||
| #include <sys/wait.h> | ||||
| #include <syslog.h> /* FIXME Replace syslog with multilog */ | ||||
| #include <stdarg.h> | ||||
| #include <pthread.h> | ||||
| /* FIXME Missing openlog? */ | ||||
|  | ||||
| /* TODO - move this mountinfo code into library to be reusable */ | ||||
| #ifdef __linux__ | ||||
| #  include "kdev_t.h" | ||||
| #else | ||||
| #  define MAJOR(x) major((x)) | ||||
| #  define MINOR(x) minor((x)) | ||||
| #endif | ||||
|  | ||||
| /* First warning when thin data or metadata is 80% full. */ | ||||
| #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) | ||||
| /* First warning when thin is 80% full. */ | ||||
| #define WARNING_THRESH 80 | ||||
| /* Run a check every 5%. */ | ||||
| #define CHECK_STEP	(DM_PERCENT_1 *  5) | ||||
| /* Do not bother checking thin data or metadata is less than 50% full. */ | ||||
| #define CHECK_MINIMUM	(DM_PERCENT_1 * 50) | ||||
| #define CHECK_STEP 5 | ||||
| /* Do not bother checking thins less than 50% full. */ | ||||
| #define CHECK_MINIMUM 50 | ||||
|  | ||||
| #define UMOUNT_COMMAND "/bin/umount" | ||||
|  | ||||
| #define MAX_FAILS	(10) | ||||
|  | ||||
| #define THIN_DEBUG 0 | ||||
|  | ||||
| struct dso_state { | ||||
| @@ -50,58 +39,18 @@ struct dso_state { | ||||
| 	int data_percent_check; | ||||
| 	uint64_t known_metadata_size; | ||||
| 	uint64_t known_data_size; | ||||
| 	unsigned fails; | ||||
| 	char cmd_str[1024]; | ||||
| }; | ||||
|  | ||||
| DM_EVENT_LOG_FN("thin") | ||||
|  | ||||
| #define UUID_PREFIX "LVM-" | ||||
|  | ||||
| /* Figure out device UUID has LVM- prefix and is OPEN */ | ||||
| static int _has_unmountable_prefix(int major, int minor) | ||||
| { | ||||
| 	struct dm_task *dmt; | ||||
| 	struct dm_info info; | ||||
| 	const char *uuid; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) | ||||
| 		return_0; | ||||
|  | ||||
| 	if (!dm_task_set_major_minor(dmt, major, minor, 1)) | ||||
| 		goto_out; | ||||
|  | ||||
| 	if (!dm_task_no_flush(dmt)) | ||||
| 		stack; | ||||
|  | ||||
| 	if (!dm_task_run(dmt)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_task_get_info(dmt, &info)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!info.exists || !info.open_count) | ||||
| 		goto out; /* Not open -> not mounted */ | ||||
|  | ||||
| 	if (!(uuid = dm_task_get_uuid(dmt))) | ||||
| 		goto out; | ||||
|  | ||||
| 	/* Check it's public mountable LV | ||||
| 	 * has prefix  LVM-  and UUID size is 68 chars */ | ||||
| 	if (memcmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1) || | ||||
| 	    strlen(uuid) != 68) | ||||
| 		goto out; | ||||
|  | ||||
| #if THIN_DEBUG | ||||
| 	log_debug("Found logical volume %s (%u:%u).", uuid, major, minor); | ||||
| /* TODO - move this mountinfo code into library to be reusable */ | ||||
| #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 | ||||
| 	r = 1; | ||||
| out: | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* 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) | ||||
| @@ -140,15 +89,12 @@ static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_mino | ||||
|  | ||||
| 	*dev_minor = info.minor; | ||||
|  | ||||
| 	if (!_has_unmountable_prefix(major, info.minor)) | ||||
| 		goto out; | ||||
|  | ||||
| #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); | ||||
| 			syslog(LOG_DEBUG, "Found %s (%u:%u) depends on %s", | ||||
| 			       name, major, *dev_minor, dev_name); | ||||
| 	} | ||||
| #endif | ||||
| 	r = 1; | ||||
| @@ -195,6 +141,14 @@ out: | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _extend(struct dso_state *state) | ||||
| { | ||||
| #if THIN_DEBUG | ||||
| 	syslog(LOG_INFO, "dmeventd executes: %s.\n", state->cmd_str); | ||||
| #endif | ||||
| 	return dmeventd_lvm2_run(state->cmd_str); | ||||
| } | ||||
|  | ||||
| static int _run(const char *cmd, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| @@ -214,12 +168,12 @@ static int _run(const char *cmd, ...) | ||||
| 		argv = alloca(sizeof(const char *) * (argc + 1)); | ||||
|  | ||||
| 		argv[0] = cmd; | ||||
| 		va_start(ap, 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); | ||||
| 		syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno)); | ||||
| 		exit(127); | ||||
| 	} | ||||
|  | ||||
| @@ -237,135 +191,103 @@ static int _run(const char *cmd, ...) | ||||
| } | ||||
|  | ||||
| struct mountinfo_s { | ||||
| 	const char *device; | ||||
| 	struct dm_info info; | ||||
| 	dm_bitset_t minors; /* Bitset for active thin pool minors */ | ||||
| 	const char *device; | ||||
| }; | ||||
|  | ||||
| static int _umount_device(char *buffer, unsigned major, unsigned minor, | ||||
| 			  char *target, void *cb_data) | ||||
| { | ||||
| 	struct mountinfo_s *data = cb_data; | ||||
| 	char *words[10]; | ||||
|  | ||||
| 	if ((major == data->info.major) && dm_bit(data->minors, minor)) { | ||||
| 		if (dm_split_words(buffer, DM_ARRAY_SIZE(words), 0, words) < DM_ARRAY_SIZE(words)) | ||||
| 			words[9] = NULL; /* just don't show device name */ | ||||
| 		log_info("Unmounting thin %s (%d:%d) of thin pool %s (%u:%u) from mount point \"%s\".", | ||||
| 			 words[9] ? : "", major, minor, data->device, | ||||
| 			 data->info.major, data->info.minor, | ||||
| 			 target); | ||||
| 		syslog(LOG_INFO, "Unmounting thin volume %s from %s.\n", | ||||
| 		       data->device, target); | ||||
| 		if (!_run(UMOUNT_COMMAND, "-fl", target, NULL)) | ||||
| 			log_error("Failed to lazy umount thin %s (%d:%d) from %s: %s.", | ||||
| 				  words[9], major, minor, target, strerror(errno)); | ||||
| 			syslog(LOG_ERR, "Failed to umount thin %s from %s: %s.\n", | ||||
| 			       data->device, target, strerror(errno)); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Find all thin pool LV users and try to umount them. | ||||
|  * 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) | ||||
| static void _umount(struct dm_task *dmt, const char *device) | ||||
| { | ||||
| 	/* TODO: Convert to use hash to reduce memory usage */ | ||||
| 	static const size_t MINORS = (1U << 20); /* 20 bit */ | ||||
| 	struct mountinfo_s data = { NULL }; | ||||
| 	struct mountinfo_s data = { | ||||
| 		.device = device, | ||||
| 	}; | ||||
|  | ||||
| 	if (!dm_task_get_info(dmt, &data.info)) | ||||
| 		return; | ||||
|  | ||||
| 	data.device = dm_task_get_name(dmt); | ||||
| 	dmeventd_lvm2_unlock(); | ||||
|  | ||||
| 	if (!(data.minors = dm_bitset_create(NULL, MINORS))) { | ||||
| 		log_error("Failed to allocate bitset. Not unmounting %s.", data.device); | ||||
| 		syslog(LOG_ERR, "Failed to allocate bitset. Not unmounting %s.\n", 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); | ||||
| 		syslog(LOG_ERR, "Failed to detect mounted volumes for %s.\n", device); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!dm_mountinfo_read(_umount_device, &data)) { | ||||
| 		log_error("Could not parse mountinfo file."); | ||||
| 		syslog(LOG_ERR, "Could not parse mountinfo file.\n"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	if (data.minors) | ||||
| 		dm_bitset_destroy(data.minors); | ||||
| } | ||||
|  | ||||
| static int _use_policy(struct dm_task *dmt, struct dso_state *state) | ||||
| { | ||||
| #if THIN_DEBUG | ||||
| 	log_info("dmeventd executes: %s.", state->cmd_str); | ||||
| #endif | ||||
| 	if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) { | ||||
| 		log_error("Failed to extend thin pool %s.", | ||||
| 			  dm_task_get_name(dmt)); | ||||
| 		state->fails++; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	state->fails = 0; | ||||
| 	return 1; | ||||
| 	dmeventd_lvm2_lock(); | ||||
| } | ||||
|  | ||||
| void process_event(struct dm_task *dmt, | ||||
| 		   enum dm_event_mask event __attribute__((unused)), | ||||
| 		   void **user) | ||||
| 		   void **private) | ||||
| { | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
| 	int percent; | ||||
| 	struct dso_state *state = *user; | ||||
| 	struct dso_state *state = *private; | ||||
| 	struct dm_status_thin_pool *tps = NULL; | ||||
| 	void *next = NULL; | ||||
| 	uint64_t start, length; | ||||
| 	char *target_type = NULL; | ||||
| 	char *params; | ||||
| 	int needs_policy = 0; | ||||
| 	int needs_umount = 0; | ||||
|  | ||||
| #if THIN_DEBUG | ||||
| 	log_debug("Watch for tp-data:%.2f%%  tp-metadata:%.2f%%.", | ||||
| 		  dm_percent_to_float(state->data_percent_check), | ||||
| 		  dm_percent_to_float(state->metadata_percent_check)); | ||||
| #endif | ||||
|  | ||||
| #if 0 | ||||
| 	/* No longer monitoring, waiting for remove */ | ||||
| 	if (!state->meta_percent_check && !state->data_percent_check) | ||||
| 		return; | ||||
| #endif | ||||
| 	if (event & DM_EVENT_DEVICE_ERROR) { | ||||
| 		/* Error -> no need to check and do instant resize */ | ||||
| 		if (_use_policy(dmt, state)) | ||||
| 			goto out; | ||||
|  | ||||
| 		stack; | ||||
| 	} | ||||
| 	dmeventd_lvm2_lock(); | ||||
|  | ||||
| 	dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); | ||||
|  | ||||
| 	if (!target_type || (strcmp(target_type, "thin-pool") != 0)) { | ||||
| 		log_error("Invalid target type."); | ||||
| 		syslog(LOG_ERR, "Invalid target type.\n"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!dm_get_status_thin_pool(state->mem, params, &tps)) { | ||||
| 		log_error("Failed to parse status."); | ||||
| 		needs_umount = 1; | ||||
| 		syslog(LOG_ERR, "Failed to parse status.\n"); | ||||
| 		_umount(dmt, device); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| #if THIN_DEBUG | ||||
| 	log_debug("Thin pool status " FMTu64 "/" FMTu64 "  " | ||||
| 		  FMTu64 "/" FMTu64 ".", | ||||
| 		  tps->used_metadata_blocks, tps->total_metadata_blocks, | ||||
| 		  tps->used_data_blocks, tps->total_data_blocks); | ||||
| 	syslog(LOG_INFO, "%p: Got status %" PRIu64 " / %" PRIu64 | ||||
| 	       " %" PRIu64  " / %" PRIu64 ".\n", state, | ||||
| 	       tps->used_metadata_blocks, tps->total_metadata_blocks, | ||||
| 	       tps->used_data_blocks, tps->total_data_blocks); | ||||
| #endif | ||||
|  | ||||
| 	/* Thin pool size had changed. Clear the threshold. */ | ||||
| @@ -379,7 +301,7 @@ void process_event(struct dm_task *dmt, | ||||
| 		state->known_data_size = tps->total_data_blocks; | ||||
| 	} | ||||
|  | ||||
| 	percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks); | ||||
| 	percent = 100 * tps->used_metadata_blocks / tps->total_metadata_blocks; | ||||
| 	if (percent >= state->metadata_percent_check) { | ||||
| 		/* | ||||
| 		 * Usage has raised more than CHECK_STEP since the last | ||||
| @@ -389,15 +311,18 @@ void process_event(struct dm_task *dmt, | ||||
|  | ||||
| 		/* 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.", | ||||
| 				 device, dm_percent_to_float(percent)); | ||||
| 		needs_policy = 1; | ||||
|  | ||||
| 		if (percent >= UMOUNT_THRESH) | ||||
| 			needs_umount = 1; | ||||
| 			syslog(LOG_WARNING, "Thin metadata %s is now %i%% full.\n", | ||||
| 			       device, percent); | ||||
| 		 /* Try to extend the metadata, in accord with user-set policies */ | ||||
| 		if (!_extend(state)) { | ||||
| 			syslog(LOG_ERR, "Failed to extend thin metadata %s.\n", | ||||
| 			       device); | ||||
| 			_umount(dmt, device); | ||||
| 		} | ||||
| 		/* FIXME: hmm READ-ONLY switch should happen in error path */ | ||||
| 	} | ||||
|  | ||||
| 	percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks); | ||||
| 	percent = 100 * tps->used_data_blocks / tps->total_data_blocks; | ||||
| 	if (percent >= state->data_percent_check) { | ||||
| 		/* | ||||
| 		 * Usage has raised more than CHECK_STEP since | ||||
| @@ -406,63 +331,56 @@ void process_event(struct dm_task *dmt, | ||||
| 		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; | ||||
|  | ||||
| 		if (percent >= UMOUNT_THRESH) | ||||
| 			needs_umount = 1; | ||||
| 			syslog(LOG_WARNING, "Thin %s is now %i%% full.\n", device, percent); | ||||
| 		/* Try to extend the thin data, in accord with user-set policies */ | ||||
| 		if (!_extend(state)) { | ||||
| 			syslog(LOG_ERR, "Failed to extend thin %s.\n", device); | ||||
| 			state->data_percent_check = 0; | ||||
| 			_umount(dmt, device); | ||||
| 		} | ||||
| 		/* FIXME: hmm READ-ONLY switch should happen in error path */ | ||||
| 	} | ||||
|  | ||||
| 	if (needs_policy && | ||||
| 	    _use_policy(dmt, state)) | ||||
| 		needs_umount = 0; /* No umount when command was successful */ | ||||
| out: | ||||
| 	if (needs_umount) { | ||||
| 		_umount(dmt); | ||||
| 		/* Until something changes, do not retry any more actions */ | ||||
| 		state->data_percent_check = state->metadata_percent_check = (DM_PERCENT_1 * 101); | ||||
| 	} | ||||
|  | ||||
| 	if (tps) | ||||
| 		dm_pool_free(state->mem, tps); | ||||
|  | ||||
| 	if (state->fails >= MAX_FAILS) { | ||||
| 		log_warn("WARNING: Dropping monitoring of %s. " | ||||
| 			 "lvm2 command fails too often (%u times in row).", | ||||
| 			 device, state->fails); | ||||
| 		pthread_kill(pthread_self(), SIGALRM); | ||||
| 	} | ||||
| 	dmeventd_lvm2_unlock(); | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| 		    const char *uuid __attribute__((unused)), | ||||
| 		    int major __attribute__((unused)), | ||||
| 		    int minor __attribute__((unused)), | ||||
| 		    void **user) | ||||
| 		    void **private) | ||||
| { | ||||
| 	struct dm_pool *statemem = NULL; | ||||
| 	struct dso_state *state; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_init_with_pool("thin_pool_state", state)) | ||||
| 		goto_bad; | ||||
| 	if (!dmeventd_lvm2_init()) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_str, | ||||
| 	if (!(statemem = dm_pool_create("thin_pool_state", 2048)) || | ||||
| 	    !(state = dm_pool_zalloc(statemem, sizeof(*state))) || | ||||
| 	    !dmeventd_lvm2_command(statemem, state->cmd_str, | ||||
| 				   sizeof(state->cmd_str), | ||||
| 				   "lvextend --use-policies", | ||||
| 				   device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 		if (statemem) | ||||
| 			dm_pool_destroy(statemem); | ||||
| 		dmeventd_lvm2_exit(); | ||||
| 		goto bad; | ||||
| 	} | ||||
|  | ||||
| 	state->mem = statemem; | ||||
| 	state->metadata_percent_check = CHECK_MINIMUM; | ||||
| 	state->data_percent_check = CHECK_MINIMUM; | ||||
| 	*user = state; | ||||
| 	*private = state; | ||||
|  | ||||
| 	log_info("Monitoring thin pool %s.", device); | ||||
| 	syslog(LOG_INFO, "Monitoring thin %s.\n", device); | ||||
|  | ||||
| 	return 1; | ||||
| bad: | ||||
| 	log_error("Failed to monitor thin pool %s.", device); | ||||
| 	syslog(LOG_ERR, "Failed to monitor thin %s.\n", device); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| @@ -471,12 +389,13 @@ int unregister_device(const char *device, | ||||
| 		      const char *uuid __attribute__((unused)), | ||||
| 		      int major __attribute__((unused)), | ||||
| 		      int minor __attribute__((unused)), | ||||
| 		      void **user) | ||||
| 		      void **private) | ||||
| { | ||||
| 	struct dso_state *state = *user; | ||||
| 	struct dso_state *state = *private; | ||||
|  | ||||
| 	dmeventd_lvm2_exit_with_pool(state); | ||||
| 	log_info("No longer monitoring thin pool %s.", device); | ||||
| 	syslog(LOG_INFO, "No longer monitoring thin %s.\n", device); | ||||
| 	dm_pool_destroy(state->mem); | ||||
| 	dmeventd_lvm2_exit(); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|   | ||||
| @@ -1,67 +0,0 @@ | ||||
| # | ||||
| # Copyright (C) 2016 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| # This file is part of LVM2. | ||||
| # | ||||
| # 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 | ||||
|  | ||||
| srcdir = @srcdir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| lvmdbusdir = $(python3dir)/lvmdbusd | ||||
|  | ||||
| LVMDBUS_SRCDIR_FILES = \ | ||||
| 	automatedproperties.py \ | ||||
| 	background.py \ | ||||
| 	cfg.py \ | ||||
| 	cmdhandler.py \ | ||||
| 	fetch.py \ | ||||
| 	__init__.py \ | ||||
| 	job.py \ | ||||
| 	loader.py \ | ||||
| 	lvmdb.py \ | ||||
| 	main.py \ | ||||
| 	lvm_shell_proxy.py \ | ||||
| 	lv.py \ | ||||
| 	manager.py \ | ||||
| 	objectmanager.py \ | ||||
| 	pv.py \ | ||||
| 	refresh.py \ | ||||
| 	request.py \ | ||||
| 	state.py \ | ||||
| 	udevwatch.py \ | ||||
| 	utils.py \ | ||||
| 	vg.py | ||||
|  | ||||
| LVMDBUS_BUILDDIR_FILES = \ | ||||
| 	path.py | ||||
|  | ||||
| LVMDBUSD = $(srcdir)/lvmdbusd | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| .PHONY: install_lvmdbusd | ||||
|  | ||||
| install_lvmdbusd: | ||||
| 	$(INSTALL_DIR) $(sbindir) | ||||
| 	$(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir) | ||||
| 	$(INSTALL_DIR) $(DESTDIR)$(lvmdbusdir) | ||||
| 	(cd $(srcdir); $(INSTALL_DATA) $(LVMDBUS_SRCDIR_FILES) $(DESTDIR)$(lvmdbusdir)) | ||||
| 	$(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(DESTDIR)$(lvmdbusdir) | ||||
| 	PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbusdir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES) | ||||
| 	$(CHMOD) 755 $(DESTDIR)$(lvmdbusdir)/__pycache__ | ||||
| 	$(CHMOD) 444 $(DESTDIR)$(lvmdbusdir)/__pycache__/*.py[co] | ||||
|  | ||||
| install_lvm2: install_lvmdbusd | ||||
|  | ||||
| install: install_lvm2 | ||||
|  | ||||
| DISTCLEAN_TARGETS+= \ | ||||
| 	$(LVMDBUS_BUILDDIR_FILES) | ||||
| @@ -1,10 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from .main import main | ||||
| @@ -1,176 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| import dbus | ||||
| import dbus.service | ||||
| from . import cfg | ||||
| from .utils import get_properties, add_properties, get_object_property_diff, \ | ||||
| 	log_debug | ||||
| from .state import State | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming,PyUnresolvedReferences | ||||
| class AutomatedProperties(dbus.service.Object): | ||||
| 	""" | ||||
| 	This class implements the needed interfaces for: | ||||
| 	org.freedesktop.DBus.Properties | ||||
|  | ||||
| 	Other classes inherit from it to get the same behavior | ||||
| 	""" | ||||
|  | ||||
| 	def __init__(self, object_path, search_method=None): | ||||
| 		dbus.service.Object.__init__(self, cfg.bus, object_path) | ||||
| 		self._ap_interface = [] | ||||
| 		self._ap_o_path = object_path | ||||
| 		self._ap_search_method = search_method | ||||
| 		self.state = None | ||||
|  | ||||
| 	def dbus_object_path(self): | ||||
| 		return self._ap_o_path | ||||
|  | ||||
| 	def emit_data(self): | ||||
| 		props = {} | ||||
|  | ||||
| 		for i in self.interface(): | ||||
| 			props[i] = self.GetAll(i) | ||||
|  | ||||
| 		return self._ap_o_path, props | ||||
|  | ||||
| 	def set_interface(self, interface): | ||||
| 		""" | ||||
| 		With inheritance we can't easily tell what interfaces a class provides | ||||
| 		so we will have each class that implements an interface tell the | ||||
| 		base AutomatedProperties what it is they do provide.  This is kind of | ||||
| 		clunky and perhaps we can figure out a better way to do this later. | ||||
| 		:param interface:       An interface the object supports | ||||
| 		:return: | ||||
| 		""" | ||||
| 		if interface not in self._ap_interface: | ||||
| 			self._ap_interface.append(interface) | ||||
|  | ||||
| 	# noinspection PyUnusedLocal | ||||
| 	def interface(self, all_interfaces=False): | ||||
| 		if all_interfaces: | ||||
| 			cpy = list(self._ap_interface) | ||||
| 			cpy.extend( | ||||
| 				["org.freedesktop.DBus.Introspectable", | ||||
| 					"org.freedesktop.DBus.Properties"]) | ||||
| 			return cpy | ||||
|  | ||||
| 		return self._ap_interface | ||||
|  | ||||
| 	# Properties | ||||
| 	# noinspection PyUnusedLocal | ||||
| 	@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, | ||||
| 		# only the side effect of no returned value! | ||||
| 		log_debug('Get (%s), type (%s), value(%s)' % | ||||
| 					(property_name, str(type(value)), str(value))) | ||||
| 		return value | ||||
|  | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='s', out_signature='a{sv}') | ||||
| 	def GetAll(self, interface_name): | ||||
| 		if interface_name in self.interface(True): | ||||
| 			# Using introspection, lets build this dynamically | ||||
| 			properties = get_properties(self) | ||||
| 			if interface_name in properties: | ||||
| 				return properties[interface_name][1] | ||||
| 			return {} | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			self._ap_interface, | ||||
| 			'The object %s does not implement the %s interface' | ||||
| 			% (self.__class__, interface_name)) | ||||
|  | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='ssv') | ||||
| 	def Set(self, interface_name, property_name, new_value): | ||||
| 		setattr(self, property_name, new_value) | ||||
| 		self.PropertiesChanged(interface_name, | ||||
| 								{property_name: new_value}, []) | ||||
|  | ||||
| 	# As dbus-python does not support introspection for properties we will | ||||
| 	# get the autogenerated xml and then add our wanted properties to it. | ||||
| 	@dbus.service.method(dbus_interface=dbus.INTROSPECTABLE_IFACE, | ||||
| 							out_signature='s') | ||||
| 	def Introspect(self): | ||||
| 		r = dbus.service.Object.Introspect(self, self._ap_o_path, cfg.bus) | ||||
| 		# Look at the properties in the class | ||||
| 		props = get_properties(self) | ||||
|  | ||||
| 		for int_f, v in props.items(): | ||||
| 			r = add_properties(r, int_f, v[0]) | ||||
|  | ||||
| 		return r | ||||
|  | ||||
| 	@dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							signature='sa{sv}as') | ||||
| 	def PropertiesChanged(self, interface_name, changed_properties, | ||||
| 							invalidated_properties): | ||||
| 		log_debug(('SIGNAL: PropertiesChanged(%s, %s, %s, %s)' % | ||||
| 					(str(self._ap_o_path), str(interface_name), | ||||
| 					str(changed_properties), str(invalidated_properties)))) | ||||
|  | ||||
| 	def refresh(self, search_key=None, object_state=None): | ||||
| 		""" | ||||
| 		Take the values (properties) of an object and update them with what | ||||
| 		lvm currently has.  You can either fetch the new ones or supply the | ||||
| 		new state to be updated with | ||||
| 		:param search_key: The value to use to search for | ||||
| 		:param object_state: Use this as the new object state | ||||
| 		""" | ||||
| 		num_changed = 0 | ||||
|  | ||||
| 		# If we can't do a lookup, bail now, this happens if we blindly walk | ||||
| 		# through all dbus objects as some don't have a search method, like | ||||
| 		# 'Manager' object. | ||||
| 		if not self._ap_search_method: | ||||
| 			return | ||||
|  | ||||
| 		search = self.lvm_id | ||||
| 		if search_key: | ||||
| 			search = search_key | ||||
|  | ||||
| 		# Either we have the new object state or we need to go fetch it | ||||
| 		if object_state: | ||||
| 			new_state = object_state | ||||
| 		else: | ||||
| 			new_state = self._ap_search_method([search])[0] | ||||
| 			assert isinstance(new_state, State) | ||||
|  | ||||
| 		assert new_state | ||||
|  | ||||
| 		# When we refresh an object the object identifiers might have changed | ||||
| 		# because LVM allows the user to change them (name & uuid), thus if | ||||
| 		# they have changed we need to update the object manager so that | ||||
| 		# look-ups will happen correctly | ||||
| 		old_id = self.state.identifiers() | ||||
| 		new_id = new_state.identifiers() | ||||
| 		if old_id[0] != new_id[0] or old_id[1] != 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 | ||||
| 		# 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) | ||||
| 		self.state = new_state | ||||
| 		n_prop = get_properties(self) | ||||
|  | ||||
| 		changed = get_object_property_diff(o_prop, n_prop) | ||||
|  | ||||
| 		if changed: | ||||
| 			for int_f, v in changed.items(): | ||||
| 				self.PropertiesChanged(int_f, v, []) | ||||
| 			num_changed += 1 | ||||
| 		return num_changed | ||||
| @@ -1,214 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| import threading | ||||
| import subprocess | ||||
| from . import cfg | ||||
| import time | ||||
| from .cmdhandler import options_to_cli_args | ||||
| import dbus | ||||
| from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug | ||||
| import traceback | ||||
| import os | ||||
|  | ||||
| _rlock = threading.RLock() | ||||
| _thread_list = list() | ||||
|  | ||||
|  | ||||
| def pv_move_lv_cmd(move_options, lv_full_name, | ||||
| 					pv_source, pv_source_range, pv_dest_range_list): | ||||
| 	cmd = ['pvmove', '-i', '1'] | ||||
| 	cmd.extend(options_to_cli_args(move_options)) | ||||
|  | ||||
| 	if lv_full_name: | ||||
| 		cmd.extend(['-n', lv_full_name]) | ||||
|  | ||||
| 	pv_range_append(cmd, pv_source, *pv_source_range) | ||||
| 	pv_dest_ranges(cmd, pv_dest_range_list) | ||||
|  | ||||
| 	return cmd | ||||
|  | ||||
|  | ||||
| def lv_merge_cmd(merge_options, lv_full_name): | ||||
| 	cmd = ['lvconvert', '--merge', '-i', '1'] | ||||
| 	cmd.extend(options_to_cli_args(merge_options)) | ||||
| 	cmd.append(lv_full_name) | ||||
| 	return cmd | ||||
|  | ||||
|  | ||||
| def _move_merge(interface_name, cmd, job_state): | ||||
| 	add(cmd, job_state) | ||||
|  | ||||
| 	done = job_state.Wait(-1) | ||||
| 	if not done: | ||||
| 		ec, err_msg = job_state.GetError | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			interface_name, | ||||
| 			'Exit code %s, stderr = %s' % (str(ec), err_msg)) | ||||
|  | ||||
| 	cfg.load() | ||||
| 	return '/' | ||||
|  | ||||
|  | ||||
| def move(interface_name, lv_name, pv_src_obj, pv_source_range, | ||||
| 			pv_dests_and_ranges, move_options, job_state): | ||||
| 	""" | ||||
| 	Common code for the pvmove handling. | ||||
| 	:param interface_name:  What dbus interface we are providing for | ||||
| 	:param lv_name:     Optional (None or name of LV to move) | ||||
| 	:param pv_src_obj:  dbus object patch for source PV | ||||
| 	: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 move_options: Hash with optional arguments | ||||
| 	:param job_state: Used to convey information about jobs between processes | ||||
| 	:return: '/' When complete, the empty object path | ||||
| 	""" | ||||
| 	pv_dests = [] | ||||
| 	pv_src = cfg.om.get_object_by_path(pv_src_obj) | ||||
| 	if pv_src: | ||||
|  | ||||
| 		# Check to see if we are handling a move to a specific | ||||
| 		# destination(s) | ||||
| 		if len(pv_dests_and_ranges): | ||||
| 			for pr in pv_dests_and_ranges: | ||||
| 				pv_dbus_obj = cfg.om.get_object_by_path(pr[0]) | ||||
| 				if not pv_dbus_obj: | ||||
| 					raise dbus.exceptions.DBusException( | ||||
| 						interface_name, | ||||
| 						'PV Destination (%s) not found' % pr[0]) | ||||
|  | ||||
| 				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2])) | ||||
|  | ||||
| 		# Generate the command line for this command, but don't | ||||
| 		# execute it. | ||||
| 		cmd = pv_move_lv_cmd(move_options, | ||||
| 								lv_name, | ||||
| 								pv_src.lvm_id, | ||||
| 								pv_source_range, | ||||
| 								pv_dests) | ||||
|  | ||||
| 		return _move_merge(interface_name, cmd, job_state) | ||||
| 	else: | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			interface_name, 'pv_src_obj (%s) not found' % pv_src_obj) | ||||
|  | ||||
|  | ||||
| def merge(interface_name, lv_uuid, lv_name, merge_options, job_state): | ||||
| 	# Make sure we have a dbus object representing it | ||||
| 	dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
| 	if dbo: | ||||
| 		cmd = lv_merge_cmd(merge_options, dbo.lvm_id) | ||||
| 		return _move_merge(interface_name, cmd, job_state) | ||||
| 	else: | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			interface_name, | ||||
| 			'LV with uuid %s and name %s not present!' % (lv_uuid, lv_name)) | ||||
|  | ||||
|  | ||||
| def background_reaper(): | ||||
| 	while cfg.run.value != 0: | ||||
| 		with _rlock: | ||||
| 			num_threads = len(_thread_list) - 1 | ||||
| 			if num_threads >= 0: | ||||
| 				for i in range(num_threads, -1, -1): | ||||
| 					_thread_list[i].join(0) | ||||
| 					if not _thread_list[i].is_alive(): | ||||
| 						log_debug("Removing thread: %s" % _thread_list[i].name) | ||||
| 						_thread_list.pop(i) | ||||
|  | ||||
| 		time.sleep(3) | ||||
|  | ||||
|  | ||||
| def background_execute(command, background_job): | ||||
|  | ||||
| 	# Wrap this whole operation in an exception handler, otherwise if we | ||||
| 	# hit a code bug we will silently exit this thread without anyone being | ||||
| 	# the wiser. | ||||
| 	try: | ||||
| 		# We need to execute these command stand alone by forking & exec'ing | ||||
| 		# the command always! | ||||
| 		command.insert(0, cfg.LVM_CMD) | ||||
| 		process = subprocess.Popen(command, stdout=subprocess.PIPE, | ||||
| 									env=os.environ, | ||||
| 									stderr=subprocess.PIPE, close_fds=True) | ||||
|  | ||||
| 		log_debug("Background process for %s is %d" % | ||||
| 					(str(command), process.pid)) | ||||
|  | ||||
| 		lines_iterator = iter(process.stdout.readline, b"") | ||||
| 		for line in lines_iterator: | ||||
| 			line_str = line.decode("utf-8") | ||||
|  | ||||
| 			# Check to see if the line has the correct number of separators | ||||
| 			try: | ||||
| 				if line_str.count(':') == 2: | ||||
| 					(device, ignore, percentage) = line_str.split(':') | ||||
| 					background_job.Percent = round( | ||||
| 						float(percentage.strip()[:-1]), 1) | ||||
| 			except ValueError: | ||||
| 				log_error("Trying to parse percentage which failed for %s" % | ||||
| 					line_str) | ||||
|  | ||||
| 		out = process.communicate() | ||||
|  | ||||
| 		if process.returncode == 0: | ||||
| 			background_job.Percent = 100 | ||||
| 		else: | ||||
| 			log_error("Failed to execute background job %s, STDERR= %s" | ||||
| 						% (str(command), out[1])) | ||||
|  | ||||
| 		background_job.set_result(process.returncode, out[1]) | ||||
| 		log_debug("Background process %d complete!" % process.pid) | ||||
|  | ||||
| 	except Exception: | ||||
| 		# In the unlikely event that we blow up, we need to unblock caller which | ||||
| 		# is waiting on an answer. | ||||
| 		st = traceback.format_exc() | ||||
| 		error = "Exception in background thread: \n%s" % st | ||||
| 		log_error(error) | ||||
| 		background_job.set_result(1, error) | ||||
|  | ||||
|  | ||||
| def add(command, reporting_job): | ||||
| 	# Create the thread, get it running and then add it to the list | ||||
| 	t = threading.Thread( | ||||
| 		target=background_execute, | ||||
| 		name="thread: " + ' '.join(command), | ||||
| 		args=(command, reporting_job)) | ||||
| 	t.start() | ||||
|  | ||||
| 	with _rlock: | ||||
| 		_thread_list.append(t) | ||||
|  | ||||
|  | ||||
| def wait_thread(job, timeout, cb, cbe): | ||||
| 	# We need to put the wait on it's own thread, so that we don't block the | ||||
| 	# entire dbus queue processing thread | ||||
| 	try: | ||||
| 		cb(job.state.Wait(timeout)) | ||||
| 	except Exception as e: | ||||
| 		cbe("Wait exception: %s" % str(e)) | ||||
| 	return 0 | ||||
|  | ||||
|  | ||||
| def add_wait(job, timeout, cb, cbe): | ||||
|  | ||||
| 	if timeout == 0: | ||||
| 		# Users are basically polling, do not create thread | ||||
| 		cb(job.Complete) | ||||
| 	else: | ||||
| 		t = threading.Thread( | ||||
| 			target=wait_thread, | ||||
| 			name="thread job.Wait: %s" % job.dbus_object_path(), | ||||
| 			args=(job, timeout, cb, cbe) | ||||
| 		) | ||||
|  | ||||
| 		t.start() | ||||
| 		with _rlock: | ||||
| 			_thread_list.append(t) | ||||
| @@ -1,85 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| import os | ||||
| import multiprocessing | ||||
| import queue | ||||
| import itertools | ||||
|  | ||||
| from lvmdbusd import path | ||||
|  | ||||
| LVM_CMD = os.getenv('LVM_BINARY', path.LVM_BINARY) | ||||
|  | ||||
| # This is the global object manager | ||||
| om = None | ||||
|  | ||||
| # This is the global bus connection | ||||
| bus = None | ||||
|  | ||||
| # Command line args | ||||
| args = None | ||||
|  | ||||
| # Set to true if we are depending on external events for updates | ||||
| ee = False | ||||
|  | ||||
| # Shared state variable across all processes | ||||
| run = multiprocessing.Value('i', 1) | ||||
|  | ||||
| # If this is set to true, the current setup support lvm shell and we are | ||||
| # running in that mode of operation | ||||
| SHELL_IN_USE = None | ||||
|  | ||||
| # Lock used by pprint | ||||
| stdout_lock = multiprocessing.Lock() | ||||
|  | ||||
| kick_q = multiprocessing.Queue() | ||||
| worker_q = queue.Queue() | ||||
|  | ||||
| # Main event loop | ||||
| loop = None | ||||
|  | ||||
| BASE_INTERFACE = 'com.redhat.lvmdbus1' | ||||
| PV_INTERFACE = BASE_INTERFACE + '.Pv' | ||||
| VG_INTERFACE = BASE_INTERFACE + '.Vg' | ||||
| LV_INTERFACE = BASE_INTERFACE + '.Lv' | ||||
| LV_COMMON_INTERFACE = BASE_INTERFACE + '.LvCommon' | ||||
| THIN_POOL_INTERFACE = BASE_INTERFACE + '.ThinPool' | ||||
| CACHE_POOL_INTERFACE = BASE_INTERFACE + '.CachePool' | ||||
| LV_CACHED = BASE_INTERFACE + '.CachedLv' | ||||
| SNAPSHOT_INTERFACE = BASE_INTERFACE + '.Snapshot' | ||||
| MANAGER_INTERFACE = BASE_INTERFACE + '.Manager' | ||||
| JOB_INTERFACE = BASE_INTERFACE + '.Job' | ||||
|  | ||||
| BASE_OBJ_PATH = '/' + BASE_INTERFACE.replace('.', '/') | ||||
| PV_OBJ_PATH = BASE_OBJ_PATH + '/Pv' | ||||
| VG_OBJ_PATH = BASE_OBJ_PATH + '/Vg' | ||||
| LV_OBJ_PATH = BASE_OBJ_PATH + '/Lv' | ||||
| THIN_POOL_PATH = BASE_OBJ_PATH + "/ThinPool" | ||||
| CACHE_POOL_PATH = BASE_OBJ_PATH + "/CachePool" | ||||
| HIDDEN_LV_PATH = BASE_OBJ_PATH + "/HiddenLv" | ||||
| MANAGER_OBJ_PATH = BASE_OBJ_PATH + '/Manager' | ||||
| JOB_OBJ_PATH = BASE_OBJ_PATH + '/Job' | ||||
|  | ||||
| # Counters for object path generation | ||||
| pv_id = itertools.count() | ||||
| vg_id = itertools.count() | ||||
| lv_id = itertools.count() | ||||
| thin_id = itertools.count() | ||||
| cache_pool_id = itertools.count() | ||||
| job_id = itertools.count() | ||||
| hidden_lv = itertools.count() | ||||
|  | ||||
| # Used to prevent circular imports... | ||||
| load = None | ||||
|  | ||||
| # Global cached state | ||||
| db = None | ||||
|  | ||||
| # lvm flight recorder | ||||
| blackbox = None | ||||
| @@ -1,753 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from subprocess import Popen, PIPE | ||||
| import time | ||||
| import threading | ||||
| from itertools import chain | ||||
| import collections | ||||
| import traceback | ||||
| import os | ||||
|  | ||||
| try: | ||||
| 	from . import cfg | ||||
| 	from .utils import pv_dest_ranges, log_debug, log_error | ||||
| 	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 | ||||
|  | ||||
| try: | ||||
| 	import simplejson as json | ||||
| except ImportError: | ||||
| 	import json | ||||
|  | ||||
| SEP = '{|}' | ||||
|  | ||||
| total_time = 0.0 | ||||
| total_count = 0 | ||||
|  | ||||
| # We need to prevent different threads from using the same lvm shell | ||||
| # at the same time. | ||||
| cmd_lock = threading.RLock() | ||||
|  | ||||
|  | ||||
| class LvmExecutionMeta(object): | ||||
|  | ||||
| 	def __init__(self, start, ended, cmd, ec, stdout_txt, stderr_txt): | ||||
| 		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): | ||||
| 		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): | ||||
| 		self.queue = collections.deque(maxlen=16) | ||||
|  | ||||
| 	def add(self, lvm_exec_meta): | ||||
| 		self.queue.append(lvm_exec_meta) | ||||
|  | ||||
| 	def dump(self): | ||||
| 		with cmd_lock: | ||||
| 			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): | ||||
| 	log_error('CMD= %s' % ' '.join(cmd)) | ||||
| 	log_error(("EC= %d" % exit_code)) | ||||
| 	log_error(("STDOUT=\n %s\n" % out[0])) | ||||
| 	log_error(("STDERR=\n %s\n" % out[1])) | ||||
|  | ||||
|  | ||||
| def call_lvm(command, debug=False): | ||||
| 	""" | ||||
| 	Call an executable and return a tuple of exitcode, stdout, stderr | ||||
| 	:param command:     Command to execute | ||||
| 	:param debug:       Dump debug to stdout | ||||
| 	""" | ||||
| 	# print 'STACK:' | ||||
| 	# for line in traceback.format_stack(): | ||||
| 	#    print line.strip() | ||||
|  | ||||
| 	# Prepend the full lvm executable so that we can run different versions | ||||
| 	# in different locations on the same box | ||||
| 	command.insert(0, cfg.LVM_CMD) | ||||
|  | ||||
| 	process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True, | ||||
| 					env=os.environ) | ||||
| 	out = process.communicate() | ||||
|  | ||||
| 	stdout_text = bytes(out[0]).decode("utf-8") | ||||
| 	stderr_text = bytes(out[1]).decode("utf-8") | ||||
|  | ||||
| 	if debug or process.returncode != 0: | ||||
| 		_debug_c(command, process.returncode, (stdout_text, stderr_text)) | ||||
|  | ||||
| 	return process.returncode, stdout_text, stderr_text | ||||
|  | ||||
| # The actual method which gets called to invoke the lvm command, can vary | ||||
| # from forking a new process to using lvm shell | ||||
| _t_call = call_lvm | ||||
|  | ||||
|  | ||||
| def _shell_cfg(): | ||||
| 	global _t_call | ||||
| 	try: | ||||
| 		lvm_shell = LVMShellProxy() | ||||
| 		_t_call = lvm_shell.call_lvm | ||||
| 		cfg.SHELL_IN_USE = lvm_shell | ||||
| 		return True | ||||
| 	except Exception: | ||||
| 		_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): | ||||
| 	global _t_call | ||||
| 	with cmd_lock: | ||||
| 		# If the user requested lvm shell and we are currently setup that | ||||
| 		# 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 cfg.args.use_json: | ||||
| 				return _shell_cfg() | ||||
| 			else: | ||||
| 				return False | ||||
| 		return True | ||||
|  | ||||
|  | ||||
| def time_wrapper(command, debug=False): | ||||
| 	global total_time | ||||
| 	global total_count | ||||
|  | ||||
| 	with cmd_lock: | ||||
| 		start = time.time() | ||||
| 		results = _t_call(command, debug) | ||||
| 		ended = time.time() | ||||
| 		total_time += (ended - start) | ||||
| 		total_count += 1 | ||||
| 		cfg.blackbox.add(LvmExecutionMeta(start, ended, command, *results)) | ||||
| 	return results | ||||
|  | ||||
|  | ||||
| call = time_wrapper | ||||
|  | ||||
|  | ||||
| # Default cmd | ||||
| # Place default arguments for every command here. | ||||
| def _dc(cmd, args): | ||||
| 	c = [cmd, '--noheading', '--separator', '%s' % SEP, '--nosuffix', | ||||
| 		'--unbuffered', '--units', 'b'] | ||||
| 	c.extend(args) | ||||
| 	return c | ||||
|  | ||||
|  | ||||
| def parse(out): | ||||
| 	rc = [] | ||||
|  | ||||
| 	for line in out.split('\n'): | ||||
| 		# This line includes separators, so process them | ||||
| 		if SEP in line: | ||||
| 			elem = line.split(SEP) | ||||
| 			cleaned_elem = [] | ||||
| 			for e in elem: | ||||
| 				e = e.strip() | ||||
| 				cleaned_elem.append(e) | ||||
|  | ||||
| 			if len(cleaned_elem) > 1: | ||||
| 				rc.append(cleaned_elem) | ||||
| 		else: | ||||
| 			t = line.strip() | ||||
| 			if len(t) > 0: | ||||
| 				rc.append(t) | ||||
| 	return rc | ||||
|  | ||||
|  | ||||
| def parse_column_names(out, column_names): | ||||
| 	lines = parse(out) | ||||
| 	rc = [] | ||||
|  | ||||
| 	for i in range(0, len(lines)): | ||||
| 		d = dict(list(zip(column_names, lines[i]))) | ||||
| 		rc.append(d) | ||||
|  | ||||
| 	return rc | ||||
|  | ||||
|  | ||||
| def options_to_cli_args(options): | ||||
| 	rc = [] | ||||
| 	for k, v in list(dict(options).items()): | ||||
| 		if k.startswith("-"): | ||||
| 			rc.append(k) | ||||
| 		else: | ||||
| 			rc.append("--%s" % k) | ||||
| 		if v != "": | ||||
| 			rc.append(str(v)) | ||||
| 	return rc | ||||
|  | ||||
|  | ||||
| def pv_remove(device, remove_options): | ||||
| 	cmd = ['pvremove'] | ||||
| 	cmd.extend(options_to_cli_args(remove_options)) | ||||
| 	cmd.append(device) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def _qt(tag_name): | ||||
| 	return '@%s' % tag_name | ||||
|  | ||||
|  | ||||
| def _tag(operation, what, add, rm, tag_options): | ||||
| 	cmd = [operation] | ||||
| 	cmd.extend(options_to_cli_args(tag_options)) | ||||
|  | ||||
| 	if isinstance(what, list): | ||||
| 		cmd.extend(what) | ||||
| 	else: | ||||
| 		cmd.append(what) | ||||
|  | ||||
| 	if add: | ||||
| 		cmd.extend(list(chain.from_iterable( | ||||
| 			('--addtag', _qt(x)) for x in add))) | ||||
| 	if rm: | ||||
| 		cmd.extend(list(chain.from_iterable( | ||||
| 			('--deltag', _qt(x)) for x in rm))) | ||||
|  | ||||
| 	return call(cmd, False) | ||||
|  | ||||
|  | ||||
| def pv_tag(pv_devices, add, rm, tag_options): | ||||
| 	return _tag('pvchange', pv_devices, add, rm, tag_options) | ||||
|  | ||||
|  | ||||
| def vg_tag(vg_name, add, rm, tag_options): | ||||
| 	return _tag('vgchange', vg_name, add, rm, tag_options) | ||||
|  | ||||
|  | ||||
| def lv_tag(lv_name, add, rm, tag_options): | ||||
| 	return _tag('lvchange', lv_name, add, rm, tag_options) | ||||
|  | ||||
|  | ||||
| def vg_rename(vg, new_name, rename_options): | ||||
| 	cmd = ['vgrename'] | ||||
| 	cmd.extend(options_to_cli_args(rename_options)) | ||||
| 	cmd.extend([vg, new_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_remove(vg_name, remove_options): | ||||
| 	cmd = ['vgremove'] | ||||
| 	cmd.extend(options_to_cli_args(remove_options)) | ||||
| 	cmd.extend(['-f', vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_lv_create(vg_name, create_options, name, size_bytes, pv_dests): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	pv_dest_ranges(cmd, pv_dests) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_lv_snapshot(vg_name, snapshot_options, name, size_bytes): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(snapshot_options)) | ||||
| 	cmd.extend(["-s"]) | ||||
|  | ||||
| 	if size_bytes != 0: | ||||
| 		cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
|  | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| 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.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(['--stripes', str(num_stripes)]) | ||||
|  | ||||
| 	if stripe_size_kb != 0: | ||||
| 		cmd.extend(['--stripesize', str(stripe_size_kb)]) | ||||
|  | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def _vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes, | ||||
| 						num_stripes, stripe_size_kb): | ||||
| 	cmd = ['lvcreate'] | ||||
|  | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	cmd.extend(['--type', raid_type]) | ||||
| 	cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
|  | ||||
| 	if num_stripes != 0: | ||||
| 		cmd.extend(['--stripes', str(num_stripes)]) | ||||
|  | ||||
| 	if stripe_size_kb != 0: | ||||
| 		cmd.extend(['--stripesize', str(stripe_size_kb)]) | ||||
|  | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes, | ||||
| 						num_stripes, stripe_size_kb): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	return _vg_lv_create_raid(vg_name, create_options, name, raid_type, | ||||
| 								size_bytes, num_stripes, stripe_size_kb) | ||||
|  | ||||
|  | ||||
| def vg_lv_create_mirror(vg_name, create_options, name, size_bytes, num_copies): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	cmd.extend(['--type', 'mirror']) | ||||
| 	cmd.extend(['--mirrors', str(num_copies)]) | ||||
| 	cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_create_cache_pool(md_full_name, data_full_name, create_options): | ||||
| 	cmd = ['lvconvert'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.extend(['--type', 'cache-pool', '--force', '-y', | ||||
| 				'--poolmetadata', md_full_name, data_full_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_create_thin_pool(md_full_name, data_full_name, create_options): | ||||
| 	cmd = ['lvconvert'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.extend(['--type', 'thin-pool', '--force', '-y', | ||||
| 				'--poolmetadata', md_full_name, data_full_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_remove(lv_path, remove_options): | ||||
| 	cmd = ['lvremove'] | ||||
| 	cmd.extend(options_to_cli_args(remove_options)) | ||||
| 	cmd.extend(['-f', lv_path]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_rename(lv_path, new_name, rename_options): | ||||
| 	cmd = ['lvrename'] | ||||
| 	cmd.extend(options_to_cli_args(rename_options)) | ||||
| 	cmd.extend([lv_path, new_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_resize(lv_full_name, size_change, pv_dests, | ||||
| 				resize_options): | ||||
| 	cmd = ['lvresize', '--force'] | ||||
|  | ||||
| 	cmd.extend(options_to_cli_args(resize_options)) | ||||
|  | ||||
| 	if size_change < 0: | ||||
| 		cmd.append("-L-%dB" % (-size_change)) | ||||
| 	else: | ||||
| 		cmd.append("-L+%dB" % (size_change)) | ||||
|  | ||||
| 	cmd.append(lv_full_name) | ||||
| 	pv_dest_ranges(cmd, pv_dests) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_lv_create(lv_full_name, create_options, name, size_bytes): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.extend(['--virtualsize', str(size_bytes) + 'B', '-T']) | ||||
| 	cmd.extend(['--name', name, lv_full_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_cache_lv(cache_pool_full_name, lv_full_name, cache_options): | ||||
| 	# lvconvert --type cache --cachepool VG/CachePoolLV VG/OriginLV | ||||
| 	cmd = ['lvconvert'] | ||||
| 	cmd.extend(options_to_cli_args(cache_options)) | ||||
| 	cmd.extend(['-y', '--type', 'cache', '--cachepool', | ||||
| 				cache_pool_full_name, lv_full_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def lv_detach_cache(lv_full_name, detach_options, destroy_cache): | ||||
| 	cmd = ['lvconvert'] | ||||
| 	if destroy_cache: | ||||
| 		option = '--uncache' | ||||
| 	else: | ||||
| 		# Currently fairly dangerous | ||||
| 		# see: https://bugzilla.redhat.com/show_bug.cgi?id=1248972 | ||||
| 		option = '--splitcache' | ||||
| 	cmd.extend(options_to_cli_args(detach_options)) | ||||
| 	# needed to prevent interactive questions | ||||
| 	cmd.extend(["--yes", "--force"]) | ||||
| 	cmd.extend([option, lv_full_name]) | ||||
| 	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): | ||||
| 	d = [] | ||||
| 	err = "" | ||||
| 	out = "" | ||||
| 	rc = 0 | ||||
|  | ||||
| 	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', 'pvseg_start', 'pvseg_size', 'segtype', 'pv_missing'] | ||||
|  | ||||
| 	# Lvm has some issues where it returns failure when querying pvs when other | ||||
| 	# operations are in process, see: | ||||
| 	# https://bugzilla.redhat.com/show_bug.cgi?id=1274085 | ||||
| 	for i in range(0, 10): | ||||
| 		cmd = _dc('pvs', ['-o', ','.join(columns)]) | ||||
|  | ||||
| 		if device: | ||||
| 			cmd.extend(device) | ||||
|  | ||||
| 		rc, out, err = call(cmd) | ||||
|  | ||||
| 		if rc == 0: | ||||
| 			d = parse_column_names(out, columns) | ||||
| 			break | ||||
| 		else: | ||||
| 			time.sleep(0.2) | ||||
| 			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 | ||||
|  | ||||
|  | ||||
| def pv_resize(device, size_bytes, create_options): | ||||
| 	cmd = ['pvresize'] | ||||
|  | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	if size_bytes != 0: | ||||
| 		cmd.extend(['--setphysicalvolumesize', str(size_bytes) + 'B']) | ||||
|  | ||||
| 	cmd.extend([device]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def pv_create(create_options, devices): | ||||
| 	cmd = ['pvcreate', '-ff'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.extend(devices) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def pv_allocatable(device, yes, allocation_options): | ||||
| 	yn = 'n' | ||||
|  | ||||
| 	if yes: | ||||
| 		yn = 'y' | ||||
|  | ||||
| 	cmd = ['pvchange'] | ||||
| 	cmd.extend(options_to_cli_args(allocation_options)) | ||||
| 	cmd.extend(['-x', yn, device]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def pv_scan(activate, cache, device_paths, major_minors, scan_options): | ||||
| 	cmd = ['pvscan'] | ||||
| 	cmd.extend(options_to_cli_args(scan_options)) | ||||
|  | ||||
| 	if activate: | ||||
| 		cmd.extend(['--activate', "ay"]) | ||||
|  | ||||
| 	if cache: | ||||
| 		cmd.append('--cache') | ||||
|  | ||||
| 		if len(device_paths) > 0: | ||||
| 			for d in device_paths: | ||||
| 				cmd.append(d) | ||||
|  | ||||
| 		if len(major_minors) > 0: | ||||
| 			for mm in major_minors: | ||||
| 				cmd.append("%s:%s" % (mm)) | ||||
|  | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_create(create_options, pv_devices, name): | ||||
| 	cmd = ['vgcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
| 	cmd.append(name) | ||||
| 	cmd.extend(pv_devices) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_change(change_options, name): | ||||
| 	cmd = ['vgchange'] | ||||
| 	cmd.extend(options_to_cli_args(change_options)) | ||||
| 	cmd.append(name) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_reduce(vg_name, missing, pv_devices, reduce_options): | ||||
| 	cmd = ['vgreduce'] | ||||
| 	cmd.extend(options_to_cli_args(reduce_options)) | ||||
|  | ||||
| 	if len(pv_devices) == 0: | ||||
| 		cmd.append('--all') | ||||
| 	if missing: | ||||
| 		cmd.append('--removemissing') | ||||
|  | ||||
| 	cmd.append(vg_name) | ||||
| 	cmd.extend(pv_devices) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_extend(vg_name, extend_devices, extend_options): | ||||
| 	cmd = ['vgextend'] | ||||
| 	cmd.extend(options_to_cli_args(extend_options)) | ||||
| 	cmd.append(vg_name) | ||||
| 	cmd.extend(extend_devices) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def _vg_value_set(name, arguments, options): | ||||
| 	cmd = ['vgchange'] | ||||
| 	cmd.extend(options_to_cli_args(options)) | ||||
| 	cmd.append(name) | ||||
| 	cmd.extend(arguments) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_allocation_policy(vg_name, policy, policy_options): | ||||
| 	return _vg_value_set(vg_name, ['--alloc', policy], policy_options) | ||||
|  | ||||
|  | ||||
| def vg_max_pv(vg_name, number, max_options): | ||||
| 	return _vg_value_set(vg_name, ['--maxphysicalvolumes', str(number)], | ||||
| 							max_options) | ||||
|  | ||||
|  | ||||
| def vg_max_lv(vg_name, number, max_options): | ||||
| 	return _vg_value_set(vg_name, ['-l', str(number)], max_options) | ||||
|  | ||||
|  | ||||
| def vg_uuid_gen(vg_name, ignore, options): | ||||
| 	assert ignore is None | ||||
| 	return _vg_value_set(vg_name, ['--uuid'], options) | ||||
|  | ||||
|  | ||||
| def activate_deactivate(op, name, activate, control_flags, options): | ||||
| 	cmd = [op] | ||||
| 	cmd.extend(options_to_cli_args(options)) | ||||
|  | ||||
| 	op = '-a' | ||||
|  | ||||
| 	if control_flags: | ||||
| 		# Autoactivation | ||||
| 		if (1 << 0) & control_flags: | ||||
| 			op += 'a' | ||||
| 		# Exclusive locking (Cluster) | ||||
| 		if (1 << 1) & control_flags: | ||||
| 			op += 'e' | ||||
|  | ||||
| 		# Local node activation | ||||
| 		if (1 << 2) & control_flags: | ||||
| 			op += 'l' | ||||
|  | ||||
| 		# Activation modes | ||||
| 		if (1 << 3) & control_flags: | ||||
| 			cmd.extend(['--activationmode', 'complete']) | ||||
| 		elif (1 << 4) & control_flags: | ||||
| 			cmd.extend(['--activationmode', 'partial']) | ||||
|  | ||||
| 		# Ignore activation skip | ||||
| 		if (1 << 5) & control_flags: | ||||
| 			cmd.append('--ignoreactivationskip') | ||||
|  | ||||
| 	if activate: | ||||
| 		op += 'y' | ||||
| 	else: | ||||
| 		op += 'n' | ||||
|  | ||||
| 	cmd.append(op) | ||||
| 	cmd.append(name) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_retrieve(vg_specific): | ||||
| 	if vg_specific: | ||||
| 		assert isinstance(vg_specific, list) | ||||
|  | ||||
| 	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'] | ||||
|  | ||||
| 	cmd = _dc('vgs', ['-o', ','.join(columns)]) | ||||
|  | ||||
| 	if vg_specific: | ||||
| 		cmd.extend(vg_specific) | ||||
|  | ||||
| 	d = [] | ||||
| 	rc, out, err = call(cmd) | ||||
| 	if rc == 0: | ||||
| 		d = parse_column_names(out, columns) | ||||
|  | ||||
| 	return d | ||||
|  | ||||
|  | ||||
| def lv_retrieve_with_segments(): | ||||
| 	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', 'seg_pe_ranges', 'segtype', 'lv_parent', | ||||
| 				'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)]) | ||||
| 	rc, out, err = call(cmd) | ||||
|  | ||||
| 	d = [] | ||||
|  | ||||
| 	if rc == 0: | ||||
| 		d = parse_column_names(out, columns) | ||||
|  | ||||
| 	return d | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
| 	pv_data = pv_retrieve_with_segs() | ||||
|  | ||||
| 	for p in pv_data: | ||||
| 		log_debug(str(p)) | ||||
| @@ -1,30 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from .pv import load_pvs | ||||
| from .vg import load_vgs | ||||
| from .lv import load_lvs | ||||
| from . import cfg | ||||
|  | ||||
|  | ||||
| def load(refresh=True, emit_signal=True, cache_refresh=True, log=True): | ||||
| 	num_total_changes = 0 | ||||
|  | ||||
| 	# Go through and load all the PVs, VGs and LVs | ||||
| 	if cache_refresh: | ||||
| 		cfg.db.refresh(log) | ||||
|  | ||||
| 	num_total_changes += load_pvs(refresh=refresh, emit_signal=emit_signal, | ||||
| 									cache_refresh=False)[1] | ||||
| 	num_total_changes += load_vgs(refresh=refresh, emit_signal=emit_signal, | ||||
| 									cache_refresh=False)[1] | ||||
| 	num_total_changes += load_lvs(refresh=refresh, emit_signal=emit_signal, | ||||
| 									cache_refresh=False)[1] | ||||
|  | ||||
| 	return num_total_changes | ||||
| @@ -1,171 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from .automatedproperties import AutomatedProperties | ||||
| from .utils import job_obj_path_generate | ||||
| from . import cfg | ||||
| from .cfg import JOB_INTERFACE | ||||
| import dbus | ||||
| import threading | ||||
| from . import background | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| class JobState(object): | ||||
| 	def __init__(self, request=None): | ||||
| 		self.rlock = threading.RLock() | ||||
|  | ||||
| 		self._percent = 0 | ||||
| 		self._complete = False | ||||
| 		self._request = request | ||||
| 		self._cond = threading.Condition(self.rlock) | ||||
| 		self._ec = 0 | ||||
| 		self._stderr = '' | ||||
|  | ||||
| 		# This is an lvm command that is just taking too long and doesn't | ||||
| 		# support background operation | ||||
| 		if self._request: | ||||
| 			# Faking the percentage when we don't have one | ||||
| 			self._percent = 1 | ||||
|  | ||||
| 	@property | ||||
| 	def Percent(self): | ||||
| 		with self.rlock: | ||||
| 			return self._percent | ||||
|  | ||||
| 	@Percent.setter | ||||
| 	def Percent(self, value): | ||||
| 		with self.rlock: | ||||
| 			self._percent = value | ||||
|  | ||||
| 	@property | ||||
| 	def Complete(self): | ||||
| 		with self.rlock: | ||||
| 			if self._request: | ||||
| 				self._complete = self._request.is_done() | ||||
|  | ||||
| 			return self._complete | ||||
|  | ||||
| 	@Complete.setter | ||||
| 	def Complete(self, value): | ||||
| 		with self.rlock: | ||||
| 			self._complete = value | ||||
| 			self._percent = 100 | ||||
| 			self._cond.notify_all() | ||||
|  | ||||
| 	@property | ||||
| 	def GetError(self): | ||||
| 		with self.rlock: | ||||
| 			if self.Complete: | ||||
| 				if self._request: | ||||
| 					(rc, error) = self._request.get_errors() | ||||
| 					return (rc, str(error)) | ||||
| 				else: | ||||
| 					return (self._ec, self._stderr) | ||||
| 			else: | ||||
| 				return (-1, 'Job is not complete!') | ||||
|  | ||||
| 	def set_result(self, ec, msg): | ||||
| 		with self.rlock: | ||||
| 			self.Complete = True | ||||
| 			self._ec = ec | ||||
| 			self._stderr = msg | ||||
|  | ||||
| 	def dtor(self): | ||||
| 		with self.rlock: | ||||
| 			self._request = None | ||||
|  | ||||
| 	def Wait(self, timeout): | ||||
| 		try: | ||||
| 			with self._cond: | ||||
| 				# Check to see if we are done, before we wait | ||||
| 				if not self.Complete: | ||||
| 					if timeout != -1: | ||||
| 						self._cond.wait(timeout) | ||||
| 					else: | ||||
| 						self._cond.wait() | ||||
| 				return self.Complete | ||||
| 		except RuntimeError: | ||||
| 			return False | ||||
|  | ||||
| 	@property | ||||
| 	def Result(self): | ||||
| 		with self.rlock: | ||||
| 			if self._request: | ||||
| 				return self._request.result() | ||||
| 			return '/' | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| class Job(AutomatedProperties): | ||||
| 	_Percent_meta = ('d', JOB_INTERFACE) | ||||
| 	_Complete_meta = ('b', JOB_INTERFACE) | ||||
| 	_Result_meta = ('o', JOB_INTERFACE) | ||||
| 	_GetError_meta = ('(is)', JOB_INTERFACE) | ||||
|  | ||||
| 	def __init__(self, request, job_state=None): | ||||
| 		super(Job, self).__init__(job_obj_path_generate()) | ||||
| 		self.set_interface(JOB_INTERFACE) | ||||
|  | ||||
| 		if job_state: | ||||
| 			self.state = job_state | ||||
| 		else: | ||||
| 			self.state = JobState(request) | ||||
|  | ||||
| 	@property | ||||
| 	def Percent(self): | ||||
| 		return dbus.Double(float(self.state.Percent)) | ||||
|  | ||||
| 	@Percent.setter | ||||
| 	def Percent(self, value): | ||||
| 		self.state.Percent = value | ||||
|  | ||||
| 	@property | ||||
| 	def Complete(self): | ||||
| 		return dbus.Boolean(self.state.Complete) | ||||
|  | ||||
| 	@Complete.setter | ||||
| 	def Complete(self, value): | ||||
| 		self.state.Complete = value | ||||
|  | ||||
| 	@property | ||||
| 	def GetError(self): | ||||
| 		return dbus.Struct(self.state.GetError, signature="(is)") | ||||
|  | ||||
| 	def set_result(self, ec, msg): | ||||
| 		self.state.set_result(ec, msg) | ||||
|  | ||||
| 	@dbus.service.method(dbus_interface=JOB_INTERFACE) | ||||
| 	def Remove(self): | ||||
| 		if self.state.Complete: | ||||
| 			cfg.om.remove_object(self, True) | ||||
| 			self.state.dtor() | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				JOB_INTERFACE, 'Job is not complete!') | ||||
|  | ||||
| 	@dbus.service.method(dbus_interface=JOB_INTERFACE, | ||||
| 							in_signature='i', | ||||
| 							out_signature='b', | ||||
| 							async_callbacks=('cb', 'cbe')) | ||||
| 	def Wait(self, timeout, cb, cbe): | ||||
| 		background.add_wait(self, timeout, cb, cbe) | ||||
|  | ||||
| 	@property | ||||
| 	def Result(self): | ||||
| 		return dbus.ObjectPath(self.state.Result) | ||||
|  | ||||
| 	@property | ||||
| 	def lvm_id(self): | ||||
| 		return str(id(self)) | ||||
|  | ||||
| 	@property | ||||
| 	def Uuid(self): | ||||
| 		import uuid | ||||
| 		return uuid.uuid1() | ||||
| @@ -1,85 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from . import cfg | ||||
|  | ||||
|  | ||||
| def _compare_construction(o_state, new_state): | ||||
| 	# We need to check to see if the objects would get constructed | ||||
| 	# the same | ||||
| 	existing_ctor, existing_path = o_state.creation_signature() | ||||
| 	new_ctor, new_path = new_state.creation_signature() | ||||
|  | ||||
| 	# print("%s == %s and %s == %s" % (str(existing_ctor), str(new_ctor), | ||||
| 	#      str(existing_path), str(new_path))) | ||||
|  | ||||
| 	return ((existing_ctor == new_ctor) and (existing_path == new_path)) | ||||
|  | ||||
|  | ||||
| def common(retrieve, o_type, search_keys, | ||||
| 			object_path, refresh, emit_signal, cache_refresh): | ||||
| 	num_changes = 0 | ||||
| 	existing_paths = [] | ||||
| 	rc = [] | ||||
|  | ||||
| 	if search_keys: | ||||
| 		assert isinstance(search_keys, list) | ||||
|  | ||||
| 	if cache_refresh: | ||||
| 		cfg.db.refresh() | ||||
|  | ||||
| 	objects = retrieve(search_keys, cache_refresh=False) | ||||
|  | ||||
| 	# If we are doing a refresh we need to know what we have in memory, what's | ||||
| 	# in lvm and add those that are new and remove those that are gone! | ||||
| 	if refresh: | ||||
| 		existing_paths = cfg.om.object_paths_by_type(o_type) | ||||
|  | ||||
| 	for o in objects: | ||||
| 		# Assume we need to add this one to dbus, unless we are refreshing | ||||
| 		# and it's already present | ||||
| 		return_object = True | ||||
|  | ||||
| 		if refresh: | ||||
| 			# We are refreshing all the PVs from LVM, if this one exists | ||||
| 			# we need to refresh our state. | ||||
| 			dbus_object = cfg.om.get_object_by_uuid_lvm_id(*o.identifiers()) | ||||
|  | ||||
| 			if dbus_object: | ||||
| 				del existing_paths[dbus_object.dbus_object_path()] | ||||
|  | ||||
| 				# If the old object state and new object state wouldn't be | ||||
| 				# created with the same path and same object constructor we | ||||
| 				# need to remove the old object and construct the new one | ||||
| 				# instead! | ||||
| 				if not _compare_construction(dbus_object.state, o): | ||||
| 					# Remove existing and construct new one | ||||
| 					cfg.om.remove_object(dbus_object, emit_signal) | ||||
| 					dbus_object = o.create_dbus_object(None) | ||||
| 					cfg.om.register_object(dbus_object, emit_signal) | ||||
| 					num_changes += 1 | ||||
| 				else: | ||||
| 					num_changes += dbus_object.refresh(object_state=o) | ||||
| 				return_object = False | ||||
|  | ||||
| 		if return_object: | ||||
| 			dbus_object = o.create_dbus_object(object_path) | ||||
| 			cfg.om.register_object(dbus_object, emit_signal) | ||||
| 			rc.append(dbus_object) | ||||
|  | ||||
| 		object_path = None | ||||
|  | ||||
| 	if refresh: | ||||
| 		for k in list(existing_paths.keys()): | ||||
| 			cfg.om.remove_object(cfg.om.get_object_by_path(k), True) | ||||
| 			num_changes += 1 | ||||
|  | ||||
| 	num_changes += len(rc) | ||||
|  | ||||
| 	return rc, num_changes | ||||
| @@ -1,929 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from .automatedproperties import AutomatedProperties | ||||
|  | ||||
| from . import utils | ||||
| from .utils import vg_obj_path_generate | ||||
| import dbus | ||||
| from . import cmdhandler | ||||
| from . import cfg | ||||
| from .cfg import LV_INTERFACE, THIN_POOL_INTERFACE, SNAPSHOT_INTERFACE, \ | ||||
| 	LV_COMMON_INTERFACE, CACHE_POOL_INTERFACE, LV_CACHED | ||||
| from .request import RequestEntry | ||||
| from .utils import n, n32 | ||||
| from .loader import common | ||||
| from .state import State | ||||
| from . import background | ||||
| 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 | ||||
| def lvs_state_retrieve(selection, cache_refresh=True): | ||||
| 	rc = [] | ||||
|  | ||||
| 	if cache_refresh: | ||||
| 		cfg.db.refresh() | ||||
|  | ||||
| 	# When building up the model, it's best to process LVs with the least | ||||
| 	# 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( | ||||
| 			l['lv_uuid'], l['lv_name'], | ||||
| 			l['lv_path'], n(l['lv_size']), | ||||
| 			l['vg_name'], | ||||
| 			l['vg_uuid'], l['pool_lv_uuid'], | ||||
| 			l['pool_lv'], l['origin_uuid'], l['origin'], | ||||
| 			n32(l['data_percent']), l['lv_attr'], | ||||
| 			l['lv_tags'], l['lv_active'], l['data_lv'], | ||||
| 			l['metadata_lv'], l['segtype'], l['lv_role'], | ||||
| 			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 | ||||
|  | ||||
|  | ||||
| def load_lvs(lv_name=None, object_path=None, refresh=False, emit_signal=False, | ||||
| 				cache_refresh=True): | ||||
| 	# noinspection PyUnresolvedReferences | ||||
| 	return common( | ||||
| 		lvs_state_retrieve, | ||||
| 		(LvCommon, Lv, LvThinPool, LvSnapShot), | ||||
| 		lv_name, object_path, refresh, emit_signal, cache_refresh) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming,PyUnresolvedReferences,PyUnusedLocal | ||||
| class LvState(State): | ||||
| 	@staticmethod | ||||
| 	def _pv_devices(uuid): | ||||
| 		rc = [] | ||||
| 		for pv in sorted(cfg.db.lv_contained_pv(uuid)): | ||||
| 			(pv_uuid, pv_name, pv_segs) = pv | ||||
| 			pv_obj = cfg.om.get_object_path_by_uuid_lvm_id(pv_uuid, pv_name) | ||||
|  | ||||
| 			segs_decorate = [] | ||||
| 			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))") | ||||
|  | ||||
| 	def vg_name_lookup(self): | ||||
| 		return cfg.om.get_object_by_path(self.Vg).Name | ||||
|  | ||||
| 	@property | ||||
| 	def lvm_id(self): | ||||
| 		return "%s/%s" % (self.vg_name_lookup(), self.Name) | ||||
|  | ||||
| 	def identifiers(self): | ||||
| 		return (self.Uuid, self.lvm_id) | ||||
|  | ||||
| 	def _get_hidden_lv(self): | ||||
| 		rc = dbus.Array([], "o") | ||||
|  | ||||
| 		vg_name = self.vg_name_lookup() | ||||
|  | ||||
| 		for l in cfg.db.hidden_lvs(self.Uuid): | ||||
| 			full_name = "%s/%s" % (vg_name, l[1]) | ||||
| 			op = cfg.om.get_object_path_by_uuid_lvm_id(l[0], full_name) | ||||
| 			assert op | ||||
| 			rc.append(dbus.ObjectPath(op)) | ||||
| 		return rc | ||||
|  | ||||
| 	def __init__(self, Uuid, Name, Path, SizeBytes, | ||||
| 			vg_name, vg_uuid, pool_lv_uuid, PoolLv, | ||||
| 			origin_uuid, OriginLv, DataPercent, Attr, Tags, active, | ||||
| 			data_lv, metadata_lv, segtypes, role, layout, SnapPercent, | ||||
| 			MetaDataPercent, CopyPercent, SyncPercent, MetaDataSizeBytes, | ||||
| 			move_pv, move_pv_uuid): | ||||
| 		utils.init_class_from_arguments(self) | ||||
|  | ||||
| 		# The segtypes is possibly an array with potentially dupes or a single | ||||
| 		# value | ||||
| 		self._segs = dbus.Array([], signature='s') | ||||
| 		if not isinstance(segtypes, list): | ||||
| 			self._segs.append(dbus.String(segtypes)) | ||||
| 		else: | ||||
| 			self._segs.extend([dbus.String(x) for x in set(segtypes)]) | ||||
|  | ||||
| 		self.Vg = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 			vg_uuid, vg_name, vg_obj_path_generate) | ||||
|  | ||||
| 		self.Devices = LvState._pv_devices(self.Uuid) | ||||
|  | ||||
| 		if PoolLv: | ||||
| 			gen = utils.lv_object_path_method(Name, (Attr, layout, role)) | ||||
|  | ||||
| 			self.PoolLv = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 				pool_lv_uuid, '%s/%s' % (vg_name, PoolLv), gen) | ||||
| 		else: | ||||
| 			self.PoolLv = '/' | ||||
|  | ||||
| 		if OriginLv: | ||||
| 			self.OriginLv = \ | ||||
| 				cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 					origin_uuid, '%s/%s' % (vg_name, OriginLv), | ||||
| 					vg_obj_path_generate) | ||||
| 		else: | ||||
| 			self.OriginLv = '/' | ||||
|  | ||||
| 		self.HiddenLvs = self._get_hidden_lv() | ||||
|  | ||||
| 	@property | ||||
| 	def SegType(self): | ||||
| 		return self._segs | ||||
|  | ||||
| 	def _object_path_create(self): | ||||
| 		return utils.lv_object_path_method( | ||||
| 			self.Name, (self.Attr, self.layout, self.role)) | ||||
|  | ||||
| 	def _object_type_create(self): | ||||
| 		if self.Attr[0] == 't': | ||||
| 			return LvThinPool | ||||
| 		elif self.Attr[0] == 'C': | ||||
| 			if 'pool' in self.layout: | ||||
| 				return LvCachePool | ||||
| 			else: | ||||
| 				return LvCacheLv | ||||
| 		elif self.Name[0] == '[': | ||||
| 			return LvCommon | ||||
| 		elif self.OriginLv != '/': | ||||
| 			return LvSnapShot | ||||
| 		else: | ||||
| 			return Lv | ||||
|  | ||||
| 	def create_dbus_object(self, path): | ||||
| 		if not path: | ||||
| 			path = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 				self.Uuid, self.lvm_id, self._object_path_create()) | ||||
|  | ||||
| 		obj_ctor = self._object_type_create() | ||||
| 		return obj_ctor(path, self) | ||||
|  | ||||
| 	def creation_signature(self): | ||||
| 		klass = self._object_type_create() | ||||
| 		path_method = self._object_path_create() | ||||
| 		return (klass, path_method) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'Uuid', 's') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'Name', 's') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'Path', 's') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'SizeBytes', 't') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'SegType', 'as') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'Vg', 'o') | ||||
| @utils.dbus_property(LV_COMMON_INTERFACE, 'OriginLv', '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, '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): | ||||
| 	_Tags_meta = ("as", LV_COMMON_INTERFACE) | ||||
| 	_Roles_meta = ("as", LV_COMMON_INTERFACE) | ||||
| 	_IsThinVolume_meta = ("b", LV_COMMON_INTERFACE) | ||||
| 	_IsThinPool_meta = ("b", LV_COMMON_INTERFACE) | ||||
| 	_Active_meta = ("b", LV_COMMON_INTERFACE) | ||||
| 	_VolumeType_meta = ("(ss)", LV_COMMON_INTERFACE) | ||||
| 	_Permissions_meta = ("(ss)", LV_COMMON_INTERFACE) | ||||
| 	_AllocationPolicy_meta = ("(ss)", LV_COMMON_INTERFACE) | ||||
| 	_State_meta = ("(ss)", LV_COMMON_INTERFACE) | ||||
| 	_TargetType_meta = ("(ss)", LV_COMMON_INTERFACE) | ||||
| 	_Health_meta = ("(ss)", LV_COMMON_INTERFACE) | ||||
| 	_FixedMinor_meta = ('b', LV_COMMON_INTERFACE) | ||||
| 	_ZeroBlocks_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 | ||||
| 	def __init__(self, object_path, object_state): | ||||
| 		super(LvCommon, self).__init__(object_path, lvs_state_retrieve) | ||||
| 		self.set_interface(LV_COMMON_INTERFACE) | ||||
| 		self.state = object_state | ||||
| 		self._move_pv = self._get_move_pv() | ||||
|  | ||||
| 	@property | ||||
| 	def VolumeType(self): | ||||
| 		type_map = {'C': 'Cache', 'm': 'mirrored', | ||||
| 					'M': 'Mirrored without initial sync', 'o': 'origin', | ||||
| 					'O': 'Origin with merging snapshot', 'r': 'raid', | ||||
| 					'R': 'Raid without initial sync', 's': 'snapshot', | ||||
| 					'S': 'merging Snapshot', 'p': 'pvmove', | ||||
| 					'v': 'virtual', 'i': 'mirror  or  raid  image', | ||||
| 					'I': 'mirror or raid Image out-of-sync', | ||||
| 					'l': 'mirror log device', 'c': 'under conversion', | ||||
| 					'V': 'thin Volume', 't': 'thin pool', 'T': 'Thin pool data', | ||||
| 					'e': 'raid or pool metadata or pool metadata spare', | ||||
| 					'-': 'Unspecified'} | ||||
| 		return dbus.Struct((self.state.Attr[0], type_map[self.state.Attr[0]]), | ||||
| 						signature="as") | ||||
|  | ||||
| 	@property | ||||
| 	def Permissions(self): | ||||
| 		type_map = {'w': 'writable', 'r': 'read-only', | ||||
| 					'R': 'Read-only activation of non-read-only volume', | ||||
| 					'-': 'Unspecified'} | ||||
| 		return dbus.Struct((self.state.Attr[1], type_map[self.state.Attr[1]]), | ||||
| 						signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def AllocationPolicy(self): | ||||
| 		type_map = {'a': 'anywhere', 'A': 'anywhere locked', | ||||
| 					'c': 'contiguous', 'C': 'contiguous locked', | ||||
| 					'i': 'inherited', 'I': 'inherited locked', | ||||
| 					'l': 'cling', 'L': 'cling locked', | ||||
| 					'n': 'normal', 'N': 'normal locked', '-': 'Unspecified'} | ||||
| 		return dbus.Struct((self.state.Attr[2], type_map[self.state.Attr[2]]), | ||||
| 						signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def FixedMinor(self): | ||||
| 		return dbus.Boolean(self.state.Attr[3] == 'm') | ||||
|  | ||||
| 	@property | ||||
| 	def State(self): | ||||
| 		type_map = {'a': 'active', 's': 'suspended', 'I': 'Invalid snapshot', | ||||
| 					'S': 'invalid Suspended snapshot', | ||||
| 					'm': 'snapshot merge failed', | ||||
| 					'M': 'suspended snapshot (M)erge failed', | ||||
| 					'd': 'mapped device present without  tables', | ||||
| 					'i': 'mapped device present with inactive table', | ||||
| 					'X': 'unknown', '-': 'Unspecified'} | ||||
| 		return dbus.Struct((self.state.Attr[4], type_map[self.state.Attr[4]]), | ||||
| 						signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def TargetType(self): | ||||
| 		type_map = {'C': 'Cache', 'm': 'mirror', 'r': 'raid', | ||||
| 					's': 'snapshot', 't': 'thin', 'u': 'unknown', | ||||
| 					'v': 'virtual', '-': 'Unspecified'} | ||||
| 		return dbus.Struct((self.state.Attr[6], type_map[self.state.Attr[6]]), | ||||
| 						signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def ZeroBlocks(self): | ||||
| 		return dbus.Boolean(self.state.Attr[7] == 'z') | ||||
|  | ||||
| 	@property | ||||
| 	def Health(self): | ||||
| 		type_map = {'p': 'partial', 'r': 'refresh', | ||||
| 					'm': 'mismatches', 'w': 'writemostly', | ||||
| 					'X': 'X unknown', '-': 'Unspecified'} | ||||
| 		return dbus.Struct((self.state.Attr[8], type_map[self.state.Attr[8]]), | ||||
| 					signature="(ss)") | ||||
|  | ||||
| 	@property | ||||
| 	def SkipActivation(self): | ||||
| 		return dbus.Boolean(self.state.Attr[9] == 'k') | ||||
|  | ||||
| 	def vg_name_lookup(self): | ||||
| 		return self.state.vg_name_lookup() | ||||
|  | ||||
| 	def lv_full_name(self): | ||||
| 		return "%s/%s" % (self.state.vg_name_lookup(), self.state.Name) | ||||
|  | ||||
| 	@property | ||||
| 	def identifiers(self): | ||||
| 		return self.state.identifiers | ||||
|  | ||||
| 	@property | ||||
| 	def Tags(self): | ||||
| 		return utils.parse_tags(self.state.Tags) | ||||
|  | ||||
| 	@property | ||||
| 	def Roles(self): | ||||
| 		return utils.parse_tags(self.state.role) | ||||
|  | ||||
| 	@property | ||||
| 	def lvm_id(self): | ||||
| 		return self.state.lvm_id | ||||
|  | ||||
| 	@property | ||||
| 	def IsThinVolume(self): | ||||
| 		return dbus.Boolean(self.state.Attr[0] == 'V') | ||||
|  | ||||
| 	@property | ||||
| 	def IsThinPool(self): | ||||
| 		return dbus.Boolean(self.state.Attr[0] == 't') | ||||
|  | ||||
| 	@property | ||||
| 	def Active(self): | ||||
| 		return dbus.Boolean(self.state.active == "active") | ||||
|  | ||||
| 	@property | ||||
| 	def MovePv(self): | ||||
| 		return dbus.ObjectPath(self._move_pv) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| 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 | ||||
| 	def __init__(self, object_path, object_state): | ||||
| 		super(Lv, self).__init__(object_path, object_state) | ||||
| 		self.set_interface(LV_INTERFACE) | ||||
| 		self.state = object_state | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _remove(lv_uuid, lv_name, remove_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		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 | ||||
| 			rc, out, err = cmdhandler.lv_remove(lv_name, remove_options) | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				cfg.om.remove_object(dbo, True) | ||||
| 				cfg.load() | ||||
| 			else: | ||||
| 				# Need to work on error handling, need consistent | ||||
| 				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 '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| 		in_signature='ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Remove(self, tmo, remove_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Lv._remove, | ||||
| 			(self.Uuid, self.lvm_id, remove_options), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _rename(lv_uuid, lv_name, new_name, rename_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			# Rename the logical volume | ||||
| 			rc, out, err = cmdhandler.lv_rename(lv_name, new_name, | ||||
| 												rename_options) | ||||
| 			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 '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| 		in_signature='sia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Rename(self, name, tmo, rename_options, cb, cbe): | ||||
| 		utils.validate_lv_name(LV_INTERFACE, self.vg_name_lookup(), name) | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Lv._rename, | ||||
| 			(self.Uuid, self.lvm_id, name, rename_options), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| 		in_signature='o(tt)a(ott)ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Move(self, pv_src_obj, pv_source_range, | ||||
| 				pv_dests_and_ranges, | ||||
| 				tmo, move_options, cb, cbe): | ||||
|  | ||||
| 		job_state = JobState() | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 				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) | ||||
|  | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _snap_shot(lv_uuid, lv_name, name, optional_size, | ||||
| 			snapshot_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		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 | ||||
| 			# it is a thin lv | ||||
| 			if not dbo.IsThinVolume: | ||||
| 				if optional_size == 0: | ||||
| 					space = dbo.SizeBytes / 80 | ||||
| 					remainder = space % 512 | ||||
| 					optional_size = space + 512 - remainder | ||||
|  | ||||
| 			rc, out, err = cmdhandler.vg_lv_snapshot( | ||||
| 				lv_name, snapshot_options, name, optional_size) | ||||
| 			if rc == 0: | ||||
| 				return_path = '/' | ||||
| 				full_name = "%s/%s" % (dbo.vg_name_lookup(), name) | ||||
| 				lvs = load_lvs([full_name], emit_signal=True)[0] | ||||
| 				for l in lvs: | ||||
| 					return_path = l.dbus_object_path() | ||||
|  | ||||
| 				# Refresh self and all included PVs | ||||
| 				cfg.load(cache_refresh=False) | ||||
| 				return return_path | ||||
| 			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_interface=LV_INTERFACE, | ||||
| 		in_signature='stia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Snapshot(self, name, optional_size, tmo, | ||||
| 			snapshot_options, cb, cbe): | ||||
|  | ||||
| 		utils.validate_lv_name(LV_INTERFACE, self.vg_name_lookup(), name) | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Lv._snap_shot, | ||||
| 			(self.Uuid, self.lvm_id, name, | ||||
| 			optional_size, snapshot_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _resize(lv_uuid, lv_name, new_size_bytes, pv_dests_and_ranges, | ||||
| 				resize_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		pv_dests = [] | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			# If we have PVs, verify them | ||||
| 			if len(pv_dests_and_ranges): | ||||
| 				for pr in pv_dests_and_ranges: | ||||
| 					pv_dbus_obj = cfg.om.get_object_by_path(pr[0]) | ||||
| 					if not pv_dbus_obj: | ||||
| 						raise dbus.exceptions.DBusException( | ||||
| 							LV_INTERFACE, | ||||
| 							'PV Destination (%s) not found' % pr[0]) | ||||
|  | ||||
| 					pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2])) | ||||
|  | ||||
| 			size_change = new_size_bytes - dbo.SizeBytes | ||||
|  | ||||
| 			rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change, | ||||
| 												pv_dests, resize_options) | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				# Refresh what's changed | ||||
| 				cfg.load() | ||||
| 				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_interface=LV_INTERFACE, | ||||
| 		in_signature='ta(ott)ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Resize(self, new_size_bytes, pv_dests_and_ranges, tmo, | ||||
| 			resize_options, cb, cbe): | ||||
| 		""" | ||||
| 		Resize a LV | ||||
| 		:param new_size_bytes: The requested final size in bytes | ||||
| 		:param pv_dests_and_ranges: An array of pv object paths and src & | ||||
| 									dst. segment ranges | ||||
| 		:param tmo: -1 to wait forever, 0 to return job immediately, else | ||||
| 					number of seconds to wait for operation to complete | ||||
| 					before getting a job | ||||
| 		:param resize_options: key/value hash of options | ||||
| 		:param cb:  Used by framework not client facing API | ||||
| 		:param cbe: Used by framework not client facing API | ||||
| 		:return: '/' if complete, else job object path | ||||
| 		""" | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Lv._resize, | ||||
| 			(self.Uuid, self.lvm_id, round_size(new_size_bytes), | ||||
| 			pv_dests_and_ranges, | ||||
| 			resize_options), cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lv_activate_deactivate(uuid, lv_name, activate, control_flags, | ||||
| 								options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.activate_deactivate( | ||||
| 				'lvchange', lv_name, activate, control_flags, options) | ||||
| 			if rc == 0: | ||||
| 				dbo.refresh() | ||||
| 				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_interface=LV_INTERFACE, | ||||
| 		in_signature='tia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Activate(self, control_flags, tmo, activate_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Lv._lv_activate_deactivate, | ||||
| 			(self.state.Uuid, self.state.lvm_id, True, | ||||
| 			control_flags, activate_options), | ||||
| 			cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	# noinspection PyProtectedMember | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| 		in_signature='tia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Deactivate(self, control_flags, tmo, activate_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Lv._lv_activate_deactivate, | ||||
| 			(self.state.Uuid, self.state.lvm_id, False, | ||||
| 			control_flags, activate_options), | ||||
| 			cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _add_rm_tags(uuid, lv_name, tags_add, tags_del, tag_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name) | ||||
|  | ||||
| 		if dbo: | ||||
|  | ||||
| 			rc, out, err = cmdhandler.lv_tag( | ||||
| 				lv_name, tags_add, tags_del, tag_options) | ||||
| 			if rc == 0: | ||||
| 				dbo.refresh() | ||||
| 				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_interface=LV_INTERFACE, | ||||
| 		in_signature='asia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def TagsAdd(self, tags, tmo, tag_options, cb, cbe): | ||||
|  | ||||
| 		for t in tags: | ||||
| 			utils.validate_tag(LV_INTERFACE, t) | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Lv._add_rm_tags, | ||||
| 			(self.state.Uuid, self.state.lvm_id, | ||||
| 			tags, None, tag_options), | ||||
| 			cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| 		in_signature='asia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def TagsDel(self, tags, tmo, tag_options, cb, cbe): | ||||
|  | ||||
| 		for t in tags: | ||||
| 			utils.validate_tag(LV_INTERFACE, t) | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Lv._add_rm_tags, | ||||
| 			(self.state.Uuid, self.state.lvm_id, | ||||
| 			None, tags, tag_options), | ||||
| 			cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| class LvThinPool(Lv): | ||||
| 	_DataLv_meta = ("o", THIN_POOL_INTERFACE) | ||||
| 	_MetaDataLv_meta = ("o", THIN_POOL_INTERFACE) | ||||
|  | ||||
| 	def __init__(self, object_path, object_state): | ||||
| 		super(LvThinPool, self).__init__(object_path, object_state) | ||||
| 		self.set_interface(THIN_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 | ||||
| 	def _lv_create(lv_uuid, lv_name, name, size_bytes, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		lv_created = '/' | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.lv_lv_create( | ||||
| 				lv_name, create_options, name, size_bytes) | ||||
| 			if rc == 0: | ||||
| 				full_name = "%s/%s" % (dbo.vg_name_lookup(), name) | ||||
| 				lvs = load_lvs([full_name], emit_signal=True)[0] | ||||
| 				for l in lvs: | ||||
| 					lv_created = l.dbus_object_path() | ||||
| 			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_interface=THIN_POOL_INTERFACE, | ||||
| 		in_signature='stia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def LvCreate(self, name, size_bytes, tmo, create_options, cb, cbe): | ||||
| 		utils.validate_lv_name(THIN_POOL_INTERFACE, self.vg_name_lookup(), name) | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 			tmo, LvThinPool._lv_create, | ||||
| 			(self.Uuid, self.lvm_id, name, | ||||
| 			round_size(size_bytes), create_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| class LvCachePool(Lv): | ||||
| 	_DataLv_meta = ("o", CACHE_POOL_INTERFACE) | ||||
| 	_MetaDataLv_meta = ("o", CACHE_POOL_INTERFACE) | ||||
|  | ||||
| 	def __init__(self, object_path, object_state): | ||||
| 		super(LvCachePool, self).__init__(object_path, object_state) | ||||
| 		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 | ||||
| 	def _cache_lv(lv_uuid, lv_name, lv_object_path, cache_options): | ||||
|  | ||||
| 		# Make sure we have a dbus object representing cache pool | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		# Make sure we have dbus object representing lv to cache | ||||
| 		lv_to_cache = cfg.om.get_object_by_path(lv_object_path) | ||||
|  | ||||
| 		if dbo and lv_to_cache: | ||||
| 			fcn = lv_to_cache.lv_full_name() | ||||
| 			rc, out, err = cmdhandler.lv_cache_lv( | ||||
| 				dbo.lv_full_name(), fcn, cache_options) | ||||
| 			if rc == 0: | ||||
| 				# When we cache an LV, the cache pool and the lv that is getting | ||||
| 				# cached need to be removed from the object manager and | ||||
| 				# re-created as their interfaces have changed! | ||||
| 				cfg.om.remove_object(dbo, emit_signal=True) | ||||
| 				cfg.om.remove_object(lv_to_cache, emit_signal=True) | ||||
| 				cfg.load() | ||||
|  | ||||
| 				lv_converted = cfg.om.get_object_path_by_lvm_id(fcn) | ||||
|  | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			msg = "" | ||||
| 			if not dbo: | ||||
| 				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 | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=CACHE_POOL_INTERFACE, | ||||
| 		in_signature='oia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def CacheLv(self, lv_object, tmo, cache_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, LvCachePool._cache_lv, | ||||
| 			(self.Uuid, self.lvm_id, lv_object, | ||||
| 			cache_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| class LvCacheLv(Lv): | ||||
| 	_CachePool_meta = ("o", LV_CACHED) | ||||
|  | ||||
| 	def __init__(self, object_path, object_state): | ||||
| 		super(LvCacheLv, self).__init__(object_path, object_state) | ||||
| 		self.set_interface(LV_CACHED) | ||||
|  | ||||
| 	@property | ||||
| 	def CachePool(self): | ||||
| 		return dbus.ObjectPath(self.state.PoolLv) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _detach_lv(lv_uuid, lv_name, detach_options, destroy_cache): | ||||
| 		# Make sure we have a dbus object representing cache pool | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		if dbo: | ||||
|  | ||||
| 			# Get current cache name | ||||
| 			cache_pool = cfg.om.get_object_by_path(dbo.CachePool) | ||||
|  | ||||
| 			rc, out, err = cmdhandler.lv_detach_cache( | ||||
| 				dbo.lv_full_name(), detach_options, destroy_cache) | ||||
| 			if rc == 0: | ||||
| 				# The cache pool gets removed as hidden and put back to | ||||
| 				# visible, so lets delete | ||||
| 				cfg.om.remove_object(cache_pool, emit_signal=True) | ||||
| 				cfg.om.remove_object(dbo, emit_signal=True) | ||||
| 				cfg.load() | ||||
|  | ||||
| 				uncached_lv_path = cfg.om.get_object_path_by_lvm_id(lv_name) | ||||
| 			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 uncached_lv_path | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_CACHED, | ||||
| 		in_signature='bia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def DetachCachePool(self, destroy_cache, tmo, detach_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, LvCacheLv._detach_lv, | ||||
| 			(self.Uuid, self.lvm_id, detach_options, | ||||
| 			destroy_cache), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| class LvSnapShot(Lv): | ||||
| 	def __init__(self, object_path, object_state): | ||||
| 		super(LvSnapShot, self).__init__(object_path, object_state) | ||||
| 		self.set_interface(SNAPSHOT_INTERFACE) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=SNAPSHOT_INTERFACE, | ||||
| 		in_signature='ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Merge(self, tmo, merge_options, cb, cbe): | ||||
| 		job_state = JobState() | ||||
|  | ||||
| 		r = RequestEntry(tmo, background.merge, | ||||
| 							(SNAPSHOT_INTERFACE, self.Uuid, self.lvm_id, | ||||
| 							merge_options, job_state), cb, cbe, False, | ||||
| 							job_state) | ||||
| 		cfg.worker_q.put(r) | ||||
| @@ -1,255 +0,0 @@ | ||||
| #!/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 | ||||
| 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 | ||||
|  | ||||
| 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): | ||||
| 		stdout = "" | ||||
| 		report = "" | ||||
| 		stderr = "" | ||||
|  | ||||
| 		# Try reading from all FDs to prevent one from filling up and causing | ||||
| 		# a hang.  We are also assuming that we won't get the lvm prompt back | ||||
| 		# until we have already received all the output from stderr and the | ||||
| 		# report descriptor too. | ||||
| 		while not stdout.endswith(SHELL_PROMPT): | ||||
| 			try: | ||||
| 				rd_fd = [ | ||||
| 					self.lvm_shell.stdout.fileno(), | ||||
| 					self.report_r, | ||||
| 					self.lvm_shell.stderr.fileno()] | ||||
| 				ready = select.select(rd_fd, [], [], 2) | ||||
|  | ||||
| 				for r in ready[0]: | ||||
| 					if r == self.lvm_shell.stdout.fileno(): | ||||
| 						while True: | ||||
| 							tmp = self.lvm_shell.stdout.read() | ||||
| 							if tmp: | ||||
| 								stdout += tmp.decode("utf-8") | ||||
| 							else: | ||||
| 								break | ||||
|  | ||||
| 					elif r == self.report_r: | ||||
| 						while True: | ||||
| 							tmp = os.read(self.report_r, 16384) | ||||
| 							if tmp: | ||||
| 								report += tmp.decode("utf-8") | ||||
| 								if len(tmp) != 16384: | ||||
| 									break | ||||
| 							else: | ||||
| 								break | ||||
|  | ||||
| 					elif r == self.lvm_shell.stderr.fileno(): | ||||
| 						while True: | ||||
| 							tmp = self.lvm_shell.stderr.read() | ||||
| 							if tmp: | ||||
| 								stderr += tmp.decode("utf-8") | ||||
| 							else: | ||||
| 								break | ||||
|  | ||||
| 				# Check to see if the lvm process died on us | ||||
| 				if self.lvm_shell.poll(): | ||||
| 					raise Exception(self.lvm_shell.returncode, "%s" % stderr) | ||||
|  | ||||
| 			except IOError as ioe: | ||||
| 				log_debug(str(ioe)) | ||||
| 				pass | ||||
|  | ||||
| 		return stdout, report, 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() | ||||
|  | ||||
| 	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 | ||||
|  | ||||
| 		self.report_r = os.open(tmp_file, os.O_NONBLOCK) | ||||
|  | ||||
| 		# 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" | ||||
|  | ||||
| 		flags = fcntl(self.report_r, F_GETFL) | ||||
| 		fcntl(self.report_r, F_SETFL, flags | os.O_NONBLOCK) | ||||
|  | ||||
| 		# 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: | ||||
| 			flags = fcntl(self.lvm_shell.stdout, F_GETFL) | ||||
| 			fcntl(self.lvm_shell.stdout, F_SETFL, flags | os.O_NONBLOCK) | ||||
| 			flags = fcntl(self.lvm_shell.stderr, F_GETFL) | ||||
| 			fcntl(self.lvm_shell.stderr, F_SETFL, flags | os.O_NONBLOCK) | ||||
|  | ||||
| 			# wait for the first prompt | ||||
| 			errors = self._read_until_prompt()[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, stderr = self._read_until_prompt() | ||||
|  | ||||
| 		try: | ||||
| 			log = json.loads(report) | ||||
|  | ||||
| 			if 'log' in log: | ||||
| 				error_msg = "" | ||||
| 				# Walk the entire log array and build an error string | ||||
| 				for log_entry in log['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)' | ||||
| 		except ValueError: | ||||
| 			log_error("Invalid JSON returned from LVM") | ||||
| 			log_error("BEGIN>>\n%s\n<<END" % report) | ||||
| 			return "Invalid JSON returned from LVM when retrieving exit code" | ||||
|  | ||||
| 	def call_lvm(self, argv, debug=False): | ||||
| 		rc = 1 | ||||
| 		error_msg = "" | ||||
| 		json_result = "" | ||||
|  | ||||
| 		if self.lvm_shell.poll(): | ||||
| 			raise Exception( | ||||
| 				self.lvm_shell.returncode, | ||||
| 				"Underlying lvm shell process is not present!") | ||||
|  | ||||
| 		# 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, stderr = self._read_until_prompt() | ||||
|  | ||||
| 		# Parse the report to see what happened | ||||
| 		if report and len(report): | ||||
| 			json_result = json.loads(report) | ||||
| 			if 'log' in json_result: | ||||
| 				if json_result['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, json_result, 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) | ||||
| 	finally: | ||||
| 		print() | ||||
| @@ -1,547 +0,0 @@ | ||||
| #!/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/>. | ||||
|  | ||||
| from collections import OrderedDict | ||||
|  | ||||
| import pprint as prettyprint | ||||
| import os | ||||
| import sys | ||||
|  | ||||
| from lvmdbusd import cmdhandler | ||||
| from lvmdbusd.utils import log_debug, log_error | ||||
|  | ||||
|  | ||||
| class DataStore(object): | ||||
| 	def __init__(self, usejson=True): | ||||
| 		self.pvs = {} | ||||
| 		self.vgs = {} | ||||
| 		self.lvs = {} | ||||
| 		self.pv_lvs = {} | ||||
| 		self.lv_pvs = {} | ||||
| 		self.lvs_hidden = {} | ||||
|  | ||||
| 		self.pv_path_to_uuid = {} | ||||
| 		self.vg_name_to_uuid = {} | ||||
| 		self.lv_full_name_to_uuid = {} | ||||
|  | ||||
| 		self.lvs_in_vgs = {} | ||||
| 		self.pvs_in_vgs = {} | ||||
|  | ||||
| 		# self.refresh() | ||||
| 		self.num_refreshes = 0 | ||||
|  | ||||
| 		if usejson: | ||||
| 			self.json = cmdhandler.supports_json() | ||||
| 		else: | ||||
| 			self.json = usejson | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _insert_record(table, key, record, allowed_multiple): | ||||
| 		if key in table: | ||||
| 			existing = table[key] | ||||
|  | ||||
| 			for rec_k, rec_v in record.items(): | ||||
| 				if rec_k in allowed_multiple: | ||||
| 					# This column name allows us to store multiple value for | ||||
| 					# each type | ||||
| 					if not isinstance(existing[rec_k], list): | ||||
| 						existing_value = existing[rec_k] | ||||
| 						existing[rec_k] = [existing_value, rec_v] | ||||
| 					else: | ||||
| 						existing[rec_k].append(rec_v) | ||||
| 				else: | ||||
| 					# If something is not expected to have changing values | ||||
| 					# lets ensure that | ||||
| 					if existing[rec_k] != rec_v: | ||||
| 						raise RuntimeError( | ||||
| 							"existing[%s]=%s != %s" % | ||||
| 							(rec_k, str(existing[rec_k]), | ||||
| 							str(rec_v))) | ||||
| 		else: | ||||
| 			table[key] = record | ||||
|  | ||||
| 	@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']) | ||||
|  | ||||
| 		for p in c_pvs.values(): | ||||
| 			# Capture which PVs are associated with which VG | ||||
| 			if p['vg_uuid'] not in c_pvs_in_vgs: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']] = [] | ||||
|  | ||||
| 			if p['vg_name']: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']].append( | ||||
| 					(p['pv_name'], p['pv_uuid'])) | ||||
|  | ||||
| 			# Lookup for translating between /dev/<name> and pv uuid | ||||
| 			c_lookup[p['pv_name']] = p['pv_uuid'] | ||||
|  | ||||
| 		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' | ||||
|  | ||||
| 		for p in c_pvs.values(): | ||||
| 			# Capture which PVs are associated with which VG | ||||
| 			if p['vg_uuid'] not in c_pvs_in_vgs: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']] = [] | ||||
|  | ||||
| 			if p['vg_name']: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']].append( | ||||
| 					(p['pv_name'], p['pv_uuid'])) | ||||
|  | ||||
| 			# Lookup for translating between /dev/<name> and pv uuid | ||||
| 			c_lookup[p['pv_name']] = p['pv_uuid'] | ||||
|  | ||||
| 		return c_pvs, c_lookup, c_pvs_in_vgs | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _parse_vgs(_vgs): | ||||
| 		vgs = sorted(_vgs, key=lambda vk: vk['vg_name']) | ||||
|  | ||||
| 		c_vgs = OrderedDict() | ||||
| 		c_lookup = {} | ||||
|  | ||||
| 		for i in vgs: | ||||
| 			c_lookup[i['vg_name']] = i['vg_uuid'] | ||||
| 			DataStore._insert_record(c_vgs, i['vg_uuid'], i, []) | ||||
|  | ||||
| 		return c_vgs, c_lookup | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _parse_vgs_json(_all): | ||||
|  | ||||
| 		tmp_vg = [] | ||||
| 		for r in _all['report']: | ||||
| 			# Get the pv data for this VG. | ||||
| 			if 'vg' in r: | ||||
| 				tmp_vg.extend(r['vg']) | ||||
|  | ||||
| 		# Sort for consistent output, however this is optional | ||||
| 		vgs = sorted(tmp_vg, key=lambda vk: vk['vg_name']) | ||||
|  | ||||
| 		c_vgs = OrderedDict() | ||||
| 		c_lookup = {} | ||||
|  | ||||
| 		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(): | ||||
| 			if i['vg_uuid'] not in c_lvs_in_vgs: | ||||
| 				c_lvs_in_vgs[i['vg_uuid']] = [] | ||||
|  | ||||
| 			c_lvs_in_vgs[ | ||||
| 				i['vg_uuid']].append( | ||||
| 					(i['lv_name'], | ||||
| 					(i['lv_attr'], i['lv_layout'], i['lv_role']), | ||||
| 					i['lv_uuid'])) | ||||
|  | ||||
| 			if i['lv_parent']: | ||||
| 				# Lookup what the parent refers too | ||||
| 				parent_name = i['lv_parent'] | ||||
| 				full_parent_name = "%s/%s" % (i['vg_name'], parent_name) | ||||
| 				if full_parent_name not in c_lv_full_lookup: | ||||
| 					parent_name = '[%s]' % (parent_name) | ||||
| 					full_parent_name = "%s/%s" % (i['vg_name'], parent_name) | ||||
|  | ||||
| 				parent_uuid = c_lv_full_lookup[full_parent_name] | ||||
|  | ||||
| 				if parent_uuid not in c_lvs_hidden: | ||||
| 					c_lvs_hidden[parent_uuid] = [] | ||||
|  | ||||
| 				c_lvs_hidden[parent_uuid].append( | ||||
| 					(i['lv_uuid'], i['lv_name'])) | ||||
|  | ||||
| 		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 | ||||
| 	def _make_list(l): | ||||
| 		if not isinstance(l, list): | ||||
| 			l = [l] | ||||
| 		return l | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _parse_seg_entry(se, segtype): | ||||
| 		if se: | ||||
| 			# print("_parse_seg_entry %s %s" % (str(se), str(segtype))) | ||||
| 			device, segs = se.split(":") | ||||
| 			start, end = segs.split('-') | ||||
| 			return (device, (start, end), segtype) | ||||
| 		else: | ||||
| 			return ("", (), segtype) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _build_segments(l, seg_types): | ||||
| 		rc = [] | ||||
| 		l = DataStore._make_list(l) | ||||
| 		s = DataStore._make_list(seg_types) | ||||
|  | ||||
| 		assert len(l) == len(s) | ||||
| 		ls = list(zip(l, s)) | ||||
|  | ||||
| 		for i in ls: | ||||
| 			if ' ' in i[0]: | ||||
| 				tmp = i[0].split(' ') | ||||
| 				for t in tmp: | ||||
| 					rc.append(DataStore._parse_seg_entry(t, i[1])) | ||||
| 			else: | ||||
| 				rc.append(DataStore._parse_seg_entry(*i)) | ||||
| 		return rc | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _pv_device_lv_entry(table, pv_device, lv_uuid, meta, lv_attr, | ||||
| 							segment_info): | ||||
|  | ||||
| 		if pv_device not in table: | ||||
| 			table[pv_device] = {} | ||||
|  | ||||
| 		if lv_uuid not in table[pv_device]: | ||||
| 			table[pv_device][lv_uuid] = {} | ||||
| 			table[pv_device][lv_uuid]['segs'] = [segment_info] | ||||
| 			table[pv_device][lv_uuid]['name'] = meta | ||||
| 			table[pv_device][lv_uuid]['meta'] = lv_attr | ||||
| 		else: | ||||
| 			table[pv_device][lv_uuid]['segs'].append(segment_info) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _pv_device_lv_format(pv_device_lvs): | ||||
| 		rc = {} | ||||
|  | ||||
| 		for pv_device, pd in pv_device_lvs.items(): | ||||
| 			lvs = [] | ||||
| 			for lv_uuid, ld in sorted(pd.items()): | ||||
| 				lvs.append((lv_uuid, ld['name'], ld['meta'], ld['segs'])) | ||||
|  | ||||
| 			rc[pv_device] = lvs | ||||
| 		return rc | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lvs_device_pv_entry(table, lv_uuid, pv_device, pv_uuid, segment_info): | ||||
| 		if lv_uuid not in table: | ||||
| 			table[lv_uuid] = {} | ||||
|  | ||||
| 		if pv_device not in table[lv_uuid]: | ||||
| 			table[lv_uuid][pv_device] = {} | ||||
| 			table[lv_uuid][pv_device]['segs'] = [segment_info] | ||||
| 			table[lv_uuid][pv_device]['pv_uuid'] = pv_uuid | ||||
| 		else: | ||||
| 			table[lv_uuid][pv_device]['segs'].append(segment_info) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lvs_device_pv_format(lvs_device_pvs): | ||||
| 		rc = {} | ||||
|  | ||||
| 		for lv_uuid, ld in lvs_device_pvs.items(): | ||||
| 			pvs = [] | ||||
| 			for pv_device, pd in sorted(ld.items()): | ||||
| 				pvs.append((pd['pv_uuid'], pv_device, pd['segs'])) | ||||
|  | ||||
| 			rc[lv_uuid] = pvs | ||||
| 		return rc | ||||
|  | ||||
| 	def _parse_pv_in_lvs(self): | ||||
| 		pv_device_lvs = {}  # What LVs are stored on a PV | ||||
| 		lvs_device_pv = {}  # Where LV data is stored | ||||
|  | ||||
| 		for i in self.lvs.values(): | ||||
| 			segs = self._build_segments(i['seg_pe_ranges'], i['segtype']) | ||||
| 			for s in segs: | ||||
| 				# We are referring to physical device | ||||
| 				if '/dev/' in s[0]: | ||||
| 					device, r, seg_type = s | ||||
|  | ||||
| 					DataStore._pv_device_lv_entry( | ||||
| 						pv_device_lvs, device, i['lv_uuid'], i['lv_name'], | ||||
| 						(i['lv_attr'], i['lv_layout'], i['lv_role']), | ||||
| 						(r[0], r[1], seg_type)) | ||||
|  | ||||
| 					# (pv_name, pv_segs, pv_uuid) | ||||
| 					DataStore._lvs_device_pv_entry( | ||||
| 						lvs_device_pv, i['lv_uuid'], device, | ||||
| 						self.pv_path_to_uuid[device], (r[0], r[1], seg_type)) | ||||
| 				else: | ||||
| 					# TODO Handle the case where the segments refer to a LV | ||||
| 					# and not a PV | ||||
| 					pass | ||||
| 					# print("Handle this %s %s %s" % (s[0], s[1], s[2])) | ||||
|  | ||||
| 		# Convert form to needed result for consumption | ||||
| 		pv_device_lvs_result = DataStore._pv_device_lv_format(pv_device_lvs) | ||||
| 		lvs_device_pv_result = DataStore._lvs_device_pv_format(lvs_device_pv) | ||||
|  | ||||
| 		return pv_device_lvs_result, lvs_device_pv_result | ||||
|  | ||||
| 	def refresh(self, log=True): | ||||
| 		""" | ||||
| 		Go out and query lvm for the latest data in as few trips as possible | ||||
| 		:param log  Add debug log entry/exit messages | ||||
| 		:return: None | ||||
| 		""" | ||||
| 		self.num_refreshes += 1 | ||||
| 		if log: | ||||
| 			log_debug("lvmdb - refresh entry") | ||||
|  | ||||
| 		# 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_vgs = cmdhandler.vg_retrieve(None) | ||||
| 			_raw_lvs = cmdhandler.lv_retrieve_with_segments() | ||||
|  | ||||
| 			_pvs, _pvs_lookup, _pvs_in_vgs = self._parse_pvs(_raw_pvs) | ||||
| 			_vgs, _vgs_lookup = self._parse_vgs(_raw_vgs) | ||||
| 			_lvs, _lvs_in_vgs, _lvs_hidden, _lvs_lookup = self._parse_lvs(_raw_lvs) | ||||
|  | ||||
| 		# Set all | ||||
| 		self.pvs = _pvs | ||||
| 		self.pv_path_to_uuid = _pvs_lookup | ||||
| 		self.vg_name_to_uuid = _vgs_lookup | ||||
| 		self.lv_full_name_to_uuid = _lvs_lookup | ||||
|  | ||||
| 		self.vgs = _vgs | ||||
| 		self.lvs = _lvs | ||||
| 		self.lvs_in_vgs = _lvs_in_vgs | ||||
| 		self.pvs_in_vgs = _pvs_in_vgs | ||||
| 		self.lvs_hidden = _lvs_hidden | ||||
|  | ||||
| 		# Create lookup table for which LV and segments are on each PV | ||||
| 		self.pv_lvs, self.lv_pvs = self._parse_pv_in_lvs() | ||||
|  | ||||
| 		if log: | ||||
| 			log_debug("lvmdb - refresh exit") | ||||
|  | ||||
| 	def fetch_pvs(self, pv_name): | ||||
| 		if not pv_name: | ||||
| 			return self.pvs.values() | ||||
| 		else: | ||||
| 			rc = [] | ||||
| 			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]]) | ||||
| 			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): | ||||
| 		if not vg_name: | ||||
| 			return self.vgs.values() | ||||
| 		else: | ||||
| 			rc = [] | ||||
| 			for s in vg_name: | ||||
| 				rc.append(self.vgs[self.vg_name_to_uuid[s]]) | ||||
| 			return rc | ||||
|  | ||||
| 	def fetch_lvs(self, lv_names): | ||||
| 		try: | ||||
| 			if not lv_names: | ||||
| 				return self.lvs.values() | ||||
| 			else: | ||||
| 				rc = [] | ||||
| 				for s in lv_names: | ||||
| 					rc.append(self.lvs[self.lv_full_name_to_uuid[s]]) | ||||
| 				return rc | ||||
| 		except KeyError as ke: | ||||
| 			log_error("Key %s not found!" % (str(lv_names))) | ||||
| 			log_error("lv name to uuid lookup") | ||||
| 			for keys in sorted(self.lv_full_name_to_uuid.keys()): | ||||
| 				log_error("%s" % (keys)) | ||||
| 			log_error("lvs entries by uuid") | ||||
| 			for keys in sorted(self.lvs.keys()): | ||||
| 				log_error("%s" % (keys)) | ||||
| 			raise ke | ||||
|  | ||||
| 	def pv_pe_segments(self, pv_uuid): | ||||
| 		pv = self.pvs[pv_uuid] | ||||
| 		return list(zip(pv['pvseg_start'], pv['pvseg_size'])) | ||||
|  | ||||
| 	def pv_contained_lv(self, pv_device): | ||||
| 		rc = [] | ||||
| 		if pv_device in self.pv_lvs: | ||||
| 			rc = self.pv_lvs[pv_device] | ||||
| 		return rc | ||||
|  | ||||
| 	def lv_contained_pv(self, lv_uuid): | ||||
| 		rc = [] | ||||
| 		if lv_uuid in self.lv_pvs: | ||||
| 			rc = self.lv_pvs[lv_uuid] | ||||
| 		return rc | ||||
|  | ||||
| 	def lvs_in_vg(self, vg_uuid): | ||||
| 		# Return an array of | ||||
| 		# (lv_name, (lv_attr, lv_layout, lv_role), lv_uuid) | ||||
| 		rc = [] | ||||
| 		if vg_uuid in self.lvs_in_vgs: | ||||
| 			rc = self.lvs_in_vgs[vg_uuid] | ||||
| 		return rc | ||||
|  | ||||
| 	def pvs_in_vg(self, vg_uuid): | ||||
| 		# Returns an array of (pv_name, pv_uuid) | ||||
| 		rc = [] | ||||
| 		if vg_uuid in self.pvs_in_vgs: | ||||
| 			rc = self.pvs_in_vgs[vg_uuid] | ||||
| 		return rc | ||||
|  | ||||
| 	def hidden_lvs(self, lv_uuid): | ||||
| 		# For a specified LV, return a list of hidden lv_uuid, lv_name | ||||
| 		# for it | ||||
| 		rc = [] | ||||
| 		if lv_uuid in self.lvs_hidden: | ||||
| 			rc = self.lvs_hidden[lv_uuid] | ||||
| 		return rc | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
| 	pp = prettyprint.PrettyPrinter(indent=4) | ||||
|  | ||||
| 	use_json = False | ||||
|  | ||||
| 	if len(sys.argv) != 1: | ||||
| 		print(len(sys.argv)) | ||||
| 		use_json = True | ||||
|  | ||||
| 	ds = DataStore(use_json) | ||||
| 	ds.refresh() | ||||
|  | ||||
| 	print("PVS") | ||||
| 	for v in ds.pvs.values(): | ||||
| 		pp.pprint(v) | ||||
| 		print('PV missing is %s' % ds.pv_missing(v['pv_uuid'])) | ||||
|  | ||||
| 	print("VGS") | ||||
| 	for v in ds.vgs.values(): | ||||
| 		pp.pprint(v) | ||||
|  | ||||
| 	print("LVS") | ||||
| 	for v in ds.lvs.values(): | ||||
| 		pp.pprint(v) | ||||
|  | ||||
| 	print("LVS in VG") | ||||
| 	for k, v in ds.lvs_in_vgs.items(): | ||||
| 		print("VG uuid = %s" % (k)) | ||||
| 		pp.pprint(v) | ||||
|  | ||||
| 	print("pv_in_lvs") | ||||
| 	for k, v in ds.pv_lvs.items(): | ||||
| 		print("PV %s contains LVS:" % (k)) | ||||
| 		pp.pprint(v) | ||||
|  | ||||
| 	for k, v in ds.lv_pvs.items(): | ||||
| 		print("LV device = %s" % (k)) | ||||
| 		pp.pprint(v) | ||||
| @@ -1,16 +0,0 @@ | ||||
| #!/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/>. | ||||
|  | ||||
| import sys | ||||
| from lvmdbusd import main | ||||
|  | ||||
| if __name__ == '__main__': | ||||
| 	sys.exit(main()) | ||||
| @@ -1,197 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from . import cfg | ||||
| from . import objectmanager | ||||
| from . import utils | ||||
| from .cfg import BASE_INTERFACE, BASE_OBJ_PATH, MANAGER_OBJ_PATH | ||||
| import threading | ||||
| from . import cmdhandler | ||||
| import time | ||||
| import signal | ||||
| import dbus | ||||
| import dbus.mainloop.glib | ||||
| from . import lvmdb | ||||
| # noinspection PyUnresolvedReferences | ||||
| from gi.repository import GLib | ||||
| from .fetch import load | ||||
| from .manager import Manager | ||||
| from .background import background_reaper | ||||
| import traceback | ||||
| import queue | ||||
| from . import udevwatch | ||||
| from .utils import log_debug, log_error | ||||
| import argparse | ||||
| import os | ||||
| import sys | ||||
| from .refresh import handle_external_event, event_complete | ||||
|  | ||||
|  | ||||
| class Lvm(objectmanager.ObjectManager): | ||||
| 	def __init__(self, object_path): | ||||
| 		super(Lvm, self).__init__(object_path, BASE_INTERFACE) | ||||
|  | ||||
|  | ||||
| def _discard_pending_refreshes(): | ||||
| 	# We just handled a refresh, if we have any in the queue they can be | ||||
| 	# removed because by definition they are older than the refresh we just did. | ||||
| 	# As we limit the number of refreshes getting into the queue | ||||
| 	# we should only ever have one to remove. | ||||
| 	requests = [] | ||||
| 	while not cfg.worker_q.empty(): | ||||
| 		try: | ||||
| 			r = cfg.worker_q.get(block=False) | ||||
| 			if r.method != handle_external_event: | ||||
| 				requests.append(r) | ||||
| 			else: | ||||
| 				# Make sure we make this event complete even though it didn't | ||||
| 				# run, otherwise no other events will get processed | ||||
| 				event_complete() | ||||
| 				break | ||||
| 		except queue.Empty: | ||||
| 			break | ||||
|  | ||||
| 	# Any requests we removed, but did not discard need to be re-queued | ||||
| 	for r in requests: | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| def process_request(): | ||||
| 	while cfg.run.value != 0: | ||||
| 		try: | ||||
| 			req = cfg.worker_q.get(True, 5) | ||||
|  | ||||
| 			start = cfg.db.num_refreshes | ||||
|  | ||||
| 			log_debug( | ||||
| 				"Running method: %s with args %s" % | ||||
| 				(str(req.method), str(req.arguments))) | ||||
| 			req.run_cmd() | ||||
|  | ||||
| 			end = cfg.db.num_refreshes | ||||
|  | ||||
| 			num_refreshes = end - start | ||||
|  | ||||
| 			if num_refreshes > 0: | ||||
| 				_discard_pending_refreshes() | ||||
|  | ||||
| 				if num_refreshes > 1: | ||||
| 					log_debug( | ||||
| 						"Inspect method %s for too many refreshes" % | ||||
| 						(str(req.method))) | ||||
| 			log_debug("Complete ") | ||||
| 		except queue.Empty: | ||||
| 			pass | ||||
| 		except Exception: | ||||
| 			st = traceback.format_exc() | ||||
| 			utils.log_error("process_request exception: \n%s" % st) | ||||
|  | ||||
|  | ||||
| def main(): | ||||
| 	start = time.time() | ||||
| 	# Add simple command line handling | ||||
| 	parser = argparse.ArgumentParser() | ||||
| 	parser.add_argument( | ||||
| 		"--udev", action='store_true', | ||||
| 		help="Use udev for updating state", | ||||
| 		default=False, | ||||
| 		dest='use_udev') | ||||
| 	parser.add_argument( | ||||
| 		"--debug", action='store_true', | ||||
| 		help="Dump debug messages", default=False, | ||||
| 		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') | ||||
|  | ||||
| 	use_session = os.getenv('LVMDBUSD_USE_SESSION', False) | ||||
|  | ||||
| 	# Ensure that we get consistent output for parsing stdout/stderr | ||||
| 	os.environ["LC_ALL"] = "C" | ||||
|  | ||||
| 	cfg.args = parser.parse_args() | ||||
|  | ||||
| 	if cfg.args.use_lvm_shell and not cfg.args.use_json: | ||||
| 		log_error("You cannot specify --lvmshell and --nojson") | ||||
| 		sys.exit(1) | ||||
|  | ||||
| 	cmdhandler.set_execution(cfg.args.use_lvm_shell) | ||||
|  | ||||
| 	# List of threads that we start up | ||||
| 	thread_list = [] | ||||
|  | ||||
| 	# 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.threads_init() | ||||
|  | ||||
| 	if use_session: | ||||
| 		cfg.bus = dbus.SessionBus() | ||||
| 	else: | ||||
| 		cfg.bus = dbus.SystemBus() | ||||
| 	# The base name variable needs to exist for things to work. | ||||
| 	# noinspection PyUnusedLocal | ||||
| 	base_name = dbus.service.BusName(BASE_INTERFACE, cfg.bus) | ||||
| 	cfg.om = Lvm(BASE_OBJ_PATH) | ||||
| 	cfg.om.register_object(Manager(MANAGER_OBJ_PATH)) | ||||
|  | ||||
| 	cfg.load = load | ||||
|  | ||||
| 	cfg.db = lvmdb.DataStore(cfg.args.use_json) | ||||
|  | ||||
| 	# Start up thread to monitor pv moves | ||||
| 	thread_list.append( | ||||
| 		threading.Thread(target=background_reaper, name="pv_move_reaper")) | ||||
|  | ||||
| 	# Using a thread to process requests. | ||||
| 	thread_list.append(threading.Thread(target=process_request)) | ||||
|  | ||||
| 	cfg.load(refresh=False, emit_signal=False) | ||||
| 	cfg.loop = GLib.MainLoop() | ||||
|  | ||||
| 	for process in thread_list: | ||||
| 		process.damon = True | ||||
| 		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() | ||||
| 	log_debug( | ||||
| 		'Service ready! total time= %.4f, lvm time= %.4f count= %d' % | ||||
| 		(end - start, cmdhandler.total_time, cmdhandler.total_count), | ||||
| 		'bg_black', 'fg_light_green') | ||||
|  | ||||
| 	try: | ||||
| 		if cfg.run.value != 0: | ||||
| 			cfg.loop.run() | ||||
| 			udevwatch.remove() | ||||
|  | ||||
| 			for process in thread_list: | ||||
| 				process.join() | ||||
| 	except KeyboardInterrupt: | ||||
| 		utils.handler(signal.SIGINT, None) | ||||
| 	return 0 | ||||
| @@ -1,256 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from .automatedproperties import AutomatedProperties | ||||
|  | ||||
| from . import utils | ||||
| from .cfg import MANAGER_INTERFACE | ||||
| import dbus | ||||
| from . import cfg | ||||
| from . import cmdhandler | ||||
| from .fetch import load_pvs, load_vgs | ||||
| from .request import RequestEntry | ||||
| from .refresh import event_add | ||||
| from . import udevwatch | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| class Manager(AutomatedProperties): | ||||
| 	_Version_meta = ("s", MANAGER_INTERFACE) | ||||
|  | ||||
| 	def __init__(self, object_path): | ||||
| 		super(Manager, self).__init__(object_path) | ||||
| 		self.set_interface(MANAGER_INTERFACE) | ||||
|  | ||||
| 	@property | ||||
| 	def Version(self): | ||||
| 		return dbus.String('1.0.0') | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _pv_create(device, create_options): | ||||
|  | ||||
| 		# Check to see if we are already trying to create a PV for an existing | ||||
| 		# PV | ||||
| 		pv = cfg.om.get_object_path_by_uuid_lvm_id(device, device) | ||||
| 		if pv: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				MANAGER_INTERFACE, "PV Already exists!") | ||||
|  | ||||
| 		created_pv = [] | ||||
| 		rc, out, err = cmdhandler.pv_create(create_options, [device]) | ||||
| 		if rc == 0: | ||||
| 			pvs = load_pvs([device], emit_signal=True)[0] | ||||
| 			for p in pvs: | ||||
| 				created_pv = p.dbus_object_path() | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				MANAGER_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 		return created_pv | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='sia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def PvCreate(self, device, tmo, create_options, cb, cbe): | ||||
| 		utils.validate_device_path(MANAGER_INTERFACE, device) | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Manager._pv_create, | ||||
| 			(device, create_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _create_vg(name, pv_object_paths, create_options): | ||||
| 		pv_devices = [] | ||||
|  | ||||
| 		for p in pv_object_paths: | ||||
| 			pv = cfg.om.get_object_by_path(p) | ||||
| 			if pv: | ||||
| 				pv_devices.append(pv.Name) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					MANAGER_INTERFACE, 'object path = %s not found' % p) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.vg_create(create_options, pv_devices, name) | ||||
| 		created_vg = "/" | ||||
|  | ||||
| 		if rc == 0: | ||||
| 			vgs = load_vgs([name], emit_signal=True)[0] | ||||
| 			for v in vgs: | ||||
| 				created_vg = v.dbus_object_path() | ||||
|  | ||||
| 			# Update the PVS | ||||
| 			load_pvs(refresh=True, emit_signal=True, cache_refresh=False) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				MANAGER_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		return created_vg | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='saoia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def VgCreate(self, name, pv_object_paths, tmo, create_options, cb, cbe): | ||||
| 		utils.validate_vg_name(MANAGER_INTERFACE, name) | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Manager._create_vg, | ||||
| 			(name, pv_object_paths, create_options,), | ||||
| 			cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _refresh(): | ||||
| 		utils.log_debug('Manager.Refresh - entry') | ||||
|  | ||||
| 		# This is a diagnostic and should not be run in normal operation, so | ||||
| 		# 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) | ||||
|  | ||||
| 		if rc != 0: | ||||
| 			utils.log_debug('Manager.Refresh - exit %d' % (rc), | ||||
| 							'bg_black', 'fg_light_red') | ||||
| 		else: | ||||
| 			utils.log_debug('Manager.Refresh - exit %d' % (rc)) | ||||
| 		return rc + lc | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		out_signature='t', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Refresh(self, cb, cbe): | ||||
| 		""" | ||||
| 		Take all the objects we know about and go out and grab the latest | ||||
| 		more of a test method at the moment to make sure we are handling object | ||||
| 		paths correctly. | ||||
|  | ||||
| 		:param cb   Callback for result | ||||
| 		:param cbe  Callback for errors | ||||
|  | ||||
| 		Returns the number of changes, object add/remove/properties changed | ||||
| 		""" | ||||
| 		r = RequestEntry(-1, Manager._refresh, (), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='s', | ||||
| 		out_signature='o') | ||||
| 	def LookUpByLvmId(self, key): | ||||
| 		""" | ||||
| 		Given a lvm id in one of the forms: | ||||
|  | ||||
| 		/dev/sda | ||||
| 		some_vg | ||||
| 		some_vg/some_lv | ||||
| 		Oe1rPX-Pf0W-15E5-n41N-ZmtF-jXS0-Osg8fn | ||||
|  | ||||
| 		return the object path in O(1) time. | ||||
|  | ||||
| 		:param key: The lookup value | ||||
| 		:return: Return the object path.  If object not found you will get '/' | ||||
| 		""" | ||||
| 		p = cfg.om.get_object_path_by_uuid_lvm_id(key, key) | ||||
| 		if p: | ||||
| 			return p | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='b', out_signature='b') | ||||
| 	def UseLvmShell(self, yes_no): | ||||
| 		""" | ||||
| 		Allow the client to enable/disable lvm shell, used for testing | ||||
| 		:param yes_no: | ||||
| 		:return: Nothing | ||||
| 		""" | ||||
| 		return dbus.Boolean(cmdhandler.set_execution(yes_no)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='s', out_signature='i') | ||||
| 	def ExternalEvent(self, 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.ee = True | ||||
| 		event_add((command,)) | ||||
| 		return dbus.Int32(0) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _pv_scan(activate, cache, device_path, major_minor, scan_options): | ||||
|  | ||||
| 		rc, out, err = cmdhandler.pv_scan( | ||||
| 			activate, cache, device_path, | ||||
| 			major_minor, scan_options) | ||||
|  | ||||
| 		if rc == 0: | ||||
| 			# This could potentially change the state quite a bit, so lets | ||||
| 			# update everything to be safe | ||||
| 			cfg.load() | ||||
| 			return '/' | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				MANAGER_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='bbasa(ii)ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def PvScan(self, activate, cache, device_paths, major_minors, | ||||
| 			tmo, scan_options, cb, cbe): | ||||
| 		""" | ||||
| 		Scan all supported LVM block devices in the system for physical volumes | ||||
| 		NOTE: major_minors & device_paths only usable when cache == True | ||||
| 		:param activate: If True, activate any newly found LVs | ||||
| 		:param cache:    If True, update lvmetad | ||||
| 		:param device_paths: Array of device paths or empty | ||||
| 		:param major_minors: Array of structures (major,minor) | ||||
| 		:param tmo: Timeout for operation | ||||
| 		:param scan_options:  Additional options to pvscan | ||||
| 		:param cb: Not visible in API (used for async. callback) | ||||
| 		:param cbe: Not visible in API (used for async. error callback) | ||||
| 		:return: '/' if operation done, else job path | ||||
| 		""" | ||||
| 		for d in device_paths: | ||||
| 			utils.validate_device_path(MANAGER_INTERFACE, d) | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Manager._pv_scan, | ||||
| 			(activate, cache, device_paths, major_minors, | ||||
| 			scan_options), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@property | ||||
| 	def lvm_id(self): | ||||
| 		""" | ||||
| 		Intended to be overridden by classes that inherit | ||||
| 		""" | ||||
| 		return str(id(self)) | ||||
|  | ||||
| 	@property | ||||
| 	def Uuid(self): | ||||
| 		""" | ||||
| 		Intended to be overridden by classes that inherit | ||||
| 		""" | ||||
| 		import uuid | ||||
| 		return uuid.uuid1() | ||||
| @@ -1,377 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| import sys | ||||
| import threading | ||||
| import traceback | ||||
| import dbus | ||||
| import os | ||||
| import copy | ||||
| from . import cfg | ||||
| from .utils import log_debug, pv_obj_path_generate, log_error | ||||
| from .automatedproperties import AutomatedProperties | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| class ObjectManager(AutomatedProperties): | ||||
| 	""" | ||||
| 	Implements the org.freedesktop.DBus.ObjectManager interface | ||||
| 	""" | ||||
|  | ||||
| 	def __init__(self, object_path, interface): | ||||
| 		super(ObjectManager, self).__init__(object_path, interface) | ||||
| 		self.set_interface(interface) | ||||
| 		self._ap_o_path = object_path | ||||
| 		self._objects = {} | ||||
| 		self._id_to_object_path = {} | ||||
| 		self.rlock = threading.RLock() | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface="org.freedesktop.DBus.ObjectManager", | ||||
| 		out_signature='a{oa{sa{sv}}}') | ||||
| 	def GetManagedObjects(self): | ||||
| 		with self.rlock: | ||||
| 			rc = {} | ||||
| 			try: | ||||
| 				for k, v in list(self._objects.items()): | ||||
| 					path, props = v[0].emit_data() | ||||
| 					rc[path] = props | ||||
| 			except Exception: | ||||
| 				traceback.print_exc(file=sys.stdout) | ||||
| 				sys.exit(1) | ||||
| 			return rc | ||||
|  | ||||
| 	def locked(self): | ||||
| 		""" | ||||
| 		If some external code need to run across a number of different | ||||
| 		calls into ObjectManager while blocking others they can use this method | ||||
| 		to lock others out. | ||||
| 		:return: | ||||
| 		""" | ||||
| 		return ObjectManagerLock(self.rlock) | ||||
|  | ||||
| 	@dbus.service.signal( | ||||
| 		dbus_interface="org.freedesktop.DBus.ObjectManager", | ||||
| 		signature='oa{sa{sv}}') | ||||
| 	def InterfacesAdded(self, object_path, int_name_prop_dict): | ||||
| 		log_debug( | ||||
| 			('SIGNAL: InterfacesAdded(%s, %s)' % | ||||
| 			(str(object_path), str(int_name_prop_dict)))) | ||||
|  | ||||
| 	@dbus.service.signal( | ||||
| 		dbus_interface="org.freedesktop.DBus.ObjectManager", | ||||
| 		signature='oas') | ||||
| 	def InterfacesRemoved(self, object_path, interface_list): | ||||
| 		log_debug(('SIGNAL: InterfacesRemoved(%s, %s)' % | ||||
| 			(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): | ||||
| 		""" | ||||
| 		Store information about what we added to the caches so that we | ||||
| 		can remove it cleanly | ||||
| 		:param obj:     The dbus object we are storing | ||||
| 		:param lvm_id:  The lvm id for the asset | ||||
| 		:param uuid:    The uuid for the asset | ||||
| 		:return: | ||||
| 		""" | ||||
| 		# Note: Only called internally, lock implied | ||||
|  | ||||
| 		# We could have a temp entry from the forward creation of a path | ||||
| 		self._lookup_remove(path) | ||||
|  | ||||
| 		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 | ||||
|  | ||||
| 		if uuid: | ||||
| 			self._id_to_object_path[uuid] = path | ||||
|  | ||||
| 	def _lookup_remove(self, obj_path): | ||||
| 		# Note: Only called internally, lock implied | ||||
| 		if obj_path in self._objects: | ||||
| 			(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] | ||||
|  | ||||
| 			if uuid in self._id_to_object_path: | ||||
| 				del self._id_to_object_path[uuid] | ||||
|  | ||||
| 			del self._objects[obj_path] | ||||
|  | ||||
| 	def lookup_update(self, dbus_obj, new_uuid, new_lvm_id): | ||||
| 		with self.rlock: | ||||
| 			obj_path = dbus_obj.dbus_object_path() | ||||
| 			self._lookup_remove(obj_path) | ||||
| 			self._lookup_add( | ||||
| 				dbus_obj, obj_path, | ||||
| 				new_lvm_id, new_uuid) | ||||
|  | ||||
| 	def object_paths_by_type(self, o_type): | ||||
| 		with self.rlock: | ||||
| 			rc = {} | ||||
|  | ||||
| 			for k, v in list(self._objects.items()): | ||||
| 				if isinstance(v[0], o_type): | ||||
| 					rc[k] = True | ||||
| 			return rc | ||||
|  | ||||
| 	def register_object(self, dbus_object, emit_signal=False): | ||||
| 		""" | ||||
| 		Given a dbus object add it to the collection | ||||
| 		:param dbus_object: Dbus object to register | ||||
| 		:param emit_signal: If true emit a signal for interfaces added | ||||
| 		""" | ||||
| 		with self.rlock: | ||||
| 			path, props = dbus_object.emit_data() | ||||
|  | ||||
| 			# print('Registering object path %s for %s' % | ||||
| 			# (path, dbus_object.lvm_id)) | ||||
|  | ||||
| 			# We want fast access to the object by a number of different ways | ||||
| 			# so we use multiple hashs with different keys | ||||
| 			self._lookup_add(dbus_object, path, dbus_object.lvm_id, | ||||
| 				dbus_object.Uuid) | ||||
|  | ||||
| 			if emit_signal: | ||||
| 				self.InterfacesAdded(path, props) | ||||
|  | ||||
| 	def remove_object(self, dbus_object, emit_signal=False): | ||||
| 		""" | ||||
| 		Given a dbus object, remove it from the collection and remove it | ||||
| 		from the dbus framework as well | ||||
| 		:param dbus_object:  Dbus object to remove | ||||
| 		:param emit_signal:  If true emit the interfaces removed signal | ||||
| 		""" | ||||
| 		with self.rlock: | ||||
| 			# Store off the object path and the interface first | ||||
| 			path = dbus_object.dbus_object_path() | ||||
| 			interfaces = dbus_object.interface() | ||||
|  | ||||
| 			# print 'UN-Registering object path %s for %s' % \ | ||||
| 			#      (path, dbus_object.lvm_id) | ||||
|  | ||||
| 			self._lookup_remove(path) | ||||
|  | ||||
| 			# Remove from dbus library | ||||
| 			dbus_object.remove_from_connection(cfg.bus, path) | ||||
|  | ||||
| 			# Optionally emit a signal | ||||
| 			if emit_signal: | ||||
| 				self.InterfacesRemoved(path, interfaces) | ||||
|  | ||||
| 	def get_object_by_path(self, path): | ||||
| 		""" | ||||
| 		Given a dbus path return the object registered for it | ||||
| 		:param path: The dbus path | ||||
| 		:return: The object | ||||
| 		""" | ||||
| 		with self.rlock: | ||||
| 			if path in self._objects: | ||||
| 				return self._objects[path][0] | ||||
| 			return None | ||||
|  | ||||
| 	def get_object_by_uuid_lvm_id(self, uuid, lvm_id): | ||||
| 		with self.rlock: | ||||
| 			return self.get_object_by_path( | ||||
| 				self.get_object_path_by_uuid_lvm_id(uuid, lvm_id)) | ||||
|  | ||||
| 	def get_object_by_lvm_id(self, lvm_id): | ||||
| 		""" | ||||
| 		Given an lvm identifier, return the object registered for it | ||||
| 		:param lvm_id: The lvm identifier | ||||
| 		""" | ||||
| 		with self.rlock: | ||||
| 			if lvm_id in self._id_to_object_path: | ||||
| 				return self.get_object_by_path(self._id_to_object_path[lvm_id]) | ||||
| 			return None | ||||
|  | ||||
| 	def get_object_path_by_lvm_id(self, lvm_id): | ||||
| 		""" | ||||
| 		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: | ||||
| 			if lvm_id in self._id_to_object_path: | ||||
| 				return self._id_to_object_path[lvm_id] | ||||
| 			return '/' | ||||
|  | ||||
| 	def _uuid_verify(self, path, uuid, lvm_id): | ||||
| 		""" | ||||
| 		Ensure uuid is present for a successful lvm_id lookup | ||||
| 		NOTE: Internal call, assumes under object manager lock | ||||
| 		:param path: 		Path to object we looked up | ||||
| 		:param uuid: 		lvm uuid to verify | ||||
| 		:param lvm_id:		lvm_id used to find object | ||||
| 		:return: None | ||||
| 		""" | ||||
| 		# 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 | ||||
| 		# non-changeable name in lvm | ||||
| 		if lvm_id != uuid: | ||||
| 			if uuid and uuid not in self._id_to_object_path: | ||||
| 				obj = self.get_object_by_path(path) | ||||
| 				self._lookup_add(obj, path, lvm_id, uuid) | ||||
|  | ||||
| 	def _lvm_id_verify(self, path, uuid, lvm_id): | ||||
| 		""" | ||||
| 		Ensure lvm_id is present for a successful uuid lookup | ||||
| 		NOTE: Internal call, assumes under object manager lock | ||||
| 		:param path: 		Path to object we looked up | ||||
| 		:param uuid: 		uuid used to find object | ||||
| 		:param lvm_id:		lvm_id to verify | ||||
| 		:return: None | ||||
| 		""" | ||||
| 		# 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: | ||||
| 			assert lvm_id | ||||
| 			assert uuid | ||||
|  | ||||
| 			if path_create: | ||||
| 				assert uuid != lvm_id | ||||
|  | ||||
| 			# Check for Manager.LookUpByLvmId query, we cannot | ||||
| 			# 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 | ||||
| 				# other text derivation of unknown.  When we find that a PV is | ||||
| 				# missing we will clear out the lvm_id as it's likely not unique | ||||
| 				# and thus not useful and potentially harmful for lookups. | ||||
| 				if path_create == pv_obj_path_generate and \ | ||||
| 						cfg.db.pv_missing(uuid): | ||||
| 					lvm_id = None | ||||
|  | ||||
| 				# Lets check for the uuid first | ||||
| 				path = self._id_lookup(uuid) | ||||
| 				if path: | ||||
| 					# Verify the lvm_id is sane | ||||
| 					self._lvm_id_verify(path, uuid, lvm_id) | ||||
| 				else: | ||||
| 					# Unable to find by UUID, lets lookup by lvm_id | ||||
| 					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() | ||||
| 							self._lookup_add(None, path, lvm_id, uuid) | ||||
|  | ||||
| 			# print('get_object_path_by_lvm_id(%s, %s, %s, %s: return %s' % | ||||
| 			# 	   (uuid, lvm_id, str(path_create), str(gen_new), path)) | ||||
|  | ||||
| 			return path | ||||
|  | ||||
|  | ||||
| class ObjectManagerLock(object): | ||||
| 	""" | ||||
| 	The sole purpose of this class is to allow other code the ability to | ||||
| 	lock the object manager using a `with` statement, eg. | ||||
|  | ||||
| 	with cfg.om.locked(): | ||||
| 		# Do stuff with object manager | ||||
|  | ||||
| 	This will ensure that the lock is always released (assuming this is done | ||||
| 	correctly) | ||||
| 	""" | ||||
|  | ||||
| 	def __init__(self, recursive_lock): | ||||
| 		self._lock = recursive_lock | ||||
|  | ||||
| 	def __enter__(self): | ||||
| 		# Acquire lock | ||||
| 		self._lock.acquire() | ||||
|  | ||||
| 	# noinspection PyUnusedLocal | ||||
| 	def __exit__(self, e_type, e_value, e_traceback): | ||||
| 		# Release lock | ||||
| 		self._lock.release() | ||||
| 		self._lock = None | ||||
| @@ -1,10 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| LVM_BINARY = "@LVM_PATH@" | ||||
| @@ -1,273 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from .automatedproperties import AutomatedProperties | ||||
| from . import utils | ||||
| from . import cfg | ||||
| import dbus | ||||
| from .cfg import PV_INTERFACE | ||||
| from . import cmdhandler | ||||
| from .utils import vg_obj_path_generate, n, pv_obj_path_generate, \ | ||||
| 	lv_object_path_method | ||||
| from .loader import common | ||||
| from .request import RequestEntry | ||||
| from .state import State | ||||
| from .utils import round_size | ||||
|  | ||||
|  | ||||
| # noinspection PyUnusedLocal | ||||
| def pvs_state_retrieve(selection, cache_refresh=True): | ||||
| 	rc = [] | ||||
|  | ||||
| 	if cache_refresh: | ||||
| 		cfg.db.refresh() | ||||
|  | ||||
| 	for p in cfg.db.fetch_pvs(selection): | ||||
| 		rc.append( | ||||
| 			PvState( | ||||
| 				p["pv_name"], p["pv_uuid"], p["pv_name"], | ||||
| 				p["pv_fmt"], n(p["pv_size"]), n(p["pv_free"]), | ||||
| 				n(p["pv_used"]), n(p["dev_size"]), n(p["pv_mda_size"]), | ||||
| 				n(p["pv_mda_free"]), int(p["pv_ba_start"]), | ||||
| 				n(p["pv_ba_size"]), n(p["pe_start"]), | ||||
| 				int(p["pv_pe_count"]), int(p["pv_pe_alloc_count"]), | ||||
| 				p["pv_attr"], p["pv_tags"], p["vg_name"], p["vg_uuid"])) | ||||
| 	return rc | ||||
|  | ||||
|  | ||||
| def load_pvs(device=None, object_path=None, refresh=False, emit_signal=False, | ||||
| 		cache_refresh=True): | ||||
| 	return common( | ||||
| 		pvs_state_retrieve, (Pv,), device, object_path, refresh, | ||||
| 		emit_signal, cache_refresh) | ||||
|  | ||||
|  | ||||
| # noinspection PyUnresolvedReferences | ||||
| class PvState(State): | ||||
| 	@property | ||||
| 	def lvm_id(self): | ||||
| 		return self.lvm_path | ||||
|  | ||||
| 	def _lv_object_list(self, vg_name): | ||||
| 		rc = [] | ||||
| 		if vg_name: | ||||
| 			for lv in sorted(cfg.db.pv_contained_lv(self.lvm_id)): | ||||
| 				lv_uuid, lv_name, meta, segs = lv | ||||
| 				full_name = "%s/%s" % (vg_name, lv_name) | ||||
|  | ||||
| 				path_create = lv_object_path_method(lv_name, meta) | ||||
| 				lv_path = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 					lv_uuid, full_name, path_create) | ||||
|  | ||||
| 				rc.append((lv_path, segs)) | ||||
| 		return rc | ||||
|  | ||||
| 	# noinspection PyUnusedLocal,PyPep8Naming | ||||
| 	def __init__(self, lvm_path, Uuid, Name, | ||||
| 			Fmt, SizeBytes, FreeBytes, UsedBytes, DevSizeBytes, | ||||
| 			MdaSizeBytes, MdaFreeBytes, BaStart, BaSizeBytes, | ||||
| 			PeStart, PeCount, PeAllocCount, attr, Tags, vg_name, | ||||
| 			vg_uuid): | ||||
| 		utils.init_class_from_arguments(self) | ||||
| 		self.pe_segments = cfg.db.pv_pe_segments(Uuid) | ||||
|  | ||||
| 		self.lv = self._lv_object_list(vg_name) | ||||
|  | ||||
| 		if vg_name: | ||||
| 			self.vg_path = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 				vg_uuid, vg_name, vg_obj_path_generate) | ||||
| 		else: | ||||
| 			self.vg_path = '/' | ||||
|  | ||||
| 	def identifiers(self): | ||||
| 		return (self.Uuid, self.lvm_path) | ||||
|  | ||||
| 	def create_dbus_object(self, path): | ||||
| 		if not path: | ||||
| 			path = cfg.om.get_object_path_by_uuid_lvm_id(self.Uuid, self.Name, | ||||
| 														pv_obj_path_generate) | ||||
| 		return Pv(path, self) | ||||
|  | ||||
| 	# noinspection PyMethodMayBeStatic | ||||
| 	def creation_signature(self): | ||||
| 		return (Pv, pv_obj_path_generate) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| @utils.dbus_property(PV_INTERFACE, 'Uuid', 's')  # PV UUID/pv_uuid | ||||
| @utils.dbus_property(PV_INTERFACE, 'Name', 's')  # PV/pv_name | ||||
| @utils.dbus_property(PV_INTERFACE, 'Fmt', 's')  # Fmt/pv_fmt | ||||
| @utils.dbus_property(PV_INTERFACE, 'SizeBytes', 't')  # PSize/pv_size | ||||
| @utils.dbus_property(PV_INTERFACE, 'FreeBytes', 't')  # PFree/pv_free | ||||
| @utils.dbus_property(PV_INTERFACE, 'UsedBytes', 't')  # Used/pv_used | ||||
| @utils.dbus_property(PV_INTERFACE, 'DevSizeBytes', 't')  # DevSize/dev_size | ||||
| @utils.dbus_property(PV_INTERFACE, 'MdaSizeBytes', 't')  # PMdaSize/pv_mda_size | ||||
| @utils.dbus_property(PV_INTERFACE, 'MdaFreeBytes', 't')  # PMdaFree/pv_mda_free | ||||
| @utils.dbus_property(PV_INTERFACE, 'BaStart', 't')  # BA start/pv_ba_start | ||||
| @utils.dbus_property(PV_INTERFACE, 'BaSizeBytes', 't')  # BA size/pv_ba_size | ||||
| @utils.dbus_property(PV_INTERFACE, 'PeStart', 't')  # 1st PE/pe_start | ||||
| @utils.dbus_property(PV_INTERFACE, 'PeCount', 't')  # PE/pv_pe_count | ||||
| @utils.dbus_property(PV_INTERFACE, 'PeAllocCount', 't')  # PE Allocation count | ||||
| class Pv(AutomatedProperties): | ||||
| 	# For properties that we need custom handlers we need these, otherwise | ||||
| 	# we won't get our introspection data | ||||
| 	_Tags_meta = ("as", PV_INTERFACE) | ||||
| 	_PeSegments_meta = ("a(tt)", PV_INTERFACE) | ||||
| 	_Exportable_meta = ("b", PV_INTERFACE) | ||||
| 	_Allocatable_meta = ("b", PV_INTERFACE) | ||||
| 	_Missing_meta = ("b", PV_INTERFACE) | ||||
| 	_Lv_meta = ("a(oa(tts))", PV_INTERFACE) | ||||
| 	_Vg_meta = ("o", PV_INTERFACE) | ||||
|  | ||||
| 	# noinspection PyUnusedLocal,PyPep8Naming | ||||
| 	def __init__(self, object_path, state_obj): | ||||
| 		super(Pv, self).__init__(object_path, pvs_state_retrieve) | ||||
| 		self.set_interface(PV_INTERFACE) | ||||
| 		self.state = state_obj | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _remove(pv_uuid, pv_name, remove_options): | ||||
| 		# Remove the PV, if successful then remove from the model | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.pv_remove(pv_name, remove_options) | ||||
| 			if rc == 0: | ||||
| 				cfg.om.remove_object(dbo, True) | ||||
| 			else: | ||||
| 				# Need to work on error handling, need consistent | ||||
| 				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 '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=PV_INTERFACE, | ||||
| 		in_signature='ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Remove(self, tmo, remove_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Pv._remove, | ||||
| 			(self.Uuid, self.lvm_id, remove_options), | ||||
| 			cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _resize(pv_uuid, pv_name, new_size_bytes, resize_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		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, | ||||
| 												resize_options) | ||||
| 			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 '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=PV_INTERFACE, | ||||
| 		in_signature='tia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def ReSize(self, new_size_bytes, tmo, resize_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Pv._resize, | ||||
| 			(self.Uuid, self.lvm_id, round_size(new_size_bytes), | ||||
| 			resize_options), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _allocation_enabled(pv_uuid, pv_name, yes_no, allocation_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.pv_allocatable( | ||||
| 				pv_name, yes_no, allocation_options) | ||||
| 			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 '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=PV_INTERFACE, | ||||
| 		in_signature='bia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def AllocationEnabled(self, yes, tmo, allocation_options, cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Pv._allocation_enabled, | ||||
| 			(self.Uuid, self.lvm_id, | ||||
| 			yes, allocation_options), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@property | ||||
| 	def Tags(self): | ||||
| 		return utils.parse_tags(self.state.Tags) | ||||
|  | ||||
| 	@property | ||||
| 	def PeSegments(self): | ||||
| 		if len(self.state.pe_segments): | ||||
| 			return dbus.Array(self.state.pe_segments, signature='(tt)') | ||||
| 		return dbus.Array([], '(tt)') | ||||
|  | ||||
| 	@property | ||||
| 	def Exportable(self): | ||||
| 		return dbus.Boolean(self.state.attr[1] == 'x') | ||||
|  | ||||
| 	@property | ||||
| 	def Allocatable(self): | ||||
| 		return dbus.Boolean(self.state.attr[0] == 'a') | ||||
|  | ||||
| 	@property | ||||
| 	def Missing(self): | ||||
| 		return dbus.Boolean(self.state.attr[2] == 'm') | ||||
|  | ||||
| 	def object_path(self): | ||||
| 		return self._object_path | ||||
|  | ||||
| 	@property | ||||
| 	def lvm_id(self): | ||||
| 		return self.state.lvm_id | ||||
|  | ||||
| 	@property | ||||
| 	def identifiers(self): | ||||
| 		return self.state.identifiers() | ||||
|  | ||||
| 	@property | ||||
| 	def Lv(self): | ||||
| 		return dbus.Array(self.state.lv, signature="(oa(tts))") | ||||
|  | ||||
| 	@property | ||||
| 	def Vg(self): | ||||
| 		return dbus.ObjectPath(self.state.vg_path) | ||||
| @@ -1,45 +0,0 @@ | ||||
| # 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 | ||||
| @@ -1,151 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| import threading | ||||
| # noinspection PyUnresolvedReferences | ||||
| from gi.repository import GLib | ||||
| from .job import Job | ||||
| from . import cfg | ||||
| import traceback | ||||
| from .utils import log_error | ||||
|  | ||||
|  | ||||
| class RequestEntry(object): | ||||
| 	def __init__(self, tmo, method, arguments, cb, cb_error, | ||||
| 			return_tuple=True, job_state=None): | ||||
| 		self.tmo = tmo | ||||
| 		self.method = method | ||||
| 		self.arguments = arguments | ||||
| 		self.cb = cb | ||||
| 		self.cb_error = cb_error | ||||
|  | ||||
| 		self.timer_id = -1 | ||||
| 		self.lock = threading.RLock() | ||||
| 		self.done = False | ||||
| 		self._result = None | ||||
| 		self._job = None | ||||
| 		self._rc = 0 | ||||
| 		self._rc_error = None | ||||
| 		self._return_tuple = return_tuple | ||||
| 		self._job_state = job_state | ||||
|  | ||||
| 		if self.tmo < 0: | ||||
| 			# Client is willing to block forever | ||||
| 			pass | ||||
| 		elif tmo == 0: | ||||
| 			self._return_job() | ||||
| 		else: | ||||
| 			self.timer_id = GLib.timeout_add_seconds( | ||||
| 				tmo, RequestEntry._request_timeout, self) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _request_timeout(r): | ||||
| 		""" | ||||
| 		Method which gets called when the timer runs out! | ||||
| 		:param r:  RequestEntry which timed out | ||||
| 		:return: Nothing | ||||
| 		""" | ||||
| 		r.timer_expired() | ||||
|  | ||||
| 	def _return_job(self): | ||||
| 		self._job = Job(self, self._job_state) | ||||
| 		cfg.om.register_object(self._job, True) | ||||
| 		if self._return_tuple: | ||||
| 			self.cb(('/', self._job.dbus_object_path())) | ||||
| 		else: | ||||
| 			self.cb(self._job.dbus_object_path()) | ||||
|  | ||||
| 	def run_cmd(self): | ||||
| 		try: | ||||
| 			result = self.method(*self.arguments) | ||||
| 			self.register_result(result) | ||||
| 		except Exception as e: | ||||
| 			# Use the request entry to return the result as the client may | ||||
| 			# have gotten a job by the time we hit an error | ||||
| 			# Lets get the stacktrace and set that to the error message | ||||
| 			st = traceback.format_exc() | ||||
| 			cfg.blackbox.dump() | ||||
| 			log_error("Exception returned to client: \n%s" % st) | ||||
| 			self.register_error(-1, str(e), e) | ||||
|  | ||||
| 	def is_done(self): | ||||
| 		with self.lock: | ||||
| 			rc = self.done | ||||
| 		return rc | ||||
|  | ||||
| 	def get_errors(self): | ||||
| 		with self.lock: | ||||
| 			return (self._rc, self._rc_error) | ||||
|  | ||||
| 	def result(self): | ||||
| 		with self.lock: | ||||
| 			if self.done: | ||||
| 				return self._result | ||||
| 			return '/' | ||||
|  | ||||
| 	def _reg_ending(self, result, error_rc=0, error_msg=None, | ||||
| 					error_exception=None): | ||||
| 		with self.lock: | ||||
| 			self.done = True | ||||
| 			if self.timer_id != -1: | ||||
| 				# Try to prevent the timer from firing | ||||
| 				GLib.source_remove(self.timer_id) | ||||
|  | ||||
| 			self._result = result | ||||
| 			self._rc = error_rc | ||||
| 			self._rc_error = error_msg | ||||
|  | ||||
| 			if not self._job: | ||||
| 				# We finished and there is no job, so return result or error | ||||
| 				# now! | ||||
| 				# Note: If we don't have a valid cb or cbe, this indicates a | ||||
| 				# request that doesn't need a response as we already returned | ||||
| 				# one before the request was processed. | ||||
| 				if error_rc == 0: | ||||
| 					if self.cb: | ||||
| 						if self._return_tuple: | ||||
| 							self.cb((result, '/')) | ||||
| 						else: | ||||
| 							self.cb(result) | ||||
| 				else: | ||||
| 					if self.cb_error: | ||||
| 						if not error_exception: | ||||
| 							if not error_msg: | ||||
| 								error_exception = Exception( | ||||
| 									"An error occurred, but no reason was " | ||||
| 									"given, see service logs!") | ||||
| 							else: | ||||
| 								error_exception = Exception(error_msg) | ||||
|  | ||||
| 						self.cb_error(error_exception) | ||||
| 			else: | ||||
| 				# We have a job and it's complete, indicate that it's done. | ||||
| 				# TODO: We need to signal the job is done too. | ||||
| 				self._job.Complete = True | ||||
| 				self._job = None | ||||
|  | ||||
| 	def register_error(self, error_rc, error_message, error_exception): | ||||
| 		self._reg_ending('/', error_rc, error_message, error_exception) | ||||
|  | ||||
| 	def register_result(self, result): | ||||
| 		self._reg_ending(result) | ||||
|  | ||||
| 	def timer_expired(self): | ||||
| 		with self.lock: | ||||
| 			# Set the timer back to -1 as we will get a warning if we try | ||||
| 			# to remove a timer that doesn't exist | ||||
| 			self.timer_id = -1 | ||||
| 			if not self.done: | ||||
| 				# Create dbus job object and return path to caller | ||||
| 				self._return_job() | ||||
| 			else: | ||||
| 				# The job is done, we have nothing to do | ||||
| 				pass | ||||
|  | ||||
| 		return False | ||||
| @@ -1,27 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from abc import ABCMeta, abstractmethod | ||||
|  | ||||
|  | ||||
| class State(object, metaclass=ABCMeta): | ||||
| 	@abstractmethod | ||||
| 	def lvm_id(self): | ||||
| 		pass | ||||
|  | ||||
| 	@abstractmethod | ||||
| 	def identifiers(self): | ||||
| 		pass | ||||
|  | ||||
| 	@abstractmethod | ||||
| 	def create_dbus_object(self, path): | ||||
| 		pass | ||||
|  | ||||
| 	def __str__(self): | ||||
| 		return '*****\n' + str(self.__dict__) + '\n******\n' | ||||
| @@ -1,61 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| import pyudev | ||||
| import threading | ||||
| from .refresh import event_add | ||||
| from . import cfg | ||||
|  | ||||
| observer = None | ||||
| observer_lock = threading.RLock() | ||||
|  | ||||
|  | ||||
| # noinspection PyUnusedLocal | ||||
| def filter_event(action, device): | ||||
| 	# Filter for events of interest and add a request object to be processed | ||||
| 	# when appropriate. | ||||
| 	refresh = False | ||||
|  | ||||
| 	if '.ID_FS_TYPE_NEW' in device: | ||||
| 		fs_type_new = device['.ID_FS_TYPE_NEW'] | ||||
|  | ||||
| 		if 'LVM' in fs_type_new: | ||||
| 			refresh = True | ||||
| 		elif fs_type_new == '': | ||||
| 			# Check to see if the device was one we knew about | ||||
| 			if 'DEVNAME' in device: | ||||
| 				found = cfg.om.get_object_by_lvm_id(device['DEVNAME']) | ||||
| 				if found: | ||||
| 					refresh = True | ||||
|  | ||||
| 	if 'DM_LV_NAME' in device: | ||||
| 		refresh = True | ||||
|  | ||||
| 	if refresh: | ||||
| 		event_add(('udev',)) | ||||
|  | ||||
|  | ||||
| def add(): | ||||
| 	with observer_lock: | ||||
| 		global observer | ||||
| 		context = pyudev.Context() | ||||
| 		monitor = pyudev.Monitor.from_netlink(context) | ||||
| 		monitor.filter_by('block') | ||||
| 		observer = pyudev.MonitorObserver(monitor, filter_event) | ||||
| 		observer.start() | ||||
|  | ||||
|  | ||||
| def remove(): | ||||
| 	with observer_lock: | ||||
| 		global observer | ||||
| 		if observer: | ||||
| 			observer.stop() | ||||
| 			observer = None | ||||
| 			return True | ||||
| 		return False | ||||
| @@ -1,496 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| import xml.etree.ElementTree as Et | ||||
| import sys | ||||
| import inspect | ||||
| import ctypes | ||||
| import os | ||||
| import string | ||||
| import datetime | ||||
|  | ||||
| import dbus | ||||
| from lvmdbusd import cfg | ||||
|  | ||||
|  | ||||
| STDOUT_TTY = os.isatty(sys.stdout.fileno()) | ||||
|  | ||||
|  | ||||
| def rtype(dbus_type): | ||||
| 	""" | ||||
| 	Decorator making sure that the decorated function returns a value of | ||||
| 	specified type. | ||||
| 	:param dbus_type: The specific dbus type to return value as | ||||
| 	""" | ||||
|  | ||||
| 	def decorator(fn): | ||||
| 		def decorated(*args, **kwargs): | ||||
| 			return dbus_type(fn(*args, **kwargs)) | ||||
|  | ||||
| 		return decorated | ||||
|  | ||||
| 	return decorator | ||||
|  | ||||
|  | ||||
| # Field is expected to be a number, handle the corner cases when parsing | ||||
| @rtype(dbus.UInt64) | ||||
| def n(v): | ||||
| 	if not v: | ||||
| 		return 0 | ||||
| 	return int(float(v)) | ||||
|  | ||||
|  | ||||
| @rtype(dbus.UInt32) | ||||
| def n32(v): | ||||
| 	if not v: | ||||
| 		return 0 | ||||
| 	return int(float(v)) | ||||
|  | ||||
|  | ||||
| # noinspection PyProtectedMember | ||||
| def init_class_from_arguments(obj_instance): | ||||
| 	for k, v in list(sys._getframe(1).f_locals.items()): | ||||
| 		if k != 'self': | ||||
| 			nt = k | ||||
|  | ||||
| 			# If the current attribute has a value, but the incoming does | ||||
| 			# not, don't overwrite it.  Otherwise the default values on the | ||||
| 			# property decorator don't work as expected. | ||||
| 			cur = getattr(obj_instance, nt, v) | ||||
|  | ||||
| 			# print 'Init class %s = %s' % (nt, str(v)) | ||||
| 			if not (cur and len(str(cur)) and (v is None or len(str(v))) == 0): | ||||
| 				setattr(obj_instance, nt, v) | ||||
|  | ||||
|  | ||||
| def get_properties(f): | ||||
| 	""" | ||||
| 	Walks through an object instance or it's parent class(es) and determines | ||||
| 	which attributes are properties and if they were created to be used for | ||||
| 	dbus. | ||||
| 	:param f:   Object to inspect | ||||
| 	:return:    A dictionary of tuples with each tuple being: | ||||
| 				0 = An array of dicts with the keys being: p_t, p_name, | ||||
| 				p_access(type, name, access) | ||||
| 				1 = Hash of property names and current value | ||||
| 	""" | ||||
| 	interfaces = dict() | ||||
|  | ||||
| 	for c in inspect.getmro(f.__class__): | ||||
|  | ||||
| 		h = vars(c) | ||||
| 		for p, value in h.items(): | ||||
| 			if isinstance(value, property): | ||||
| 				# We found a property, see if it has a metadata type | ||||
| 				key = attribute_type_name(p) | ||||
| 				if key in h: | ||||
| 					interface = h[key][1] | ||||
|  | ||||
| 					if interface not in interfaces: | ||||
| 						interfaces[interface] = ([], {}) | ||||
|  | ||||
| 					access = '' | ||||
| 					if getattr(f.__class__, p).fget: | ||||
| 						access += 'read' | ||||
| 					if getattr(f.__class__, p).fset: | ||||
| 						access += 'write' | ||||
|  | ||||
| 					interfaces[interface][0].append( | ||||
| 						dict( | ||||
| 							p_t=getattr(f, key)[0], | ||||
| 							p_name=p, | ||||
| 							p_access=access)) | ||||
|  | ||||
| 					interfaces[interface][1][p] = getattr(f, p) | ||||
|  | ||||
| 	return interfaces | ||||
|  | ||||
|  | ||||
| def get_object_property_diff(o_prop, n_prop): | ||||
| 	""" | ||||
| 	Walk through each object properties and report what has changed and with | ||||
| 	the new values | ||||
| 	:param o_prop:   Old keys/values | ||||
| 	:param n_prop:   New keys/values | ||||
| 	:return: hash of properties that have changed and their new value | ||||
| 	""" | ||||
| 	rc = {} | ||||
|  | ||||
| 	for intf_k, intf_v in o_prop.items(): | ||||
| 		for k, v in list(intf_v[1].items()): | ||||
| 			# print('Comparing %s:%s to %s:%s' % | ||||
| 			#      (k, o_prop[intf_k][1][k], k, str(n_prop[intf_k][1][k]))) | ||||
| 			if o_prop[intf_k][1][k] != n_prop[intf_k][1][k]: | ||||
| 				new_value = n_prop[intf_k][1][k] | ||||
|  | ||||
| 				if intf_k not in rc: | ||||
| 					rc[intf_k] = dict() | ||||
|  | ||||
| 				rc[intf_k][k] = new_value | ||||
| 	return rc | ||||
|  | ||||
|  | ||||
| def add_properties(xml, interface, props): | ||||
| 	""" | ||||
| 	Given xml that describes the interface, add property values to the XML | ||||
| 	for the specified interface. | ||||
| 	:param xml:         XML to edit | ||||
| 	:param interface:   Interface to add the properties too | ||||
| 	:param props:       Output from get_properties | ||||
| 	:return: updated XML string | ||||
| 	""" | ||||
| 	if props: | ||||
| 		root = Et.fromstring(xml) | ||||
| 		interface_element = None | ||||
|  | ||||
| 		# Check to see if interface is present | ||||
| 		for c in root: | ||||
| 			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: | ||||
| 			temp = '<property type="%s" name="%s" access="%s"/>\n' % \ | ||||
| 				(p['p_t'], p['p_name'], p['p_access']) | ||||
| 			interface_element.append(Et.fromstring(temp)) | ||||
|  | ||||
| 		return Et.tostring(root, encoding='utf8') | ||||
| 	return xml | ||||
|  | ||||
|  | ||||
| def attribute_type_name(name): | ||||
| 	""" | ||||
| 	Given the property name, return string of the attribute type | ||||
| 	:param name: | ||||
| 	:return: | ||||
| 	""" | ||||
| 	return "_%s_meta" % name | ||||
|  | ||||
|  | ||||
| _type_map = dict( | ||||
| 	s=dbus.String, | ||||
| 	o=dbus.ObjectPath, | ||||
| 	t=dbus.UInt64, | ||||
| 	x=dbus.Int64, | ||||
| 	u=dbus.UInt32, | ||||
| 	i=dbus.Int32, | ||||
| 	n=dbus.Int16, | ||||
| 	q=dbus.UInt16, | ||||
| 	d=dbus.Double, | ||||
| 	y=dbus.Byte, | ||||
| 	b=dbus.Boolean) | ||||
|  | ||||
|  | ||||
| def _pass_through(v): | ||||
| 	""" | ||||
| 	If we have something which is not a simple type we return the original | ||||
| 	value un-wrapped. | ||||
| 	:param v: | ||||
| 	:return: | ||||
| 	""" | ||||
| 	return v | ||||
|  | ||||
|  | ||||
| def _dbus_type(t, value): | ||||
| 	return _type_map.get(t, _pass_through)(value) | ||||
|  | ||||
|  | ||||
| def dbus_property(interface_name, name, dbus_type, doc=None): | ||||
| 	""" | ||||
| 	Creates the get/set properties for the given name.  It assumes that the | ||||
| 	actual attribute is '_' + name and the attribute metadata is stuffed in | ||||
| 	_name_type. | ||||
|  | ||||
| 	There is probably a better way todo this. | ||||
| 	:param interface_name:  Dbus interface this property is associated with | ||||
| 	:param name:            Name of property | ||||
| 	:param dbus_type:       dbus string type eg. s,t,i,x | ||||
| 	:param doc:             Python __doc__ for the property | ||||
| 	:return: | ||||
| 	""" | ||||
| 	attribute_name = '_' + name | ||||
|  | ||||
| 	def getter(self): | ||||
| 		t = getattr(self, attribute_name + '_meta')[0] | ||||
| 		return _dbus_type(t, getattr(self.state, attribute_name[1:])) | ||||
|  | ||||
| 	prop = property(getter, None, None, doc) | ||||
|  | ||||
| 	def decorator(cls): | ||||
| 		setattr(cls, attribute_name + '_meta', (dbus_type, interface_name)) | ||||
| 		setattr(cls, name, prop) | ||||
| 		return cls | ||||
|  | ||||
| 	return decorator | ||||
|  | ||||
|  | ||||
| def parse_tags(tags): | ||||
| 	if len(tags): | ||||
| 		if ',' in tags: | ||||
| 			return tags.split(',') | ||||
| 		return dbus.Array(sorted([tags]), signature='s') | ||||
| 	return dbus.Array([], signature='s') | ||||
|  | ||||
|  | ||||
| def _common_log(msg, *attributes): | ||||
| 	cfg.stdout_lock.acquire() | ||||
| 	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) | ||||
|  | ||||
| 	if STDOUT_TTY and attributes: | ||||
| 		print(color(msg, *attributes)) | ||||
| 	else: | ||||
| 		print(msg) | ||||
|  | ||||
| 	cfg.stdout_lock.release() | ||||
| 	sys.stdout.flush() | ||||
|  | ||||
|  | ||||
| # Serializes access to stdout to prevent interleaved output | ||||
| # @param msg    Message to output to stdout | ||||
| # @return None | ||||
| def log_debug(msg, *attributes): | ||||
| 	if cfg.args and cfg.args.debug: | ||||
| 		_common_log(msg, *attributes) | ||||
|  | ||||
|  | ||||
| def log_error(msg, *attributes): | ||||
| 	_common_log(msg, *attributes) | ||||
|  | ||||
|  | ||||
| # noinspection PyUnusedLocal | ||||
| def handler(signum, frame): | ||||
| 	cfg.run.value = 0 | ||||
| 	log_debug('Signal handler called with signal %d' % signum) | ||||
| 	if cfg.loop is not None: | ||||
| 		cfg.loop.quit() | ||||
|  | ||||
|  | ||||
| def pv_obj_path_generate(): | ||||
| 	return cfg.PV_OBJ_PATH + "/%d" % next(cfg.pv_id) | ||||
|  | ||||
|  | ||||
| def vg_obj_path_generate(): | ||||
| 	return cfg.VG_OBJ_PATH + "/%d" % next(cfg.vg_id) | ||||
|  | ||||
|  | ||||
| def lv_object_path_method(name, meta): | ||||
| 	if name[0] == '[': | ||||
| 		return _hidden_lv_obj_path_generate | ||||
| 	elif meta[0][0] == 't': | ||||
| 		return _thin_pool_obj_path_generate | ||||
| 	elif meta[0][0] == 'C' and 'pool' in meta[1]: | ||||
| 		return _cache_pool_obj_path_generate | ||||
|  | ||||
| 	return _lv_obj_path_generate | ||||
|  | ||||
|  | ||||
| # Note: None of the individual LV path generate functions should be called | ||||
| # directly, they should only be dispatched through lv_object_path_method | ||||
|  | ||||
| def _lv_obj_path_generate(): | ||||
| 	return cfg.LV_OBJ_PATH + "/%d" % next(cfg.lv_id) | ||||
|  | ||||
|  | ||||
| def _thin_pool_obj_path_generate(): | ||||
| 	return cfg.THIN_POOL_PATH + "/%d" % next(cfg.thin_id) | ||||
|  | ||||
|  | ||||
| def _cache_pool_obj_path_generate(): | ||||
| 	return cfg.CACHE_POOL_PATH + "/%d" % next(cfg.cache_pool_id) | ||||
|  | ||||
|  | ||||
| def _hidden_lv_obj_path_generate(): | ||||
| 	return cfg.HIDDEN_LV_PATH + "/%d" % next(cfg.hidden_lv) | ||||
|  | ||||
|  | ||||
| def job_obj_path_generate(): | ||||
| 	return cfg.JOB_OBJ_PATH + "/%d" % next(cfg.job_id) | ||||
|  | ||||
|  | ||||
| def color(text, *user_styles): | ||||
| 	styles = { | ||||
| 		# styles | ||||
| 		'reset': '\033[0m', | ||||
| 		'bold': '\033[01m', | ||||
| 		'disabled': '\033[02m', | ||||
| 		'underline': '\033[04m', | ||||
| 		'reverse': '\033[07m', | ||||
| 		'strike_through': '\033[09m', | ||||
| 		'invisible': '\033[08m', | ||||
| 		# text colors | ||||
| 		'fg_black': '\033[30m', | ||||
| 		'fg_red': '\033[31m', | ||||
| 		'fg_green': '\033[32m', | ||||
| 		'fg_orange': '\033[33m', | ||||
| 		'fg_blue': '\033[34m', | ||||
| 		'fg_purple': '\033[35m', | ||||
| 		'fg_cyan': '\033[36m', | ||||
| 		'fg_light_grey': '\033[37m', | ||||
| 		'fg_dark_grey': '\033[90m', | ||||
| 		'fg_light_red': '\033[91m', | ||||
| 		'fg_light_green': '\033[92m', | ||||
| 		'fg_yellow': '\033[93m', | ||||
| 		'fg_light_blue': '\033[94m', | ||||
| 		'fg_pink': '\033[95m', | ||||
| 		'fg_light_cyan': '\033[96m', | ||||
| 		# background colors | ||||
| 		'bg_black': '\033[40m', | ||||
| 		'bg_red': '\033[41m', | ||||
| 		'bg_green': '\033[42m', | ||||
| 		'bg_orange': '\033[43m', | ||||
| 		'bg_blue': '\033[44m', | ||||
| 		'bg_purple': '\033[45m', | ||||
| 		'bg_cyan': '\033[46m', | ||||
| 		'bg_light_grey': '\033[47m' | ||||
| 	} | ||||
|  | ||||
| 	color_text = '' | ||||
| 	for style in user_styles: | ||||
| 		try: | ||||
| 			color_text += styles[style] | ||||
| 		except KeyError: | ||||
| 			return 'def color: parameter {} does not exist'.format(style) | ||||
| 	color_text += text | ||||
| 	return '\033[0m{0}\033[0m'.format(color_text) | ||||
|  | ||||
|  | ||||
| def pv_range_append(cmd, device, start, end): | ||||
| 	if (start, end) == (0, 0): | ||||
| 		cmd.append(device) | ||||
| 	else: | ||||
| 		if start != 0 and end == 0: | ||||
| 			cmd.append("%s:%d-" % (device, start)) | ||||
| 		else: | ||||
| 			cmd.append( | ||||
| 				"%s:%d-%d" % | ||||
| 				(device, start, end)) | ||||
|  | ||||
|  | ||||
| def pv_dest_ranges(cmd, pv_dest_range_list): | ||||
| 	if len(pv_dest_range_list): | ||||
| 		for i in pv_dest_range_list: | ||||
| 			pv_range_append(cmd, *i) | ||||
|  | ||||
|  | ||||
| def round_size(size_bytes): | ||||
| 	bs = 512 | ||||
| 	remainder = size_bytes % bs | ||||
| 	if not remainder: | ||||
| 		return size_bytes | ||||
| 	return size_bytes + bs - remainder | ||||
|  | ||||
|  | ||||
| _ALLOWABLE_CH = string.ascii_letters + string.digits + '#+-.:=@_\/%' | ||||
| _ALLOWABLE_CH_SET = set(_ALLOWABLE_CH) | ||||
|  | ||||
| _ALLOWABLE_VG_LV_CH = string.ascii_letters + string.digits + '.-_+' | ||||
| _ALLOWABLE_VG_LV_CH_SET = set(_ALLOWABLE_VG_LV_CH) | ||||
| _LV_NAME_RESERVED = ("_cdata", "_cmeta", "_corig", "_mimage", "_mlog", | ||||
| 	"_pmspare", "_rimage", "_rmeta", "_tdata", "_tmeta", "_vorigin") | ||||
|  | ||||
| # Tags can have the characters, based on the code | ||||
| # a-zA-Z0-9._-+/=!:&# | ||||
| _ALLOWABLE_TAG_CH = string.ascii_letters + string.digits + "._-+/=!:&#" | ||||
| _ALLOWABLE_TAG_CH_SET = set(_ALLOWABLE_TAG_CH) | ||||
|  | ||||
|  | ||||
| def _allowable_tag(tag_name): | ||||
| 	# LVM should impose a length restriction | ||||
| 	return set(tag_name) <= _ALLOWABLE_TAG_CH_SET | ||||
|  | ||||
|  | ||||
| def _allowable_vg_name(vg_name): | ||||
| 	if vg_name is None: | ||||
| 		raise ValueError("VG name is None or empty") | ||||
|  | ||||
| 	vg_len = len(vg_name) | ||||
| 	if vg_len == 0 or vg_len > 127: | ||||
| 		raise ValueError("VG name (%s) length (%d) not in the domain 1..127" % | ||||
| 			(vg_name, vg_len)) | ||||
|  | ||||
| 	if not set(vg_name) <= _ALLOWABLE_VG_LV_CH_SET: | ||||
| 		raise ValueError("VG name (%s) contains invalid character, " | ||||
| 			"allowable set(%s)" % (vg_name, _ALLOWABLE_VG_LV_CH)) | ||||
|  | ||||
| 	if vg_name == "." or vg_name == "..": | ||||
| 		raise ValueError('VG name (%s) cannot be "." or ".."' % (vg_name)) | ||||
|  | ||||
|  | ||||
| def _allowable_lv_name(vg_name, lv_name): | ||||
|  | ||||
| 	if lv_name is None: | ||||
| 		raise ValueError("LV name is None or empty") | ||||
|  | ||||
| 	lv_len = len(lv_name) | ||||
|  | ||||
| 	# This length is derived from empirical testing | ||||
| 	if lv_len == 0 or (len(vg_name) + lv_len) > 125: | ||||
| 		raise ValueError("LV name (%s) length (%d) + VG name length " | ||||
| 			"not in the domain 1..125" % (lv_name, lv_len)) | ||||
|  | ||||
| 	if not set(lv_name) <= _ALLOWABLE_VG_LV_CH_SET: | ||||
| 		raise ValueError("LV name (%s) contains invalid character, " | ||||
| 			"allowable (%s)" % (lv_name, _ALLOWABLE_VG_LV_CH)) | ||||
|  | ||||
| 	if any(x in lv_name for x in _LV_NAME_RESERVED): | ||||
| 		raise ValueError("LV name (%s) contains a reserved word, " | ||||
| 			"reserved set(%s)" % (lv_name, str(_LV_NAME_RESERVED))) | ||||
|  | ||||
| 	if lv_name.startswith("snapshot") or lv_name.startswith("pvmove"): | ||||
| 		raise ValueError("LV name (%s) starts with a reserved word, " | ||||
| 			"reserved set(%s)" % (lv_name, str(["snapshot", "pvmove"]))) | ||||
|  | ||||
| 	if lv_name[0] == '-': | ||||
| 		raise ValueError("LV name (%s) cannot start with a '-' " | ||||
| 				"character" % lv_name) | ||||
|  | ||||
|  | ||||
| def validate_device_path(interface, device): | ||||
| 	if not set(device) <= _ALLOWABLE_CH_SET: | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			interface, 'Device path (%s) has invalid characters, ' | ||||
| 			'allowable (%s)' % (device, _ALLOWABLE_CH)) | ||||
|  | ||||
|  | ||||
| def validate_vg_name(interface, vg_name): | ||||
| 	try: | ||||
| 		_allowable_vg_name(vg_name) | ||||
| 	except ValueError as ve: | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			interface, str(ve)) | ||||
|  | ||||
|  | ||||
| def validate_lv_name(interface, vg_name, lv_name): | ||||
| 	try: | ||||
| 		_allowable_lv_name(vg_name, lv_name) | ||||
| 	except ValueError as ve: | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			interface, str(ve)) | ||||
|  | ||||
|  | ||||
| def validate_tag(interface, tag): | ||||
| 	if not _allowable_tag(tag): | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			interface, 'tag (%s) contains invalid character, allowable set(%s)' | ||||
| 			% (tag, _ALLOWABLE_TAG_CH)) | ||||
| @@ -1,957 +0,0 @@ | ||||
| # 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/>. | ||||
|  | ||||
| from .automatedproperties import AutomatedProperties | ||||
|  | ||||
| from . import utils | ||||
| from .utils import pv_obj_path_generate, vg_obj_path_generate, n | ||||
| import dbus | ||||
| from . import cfg | ||||
| from .cfg import VG_INTERFACE | ||||
| from . import cmdhandler | ||||
| from .request import RequestEntry | ||||
| from .loader import common | ||||
| from .state import State | ||||
| from . import background | ||||
| from .utils import round_size | ||||
| from .job import JobState | ||||
|  | ||||
|  | ||||
| # noinspection PyUnusedLocal | ||||
| def vgs_state_retrieve(selection, cache_refresh=True): | ||||
| 	rc = [] | ||||
|  | ||||
| 	if cache_refresh: | ||||
| 		cfg.db.refresh() | ||||
|  | ||||
| 	for v in cfg.db.fetch_vgs(selection): | ||||
| 		rc.append( | ||||
| 			VgState( | ||||
| 				v['vg_uuid'], v['vg_name'], v['vg_fmt'], n(v['vg_size']), | ||||
| 				n(v['vg_free']), v['vg_sysid'], n(v['vg_extent_size']), | ||||
| 				n(v['vg_extent_count']), n(v['vg_free_count']), | ||||
| 				v['vg_profile'], n(v['max_lv']), n(v['max_pv']), | ||||
| 				n(v['pv_count']), n(v['lv_count']), n(v['snap_count']), | ||||
| 				n(v['vg_seqno']), n(v['vg_mda_count']), | ||||
| 				n(v['vg_mda_free']), n(v['vg_mda_size']), | ||||
| 				n(v['vg_mda_used_count']), v['vg_attr'], v['vg_tags'])) | ||||
| 	return rc | ||||
|  | ||||
|  | ||||
| def load_vgs(vg_specific=None, object_path=None, refresh=False, | ||||
| 		emit_signal=False, cache_refresh=True): | ||||
| 	return common(vgs_state_retrieve, (Vg,), vg_specific, object_path, refresh, | ||||
| 					emit_signal, cache_refresh) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming,PyUnresolvedReferences,PyUnusedLocal | ||||
| class VgState(State): | ||||
| 	@property | ||||
| 	def lvm_id(self): | ||||
| 		return self.Name | ||||
|  | ||||
| 	def identifiers(self): | ||||
| 		return (self.Uuid, self.Name) | ||||
|  | ||||
| 	def _lv_paths_build(self): | ||||
| 		rc = [] | ||||
| 		for lv in cfg.db.lvs_in_vg(self.Uuid): | ||||
| 			(lv_name, meta, lv_uuid) = lv | ||||
| 			full_name = "%s/%s" % (self.Name, lv_name) | ||||
|  | ||||
| 			gen = utils.lv_object_path_method(lv_name, meta) | ||||
|  | ||||
| 			lv_path = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 				lv_uuid, full_name, gen) | ||||
| 			rc.append(lv_path) | ||||
| 		return dbus.Array(rc, signature='o') | ||||
|  | ||||
| 	def _pv_paths_build(self): | ||||
| 		rc = [] | ||||
| 		for p in cfg.db.pvs_in_vg(self.Uuid): | ||||
| 			(pv_name, pv_uuid) = p | ||||
| 			rc.append(cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 				pv_uuid, pv_name, pv_obj_path_generate)) | ||||
| 		return rc | ||||
|  | ||||
| 	def __init__(self, Uuid, Name, Fmt, | ||||
| 			SizeBytes, FreeBytes, SysId, ExtentSizeBytes, | ||||
| 			ExtentCount, FreeCount, Profile, MaxLv, MaxPv, PvCount, | ||||
| 			LvCount, SnapCount, Seqno, MdaCount, MdaFree, | ||||
| 			MdaSizeBytes, MdaUsedCount, attr, tags): | ||||
| 		utils.init_class_from_arguments(self) | ||||
| 		self.Pvs = self._pv_paths_build() | ||||
| 		self.Lvs = self._lv_paths_build() | ||||
|  | ||||
| 	def create_dbus_object(self, path): | ||||
| 		if not path: | ||||
| 			path = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 				self.Uuid, self.Name, vg_obj_path_generate) | ||||
| 		return Vg(path, self) | ||||
|  | ||||
| 	# noinspection PyMethodMayBeStatic | ||||
| 	def creation_signature(self): | ||||
| 		return (Vg, vg_obj_path_generate) | ||||
|  | ||||
|  | ||||
| # noinspection PyPep8Naming | ||||
| @utils.dbus_property(VG_INTERFACE, 'Uuid', 's') | ||||
| @utils.dbus_property(VG_INTERFACE, 'Name', 's') | ||||
| @utils.dbus_property(VG_INTERFACE, 'Fmt', 's') | ||||
| @utils.dbus_property(VG_INTERFACE, 'SizeBytes', 't', 0) | ||||
| @utils.dbus_property(VG_INTERFACE, 'FreeBytes', 't', 0) | ||||
| @utils.dbus_property(VG_INTERFACE, 'SysId', 's') | ||||
| @utils.dbus_property(VG_INTERFACE, 'ExtentSizeBytes', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'ExtentCount', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'FreeCount', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'Profile', 's') | ||||
| @utils.dbus_property(VG_INTERFACE, 'MaxLv', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'MaxPv', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'PvCount', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'LvCount', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'SnapCount', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'Seqno', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'MdaCount', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'MdaFree', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'MdaSizeBytes', 't') | ||||
| @utils.dbus_property(VG_INTERFACE, 'MdaUsedCount', 't') | ||||
| class Vg(AutomatedProperties): | ||||
| 	_Tags_meta = ("as", VG_INTERFACE) | ||||
| 	_Pvs_meta = ("ao", VG_INTERFACE) | ||||
| 	_Lvs_meta = ("ao", VG_INTERFACE) | ||||
| 	_Writeable_meta = ("b", VG_INTERFACE) | ||||
| 	_Readable_meta = ("b", VG_INTERFACE) | ||||
| 	_Resizeable_meta = ("b", VG_INTERFACE) | ||||
| 	_Exportable_meta = ('b', VG_INTERFACE) | ||||
| 	_Partial_meta = ('b', VG_INTERFACE) | ||||
| 	_AllocContiguous_meta = ('b', VG_INTERFACE) | ||||
| 	_AllocCling_meta = ('b', VG_INTERFACE) | ||||
| 	_AllocNormal_meta = ('b', VG_INTERFACE) | ||||
| 	_AllocAnywhere_meta = ('b', VG_INTERFACE) | ||||
| 	_Clustered_meta = ('b', VG_INTERFACE) | ||||
|  | ||||
| 	# noinspection PyUnusedLocal,PyPep8Naming | ||||
| 	def __init__(self, object_path, object_state): | ||||
| 		super(Vg, self).__init__(object_path, vgs_state_retrieve) | ||||
| 		self.set_interface(VG_INTERFACE) | ||||
| 		self._object_path = object_path | ||||
| 		self.state = object_state | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def fetch_new_lv(vg_name, lv_name): | ||||
| 		cfg.load() | ||||
| 		return cfg.om.get_object_path_by_lvm_id("%s/%s" % (vg_name, lv_name)) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	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: | ||||
| 				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 '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='sia{sv}', out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Rename(self, name, tmo, rename_options, cb, cbe): | ||||
| 		utils.validate_vg_name(VG_INTERFACE, name) | ||||
| 		r = RequestEntry(tmo, Vg._rename, | ||||
| 				(self.state.Uuid, self.state.lvm_id, name, | ||||
| 				rename_options), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _remove(uuid, vg_name, remove_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			# Remove the VG, if successful then remove from the model | ||||
| 			rc, out, err = cmdhandler.vg_remove(vg_name, remove_options) | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				# Remove the VG | ||||
| 				cfg.om.remove_object(dbo, True) | ||||
|  | ||||
| 				# If an LV has hidden LVs, things can get quite involved, | ||||
| 				# especially if it's the last thin pool to get removed, so | ||||
| 				# lets refresh all | ||||
| 				cfg.load() | ||||
|  | ||||
| 			else: | ||||
| 				# Need to work on error handling, need consistent | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					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 '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='ia{sv}', out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Remove(self, tmo, remove_options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._remove, | ||||
| 				(self.state.Uuid, self.state.lvm_id, remove_options), | ||||
| 				cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _change(uuid, vg_name, change_options): | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.vg_change(change_options, vg_name) | ||||
|  | ||||
| 			# To use an example with d-feet (Method input) | ||||
| 			# {"activate": __import__('gi.repository.GLib', globals(), | ||||
| 			# locals(), ['Variant']).Variant("s", "n")} | ||||
|  | ||||
| 			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 '/' | ||||
|  | ||||
| 	# TODO: This should be broken into a number of different methods | ||||
| 	# instead of having one method that takes a hash for parameters.  Some of | ||||
| 	# the changes that vgchange does works on entire system, not just a | ||||
| 	# specfic vg, thus that should be in the Manager interface. | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Change(self, tmo, change_options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._change, | ||||
| 				(self.state.Uuid, self.state.lvm_id, change_options), | ||||
| 				cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _reduce(uuid, vg_name, missing, pv_object_paths, reduce_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			pv_devices = [] | ||||
|  | ||||
| 			# If pv_object_paths is not empty, then get the device paths | ||||
| 			if pv_object_paths and len(pv_object_paths) > 0: | ||||
| 				for pv_op in pv_object_paths: | ||||
| 					pv = cfg.om.get_object_by_path(pv_op) | ||||
| 					if pv: | ||||
| 						pv_devices.append(pv.lvm_id) | ||||
| 					else: | ||||
| 						raise dbus.exceptions.DBusException( | ||||
| 							VG_INTERFACE, | ||||
| 							'PV Object path not found = %s!' % pv_op) | ||||
|  | ||||
| 			rc, out, err = cmdhandler.vg_reduce(vg_name, missing, pv_devices, | ||||
| 												reduce_options) | ||||
| 			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 '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='baoia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Reduce(self, missing, pv_object_paths, tmo, reduce_options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._reduce, | ||||
| 				(self.state.Uuid, self.state.lvm_id, missing, | ||||
| 				pv_object_paths, reduce_options), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _extend(uuid, vg_name, pv_object_paths, extend_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			extend_devices = [] | ||||
|  | ||||
| 			for i in pv_object_paths: | ||||
| 				pv = cfg.om.get_object_by_path(i) | ||||
| 				if pv: | ||||
| 					extend_devices.append(pv.lvm_id) | ||||
| 				else: | ||||
| 					raise dbus.exceptions.DBusException( | ||||
| 						VG_INTERFACE, 'PV Object path not found = %s!' % i) | ||||
|  | ||||
| 			if len(extend_devices): | ||||
| 				rc, out, err = cmdhandler.vg_extend(vg_name, extend_devices, | ||||
| 													extend_options) | ||||
| 				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, '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 '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='aoia{sv}', out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Extend(self, pv_object_paths, tmo, extend_options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._extend, | ||||
| 				(self.state.Uuid, self.state.lvm_id, pv_object_paths, | ||||
| 				extend_options), | ||||
| 				cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='o(tt)a(ott)ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Move(self, pv_src_obj, pv_source_range, pv_dests_and_ranges, | ||||
| 			tmo, move_options, cb, cbe): | ||||
|  | ||||
| 		job_state = JobState() | ||||
|  | ||||
| 		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 | ||||
| 	def _lv_create(uuid, vg_name, name, size_bytes, pv_dests_and_ranges, | ||||
| 			create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		pv_dests = [] | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			if len(pv_dests_and_ranges): | ||||
| 				for pr in pv_dests_and_ranges: | ||||
| 					pv_dbus_obj = cfg.om.get_object_by_path(pr[0]) | ||||
| 					if not pv_dbus_obj: | ||||
| 						raise dbus.exceptions.DBusException( | ||||
| 							VG_INTERFACE, | ||||
| 							'PV Destination (%s) not found' % pr[0]) | ||||
|  | ||||
| 					pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2])) | ||||
|  | ||||
| 			rc, out, err = cmdhandler.vg_lv_create( | ||||
| 				vg_name, create_options, name, size_bytes, pv_dests) | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				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_interface=VG_INTERFACE, | ||||
| 		in_signature='sta(ott)ia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def LvCreate(self, name, size_bytes, pv_dests_and_ranges, | ||||
| 			tmo, create_options, cb, cbe): | ||||
| 		""" | ||||
| 		This one it for the advanced users that want to roll their own | ||||
| 		:param name:            Name of the LV | ||||
| 		:param size_bytes:      Size of LV in bytes | ||||
| 		:param pv_dests_and_ranges:   Optional array of PV object paths and | ||||
| 									ranges | ||||
| 		:param tmo: -1 == Wait forever, 0 == return job immediately, > 0 == | ||||
| 							willing to wait that number of seconds before | ||||
| 							getting a job | ||||
| 		:param create_options:  hash of key/value pairs | ||||
| 		:param cb: Internal, not accessible by dbus API user | ||||
| 		:param cbe: Internal, not accessible by dbus API user | ||||
| 		:return: (oo) First object path is newly created object, second is | ||||
| 					job object path if created.  Each == '/' when it doesn't | ||||
| 					apply. | ||||
| 		""" | ||||
| 		utils.validate_lv_name(VG_INTERFACE, self.Name, name) | ||||
| 		r = RequestEntry(tmo, Vg._lv_create, | ||||
| 				(self.state.Uuid, self.state.lvm_id, | ||||
| 				name, round_size(size_bytes), pv_dests_and_ranges, | ||||
| 				create_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lv_create_linear(uuid, vg_name, name, size_bytes, | ||||
| 			thin_pool, create_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_lv_create_linear( | ||||
| 				vg_name, create_options, name, size_bytes, thin_pool) | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				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_interface=VG_INTERFACE, | ||||
| 		in_signature='stbia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def LvCreateLinear(self, name, size_bytes, | ||||
| 			thin_pool, tmo, create_options, cb, cbe): | ||||
| 		utils.validate_lv_name(VG_INTERFACE, self.Name, name) | ||||
| 		r = RequestEntry(tmo, Vg._lv_create_linear, | ||||
| 						(self.state.Uuid, self.state.lvm_id, | ||||
| 						name, round_size(size_bytes), thin_pool, | ||||
| 						create_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lv_create_striped(uuid, vg_name, name, size_bytes, num_stripes, | ||||
| 			stripe_size_kb, thin_pool, create_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_lv_create_striped( | ||||
| 				vg_name, create_options, name, size_bytes, | ||||
| 				num_stripes, stripe_size_kb, thin_pool) | ||||
| 			if rc == 0: | ||||
| 				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_interface=VG_INTERFACE, | ||||
| 		in_signature='stuubia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def LvCreateStriped(self, name, size_bytes, num_stripes, | ||||
| 						stripe_size_kb, thin_pool, tmo, create_options, | ||||
| 						cb, cbe): | ||||
| 		utils.validate_lv_name(VG_INTERFACE, self.Name, name) | ||||
| 		r = RequestEntry( | ||||
| 				tmo, Vg._lv_create_striped, | ||||
| 				(self.state.Uuid, self.state.lvm_id, name, | ||||
| 				round_size(size_bytes), num_stripes, stripe_size_kb, | ||||
| 				thin_pool, create_options), | ||||
| 				cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lv_create_mirror(uuid, vg_name, name, size_bytes, | ||||
| 			num_copies, create_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_lv_create_mirror( | ||||
| 				vg_name, create_options, name, size_bytes, num_copies) | ||||
| 			if rc == 0: | ||||
| 				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_interface=VG_INTERFACE, | ||||
| 		in_signature='stuia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def LvCreateMirror(self, name, size_bytes, num_copies, | ||||
| 			tmo, create_options, cb, cbe): | ||||
| 		utils.validate_lv_name(VG_INTERFACE, self.Name, name) | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Vg._lv_create_mirror, | ||||
| 			(self.state.Uuid, self.state.lvm_id, name, | ||||
| 			round_size(size_bytes), num_copies, | ||||
| 			create_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lv_create_raid(uuid, vg_name, name, raid_type, size_bytes, | ||||
| 						num_stripes, stripe_size_kb, create_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_lv_create_raid( | ||||
| 				vg_name, create_options, name, raid_type, size_bytes, | ||||
| 				num_stripes, stripe_size_kb) | ||||
| 			if rc == 0: | ||||
| 				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_interface=VG_INTERFACE, | ||||
| 		in_signature='sstuuia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def LvCreateRaid(self, name, raid_type, size_bytes, | ||||
| 			num_stripes, stripe_size_kb, tmo, | ||||
| 			create_options, cb, cbe): | ||||
| 		utils.validate_lv_name(VG_INTERFACE, self.Name, name) | ||||
| 		r = RequestEntry(tmo, Vg._lv_create_raid, | ||||
| 				(self.state.Uuid, self.state.lvm_id, name, | ||||
| 				raid_type, round_size(size_bytes), num_stripes, | ||||
| 				stripe_size_kb, create_options), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _create_pool(uuid, vg_name, meta_data_lv, data_lv, | ||||
| 						create_options, create_method): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		# Retrieve the full names for the metadata and data lv | ||||
| 		md = cfg.om.get_object_by_path(meta_data_lv) | ||||
| 		data = cfg.om.get_object_by_path(data_lv) | ||||
|  | ||||
| 		if dbo and md and data: | ||||
|  | ||||
| 			new_name = data.Name | ||||
|  | ||||
| 			rc, out, err = create_method( | ||||
| 				md.lv_full_name(), data.lv_full_name(), create_options) | ||||
| 			if rc == 0: | ||||
| 				cfg.om.remove_object(md, emit_signal=True) | ||||
| 				cfg.om.remove_object(data, emit_signal=True) | ||||
|  | ||||
| 				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: | ||||
| 			msg = "" | ||||
|  | ||||
| 			if not dbo: | ||||
| 				msg += 'VG with uuid %s and name %s not present!' % \ | ||||
| 					(uuid, vg_name) | ||||
|  | ||||
| 			if not md: | ||||
| 				msg += 'Meta data LV with object path %s not present!' % \ | ||||
| 					(meta_data_lv) | ||||
|  | ||||
| 			if not data_lv: | ||||
| 				msg += 'Data LV with object path %s not present!' % \ | ||||
| 					(meta_data_lv) | ||||
|  | ||||
| 			raise dbus.exceptions.DBusException(VG_INTERFACE, msg) | ||||
|  | ||||
| 		return cache_pool_lv | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='ooia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def CreateCachePool(self, meta_data_lv, data_lv, tmo, create_options, | ||||
| 						cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Vg._create_pool, | ||||
| 			(self.state.Uuid, self.state.lvm_id, meta_data_lv, | ||||
| 			data_lv, create_options, cmdhandler.vg_create_cache_pool), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='ooia{sv}', | ||||
| 		out_signature='(oo)', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def CreateThinPool(self, meta_data_lv, data_lv, tmo, create_options, | ||||
| 						cb, cbe): | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Vg._create_pool, | ||||
| 			(self.state.Uuid, self.state.lvm_id, meta_data_lv, | ||||
| 			data_lv, create_options, cmdhandler.vg_create_thin_pool), cb, cbe) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _pv_add_rm_tags(uuid, vg_name, pv_object_paths, tags_add, | ||||
| 						tags_del, tag_options): | ||||
| 		pv_devices = [] | ||||
|  | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			# Check for existence of pv object paths | ||||
| 			for p in pv_object_paths: | ||||
| 				pv = cfg.om.get_object_by_path(p) | ||||
| 				if pv: | ||||
| 					pv_devices.append(pv.Name) | ||||
| 				else: | ||||
| 					raise dbus.exceptions.DBusException( | ||||
| 						VG_INTERFACE, 'PV object path = %s not found' % p) | ||||
|  | ||||
| 			rc, out, err = cmdhandler.pv_tag( | ||||
| 				pv_devices, tags_add, tags_del, tag_options) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 				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_interface=VG_INTERFACE, | ||||
| 		in_signature='aoasia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def PvTagsAdd(self, pvs, tags, tmo, tag_options, cb, cbe): | ||||
|  | ||||
| 		for t in tags: | ||||
| 			utils.validate_tag(VG_INTERFACE, t) | ||||
|  | ||||
| 		r = RequestEntry(tmo, Vg._pv_add_rm_tags, | ||||
| 				(self.state.Uuid, self.state.lvm_id, | ||||
| 				pvs, tags, None, tag_options), | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='aoasia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def PvTagsDel(self, pvs, tags, tmo, tag_options, cb, cbe): | ||||
|  | ||||
| 		for t in tags: | ||||
| 			utils.validate_tag(VG_INTERFACE, t) | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 			tmo, Vg._pv_add_rm_tags, | ||||
| 			(self.state.Uuid, self.state.lvm_id, | ||||
| 			pvs, None, tags, tag_options), | ||||
| 			cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _vg_add_rm_tags(uuid, vg_name, tags_add, tags_del, tag_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_tag( | ||||
| 				vg_name, tags_add, tags_del, tag_options) | ||||
| 			if rc == 0: | ||||
| 				dbo.refresh() | ||||
| 				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_interface=VG_INTERFACE, | ||||
| 		in_signature='asia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def TagsAdd(self, tags, tmo, tag_options, cb, cbe): | ||||
|  | ||||
| 		for t in tags: | ||||
| 			utils.validate_tag(VG_INTERFACE, t) | ||||
|  | ||||
| 		r = RequestEntry(tmo, Vg._vg_add_rm_tags, | ||||
| 				(self.state.Uuid, self.state.lvm_id, | ||||
| 				tags, None, tag_options), | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='asia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def TagsDel(self, tags, tmo, tag_options, cb, cbe): | ||||
|  | ||||
| 		for t in tags: | ||||
| 			utils.validate_tag(VG_INTERFACE, t) | ||||
|  | ||||
| 		r = RequestEntry(tmo, Vg._vg_add_rm_tags, | ||||
| 				(self.state.Uuid, self.state.lvm_id, | ||||
| 				None, tags, tag_options), | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _vg_change_set(uuid, vg_name, method, value, 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 = method(vg_name, value, options) | ||||
| 			if rc == 0: | ||||
| 				dbo.refresh() | ||||
| 				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_interface=VG_INTERFACE, | ||||
| 		in_signature='sia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def AllocationPolicySet(self, policy, tmo, policy_options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._vg_change_set, | ||||
| 				(self.state.Uuid, self.state.lvm_id, | ||||
| 				cmdhandler.vg_allocation_policy, | ||||
| 				policy, policy_options), | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='tia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def MaxPvSet(self, number, tmo, max_options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._vg_change_set, | ||||
| 				(self.state.Uuid, self.state.lvm_id, | ||||
| 				cmdhandler.vg_max_pv, number, max_options), | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='ia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def UuidGenerate(self, tmo, options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._vg_change_set, | ||||
| 				(self.state.Uuid, self.state.lvm_id, | ||||
| 				cmdhandler.vg_uuid_gen, None, options), | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	def _attribute(self, pos, ch): | ||||
| 		return dbus.Boolean(self.state.attr[pos] == ch) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='tia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def MaxLvSet(self, number, tmo, max_options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._vg_change_set, | ||||
| 				(self.state.Uuid, self.state.lvm_id, | ||||
| 				cmdhandler.vg_max_lv, number, max_options), | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _vg_activate_deactivate(uuid, vg_name, activate, control_flags, | ||||
| 								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.activate_deactivate( | ||||
| 				'vgchange', vg_name, activate, control_flags, options) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 				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_interface=VG_INTERFACE, | ||||
| 		in_signature='tia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Activate(self, control_flags, tmo, activate_options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._vg_activate_deactivate, | ||||
| 				(self.state.Uuid, self.state.lvm_id, True, | ||||
| 				control_flags, activate_options), | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| 		in_signature='tia{sv}', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def Deactivate(self, control_flags, tmo, activate_options, cb, cbe): | ||||
| 		r = RequestEntry(tmo, Vg._vg_activate_deactivate, | ||||
| 				(self.state.Uuid, self.state.lvm_id, False, | ||||
| 				control_flags, activate_options), | ||||
| 				cb, cbe, return_tuple=False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@property | ||||
| 	def Tags(self): | ||||
| 		return utils.parse_tags(self.state.tags) | ||||
|  | ||||
| 	@property | ||||
| 	def Pvs(self): | ||||
| 		return dbus.Array(self.state.Pvs, signature='o') | ||||
|  | ||||
| 	@property | ||||
| 	def Lvs(self): | ||||
| 		return dbus.Array(self.state.Lvs, signature='o') | ||||
|  | ||||
| 	@property | ||||
| 	def lvm_id(self): | ||||
| 		return self.state.lvm_id | ||||
|  | ||||
| 	@property | ||||
| 	def Writeable(self): | ||||
| 		return self._attribute(0, 'w') | ||||
|  | ||||
| 	@property | ||||
| 	def Readable(self): | ||||
| 		return self._attribute(0, 'r') | ||||
|  | ||||
| 	@property | ||||
| 	def Resizeable(self): | ||||
| 		return self._attribute(1, 'z') | ||||
|  | ||||
| 	@property | ||||
| 	def Exportable(self): | ||||
| 		return self._attribute(2, 'x') | ||||
|  | ||||
| 	@property | ||||
| 	def Partial(self): | ||||
| 		return self._attribute(3, 'p') | ||||
|  | ||||
| 	@property | ||||
| 	def AllocContiguous(self): | ||||
| 		return self._attribute(4, 'c') | ||||
|  | ||||
| 	@property | ||||
| 	def AllocCling(self): | ||||
| 		return self._attribute(4, 'l') | ||||
|  | ||||
| 	@property | ||||
| 	def AllocNormal(self): | ||||
| 		return self._attribute(4, 'n') | ||||
|  | ||||
| 	@property | ||||
| 	def AllocAnywhere(self): | ||||
| 		return self._attribute(4, 'a') | ||||
|  | ||||
| 	@property | ||||
| 	def Clustered(self): | ||||
| 		return self._attribute(5, 'c') | ||||
							
								
								
									
										2
									
								
								daemons/lvmetad/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								daemons/lvmetad/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +0,0 @@ | ||||
| lvmetad | ||||
| lvmetactl | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user