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

plugin signature based on options #69947

Draft
wants to merge 12 commits into
base: devel
Choose a base branch
from
2 changes: 2 additions & 0 deletions changelogs/fragments/plugin_sigs.yml
@@ -0,0 +1,2 @@
minor_changes:
- plugins now have unique signatures based on the options set
16 changes: 16 additions & 0 deletions lib/ansible/plugins/__init__.py
Expand Up @@ -21,6 +21,8 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import pickle

from abc import ABC

import types
Expand Down Expand Up @@ -57,6 +59,17 @@ class AnsiblePlugin(ABC):

def __init__(self):
self._options = {}
self._hash = None

def _gen_signature(self):
s-hertel marked this conversation as resolved.
Show resolved Hide resolved
# create immutable
s_options = pickle.dumps(self.get_options(hostvars=variables))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I copy an inventory config file and use both, should the plugin have the same hash, or be unique per source? The latter happens now with pickle.dumps.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would need the same as connections it's own _gen_signature to include 'source path'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that would make sense. It seems like it includes source path now (implicitly) since the hashes are different, but I'm not sure exactly what's happening.

self._hash = hash(self._load_name) + hash(s_options)

def signature(self):
if self._hash is None:
self._gen_signature()
return self._hash

def get_option(self, option, hostvars=None):
if option not in self._options:
Expand All @@ -76,6 +89,7 @@ def get_options(self, hostvars=None):

def set_option(self, option, value):
self._options[option] = value
self._hash = None

def set_options(self, task_keys=None, var_options=None, direct=None):
'''
Expand All @@ -92,6 +106,8 @@ def set_options(self, task_keys=None, var_options=None, direct=None):
if self.allow_extras and var_options and '_extras' in var_options:
self.set_option('_extras', var_options['_extras'])

self._gen_signature()

def has_option(self, option):
if not self._options:
self.set_options()
Expand Down
5 changes: 5 additions & 0 deletions lib/ansible/plugins/callback/__init__.py
Expand Up @@ -143,6 +143,9 @@ class CallbackBase(AnsiblePlugin):
'''

def __init__(self, display=None, options=None):

super(CallbackBase, self).__init__()

if display:
self._display = display
else:
Expand All @@ -168,6 +171,7 @@ def __init__(self, display=None, options=None):

def set_option(self, k, v):
self._plugin_options[k] = v
self._hash = None

def get_option(self, k):
return self._plugin_options[k]
Expand All @@ -179,6 +183,7 @@ def set_options(self, task_keys=None, var_options=None, direct=None):

# load from config
self._plugin_options = C.config.get_plugin_options(get_plugin_class(self), self._load_name, keys=task_keys, variables=var_options, direct=direct)
self._hash = None

@staticmethod
def host_label(result):
Expand Down
31 changes: 31 additions & 0 deletions lib/ansible/plugins/connection/__init__.py
Expand Up @@ -90,8 +90,23 @@ def __init__(self, play_context, new_stdin, shell=None, *args, **kwargs):

self.become = None

def _gen_signature(self):

# prime self, with own options
super(ConnectionBase, self)._gen_signature()

# also shell, we always have!
self._hash += self._shell._gen_signature()

# and become if we have it
if self.become is not None:
self._hash += self.become._gen_signature()


def set_become_plugin(self, plugin):
self.become = plugin
# reset hash jic
self._hash = None

@property
def connected(self):
Expand Down Expand Up @@ -380,3 +395,19 @@ def _update_connection_state(self):
def _log_messages(self, message):
if self.get_option('persistent_log_messages'):
self.queue_message('log', message)

def signature(self):

if self._hash is None:
# prime self, with own options
super(NetworkConnectionBase, self).signature()

if self._sub_plugin:
# also shell, we always have!
self._hash += self._sub_plugin.signature()
return self._hash

def set_become_plugin(self, plugin):
self.become = plugin
# reset hash jic
self._hash = None