From e86279cee4a500d13ceaf577ec587232b1745f88 Mon Sep 17 00:00:00 2001 From: QijunPan Date: Fri, 26 May 2017 00:33:57 +0800 Subject: [PATCH] Contributing new /lib/ansible/plugins/action/ce.py module to manage HUAWEI data center CloudEngine switch (#21645) * commit plugins action ce module commit plugins action ce module * update plugins/action/ce.py update plugins/action/ce.py * update action ce.py update action ce.py * fix CI issues * update ce.py * add ce action --- lib/ansible/constants.py | 4 +- lib/ansible/plugins/action/ce.py | 134 +++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 lib/ansible/plugins/action/ce.py diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index eb7f12284b1..b914959541b 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -343,7 +343,7 @@ DEFAULT_TEST_PLUGIN_PATH = get_config(p, DEFAULTS, 'test_plugins', 'ANSIBLE_TEST DEFAULT_STRATEGY_PLUGIN_PATH = get_config(p, DEFAULTS, 'strategy_plugins', 'ANSIBLE_STRATEGY_PLUGINS', '~/.ansible/plugins/strategy:/usr/share/ansible/plugins/strategy', value_type='pathlist') -NETWORK_GROUP_MODULES = get_config(p, DEFAULTS, 'network_group_modules', 'NETWORK_GROUP_MODULES', ['eos', 'nxos', 'ios', 'iosxr', 'junos', +NETWORK_GROUP_MODULES = get_config(p, DEFAULTS, 'network_group_modules', 'NETWORK_GROUP_MODULES', ['eos', 'nxos', 'ios', 'iosxr', 'junos', 'ce', 'vyos', 'sros', 'dellos9', 'dellos10', 'dellos6'], value_type='list') DEFAULT_STRATEGY = get_config(p, DEFAULTS, 'strategy', 'ANSIBLE_STRATEGY', 'linear') @@ -373,7 +373,7 @@ RETRY_FILES_ENABLED = get_config(p, DEFAULTS, 'retry_files_enabled', 'ANSIBLE_RE RETRY_FILES_SAVE_PATH = get_config(p, DEFAULTS, 'retry_files_save_path', 'ANSIBLE_RETRY_FILES_SAVE_PATH', None, value_type='path') DEFAULT_NULL_REPRESENTATION = get_config(p, DEFAULTS, 'null_representation', 'ANSIBLE_NULL_REPRESENTATION', None, value_type='none') DISPLAY_ARGS_TO_STDOUT = get_config(p, DEFAULTS, 'display_args_to_stdout', 'ANSIBLE_DISPLAY_ARGS_TO_STDOUT', False, value_type='boolean') -MAX_FILE_SIZE_FOR_DIFF = get_config(p, DEFAULTS, 'max_diff_size', 'ANSIBLE_MAX_DIFF_SIZE', 1024 * 1024, value_type='integer') +MAAX_FILE_SIZE_FOR_DIFF = get_config(p, DEFAULTS, 'max_diff_size', 'ANSIBLE_MAX_DIFF_SIZE', 1024 * 1024, value_type='integer') # CONNECTION RELATED USE_PERSISTENT_CONNECTIONS = get_config(p, DEFAULTS, 'use_persistent_connections', 'ANSIBLE_USE_PERSISTENT_CONNECTIONS', False, value_type='boolean') diff --git a/lib/ansible/plugins/action/ce.py b/lib/ansible/plugins/action/ce.py new file mode 100644 index 00000000000..f10ff293d77 --- /dev/null +++ b/lib/ansible/plugins/action/ce.py @@ -0,0 +1,134 @@ +# +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import sys +import copy + +from ansible.plugins.action.normal import ActionModule as _ActionModule +from ansible.utils.path import unfrackpath +from ansible.plugins import connection_loader +from ansible.module_utils.six import iteritems +from ansible.module_utils.ce import ce_argument_spec +from ansible.module_utils.basic import AnsibleFallbackNotFound +from ansible.module_utils._text import to_bytes + +try: + from __main__ import display +except ImportError: + from ansible.utils.display import Display + display = Display() + + +class ActionModule(_ActionModule): + + def run(self, tmp=None, task_vars=None): + if self._play_context.connection != 'local': + return dict( + failed=True, + msg='invalid connection specified, expected connection=local, ' + 'got %s' % self._play_context.connection + ) + + provider = self.load_provider() + transport = provider['transport'] or 'cli' + + display.vvvv('connection transport is %s' % transport, self._play_context.remote_addr) + + if transport == 'cli': + pc = copy.deepcopy(self._play_context) + pc.connection = 'network_cli' + pc.network_os = 'ce' + pc.remote_addr = provider['host'] or self._play_context.remote_addr + pc.port = int(provider['port']) or int(self._play_context.port) or 22 + pc.remote_user = provider['username'] or self._play_context.connection_user + pc.password = provider['password'] or self._play_context.password + pc.timeout = provider['timeout'] or self._play_context.timeout + self._task.args['provider'] = provider.update( + host=pc.remote_addr, + port=pc.port, + username=pc.remote_user, + password=pc.password, + ssh_keyfile=pc.private_key_file + ) + display.vvv('using connection plugin %s' % pc.connection, pc.remote_addr) + connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin) + socket_path = self._get_socket_path(pc) + display.vvvv('socket_path: %s' % socket_path, pc.remote_addr) + + if not os.path.exists(socket_path): + # start the connection if it isn't started + rc, out, err = connection.exec_command('open_shell()') + display.vvvv('open_shell() returned %s %s %s' % (rc, out, err)) + if rc != 0: + return {'failed': True, + 'msg': 'unable to open shell. Please see: ' + + 'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell', + 'rc': rc} + else: + # make sure we are in the right cli context which should be + # enable mode and not config module + rc, out, err = connection.exec_command('prompt()') + while str(out).strip().endswith(']'): + display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr) + connection.exec_command('return') + rc, out, err = connection.exec_command('prompt()') + + task_vars['ansible_socket'] = socket_path + + # make sure a transport value is set in args + self._task.args['transport'] = transport + + result = super(ActionModule, self).run(tmp, task_vars) + return result + + def _get_socket_path(self, play_context): + ssh = connection_loader.get('ssh', class_only=True) + cp = ssh._create_control_path(play_context.remote_addr, play_context.port, play_context.remote_user) + path = unfrackpath("$HOME/.ansible/pc") + return cp % dict(directory=path) + + def load_provider(self): + provider = self._task.args.get('provider', {}) + for key, value in iteritems(ce_argument_spec): + if key != 'provider' and key not in provider: + if key in self._task.args: + provider[key] = self._task.args[key] + elif 'fallback' in value: + provider[key] = self._fallback(value['fallback']) + elif key not in provider: + provider[key] = None + return provider + + def _fallback(self, fallback): + strategy = fallback[0] + args = [] + kwargs = {} + + for item in fallback[1:]: + if isinstance(item, dict): + kwargs = item + else: + args = item + try: + return strategy(*args, **kwargs) + except AnsibleFallbackNotFound: + pass