Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate Python win32_event_log check implementation #16108

Merged
merged 5 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions win32_event_log/assets/configuration/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ files:
description: |
Whether or not to use a mode of operation that is now unmaintained and will be removed in a future version.

When set to `true`, WMI is used to collect events.
/\ WARNING /\
This mode, by nature of the underlying technology, is significantly more resource intensive.

Expand All @@ -58,6 +59,14 @@ files:
type: boolean
display_default: true
example: false
- name: legacy_mode_v2
description: |
Whether or not to use a mode of operation that is now unmaintained and will be removed in a future version.

When set to `true`, the Python Event Log API is used to collect events.
value:
type: boolean
example: false
- template: init_config/default
- template: instances
overrides:
Expand Down Expand Up @@ -271,11 +280,6 @@ files:
of the current user will be used.
value:
type: string
- name: timeout
description: The number of seconds to wait for new event signals.
value:
type: integer
example: 5
- name: payload_size
description: |
The number of events to request at a time.
Expand All @@ -292,10 +296,28 @@ files:
This is useful for preventing duplicate events being sent as a consequence of Agent restarts.
value:
type: integer
- name: legacy_mode_v2
description: |
Whether or not to use a mode of operation that is now unmaintained and will be removed in a future version.

When set to `true`, the Python Event Log API is used to collect events.
value:
type: boolean
example: false
- name: timeout
description: |
The number of seconds to wait for new event signals.

Note: This is only used when `legacy_mode_v2` is set to `true`.
Starting in Agent version 7.50.0 this option is no longer applicable and can be removed.
value:
type: integer
example: 5
- name: legacy_mode
description: |
Whether or not to use a mode of operation that is now unmaintained and will be removed in a future version.

When set to `true`, WMI is used to collect events.
/\ WARNING /\
This mode, by nature of the underlying technology, is significantly more resource intensive.

Expand Down
1 change: 1 addition & 0 deletions win32_event_log/changelog.d/16108.deprecated
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecate the Python implementation of the check. Setting `legacy_mode: false` now runs a check defined in [datadog-agent](https://github.com/DataDog/datadog-agent). See [datadog-agent#20701](https://github.com/DataDog/datadog-agent/pull/20701) for more information on the new check implementation. Set `legacy_mode_v2: true` to revert to the Python implementation of the check. The Python implementation may be removed in a future version of the agent.
34 changes: 28 additions & 6 deletions win32_event_log/datadog_checks/win32_event_log/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from six import PY2

from datadog_checks.base import AgentCheck, ConfigurationError, is_affirmative
from datadog_checks.base.errors import SkipInstanceError
from datadog_checks.base.utils.common import exclude_undefined_keys
from datadog_checks.base.utils.time import get_timestamp

Expand Down Expand Up @@ -69,14 +70,36 @@ class Win32EventLogCheck(AgentCheck, ConfigMixin):
def __new__(cls, name, init_config, instances):
instance = instances[0]

# default to legacy mode for configuration backwards compatibility
init_config_legacy_mode = is_affirmative(init_config.get('legacy_mode', True))
init_config_legacy_mode = init_config.get('legacy_mode', None)
init_config_legacy_mode_v2 = init_config.get('legacy_mode_v2', None)
# If legacy_mode is unset for an instance, default to the init_config option
instance_legacy_mode = is_affirmative(instance.get('legacy_mode', init_config_legacy_mode))
if PY2 or instance_legacy_mode:
use_legacy_mode = instance.get('legacy_mode', init_config_legacy_mode)
use_legacy_mode_v2 = instance.get('legacy_mode_v2', init_config_legacy_mode_v2)

# If legacy_mode and legacy_mode_v2 are unset, default to legacy mode for configuration backwards compatibility
if use_legacy_mode is None and not is_affirmative(use_legacy_mode_v2):
use_legacy_mode = True

use_legacy_mode = is_affirmative(use_legacy_mode)
use_legacy_mode_v2 = is_affirmative(use_legacy_mode_v2)

if use_legacy_mode and use_legacy_mode_v2:
raise ConfigurationError(
"legacy_mode and legacy_mode_v2 are both true. Each instance must set a single mode to true."
)

if use_legacy_mode:
# Supports PY2 and PY3
return Win32EventLogWMI(name, init_config, instances)
else:
elif use_legacy_mode_v2:
# Supports PY3
if PY2:
raise ConfigurationError("legacy_mode_v2 is not supported on Python2")
return super(Win32EventLogCheck, cls).__new__(cls)
else:
raise SkipInstanceError(
"Set the legacy_mode and legacy_mode_v2 options to configure the implementation to use."
)

def __init__(self, name, init_config, instances):
super(Win32EventLogCheck, self).__init__(name, init_config, instances)
Expand Down Expand Up @@ -283,7 +306,6 @@ def consume_events(self):

def poll_events(self):
while True:

# IMPORTANT: the subscription starts immediately so you must consume before waiting for the first signal
while True:
# https://docs.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtnext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def shared_legacy_mode():
return True


def shared_legacy_mode_v2():
return False


def shared_tag_event_id():
return False

Expand Down Expand Up @@ -64,6 +68,10 @@ def instance_legacy_mode():
return True


def instance_legacy_mode_v2():
return False


def instance_min_collection_interval():
return 15

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class InstanceConfig(BaseModel):
included_messages: Optional[tuple[str, ...]] = None
interpret_messages: Optional[bool] = None
legacy_mode: Optional[bool] = None
legacy_mode_v2: Optional[bool] = None
log_file: Optional[tuple[str, ...]] = None
message_filters: Optional[tuple[str, ...]] = None
metric_patterns: Optional[MetricPatterns] = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class SharedConfig(BaseModel):
event_priority: Optional[Literal['normal', 'low']] = None
interpret_messages: Optional[bool] = None
legacy_mode: Optional[bool] = None
legacy_mode_v2: Optional[bool] = None
service: Optional[str] = None
tag_event_id: Optional[bool] = None
tag_sid: Optional[bool] = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,21 @@ init_config:
## @param legacy_mode - boolean - optional - default: true
## Whether or not to use a mode of operation that is now unmaintained and will be removed in a future version.
##
## When set to `true`, WMI is used to collect events.
## /\ WARNING /\
## This mode, by nature of the underlying technology, is significantly more resource intensive.
##
## Setting this option to `false` is only supported on Agent versions 7 and above.
#
legacy_mode: false

## @param legacy_mode_v2 - boolean - optional - default: false
## Whether or not to use a mode of operation that is now unmaintained and will be removed in a future version.
##
## When set to `true`, the Python Event Log API is used to collect events.
#
# legacy_mode_v2: false

## @param service - string - optional
## Attach the tag `service:<SERVICE>` to every metric, event, and service check emitted by this integration.
##
Expand Down Expand Up @@ -222,11 +230,6 @@ instances:
#
# domain: <DOMAIN>

## @param timeout - integer - optional - default: 5
## The number of seconds to wait for new event signals.
#
# timeout: 5

## @param payload_size - integer - optional - default: 10
## The number of events to request at a time.
##
Expand All @@ -242,9 +245,25 @@ instances:
#
# bookmark_frequency: <BOOKMARK_FREQUENCY>

## @param legacy_mode_v2 - boolean - optional - default: false
## Whether or not to use a mode of operation that is now unmaintained and will be removed in a future version.
##
## When set to `true`, the Python Event Log API is used to collect events.
#
# legacy_mode_v2: false

## @param timeout - integer - optional - default: 5
## The number of seconds to wait for new event signals.
##
## Note: This is only used when `legacy_mode_v2` is set to `true`.
## Starting in Agent version 7.50.0 this option is no longer applicable and can be removed.
#
# timeout: 5

## @param legacy_mode - boolean - optional - default: true
## Whether or not to use a mode of operation that is now unmaintained and will be removed in a future version.
##
## When set to `true`, WMI is used to collect events.
## /\ WARNING /\
## This mode, by nature of the underlying technology, is significantly more resource intensive.
##
Expand Down
2 changes: 1 addition & 1 deletion win32_event_log/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ classifiers = [
"Private :: Do Not Upload",
]
dependencies = [
"datadog-checks-base>=32.6.0",
"datadog-checks-base>=34.2.0",
]
dynamic = [
"version",
Expand Down
2 changes: 1 addition & 1 deletion win32_event_log/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
EVENT_CATEGORY = 42

INSTANCE = {
'legacy_mode': False,
'legacy_mode_v2': True,
'timeout': 2,
'path': 'Application',
'filters': {'source': [EVENT_SOURCE]},
Expand Down
80 changes: 61 additions & 19 deletions win32_event_log/tests/legacy/test_win32_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import pytest
from six import PY2

from datadog_checks.base import ConfigurationError
from datadog_checks.base.errors import SkipInstanceError
from datadog_checks.win32_event_log import Win32EventLogCheck
from datadog_checks.win32_event_log.legacy import Win32EventLogWMI

Expand All @@ -30,43 +32,83 @@ def test_deprecation_notice(dd_run_check):

@pytest.mark.parametrize('shared_legacy_mode', [None, False, True])
@pytest.mark.parametrize('instance_legacy_mode', [None, False, True])
def test_legacy_mode_select(new_check, shared_legacy_mode, instance_legacy_mode):
@pytest.mark.parametrize('shared_legacy_mode_v2', [None, False, True])
@pytest.mark.parametrize('instance_legacy_mode_v2', [None, False, True])
def test_legacy_mode_select(
new_check, shared_legacy_mode, instance_legacy_mode, shared_legacy_mode_v2, instance_legacy_mode_v2
):
instance = {}
init_config = None

init_config = {}
if shared_legacy_mode is not None:
init_config = {'legacy_mode': shared_legacy_mode}
init_config['legacy_mode'] = shared_legacy_mode
if instance_legacy_mode is not None:
instance['legacy_mode'] = instance_legacy_mode
if shared_legacy_mode_v2 is not None:
init_config['legacy_mode_v2'] = shared_legacy_mode_v2
if instance_legacy_mode_v2 is not None:
instance['legacy_mode_v2'] = instance_legacy_mode_v2

print(init_config, instance)

legacy_mode_opts = [
# legacy mode set by init_config
shared_legacy_mode and instance_legacy_mode is None,
# legacy_mode set by instance
instance_legacy_mode,
]
legacy_mode_v2_opts = [
# legacy mode v2 set by init_config
shared_legacy_mode_v2 and instance_legacy_mode_v2 is None,
# legacy_mode v2 set by instance
instance_legacy_mode_v2,
]
# default to legacy_mode=True if other opts are unset
if not any(legacy_mode_v2_opts):
selected_default = shared_legacy_mode is not False and instance_legacy_mode is None
else:
selected_default = False

# Must only set a single mode
if any(legacy_mode_opts) and any(legacy_mode_v2_opts):
with pytest.raises(ConfigurationError, match="are both true"):
check = new_check(instance, init_config=init_config)
return

check = new_check(instance, init_config=init_config)
# legacy_mode_v2 is not supported on Python2
if PY2 and any(legacy_mode_v2_opts):
with pytest.raises(ConfigurationError, match="not supported on Python2"):
check = new_check(instance, init_config=init_config)
return

# if python2 should alawys choose legacy mode
if PY2:
assert type(check) is Win32EventLogWMI
# Must raise SkipInstanceError if both options are False
if not selected_default and not any(legacy_mode_opts) and not any(legacy_mode_v2_opts):
with pytest.raises(SkipInstanceError):
check = new_check(instance, init_config=init_config)
return

# Check constructor must now succeed without raising an exception
check = new_check(instance, init_config=init_config)

# if instance option is set it should take precedence
if instance_legacy_mode:
assert type(check) is Win32EventLogWMI
return
elif instance_legacy_mode is False:
elif instance_legacy_mode_v2:
assert type(check) is Win32EventLogCheck
return

# instance option is unset
assert instance_legacy_mode is None

# shared/init_config option should apply now
if shared_legacy_mode:
# shared/init_config option should apply now, instance option must be None
if shared_legacy_mode and instance_legacy_mode is None:
assert type(check) is Win32EventLogWMI
return
elif shared_legacy_mode is False:
elif shared_legacy_mode_v2 and instance_legacy_mode_v2 is None:
assert type(check) is Win32EventLogCheck
return

# shared/init_config option is unset
assert shared_legacy_mode is None
# No option was selected, confirm the default type
if selected_default:
assert type(check) is Win32EventLogWMI
return

# should default to true for backwards compatibility
assert type(check) is Win32EventLogWMI
# We should have covered all the cases above and should not reach here
raise AssertionError("Case was not tested")
Loading