[gparted] Cache results from querying all LVM2 PVs (#160787)



commit 608d992e822a6ea0a8b5612c134ab76ca5a394d9
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Sat Jan 28 14:25:31 2012 +0000

    Cache results from querying all LVM2 PVs (#160787)
    
    Cache results from querying all LVM2 PVs in one go to minimise the
    number of times lvm commands are executed.  Take inspiration from
    caching performed by FS_Info and Proc_Partitions_Info.
    
    Bug #160787 - lvm support

 include/LVM2_PV_Info.h |   57 +++++++++++++++
 include/Makefile.am    |    1 +
 include/Utils.h        |    3 +
 po/POTFILES.in         |    1 +
 src/GParted_Core.cc    |    2 +
 src/LVM2_PV_Info.cc    |  184 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/Makefile.am        |    1 +
 src/Utils.cc           |   24 ++++++
 src/lvm2_pv.cc         |   21 ++----
 9 files changed, 280 insertions(+), 14 deletions(-)
---
diff --git a/include/LVM2_PV_Info.h b/include/LVM2_PV_Info.h
new file mode 100644
index 0000000..9a03ddf
--- /dev/null
+++ b/include/LVM2_PV_Info.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2012 Mike Fleetwood
+ *
+ *  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 of the License, 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 Library General Public License for more details.
+ *
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+/* LVM2_PV_Info
+ *
+ * A persistent cache of information about LVM2 PVs that helps to
+ * minimize the number of executions of lvm commands used to query
+ * their attributes.
+ */
+
+#ifndef LVM2_PV_INFO_H_
+#define LVM2_PV_INFO_H_
+
+#include "../include/Utils.h"
+
+namespace GParted
+{
+
+class LVM2_PV_Info
+{
+public:
+	LVM2_PV_Info() ;
+	LVM2_PV_Info( bool do_refresh ) ;
+	~LVM2_PV_Info() ;
+	bool is_lvm2_pv_supported() ;
+	Glib::ustring get_vg_name( const Glib::ustring & path ) ;
+	Byte_Value get_free_bytes( const Glib::ustring & path ) ;
+	bool has_active_lvs( const Glib::ustring & path ) ;
+private:
+	void set_commands_found() ;
+	void load_lvm2_pv_info_cache() ;
+	Glib::ustring get_pv_attr( const Glib::ustring & path, unsigned int entry ) ;
+	static bool lvm2_pv_info_cache_initialized ;
+	static bool lvm_found ;
+	static bool dmsetup_found ;
+	static std::vector<Glib::ustring> lvm2_pv_cache ;
+	static Glib::ustring dmsetup_info_cache ;
+};
+
+}//GParted
+
+#endif /*LVM2_PV_INFO_H_*/
diff --git a/include/Makefile.am b/include/Makefile.am
index 0544fec..31eab07 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -21,6 +21,7 @@ EXTRA_DIST = \
 	FS_Info.h				\
 	GParted_Core.h    		\
 	HBoxOperations.h    		\
+	LVM2_PV_Info.h			\
 	Operation.h 			\
 	OperationCopy.h			\
 	OperationCheck.h		\
diff --git a/include/Utils.h b/include/Utils.h
index 6bcca41..e472526 100644
--- a/include/Utils.h
+++ b/include/Utils.h
@@ -166,6 +166,9 @@ public:
 	static void tokenize( const Glib::ustring& str,
 	                      std::vector<Glib::ustring>& tokens,
 	                      const Glib::ustring& delimiters ) ;
+	static void split( const Glib::ustring& str,
+	                   std::vector<Glib::ustring>& result,
+	                   const Glib::ustring& delimiters     ) ;
 	static int convert_to_int(const Glib::ustring & src);
 	static Glib::ustring generate_uuid(void);
 };
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 35cd984..21b2ec3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -16,6 +16,7 @@ src/DMRaid.cc
 src/FileSystem.cc
 src/GParted_Core.cc
 src/HBoxOperations.cc
+src/LVM2_PV_Info.cc
 src/OperationChangeUUID.cc
 src/OperationCopy.cc
 src/OperationCheck.cc
diff --git a/src/GParted_Core.cc b/src/GParted_Core.cc
index d740ae4..8b3ebfe 100644
--- a/src/GParted_Core.cc
+++ b/src/GParted_Core.cc
@@ -21,6 +21,7 @@
 #include "../include/DMRaid.h"
 #include "../include/SWRaid.h"
 #include "../include/FS_Info.h"
+#include "../include/LVM2_PV_Info.h"
 #include "../include/OperationCopy.h"
 #include "../include/OperationCreate.h"
 #include "../include/OperationDelete.h"
@@ -172,6 +173,7 @@ void GParted_Core::set_devices( std::vector<Device> & devices )
 	FS_Info fs_info( true ) ;  //Refresh cache of file system information
 	DMRaid dmraid( true ) ;    //Refresh cache of dmraid device information
 	SWRaid swraid( true ) ;    //Refresh cache of swraid device information
+	LVM2_PV_Info lvm2_pv_info( true ) ;	//Refresh cache of LVM2 PV information
 	
 	init_maps() ;
 	
diff --git a/src/LVM2_PV_Info.cc b/src/LVM2_PV_Info.cc
new file mode 100644
index 0000000..28ef858
--- /dev/null
+++ b/src/LVM2_PV_Info.cc
@@ -0,0 +1,184 @@
+/* Copyright (C) 2012 Mike Fleetwood
+ *
+ *  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 of the License, 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 Library General Public License for more details.
+ *
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../include/LVM2_PV_Info.h"
+
+namespace GParted
+{
+
+enum PV_ATTRIBUTE
+{
+	PVATTR_PV_NAME = 0,
+	PVATTR_VG_NAME = 1,
+	PVATTR_PV_FREE = 2
+} ;
+
+//Data model:
+//  lvm2_pv_info_cache_initialized
+//                      - Has the cache been loaded let?
+//  lvm_found           - Is the "lvm" command available?
+//  dmsetup_found       - Is the "dmsetup" command available?
+//  lvm2_pv_cache       - String vector storing attributes of a PV.
+//                        Attributes are: pv_name,vg_name,pv_free.
+//                        Pv_free is the number of free bytes.
+//                        E.g.
+//                        ["/dev/sda6,vg_test,4022337536",
+//                         "/dev/sda8,vg_test2,5087690752",
+//                         "/dev/sda10,,2147483648"]
+//  dmsetup_info_cache  - String storing active LVs.  E.g.
+//                        "vg_test-lvol1\nvg_test-lvol0\nvg_test2-lvol0\n"
+
+//Initialize static data elements
+bool LVM2_PV_Info::lvm2_pv_info_cache_initialized = false ;
+bool LVM2_PV_Info::lvm_found = false ;
+bool LVM2_PV_Info::dmsetup_found = false ;
+std::vector<Glib::ustring> LVM2_PV_Info::lvm2_pv_cache ;
+Glib::ustring LVM2_PV_Info::dmsetup_info_cache = "" ;
+
+LVM2_PV_Info::LVM2_PV_Info()
+{
+	//Ensure that cache has been loaded at least once
+	if ( ! lvm2_pv_info_cache_initialized )
+	{
+		lvm2_pv_info_cache_initialized = true ;
+		set_commands_found() ;
+		load_lvm2_pv_info_cache() ;
+	}
+}
+
+LVM2_PV_Info::LVM2_PV_Info( bool do_refresh )
+{
+	//Ensure that cache has been loaded at least once
+	if ( ! lvm2_pv_info_cache_initialized )
+	{
+		lvm2_pv_info_cache_initialized = true ;
+		set_commands_found() ;
+		if ( do_refresh == false )
+			load_lvm2_pv_info_cache() ;
+	}
+
+	if ( do_refresh )
+		load_lvm2_pv_info_cache() ;
+}
+
+LVM2_PV_Info::~LVM2_PV_Info()
+{
+}
+
+bool LVM2_PV_Info::is_lvm2_pv_supported()
+{
+	return ( lvm_found && dmsetup_found ) ;
+}
+
+Glib::ustring LVM2_PV_Info::get_vg_name( const Glib::ustring & path )
+{
+	return get_pv_attr( path, PVATTR_VG_NAME ) ;
+}
+
+//Return number of free bytes in the PV, or -1 for error.
+Byte_Value LVM2_PV_Info::get_free_bytes( const Glib::ustring & path )
+{
+	Byte_Value free_bytes = -1 ;
+	Glib::ustring fb_str = get_pv_attr( path, PVATTR_PV_FREE ) ;
+	if ( fb_str != "" )
+	{
+		gchar * suffix ;
+		free_bytes = (Byte_Value) g_ascii_strtoll( fb_str .c_str(), & suffix, 10 ) ;
+		if ( free_bytes < 0 || ( free_bytes == 0 && suffix == fb_str ) )
+			//Negative number or conversion failed
+			free_bytes = -1 ;
+	}
+	return free_bytes ;
+}
+
+//Report if any LVs are active in the VG stored in the PV.
+bool LVM2_PV_Info::has_active_lvs( const Glib::ustring & path )
+{
+	Glib::ustring vgname = get_pv_attr( path, PVATTR_VG_NAME ) ;
+	if ( vgname == "" )
+		//PV not yet included in any VG
+		return false ;
+
+	Glib::ustring regexp = "^(" + vgname + ")-" ;
+	Glib::ustring match = Utils::regexp_label( dmsetup_info_cache, regexp ) ;
+	return ( match == vgname ) ;
+}
+
+//Private methods
+
+void LVM2_PV_Info::set_commands_found()
+{
+	//Set status of commands found
+	lvm_found = ( ! Glib::find_program_in_path( "lvm" ) .empty() ) ;
+	dmsetup_found = ( ! Glib::find_program_in_path( "dmsetup" ) .empty() ) ;
+}
+
+void LVM2_PV_Info::load_lvm2_pv_info_cache()
+{
+	Glib::ustring output, error ;
+
+	//Load LVM2 PV attribute cache
+	lvm2_pv_cache .clear() ;
+	if ( lvm_found )
+	{
+		//The OS is expected to fully enable LVM, this scan does
+		//  not do the full job.  It is included incase anything
+		//  is changed not using lvm commands.
+		Utils::execute_command( "lvm vgscan", output, error, true ) ;
+		//Output PV attributes, in PV_ATTRIBUTE order
+		if ( ! Utils::execute_command( "lvm pvs --config \"log{command_names=0}\" --nosuffix --noheadings --separator , --units b -o pv_name,vg_name,pv_free", output, error, true ) )
+		{
+			if ( output != "" )
+				Utils::tokenize( output, lvm2_pv_cache, " \n" ) ;
+		}
+	}
+
+	//Load dmsetup info cache.  This is a list of active LV names as
+	//reported by the kernel's device-mapper driver.
+	dmsetup_info_cache = "" ;
+	if ( dmsetup_found )
+	{
+		if ( ! Utils::execute_command( "dmsetup info --columns --noheadings --separator , -o name", output, error, true ) )
+		{
+			Glib::ustring match = Utils::regexp_label( output, "^(No devices found).*" ) ;
+			if ( match != "No devices found" )
+				dmsetup_info_cache = output ;
+		}
+	}
+}
+
+//Return PV's nth attribute.  Attributes are numbered 0 upwards for:
+//  pv_name,vg_name,pv_free
+Glib::ustring LVM2_PV_Info::get_pv_attr( const Glib::ustring & path, unsigned int entry )
+{
+	for ( unsigned int i = 0 ; i < lvm2_pv_cache .size() ; i ++ )
+	{
+		std::vector<Glib::ustring> pv_attrs ;
+		Utils::split( lvm2_pv_cache [i], pv_attrs, "," ) ;
+		if ( path == pv_attrs [PVATTR_PV_NAME] )
+		{
+			if ( entry < pv_attrs .size() )
+				return pv_attrs [entry] ;
+			else
+				break ;
+		}
+	}
+
+	return "" ;
+}
+
+}//GParted
diff --git a/src/Makefile.am b/src/Makefile.am
index 66a8e53..5fe75fb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -31,6 +31,7 @@ gpartedbin_SOURCES = \
 	FS_Info.cc				\
 	GParted_Core.cc			\
 	HBoxOperations.cc		\
+	LVM2_PV_Info.cc			\
 	Operation.cc			\
 	OperationChangeUUID.cc		\
 	OperationCopy.cc		\
diff --git a/src/Utils.cc b/src/Utils.cc
index 8a14802..c778974 100644
--- a/src/Utils.cc
+++ b/src/Utils.cc
@@ -544,6 +544,30 @@ void Utils::tokenize( const Glib::ustring& str,
 	}
 }
 
+//Split string on every delimiter, appending to the vector.  Inspired by:
+//  http://stackoverflow.com/questions/236129/how-to-split-a-string-in-c/3616605#3616605
+//  E.g. using Utils::split(str, result, ":") for str -> result
+//  "" -> []   "a" -> ["a"]   "::" -> ["","",""]   ":a::bb" -> ["","a","","bb"]
+void Utils::split( const Glib::ustring& str,
+                   std::vector<Glib::ustring>& result,
+                   const Glib::ustring& delimiters     )
+{
+	//Special case zero length string to empty vector
+	if ( str == "" )
+		return ;
+	Glib::ustring::size_type fromPos  = 0 ;
+	Glib::ustring::size_type delimPos = str.find_first_of( delimiters );
+	while ( Glib::ustring::npos != delimPos )
+	{
+		Glib::ustring word( str, fromPos, delimPos - fromPos ) ;
+		result .push_back( word ) ;
+		fromPos = delimPos + 1 ;
+		delimPos = str.find_first_of( delimiters, fromPos ) ;
+	}
+	Glib::ustring word( str, fromPos ) ;
+	result. push_back( word ) ;
+}
+
 // Converts a Glib::ustring into a int
 int Utils::convert_to_int(const Glib::ustring & src)
 {
diff --git a/src/lvm2_pv.cc b/src/lvm2_pv.cc
index f022145..8bd2d84 100644
--- a/src/lvm2_pv.cc
+++ b/src/lvm2_pv.cc
@@ -16,6 +16,7 @@
  */
 
 
+#include "../include/LVM2_PV_Info.h"
 #include "../include/lvm2_pv.h"
 
 namespace GParted
@@ -26,7 +27,8 @@ FS lvm2_pv::get_filesystem_support()
 	FS fs ;
 	fs .filesystem = GParted::FS_LVM2_PV ;
 
-	if ( ! Glib::find_program_in_path( "lvm" ) .empty() )
+	LVM2_PV_Info lvm2_pv_info ;
+	if ( lvm2_pv_info .is_lvm2_pv_supported() )
 	{
 		fs .read = GParted::FS::EXTERNAL ;
 	}
@@ -36,19 +38,10 @@ FS lvm2_pv::get_filesystem_support()
 
 void lvm2_pv::set_used_sectors( Partition & partition )
 {
-	if ( ! Utils::execute_command( "lvm pvs --units b --noheadings --nosuffix -o pv_free " + partition .get_path(), output, error, true ) )
-	{
-		gdouble free_bytes = g_ascii_strtod( output .c_str(), NULL ) ;
-		partition .Set_Unused( Utils::round( free_bytes / double(partition .sector_size) ) ) ;
-	}
-	else
-	{
-		if ( ! output .empty() )
-			partition .messages .push_back( output ) ;
-
-		if ( ! error .empty() )
-			partition .messages .push_back( error ) ;
-	}
+	LVM2_PV_Info lvm2_pv_info ;
+	Byte_Value free_bytes = lvm2_pv_info .get_free_bytes( partition .get_path() ) ;
+	if ( free_bytes >= 0 )
+		partition .Set_Unused( Utils::round( double(free_bytes) / double(partition .sector_size) ) ) ;
 }
 
 void lvm2_pv::read_label( Partition & partition )



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]