From fdb7e9fe89c951efd93a3d8d92a61a9ff995b4ee Mon Sep 17 00:00:00 2001 From: Mike Fleetwood Date: Sat, 18 Aug 2012 09:12:47 +0100 Subject: [PATCH] Correctly show multiple "unknown device" LVM2 VG members (#670171) If an LVM2 Volume Group has two or more missing Physical Volumes, the VG is displayed as only having one "unknown device" because get_vg_members() only adds unique names to the list of members. # lvm pvcreate /dev/sda11 /dev/sda12 /dev/sda13 # lvm vgcreate Test-VG1 /dev/sda11 /dev/sda12 /dev/sda13 # wipefs -a /dev/sda12 # wipefs -a /dev/sda13 View partition information in GParted The simplest fix would be to include the PV's UUID in the cache of LVM2 information and add PV names based on unique UUIDs being a member of the relevant VG. Unfortunately "lvm pvs" seems to have a bug when displaying Logical Volume attributes, and there are two or more missing PVs, which causes one of the PVs to be displayed multiple times, rather than displaying each PV once. Without LV attributes, every PV is listed: # lvm pvs --nosuffix --separator , --units b -o pv_name,pv_uuid,vg_name,vg_attr 2> /dev/null PV,PV UUID,VG,Attr /dev/sda11,pJ3R51-AOPP-rKlr-CKCT-nfPS-G5FP-B5Vyjm,Test-VG1,wz-pn- unknown device,Y72oSm-uBcE-ktZL-OIFA-Q129-Uv1B-x5IsrA,Test-VG1,wz-pn- unknown device,1ESORF-7wlR-0tnO-fy2z-nOL1-MrnJ-2O5yjK,Test-VG1,wz-pn- With LV attributes, one missing PV is repeated: # lvm pvs --nosuffix --separator , --units b -o pv_name,pv_uuid,vg_name,vg_attr,lv_name,lv_attr 2> /dev/null PV,PV UUID,VG,Attr,LV,Attr /dev/sda11,pJ3R51-AOPP-rKlr-CKCT-nfPS-G5FP-B5Vyjm,Test-VG1,wz-pn-,, unknown device,Y72oSm-uBcE-ktZL-OIFA-Q129-Uv1B-x5IsrA,Test-VG1,wz-pn-,, unknown device,Y72oSm-uBcE-ktZL-OIFA-Q129-Uv1B-x5IsrA,Test-VG1,wz-pn-,, Also "lvm vgs" and "lvm lvs" don't display anything when including both VG and LV attributes. Instead query the LVM2 information in two separate commands, one querying PV attributes and one querying VG and LV attributes, saving the results in lvm_pv_cache and lvm_vg_cache respectively. Bug #670171 - Add LVM PV read-write support --- include/LVM2_PV_Info.h | 3 + src/LVM2_PV_Info.cc | 163 +++++++++++++++++++++++++++-------------- 2 files changed, 110 insertions(+), 56 deletions(-) diff --git a/include/LVM2_PV_Info.h b/include/LVM2_PV_Info.h index 5e228ac9..5de6989c 100644 --- a/include/LVM2_PV_Info.h +++ b/include/LVM2_PV_Info.h @@ -51,11 +51,14 @@ private: void load_lvm2_pv_info_cache() ; Glib::ustring get_pv_attr_by_path( const Glib::ustring & path, unsigned int entry ) const ; Glib::ustring get_pv_attr_by_row( unsigned int row, unsigned int entry ) const ; + Glib::ustring get_vg_attr_by_name( const Glib::ustring & vgname, unsigned int entry ) const ; + Glib::ustring get_vg_attr_by_row( unsigned int row, unsigned int entry ) const ; static Byte_Value lvm2_pv_attr_to_num( const Glib::ustring str ) ; static bool bit_set( const Glib::ustring & attr, unsigned int bit ) ; static bool lvm2_pv_info_cache_initialized ; static bool lvm_found ; static std::vector lvm2_pv_cache ; + static std::vector lvm2_vg_cache ; static std::vector error_messages ; }; diff --git a/src/LVM2_PV_Info.cc b/src/LVM2_PV_Info.cc index ce5d273a..521ef33f 100644 --- a/src/LVM2_PV_Info.cc +++ b/src/LVM2_PV_Info.cc @@ -25,10 +25,15 @@ enum PV_ATTRIBUTE PVATTR_PV_NAME = 0, PVATTR_PV_SIZE = 1, PVATTR_PV_FREE = 2, - PVATTR_VG_NAME = 3, - PVATTR_VG_BITS = 4, - PVATTR_LV_NAME = 5, - PVATTR_LV_BITS = 6 + PVATTR_VG_NAME = 3 +} ; + +enum VG_ATTRIBUTE +{ + VGATTR_VG_NAME = 0, + VGATTR_VG_BITS = 1, + VGATTR_LV_NAME = 2, + VGATTR_LV_BITS = 3 } ; enum VG_BIT @@ -45,32 +50,39 @@ enum LV_BIT //Data model: // lvm2_pv_info_cache_initialized -// - Has the cache been loaded let? +// - Has the cache been loaded yet? // lvm_found - Is the "lvm" command available? -// lvm2_pv_cache - String vector storing attributes of a PV. -// Attributes are: pv_name,pv_size,pv_free, -// vg_name,vg_attr,lv_name,lv_attr. Pv_size and -// pv_free are the size of the PV and number of -// free bytes. See vgs(8) and lvs(8) for details -// of vg_attr and lv_attr respectively. +// lvm2_pv_cache - String vector storing PV attributes: pv_name, pv_size, pv_free, vg_name. +// One string per PV. // E.g. -// ["/dev/sda10,2147483648,2147483648,,r-----,,", -// "/dev/sda11,2143289344,2143289344,GParted-VG1,wz--n-,,", -// "/dev/sda12,2143289344,1619001344,GParted-VG2,wz--n-,lvol0,-wi---", -// "/dev/sda12,2143289344,1619001344,GParted-VG2,wz--n-,,", -// "/dev/sda13,2143289344,830472192,GParted_VG3,wz--n-,lvol0,-wi-a-", -// "/dev/sda13,2143289344,830472192,GParted_VG3,wz--n-,lvol1,-wi-a-", -// "/dev/sda13,2143289344,830472192,GParted_VG3,wz--n-,,", -// "/dev/sda14,2143289344,1828716544,GParted-VG4,wzx-n-,lvol0,-wi---", -// "/dev/sda14,2143289344,1828716544,GParted-VG4,wzx-n-,,"] -// error_messages - String vector storing whole cache error -// messsages. +// ["/dev/sda10,1073741824,1073741824,", +// "/dev/sda11,1069547520,1069547520,Test-VG1", +// "/dev/sda12,1069547520,335544320,Test_VG2", +// "/dev/sda13,1069547520,0,Test_VG3", +// "/dev/sda14,1069547520,566231040,Test_VG3", +// "/dev/sda15,1069547520,545259520,Test-VG4" +// ] +// lvm2_vg_cache - String vector storing VG attributes: vg_name, vg_attr, lv_name, lv_attr. +// See vgs(8) and lvs(8) for details of vg_attr and lv_attr respectively. +// [",r-----,,", +// "Test-VG1,wz--n-,,", +// "Test_VG2,wz--n-,lvol0,-wi---", +// "Test_VG2,wz--n-,lvol1,-wi---", +// "Test_VG2,wz--n-,,", +// "Test_VG3,wz--n-,lvol0,-wi-a-", +// "Test_VG3,wz--n-,lvol0,-wi-a-", +// "Test_VG3,wz--n-,,", +// "Test-VG4,wzx-n-,lvol0,-wi---", +// "Test-VG4,wzx-n-,," +// ] +// error_messages - String vector storing whole cache error messages. //Initialize static data elements bool LVM2_PV_Info::lvm2_pv_info_cache_initialized = false ; bool LVM2_PV_Info::lvm_found = false ; std::vector LVM2_PV_Info::lvm2_pv_cache ; +std::vector LVM2_PV_Info::lvm2_vg_cache ; std::vector LVM2_PV_Info::error_messages ; LVM2_PV_Info::LVM2_PV_Info() @@ -129,11 +141,11 @@ bool LVM2_PV_Info::has_active_lvs( const Glib::ustring & path ) //PV not yet included in any VG return false ; - for ( unsigned int i = 0 ; i < lvm2_pv_cache .size() ; i ++ ) + for ( unsigned int i = 0 ; i < lvm2_vg_cache .size() ; i ++ ) { - if ( vgname == get_pv_attr_by_row( i, PVATTR_VG_NAME ) ) + if ( vgname == get_vg_attr_by_row( i, VGATTR_VG_NAME ) ) { - if ( bit_set( get_pv_attr_by_row( i, PVATTR_LV_BITS ), LVBIT_STATE ) ) + if ( bit_set( get_vg_attr_by_row( i, VGATTR_LV_BITS ), LVBIT_STATE ) ) //LV in VG is active return true ; } @@ -146,16 +158,7 @@ bool LVM2_PV_Info::is_vg_exported( const Glib::ustring & vgname ) { initialize_if_required() ; - for ( unsigned int i = 0 ; i < lvm2_pv_cache .size() ; i ++ ) - { - if ( vgname == get_pv_attr_by_row( i, PVATTR_VG_NAME ) ) - { - if ( bit_set( get_pv_attr_by_row( i, PVATTR_VG_BITS), VGBIT_EXPORTED ) ) - //VG is exported - return true ; - } - } - return false ; + return bit_set( get_vg_attr_by_name( vgname, VGATTR_VG_BITS ), VGBIT_EXPORTED ) ; } //Return vector of PVs which are members of the VG. Passing "" returns all empty PVs. @@ -164,25 +167,11 @@ std::vector LVM2_PV_Info::get_vg_members( const Glib::ustring & v initialize_if_required() ; std::vector members ; - //Generate array of unique PV member names of a VG for ( unsigned int i = 0 ; i < lvm2_pv_cache .size() ; i ++ ) { if ( vgname == get_pv_attr_by_row( i, PVATTR_VG_NAME ) ) { - bool already_added = false ; - Glib::ustring pvname = get_pv_attr_by_row( i, PVATTR_PV_NAME ) ; - for ( unsigned int j = 0 ; j < members .size() ; j ++ ) - { - if ( pvname == members[ j ] ) - { - already_added = true ; - break ; - } - } - if ( ! already_added ) - { - members .push_back( get_pv_attr_by_row( i, PVATTR_PV_NAME ) ) ; - } + members .push_back( get_pv_attr_by_row( i, PVATTR_PV_NAME ) ) ; } } @@ -197,11 +186,11 @@ std::vector LVM2_PV_Info::get_error_messages( const Glib::ustring return error_messages ; std::vector partition_specific_messages ; - partition_specific_messages .clear() ; Glib::ustring temp ; //Check for partition specific message: partial VG - if ( bit_set( get_pv_attr_by_path( path, PVATTR_VG_BITS ), VGBIT_PARTIAL ) ) + Glib::ustring vgname = get_pv_attr_by_path( path, PVATTR_VG_NAME ) ; + if ( bit_set( get_vg_attr_by_name( vgname, VGATTR_VG_BITS ), VGBIT_PARTIAL ) ) { temp = _("One or more Physical Volumes belonging to the Volume Group is missing.") ; temp += "\n" ; @@ -232,8 +221,10 @@ void LVM2_PV_Info::set_command_found() void LVM2_PV_Info::load_lvm2_pv_info_cache() { Glib::ustring output, error ; + unsigned int i ; lvm2_pv_cache .clear() ; + lvm2_vg_cache .clear() ; error_messages .clear() ; if ( lvm_found ) { @@ -242,15 +233,15 @@ void LVM2_PV_Info::load_lvm2_pv_info_cache() // is changed not using lvm commands. Utils::execute_command( "lvm vgscan", output, error, true ) ; - //Load LVM2 PV attribute cache. Output PV attributes in - // PV_ATTRIBUTE order - Glib::ustring cmd = "lvm pvs --config \"log{command_names=0}\" --nosuffix --noheadings --separator , --units b -o pv_name,pv_size,pv_free,vg_name,vg_attr,lv_name,lv_attr" ; + //Load LVM2 PV attribute cache. Output PV attributes in PV_ATTRIBUTE order + Glib::ustring cmd = "lvm pvs --config \"log{command_names=0}\" --nosuffix " + "--noheadings --separator , --units b -o pv_name,pv_size,pv_free,vg_name" ; if ( ! Utils::execute_command( cmd, output, error, true ) ) { if ( output != "" ) { Utils::tokenize( output, lvm2_pv_cache, "\n" ) ; - for ( unsigned int i = 0 ; i < lvm2_pv_cache .size() ; i ++ ) + for ( i = 0 ; i < lvm2_pv_cache .size() ; i ++ ) lvm2_pv_cache [i] = Utils::trim( lvm2_pv_cache [i] ) ; } } @@ -261,6 +252,31 @@ void LVM2_PV_Info::load_lvm2_pv_info_cache() error_messages .push_back ( output ) ; if ( ! error .empty() ) error_messages .push_back ( error ) ; + } + + //Load LVM2 VG attribute cache. Output VG and LV attributes in VG_ATTRIBUTE order + cmd = "lvm pvs --config \"log{command_names=0}\" --nosuffix " + "--noheadings --separator , --units b -o vg_name,vg_attr,lv_name,lv_attr" ; + if ( ! Utils::execute_command( cmd, output, error, true ) ) + { + if ( output != "" ) + { + Utils::tokenize( output, lvm2_vg_cache, "\n" ) ; + for ( i = 0 ; i < lvm2_vg_cache .size() ; i ++ ) + lvm2_vg_cache [i] = Utils::trim( lvm2_vg_cache [i] ) ; + } + } + else + { + error_messages .push_back( cmd ) ; + if ( ! output .empty() ) + error_messages .push_back ( output ) ; + if ( ! error .empty() ) + error_messages .push_back ( error ) ; + } + + if ( ! error_messages .empty() ) + { Glib::ustring temp ; temp = _("An error occurred reading LVM2 configuration!") ; temp += "\n" ; @@ -308,6 +324,41 @@ Glib::ustring LVM2_PV_Info::get_pv_attr_by_row( unsigned int row, unsigned int e return "" ; } +//Return VG's nth attribute. Performs linear search of the cache and +// uses the first matching VG entry. Attributes are numbered 0 upward +// using VG_ATTRIBUTE enumeration. +Glib::ustring LVM2_PV_Info::get_vg_attr_by_name( const Glib::ustring & vgname, unsigned int entry ) const +{ + for ( unsigned int i = 0 ; i < lvm2_vg_cache .size() ; i ++ ) + { + std::vector vg_attrs ; + Utils::split( lvm2_vg_cache [i], vg_attrs, "," ) ; + if ( VGATTR_VG_NAME < vg_attrs .size() && vgname == vg_attrs [VGATTR_VG_NAME] ) + { + if ( entry < vg_attrs .size() ) + return vg_attrs [entry] ; + else + break ; + } + } + + return "" ; +} + +//Return VG's nth attribute from specified cache row. Row is numbered +// 0 upwards and attributes are numbers numbered 0 upwards using +// VG_ATTRIBUTE enumeration. +Glib::ustring LVM2_PV_Info::get_vg_attr_by_row( unsigned int row, unsigned int entry ) const +{ + if ( row >= lvm2_vg_cache .size() ) + return "" ; + std::vector vg_attrs ; + Utils::split( lvm2_vg_cache [row], vg_attrs, "," ) ; + if ( entry < vg_attrs .size() ) + return vg_attrs [entry] ; + return "" ; +} + //Return string converted to a number, or -1 for error. //Used to convert PVs size or free bytes. Byte_Value LVM2_PV_Info::lvm2_pv_attr_to_num( const Glib::ustring str )