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 _shell_cfg(): global _t_call # noinspection PyBroadException 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 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 dump(self): with cmd_lock: if len(self.queue): log_error("LVM dbus flight recorder START") for c in self.queue: log_error(str(c)) log_error("LVM dbus flight recorder END")
def call_lvm(self, argv, debug=False): rc = 1 error_msg = "" json_result = "" # 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 dump(self): with cmd_lock: if len(self.queue): log_error("LVM dbus flight recorder START") for c in reversed(self.queue): log_error(str(c)) log_error("LVM dbus flight recorder END")
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 call_lvm(self, argv, debug=False): rc = 1 error_msg = "" if self.lvm_shell.poll(): raise Exception( self.lvm_shell.returncode, "Underlying lvm shell process is not present!") argv = add_no_notify(argv) # 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_json, stderr = self._read_until_prompt() # Parse the report to see what happened if 'log' in report_json: if report_json['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, report_json, error_msg
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 _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, line_cb=None, cb_data=None): """ Call an executable and return a tuple of exitcode, stdout, stderr :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 """ # 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) command = add_no_notify(command) process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True, env=os.environ) stdout_text = "" stderr_text = "" stdout_index = 0 make_non_block(process.stdout) make_non_block(process.stderr) while True: 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]) except: st = traceback.format_exc() 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)) pass if debug or process.returncode != 0: _debug_c(command, process.returncode, (stdout_text, stderr_text)) return process.returncode, stdout_text, stderr_text
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'] 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']) 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) # When we have an exported vg the exit code of lvs or fullreport will be 5 if rc == 0 or rc == 5: # 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: try: return json.loads(out) except json.decoder.JSONDecodeError as joe: log_error("JSONDecodeError %s, \n JSON=\n%s\n" % (str(joe), out)) raise joe return None
def exit_shell(self): try: self._write_cmd('exit\n') except Exception as e: log_error(str(e))