Blame src/python-problem/problem/__init__.py

Packit 8ea169
import os
Packit 8ea169
import hashlib
Packit 8ea169
import inspect
Packit 8ea169
import datetime
Packit 8ea169
Packit 8ea169
from problem import proxies, exception, tools, watch
Packit 8ea169
try:
Packit 8ea169
    from _pyabrt import *
Packit 8ea169
except ImportError:
Packit 8ea169
    try:
Packit 8ea169
        from _py3abrt import *
Packit 8ea169
    except ImportError:
Packit 8ea169
        from problem._py3abrt import *
Packit 8ea169
Packit 8ea169
JAVA = 'java'
Packit 8ea169
SELINUX = 'selinux'
Packit 8ea169
CCPP = 'CCpp'
Packit 8ea169
PYTHON = 'Python'
Packit 8ea169
PYTHON3 = 'Python3'
Packit 8ea169
KERNELOOPS = 'Kerneloops'
Packit 8ea169
RUNTIME = 'runtime'
Packit 8ea169
XORG = 'xorg'
Packit 8ea169
UNKNOWN = 'libreport'
Packit 8ea169
Packit 8ea169
REQUIRED_FIELDS = ['executable']
Packit 8ea169
PREFETCH_FIELDS = [
Packit 8ea169
    # core fields
Packit 8ea169
    'component', 'hostname', 'os_release', 'uid',
Packit 8ea169
    'username', 'architecture', 'kernel', 'package',
Packit 8ea169
    'time', 'count', 'pkg_arch', 'pkg_name',
Packit 8ea169
    'pkg_epoch', 'pkg_version', 'pkg_release',
Packit 8ea169
    'uuid',
Packit 8ea169
    # type specific
Packit 8ea169
    'cgroup', 'core_backtrace', 'backtrace',
Packit 8ea169
    'dso_list', 'exploitable', 'maps',
Packit 8ea169
    'cmdline', 'environ', 'open_fds', 'pid',
Packit 8ea169
    'proc_pid_status', 'limits', 'var_log_messages',
Packit 8ea169
    'suspend_stats', 'reported_to', 'event_log',
Packit 8ea169
    'dmesg',
Packit 8ea169
]
Packit 8ea169
Packit 8ea169
PROBLEM_TYPES = {
Packit 8ea169
    'JAVA': JAVA,
Packit 8ea169
    'SELINUX': SELINUX,
Packit 8ea169
    'CCPP': CCPP,
Packit 8ea169
    'PYTHON': PYTHON,
Packit 8ea169
    'PYTHON3': PYTHON3,
Packit 8ea169
    'KERNELOOPS': KERNELOOPS,
Packit 8ea169
    'RUNTIME': RUNTIME,
Packit 8ea169
    'XORG': XORG,
Packit 8ea169
    'UNKNOWN': UNKNOWN,
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Problem(object):
Packit 8ea169
    '''
Packit 8ea169
    Base class for the other problem types.
Packit 8ea169
Packit 8ea169
    No need to use this class directly, use one
Packit 8ea169
    of the specific problem classes.
Packit 8ea169
Packit 8ea169
    '''
Packit 8ea169
    def __init__(self, typ, reason, analyzer=None):
Packit 8ea169
        self._data = dict()
Packit 8ea169
        self._dirty_data = dict()
Packit 8ea169
        self._persisted = False
Packit 8ea169
        self._proxy = None
Packit 8ea169
        self._probdir = None
Packit 8ea169
        self._id = None
Packit 8ea169
Packit 8ea169
        self.type = typ
Packit 8ea169
        if analyzer is None:
Packit 8ea169
            self.analyzer = typ
Packit 8ea169
        self.reason = reason
Packit 8ea169
        self._proxy = proxies.get_proxy()
Packit 8ea169
Packit 8ea169
    def __cast(self, attr, val, reverse=False):
Packit 8ea169
        # str with digits -> int
Packit 8ea169
        if not reverse and type(val) == str and val.isdigit():
Packit 8ea169
            val = int(val)
Packit 8ea169
Packit 8ea169
        # by attr name
Packit 8ea169
        mapping = {
Packit 8ea169
            'time': (datetime.datetime.fromtimestamp,
Packit 8ea169
                     lambda x: x.strftime('%s'))
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        if attr in mapping:
Packit 8ea169
            fun, revfun = mapping[attr]
Packit 8ea169
            if reverse:
Packit 8ea169
                fun = revfun
Packit 8ea169
Packit 8ea169
            val = fun(val)
Packit 8ea169
Packit 8ea169
        if reverse:
Packit 8ea169
            return str(val)
Packit 8ea169
Packit 8ea169
        return val
Packit 8ea169
Packit 8ea169
    def __getattr__(self, attr):
Packit 8ea169
        exc = AttributeError("object has no attribute '{0}'".format(attr))
Packit 8ea169
        val = None
Packit 8ea169
Packit 8ea169
        # was deleted before?
Packit 8ea169
        if attr in self._dirty_data and self._dirty_data[attr] is None:
Packit 8ea169
            raise exc
Packit 8ea169
Packit 8ea169
        if attr in self._data:
Packit 8ea169
            val = self._data[attr]
Packit 8ea169
Packit 8ea169
        # try to fetch the item
Packit 8ea169
        if self._persisted:
Packit 8ea169
            val = self._proxy.get_item(self._probdir, attr)
Packit 8ea169
            self._data[attr] = val
Packit 8ea169
Packit 8ea169
        if val is None:
Packit 8ea169
            raise exc
Packit 8ea169
Packit 8ea169
        val = self.__cast(attr, val)
Packit 8ea169
        super(Problem, self).__setattr__(attr, val)
Packit 8ea169
Packit 8ea169
        return val
Packit 8ea169
Packit 8ea169
    def __setattr__(self, attr, value):
Packit 8ea169
        super(Problem, self).__setattr__(attr, value)
Packit 8ea169
        if not attr[0] == '_':
Packit 8ea169
            self._data[attr] = value
Packit 8ea169
            if self._persisted:
Packit 8ea169
                self._dirty_data[attr] = value
Packit 8ea169
Packit 8ea169
    def __delattr__(self, attr):
Packit 8ea169
        # it might not be loaded at first
Packit 8ea169
        self.__getattr__(attr)
Packit 8ea169
        super(Problem, self).__delattr__(attr)
Packit 8ea169
        del self._data[attr]
Packit 8ea169
        if self._persisted:
Packit 8ea169
            self._dirty_data[attr] = None
Packit 8ea169
Packit 8ea169
    def __getitem__(self, attr):
Packit 8ea169
        try:
Packit 8ea169
            return self.__getattr__(attr)
Packit 8ea169
        except AttributeError as e:
Packit 8ea169
            raise KeyError(e)
Packit 8ea169
Packit 8ea169
    def __setitem__(self, attr, value):
Packit 8ea169
        self.__setattr__(attr, value)
Packit 8ea169
Packit 8ea169
    def __delitem__(self, attr):
Packit 8ea169
        try:
Packit 8ea169
            self.__delattr__(attr)
Packit 8ea169
        except AttributeError as e:
Packit 8ea169
            raise KeyError(e)
Packit 8ea169
Packit 8ea169
    def __repr__(self):
Packit 8ea169
        return '<problem.{0} ({1})>'.format(self.__class__.__name__, self.reason)
Packit 8ea169
Packit 8ea169
    def add_current_process_data(self):
Packit 8ea169
        ''' Add pid, gid and executable of current
Packit 8ea169
        process to this problem object
Packit 8ea169
Packit 8ea169
        '''
Packit 8ea169
        self.pid = os.getpid()
Packit 8ea169
        self.gid = os.getgid()
Packit 8ea169
        #self.executable = os.readlink('/proc/{0}/exe'.format(os.getpid()))
Packit 8ea169
        # ^ always '/usr/bin/python' so we need:
Packit 8ea169
        self.executable = os.path.abspath(inspect.stack()[-1][1])
Packit 8ea169
Packit 8ea169
    def add_current_environment(self):
Packit 8ea169
        ''' Add environment of current process to this problem object '''
Packit 8ea169
        self.environ = ''
Packit 8ea169
        for key, value in os.environ.items():
Packit 8ea169
            self.environ += '{0}={1}\n'.format(key, value)
Packit 8ea169
Packit 8ea169
    def prefetch_data(self):
Packit 8ea169
        ''' Prefetch possible data fields of this problem '''
Packit 8ea169
        if not self._persisted:
Packit 8ea169
            return
Packit 8ea169
Packit 8ea169
        for field in PREFETCH_FIELDS:
Packit 8ea169
            try:
Packit 8ea169
                self.__getattr__(field)
Packit 8ea169
            except AttributeError:
Packit 8ea169
                pass
Packit 8ea169
Packit 8ea169
    @property
Packit 8ea169
    def path(self):
Packit 8ea169
        if self._persisted:
Packit 8ea169
            return self._probdir
Packit 8ea169
Packit 8ea169
        return None
Packit 8ea169
Packit 8ea169
    @property
Packit 8ea169
    def id(self):
Packit 8ea169
        if not self._id and self._persisted:
Packit 8ea169
            self._id = hashlib.sha1(self.path.encode('utf-8')).hexdigest()
Packit 8ea169
Packit 8ea169
        return self._id
Packit 8ea169
Packit 8ea169
    @property
Packit 8ea169
    def short_id(self):
Packit 8ea169
        if not self.id:
Packit 8ea169
            return None
Packit 8ea169
Packit 8ea169
        return self.id[:7]
Packit 8ea169
Packit 8ea169
    @property
Packit 8ea169
    def not_reportable(self):
Packit 8ea169
        return hasattr(self, 'not-reportable')
Packit 8ea169
Packit 8ea169
    @not_reportable.setter
Packit 8ea169
    def not_reportable(self, value):
Packit 8ea169
        if isinstance(value, bool):
Packit 8ea169
            if value:
Packit 8ea169
                setattr(self, 'not-reportable', '')
Packit 8ea169
            else:
Packit 8ea169
                delattr(self, 'not-reportable')
Packit 8ea169
Packit 8ea169
        else:  # if value is used as reason
Packit 8ea169
            setattr(self, 'not-reportable', value)
Packit 8ea169
Packit 8ea169
    @property
Packit 8ea169
    def not_reportable_reason(self):
Packit 8ea169
        if hasattr(self, 'not-reportable'):
Packit 8ea169
            return getattr(self, 'not-reportable')
Packit 8ea169
Packit 8ea169
        return None
Packit 8ea169
Packit 8ea169
    @not_reportable_reason.setter
Packit 8ea169
    def not_reportable_reason(self, value):
Packit 8ea169
        setattr(self, 'not-reportable', value)
Packit 8ea169
Packit 8ea169
    def items(self):
Packit 8ea169
        return self._data.items()
Packit 8ea169
Packit 8ea169
    def validate(self):
Packit 8ea169
        for field in REQUIRED_FIELDS:
Packit 8ea169
            if not hasattr(self, field):
Packit 8ea169
                raise exception.ValidationError(
Packit 8ea169
                    'Missing required field {0}'.format(field))
Packit 8ea169
Packit 8ea169
    def save(self):
Packit 8ea169
        ''' Create this problem or update modified data
Packit 8ea169
Packit 8ea169
        Create or update the project if some of its fields
Packit 8ea169
        were modified.
Packit 8ea169
Packit 8ea169
        Return ``None`` in case of modification, identifier
Packit 8ea169
        if new problem was created.
Packit 8ea169
Packit 8ea169
        '''
Packit 8ea169
        self.validate()
Packit 8ea169
Packit 8ea169
        # convert to strings
Packit 8ea169
        str_data = dict()
Packit 8ea169
        for key, value in self._data.items():
Packit 8ea169
            str_data[str(key)] = self.__cast(key, value, reverse=True)
Packit 8ea169
Packit 8ea169
        # already persisted?
Packit 8ea169
        if self._persisted:
Packit 8ea169
            for key, value in self._dirty_data.items():
Packit 8ea169
                if value is None:
Packit 8ea169
                    self._proxy.del_item(self._probdir, key)
Packit 8ea169
                else:
Packit 8ea169
                    self._proxy.set_item(self._probdir, key,
Packit 8ea169
                                         self.__cast(key, value, reverse=True))
Packit 8ea169
Packit 8ea169
            self._dirty_data = dict()
Packit 8ea169
        else:
Packit 8ea169
            # create
Packit 8ea169
            ret = self._proxy.create(str_data)
Packit 8ea169
            self._persisted = True
Packit 8ea169
            self._probdir = str(ret)
Packit 8ea169
            return self._probdir
Packit 8ea169
Packit 8ea169
    def delete(self):
Packit 8ea169
        ''' Delete this problem '''
Packit 8ea169
        if self._persisted:
Packit 8ea169
            self._proxy.delete(self._probdir)
Packit 8ea169
            self._persisted = False
Packit 8ea169
            self._probdir = None
Packit 8ea169
            self._dirty_data = {}
Packit 8ea169
Packit 8ea169
    def chown(self):
Packit 8ea169
        ''' Assures ownership of a problem for the caller '''
Packit 8ea169
        if self._persisted:
Packit 8ea169
            self._proxy.chown(self._probdir)
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Java(Problem):
Packit 8ea169
    ''' Java problem '''
Packit 8ea169
    def __init__(self, reason):
Packit 8ea169
        super(Java, self).__init__(JAVA, reason)
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Selinux(Problem):
Packit 8ea169
    ''' Selinux problem '''
Packit 8ea169
    def __init__(self, reason):
Packit 8ea169
        super(Selinux, self).__init__(SELINUX, reason)
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Ccpp(Problem):
Packit 8ea169
    ''' C, C++ problem '''
Packit 8ea169
    def __init__(self, reason):
Packit 8ea169
        super(Ccpp, self).__init__(CCPP, reason)
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Python(Problem):
Packit 8ea169
    ''' Python problem '''
Packit 8ea169
    def __init__(self, reason):
Packit 8ea169
        super(Python, self).__init__(PYTHON, reason)
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Python3(Problem):
Packit 8ea169
    ''' Python3 problem '''
Packit 8ea169
    def __init__(self, reason):
Packit 8ea169
        super(Python3, self).__init__(PYTHON3, reason)
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Kerneloops(Problem):
Packit 8ea169
    ''' Kerneloops problem '''
Packit 8ea169
    def __init__(self, reason):
Packit 8ea169
        super(Kerneloops, self).__init__(KERNELOOPS, reason)
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Xorg(Problem):
Packit 8ea169
    ''' Xorg problem '''
Packit 8ea169
    def __init__(self, reason):
Packit 8ea169
        super(Xorg, self).__init__(XORG, reason)
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Runtime(Problem):
Packit 8ea169
    ''' Runtime problem '''
Packit 8ea169
    def __init__(self, reason):
Packit 8ea169
        super(Runtime, self).__init__(RUNTIME, reason)
Packit 8ea169
Packit 8ea169
Packit 8ea169
class Unknown(Problem):
Packit 8ea169
    ''' Unknown problem '''
Packit 8ea169
    def __init__(self, reason):
Packit 8ea169
        super(Unknown, self).__init__('libreport', reason)
Packit 8ea169
Packit 8ea169
Packit 8ea169
def list(auth=False, __proxy=proxies.get_proxy()):
Packit 8ea169
    ''' Return the list of the problems
Packit 8ea169
Packit 8ea169
    Use ``auth=True`` if authentication should be attempted.
Packit 8ea169
Packit 8ea169
    If authentication via polkit fails, function behaves
Packit 8ea169
    as if ``auth=False`` was specified (only users problems are
Packit 8ea169
    returned).
Packit 8ea169
    '''
Packit 8ea169
    fun = __proxy.list
Packit 8ea169
    if auth:
Packit 8ea169
        fun = __proxy.list_all
Packit 8ea169
Packit 8ea169
    return [tools.problemify(prob, __proxy) for prob in fun()]
Packit 8ea169
Packit 8ea169
Packit 8ea169
def get(identifier, auth=False, __proxy=proxies.get_proxy()):
Packit 8ea169
    ''' Return problem object matching ``identifier``
Packit 8ea169
Packit 8ea169
    Return ``None`` in case the problem does not exist.
Packit 8ea169
    Use ``auth=True`` if authentication should be attempted.
Packit 8ea169
Packit 8ea169
    '''
Packit 8ea169
Packit 8ea169
    fun = __proxy.list
Packit 8ea169
    if auth:
Packit 8ea169
        fun = __proxy.list_all
Packit 8ea169
Packit 8ea169
    if identifier not in fun():
Packit 8ea169
        return None
Packit 8ea169
Packit 8ea169
    return tools.problemify(identifier, __proxy)
Packit 8ea169
Packit 8ea169
Packit 8ea169
def get_problem_watcher(auth=False):
Packit 8ea169
    ''' Return ``ProblemWatcher`` object which can be used
Packit 8ea169
    to attach callbacks called when new problem is created
Packit 8ea169
Packit 8ea169
    Use ``auth=True`` if authentication should be attempted for
Packit 8ea169
    new problem that doesn't belong to current user. If not
Packit 8ea169
    set such a problem is ignored.
Packit 8ea169
Packit 8ea169
    '''
Packit 8ea169
Packit 8ea169
    return watch.ProblemWatcher(auth)