def test_all_no_duplicate_names(self, gp_mock, glob_mock): ''' This test goes along with ``test__load_module_source_no_duplicate_names`` and ensures that we ignore duplicate imports on multiple paths ''' fixture_path = os.path.join(os.path.dirname(__file__), 'loader_fixtures') gp_mock.return_value = [ fixture_path, '/path/to' ] glob_mock.glob.side_effect = [ [os.path.join(fixture_path, 'import_fixture.py')], ['/path/to/import_fixture.py'] ] pl = PluginLoader('test', '', 'test', 'test_plugin') # Aside from needing ``list()`` so we can do a len, ``PluginLoader.all`` returns a generator # so ``list()`` actually causes ``PluginLoader.all`` to run. plugins = list(pl.all()) self.assertEqual(len(plugins), 1) self.assertIn(os.path.join(fixture_path, 'import_fixture.py'), pl._module_cache) self.assertNotIn('/path/to/import_fixture.py', pl._module_cache)
def test_print_paths(self, mock_method): mock_method.return_value = ['/path/one', '/path/two', '/path/three'] pl = PluginLoader('foo', 'foo', '', 'test_plugins') paths = pl.print_paths() expected_paths = os.pathsep.join( ['/path/one', '/path/two', '/path/three']) self.assertEqual(paths, expected_paths)
def test_plugins__get_paths(self): pl = PluginLoader('test', '', 'test', 'test_plugin') pl._paths = [ PluginPathContext('/path/one', False), PluginPathContext('/path/two', True) ] self.assertEqual(pl._get_paths(), ['/path/one', '/path/two'])
def setUp(self): class Inventory(): cache = dict() class PopenResult(): returncode = 0 stdout = b"" stderr = b"" def communicate(self): return (self.stdout, self.stderr) self.popen_result = PopenResult() self.inventory = Inventory() self.loader = mock.MagicMock() self.loader.load = mock.MagicMock() inv_loader = PluginLoader('InventoryModule', 'ansible.plugins.inventory', C.DEFAULT_INVENTORY_PLUGIN_PATH, 'inventory_plugins') self.inventory_module = inv_loader.get('script') self.inventory_module.set_options() def register_patch(name): patcher = mock.patch(name) self.addCleanup(patcher.stop) return patcher.start() self.popen = register_patch('subprocess.Popen') self.popen.return_value = self.popen_result self.BaseInventoryPlugin = register_patch('ansible.plugins.inventory.BaseInventoryPlugin') self.BaseInventoryPlugin.get_cache_prefix.return_value = 'abc123'
def test_plugins__get_package_paths_with_package(self): # the _get_package_paths() call uses __import__ to load a # python library, and then uses the __file__ attribute of # the result for that to get the library path, so we mock # that here and patch the builtin to use our mocked result foo = MagicMock() bar = MagicMock() bam = MagicMock() bam.__file__ = '/path/to/my/foo/bar/bam/__init__.py' bar.bam = bam foo.return_value.bar = bar pl = PluginLoader('test', 'foo.bar.bam', 'test', 'test_plugin') with patch('{0}.__import__'.format(BUILTINS), foo): self.assertEqual(pl._get_package_paths(), ['/path/to/my/foo/bar/bam'])
def test__load_module_source_no_duplicate_names(self): ''' This test simulates importing 2 plugins with the same name, and validating that the import is shortcirtuited if a file with the same name has already been imported ''' fixture_path = os.path.join(os.path.dirname(__file__), 'loader_fixtures') pl = PluginLoader('test', '', 'test', 'test_plugin') one = pl._load_module_source('import_fixture', os.path.join(fixture_path, 'import_fixture.py')) # This line wouldn't even succeed if we didn't short cirtuit on finding a duplicate name two = pl._load_module_source('import_fixture', '/path/to/import_fixture.py') self.assertEqual(one, two)
def test__load_module_source_no_duplicate_names(self): ''' This test simulates importing 2 plugins with the same name, and validating that the import is short circuited if a file with the same name has already been imported ''' fixture_path = os.path.join(os.path.dirname(__file__), 'loader_fixtures') pl = PluginLoader('test', '', 'test', 'test_plugin') one = pl._load_module_source('import_fixture', os.path.join(fixture_path, 'import_fixture.py')) # This line wouldn't even succeed if we didn't short circuit on finding a duplicate name two = pl._load_module_source('import_fixture', '/path/to/import_fixture.py') self.assertEqual(one, two)
def _setup_inventory_plugins(self): ''' sets up loaded inventory plugins for usage ''' inventory_loader = PluginLoader('InventoryModule', 'ansible.plugins.inventory', 'inventory_plugins', 'inventory_plugins') display.vvvv('setting up inventory plugins') for name in C.INVENTORY_ENABLED: plugin = inventory_loader.get(name) if plugin: self._inventory_plugins.append(plugin) else: display.warning('Failed to load inventory plugin, skipping %s' % name) if not self._inventory_plugins: raise AnsibleError("No inventory plugins available to generate inventory, make sure you have at least one whitelisted.")
def _setup_inventory_plugins(self): ''' sets up loaded inventory plugins for usage ''' inventory_loader = PluginLoader('InventoryModule', 'ansible.plugins.inventory', C.DEFAULT_INVENTORY_PLUGIN_PATH, 'inventory_plugins') display.vvvv('setting up inventory plugins') for name in C.INVENTORY_ENABLED: plugin = inventory_loader.get(name) if plugin: self._inventory_plugins.append(plugin) else: display.warning('Failed to load inventory plugin, skipping %s' % name) if not self._inventory_plugins: raise AnsibleError("No inventory plugins available to generate inventory, make sure you have at least one whitelisted.")
class Template(object): def __init__(self, root_dir, ops_config): loader = ChoiceLoader( [FileSystemLoader(root_dir), FileSystemLoader("/")]) mode = ops_config.get('jinja2.undefined') undefined = Undefined if mode == 'StrictUndefined': undefined = StrictUndefined elif mode == 'DebugUndefined': undefined = DebugUndefined self.env = Environment(loader=loader, undefined=undefined) self.filter_plugin_loader = PluginLoader( 'FilterModule', 'ansible.plugins.filter', ops_config.ansible_filter_plugins.split(':'), 'filter_plugins') for filter in self.filter_plugin_loader.all(): self.env.filters.update(filter.filters()) def render(self, source, vars): jinja_template = self.env.get_template(source) return jinja_template.render(**vars)
def get_fragment_loader(): fragment_loader = PluginLoader( 'ModuleDocFragment', # class_name '', # package '{0}/library/plugins/doc_fragments'.format(BASE_DIR), # config '', # subdir ) return fragment_loader
import jinja2 import yaml from jinja2 import Environment, FileSystemLoader from six import iteritems, string_types from ansible.errors import AnsibleError from ansible.module_utils._text import to_bytes, to_text from ansible.plugins.loader import PluginLoader from ansible.utils import plugin_docs from ansible.utils.display import Display fragment_loader = PluginLoader( 'ModuleDocFragment', # class_name '', # package '/here/library/utils/module_docs_fragments', # config '', # subdir ) ##################################################################################### # constants and paths # if a module is added in a version of Ansible older than this, don't print the version added information # in the module documentation because everyone is assumed to be running something newer than this already. TOO_OLD_TO_BE_NOTABLE = 1.3 # Get parent directory of the directory this script lives in MODULEDIR = os.path.abspath( os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, 'lib', 'ansible', 'modules'))
# (c) 2018, Ansible by Red Hat, inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. # from __future__ import (absolute_import, division, print_function) __metaclass__ = type from ansible.plugins.loader import PluginLoader import json parser_loader = PluginLoader('ParserEngine', 'parsers', None, 'show_to_facts_parsers') class ToFacts(json.JSONEncoder): def default(self, obj): if hasattr(obj, 'to_facts'): return obj.to_facts() else: return json.JSONEncoder.default(self, obj)
# (c) 2018, Ansible by Red Hat, inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. # from __future__ import (absolute_import, division, print_function) __metaclass__ = type from ansible.plugins.loader import PluginLoader template_loader = PluginLoader('TemplateEngine', 'network_engine.plugins.template', None, 'template_plugins', required_base_class='TemplateBase') parser_loader = PluginLoader( 'ParserEngine', 'network_engine.plugins.parser', None, 'parser_plugins', # required_base_class='ParserBase' )
def run(self): super(DocCLI, self).run() plugin_type = self.options.type # choose plugin type if plugin_type == 'cache': loader = cache_loader elif plugin_type == 'callback': loader = callback_loader elif plugin_type == 'connection': loader = connection_loader elif plugin_type == 'lookup': loader = lookup_loader elif plugin_type == 'strategy': loader = strategy_loader elif plugin_type == 'vars': loader = vars_loader elif plugin_type == 'inventory': loader = PluginLoader('InventoryModule', 'ansible.plugins.inventory', 'inventory_plugins', 'inventory_plugins') else: loader = module_loader # add to plugin path from command line if self.options.module_path: for path in self.options.module_path: if path: loader.add_directory(path) # save only top level paths for errors search_paths = DocCLI.print_paths(loader) loader._paths = None # reset so we can use subdirs below # list plugins for type if self.options.list_dir: paths = loader._get_paths() for path in paths: self.find_plugins(path, plugin_type) self.pager(self.get_plugin_list_text(loader)) return 0 # process all plugins of type if self.options.all_plugins: paths = loader._get_paths() for path in paths: self.find_plugins(path, plugin_type) self.args = sorted(set(self.plugin_list)) if len(self.args) == 0: raise AnsibleOptionsError("Incorrect options passed") # process command line list text = '' for plugin in self.args: try: # if the plugin lives in a non-python file (eg, win_X.ps1), require the corresponding python file for docs filename = loader.find_plugin(plugin, mod_type='.py', ignore_deprecated=True, check_aliases=True) if filename is None: display.warning("%s %s not found in:\n%s\n" % (plugin_type, plugin, search_paths)) continue if any(filename.endswith(x) for x in C.BLACKLIST_EXTS): continue try: doc, plainexamples, returndocs, metadata = plugin_docs.get_docstring( filename, verbose=(self.options.verbosity > 0)) except: display.vvv(traceback.format_exc()) display.error( "%s %s has a documentation error formatting or is missing documentation." % (plugin_type, plugin)) continue if doc is not None: # assign from other sections doc['plainexamples'] = plainexamples doc['returndocs'] = returndocs doc['metadata'] = metadata # generate extra data if plugin_type == 'module': # is there corresponding action plugin? if plugin in action_loader: doc['action'] = True else: doc['action'] = False doc['filename'] = filename doc['now_date'] = datetime.date.today().strftime( '%Y-%m-%d') if 'docuri' in doc: doc['docuri'] = doc[plugin_type].replace('_', '-') if self.options.show_snippet and plugin_type == 'module': text += self.get_snippet_text(doc) else: text += self.get_man_text(doc) else: # this typically means we couldn't even parse the docstring, not just that the YAML is busted, # probably a quoting issue. raise AnsibleError("Parsing produced an empty object.") except Exception as e: display.vvv(traceback.format_exc()) raise AnsibleError( "%s %s missing documentation (or could not parse documentation): %s\n" % (plugin_type, plugin, str(e))) if text: self.pager(text) return 0
def test_plugins__get_paths(self): pl = PluginLoader('test', '', 'test', 'test_plugin') pl._paths = ['/path/one', '/path/two'] self.assertEqual(pl._get_paths(), ['/path/one', '/path/two'])
def test_plugins__get_package_paths_no_package(self): pl = PluginLoader('test', '', 'test', 'test_plugin') self.assertEqual(pl._get_package_paths(), [])
def test_print_paths(self, mock_method): mock_method.return_value = ['/path/one', '/path/two', '/path/three'] pl = PluginLoader('foo', 'foo', '', 'test_plugins') paths = pl.print_paths() expected_paths = os.pathsep.join(['/path/one', '/path/two', '/path/three']) self.assertEqual(paths, expected_paths)
def assertPluginLoaderConfigBecomes(self, arg, expected): pl = PluginLoader('test', '', arg, 'test_plugin') self.assertEqual(pl.config, expected)
def run(self): super(DocCLI, self).run() plugin_type = self.options.type # choose plugin type if plugin_type == 'cache': loader = cache_loader elif plugin_type == 'callback': loader = callback_loader elif plugin_type == 'connection': loader = connection_loader elif plugin_type == 'lookup': loader = lookup_loader elif plugin_type == 'strategy': loader = strategy_loader elif plugin_type == 'vars': loader = vars_loader elif plugin_type == 'inventory': loader = PluginLoader('InventoryModule', 'ansible.plugins.inventory', 'inventory_plugins', 'inventory_plugins') else: loader = module_loader # add to plugin path from command line if self.options.module_path is not None: for i in self.options.module_path.split(os.pathsep): loader.add_directory(i) # save only top level paths for errors search_paths = DocCLI.print_paths(loader) loader._paths = None # reset so we can use subdirs below # list plugins for type if self.options.list_dir: paths = loader._get_paths() for path in paths: self.find_plugins(path, plugin_type) self.pager(self.get_plugin_list_text(loader)) return 0 # process all plugins of type if self.options.all_plugins: paths = loader._get_paths() for path in paths: self.find_plugins(path, plugin_type) self.args = sorted(set(self.plugin_list)) if len(self.args) == 0: raise AnsibleOptionsError("Incorrect options passed") # process command line list text = '' for plugin in self.args: try: # if the plugin lives in a non-python file (eg, win_X.ps1), require the corresponding python file for docs filename = loader.find_plugin(plugin, mod_type='.py', ignore_deprecated=True, check_aliases=True) if filename is None: display.warning("%s %s not found in:\n%s\n" % (plugin_type, plugin, search_paths)) continue if any(filename.endswith(x) for x in C.BLACKLIST_EXTS): continue try: doc, plainexamples, returndocs, metadata = plugin_docs.get_docstring(filename, verbose=(self.options.verbosity > 0)) except: display.vvv(traceback.format_exc()) display.error("%s %s has a documentation error formatting or is missing documentation." % (plugin_type, plugin)) continue if doc is not None: # assign from other sections doc['plainexamples'] = plainexamples doc['returndocs'] = returndocs doc['metadata'] = metadata # generate extra data if plugin_type == 'module': # is there corresponding action plugin? if plugin in action_loader: doc['action'] = True else: doc['action'] = False doc['filename'] = filename doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d') if 'docuri' in doc: doc['docuri'] = doc[plugin_type].replace('_', '-') if self.options.show_snippet and plugin_type == 'module': text += self.get_snippet_text(doc) else: text += self.get_man_text(doc) else: # this typically means we couldn't even parse the docstring, not just that the YAML is busted, # probably a quoting issue. raise AnsibleError("Parsing produced an empty object.") except Exception as e: display.vvv(traceback.format_exc()) raise AnsibleError("%s %s missing documentation (or could not parse documentation): %s\n" % (plugin_type, plugin, str(e))) if text: self.pager(text) return 0