|
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)
|