summaryrefslogtreecommitdiff
path: root/qpid/tools/src/py/qls/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/tools/src/py/qls/utils.py')
-rw-r--r--qpid/tools/src/py/qls/utils.py206
1 files changed, 206 insertions, 0 deletions
diff --git a/qpid/tools/src/py/qls/utils.py b/qpid/tools/src/py/qls/utils.py
new file mode 100644
index 0000000000..758dc446c0
--- /dev/null
+++ b/qpid/tools/src/py/qls/utils.py
@@ -0,0 +1,206 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+"""
+Module: qls.utils
+
+Contains helper functions for qpid_qls_analyze.
+"""
+
+import os
+import qls.jrnl
+import stat
+import string
+import struct
+import subprocess
+import zlib
+
+DEFAULT_DBLK_SIZE = 128
+DEFAULT_SBLK_SIZE = 4096 # 32 dblks
+DEFAULT_SBLK_SIZE_KB = DEFAULT_SBLK_SIZE / 1024
+DEFAULT_RECORD_VERSION = 2
+DEFAULT_HEADER_SIZE_SBLKS = 1
+
+def adler32(data):
+ """return the adler32 checksum of data"""
+ return zlib.adler32(data) & 0xffffffff
+
+def create_record(magic, uflags, journal_file, record_id, dequeue_record_id, xid, data):
+ """Helper function to construct a record with xid, data (where applicable) and consistent tail with checksum"""
+ record_class = qls.jrnl.CLASSES.get(magic[-1])
+ record = record_class(0, magic, DEFAULT_RECORD_VERSION, uflags, journal_file.file_header.serial, record_id)
+ xid_length = len(xid) if xid is not None else 0
+ if isinstance(record, qls.jrnl.EnqueueRecord):
+ data_length = len(data) if data is not None else 0
+ record.init(None, xid_length, data_length)
+ elif isinstance(record, qls.jrnl.DequeueRecord):
+ record.init(None, dequeue_record_id, xid_length)
+ elif isinstance(record, qls.jrnl.TransactionRecord):
+ record.init(None, xid_length)
+ else:
+ raise qls.err.InvalidClassError(record.__class__.__name__)
+ if xid is not None:
+ record.xid = xid
+ record.xid_complete = True
+ if data is not None:
+ record.data = data
+ record.data_complete = True
+ record.record_tail = _mk_record_tail(record)
+ return record
+
+def efp_directory_size(directory_name):
+ """"Decode the directory name in the format NNNk to a numeric size, where NNN is a number string"""
+ try:
+ if directory_name[-1] == 'k':
+ return int(directory_name[:-1])
+ except ValueError:
+ pass
+ return 0
+
+def format_data(data, data_size=None, show_data_flag=True):
+ """Format binary data for printing"""
+ return _format_binary(data, data_size, show_data_flag, 'data', qls.err.DataSizeError, False)
+
+def format_xid(xid, xid_size=None, show_xid_flag=True):
+ """Format binary XID for printing"""
+ return _format_binary(xid, xid_size, show_xid_flag, 'xid', qls.err.XidSizeError, True)
+
+def get_avail_disk_space(path):
+ df_proc = subprocess.Popen(["df", path], stdout=subprocess.PIPE)
+ output = df_proc.communicate()[0]
+ return int(output.split('\n')[1].split()[3])
+
+def has_write_permission(path):
+ stat_info = os.stat(path)
+ return bool(stat_info.st_mode & stat.S_IRGRP)
+
+def inv_str(in_string):
+ """Perform a binary 1's compliment (invert all bits) on a binary string"""
+ istr = ''
+ for index in range(0, len(in_string)):
+ istr += chr(~ord(in_string[index]) & 0xff)
+ return istr
+
+def load(file_handle, klass):
+ """Load a record of class klass from a file"""
+ args = load_args(file_handle, klass)
+ subclass = klass.discriminate(args)
+ result = subclass(*args) # create instance of record
+ if subclass != klass:
+ result.init(*load_args(file_handle, subclass))
+ return result
+
+def load_args(file_handle, klass):
+ """Load the arguments from class klass"""
+ size = struct.calcsize(klass.FORMAT)
+ foffs = file_handle.tell(),
+ fbin = file_handle.read(size)
+ if len(fbin) != size:
+ raise qls.err.UnexpectedEndOfFileError(len(fbin), size, foffs, file_handle.name)
+ return foffs + struct.unpack(klass.FORMAT, fbin)
+
+def load_data(file_handle, element, element_size):
+ """Read element_size bytes of binary data from file_handle into element"""
+ if element_size == 0:
+ return element, True
+ if element is None:
+ element = file_handle.read(element_size)
+ else:
+ read_size = element_size - len(element)
+ element += file_handle.read(read_size)
+ return element, len(element) == element_size
+
+def skip(file_handle, boundary):
+ """Read and discard disk bytes until the next multiple of boundary"""
+ if not file_handle.closed:
+ file_handle.read(_rem_bytes_in_block(file_handle, boundary))
+
+#--- protected functions ---
+
+def _format_binary(bin_str, bin_size, show_bin_flag, prefix, err_class, hex_num_flag):
+ """Format binary XID for printing"""
+ if bin_str is None and bin_size is not None:
+ if bin_size > 0:
+ raise err_class(bin_size, len(bin_str), bin_str)
+ return ''
+ if bin_size is None:
+ bin_size = len(bin_str)
+ elif bin_size != len(bin_str):
+ raise err_class(bin_size, len(bin_str), bin_str)
+ out_str = '%s(%d)' % (prefix, bin_size)
+ if show_bin_flag:
+ if _is_printable(bin_str):
+ binstr = '"%s"' % _split_str(bin_str)
+ elif hex_num_flag:
+ binstr = '0x%s' % _str_to_hex_num(bin_str)
+ else:
+ binstr = _hex_split_str(bin_str)
+ out_str += '=%s' % binstr
+ return out_str
+
+def _hex_str(in_str, begin, end):
+ """Return a binary string as a hex string"""
+ hstr = ''
+ for index in range(begin, end):
+ if _is_printable(in_str[index]):
+ hstr += in_str[index]
+ else:
+ hstr += '\\%02x' % ord(in_str[index])
+ return hstr
+
+def _hex_split_str(in_str, split_size = 50):
+ """Split a hex string into two parts separated by an ellipsis"""
+ if len(in_str) <= split_size:
+ return _hex_str(in_str, 0, len(in_str))
+ return _hex_str(in_str, 0, 10) + ' ... ' + _hex_str(in_str, len(in_str)-10, len(in_str))
+ #return ''.join(x.encode('hex') for x in reversed(in_str))
+
+def _is_printable(in_str):
+ """Return True if in_str in printable; False otherwise."""
+ for this_char in in_str:
+ if this_char not in string.letters and this_char not in string.digits and this_char not in string.punctuation:
+ return False
+ return True
+
+def _mk_record_tail(record):
+ record_tail = qls.jrnl.RecordTail(None)
+ record_tail.xmagic = inv_str(record.magic)
+ record_tail.checksum = adler32(record.checksum_encode())
+ record_tail.serial = record.serial
+ record_tail.record_id = record.record_id
+ return record_tail
+
+def _rem_bytes_in_block(file_handle, block_size):
+ """Return the remaining bytes in a block"""
+ foffs = file_handle.tell()
+ return (_size_in_blocks(foffs, block_size) * block_size) - foffs
+
+def _size_in_blocks(size, block_size):
+ """Return the size in terms of data blocks"""
+ return int((size + block_size - 1) / block_size)
+
+def _split_str(in_str, split_size = 50):
+ """Split a string into two parts separated by an ellipsis if it is longer than split_size"""
+ if len(in_str) < split_size:
+ return in_str
+ return in_str[:25] + ' ... ' + in_str[-25:]
+
+def _str_to_hex_num(in_str):
+ """Turn a string into a hex number representation, little endian assumed (ie LSB is first, MSB is last)"""
+ return ''.join(x.encode('hex') for x in reversed(in_str))