Add query function, docs

Add query function that accepts XPath queries
Add docs and CLI examples for all functions
Fix issues with the get_filtered function
This commit is contained in:
Twangboy 2022-08-24 16:15:32 -06:00 committed by Megan Wilhite
parent 2902f2d8a3
commit 8660718fa7
5 changed files with 819 additions and 436 deletions

726
salt/modules/win_event.py Normal file
View file

@ -0,0 +1,726 @@
"""
A module for working with the Windows Event log system.
"""
# https://docs.microsoft.com/en-us/windows/win32/eventlog/event-logging
from __future__ import absolute_import
# Import Python libs
import collections
import logging
import xmltodict
# Import Salt Libs
import salt.utils.platform
import salt.utils.stringutils
from salt.exceptions import CommandExecutionError
# Import Third Party Libs
try:
import win32evtlog
import win32evtlogutil
import winerror
import pywintypes
IMPORT_STATUS = True
except ImportError:
IMPORT_STATUS = False
# keys of all the parts of a Event supported by the API
EVENT_PARTS = ("closingRecordNumber",
"computerName",
"data",
"eventCategory",
"eventID",
"eventType",
"recordNumber",
"reserved",
"reservedFlags",
"sid",
"sourceName",
"stringInserts",
"timeGenerated",
"timeWritten",
)
EVENT_TYPES = {
"Success": 0x0000,
"Error": 0x0001,
"Warning": 0x0002,
"Information": 0x0004,
"AuditSuccess": 0x0008,
"AuditFailure": 0x0010,
0x0000: "Success",
0x0001: "Error",
0x0002: "Warning",
0x0004: "Information",
0x0008: "AuditSuccess",
0x0010: "AuditFailure",
}
# keys time
TIME_PARTS = ("year",
"month",
"day",
"hour",
"minute",
"second",
)
TimeTuple = collections.namedtuple("TimeTuple", "year, month, day, hour, minute, second")
log = logging.getLogger(__name__)
__virtualname__ = "win_event"
def __virtual__():
"""
Load only on minions running on Windows.
"""
if not salt.utils.platform.is_windows():
return False, "win_event: Most be on Windows"
if not IMPORT_STATUS:
return False, "win_event: Missing PyWin32"
return __virtualname__
def _to_bytes(data, encoding="utf-8", encode_keys=False):
"""
Convert string objects to byte objects.
.. warning::
This function will destroy the data object and objects that data links
to.
Args:
data (object): The string object to encode
encoding(str): The encoding type
encode_keys(bool): If false key strings will not be turned into bytes
Returns:
(object): An object with the new encoding
"""
if isinstance(data, dict):
new_dict = {}
# recursively check every item in the dict
for key in data:
item = _to_bytes(data[key], encoding)
if encode_keys:
# keys that are strings most be made into bytes
key = _to_bytes(key, encoding)
new_dict[key] = item
data = new_dict
elif isinstance(data, list):
new_list = []
# recursively check every item in the list
for item in data:
new_list.append(_to_bytes(item, encoding))
data = new_list
elif isinstance(data, tuple):
new_list = []
# recursively check every item in the tuple
for item in data:
new_list.append(_to_bytes(item, encoding))
data = tuple(new_list)
elif isinstance(data, str):
# encode string data to bytes
data = data.encode(encoding)
return data
def _raw_time(time):
"""
Will make a pywintypes.datetime into a TimeTuple.
Args:
time (ob): A datetime object
Returns:
TimeTuple: A TimeTuple
"""
return TimeTuple._make((time.year, time.month, time.day, time.hour, time.minute, time.second))
def _make_event_dict(event):
"""
Will make a PyEventLogRecord into a dictionary
Args:
event (PyEventLogRecord): An event to convert to a dictionary
Returns:
dict: A dictionary containing the event information
"""
event_dict = {}
for event_part in EVENT_PARTS:
# get object value and add it to the event dict
event_dict[event_part] = getattr(event, event_part[0].upper() + event_part[1:], None)
# format items
event_dict["eventID"] = winerror.HRESULT_CODE(event_dict["eventID"])
if event_dict["sid"] is not None:
event_dict["sid"] = event_dict["sid"].GetSidIdentifierAuthority()
event_dict["timeGenerated"] = _raw_time(event_dict["timeGenerated"])
event_dict["timeWritten"] = _raw_time(event_dict["timeWritten"])
return _to_bytes(event_dict)
def _get_handle(log_name):
"""
Will try to open a PyHANDLE to the Event System
Args:
log_name (str): The name of the log to open
Returns:
PyHANDLE: A handle to the event log
"""
# TODO: upgrade windows token
# "log close" can fail if this is not done
try:
return win32evtlog.OpenEventLog(None, log_name)
except pywintypes.error:
raise FileNotFoundError(
"{0} log can not be found or access was denied!".format(log_name)
)
def _close_handle(handle):
"""
Will close the handle to the event log
Args:
handle (PyHANDLE): The handle to the event log to close
"""
# TODO: downgrade windows token
win32evtlog.CloseEventLog(handle)
def _event_generator(log_name):
"""
Get all log events one by one. Events are not ordered
Args:
log_name(str): The name of the log to retrieve
Yields:
dict: A dictionary object for each event
"""
# Get events from the local machine (None)
handle = _get_handle(log_name)
flags = win32evtlog.EVENTLOG_BACKWARDS_READ | win32evtlog.EVENTLOG_SEQUENTIAL_READ
while True:
# get list of some of the events
events = win32evtlog.ReadEventLog(handle, flags, 0)
if not events:
# event log was updated and events are not ready to be given yet
# rather than wait just return
break
for event in events:
yield _make_event_dict(event)
_close_handle(handle)
def _event_generator_sorted(log_name):
"""
Sorts the results of the event generator
Args:
log_name (str): The name of the log to retrieve
Yields:
dict: A dictionary object for each event
"""
for event in _event_generator(log_name):
event_info = {}
for part in event:
event_info[part] = event[part]
for spot, key in enumerate(TIME_PARTS):
event_info[key] = event["timeGenerated"][spot]
yield event, event_info
def _event_generator_filter(log_name, all_requirements=True, **kwargs):
"""
Will find events that meet the requirements in the filter
Args:
log_name (str): The name of the log to retrieve
all_requirements (bool): Should the results match all requirements.
``True`` matches all requirements. ``False`` matches any
requirement.
Kwargs:
Can be any item in the return for the event. Common kwargs are:
eventID (int): The event ID number
eventType (int): The event type number. Valid options and their
corresponding meaning are:
- 0 : Success
- 1 : Error
- 2 : Warning
- 4 : Information
- 8 : Audit Success
- 10 : Audit Failure
year (int): The year
month (int): The month
day (int): The day of the month
hour (int): The hour
minute (int): The minute
second (int): The second
eventCategory (int): The event category number
sid (sid): The SID of the user that created the event
sourceName (str): The name of the event source
Yields:
dict: A dictionary object for each event
CLI Example:
.. code-block::python
# Return all events from the Security log with an ID of 1100
_event_generator_filter("Security", eventID=1100)
# Return all events from the System log with an Error (1) event type
_event_generator_filter("System", eventType=1)
# Return all events from System log with an Error (1) type, source is Service Control Manager, and data is netprofm
_event_generator_filter("System", eventType=1, sourceName="Service Control Manager", data="netprofm")
"""
for event, info in _event_generator_sorted(log_name):
if all_requirements:
# all keys need to match each other
for key in kwargs:
# ignore kwargs built-ins
if key.startswith("__"):
continue
# ignore function parameters
if key in ["log_name", "all_arguments"]:
continue
# Try to handle bytestrings
if isinstance(info[key], bytes):
# try utf-8 first
try:
log.trace(
"utf-8: Does %s == %s",
repr(kwargs[key]),
repr(info[key].decode("utf-8"))
)
if kwargs[key] != info[key].decode("utf-8"):
# try utf-16 and strip null bytes
try:
log.trace(
"utf-16: Does %s == %s",
repr(kwargs[key]),
repr(info[key].decode("utf-16").strip("\x00"))
)
if kwargs[key] != info[key].decode("utf-16").strip("\x00"):
break
except UnicodeDecodeError:
log.trace("Failed to decode (utf-16): %s", info[key])
break
except UnicodeDecodeError:
log.trace("Failed to decode (utf-8): %s", info[key])
break
elif kwargs[key] != info[key]:
break
else:
yield info
else:
# just a single key pair needs to match
for key in kwargs:
# ignore kwargs built-ins
if key.startswith("__"):
continue
# ignore function parameters
if key in ["log_name", "all_arguments"]:
continue
# Try to handle bytestrings
if isinstance(info[key], bytes):
# try utf-8 first
try:
log.trace(
"utf-8: Does %s == %s",
repr(kwargs[key]),
repr(info[key].decode("utf-8"))
)
if kwargs[key] == info[key].decode("utf-8"):
yield info
except UnicodeDecodeError:
log.trace("Failed to decode (utf-8): %s", info[key])
# try utf-16 and strip null bytes
try:
log.trace(
"utf-16: Does %s == %s",
repr(kwargs[key]),
repr(info[key].decode("utf-16").strip("\x00"))
)
if kwargs[key] == info[key].decode("utf-16").strip("\x00"):
yield info
except UnicodeDecodeError:
log.trace("Failed to decode (utf-16): %s", info[key])
break
elif kwargs[key] == info[key]:
yield info
def get(log_name):
"""
Get events from the specified log. Get a list of available logs using the
:py:func:`win_event.get_log_names <salt.modules.win_event.get_log_names>`
function.
.. warning::
Running this command on a log with thousands of events, such as the
``Applications`` log, can take a long time.
Args:
log_name(str): The name of the log to retrieve.
Returns
tuple: A tuple of events as dictionaries
CLI Example:
.. code-block::bash
salt '*' win_event.get Application
"""
return tuple(_event_generator(log_name))
def query(log_name, query_text=None, records=20, latest=True, raw=False):
"""
Query a log for a specific event_id. Return the top number of records
specified. Use the
:py:func:`win_event.get_log_names <salt.modules.win_event.get_log_names>`
to see a list of available logs on the system.
.. Note::
You can use the Windows Event Viewer to create the XPath query for the
``query_text`` parameter. Click on ``Filter Current Log``, configure the
filter, then click on the XML tab. Copy the text between the two
``<Select>`` tags. This will be the contents of the ``query_text``
parameter. You will have to convert some codes. For example, ``&gt;``
becomes ``>``, ``&lt;`` becomes ``<``. Additionally, you'll need to
put spaces between comparison operators. For example: ``this >= that``.
Args:
log_name (str): The name of the log to query
query_text (str): The filter to apply to the log
records (int): The number of records to return
latest (bool): ``True`` will return the newest events. ``False`` will
return the oldest events. Default is ``True``
raw (bool): ``True`` will return the raw xml results. ``False`` will
return the xml converted to a dictionary. Default is ``False``
Returns:
list: A list of dict objects that contain information about the event
CLI Example:
.. code-block::bash
# Return the 20 most recent events from the Application log with an event ID of 22
salt '*' win_event.query Application "*[System[(EventID=22)]]"
# Return the 20 most recent events from the Application log with an event ID of 22
# Return raw xml
salt '*' win_event.query Application "*[System[(EventID=22)]]" raw=True
# Return the 20 oldest events from the Application log with an event ID of 22
salt '*' win_event.query Application "*[System[(EventID=22)]]" latest=False
# Return the 20 most recent Critical (1) events from the Application log in the last 12 hours
salt '*" win_event.query Application "*[System[(Level=1) and TimeCreated[timediff(@SystemTime) <= 43200000]]]"
# Return the 5 most recent Error (2) events from the application log
salt '*" win_event.query Application "*[System[(Level=2)]]" records=5
# Return the 20 most recent Warning (3) events from the Windows PowerShell log where the Event Source is PowerShell
salt '*" win_event.query "Windows PowerShell" "*[System[Provider[@Name='PowerShell'] and (Level=3)]]"
# Return the 20 most recent Information (0 or 4) events from the Microsoft-Windows-PowerShell/Operational on 2022-08-24 with an Event ID of 4103
salt '*" win_event.query "Microsoft-Windows-PowerShell/Operational" "*[System[(Level=4 or Level=0) and (EventID=4103) and TimeCreated[@SystemTime >= '2022-08-24T06:00:00.000Z']]]"
# Return the 20 most recent Information (0 or 4) events from the Microsoft-Windows-PowerShell/Operational within the last hour
salt '*" win_event.query "Microsoft-Windows-PowerShell/Operational" "*[System[(Level=4 or Level=0) and TimeCreated[timediff(@SystemTime) <= 3600000]]]"
"""
if not isinstance(latest, bool):
raise CommandExecutionError("latest must be a boolean")
direction = win32evtlog.EvtQueryReverseDirection
if not latest:
direction = win32evtlog.EvtQueryForwardDirection
results = win32evtlog.EvtQuery(
log_name,
direction,
query_text,
None
)
event_list = []
for evt in win32evtlog.EvtNext(results, records):
if raw:
res = win32evtlog.EvtRender(evt, 1)
else:
res = xmltodict.parse(win32evtlog.EvtRender(evt, 1))
event_list.append(res)
return event_list
def get_sorted(log_name):
"""
Make a list of events sorted by date.
.. warning::
Running this command on a log with thousands of events, such as the
``Applications`` log, can take a long time.
Args:
log_name (str): The name of the log to retrieve
Returns:
dict: A dictionary of events
CLI Example:
.. code-block::bash
# This command can take a long time
salt "*" win_event.get_sorted Application
"""
event_info = {event_part: collections.defaultdict(list) for event_part in EVENT_PARTS + TIME_PARTS}
for event, info in _event_generator_sorted(log_name):
for part in info:
event_info[part][info.get(part)].append(event)
return event_info
def get_filtered(log_name, all_requirements=True, **kwargs):
"""
Will find events that match the fields and values specified in the kwargs.
.. warning::
Running this command on a log with thousands of events, such as the
``Applications`` log, can take a long time.
Args:
log_name (str): The name of the log to retrieve
all_requirements (bool): ``True`` matches all requirements. ``False``
matches any requirement. Default is ``True``
Kwargs:
Can be any item in the return for the event. Common kwargs are:
eventID (int): The event ID number
eventType (int): The event type number. Valid options and their
corresponding meaning are:
- 0 : Success
- 1 : Error
- 2 : Warning
- 4 : Information
- 8 : Audit Success
- 10 : Audit Failure
year (int): The year
month (int): The month
day (int): The day of the month
hour (int): The hour
minute (int): The minute
second (int): The second
eventCategory (int): The event category number
sid (sid): The SID of the user that created the event
sourceName (str): The name of the event source
Returns:
tuple: A tuple of dicts of each filtered event
CLI Example:
.. code-block::bash
# Return all events from the Security log with an ID of 1100
salt "*" win_event.get_filtered Security eventID=1100
# Return all events from the System log with an Error (1) event type
salt "*" win_event.get_filtered System eventType=1
# Return all events from System log with an Error (1) type, source is Service Control Manager, and data is netprofm
salt "*" win_event.get_filtered System eventType=1 sourceName="Service Control Manager" data="netprofm"
# Return events from the System log that match any of the kwargs below
salt "*" win_event.get_filtered System eventType=1 sourceName="Service Control Manager" data="netprofm" all_requirements=False
"""
return tuple(_event_generator_filter(log_name, all_requirements, **kwargs))
def get_log_names():
"""
Get a list of event logs available on the system
Returns:
list: A list of event logs available on the system
CLI Example:
.. code-block::bash
salt "*" win_event.get_log_names
"""
h = win32evtlog.EvtOpenChannelEnum(None)
log_names = []
while win32evtlog.EvtNextChannelPath(h) is not None:
log_names.append(win32evtlog.EvtNextChannelPath(h))
return log_names
def add(
log_name,
event_id,
event_category=0,
event_type=None,
event_strings=None,
event_data=None,
event_sid=None,
):
"""
Adds an event to the application event log.
Args:
log_name (str): The name of the application or source
event_id (int): The event ID
event_category (int): The event category
event_type (str): The event category. Must be one of:
- Success
- Error
- Warning
- Information
- AuditSuccess
- AuditFailure
event_strings (list): A list of strings
event_data (bytes): Event data. Strings will be converted to bytes
event_sid (sid): The SID for the event
Raises:
CommandExecutionError: event_id is not an integer
CommandExecutionError: event_category is not an integer
CommandExecutionError: event_type is not one of the valid event types
CommandExecutionError: event_strings is not a list or string
CLI Example:
.. code-block:: bash
# A simple Application event log warning entry
salt '*' win_event.add Application 1234 12 Warning
# A more complex System event log information entry
salt '*' win_event.add System 1234 12 Information "['Event string data 1', 'Event string data 2']" "Some event data"
# Log to the System Event log with the source "Service Control Manager"
salt '*' win_event.add "Service Control Manager" 1234 12 Warning "['Event string data 1', 'Event string data 2']" "Some event data"
# Log to the PowerShell event log with the source "PowerShell (PowerShell)"
salt-call --local win_event.add "PowerShell" 6969 12 Warning
"""
try:
event_id = int(event_id)
except TypeError:
raise CommandExecutionError("event_id must be an integer")
try:
event_category = int(event_category)
except TypeError:
raise CommandExecutionError("event_category must be an integer")
if event_type is None:
event_type = EVENT_TYPES["Error"]
elif event_type not in EVENT_TYPES:
msg = "Incorrect event type: {}".format(event_type)
raise CommandExecutionError(msg)
else:
event_type = EVENT_TYPES[event_type]
if event_strings is not None:
if isinstance(event_strings, str):
event_strings = [event_strings]
elif not isinstance(event_strings, list):
raise CommandExecutionError("event_strings must be a list")
if event_data is not None:
event_data = salt.utils.stringutils.to_bytes(event_data)
# https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-reporteventw
win32evtlogutil.ReportEvent(
appName=log_name,
eventID=int(event_id),
eventCategory=int(event_category),
eventType=event_type,
strings=event_strings,
data=event_data,
sid=event_sid,
)
def clear_log(log_name):
"""
Clears the specified event log.
.. note::
A clear log event will be added to the log after it is cleared.
Args:
log_name (str): The name of the log to clear
CLI Example:
.. code-block::bash
salt "*" win_event.clear_log Application
"""
handle = _get_handle(log_name)
win32evtlog.ClearEventLog(handle, log_name)
_close_handle(handle)
def count(log_name):
"""
Gets the number of events in the specified.
Args:
log_name (str): The name of the log
Returns:
int: The number of events the log contains
CLI Example:
.. code-block::bash
salt "*" win_event.count Application
"""
handle = _get_handle(log_name)
number_of_events = win32evtlog.GetNumberOfEventLogRecords(handle)
_close_handle(handle)
return number_of_events

View file

@ -1,411 +0,0 @@
# -*- coding: utf-8 -*-
'''
Gives SaltStack access to Windows event log
Charles McMarrow <cmcmarrow@saltstack.com>
'''
from __future__ import absolute_import
# Import Python libs
import logging
import collections
# Import Salt Libs
import salt.utils.platform
# Import Third Party Libs
try:
import win32evtlog
import win32evtlogutil
import winerror
import pywintypes
IMPORT_STATUS = True
except ImportError:
IMPORT_STATUS = False
# keys of all the parts of a Event supported by the API
EVENT_PARTS = ('closingRecordNumber',
'computerName',
'data',
'eventCategory',
'eventID',
'eventType',
'recordNumber',
'reserved',
'reservedFlags',
'sid',
'sourceName',
'stringInserts',
'timeGenerated',
'timeWritten',
)
# keys time
TIME_PARTS = ('year',
'month',
'day',
'hour',
'minute',
'second',
)
TimeTuple = collections.namedtuple('TimeTuple', 'year, month, day, hour, minute, second')
log = logging.getLogger(__name__)
__virtualname__ = 'win_event_viewer'
def __virtual__():
'''
Load only on minions running on Windows.
'''
if not salt.utils.platform.is_windows() or not IMPORT_STATUS:
return False, 'win_event_viewer: most be on windows'
return __virtualname__
def _change_str_to_bytes(data, encoding='utf-8', encode_keys=False):
'''
Convert string objects to byte objects.
This function will destroy the data object and objects that data links to.
data
object
encoding
str
encode_keys
bool
if false key strings will not be turned into bytes
return
new object
'''
if isinstance(data, dict):
new_dict = {}
# recursively check every item in dict
for key in data:
item = _change_str_to_bytes(data[key], encoding)
if encode_keys:
# keys that are strings most be made into bytes
key = _change_str_to_bytes(key, encoding)
new_dict[key] = item
data = new_dict
elif isinstance(data, list):
new_list = []
# recursively check every item in list
for item in data:
new_list.append(_change_str_to_bytes(item, encoding))
data = new_list
elif isinstance(data, tuple):
new_list = []
# recursively check every item in list
for item in data:
new_list.append(_change_str_to_bytes(item, encoding))
data = tuple(new_list)
elif isinstance(data, str):
# data is turning into bytes because if was a string
data = data.encode(encoding)
return data
def _get_raw_time(time):
'''
Will make a pywintypes.datetime into a TimeTuple.
time
pywintypes.datetime
return
TimeTuple
'''
return TimeTuple._make((time.year, time.month, time.day, time.hour, time.minute, time.second))
def make_event_dict(event):
'''
Will make a PyEventLogRecord into a dict.
event
PyEventLogRecord
return
dict
'''
event_dict = {}
for event_part in EVENT_PARTS:
# get object value and add it to the event dict
event_dict[event_part] = getattr(event, event_part[0].upper() + event_part[1:], None)
# format items
event_dict['eventID'] = winerror.HRESULT_CODE(event_dict['eventID'])
if event_dict['sid'] is not None:
event_dict['sid'] = event_dict['sid'].GetSidIdentifierAuthority()
event_dict['timeGenerated'] = _get_raw_time(event_dict['timeGenerated'])
event_dict['timeWritten'] = _get_raw_time(event_dict['timeWritten'])
return _change_str_to_bytes(event_dict)
def _get_event_handler(log_name, target_computer=None):
'''
Will try to open a PyHANDLE.
log_name
str
target_computer
None or str
return
PyHANDLE
'''
# TODO: upgrade windows token
# 'log close' can fail if this is not done
try:
return win32evtlog.OpenEventLog(target_computer, log_name)
except pywintypes.error:
raise FileNotFoundError('Log "{0}" of "{1}" can not be found or access was denied!'.format(log_name,
target_computer))
def _close_event_handler(handler):
'''
Will close the event handler.
handler
PyHANDLE
'''
# TODO: downgrade windows token
win32evtlog.CloseEventLog(handler)
def get_event_generator(log_name, target_computer=None, raw=False):
'''
Will get all log events one by one.
Events are not in exact order.
log_name
str
target_computer
None or str
raw
bool
True: PyEventLogRecord
False: dict
return
PyEventLogRecord or dict
'''
handler = _get_event_handler(log_name, target_computer)
flags = win32evtlog.EVENTLOG_BACKWARDS_READ | win32evtlog.EVENTLOG_SEQUENTIAL_READ
event_count = 0
while win32evtlog.GetNumberOfEventLogRecords(handler) > event_count:
# get list of some of the events
events = win32evtlog.ReadEventLog(handler, flags, 0)
if not events:
# event log was updated and events are not ready to be given yet
# rather than wait just return
break
for event in events:
event_count += 1
if raw:
yield event
else:
yield make_event_dict(event)
_close_event_handler(handler)
def get_events(log_name, target_computer=None, raw=False):
'''
Convert pywinypes.datetime into a TimeTuple.
log_name
str
target_computer
None or str
raw
bool
True: PyEventLogRecord
False: dict
return
tuple
'''
return tuple(get_event_generator(log_name, target_computer, raw))
def get_event_sorted_by_info_generator(log_name, target_computer=None):
'''
Makes keys to event
log_name
str
target_computer
None or str
return
dict
'''
for event in get_event_generator(log_name, target_computer):
event_info = {}
for part in event:
event_info[part] = event[part]
for spot, key in enumerate(TIME_PARTS):
event_info[key] = event['timeGenerated'][spot]
yield event, event_info
def get_events_sorted_by_info(log_name, target_computer=None):
'''
Make dict of sorted events
log_name
str
target_computer
None or str
return
dict
'''
event_info = {event_part: collections.defaultdict(list) for event_part in EVENT_PARTS + TIME_PARTS}
for event, info in get_event_sorted_by_info_generator(log_name, target_computer):
for part in info:
event_info[part][info.get(part)].append(event)
return event_info
def get_event_filter_generator(log_name, target_computer=None, all_requirements=True, **kwargs):
'''
Will find events that meet the requirements
log_name
str
target_computer
None or str
all_requirements
bool
True: all requirements most be meet
False: only a single requirement most be meet
kwargs
requirements for the events
return
dict
'''
for event, info in get_event_sorted_by_info_generator(log_name, target_computer):
if all_requirements:
# all keys need to match each other
for key in kwargs:
if kwargs[key] != info[key]:
break
else:
yield event
else:
# just a single key par needs to match
if any([kwargs[key] == info[key] for key in kwargs]):
yield event
def get_events_filter(log_name, target_computer=None, all_requirements=True, **kwargs):
'''
Find events that meet the requirements.
log_name
str
target_computer
None or str
all_requirements
bool
True: all requirements most be meet
False: only a single requirement most be meet
kwargs
requirements for the events
return
list
'''
return tuple(get_event_filter_generator(log_name, target_computer, all_requirements, **kwargs))
def log_event(application_name, event_id, **kwargs):
'''
Adds event to application log.
application_name
str
event_id
int
kwargs
parts of event
'''
win32evtlogutil.ReportEvent(application_name, event_id, **kwargs)
def clear_log(log_name, target_computer=None):
'''
Clears event log.
A clear log event will be add it after the log was clear.
log_name
str
target_computer
None or str
'''
handler = _get_event_handler(log_name, target_computer)
win32evtlog.ClearEventLog(handler, log_name)
_close_event_handler(handler)
def get_number_of_events(log_name, target_computer=None):
'''
Gets the number of events in a log.
log_name
str
target_computer
None or str
return
int
'''
handler = _get_event_handler(log_name, target_computer)
number_of_events = win32evtlog.GetNumberOfEventLogRecords(handler)
_close_event_handler(handler)
return number_of_events

View file

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.unit import skipIf
from tests.support.helpers import destructiveTest
# Import Salt Libs
import salt.utils.platform
# All test will add to a log in some way.
# Because of this they all most be destructive test.
@destructiveTest
@skipIf(not salt.utils.platform.is_windows(), 'Tests for only Windows')
class WinEventViewerTest(ModuleCase):
def test_get_number_of_events(self):
'''
test get_number_of_events
'''
ret = self.run_function('win_event_viewer.get_number_of_events', ('System',), timeout=180)
self.assertEqual(int(ret), abs(int(ret)))

View file

@ -0,0 +1,15 @@
import pytest
pytestmark = [
pytest.mark.windows_whitelisted,
]
@pytest.fixture(scope="module")
def win_event(modules):
return modules.win_event
def test_get(win_event):
events = win_event.get()
assert events == {}

View file

@ -0,0 +1,78 @@
import datetime
import pytest
import salt.modules.win_event as win_event
pytestmark = [
pytest.mark.windows_whitelisted,
pytest.mark.skip_unless_on_windows,
pytest.mark.destructive_test,
]
def test__to_bytes_utf8():
data = {'key1': 'item1',
'key2': [1, 2, 'item2'],
'key3': 45,
45: str}
new_data = win_event._to_bytes(data, 'utf-8', False)
assert 'key1' in new_data
assert new_data['key1'] == 'item1'.encode('utf-8')
assert new_data['key2'][2] == 'item2'.encode('utf-8')
def test__to_bytes_cp1252():
data = {'key1': 'item1',
'key2': [1, 2, 'item2'],
'key3': 45,
45: str}
new_data = win_event._to_bytes(data, 'CP1252', True)
assert b'key1' in new_data
assert b'key2' in new_data
assert b'key3' in new_data
assert new_data['key1'.encode('CP1252')] == 'item1'.encode('CP1252')
assert new_data['key2'.encode('CP1252')][2] == 'item2'.encode('CP1252')
def test__raw_time():
raw_time = win_event._raw_time(datetime.datetime(2019, 7, 2, 10, 8, 19))
assert raw_time == (2019, 7, 2, 10, 8, 19)
def test_count():
"""
Test win_event.count
"""
ret = win_event.count("System")
assert ret > 0
def test_security():
ret = win_event.get("Security")
print(len(ret))
assert len(ret) > 0
def test_windows_powershell():
ret = win_event.get("Windows PowerShell")
print(len(ret))
assert len(ret) > 0
def test_operational():
ret = win_event.get("Operational")
print(len(ret))
assert len(ret) > 0
def test_query():
ret = win_event.query("Microsoft-Windows-TerminalServices-LocalSessionManager/Operational", 22)
print(len(ret))
assert len(ret) > 0