Example #1
0
    def start(self):
        cmd = WmiUtils.get_wmic_cmd(
            username=self.username,
            password=self.password,
            target_ip=self.target_ip,
            columns=["LogfileName"],
            table="Win32_NTEventlogFile",
        )

        self.logfile_com = ExtCom(
            self.log, cmd, debug=True,
            shell=False)  # shell=False since args must not be parsed again
        self.logfile_com.run()
Example #2
0
        def __call__(self, job):
            where_clause = "WHERE Logfile = '{}'".format(job.logfile_name)
            if job.last_known_record_number is not None:
                where_clause += "AND RecordNumber >= {}".format(
                    job.last_known_record_number)

            cmd = WmiUtils.get_wmic_cmd(
                username=job.username,
                password=job.password,
                target_ip=job.target_ip,
                columns=["RecordNumber"],
                table="Win32_NTLogEvent",
                where_clause=where_clause,
            )
            job.log(
                "Querying maximal entry for {} with last known record number {}"
                .format(job, job.last_known_record_number))

            job.ext_com = ExtCom(
                job.log, cmd, debug=True,
                shell=False)  # shell=False since args must not be parsed again
            job.ext_com.run()

            job.current_phase = WmiLogEntryJob.FindMaximumPhase()
            return True
Example #3
0
 def start_next_job(self, job):
     cmd = (
         "/opt/cluster/bin/ipmitool",
         "-H",
         job.target_ip,
         "-U",
         job.username,
         "-P",
         job.password,
         "sel",
         "get",
         hex(self.current_record_id),
     )
     self.ext_com = ExtCom(
         job.log, cmd, shell=False, debug=global_config['DEBUG']
     )  # shell=False since args must not be parsed again
     self.ext_com.run()
Example #4
0
        def __call__(self, job):
            do_continue = True

            if self.ext_com is None:
                # initial call, start the job
                cmd = (
                    "/opt/cluster/bin/ipmitool",
                    "-H",
                    job.target_ip,
                    "-U",
                    job.username,
                    "-P",
                    job.password,
                    "sel",
                    "list",
                )
                self.ext_com = ExtCom(
                    job.log, cmd, shell=False, debug=global_config['DEBUG']
                )  # shell=False since args must not be parsed again
                self.ext_com.run()
            else:
                # job has been started, check
                exit_code = self.ext_com.finished()

                if exit_code is not None:

                    stdout_out, stderr_out = self.ext_com.communicate()

                    if stderr_out:
                        job._handle_stderr(
                            stderr_out,
                            "finding out maximum ipmi sel record id for {}".
                            format(job))

                    if exit_code != 0:
                        raise RuntimeError(
                            "IpmiLogJob FindMaximumPhase ipmitool command failed with code {}"
                            .format(exit_code))

                    # lines look like this (first is record id in hex):
                    #  67 | 06/17/2015 | 04:20:03 | Temperature #0x32 | Upper Non-critical going high
                    stdout_lines = stdout_out.split("\n")
                    record_ids_string = [
                        line.split("|", 1)[0].strip() for line in stdout_lines
                    ]
                    record_ids_no_empty_ones = [
                        entry for entry in record_ids_string if entry
                    ]
                    record_ids_int = [
                        int(entry, base=16)
                        for entry in record_ids_no_empty_ones
                    ]

                    max_record_id = max(record_ids_int)

                    if job.last_known_record_id == max_record_id:
                        job.log(
                            "Maximal record id same as last known one ({}), we are done"
                            .format(max_record_id))
                        do_continue = False
                    else:
                        job.log("Need to gather records from {} to {} for {}".
                                format(job.last_known_record_id, max_record_id,
                                       job))
                        job.current_phase = IpmiLogJob.RetrieveTime(
                            job.last_known_record_id, max_record_id)

            return do_continue  # do_continue
Example #5
0
    class RetrieveEvents(object):
        def __init__(self, last_known_record_id, max_record_id,
                     device_time_diff):
            self.ext_com = None
            self.max_record_id = max_record_id
            self.current_record_id = last_known_record_id + 1 if last_known_record_id is not None else 1
            # 1 is first record id
            self.device_time_diff = device_time_diff

        def __call__(self, job):
            if self.ext_com is not None:
                self.check_current_output(job)

            do_continue = self.current_record_id <= self.max_record_id

            # start next job if there are ids yet to get and if no job is running
            if do_continue and (self.ext_com is None
                                or self.ext_com.finished() is not None):
                self.start_next_job(job)

            return do_continue

        def __unicode(self):
            return "RetrieveEvents(cur_record_id={}, max_record_id={})".format(
                self.current_record_id, self.max_record_id)

        __repr__ = __unicode

        def start_next_job(self, job):
            cmd = (
                "/opt/cluster/bin/ipmitool",
                "-H",
                job.target_ip,
                "-U",
                job.username,
                "-P",
                job.password,
                "sel",
                "get",
                hex(self.current_record_id),
            )
            self.ext_com = ExtCom(
                job.log, cmd, shell=False, debug=global_config['DEBUG']
            )  # shell=False since args must not be parsed again
            self.ext_com.run()

        def check_current_output(self, job):
            exit_code = self.ext_com.finished()

            if exit_code is not None:

                stdout_out, stderr_out = self.ext_com.communicate()

                if stderr_out:
                    job._handle_stderr(
                        stderr_out, "retrieving sel record {}".format(
                            self.current_record_id))

                if exit_code != 0:
                    raise RuntimeError(
                        "IpmiLogJob ipmitool command failed with code {}".
                        format(exit_code))

                # entries look like this:
                # SEL Record ID          : 0067
                #  Record Type           : 02
                #  Timestamp             : 06/17/2015 04:20:03
                # [...]
                #
                # Sensor ID              : Ambient Temp (0x32)
                #  Entity ID             : 12.1
                #  Sensor Type (Threshold)  : Temperature

                # sections are separated by empty lines (usually without whitespace in between, but handle anyway)
                sections_raw = re.compile("\n[ \t]*\n").split(stdout_out)
                sections_db_data = []
                timestamp = None
                for sec in sections_raw:
                    if sec:  # there might be an empty last entry
                        parsed = job._parse_section(sec)
                        if parsed:
                            sections_db_data.append(parsed)

                            raw_timestamp = parsed.get(
                                'Timestamp')  # only present in one section
                            if raw_timestamp is not None:
                                try:
                                    timestamp_device_time = job._parse_ipmi_datetime_string(
                                        raw_timestamp)
                                except ValueError:
                                    job.log(
                                        "Unparsable time stamp in ipmi entry: {}"
                                        .format(raw_timestamp))
                                    job.log(traceback.format_exc())
                                else:
                                    timestamp = timestamp_device_time + self.device_time_diff

                # timestamp section is first section (timestamp could be None for somewhat broken entries)
                sections_db_data = [{
                    'Timestamp Local':
                    str(timestamp) if timestamp is not None else None
                }] + sections_db_data

                keys_ordered = list(
                    itertools.chain.from_iterable(
                        iter(sec.keys()) for sec in sections_db_data))
                db_entry = {
                    'parse_date': django.utils.timezone.now(),
                    'creation_date': timestamp,
                    'record_id': self.current_record_id,
                    'keys_ordered': keys_ordered,
                    'sections': sections_db_data,
                    'device_pk': job.target_device.pk,
                }
                job.db.ipmi_event_log.insert(db_entry)

                if global_config['DEBUG']:
                    job.log("feeding db: {}".format(db_entry))

                self.current_record_id += 1
Example #6
0
        def __call__(self, job):
            if self.ext_com is None:
                # initial call, start the job
                cmd = (
                    "/opt/cluster/bin/ipmitool",
                    "-H",
                    job.target_ip,
                    "-U",
                    job.username,
                    "-P",
                    job.password,
                    "sel",
                    "time",
                    "get",
                )
                self.ext_com = ExtCom(
                    job.log, cmd, shell=False, debug=global_config['DEBUG']
                )  # shell=False since args must not be parsed again
                self.ext_com.run()
            else:
                exit_code = self.ext_com.finished()

                if exit_code is not None:
                    stdout_out, stderr_out = self.ext_com.communicate()

                    if stderr_out:
                        job._handle_stderr(stderr_out,
                                           "ipmi sel time get {}".format(job))

                    if exit_code != 0:
                        raise RuntimeError(
                            "IpmiLogJob RetrieveTime ipmitool command failed with code {}"
                            .format(exit_code))

                    try:
                        device_time = job._parse_ipmi_datetime_string(
                            stdout_out)
                    except ValueError as e:
                        job.log(
                            "Failed to parse time of job {}: {}".format(
                                job, e), logging_tools.LOG_LEVEL_ERROR)
                        job.log(traceback.format_exc(),
                                logging_tools.LOG_LEVEL_ERROR)
                        job.log("Assuming that device has local time")
                        device_time = django.utils.timezone.now()

                    device_time_diff = django.utils.timezone.now(
                    ) - device_time

                    job.log("Local time is {}, i.e. diff {}, for {}".format(
                        device_time,
                        device_time_diff,
                        job,
                    ))

                    job.current_phase = IpmiLogJob.RetrieveEvents(
                        job.last_known_record_id,
                        self.max_record_id,
                        device_time_diff=device_time_diff)

            return True  # do_continue
Example #7
0
class WmiLogFileJob(_WmiJobBase):
    """Job to retrieve list of log files from wmi server"""
    def __init__(self, *args, **kwargs):
        super(WmiLogFileJob, self).__init__(*args, **kwargs)
        self.logfile_com = None

    def __unicode__(self):
        return u"WmiLogFileJob(dev={}, ip={})".format(self.target_device,
                                                      self.target_ip)

    __repr__ = __unicode__

    def start(self):
        cmd = WmiUtils.get_wmic_cmd(
            username=self.username,
            password=self.password,
            target_ip=self.target_ip,
            columns=["LogfileName"],
            table="Win32_NTEventlogFile",
        )

        self.logfile_com = ExtCom(
            self.log, cmd, debug=True,
            shell=False)  # shell=False since args must not be parsed again
        self.logfile_com.run()

    def periodic_check(self):
        do_continue = True
        if self.logfile_com.finished() is not None:
            do_continue = False
            stdout_out, stderr_out = self.logfile_com.communicate()

            if stderr_out:
                self._handle_stderr(
                    stderr_out,
                    "scanning for wmi log files for {}".format(self))

            if self.logfile_com.finished() != 0:
                raise RuntimeError(
                    "Scanning for wmi log files failed with code {}".format(
                        self.logfile_com.finished()))
            else:
                parsed = WmiUtils.parse_wmic_output(stdout_out)
                logfiles = []
                for entry in parsed:
                    if 'LogfileName' not in entry:
                        self.log(
                            "Invalid entry in log file scanning: {}".format(
                                entry), logging_tools.LOG_LEVEL_WARN)
                    else:
                        logfiles.append(entry['LogfileName'])

                # logfiles = ["Application"]
                self.log("Detected {} wmi logfiles for {}".format(
                    len(logfiles), self.target_device))

                self.db.wmi_logfile.update_one(
                    filter={'device_pk': self.target_device.pk},
                    update={
                        '$set': {
                            'device_pk': self.target_device.pk,
                            'date': django.utils.timezone.now(),
                            'logfiles': logfiles,
                        }
                    },
                    upsert=True)

        return do_continue

    def __eq__(self, other):
        return isinstance(other, WmiLogFileJob) and super(WmiLogFileJob,
                                                          self).__eq__(other)
Example #8
0
        def __call__(self, job):
            do_continue = True

            com_finished = self.retrieve_ext_com is not None and self.retrieve_ext_com.finished(
            ) is not None
            is_initial = self.retrieve_ext_com is None
            if com_finished:
                # handle output

                stdout_out, stderr_out = self.retrieve_ext_com.communicate()

                if stderr_out:
                    job._handle_stderr(stderr_out,
                                       "RetrieveEvents for {}".format(job))

                if self.retrieve_ext_com.finished() != 0:
                    raise RuntimeError(
                        "RetrieveEvents wmi command failed with code {}".
                        format(self.retrieve_ext_com.finished()))

                #print 'code', self.retrieve_ext_com.finished()
                #_, tmpfilename = tempfile.mkstemp()
                #f = open(tmpfilename, 'w')
                #f.write(stdout_out)
                #f.flush()
                #print 'stdout len', len(stdout_out), tmpfilename
                #print ' stderr'
                #import pprint
                #pprint.pprint(stderr_out)

                parsed = job._parse_wmi_logfile_output(stdout_out,
                                                       try_handle_lists=True,
                                                       log=job.log)
                # print 'len', len(parsed)
                # `parsed` may be empty for RecordNumber-holes

                job.log("Found {} log entries between {} and {} for {}".format(
                    len(parsed),
                    self.from_record_number,
                    self.get_next_upper_limit(self.from_record_number),
                    job,
                ))

                maximal_record_number = self.get_next_upper_limit(
                    self.from_record_number)

                db_entries = []
                date_now = django.utils.timezone.now()
                for log_entry in parsed:
                    record_number = log_entry.get('RecordNumber')
                    if record_number is None:
                        job.log(
                            "Warning: WMI log entry without record number, ignoring:",
                            logging_tools.LOG_LEVEL_WARN)
                        job.log("{}".format(log_entry))
                    time_generated = log_entry.get('TimeGenerated')
                    if time_generated is not None:
                        try:
                            time_generated = self.parse_datetime(
                                time_generated)
                        except ValueError as e:
                            job.log(
                                "Failed to parse datetime from time generated {}: {}"
                                .format(time_generated, e))
                            job.log(traceback.format_exc())
                            time_generated = None
                    if time_generated is None:
                        job.log(
                            "Warning: WMI log entry without TimeGenerated, ignoring:",
                            logging_tools.LOG_LEVEL_WARN)
                        job.log("{}".format(log_entry))
                    else:
                        db_entries.append({
                            'time_generated': time_generated,
                            'logfile_name': job.logfile_name,
                            'entry': log_entry,
                            'record_number': record_number,
                            'device_pk': job.target_device.pk,
                        })

                # DEBUG check
                for entry in db_entries:

                    if list(
                            job.db.wmi_event_log.find({
                                'logfile_name':
                                entry['logfile_name'],
                                'record_number':
                                entry["record_number"],
                                'device_pk':
                                entry["device_pk"],
                            })):
                        raise RuntimeError("DUPLICATE WMI for " +
                                           unicode(entry))

                if db_entries:
                    # must not feed empty list to mongo
                    job.db.wmi_event_log.insert_many(db_entries)

                job.db.wmi_logfile_maximal_record_number.update_one(
                    filter={
                        'device_pk': job.target_device.pk,
                        'logfile_name': job.logfile_name,
                    },
                    update={
                        '$set': {
                            'device_pk': job.target_device.pk,
                            'date': date_now,
                            'maximal_record_number':
                            maximal_record_number,  # this entry may not actually be present
                        }
                    },
                    upsert=True,
                )
                self.from_record_number = maximal_record_number

                job.log(
                    "New maximal record number: {} (maximum to reach: {}) for {}"
                    .format(maximal_record_number, self.to_record_number, job))

                self.retrieve_ext_com = None

            if com_finished or is_initial:
                # check whether to start next run

                if self.from_record_number >= self.to_record_number:
                    do_continue = False
                    job.log("Reached maximal record number {} (by {}).".format(
                        self.to_record_number, self.from_record_number))
                else:
                    # start next run
                    cmd = WmiUtils.get_wmic_cmd(
                        username=job.username,
                        password=job.password,
                        target_ip=job.target_ip,
                        columns=[
                            "RecordNumber, Message, Category, CategoryString, ComputerName, EventCode, "
                            +
                            "EventIdentifier, InsertionStrings, SourceName, TimeGenerated, TimeWritten,  "
                            + "EventType, Type, User"
                        ],
                        table="Win32_NTLogEvent",
                        where_clause=
                        "WHERE Logfile = '{}' AND RecordNumber > {} and RecordNumber <= {}"
                        .format(
                            job.logfile_name,
                            self.from_record_number,
                            self.get_next_upper_limit(self.from_record_number),
                        ))
                    job.log("querying entries from {} to {} for {}".format(
                        self.from_record_number,
                        self.get_next_upper_limit(self.from_record_number),
                        job,
                    ))

                    self.retrieve_ext_com = ExtCom(
                        job.log, cmd, debug=True, shell=False
                    )  # shell=False since args must not be parsed again
                    self.retrieve_ext_com.run()

            return do_continue
Example #9
0
    class RetrieveEventsPhase(object):
        PAGINATION_LIMIT = 10000

        # start retrieval jobs and handle output until all entries have been retrieved

        def __init__(self, from_number, to_record_number):
            # this is increased in the process
            self.from_record_number = from_number if from_number is not None else 0
            # 1 is the first RecordNumber

            self.to_record_number = to_record_number

            self.retrieve_ext_com = None

        def parse_datetime(self, datetime_string):
            # https://technet.microsoft.com/en-us/library/ee198928.aspx
            # e.g. 20011224113047.000000-480
            parts = datetime_string.split(".")
            if len(parts) != 2:
                raise ValueError(
                    "Date does not contain 1 dot: {}".format(datetime_string))
            base_date = datetime.datetime.strptime(parts[0], "%Y%m%d%H%M%S")
            timezone_parts = parts[1].split('-')
            if len(timezone_parts) != 2:
                raise ValueError(
                    "Date does not contain proper timezone info: {}".format(
                        datetime_string))
            return base_date.replace(
                tzinfo=dateutil.tz.tzoffset("Custom wmi offset",
                                            int(timezone_parts[1]) * 60))

        def get_next_upper_limit(self, lower_limit):
            return min(lower_limit + self.__class__.PAGINATION_LIMIT,
                       self.to_record_number)

        def __call__(self, job):
            do_continue = True

            com_finished = self.retrieve_ext_com is not None and self.retrieve_ext_com.finished(
            ) is not None
            is_initial = self.retrieve_ext_com is None
            if com_finished:
                # handle output

                stdout_out, stderr_out = self.retrieve_ext_com.communicate()

                if stderr_out:
                    job._handle_stderr(stderr_out,
                                       "RetrieveEvents for {}".format(job))

                if self.retrieve_ext_com.finished() != 0:
                    raise RuntimeError(
                        "RetrieveEvents wmi command failed with code {}".
                        format(self.retrieve_ext_com.finished()))

                #print 'code', self.retrieve_ext_com.finished()
                #_, tmpfilename = tempfile.mkstemp()
                #f = open(tmpfilename, 'w')
                #f.write(stdout_out)
                #f.flush()
                #print 'stdout len', len(stdout_out), tmpfilename
                #print ' stderr'
                #import pprint
                #pprint.pprint(stderr_out)

                parsed = job._parse_wmi_logfile_output(stdout_out,
                                                       try_handle_lists=True,
                                                       log=job.log)
                # print 'len', len(parsed)
                # `parsed` may be empty for RecordNumber-holes

                job.log("Found {} log entries between {} and {} for {}".format(
                    len(parsed),
                    self.from_record_number,
                    self.get_next_upper_limit(self.from_record_number),
                    job,
                ))

                maximal_record_number = self.get_next_upper_limit(
                    self.from_record_number)

                db_entries = []
                date_now = django.utils.timezone.now()
                for log_entry in parsed:
                    record_number = log_entry.get('RecordNumber')
                    if record_number is None:
                        job.log(
                            "Warning: WMI log entry without record number, ignoring:",
                            logging_tools.LOG_LEVEL_WARN)
                        job.log("{}".format(log_entry))
                    time_generated = log_entry.get('TimeGenerated')
                    if time_generated is not None:
                        try:
                            time_generated = self.parse_datetime(
                                time_generated)
                        except ValueError as e:
                            job.log(
                                "Failed to parse datetime from time generated {}: {}"
                                .format(time_generated, e))
                            job.log(traceback.format_exc())
                            time_generated = None
                    if time_generated is None:
                        job.log(
                            "Warning: WMI log entry without TimeGenerated, ignoring:",
                            logging_tools.LOG_LEVEL_WARN)
                        job.log("{}".format(log_entry))
                    else:
                        db_entries.append({
                            'time_generated': time_generated,
                            'logfile_name': job.logfile_name,
                            'entry': log_entry,
                            'record_number': record_number,
                            'device_pk': job.target_device.pk,
                        })

                # DEBUG check
                for entry in db_entries:

                    if list(
                            job.db.wmi_event_log.find({
                                'logfile_name':
                                entry['logfile_name'],
                                'record_number':
                                entry["record_number"],
                                'device_pk':
                                entry["device_pk"],
                            })):
                        raise RuntimeError("DUPLICATE WMI for " +
                                           unicode(entry))

                if db_entries:
                    # must not feed empty list to mongo
                    job.db.wmi_event_log.insert_many(db_entries)

                job.db.wmi_logfile_maximal_record_number.update_one(
                    filter={
                        'device_pk': job.target_device.pk,
                        'logfile_name': job.logfile_name,
                    },
                    update={
                        '$set': {
                            'device_pk': job.target_device.pk,
                            'date': date_now,
                            'maximal_record_number':
                            maximal_record_number,  # this entry may not actually be present
                        }
                    },
                    upsert=True,
                )
                self.from_record_number = maximal_record_number

                job.log(
                    "New maximal record number: {} (maximum to reach: {}) for {}"
                    .format(maximal_record_number, self.to_record_number, job))

                self.retrieve_ext_com = None

            if com_finished or is_initial:
                # check whether to start next run

                if self.from_record_number >= self.to_record_number:
                    do_continue = False
                    job.log("Reached maximal record number {} (by {}).".format(
                        self.to_record_number, self.from_record_number))
                else:
                    # start next run
                    cmd = WmiUtils.get_wmic_cmd(
                        username=job.username,
                        password=job.password,
                        target_ip=job.target_ip,
                        columns=[
                            "RecordNumber, Message, Category, CategoryString, ComputerName, EventCode, "
                            +
                            "EventIdentifier, InsertionStrings, SourceName, TimeGenerated, TimeWritten,  "
                            + "EventType, Type, User"
                        ],
                        table="Win32_NTLogEvent",
                        where_clause=
                        "WHERE Logfile = '{}' AND RecordNumber > {} and RecordNumber <= {}"
                        .format(
                            job.logfile_name,
                            self.from_record_number,
                            self.get_next_upper_limit(self.from_record_number),
                        ))
                    job.log("querying entries from {} to {} for {}".format(
                        self.from_record_number,
                        self.get_next_upper_limit(self.from_record_number),
                        job,
                    ))

                    self.retrieve_ext_com = ExtCom(
                        job.log, cmd, debug=True, shell=False
                    )  # shell=False since args must not be parsed again
                    self.retrieve_ext_com.run()

            return do_continue