2016-02-18 02:53:35 +03:00
# 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/>.
2022-08-18 01:21:19 +03:00
import errno
2016-02-18 02:53:35 +03:00
from subprocess import Popen , PIPE
2021-06-11 18:35:31 +03:00
import select
2016-02-18 02:53:35 +03:00
import time
import threading
from itertools import chain
2016-03-22 01:22:56 +03:00
import collections
2016-08-25 02:29:35 +03:00
import os
2016-02-18 02:53:35 +03:00
2016-11-17 22:57:44 +03:00
from lvmdbusd import cfg
2021-06-11 18:35:31 +03:00
from lvmdbusd . utils import pv_dest_ranges , log_debug , log_error , add_no_notify , \
2022-08-31 23:04:59 +03:00
make_non_block , read_decoded , extract_stack_trace , LvmBug , add_config_option
2016-11-17 22:57:44 +03:00
from lvmdbusd . lvm_shell_proxy import LVMShellProxy
2016-02-18 02:53:35 +03:00
2016-06-03 21:18:21 +03:00
try :
import simplejson as json
except ImportError :
import json
2016-02-18 02:53:35 +03:00
total_time = 0.0
total_count = 0
# We need to prevent different threads from using the same lvm shell
# at the same time.
2016-03-22 01:22:56 +03:00
cmd_lock = threading . RLock ( )
2016-02-18 02:53:35 +03:00
2016-03-22 01:22:56 +03:00
class LvmExecutionMeta ( object ) :
2022-08-10 01:43:00 +03:00
def __init__ ( self , start , ended , cmd , ec = - 1000 , stdout_txt = None , stderr_txt = None ) :
2017-06-02 20:25:01 +03:00
self . lock = threading . RLock ( )
2016-03-22 01:22:56 +03:00
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 ) :
2017-06-02 20:25:01 +03:00
with self . lock :
2022-08-10 01:43:00 +03:00
if self . ended == 0 :
ended_txt = " still running "
self . ended = time . time ( )
else :
ended_txt = str ( time . ctime ( self . ended ) )
return ' EC= %d for " %s " \n ' \
" STARTED: %s , ENDED: %s , DURATION: %f \n " \
2017-06-02 20:25:01 +03:00
" STDOUT= %s \n " \
" STDERR= %s \n " % \
2022-08-10 01:43:00 +03:00
( self . ec , " " . join ( self . cmd ) , time . ctime ( self . start ) , ended_txt , float ( self . ended ) - self . start ,
self . stdout_txt ,
self . stderr_txt )
def completed ( self , end_time , ec , stdout_txt , stderr_txt ) :
with self . lock :
self . ended = end_time
self . ec = ec
self . stdout_txt = stdout_txt
self . stderr_txt = stderr_txt
2016-03-22 01:22:56 +03:00
class LvmFlightRecorder ( object ) :
2016-11-30 03:01:56 +03:00
def __init__ ( self , size = 16 ) :
self . queue = collections . deque ( maxlen = size )
2022-08-10 01:43:00 +03:00
self . lock = threading . RLock ( )
2016-03-22 01:22:56 +03:00
def add ( self , lvm_exec_meta ) :
2022-08-10 01:43:00 +03:00
with self . lock :
self . queue . append ( lvm_exec_meta )
2016-03-22 01:22:56 +03:00
def dump ( self ) :
2022-08-10 01:43:00 +03:00
with self . lock :
2016-11-30 03:01:56 +03:00
if len ( self . queue ) :
2022-08-10 01:43:00 +03:00
log_error ( " LVM dbus flight recorder START (in order of newest to oldest) " )
2018-12-11 22:58:23 +03:00
for c in reversed ( self . queue ) :
2016-11-30 03:01:56 +03:00
log_error ( str ( c ) )
log_error ( " LVM dbus flight recorder END " )
2022-08-18 01:24:08 +03:00
self . queue . clear ( )
2016-03-22 01:22:56 +03:00
2022-08-10 01:55:27 +03:00
cfg . flightrecorder = LvmFlightRecorder ( )
2016-03-22 01:22:56 +03:00
2016-02-18 02:53:35 +03:00
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 ] ) )
2021-06-11 18:35:31 +03:00
def call_lvm ( command , debug = False , line_cb = None ,
cb_data = None ) :
2016-02-18 02:53:35 +03:00
"""
Call an executable and return a tuple of exitcode , stdout , stderr
2021-06-11 18:35:31 +03:00
: param command : Command to execute
: param debug : Dump debug to stdout
: param line_cb : Call the supplied function for each line read from
stdin , CALL MUST EXECUTE QUICKLY and not * block *
otherwise call_lvm function will fail to read
stdin / stdout . Return value of call back is ignored
: param cb_data : Supplied to callback to allow caller access to
its own data
# Callback signature
def my_callback ( my_context , line_read_stdin )
pass
2016-02-18 02:53:35 +03:00
"""
# 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 )
2017-03-09 00:35:53 +03:00
command = add_no_notify ( command )
2016-02-18 02:53:35 +03:00
2016-08-25 02:29:35 +03:00
process = Popen ( command , stdout = PIPE , stderr = PIPE , close_fds = True ,
env = os . environ )
2016-02-18 02:53:35 +03:00
2021-06-11 18:35:31 +03:00
stdout_text = " "
stderr_text = " "
stdout_index = 0
make_non_block ( process . stdout )
make_non_block ( process . stderr )
2022-08-18 01:21:19 +03:00
while True and cfg . run . value != 0 :
2021-06-11 18:35:31 +03:00
try :
rd_fd = [ process . stdout . fileno ( ) , process . stderr . fileno ( ) ]
ready = select . select ( rd_fd , [ ] , [ ] , 2 )
for r in ready [ 0 ] :
if r == process . stdout . fileno ( ) :
stdout_text + = read_decoded ( process . stdout )
elif r == process . stderr . fileno ( ) :
stderr_text + = read_decoded ( process . stderr )
if line_cb is not None :
# Process the callback for each line read!
while True :
i = stdout_text . find ( " \n " , stdout_index )
if i != - 1 :
try :
line_cb ( cb_data , stdout_text [ stdout_index : i ] )
2022-08-31 19:18:55 +03:00
except BaseException as be :
st = extract_stack_trace ( be )
2021-06-11 18:35:31 +03:00
log_error ( " call_lvm: line_cb exception: \n %s " % st )
stdout_index = i + 1
else :
break
# Check to see if process has terminated, None when running
if process . poll ( ) is not None :
break
except IOError as ioe :
log_debug ( " call_lvm: " + str ( ioe ) )
2022-08-18 01:21:19 +03:00
break
if process . returncode is not None :
2022-08-31 23:04:59 +03:00
cfg . lvmdebug . lvm_complete ( )
2022-08-31 19:05:36 +03:00
if debug or ( process . returncode != 0 and ( process . returncode != 5 and " fullreport " in command ) ) :
2022-08-18 01:21:19 +03:00
_debug_c ( command , process . returncode , ( stdout_text , stderr_text ) )
2016-02-18 02:53:35 +03:00
2022-08-18 01:21:19 +03:00
return process . returncode , stdout_text , stderr_text
else :
if cfg . run . value == 0 :
2022-08-26 19:10:24 +03:00
raise SystemExit
2022-08-18 01:21:19 +03:00
# We can bail out before the lvm command finished when we get a signal
# which is requesting we exit
return - errno . EINTR , " " , " operation interrupted "
2016-02-18 02:53:35 +03:00
2016-08-29 22:52:41 +03:00
# 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
2016-02-18 02:53:35 +03:00
2016-08-29 23:07:55 +03:00
2016-02-18 02:53:35 +03:00
def _shell_cfg ( ) :
global _t_call
2016-11-17 22:57:44 +03:00
# noinspection PyBroadException
2016-08-12 23:23:05 +03:00
try :
lvm_shell = LVMShellProxy ( )
_t_call = lvm_shell . call_lvm
2016-08-25 02:29:35 +03:00
cfg . SHELL_IN_USE = lvm_shell
2016-08-29 22:26:16 +03:00
return True
2022-08-31 19:18:55 +03:00
except Exception as e :
2016-08-12 23:23:05 +03:00
_t_call = call_lvm
2016-08-25 02:29:35 +03:00
cfg . SHELL_IN_USE = None
2022-08-31 19:18:55 +03:00
log_error ( " Unable to utilize lvm shell, dropping "
" back to fork & exec \n %s " % extract_stack_trace ( e ) )
2016-08-29 22:26:16 +03:00
return False
2016-02-18 02:53:35 +03:00
def set_execution ( shell ) :
global _t_call
with cmd_lock :
2016-08-25 02:29:35 +03:00
# If the user requested lvm shell and we are currently setup that
# way, just return
if cfg . SHELL_IN_USE and shell :
2016-08-29 22:26:16 +03:00
return True
2016-08-25 02:29:35 +03:00
else :
if not shell and cfg . SHELL_IN_USE :
cfg . SHELL_IN_USE . exit_shell ( )
cfg . SHELL_IN_USE = None
2016-08-12 23:23:05 +03:00
_t_call = call_lvm
2016-02-18 02:53:35 +03:00
if shell :
2016-08-29 22:26:16 +03:00
if cfg . args . use_json :
return _shell_cfg ( )
else :
return False
return True
2016-02-18 02:53:35 +03:00
def time_wrapper ( command , debug = False ) :
global total_time
global total_count
with cmd_lock :
start = time . time ( )
2022-08-10 01:43:00 +03:00
meta = LvmExecutionMeta ( start , 0 , command )
2022-08-10 01:55:27 +03:00
# Add the partial metadata to flight recorder, so if the command hangs
# we will see what it was.
cfg . flightrecorder . add ( meta )
2016-02-18 02:53:35 +03:00
results = _t_call ( command , debug )
2016-03-22 01:22:56 +03:00
ended = time . time ( )
total_time + = ( ended - start )
2016-02-18 02:53:35 +03:00
total_count + = 1
2022-08-10 01:43:00 +03:00
meta . completed ( ended , * results )
2016-02-18 02:53:35 +03:00
return results
call = time_wrapper
# Default cmd
# Place default arguments for every command here.
def _dc ( cmd , args ) :
2022-08-10 01:45:04 +03:00
c = [ cmd , ' --nosuffix ' , ' --unbuffered ' , ' --units ' , ' b ' ]
2016-02-18 02:53:35 +03:00
c . extend ( args )
return c
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 != " " :
2019-08-26 15:35:51 +03:00
if isinstance ( v , int ) :
rc . append ( str ( int ( v ) ) )
else :
rc . append ( str ( v ) )
2016-02-18 02:53:35 +03:00
return rc
def pv_remove ( device , remove_options ) :
cmd = [ ' pvremove ' ]
cmd . extend ( options_to_cli_args ( remove_options ) )
cmd . append ( device )
return call ( cmd )
2016-08-12 23:23:05 +03:00
def _qt ( tag_name ) :
2016-08-30 02:00:21 +03:00
return ' @ %s ' % tag_name
2016-08-12 23:23:05 +03:00
2016-02-18 02:53:35 +03:00
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 :
2016-08-12 23:23:05 +03:00
cmd . extend ( list ( chain . from_iterable (
( ' --addtag ' , _qt ( x ) ) for x in add ) ) )
2016-02-18 02:53:35 +03:00
if rm :
2016-08-12 23:23:05 +03:00
cmd . extend ( list ( chain . from_iterable (
( ' --deltag ' , _qt ( x ) ) for x in rm ) ) )
2016-02-18 02:53:35 +03:00
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 )
2019-01-17 00:43:39 +03:00
def vg_rename ( vg_uuid , new_name , rename_options ) :
2016-02-18 02:53:35 +03:00
cmd = [ ' vgrename ' ]
cmd . extend ( options_to_cli_args ( rename_options ) )
2019-01-17 00:43:39 +03:00
cmd . extend ( [ vg_uuid , new_name ] )
2016-02-18 02:53:35 +03:00
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 ) )
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --size ' , ' %d B ' % size_bytes ] )
2017-04-22 08:03:46 +03:00
cmd . extend ( [ ' --name ' , name , vg_name , ' --yes ' ] )
2016-02-18 02:53:35 +03:00
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 :
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --size ' , ' %d B ' % size_bytes ] )
2016-02-18 02:53:35 +03:00
cmd . extend ( [ ' --name ' , name , vg_name ] )
return call ( cmd )
2017-02-02 04:05:41 +03:00
def _vg_lv_create_common_cmd ( create_options , size_bytes , thin_pool ) :
2016-02-18 02:53:35 +03:00
cmd = [ ' lvcreate ' ]
cmd . extend ( options_to_cli_args ( create_options ) )
if not thin_pool :
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --size ' , ' %d B ' % size_bytes ] )
2016-02-18 02:53:35 +03:00
else :
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --thin ' , ' --size ' , ' %d B ' % size_bytes ] )
2017-04-22 08:03:46 +03:00
cmd . extend ( [ ' --yes ' ] )
2017-02-02 04:05:41 +03:00
return cmd
def vg_lv_create_linear ( vg_name , create_options , name , size_bytes , thin_pool ) :
cmd = _vg_lv_create_common_cmd ( create_options , size_bytes , thin_pool )
2016-02-18 02:53:35 +03:00
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 ) :
2017-02-02 04:05:41 +03:00
cmd = _vg_lv_create_common_cmd ( create_options , size_bytes , thin_pool )
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --stripes ' , str ( int ( num_stripes ) ) ] )
2016-02-18 02:53:35 +03:00
if stripe_size_kb != 0 :
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --stripesize ' , str ( int ( stripe_size_kb ) ) ] )
2016-02-18 02:53:35 +03:00
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 ] )
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --size ' , ' %d B ' % size_bytes ] )
2016-02-18 02:53:35 +03:00
if num_stripes != 0 :
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --stripes ' , str ( int ( num_stripes ) ) ] )
2016-02-18 02:53:35 +03:00
if stripe_size_kb != 0 :
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --stripesize ' , str ( int ( stripe_size_kb ) ) ] )
2016-02-18 02:53:35 +03:00
2017-04-22 08:03:46 +03:00
cmd . extend ( [ ' --name ' , name , vg_name , ' --yes ' ] )
2016-02-18 02:53:35 +03:00
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 )
2016-11-30 00:01:41 +03:00
def vg_lv_create_mirror (
vg_name , create_options , name , size_bytes , num_copies ) :
2016-02-18 02:53:35 +03:00
cmd = [ ' lvcreate ' ]
cmd . extend ( options_to_cli_args ( create_options ) )
cmd . extend ( [ ' --type ' , ' mirror ' ] )
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --mirrors ' , str ( int ( num_copies ) ) ] )
cmd . extend ( [ ' --size ' , ' %d B ' % size_bytes ] )
2017-04-22 08:03:46 +03:00
cmd . extend ( [ ' --name ' , name , vg_name , ' --yes ' ] )
2016-02-18 02:53:35 +03:00
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 )
2019-10-08 00:58:10 +03:00
def vg_create_vdo_pool_lv_and_lv ( vg_name , pool_name , lv_name , data_size ,
virtual_size , create_options ) :
cmd = [ ' lvcreate ' ]
cmd . extend ( options_to_cli_args ( create_options ) )
cmd . extend ( [ ' -y ' , ' --type ' , ' vdo ' , ' -n ' , lv_name ,
' -L ' , ' %d B ' % data_size , ' -V ' , ' %d B ' % virtual_size ,
" %s / %s " % ( vg_name , pool_name ) ] )
return call ( cmd )
2020-01-06 13:28:41 +03:00
def vg_create_vdo_pool ( pool_full_name , lv_name , virtual_size , create_options ) :
cmd = [ ' lvconvert ' ]
cmd . extend ( options_to_cli_args ( create_options ) )
cmd . extend ( [ ' --type ' , ' vdo-pool ' , ' -n ' , lv_name , ' --force ' , ' -y ' ,
' -V ' , ' %d B ' % virtual_size , pool_full_name ] )
return call ( cmd )
2016-02-18 02:53:35 +03:00
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- %d B " % ( - size_change ) )
else :
cmd . append ( " -L+ %d B " % ( 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 ) )
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --virtualsize ' , ' %d B ' % size_bytes , ' -T ' ] )
2017-04-22 08:03:46 +03:00
cmd . extend ( [ ' --name ' , name , lv_full_name , ' --yes ' ] )
2016-02-18 02:53:35 +03:00
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 ) )
2016-07-28 02:43:27 +03:00
cmd . extend ( [ ' -y ' , ' --type ' , ' cache ' , ' --cachepool ' ,
2016-02-18 02:53:35 +03:00
cache_pool_full_name , lv_full_name ] )
return call ( cmd )
2020-07-01 14:27:46 +03:00
def lv_writecache_lv ( cache_lv_full_name , lv_full_name , cache_options ) :
# lvconvert --type writecache --cachevol VG/CacheLV VG/OriginLV
cmd = [ ' lvconvert ' ]
cmd . extend ( options_to_cli_args ( cache_options ) )
cmd . extend ( [ ' -y ' , ' --type ' , ' writecache ' , ' --cachevol ' ,
cache_lv_full_name , lv_full_name ] )
return call ( cmd )
2016-02-18 02:53:35 +03:00
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 )
2019-12-27 17:29:15 +03:00
def lv_vdo_compression ( lv_path , enable , comp_options ) :
cmd = [ ' lvchange ' , ' --compression ' ]
if enable :
cmd . append ( ' y ' )
else :
cmd . append ( ' n ' )
cmd . extend ( options_to_cli_args ( comp_options ) )
cmd . append ( lv_path )
return call ( cmd )
def lv_vdo_deduplication ( lv_path , enable , dedup_options ) :
cmd = [ ' lvchange ' , ' --deduplication ' ]
if enable :
cmd . append ( ' y ' )
else :
cmd . append ( ' n ' )
cmd . extend ( options_to_cli_args ( dedup_options ) )
cmd . append ( lv_path )
return call ( cmd )
2016-06-03 21:18:21 +03:00
def supports_json ( ) :
cmd = [ ' help ' ]
rc , out , err = call ( cmd )
if rc == 0 :
2016-08-25 02:29:35 +03:00
if cfg . SHELL_IN_USE :
2016-06-03 21:18:21 +03:00
return True
2016-08-12 23:23:05 +03:00
else :
if ' fullreport ' in err :
return True
2016-06-03 21:18:21 +03:00
return False
2019-10-08 00:58:10 +03:00
def supports_vdo ( ) :
cmd = [ ' segtypes ' ]
rc , out , err = call ( cmd )
if rc == 0 :
if " vdo " in out :
log_debug ( " We have VDO support " )
return True
return False
2016-06-03 21:18:21 +03:00
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 ' ,
2016-09-28 19:18:10 +03:00
' vg_uuid ' , ' pv_missing ' ]
2016-06-03 21:18:21 +03:00
2016-06-28 01:07:20 +03:00
pv_seg_columns = [ ' pvseg_start ' , ' pvseg_size ' , ' segtype ' ,
2016-06-28 20:45:28 +03:00
' pv_uuid ' , ' lv_uuid ' , ' pv_name ' ]
2016-06-03 21:18:21 +03:00
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 ' ,
2016-10-05 21:59:38 +03:00
' metadata_lv ' , ' lv_parent ' , ' lv_role ' , ' lv_layout ' ,
' snap_percent ' , ' metadata_percent ' , ' copy_percent ' ,
2016-10-05 23:28:42 +03:00
' sync_percent ' , ' lv_metadata_size ' , ' move_pv ' , ' move_pv_uuid ' ]
2016-06-03 21:18:21 +03:00
lv_seg_columns = [ ' seg_pe_ranges ' , ' segtype ' , ' lv_uuid ' ]
2019-10-09 15:49:58 +03:00
if cfg . vdo_support :
lv_columns . extend (
[ ' vdo_operating_mode ' , ' vdo_compression_state ' , ' vdo_index_state ' ,
' vdo_used_size ' , ' vdo_saving_percent ' ]
)
lv_seg_columns . extend (
[ ' vdo_compression ' , ' vdo_deduplication ' ,
' vdo_use_metadata_hints ' , ' vdo_minimum_io_size ' ,
' vdo_block_map_cache_size ' , ' vdo_block_map_era_length ' ,
' vdo_use_sparse_index ' , ' vdo_index_memory_size ' ,
' vdo_slab_size ' , ' vdo_ack_threads ' , ' vdo_bio_threads ' ,
' vdo_bio_rotation ' , ' vdo_cpu_threads ' , ' vdo_hash_zone_threads ' ,
' vdo_logical_threads ' , ' vdo_physical_threads ' ,
' vdo_max_discard ' , ' vdo_write_policy ' , ' vdo_header_size ' ] )
2016-06-03 21:18:21 +03:00
cmd = _dc ( ' fullreport ' , [
2016-06-04 00:52:17 +03:00
' -a ' , # Need hidden too
2016-06-28 01:04:44 +03:00
' --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 ) ,
2022-09-14 21:11:47 +03:00
' --configreport ' , ' pvseg ' , ' -o ' , ' , ' . join ( pv_seg_columns )
2016-06-03 21:18:21 +03:00
] )
2022-09-09 18:03:35 +03:00
# We are running the fullreport command, we will ask lvm to output the debug
# data, so we can have the required information for lvm to debug the fullreport failures.
fn = cfg . lvmdebug . setup ( )
add_config_option ( cmd , " --config " , " log { level=7 file= %s syslog=0} " % fn )
2016-06-03 21:18:21 +03:00
rc , out , err = call ( cmd )
2018-12-18 18:51:50 +03:00
# When we have an exported vg the exit code of lvs or fullreport will be 5
if rc == 0 or rc == 5 :
2022-08-31 23:08:09 +03:00
# If the 'call' implementation is lvmshell, the out is a dictionary as lvmshell has to
# parse the output to get the exit value. When doing fork & exec, out is a string
# representing the JSON. TODO: Make this consistent between implementations.
2016-08-25 02:29:35 +03:00
if cfg . SHELL_IN_USE :
2016-08-12 23:23:05 +03:00
assert ( type ( out ) == dict )
return out
else :
2021-06-15 06:04:09 +03:00
try :
return json . loads ( out )
except json . decoder . JSONDecodeError as joe :
log_error ( " JSONDecodeError %s , \n JSON= \n %s \n " %
( str ( joe ) , out ) )
2022-08-31 23:08:09 +03:00
raise LvmBug ( " ' fullreport ' returned invalid JSON " )
2021-06-15 06:04:09 +03:00
2022-08-31 19:20:49 +03:00
raise LvmBug ( " ' fullreport ' exited with code ' %d ' " % rc )
2016-06-03 21:18:21 +03:00
2016-02-18 02:53:35 +03:00
def pv_resize ( device , size_bytes , create_options ) :
cmd = [ ' pvresize ' ]
cmd . extend ( options_to_cli_args ( create_options ) )
if size_bytes != 0 :
2019-08-26 15:35:51 +03:00
cmd . extend ( [ ' --yes ' , ' --setphysicalvolumesize ' , ' %d B ' % size_bytes ] )
2016-02-18 02:53:35 +03:00
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 missing :
cmd . append ( ' --removemissing ' )
2017-06-02 20:22:24 +03:00
elif len ( pv_devices ) == 0 :
cmd . append ( ' --all ' )
2016-02-18 02:53:35 +03:00
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 ) :
2019-08-26 15:35:51 +03:00
return _vg_value_set ( vg_name , [ ' --maxphysicalvolumes ' , str ( int ( number ) ) ] ,
2016-02-18 02:53:35 +03:00
max_options )
def vg_max_lv ( vg_name , number , max_options ) :
2019-08-26 15:35:51 +03:00
return _vg_value_set ( vg_name , [ ' -l ' , str ( int ( number ) ) ] , max_options )
2016-02-18 02:53:35 +03:00
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 )
2019-10-11 00:49:09 +03:00
cmd . append ( " -y " )
2016-02-18 02:53:35 +03:00
cmd . append ( name )
return call ( cmd )
if __name__ == ' __main__ ' :
2022-06-07 16:20:06 +03:00
# Leave this for future debug as needed
pass