예제 #1
0
class BaseInfoItem(object):
    "Base class for all database monitoring objects."
    #: Weak reference to parent :class:`Monitor` instance.
    monitor = None
    def __init__(self,monitor,attributes):
        self.monitor = (type(monitor) == weakref.ProxyType) and monitor or weakref.proxy(monitor)
        self._attributes = dict(attributes)

    #--- protected

    def _strip_attribute(self,attr):
        if self._attributes.get(attr):
            self._attributes[attr] = self._attributes[attr].strip()

    #--- Protected

    def _get_stat_id(self):
        return self._attributes.get('MON$STAT_ID')
    #--- properties

    stat_id = LateBindingProperty(_get_stat_id,None,None,"Internal ID.")
예제 #2
0
class TransactionInfo(BaseInfoItem):
    "Information about transaction."

    def __init__(self, monitor, attributes):
        super(TransactionInfo, self).__init__(monitor, attributes)

    #--- Protected

    def __get_id(self):
        return self._attributes['MON$TRANSACTION_ID']

    def __get_attachment(self):
        return self.monitor.get_attachment(
            self._attributes['MON$ATTACHMENT_ID'])

    def __get_state(self):
        return self._attributes['MON$STATE']

    def __get_timestamp(self):
        return self._attributes['MON$TIMESTAMP']

    def __get_top(self):
        return self._attributes['MON$TOP_TRANSACTION']

    def __get_oldest(self):
        return self._attributes['MON$OLDEST_TRANSACTION']

    def __get_oldest_active(self):
        return self._attributes['MON$OLDEST_ACTIVE']

    def __get_isolation_mode(self):
        return self._attributes['MON$ISOLATION_MODE']

    def __get_lock_timeout(self):
        return self._attributes['MON$LOCK_TIMEOUT']

    def _get_statements(self):
        return [
            s for s in self.monitor.statements
            if s._attributes['MON$TRANSACTION_ID'] == self.id
        ]

    def _get_variables(self):
        return [
            s for s in self.monitor.variables
            if s._attributes['MON$TRANSACTION_ID'] == self.id
        ]

    def __get_iostats(self):
        for io in self.monitor.iostats:
            if (io.stat_id == self.stat_id) and (io.group == STAT_TRANSACTION):
                return io
        return None

    def __get_tablestats(self):
        res = {}
        for io in self.monitor.tablestats:
            if (io.stat_id == self.stat_id) and (io.group == STAT_TRANSACTION):
                res[io.table_name] = io
        return res

    #--- properties

    id = property(__get_id, None, None, "Transaction ID.")
    attachment = property(
        __get_attachment, None, None,
        ":class:`AttachmentInfo` instance to which this transaction belongs.")
    state = property(__get_state, None, None,
                     "Transaction state (idle/active).")
    timestamp = property(__get_timestamp, None, None,
                         "Transaction start date/time.")
    top = property(__get_top, None, None, "Top transaction.")
    oldest = property(__get_oldest, None, None,
                      "Oldest transaction (local OIT).")
    oldest_active = property(__get_oldest_active, None, None,
                             "Oldest active transaction (local OAT).")
    isolation_mode = property(__get_isolation_mode, None, None,
                              "Transaction isolation mode code.")
    lock_timeout = property(__get_lock_timeout, None, None, "Lock timeout.")
    statements = LateBindingProperty(
        _get_statements, None, None,
        "List of statements associated with transaction.\nItems are :class:`StatementInfo` objects."
    )
    variables = LateBindingProperty(
        _get_variables, None, None,
        "List of variables associated with transaction.\nItems are :class:`ContextVariableInfo` objects."
    )
    iostats = property(__get_iostats, None, None,
                       ":class:`IOStatsInfo` for this object.")
    # FB 3.0
    tablestats = property(
        __get_tablestats, None, None,
        "Dictionary of :class:`TableStatsInfo` instances for this object.")

    #--- Public

    def isactive(self):
        "Returns True if transaction is active."
        return self.state == STATE_ACTIVE

    def isidle(self):
        "Returns True if transaction is idle."
        return self.state == STATE_IDLE

    def isreadonly(self):
        "Returns True if transaction is Read Only."
        return self._attributes['MON$READ_ONLY'] == FLAG_SET
        #return bool(self._attributes['MON$READ_ONLY'])
    def isautocommit(self):
        "Returns True for autocommited transaction."
        return self._attributes['MON$AUTO_COMMIT'] == FLAG_SET
        #return bool(self._attributes['MON$AUTO_COMMIT'])
    def isautoundo(self):
        "Returns True for transaction with automatic undo."
        return self._attributes['MON$AUTO_UNDO'] == FLAG_SET
예제 #3
0
class Monitor(object):
    """Class for access to Firebird monitoring tables.
    """
    def __init__(self):
        self._con = None
        self._ic = None
        self.__internal = False

    def __del__(self):
        if not self.closed:
            self._close()

    def __get_closed(self):
        return self._con is None

    def __fail_if_closed(self):
        if self.closed:
            raise fdb.ProgrammingError("Monitor is not binded to connection.")

    def _close(self):
        self._ic.close()
        self._con = None
        self._ic = None

    def _set_as_internal(self):
        """Mark this instance as `internal` (embedded). This blocks calls to
        :meth:`bind` and :meth:`close`."""
        self.__internal = True
        self._con = weakref.proxy(self._con)

        #--- protected

    def _get_database(self):
        if self.__database is None:
            self.__fail_if_closed()
            if self._con.ods >= fdb.ODS_FB_21:
                self._ic.execute("select * from mon$database")
                self.__database = DatabaseInfo(self, self._ic.fetchonemap())
            else:
                self.__database = []
        return self.__database

    def _get_attachments(self):
        if self.__attachments is None:
            self.__fail_if_closed()
            if self._con.ods >= fdb.ODS_FB_21:
                self._ic.execute("select * from mon$attachments")
                self.__attachments = [
                    AttachmentInfo(self, row) for row in self._ic.itermap()
                ]
            else:
                self.__attachments = []
        return self.__attachments

    def _get_this_attachment(self):
        return self.get_attachment(
            self._con.db_info(fdb.isc_info_attachment_id))

    def _get_transactions(self):
        if self.__transactions is None:
            self.__fail_if_closed()
            if self._con.ods >= fdb.ODS_FB_21:
                self._ic.execute("select * from mon$transactions")
                self.__transactions = [
                    TransactionInfo(self, row) for row in self._ic.itermap()
                ]
            else:
                self.__transactions = []
        return self.__transactions

    def _get_statements(self):
        if self.__statements is None:
            self.__fail_if_closed()
            if self._con.ods >= fdb.ODS_FB_21:
                self._ic.execute("select * from mon$statements")
                self.__statements = [
                    StatementInfo(self, row) for row in self._ic.itermap()
                ]
            else:
                self.__statements = []
        return self.__statements

    def _get_callstack(self):
        if self.__callstack is None:
            self.__fail_if_closed()
            if self._con.ods >= fdb.ODS_FB_21:
                self._ic.execute("select * from mon$call_stack")
                self.__callstack = [
                    CallStackInfo(self, row) for row in self._ic.itermap()
                ]
            else:
                self.__callstack = []
        return self.__callstack

    def _get_iostats(self):
        if self.__iostats is None:
            self.__fail_if_closed()
            if self._con.ods >= fdb.ODS_FB_30:
                self._ic.execute("""SELECT r.MON$STAT_ID, r.MON$STAT_GROUP,
r.MON$RECORD_SEQ_READS, r.MON$RECORD_IDX_READS, r.MON$RECORD_INSERTS,
r.MON$RECORD_UPDATES, r.MON$RECORD_DELETES, r.MON$RECORD_BACKOUTS,
r.MON$RECORD_PURGES, r.MON$RECORD_EXPUNGES, r.MON$RECORD_LOCKS, r.MON$RECORD_WAITS,
r.MON$RECORD_CONFLICTS, r.MON$BACKVERSION_READS, r.MON$FRAGMENT_READS, r.MON$RECORD_RPT_READS,
io.MON$PAGE_FETCHES, io.MON$PAGE_MARKS, io.MON$PAGE_READS, io.MON$PAGE_WRITES,
m.MON$MEMORY_ALLOCATED, m.MON$MEMORY_USED, m.MON$MAX_MEMORY_ALLOCATED, m.MON$MAX_MEMORY_USED
FROM MON$RECORD_STATS r join MON$IO_STATS io
  on r.MON$STAT_ID = io.MON$STAT_ID and r.MON$STAT_GROUP = io.MON$STAT_GROUP
  join MON$MEMORY_USAGE m
  on r.MON$STAT_ID = m.MON$STAT_ID and r.MON$STAT_GROUP = m.MON$STAT_GROUP""")
            elif self._con.ods >= fdb.ODS_FB_25:
                self._ic.execute("""SELECT r.MON$STAT_ID, r.MON$STAT_GROUP,
r.MON$RECORD_SEQ_READS, r.MON$RECORD_IDX_READS, r.MON$RECORD_INSERTS,
r.MON$RECORD_UPDATES, r.MON$RECORD_DELETES, r.MON$RECORD_BACKOUTS,
r.MON$RECORD_PURGES, r.MON$RECORD_EXPUNGES, io.MON$PAGE_FETCHES,
io.MON$PAGE_MARKS, io.MON$PAGE_READS, io.MON$PAGE_WRITES, m.MON$MEMORY_ALLOCATED,
m.MON$MEMORY_USED, m.MON$MAX_MEMORY_ALLOCATED, m.MON$MAX_MEMORY_USED
FROM MON$RECORD_STATS r join MON$IO_STATS io
  on r.MON$STAT_ID = io.MON$STAT_ID and r.MON$STAT_GROUP = io.MON$STAT_GROUP
  join MON$MEMORY_USAGE m
  on r.MON$STAT_ID = m.MON$STAT_ID and r.MON$STAT_GROUP = m.MON$STAT_GROUP""")
            elif self._con.ods >= fdb.ODS_FB_21:
                self._ic.execute("""SELECT r.MON$STAT_ID, r.MON$STAT_GROUP,
r.MON$RECORD_SEQ_READS, r.MON$RECORD_IDX_READS, r.MON$RECORD_INSERTS,
r.MON$RECORD_UPDATES, r.MON$RECORD_DELETES, r.MON$RECORD_BACKOUTS,
r.MON$RECORD_PURGES, r.MON$RECORD_EXPUNGES, io.MON$PAGE_FETCHES,
io.MON$PAGE_MARKS, io.MON$PAGE_READS, io.MON$PAGE_WRITES
FROM MON$RECORD_STATS r join MON$IO_STATS io
on r.MON$STAT_ID = io.MON$STAT_ID and r.MON$STAT_GROUP = io.MON$STAT_GROUP""")
            if self._con.ods >= fdb.ODS_FB_21:
                self.__iostats = [
                    IOStatsInfo(self, row) for row in self._ic.itermap()
                ]
            else:
                self.__iostats = []
        return self.__iostats

    def _get_variables(self):
        if self.__variables is None:
            self.__fail_if_closed()
            if self._con.ods >= fdb.ODS_FB_25:
                self._ic.execute("select * from mon$context_variables")
                self.__variables = [
                    ContextVariableInfo(self, row)
                    for row in self._ic.itermap()
                ]
            else:
                self.__variables = []
        return self.__variables

    def _get_tablestats(self):
        if self.__tablestats is None:
            self.__fail_if_closed()
            if self._con.ods >= fdb.ODS_FB_30:
                self._ic.execute(
                    """SELECT ts.MON$STAT_ID, ts.MON$STAT_GROUP, ts.MON$TABLE_NAME,
ts.MON$RECORD_STAT_ID, r.MON$RECORD_SEQ_READS, r.MON$RECORD_IDX_READS, r.MON$RECORD_INSERTS,
r.MON$RECORD_UPDATES, r.MON$RECORD_DELETES, r.MON$RECORD_BACKOUTS,
r.MON$RECORD_PURGES, r.MON$RECORD_EXPUNGES, r.MON$RECORD_LOCKS, r.MON$RECORD_WAITS,
r.MON$RECORD_CONFLICTS, r.MON$BACKVERSION_READS, r.MON$FRAGMENT_READS, r.MON$RECORD_RPT_READS
FROM MON$TABLE_STATS ts join MON$RECORD_STATS r
  on ts.MON$RECORD_STAT_ID = r.MON$STAT_ID""")
                self.__tablestats = [
                    TableStatsInfo(self, row) for row in self._ic.itermap()
                ]
            else:
                self.__tablestats = []
        return self.__tablestats

    #--- Properties

    #: True if link to :class:`~fdb.Connection` is closed.
    closed = property(__get_closed)
    db = LateBindingProperty(
        _get_database, None, None,
        ":class:`DatabaseInfo` object for attached database.")
    attachments = LateBindingProperty(
        _get_attachments, None, None,
        "List of all attachments.\nItems are :class:`AttachmentInfo` objects.")
    this_attachment = LateBindingProperty(
        _get_this_attachment, None, None,
        ":class:`AttachmentInfo` object for current connection.")
    transactions = LateBindingProperty(
        _get_transactions, None, None,
        "List of all transactions.\nItems are :class:`TransactionInfo` objects."
    )
    statements = LateBindingProperty(
        _get_statements, None, None,
        "List of all statements.\nItems are :class:`StatementInfo` objects.")
    callstack = LateBindingProperty(
        _get_callstack, None, None,
        "List with complete call stack.\nItems are :class:`CallStackInfo` objects."
    )
    iostats = LateBindingProperty(
        _get_iostats, None, None,
        "List of all I/O statistics.\nItems are :class:`IOStatsInfo` objects.")
    variables = LateBindingProperty(
        _get_variables, None, None,
        "List of all context variables.\nItems are :class:`ContextVariableInfo` objects."
    )
    # FB 3.0
    tablestats = LateBindingProperty(
        _get_tablestats, None, None,
        "List of all table record I/O statistics.\nItems are :class:`TableStatsInfo` objects."
    )

    #--- Public

    def bind(self, connection):
        """Bind this instance to specified :class:`~fdb.Connection`.

        :param connection: :class:`~fdb.Connection` instance.

        :raises ProgrammingError: If Monitor object was set as internal (via
            :meth:`_set_as_internal`) or database has ODS lower than 11.1.
        """
        if self.__internal:
            raise fdb.ProgrammingError(
                "Call to 'bind' not allowed for embedded Monitor.")
        if self._con:
            self.close()
        if connection.ods < fdb.ODS_FB_21:
            raise fdb.ProgrammingError("Monitoring tables are available only " \
                                       "for databases with ODS 11.1 and higher.")
        self._con = connection
        self._ic = self._con.trans(
            fdb.ISOLATION_LEVEL_READ_COMMITED_RO).cursor()
        self.clear()

    def close(self):
        """Sever link to :class:`~fdb.Connection`.

        :raises ProgrammingError: If Monitor object was set as internal (via
            :meth:`_set_as_internal`).
        """
        if self.__internal:
            raise fdb.ProgrammingError(
                "Call to 'close' not allowed for embedded Monitor.")
        self._close()
        self.clear()

    def clear(self):
        """Drop all cached information objects. Force reload of fresh monitoring
        information on next reference."""
        self.__database = None
        self.__attachments = None
        self.__transactions = None
        self.__statements = None
        self.__callstack = None
        self.__iostats = None
        self.__variables = None
        self.__tablestats = None
        if not self.closed:
            self._ic.transaction.commit()

    def refresh(self):
        "Reloads fresh monitoring information."
        self.__fail_if_closed()
        self._ic.transaction.commit()
        self.clear()
        self._get_database()

    def get_attachment(self, id):
        """Get :class:`AttachmentInfo` by ID.

        :param int id: Attachment ID.

        :returns: :class:`AttachmentInfo` with specified ID or `None`.
        """
        for attachment in self.attachments:
            if attachment.id == id:
                return attachment
        else:
            return None

    def get_transaction(self, id):
        """Get :class:`TransactionInfo` by ID.

        :param int id: Transaction ID.

        :returns: :class:`TransactionInfo` with specified ID or `None`.
        """
        for transaction in self.transactions:
            if transaction.id == id:
                return transaction
        else:
            return None

    def get_statement(self, id):
        """Get :class:`StatementInfo` by ID.

        :param int id: Statement ID.

        :returns: :class:`StatementInfo` with specified ID or `None`.
        """
        for statement in self.statements:
            if statement.id == id:
                return statement
        else:
            return None

    def get_call(self, id):
        """Get :class:`CallStackInfo` by ID.

        :param int id: Callstack ID.

        :returns: :class:`CallStackInfo` with specified ID or `None`.
        """
        for call in self.callstack:
            if call.id == id:
                return call
        else:
            return None
예제 #4
0
class AttachmentInfo(BaseInfoItem):
    "Information about attachment (connection) to database."

    def __init__(self, monitor, attributes):
        super(AttachmentInfo, self).__init__(monitor, attributes)

        self._strip_attribute('MON$ATTACHMENT_NAME')
        self._strip_attribute('MON$USER')
        self._strip_attribute('MON$ROLE')
        self._strip_attribute('MON$REMOTE_PROTOCOL')
        self._strip_attribute('MON$REMOTE_ADDRESS')
        self._strip_attribute('MON$REMOTE_PROCESS')
        self._strip_attribute('MON$CLIENT_VERSION')
        self._strip_attribute('MON$REMOTE_VERSION')
        self._strip_attribute('MON$REMOTE_HOST')
        self._strip_attribute('MON$REMOTE_OS_USER')
        self._strip_attribute('MON$AUTH_METHOD')

    #--- Protected

    def __get_id(self):
        return self._attributes['MON$ATTACHMENT_ID']

    def __get_server_pid(self):
        return self._attributes['MON$SERVER_PID']

    def __get_state(self):
        return self._attributes['MON$STATE']

    def __get_name(self):
        return self._attributes['MON$ATTACHMENT_NAME']

    def __get_user(self):
        return self._attributes['MON$USER']

    def __get_role(self):
        return self._attributes['MON$ROLE']

    def __get_remote_protocol(self):
        return self._attributes['MON$REMOTE_PROTOCOL']

    def __get_remote_address(self):
        return self._attributes['MON$REMOTE_ADDRESS']

    def __get_remote_pid(self):
        return self._attributes['MON$REMOTE_PID']

    def __get_remote_process(self):
        return self._attributes['MON$REMOTE_PROCESS']

    def __get_character_set(self):
        return self.monitor._con.schema.get_character_set_by_id(
            self._attributes['MON$CHARACTER_SET_ID'])

    def __get_timestamp(self):
        return self._attributes['MON$TIMESTAMP']

    def _get_transactions(self):
        return [
            t for t in self.monitor.transactions
            if t._attributes['MON$ATTACHMENT_ID'] == self.id
        ]

    def _get_statements(self):
        return [
            s for s in self.monitor.statements
            if s._attributes['MON$ATTACHMENT_ID'] == self.id
        ]

    def _get_variables(self):
        return [
            s for s in self.monitor.variables
            if s._attributes['MON$ATTACHMENT_ID'] == self.id
        ]

    def __get_iostats(self):
        for io in self.monitor.iostats:
            if (io.stat_id == self.stat_id) and (io.group == STAT_ATTACHMENT):
                return io
        return None

    def __get_auth_method(self):
        return self._attributes.get('MON$AUTH_METHOD')

    def __get_client_version(self):
        return self._attributes.get('MON$CLIENT_VERSION')

    def __get_remote_version(self):
        return self._attributes.get('MON$REMOTE_VERSION')

    def __get_remote_os_user(self):
        return self._attributes.get('MON$REMOTE_OS_USER')

    def __get_remote_host(self):
        return self._attributes.get('MON$REMOTE_HOST')

    def __get_tablestats(self):
        res = {}
        for io in self.monitor.tablestats:
            if (io.stat_id == self.stat_id) and (io.group == STAT_ATTACHMENT):
                res[io.table_name] = io
        return res

    #--- properties

    id = property(__get_id, None, None, "Attachment ID.")
    server_pid = property(__get_server_pid, None, None, "Server process ID.")
    state = property(__get_state, None, None,
                     "Attachment state (idle/active).")
    name = property(__get_name, None, None, "Database pathname or alias.")
    user = property(__get_user, None, None, "User name.")
    role = property(__get_role, None, None, "Role name.")
    remote_protocol = property(__get_remote_protocol, None, None,
                               "Remote protocol name.")
    remote_address = property(__get_remote_address, None, None,
                              "Remote address.")
    remote_pid = property(__get_remote_pid, None, None,
                          "Remote client process ID.")
    remote_process = property(__get_remote_process, None, None,
                              "Remote client process pathname.")
    character_set = property(
        __get_character_set, None, None,
        ":class:`~fdb.schema.CharacterSet` for this attachment.")
    timestamp = property(__get_timestamp, None, None, "Attachment date/time.")
    transactions = LateBindingProperty(
        _get_transactions, None, None,
        "List of transactions associated with attachment.\nItems are :class:`TransactionInfo` objects."
    )
    statements = LateBindingProperty(
        _get_statements, None, None,
        "List of statements associated with attachment.\nItems are :class:`StatementInfo` objects."
    )
    variables = LateBindingProperty(
        _get_variables, None, None,
        "List of variables associated with attachment.\nItems are :class:`ContextVariableInfo` objects."
    )
    iostats = property(__get_iostats, None, None,
                       ":class:`IOStatsInfo` for this object.")
    # FB 3.0
    auth_method = property(__get_auth_method, None, None,
                           "Authentication method.")
    client_version = property(__get_client_version, None, None,
                              "Client library version.")
    remote_version = property(__get_remote_version, None, None,
                              "Remote protocol version.")
    remote_os_user = property(__get_remote_os_user, None, None,
                              "OS user name of client process.")
    remote_host = property(__get_remote_host, None, None,
                           "Name of remote host.")
    tablestats = property(
        __get_tablestats, None, None,
        "Dictionary of :class:`TableStatsInfo` instances for this object.")

    #--- Public

    def isactive(self):
        "Returns True if attachment is active."
        return self.state == STATE_ACTIVE

    def isidle(self):
        "Returns True if attachment is idle."
        return self.state == STATE_IDLE

    def isgcallowed(self):
        "Returns True if Garbage Collection is enabled for this attachment."
        return bool(self._attributes['MON$GARBAGE_COLLECTION'])

    def isinternal(self):
        "Returns True if attachment is internal system attachment."
        return bool(self._attributes.get('MON$SYSTEM_FLAG'))

    def terminate(self):
        """Terminates client session associated with this attachment.

        :raises ProgrammingError: If database has ODS lower than 11.2 or
            this attachement is current session.
        """
        if self.monitor._con.ods < fdb.ODS_FB_25:
            raise fdb.ProgrammingError("Attachments could be terminated only " \
                                       "for databases with ODS 11.2 and higher.")
        elif self is self.monitor.this_attachment:
            raise fdb.ProgrammingError("Can't terminate current session.")
        else:
            self.monitor._ic.execute(
                'delete from mon$attachments where mon$attachment_id = ?',
                (self.id, ))