diff options
Diffstat (limited to 'qpid/cpp/management/python/lib/qpidstore/jrnl.py')
-rw-r--r-- | qpid/cpp/management/python/lib/qpidstore/jrnl.py | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/qpid/cpp/management/python/lib/qpidstore/jrnl.py b/qpid/cpp/management/python/lib/qpidstore/jrnl.py new file mode 100644 index 0000000000..7c4d6de4a9 --- /dev/null +++ b/qpid/cpp/management/python/lib/qpidstore/jrnl.py @@ -0,0 +1,794 @@ +# +# 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. +# + +import jerr +import os.path, sys, xml.parsers.expat +from struct import pack, unpack, calcsize +from time import gmtime, strftime + +# TODO: Get rid of these! Use jinf instance instead +DBLK_SIZE = 128 +SBLK_SIZE = 4 * DBLK_SIZE + +# TODO - this is messy - find a better way to handle this +# This is a global, but is set directly by the calling program +JRNL_FILE_SIZE = None + +#== class Utils ====================================================================== + +class Utils(object): + """Class containing utility functions for dealing with the journal""" + + __printchars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{\|}~ " + + # The @staticmethod declarations are not supported in RHEL4 (python 2.3.x) + # When RHEL4 support ends, restore these declarations and remove the older + # staticmethod() declaration. + + #@staticmethod + def format_data(dsize, data): + """Format binary data for printing""" + if data == None: + return "" + if Utils._is_printable(data): + datastr = Utils._split_str(data) + else: + datastr = Utils._hex_split_str(data) + if dsize != len(data): + raise jerr.DataSizeError(dsize, len(data), datastr) + return "data(%d)=\"%s\" " % (dsize, datastr) + format_data = staticmethod(format_data) + + #@staticmethod + def format_xid(xid, xidsize=None): + """Format binary XID for printing""" + if xid == None and xidsize != None: + if xidsize > 0: + raise jerr.XidSizeError(xidsize, 0, None) + return "" + if Utils._is_printable(xid): + xidstr = Utils._split_str(xid) + else: + xidstr = Utils._hex_split_str(xid) + if xidsize == None: + xidsize = len(xid) + elif xidsize != len(xid): + raise jerr.XidSizeError(xidsize, len(xid), xidstr) + return "xid(%d)=\"%s\" " % (xidsize, xidstr) + format_xid = staticmethod(format_xid) + + #@staticmethod + def inv_str(string): + """Perform a binary 1's compliment (invert all bits) on a binary string""" + istr = "" + for index in range(0, len(string)): + istr += chr(~ord(string[index]) & 0xff) + return istr + inv_str = staticmethod(inv_str) + + #@staticmethod + def load(fhandle, klass): + """Load a record of class klass from a file""" + args = Utils._load_args(fhandle, klass) + subclass = klass.discriminate(args) + result = subclass(*args) # create instance of record + if subclass != klass: + result.init(fhandle, *Utils._load_args(fhandle, subclass)) + result.skip(fhandle) + return result + load = staticmethod(load) + + #@staticmethod + def load_file_data(fhandle, size, data): + """Load the data portion of a message from file""" + if size == 0: + return (data, True) + if data == None: + loaded = 0 + else: + loaded = len(data) + foverflow = fhandle.tell() + size - loaded > JRNL_FILE_SIZE + if foverflow: + rsize = JRNL_FILE_SIZE - fhandle.tell() + else: + rsize = size - loaded + fbin = fhandle.read(rsize) + if data == None: + data = unpack("%ds" % (rsize), fbin)[0] + else: + data = data + unpack("%ds" % (rsize), fbin)[0] + return (data, not foverflow) + load_file_data = staticmethod(load_file_data) + + #@staticmethod + def rem_bytes_in_blk(fhandle, blk_size): + """Return the remaining bytes in a block""" + foffs = fhandle.tell() + return Utils.size_in_bytes_to_blk(foffs, blk_size) - foffs + rem_bytes_in_blk = staticmethod(rem_bytes_in_blk) + + #@staticmethod + def size_in_blks(size, blk_size): + """Return the size in terms of data blocks""" + return int((size + blk_size - 1) / blk_size) + size_in_blks = staticmethod(size_in_blks) + + #@staticmethod + def size_in_bytes_to_blk(size, blk_size): + """Return the bytes remaining until the next block boundary""" + return Utils.size_in_blks(size, blk_size) * blk_size + size_in_bytes_to_blk = staticmethod(size_in_bytes_to_blk) + + #@staticmethod + 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 Utils._hex_str(in_str, 0, len(in_str)) +# if len(in_str) > split_size + 25: +# return Utils._hex_str(in_str, 0, 10) + " ... " + Utils._hex_str(in_str, 55, 65) + " ... " + \ +# Utils._hex_str(in_str, len(in_str)-10, len(in_str)) + return Utils._hex_str(in_str, 0, 10) + " ... " + Utils._hex_str(in_str, len(in_str)-10, len(in_str)) + _hex_split_str = staticmethod(_hex_split_str) + + #@staticmethod + def _hex_str(in_str, begin, end): + """Return a binary string as a hex string""" + hstr = "" + for index in range(begin, end): + if Utils._is_printable(in_str[index]): + hstr += in_str[index] + else: + hstr += "\\%02x" % ord(in_str[index]) + return hstr + _hex_str = staticmethod(_hex_str) + + #@staticmethod + def _is_printable(in_str): + """Return True if in_str in printable; False otherwise.""" + return in_str.strip(Utils.__printchars) == "" + _is_printable = staticmethod(_is_printable) + + #@staticmethod + def _load_args(fhandle, klass): + """Load the arguments from class klass""" + size = calcsize(klass.FORMAT) + foffs = fhandle.tell(), + fbin = fhandle.read(size) + if len(fbin) != size: + raise jerr.UnexpectedEndOfFileError(size, len(fbin)) + return foffs + unpack(klass.FORMAT, fbin) + _load_args = staticmethod(_load_args) + + #@staticmethod + 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:] + _split_str = staticmethod(_split_str) + + +#== class Hdr ================================================================= + +class Hdr: + """Class representing the journal header records""" + + FORMAT = "=4sBBHQ" + HDR_VER = 1 + OWI_MASK = 0x01 + BIG_ENDIAN = sys.byteorder == "big" + REC_BOUNDARY = DBLK_SIZE + + def __init__(self, foffs, magic, ver, endn, flags, rid): + """Constructor""" +# Sizeable.__init__(self) + self.foffs = foffs + self.magic = magic + self.ver = ver + self.endn = endn + self.flags = flags + self.rid = long(rid) + + def __str__(self): + """Return string representation of this header""" + if self.empty(): + return "0x%08x: <empty>" % (self.foffs) + if self.magic[-1] == "x": + return "0x%08x: [\"%s\"]" % (self.foffs, self.magic) + if self.magic[-1] in ["a", "c", "d", "e", "f", "x"]: + return "0x%08x: [\"%s\" v=%d e=%d f=0x%04x rid=0x%x]" % (self.foffs, self.magic, self.ver, self.endn, + self.flags, self.rid) + return "0x%08x: <error, unknown magic \"%s\" (possible overwrite boundary?)>" % (self.foffs, self.magic) + + #@staticmethod + def discriminate(args): + """Use the last char in the header magic to determine the header type""" + return _CLASSES.get(args[1][-1], Hdr) + discriminate = staticmethod(discriminate) + + def empty(self): + """Return True if this record is empty (ie has a magic of 0x0000""" + return self.magic == "\x00"*4 + + def encode(self): + """Encode the header into a binary string""" + return pack(Hdr.FORMAT, self.magic, self.ver, self.endn, self.flags, self.rid) + + def owi(self): + """Return the OWI (overwrite indicator) for this header""" + return self.flags & self.OWI_MASK != 0 + + def skip(self, fhandle): + """Read and discard the remainder of this record""" + fhandle.read(Utils.rem_bytes_in_blk(fhandle, self.REC_BOUNDARY)) + + def check(self): + """Check that this record is valid""" + if self.empty() or self.magic[:3] != "RHM" or self.magic[3] not in ["a", "c", "d", "e", "f", "x"]: + return True + if self.magic[-1] != "x": + if self.ver != self.HDR_VER: + raise jerr.InvalidHeaderVersionError(self.HDR_VER, self.ver) + if bool(self.endn) != self.BIG_ENDIAN: + raise jerr.EndianMismatchError(self.BIG_ENDIAN) + return False + + +#== class FileHdr ============================================================= + +class FileHdr(Hdr): + """Class for file headers, found at the beginning of journal files""" + + FORMAT = "=2H4x3Q" + REC_BOUNDARY = SBLK_SIZE + + def __str__(self): + """Return a string representation of the this FileHdr instance""" + return "%s fid=%d lid=%d fro=0x%08x t=%s" % (Hdr.__str__(self), self.fid, self.lid, self.fro, + self.timestamp_str()) + + def encode(self): + """Encode this class into a binary string""" + return Hdr.encode(self) + pack(FileHdr.FORMAT, self.fid, self.lid, self.fro, self.time_sec, self.time_ns) + + def init(self, fhandle, foffs, fid, lid, fro, time_sec, time_ns): + """Initialize this instance to known values""" + self.fid = fid + self.lid = lid + self.fro = fro + self.time_sec = time_sec + self.time_ns = time_ns + + def timestamp(self): + """Get the timestamp of this record as a tuple (secs, nsecs)""" + return (self.time_sec, self.time_ns) + + def timestamp_str(self): + """Get the timestamp of this record in string format""" + time = gmtime(self.time_sec) + fstr = "%%a %%b %%d %%H:%%M:%%S.%09d %%Y" % (self.time_ns) + return strftime(fstr, time) + + +#== class DeqRec ============================================================== + +class DeqRec(Hdr): + """Class for a dequeue record""" + + FORMAT = "=QQ" + + def __str__(self): + """Return a string representation of the this DeqRec instance""" + return "%s %sdrid=0x%x" % (Hdr.__str__(self), Utils.format_xid(self.xid, self.xidsize), self.deq_rid) + + def init(self, fhandle, foffs, deq_rid, xidsize): + """Initialize this instance to known values""" + self.deq_rid = deq_rid + self.xidsize = xidsize + self.xid = None + self.deq_tail = None + self.xid_complete = False + self.tail_complete = False + self.tail_bin = None + self.tail_offs = 0 + self.load(fhandle) + + def encode(self): + """Encode this class into a binary string""" + buf = Hdr.encode(self) + pack(DeqRec.FORMAT, self.deq_rid, self.xidsize) + if self.xidsize > 0: + fmt = "%ds" % (self.xidsize) + buf += pack(fmt, self.xid) + buf += self.deq_tail.encode() + return buf + + def load(self, fhandle): + """Load the remainder of this record (after the header has been loaded""" + if self.xidsize == 0: + self.xid_complete = True + self.tail_complete = True + else: + if not self.xid_complete: + (self.xid, self.xid_complete) = Utils.load_file_data(fhandle, self.xidsize, self.xid) + if self.xid_complete and not self.tail_complete: + ret = Utils.load_file_data(fhandle, calcsize(RecTail.FORMAT), self.tail_bin) + self.tail_bin = ret[0] + if ret[1]: + self.deq_tail = RecTail(self.tail_offs, *unpack(RecTail.FORMAT, self.tail_bin)) + magic_err = self.deq_tail.magic_inv != Utils.inv_str(self.magic) + rid_err = self.deq_tail.rid != self.rid + if magic_err or rid_err: + raise jerr.InvalidRecordTailError(magic_err, rid_err, self) + self.skip(fhandle) + self.tail_complete = ret[1] + return self.complete() + + def complete(self): + """Returns True if the entire record is loaded, False otherwise""" + return self.xid_complete and self.tail_complete + + +#== class TxnRec ============================================================== + +class TxnRec(Hdr): + """Class for a transaction commit/abort record""" + + FORMAT = "=Q" + + def __str__(self): + """Return a string representation of the this TxnRec instance""" + return "%s %s" % (Hdr.__str__(self), Utils.format_xid(self.xid, self.xidsize)) + + def init(self, fhandle, foffs, xidsize): + """Initialize this instance to known values""" + self.xidsize = xidsize + self.xid = None + self.tx_tail = None + self.xid_complete = False + self.tail_complete = False + self.tail_bin = None + self.tail_offs = 0 + self.load(fhandle) + + def encode(self): + """Encode this class into a binary string""" + return Hdr.encode(self) + pack(TxnRec.FORMAT, self.xidsize) + pack("%ds" % self.xidsize, self.xid) + \ + self.tx_tail.encode() + + def load(self, fhandle): + """Load the remainder of this record (after the header has been loaded""" + if not self.xid_complete: + ret = Utils.load_file_data(fhandle, self.xidsize, self.xid) + self.xid = ret[0] + self.xid_complete = ret[1] + if self.xid_complete and not self.tail_complete: + ret = Utils.load_file_data(fhandle, calcsize(RecTail.FORMAT), self.tail_bin) + self.tail_bin = ret[0] + if ret[1]: + self.tx_tail = RecTail(self.tail_offs, *unpack(RecTail.FORMAT, self.tail_bin)) + magic_err = self.tx_tail.magic_inv != Utils.inv_str(self.magic) + rid_err = self.tx_tail.rid != self.rid + if magic_err or rid_err: + raise jerr.InvalidRecordTailError(magic_err, rid_err, self) + self.skip(fhandle) + self.tail_complete = ret[1] + return self.complete() + + def complete(self): + """Returns True if the entire record is loaded, False otherwise""" + return self.xid_complete and self.tail_complete + + +#== class EnqRec ============================================================== + +class EnqRec(Hdr): + """Class for a enqueue record""" + + FORMAT = "=QQ" + TRANSIENT_MASK = 0x10 + EXTERN_MASK = 0x20 + + def __str__(self): + """Return a string representation of the this EnqRec instance""" + return "%s %s%s %s %s" % (Hdr.__str__(self), Utils.format_xid(self.xid, self.xidsize), + Utils.format_data(self.dsize, self.data), self.enq_tail, self.print_flags()) + + def encode(self): + """Encode this class into a binary string""" + buf = Hdr.encode(self) + pack(EnqRec.FORMAT, self.xidsize, self.dsize) + if self.xidsize > 0: + buf += pack("%ds" % self.xidsize, self.xid) + if self.dsize > 0: + buf += pack("%ds" % self.dsize, self.data) + if self.xidsize > 0 or self.dsize > 0: + buf += self.enq_tail.encode() + return buf + + def init(self, fhandle, foffs, xidsize, dsize): + """Initialize this instance to known values""" + self.xidsize = xidsize + self.dsize = dsize + self.transient = self.flags & self.TRANSIENT_MASK > 0 + self.extern = self.flags & self.EXTERN_MASK > 0 + self.xid = None + self.data = None + self.enq_tail = None + self.xid_complete = False + self.data_complete = False + self.tail_complete = False + self.tail_bin = None + self.tail_offs = 0 + self.load(fhandle) + + def load(self, fhandle): + """Load the remainder of this record (after the header has been loaded""" + if not self.xid_complete: + ret = Utils.load_file_data(fhandle, self.xidsize, self.xid) + self.xid = ret[0] + self.xid_complete = ret[1] + if self.xid_complete and not self.data_complete: + if self.extern: + self.data_complete = True + else: + ret = Utils.load_file_data(fhandle, self.dsize, self.data) + self.data = ret[0] + self.data_complete = ret[1] + if self.data_complete and not self.tail_complete: + ret = Utils.load_file_data(fhandle, calcsize(RecTail.FORMAT), self.tail_bin) + self.tail_bin = ret[0] + if ret[1]: + self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.FORMAT, self.tail_bin)) + magic_err = self.enq_tail.magic_inv != Utils.inv_str(self.magic) + rid_err = self.enq_tail.rid != self.rid + if magic_err or rid_err: + raise jerr.InvalidRecordTailError(magic_err, rid_err, self) + self.skip(fhandle) + self.tail_complete = ret[1] + return self.complete() + + def complete(self): + """Returns True if the entire record is loaded, False otherwise""" + return self.xid_complete and self.data_complete and self.tail_complete + + def print_flags(self): + """Utility function to decode the flags field in the header and print a string representation""" + fstr = "" + if self.transient: + fstr = "*TRANSIENT" + if self.extern: + if len(fstr) > 0: + fstr += ",EXTERNAL" + else: + fstr = "*EXTERNAL" + if len(fstr) > 0: + fstr += "*" + return fstr + + +#== class RecTail ============================================================= + +class RecTail: + """Class for a record tail - for all records where either an XID or data separate the header from the end of the + record""" + + FORMAT = "=4sQ" + + def __init__(self, foffs, magic_inv, rid): + """Initialize this instance to known values""" + self.foffs = foffs + self.magic_inv = magic_inv + self.rid = long(rid) + + def __str__(self): + """Return a string representation of the this RecTail instance""" + magic = Utils.inv_str(self.magic_inv) + return "[\"%s\" rid=0x%x]" % (magic, self.rid) + + def encode(self): + """Encode this class into a binary string""" + return pack(RecTail.FORMAT, self.magic_inv, self.rid) + + +#== class JrnlInfo ============================================================ + +class JrnlInfo(object): + """ + This object reads and writes journal information files (<basename>.jinf). Methods are provided + to read a file, query its properties and reset just those properties necessary for normalizing + and resizing a journal. + + Normalizing: resetting the directory and/or base filename to different values. This is necessary + if a set of journal files is copied from one location to another before being restored, as the + value of the path in the file no longer matches the actual path. + + Resizing: If the journal geometry parameters (size and number of journal files) changes, then the + .jinf file must reflect these changes, as this file is the source of information for journal + recovery. + + NOTE: Data size vs File size: There are methods which return the data size and file size of the + journal files. + + +-------------+--------------------/ /----------+ + | File header | File data | + +-------------+--------------------/ /----------+ + | | | + | |<---------- Data size ---------->| + |<------------------ File Size ---------------->| + + Data size: The size of the data content of the journal, ie that part which stores the data records. + + File size: The actual disk size of the journal including data and the file header which precedes the + data. + + The file header is fixed to 1 sblk, so file size = jrnl size + sblk size. + """ + + def __init__(self, jdir, bfn = "JournalData"): + """Constructor""" + self.__jdir = jdir + self.__bfn = bfn + self.__jinf_dict = {} + self._read_jinf() + + def __str__(self): + """Create a string containing all of the journal info contained in the jinf file""" + ostr = "Journal info file %s:\n" % os.path.join(self.__jdir, "%s.jinf" % self.__bfn) + for key, val in self.__jinf_dict.iteritems(): + ostr += " %s = %s\n" % (key, val) + return ostr + + def normalize(self, jdir = None, bfn = None): + """Normalize the directory (ie reset the directory path to match the actual current location) for this + jinf file""" + if jdir == None: + self.__jinf_dict["directory"] = self.__jdir + else: + self.__jdir = jdir + self.__jinf_dict["directory"] = jdir + if bfn != None: + self.__bfn = bfn + self.__jinf_dict["base_filename"] = bfn + + def resize(self, num_jrnl_files = None, jrnl_file_size = None): + """Reset the journal size information to allow for resizing the journal""" + if num_jrnl_files != None: + self.__jinf_dict["number_jrnl_files"] = num_jrnl_files + if jrnl_file_size != None: + self.__jinf_dict["jrnl_file_size_sblks"] = jrnl_file_size * self.get_jrnl_dblk_size_bytes() + + def write(self, jdir = None, bfn = None): + """Write the .jinf file""" + self.normalize(jdir, bfn) + if not os.path.exists(self.get_jrnl_dir()): + os.makedirs(self.get_jrnl_dir()) + fhandle = open(os.path.join(self.get_jrnl_dir(), "%s.jinf" % self.get_jrnl_base_name()), "w") + fhandle.write("<?xml version=\"1.0\" ?>\n") + fhandle.write("<jrnl>\n") + fhandle.write(" <journal_version value=\"%d\" />\n" % self.get_jrnl_version()) + fhandle.write(" <journal_id>\n") + fhandle.write(" <id_string value=\"%s\" />\n" % self.get_jrnl_id()) + fhandle.write(" <directory value=\"%s\" />\n" % self.get_jrnl_dir()) + fhandle.write(" <base_filename value=\"%s\" />\n" % self.get_jrnl_base_name()) + fhandle.write(" </journal_id>\n") + fhandle.write(" <creation_time>\n") + fhandle.write(" <seconds value=\"%d\" />\n" % self.get_creation_time()[0]) + fhandle.write(" <nanoseconds value=\"%d\" />\n" % self.get_creation_time()[1]) + fhandle.write(" <string value=\"%s\" />\n" % self.get_creation_time_str()) + fhandle.write(" </creation_time>\n") + fhandle.write(" <journal_file_geometry>\n") + fhandle.write(" <number_jrnl_files value=\"%d\" />\n" % self.get_num_jrnl_files()) + fhandle.write(" <auto_expand value=\"%s\" />\n" % str.lower(str(self.get_auto_expand()))) + fhandle.write(" <jrnl_file_size_sblks value=\"%d\" />\n" % self.get_jrnl_data_size_sblks()) + fhandle.write(" <JRNL_SBLK_SIZE value=\"%d\" />\n" % self.get_jrnl_sblk_size_dblks()) + fhandle.write(" <JRNL_DBLK_SIZE value=\"%d\" />\n" % self.get_jrnl_dblk_size_bytes()) + fhandle.write(" </journal_file_geometry>\n") + fhandle.write(" <cache_geometry>\n") + fhandle.write(" <wcache_pgsize_sblks value=\"%d\" />\n" % self.get_wr_buf_pg_size_sblks()) + fhandle.write(" <wcache_num_pages value=\"%d\" />\n" % self.get_num_wr_buf_pgs()) + fhandle.write(" <JRNL_RMGR_PAGE_SIZE value=\"%d\" />\n" % self.get_rd_buf_pg_size_sblks()) + fhandle.write(" <JRNL_RMGR_PAGES value=\"%d\" />\n" % self.get_num_rd_buf_pgs()) + fhandle.write(" </cache_geometry>\n") + fhandle.write("</jrnl>\n") + fhandle.close() + + # Journal ID + + def get_jrnl_version(self): + """Get the journal version""" + return self.__jinf_dict["journal_version"] + + def get_jrnl_id(self): + """Get the journal id""" + return self.__jinf_dict["id_string"] + + def get_current_dir(self): + """Get the current directory of the store (as opposed to that value saved in the .jinf file)""" + return self.__jdir + + def get_jrnl_dir(self): + """Get the journal directory stored in the .jinf file""" + return self.__jinf_dict["directory"] + + def get_jrnl_base_name(self): + """Get the base filename - that string used to name the journal files <basefilename>-nnnn.jdat and + <basefilename>.jinf""" + return self.__jinf_dict["base_filename"] + + # Journal creation time + + def get_creation_time(self): + """Get journal creation time as a tuple (secs, nsecs)""" + return (self.__jinf_dict["seconds"], self.__jinf_dict["nanoseconds"]) + + def get_creation_time_str(self): + """Get journal creation time as a string""" + return self.__jinf_dict["string"] + + # --- Files and geometry --- + + def get_num_jrnl_files(self): + """Get number of data files in the journal""" + return self.__jinf_dict["number_jrnl_files"] + + def get_auto_expand(self): + """Return True if auto-expand is enabled; False otherwise""" + return self.__jinf_dict["auto_expand"] + + def get_jrnl_sblk_size_dblks(self): + """Get the journal softblock size in dblks""" + return self.__jinf_dict["JRNL_SBLK_SIZE"] + + def get_jrnl_sblk_size_bytes(self): + """Get the journal softblock size in bytes""" + return self.get_jrnl_sblk_size_dblks() * self.get_jrnl_dblk_size_bytes() + + def get_jrnl_dblk_size_bytes(self): + """Get the journal datablock size in bytes""" + return self.__jinf_dict["JRNL_DBLK_SIZE"] + + def get_jrnl_data_size_sblks(self): + """Get the data capacity (excluding the file headers) for one journal file in softblocks""" + return self.__jinf_dict["jrnl_file_size_sblks"] + + def get_jrnl_data_size_dblks(self): + """Get the data capacity (excluding the file headers) for one journal file in datablocks""" + return self.get_jrnl_data_size_sblks() * self.get_jrnl_sblk_size_dblks() + + def get_jrnl_data_size_bytes(self): + """Get the data capacity (excluding the file headers) for one journal file in bytes""" + return self.get_jrnl_data_size_dblks() * self.get_jrnl_dblk_size_bytes() + + def get_jrnl_file_size_sblks(self): + """Get the size of one journal file on disk (including the file headers) in softblocks""" + return self.get_jrnl_data_size_sblks() + 1 + + def get_jrnl_file_size_dblks(self): + """Get the size of one journal file on disk (including the file headers) in datablocks""" + return self.get_jrnl_file_size_sblks() * self.get_jrnl_sblk_size_dblks() + + def get_jrnl_file_size_bytes(self): + """Get the size of one journal file on disk (including the file headers) in bytes""" + return self.get_jrnl_file_size_dblks() * self.get_jrnl_dblk_size_bytes() + + def get_tot_jrnl_data_size_sblks(self): + """Get the size of the entire jouranl's data capacity (excluding the file headers) for all files together in + softblocks""" + return self.get_num_jrnl_files() * self.get_jrnl_data_size_bytes() + + def get_tot_jrnl_data_size_dblks(self): + """Get the size of the entire jouranl's data capacity (excluding the file headers) for all files together in + datablocks""" + return self.get_num_jrnl_files() * self.get_jrnl_data_size_dblks() + + def get_tot_jrnl_data_size_bytes(self): + """Get the size of the entire jouranl's data capacity (excluding the file headers) for all files together in + bytes""" + return self.get_num_jrnl_files() * self.get_jrnl_data_size_bytes() + + # Read and write buffers + + def get_wr_buf_pg_size_sblks(self): + """Get the size of the write buffer pages in softblocks""" + return self.__jinf_dict["wcache_pgsize_sblks"] + + def get_wr_buf_pg_size_dblks(self): + """Get the size of the write buffer pages in datablocks""" + return self.get_wr_buf_pg_size_sblks() * self.get_jrnl_sblk_size_dblks() + + def get_wr_buf_pg_size_bytes(self): + """Get the size of the write buffer pages in bytes""" + return self.get_wr_buf_pg_size_dblks() * self.get_jrnl_dblk_size_bytes() + + def get_num_wr_buf_pgs(self): + """Get the number of write buffer pages""" + return self.__jinf_dict["wcache_num_pages"] + + def get_rd_buf_pg_size_sblks(self): + """Get the size of the read buffer pages in softblocks""" + return self.__jinf_dict["JRNL_RMGR_PAGE_SIZE"] + + def get_rd_buf_pg_size_dblks(self): + """Get the size of the read buffer pages in datablocks""" + return self.get_rd_buf_pg_size_sblks * self.get_jrnl_sblk_size_dblks() + + def get_rd_buf_pg_size_bytes(self): + """Get the size of the read buffer pages in bytes""" + return self.get_rd_buf_pg_size_dblks * self.get_jrnl_dblk_size_bytes() + + def get_num_rd_buf_pgs(self): + """Get the number of read buffer pages""" + return self.__jinf_dict["JRNL_RMGR_PAGES"] + + def _read_jinf(self): + """Read and initialize this instance from an existing jinf file located at the directory named in the + constructor - called by the constructor""" + fhandle = open(os.path.join(self.__jdir, "%s.jinf" % self.__bfn), "r") + parser = xml.parsers.expat.ParserCreate() + parser.StartElementHandler = self._handle_xml_start_elt + parser.CharacterDataHandler = self._handle_xml_char_data + parser.EndElementHandler = self._handle_xml_end_elt + parser.ParseFile(fhandle) + fhandle.close() + + def _handle_xml_start_elt(self, name, attrs): + """Callback for handling XML start elements. Used by the XML parser.""" + # bool values + if name == "auto_expand": + self.__jinf_dict[name] = attrs["value"] == "true" + # long values + elif name == "seconds" or \ + name == "nanoseconds": + self.__jinf_dict[name] = long(attrs["value"]) + # int values + elif name == "journal_version" or \ + name == "number_jrnl_files" or \ + name == "jrnl_file_size_sblks" or \ + name == "JRNL_SBLK_SIZE" or \ + name == "JRNL_DBLK_SIZE" or \ + name == "wcache_pgsize_sblks" or \ + name == "wcache_num_pages" or \ + name == "JRNL_RMGR_PAGE_SIZE" or \ + name == "JRNL_RMGR_PAGES": + self.__jinf_dict[name] = int(attrs["value"]) + # strings + elif "value" in attrs: + self.__jinf_dict[name] = attrs["value"] + + def _handle_xml_char_data(self, data): + """Callback for handling character data (ie within <elt>...</elt>). The jinf file does not use this in its + data. Used by the XML parser.""" + pass + + def _handle_xml_end_elt(self, name): + """Callback for handling XML end elements. Used by XML parser.""" + pass + + +#============================================================================== + +_CLASSES = { + "a": TxnRec, + "c": TxnRec, + "d": DeqRec, + "e": EnqRec, + "f": FileHdr +} + +if __name__ == "__main__": + print "This is a library, and cannot be executed." |