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
This commit is contained in:
Mike Fleetwood 2012-08-18 09:12:47 +01:00 committed by Curtis Gedak
parent fbc4e9e941
commit fdb7e9fe89
2 changed files with 110 additions and 56 deletions

View File

@ -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<Glib::ustring> lvm2_pv_cache ;
static std::vector<Glib::ustring> lvm2_vg_cache ;
static std::vector<Glib::ustring> error_messages ;
};

View File

@ -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<Glib::ustring> LVM2_PV_Info::lvm2_pv_cache ;
std::vector<Glib::ustring> LVM2_PV_Info::lvm2_vg_cache ;
std::vector<Glib::ustring> 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<Glib::ustring> LVM2_PV_Info::get_vg_members( const Glib::ustring & v
initialize_if_required() ;
std::vector<Glib::ustring> 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<Glib::ustring> LVM2_PV_Info::get_error_messages( const Glib::ustring
return error_messages ;
std::vector<Glib::ustring> 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<Glib::ustring> 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<Glib::ustring> 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 )