--- utils/log_picker/__init__.py | 139 +++++++++++++++ utils/log_picker/archiving.py | 99 +++++++++++ utils/log_picker/argparser.py | 141 +++++++++++++++ utils/log_picker/logmining.py | 251 +++++++++++++++++++++++++++ utils/log_picker/sending/__init__.py | 28 +++ utils/log_picker/sending/bugzillasender.py | 59 +++++++ utils/log_picker/sending/emailsender.py | 69 ++++++++ utils/log_picker/sending/senderbaseclass.py | 24 +++ utils/log_picker/sending/stratasender.py | 55 ++++++ utils/logpicker | 125 +++++++++++++ 10 files changed, 990 insertions(+), 0 deletions(-) create mode 100644 utils/log_picker/__init__.py create mode 100644 utils/log_picker/archiving.py create mode 100644 utils/log_picker/argparser.py create mode 100644 utils/log_picker/logmining.py create mode 100644 utils/log_picker/sending/__init__.py create mode 100644 utils/log_picker/sending/bugzillasender.py create mode 100644 utils/log_picker/sending/emailsender.py create mode 100644 utils/log_picker/sending/senderbaseclass.py create mode 100644 utils/log_picker/sending/stratasender.py create mode 100755 utils/logpicker diff --git a/utils/log_picker/__init__.py b/utils/log_picker/__init__.py new file mode 100644 index 0000000..70498d1 --- /dev/null +++ b/utils/log_picker/__init__.py @@ -0,0 +1,139 @@ +#!/usr/bin/python + +import os +import sys +import tempfile + +import archiving +from archiving import ArchivationError +from archiving import NoFilesArchivationError +import sending +from sending import SenderError +import logmining +from logmining import LogMinerError + + +class LogPickerError(Exception): + pass + + +class LogPicker(object): + + def __init__(self, archive_obj=None, sender_obj=None, miners=[], + use_one_file=False): + self.sender_obj = sender_obj + self.archive_obj = archive_obj + self.miners = miners + + self.archive = None + self.tmpdir = None + self.files = [] + self.filename = self._get_tmp_file("completelog") if use_one_file else None + + + def _errprint(self, msg): + """Print message on stderr.""" + sys.stderr.write('%s\n' % msg) + + + def _get_tmp_file(self, name, suffix="", register=True): + """Create temp file.""" + if not self.tmpdir: + self.tmpdir = tempfile.mkdtemp(prefix="z-logs-", dir="/tmp") + + fd, filename = tempfile.mkstemp(suffix=suffix, + prefix="lp-%s-" % name, dir=self.tmpdir) + if register: + self.files.append(filename) + return filename + + + def create_archive(self, name="logs"): + """Create archive (one file) containing multiple log files.""" + self.archive = self._get_tmp_file(name, + suffix=self.archive_obj.file_ext, register=False) + try: + self.archive_obj.create_archive(self.archive, self.files) + except (ArchivationError): + os.remove(self.archive) + raise + + + def send(self): + """Send log/archive with logs via sender object.""" + + if not self.archive and len(self.files) > 1: + raise LogPickerError('More than one file to send. ' + \ + 'You have to create archive. Use create_archive() method.') + + file = self.files[0] + contenttype="text/plain" + if self.archive: + file = self.archive + contenttype = self.archive_obj.mimetype + + self.sender_obj.sendfile(file, contenttype) + + + def getlogs(self): + """Collect logs generated by miners passed to the constructor.""" + + # self.filename != None means that we should put all logs into one file. + # self.filename == None means that every log should have its own file. + if self.filename: + f = open(self.filename, 'w') + + for miner in self.miners: + if not self.filename: + tmpfilename = self._get_tmp_file(miner.get_filename()) + f = open(tmpfilename, 'w') + + desc = "%s\n\n" % (miner.get_description()) + f.write(desc) + try: + miner(f).getlog() + except (LogMinerError) as e: + self._errprint("Warning: %s - %s" % (miner._name, e)) + f.write("\n%s\n\n\n" % e) + + if not self.filename: + f.close() + # XXX Cut anaconda dump into pieces. + if miner == logmining.AnacondaLogMiner: + self._cut_to_pieces(tmpfilename) + + if self.filename: + f.close() + + + def _cut_to_pieces(self, filename): + """Create multiple log files from Anaconda dump. + Attention: Anaconda dump file will be used and overwritten! + @filename file with Anaconda dump""" + actual_file = os.path.basename(filename) + files = {actual_file: []} + empty_lines = 0 + + # Split file into memmory + for line in open(filename): + striped = line.strip() + + if not striped: + empty_lines += 1 + elif empty_lines > 1 and striped.startswith('/') \ + and striped.endswith(':') and len(line) > 2: + actual_file = striped[:-1].rsplit('/', 1)[-1].replace('.', '-') + files[actual_file] = [] + empty_lines = 0 + + files[actual_file].append(line) + + # Overwrite original file + actual_file = os.path.basename(filename) + open(filename, 'w').writelines(files[actual_file]) + del files[actual_file] + + # Write other individual files + for file in files: + open(self._get_tmp_file(file), 'w').writelines(files[file]) + diff --git a/utils/log_picker/archiving.py b/utils/log_picker/archiving.py new file mode 100644 index 0000000..e7d7fa3 --- /dev/null +++ b/utils/log_picker/archiving.py @@ -0,0 +1,99 @@ +import os +import tempfile +import tarfile +import bz2 + + +class ArchivationError(Exception): + pass + +class NoFilesArchivationError(ArchivationError): + pass + +class ArchiveBaseClass(object): + """Base class for archive classes.""" + + _compression = False + _ext = ".ext" + _mimetype = "" + + def __init__(self, *args, **kwargs): + self._tar_ext = ".tar" + pass + + @property + def support_compression(self): + """Return True if compression is supported/used.""" + return self._compression + + @property + def file_ext(self): + """Return extension for output file.""" + return self._ext + + @property + def mimetype(self): + """Return archive mime type.""" + return self._mimetype + + def _create_tmp_tar(self, filelist): + _, tmpfile = tempfile.mkstemp(suffix=self._tar_ext) + tar = tarfile.open(tmpfile, "w") + for name in filelist: + arcname = name.rsplit('/', 1)[-1] + tar.add(name, arcname=arcname) + tar.close() + return tmpfile + + def create_archive(self, outfilename, filelist): + raise NotImplementedError() + + +class Bzip2Archive(ArchiveBaseClass): + """Class for bzip2 compression.""" + + _compression = True + _ext = ".bz2" + _mimetype = "application/x-bzip2" + + def __init__(self, usetar=True, *args, **kwargs): + ArchiveBaseClass.__init__(self, args, kwargs) + self.usetar = usetar + + @property + def file_ext(self): + """Return extension for output file.""" + if self.usetar: + return "%s%s" % (self._tar_ext, self._ext) + return self._ext + + def create_archive(self, outfilename, filelist): + """Create compressed archive containing files listed in filelist.""" + if not filelist: + raise NoFilesArchivationError("No files to archive.") + + size = 0 + for file in filelist: + size += os.path.getsize(file) + if size <= 0: + raise NoFilesArchivationError("No files to archive.") + + if not self.usetar and len(filelist) > 1: + raise ArchivationError("Bzip2 cannot archive multiple files without tar.") + + if self.usetar: + f_in_path = self._create_tmp_tar(filelist) + else: + f_in_path = filelist[0] + + f_in = open(f_in_path, 'rb') + f_out = bz2.BZ2File(outfilename, 'w') + f_out.writelines(f_in) + f_out.close() + f_in.close() + + if self.usetar: + os.remove(f_in_path) + + return outfilename + diff --git a/utils/log_picker/argparser.py b/utils/log_picker/argparser.py new file mode 100644 index 0000000..71170d5 --- /dev/null +++ b/utils/log_picker/argparser.py @@ -0,0 +1,141 @@ +import optparse +import sending + + +class ArgError(Exception): + pass + + +class _OptionParserWithRaise(optparse.OptionParser): + def error(self, msg): + raise ArgError(msg) + + +class ArgParser(object): + + def __init__(self): + self.options = None + self.parser = None + + + def _generate_bz_it_group(self): + if sending.RHBZ in sending.NOT_AVAILABLE and sending.STRATA in sending.NOT_AVAILABLE: + return None + + if sending.RHBZ in sending.NOT_AVAILABLE: + group_title = "Send to Red Hat Ticketing system options" + bz_group = optparse.OptionGroup(self.parser, group_title) + bz_group.add_option("-r", "--rhel", + action="store_true", + dest="strata", + help="Send report to Red Hat Ticketing system.") + bug_id_title = "Case number in Red Hat Ticketing system." + login_title = "Red Hat Customer Portal username." + + elif sending.STRATA in sending.NOT_AVAILABLE: + group_title = "Send to Bugzilla options" + bz_group = optparse.OptionGroup(self.parser, group_title) + bz_group.add_option("-b", "--bugzilla", + action="store_true", + dest="bugzilla", + help="Send report to Bugzilla.") + bug_id_title = "Bug id in bugzilla." + login_title = "Bugzilla username." + + else: + group_title = "Send to Bugzilla (Fedora) or Red Hat Ticketing system options" + bz_group = optparse.OptionGroup(self.parser, group_title) + bz_group.add_option("-b", "--bugzilla", + action="store_true", + dest="bugzilla", + help="Send report to Bugzilla.") + bz_group.add_option("-r", "--rhel", + action="store_true", + dest="strata", + help="Send report to Red Hat Ticketing system.") + bug_id_title = "Bug id in bugzilla/Case number in Red Hat Ticketing system." + login_title = "Bugzilla/Red Hat Cutomer Portal username." + + bz_group.add_option("-i", "--idbug", + dest="bug_id", + help=bug_id_title, + metavar="ID") + bz_group.add_option("-l", "--login", + dest="login", + help=login_title, + metavar="USERNAME") + + return bz_group + + + def _generate_email_group(self): + if sending.EMAIL in sending.NOT_AVAILABLE: + return None + + email_group = optparse.OptionGroup(self.parser, "Send to Email options") + email_group.add_option("-e", "--email", action="store_true", dest="email", + help="Send report to email address.") + email_group.add_option("-s", "--server", dest="smtp_addr", + help="SMTP server address.", metavar="ADDRESS") + email_group.add_option("-f", "--from", dest="from_addr", + help="Your email address.", metavar="EMAIL") + email_group.add_option("-t", "--to", dest="to_addr", + help="Destination email address.", metavar="EMAIL") + return email_group + + + def _create_parser(self): + self.parser = _OptionParserWithRaise() + self.parser.add_option("-c", "--comment", dest="bug_comment", default=None, + help="Report comment.", metavar="COMMENT") + + # Bugzilla and Red Hat Ticketing system options + group = self._generate_bz_it_group() + if group: self.parser.add_option_group(group) + + # Email options + group = self._generate_email_group() + if group: self.parser.add_option_group(group) + + + def _parse(self): + (self.options, _) = self.parser.parse_args() + + + def _validate(self): + cnt = 0 + if self.options.ensure_value('email', None): cnt += 1 + if self.options.ensure_value('bugzilla', None): cnt += 1 + if self.options.ensure_value('strata', None): cnt += 1 + + if not cnt: + raise ArgError("No place to send specified.") + elif cnt > 1: + raise ArgError("Options -b and -r and -e are mutually exclusive.") + + missing = [] + if self.options.ensure_value('email', None): + if not self.options.smtp_addr: missing.append('-s') + if not self.options.from_addr: missing.append('-f') + if not self.options.to_addr: missing.append('-t') + elif self.options.ensure_value('bugzilla', None): + if not self.options.bug_id: missing.append('-i') + if not self.options.login: missing.append('-l') + elif self.options.ensure_value('strata', None): + if not self.options.bug_id: missing.append('-i') + if not self.options.login: missing.append('-l') + + if missing: + msg = "" + for arg in missing: + msg += '\nArgument "%s" is missing!' % arg + raise ArgError(msg) + + + def parse(self): + """Parse and validate command line arguments.""" + self._create_parser() + self._parse() + self._validate() + return self.options + diff --git a/utils/log_picker/logmining.py b/utils/log_picker/logmining.py new file mode 100644 index 0000000..218ca46 --- /dev/null +++ b/utils/log_picker/logmining.py @@ -0,0 +1,251 @@ +import os +import stat +import shlex +import time +import subprocess + + +class LogMinerError(Exception): + pass + + +class LogMinerBaseClass(object): + """Base class for LogMiner classes. + LogMiner object represents one file/command/function + to get useful information (log).""" + + _name = "name" + _description = "Description" + _filename = "filename" + _prefer_separate_file = True + + def __init__(self, logfile, *args, **kwargs): + """@logfile open file object. This open file object will be used for + output generated during getlog() call.""" + self.logfile = logfile + self._used = False + + @classmethod + def get_filename(cls): + """Suggested log filename.""" + return cls._filename + + @classmethod + def get_description(cls): + """Log description.""" + return cls._description + + def _write_separator(self): + self.logfile.write('\n\n') + + def _write_files(self, files): + if not isinstance(files, list): + files = [files] + + if self._used: + self._write_separator() + self._used = True + + for filename in files: + self.logfile.write('%s:\n' % filename) + try: + f = open(filename, 'r') + except (IOError) as e: + self.logfile.write("Exception while opening: %s\n" % e) + continue + + self.logfile.writelines(f) + f.close() + + def _run_command(self, command): + if self._used: + self._write_separator() + self._used = True + + if isinstance(command, basestring): + command = shlex.split(command) + + proc = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (out, err) = proc.communicate() + self.logfile.write('STDOUT:\n%s\n' % out) + self.logfile.write('STDERR:\n%s\n' % err) + self.logfile.write('RETURN CODE: %s\n' % proc.returncode) + + def getlog(self): + """Create log and write it to a file object + recieved in the constructor.""" + self._action() + self._write_separator() + + def _action(self): + raise NotImplementedError() + + + +class AnacondaLogMiner(LogMinerBaseClass): + """Class represents way to get Anaconda dump.""" + + _name = "anaconda_log" + _description = "Log dumped from Anaconda." + _filename = "anaconda-dump" + _prefer_separate_file = True + + def _action(self): + # Actual state of /tmp + old_state = set(os.listdir('/tmp')) + + # Tell Anaconda to dump itself + try: + anaconda_pid = open('/var/run/anaconda.pid').read().strip() + except (IOError): + raise LogMinerError("Anaconda pid file doesn't exists") + + proc = subprocess.Popen(shlex.split("kill -s USR2 %s" % anaconda_pid), + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc.communicate() + if proc.returncode: + raise LogMinerError('Error while sending signal to Anaconda') + + time.sleep(5) + + # Check if new traceback file exists + new_state = set(os.listdir('/tmp')) + tbpfiles = list(new_state - old_state) + + if not len(tbpfiles): + raise LogMinerError('Error: No anaconda traceback file exist') + + for file in tbpfiles: + if file.startswith('anaconda-tb-'): + tbpfile_name = file + break + else: + raise LogMinerError('Error: No anaconda traceback file exist') + + # Copy anaconda traceback log + self._write_files('/tmp/%s' % tbpfile_name) + + + +class FileSystemLogMiner(LogMinerBaseClass): + """Class represents way to get image of filesystem structure.""" + + _name = "filesystem" + _description = "Image of disc structure." + _filename = "filesystem" + _prefer_separate_file = True + + FSTREE_FORMAT = "%1s %5s%1s %s" + DADPOINT = 1 # Number of Digits After the Decimal POINT + + def _action(self): + self._get_tree_structure() + + def _size_conversion(self, size): + """Converts bytes into KB, MB or GB""" + if size > 1073741824: # Gigabytes + size = round(size / 1073741824.0, self.DADPOINT) + unit = "G" + elif size > 1048576: # Megabytes + size = round(size / 1048576.0, self.DADPOINT) + unit = "M" + elif size > 1024: # Kilobytes + size = round(size / 1024.0, self.DADPOINT) + unit = "K" + else: + size = size + unit = "" + return size, unit + + + def _get_tree_structure(self, human_readable=True): + """Creates filesystem structure image.""" + white_list = ['/sys',] + + logfile = self.logfile + + for path, dirs, files in os.walk('/'): + line = "\n%s:" % (path) + logfile.write('%s\n' % line) + + # List dirs + for dir in dirs: + fullpath = os.path.join(path, dir) + size = os.path.getsize(fullpath) + unit = "" + if human_readable: + size, unit = self._size_conversion(size) + line = self.FSTREE_FORMAT % ("d", size, unit, dir) + logfile.write('%s\n' % line) + + # Skip mounted directories + original_dirs = dirs[:] + for dir in original_dirs: + dirpath = os.path.join(path, dir) + if os.path.ismount(dirpath) and not dirpath in white_list: + dirs.remove(dir) + + # List files + for filename in files: + fullpath = os.path.join(path, filename) + if os.path.islink(fullpath): + line = self.FSTREE_FORMAT % ("l", "0", "", filename) + line += " -> %s" % os.path.realpath(fullpath) + if not os.path.isfile(fullpath): + # Broken symlink + line += " (Broken)" + else: + stat_res = os.stat(fullpath)[stat.ST_MODE] + if stat.S_ISREG(stat_res): + filetype = "-" + elif stat.S_ISCHR(stat_res): + filetype = "c" + elif stat.S_ISBLK(stat_res): + filetype = "b" + elif stat.S_ISFIFO(stat_res): + filetype = "p" + elif stat.S_ISSOCK(stat_res): + filetype = "s" + else: + filetype = "-" + + size = os.path.getsize(fullpath) + unit = "" + if human_readable: + size, unit = self._size_conversion(size) + line = self.FSTREE_FORMAT % (filetype, size, unit, filename) + logfile.write('%s\n' % line) + + + +class DmSetupLsLogMiner(LogMinerBaseClass): + """Class represents way to get 'dmsetup ls --tree' output.""" + + _name = "dmsetup ls" + _description = "Output from \"dmsetup ls --tree\"." + _filename = "dmsetup-ls" + _prefer_separate_file = True + + def _action(self): + self._run_command("dmsetup ls --tree") + + +class DmSetupInfoLogMiner(LogMinerBaseClass): + """Class represents way to get 'dmsetup info' output.""" + + _name = "dmsetup info" + _description = "Output from \"dmsetup info -c\"." + _filename = "dmsetup-info" + _prefer_separate_file = True + + def _action(self): + self._run_command("dmsetup info -c") + + +ALL_MINERS = [AnacondaLogMiner, + FileSystemLogMiner, + DmSetupLsLogMiner, + DmSetupInfoLogMiner, + ] + diff --git a/utils/log_picker/sending/__init__.py b/utils/log_picker/sending/__init__.py new file mode 100644 index 0000000..8f7f22b --- /dev/null +++ b/utils/log_picker/sending/__init__.py @@ -0,0 +1,28 @@ +from senderbaseclass import SenderError + +RHBZ = 0 # RedHat Bugzilla +EMAIL = 1 # Email +STRATA = 2 # Red Hat ticketing system + +NOT_AVAILABLE = [] + + +try: + from bugzillasender import RedHatBugzilla +except (ImportError): + NOT_AVAILABLE.append(RHBZ) + pass + +try: + from emailsender import EmailSender +except (ImportError): + NOT_AVAILABLE.append(EMAIL) + pass + +try: + from stratasender import StrataSender +except (ImportError): + NOT_AVAILABLE.append(STRATA) + pass + + diff --git a/utils/log_picker/sending/bugzillasender.py b/utils/log_picker/sending/bugzillasender.py new file mode 100644 index 0000000..08f89ec --- /dev/null +++ b/utils/log_picker/sending/bugzillasender.py @@ -0,0 +1,59 @@ +import os +from senderbaseclass import * +from report.plugins.bugzilla import filer +from report.plugins.bugzilla.filer import CommunicationError +from report.plugins.bugzilla.filer import LoginError + + +class BugzillaBaseClass(SenderBaseClass): + + _bz_address = "" + _bz_xmlrpc = "" + _description = "" + + def __init__(self, *args, **kwargs): + SenderBaseClass.__init__(self, args, kwargs) + self.bzfiler = None + self.bug_id = None + self.comment = None + + def connect_and_login(self, username, password): + try: + self.bzfiler = filer.BugzillaFiler(self._bz_xmlrpc, self._bz_address, + filer.getVersion(), filer.getProduct()) + self.bzfiler.login(username, password) + except (CommunicationError, LoginError) as e: + raise SenderError("%s. Bad username or password?" % e) + + def set_bug(self, bug_id): + self.bug_id = bug_id + + def set_comment(self, comment): + self.comment = comment + + def sendfile(self, file, contenttype): + description = self._get_description(self._description) + + dict_args = {'isprivate': False, + 'filename': os.path.basename(file), + 'contenttype': contenttype} + + if self.comment: + dict_args['comment'] = self.comment + + try: + bug = self.bzfiler.getbug(self.bug_id) + bug.attachfile(file, description, **dict_args) + except (CommunicationError, ValueError) as e: + raise SenderError(e) + + +class RedHatBugzilla(BugzillaBaseClass): + + _bz_address = "http://bugzilla.redhat.com" + _bz_xmlrpc = "https://bugzilla.redhat.com/xmlrpc.cgi" + _description = "LogPicker" + + def __init__(self, *args, **kwargs): + BugzillaBaseClass.__init__(self, args, kwargs) + diff --git a/utils/log_picker/sending/emailsender.py b/utils/log_picker/sending/emailsender.py new file mode 100644 index 0000000..549edc5 --- /dev/null +++ b/utils/log_picker/sending/emailsender.py @@ -0,0 +1,69 @@ +import os +import smtplib +import email +import email.mime.multipart +from senderbaseclass import * + + +class EmailSender(SenderBaseClass): + + _description = "Email from LogPicker" + + def __init__(self, sendby, addresses, smtp_server, *args, **kwargs): + """Send file by email. + @sendby - Sender email address. (string) + @addresses - List of destination email addresses. (list) + @smtp_server - SMTP server address. (string)""" + + SenderBaseClass.__init__(self, args, kwargs) + self.smtp_server = smtp_server + self.sendby = sendby + self.addresses = addresses + self.comment = "" + + def set_comment(self, comment): + self.comment = comment + + def sendfile(self, file, contenttype): + # Create email message + msg = email.mime.multipart.MIMEMultipart() + msg['Subject'] = self._get_description(self._description) + msg['From'] = self.sendby + msg['To'] = ', '.join(self.addresses) + msg.preamble = 'This is a multi-part message in MIME format.' + + # Add message text + msgtext = email.mime.base.MIMEBase("text", "plain") + msgtext.set_payload(self.comment) + msg.attach(msgtext) + + # Add message attachment + cont_type = contenttype.split('/', 1) + if len(cont_type) == 1: + cont_type.append("") + elif not cont_type: + cont_type = ["application", "octet-stream"] + + attach_data = open(file, 'rb').read() + + msgattach = email.mime.base.MIMEBase(cont_type[0], cont_type[1]) + msgattach.set_payload(attach_data) + email.Encoders.encode_base64(msgattach) + msgattach.add_header('Content-Disposition', 'attachment', + filename=os.path.basename(file)) + msg.attach(msgattach) + + # Send message + try: + s = smtplib.SMTP(self.smtp_server) + except(smtplib.socket.gaierror, smtplib.SMTPServerDisconnected): + raise SenderError("Email cannot be send. " +\ + "Error while connecting to smtp server.") + + try: + s.sendmail(self.sendby, self.addresses, msg.as_string()) + except(smtplib.SMTPRecipientsRefused) as e: + raise SenderError("Email cannot be send. Wrong destination " +\ + "email address?\nErr message: %s" % e) + s.quit() + diff --git a/utils/log_picker/sending/senderbaseclass.py b/utils/log_picker/sending/senderbaseclass.py new file mode 100644 index 0000000..1605859 --- /dev/null +++ b/utils/log_picker/sending/senderbaseclass.py @@ -0,0 +1,24 @@ +import datetime +from socket import gethostname + + +class SenderError(Exception): + pass + + +class SenderBaseClass(object): + + def __init__(self, *args, **kwargs): + pass + + def sendfile(self, file, contenttype): + raise NotImplementedError() + + def _get_description(self, prefix=""): + try: + hostname = gethostname() + except: + hostname = "" + date_str = datetime.datetime.now().strftime("%Y-%m-%d") + description = "%s (%s) %s" % (prefix, hostname, date_str) + return description diff --git a/utils/log_picker/sending/stratasender.py b/utils/log_picker/sending/stratasender.py new file mode 100644 index 0000000..c872655 --- /dev/null +++ b/utils/log_picker/sending/stratasender.py @@ -0,0 +1,55 @@ +from senderbaseclass import * +from report.plugins.strata import send_report_to_existing_case, strata_client_strerror +import xml.dom.minidom + + +class StrataSender(SenderBaseClass): + + _URL = "https://api.access.redhat.com/rs" + _CERT_DATA = "INSECURE" + + def __init__(self, *args, **kwargs): + SenderBaseClass.__init__(self, args, kwargs) + self.username = None + self.password = None + self.case_number = None + + def set_login(self, username, password): + self.username = username + self.password = password + + def set_case_number(self, case_num): + self.case_number = case_num + + def sendfile(self, file, contenttype): + response = send_report_to_existing_case(self._URL, + self._CERT_DATA, + self.username, + self.password, + self.case_number, + file) + + if not response: + raise SenderError("Sending log to the Red Hat Ticket System fail" +\ + " - %s" % strata_client_strerror()) + + # Try parse response + try: + dom = xml.dom.minidom.parseString(response) + response_node = dom.getElementsByTagName("response")[0] + title = response_node.getElementsByTagName("title")[0].childNodes[0].data + body = response_node.getElementsByTagName("body")[0].childNodes[0].data + except Exception as e: + raise SenderError("Sending log to the Red Hat Ticket System fail.") + + if title == "File Attachment Failed": + if body.startswith("401 Unauthorized"): + raise SenderError("Bad login or password.") + elif body.startswith("Error : CASE_NOT_FOUND"): + raise SenderError("Selected case doesn't exist.") + else: + raise SenderError("Sending log to the " +\ + "Red Hat Ticket System fail - %s" % body.strip()) + #elif title == "File Attachment Succeeded": + # return + diff --git a/utils/logpicker b/utils/logpicker new file mode 100755 index 0000000..f1b383d --- /dev/null +++ b/utils/logpicker @@ -0,0 +1,125 @@ +#!/usr/bin/python + +import sys +import getpass +import log_picker + +import log_picker.argparser as argparser +from log_picker.argparser import ArgError +import log_picker.archiving as archiving +from log_picker.archiving import ArchivationError +from log_picker.archiving import NoFilesArchivationError +import log_picker.sending as sending +from log_picker.sending import SenderError +import log_picker.logmining as logmining +from log_picker.logmining import LogMinerError + + +class ApplicationScope(object): + """Application configuration class.""" + + def __init__(self, parser_options): + self.bug_comment = parser_options.ensure_value('bug_comment', None) + + self.bug_id = parser_options.ensure_value('bug_id', None) + self.login = parser_options.ensure_value('login', None) + self.password = None + + self.smtp_server = parser_options.ensure_value('smtp_addr', None) + self.from_addr = parser_options.ensure_value('from_addr', None) + self.to_addr = [parser_options.to_addr] if parser_options.ensure_value('to_addr', None) else [] + + # sender + if parser_options.ensure_value('email', None): + self.sender = sending.EMAIL + elif parser_options.ensure_value('strata', None): + self.sender = sending.STRATA + elif parser_options.ensure_value('bugzilla', None): + self.sender = sending.RHBZ + else: + self.sender = None + + # miners + self.miners = logmining.ALL_MINERS + + +class Injector(object): + """Main factory class.""" + + @staticmethod + def injectMainHelper(scope): + logpicker = Injector.injectLogPicker(scope) + return MainHelper(logpicker) + + @staticmethod + def injectLogPicker(scope): + sender = Injector.injectSender(scope) + archivator = Injector.injectArchivator(scope) + return log_picker.LogPicker(archive_obj=archivator, sender_obj=sender, + miners=scope.miners) + + @staticmethod + def injectSender(scope): + if scope.sender == sending.RHBZ: + sender = sending.RedHatBugzilla() + sender.set_bug(scope.bug_id) + sender.set_comment(scope.bug_comment) + sender.connect_and_login(scope.login, scope.password) + return sender + if scope.sender == sending.EMAIL: + sender = sending.EmailSender(scope.from_addr, scope.to_addr, + scope.smtp_server) + sender.set_comment(scope.bug_comment) + return sender + if scope.sender == sending.STRATA: + sender = sending.StrataSender() + sender.set_login(scope.login, scope.password) + sender.set_case_number(scope.bug_id) + return sender + + raise Exception("Unknown sender type.") + + @staticmethod + def injectArchivator(scope): + return archiving.Bzip2Archive() + + +class MainHelper(object): + """Main application class.""" + + def __init__(self, logpicker): + self.picker = logpicker + + def run(self): + self.picker.getlogs() + self.picker.create_archive() + self.picker.send() + print "Successfully completed!" + + + +if __name__ == "__main__": + + # Argument parsing + try: + options = argparser.ArgParser().parse() + except (ArgError) as e: + sys.stderr.write("Argument error: %s\n" % e) + sys.exit(1) + + # Application scope + scope = ApplicationScope(options) + if scope.sender == sending.RHBZ or scope.sender == sending.STRATA: + scope.password = getpass.getpass("Password: ") + + # Application + try: + app = Injector.injectMainHelper(scope) + app.run() + except (NoFilesArchivationError): + sys.stderr.write("Nothing to report. Select more log gathering options.\n") + sys.exit(0) + except (SenderError) as e: + sys.stderr.write("Error: %s\n" % e) + sys.exit(1) + -- 1.7.2.3 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list