From 21ff9c6a4a897a1cefefdca90a5f6a4020753015 Mon Sep 17 00:00:00 2001 From: Nathaniel Case Date: Mon, 17 Sep 2018 12:53:57 -0400 Subject: [PATCH] Add initial napalm connection plugin (#45224) * Add initial napalm connection plugin * Fix review comments --- lib/ansible/plugins/connection/__init__.py | 3 +- lib/ansible/plugins/connection/napalm.py | 198 +++++++++++++++++++++ 2 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 lib/ansible/plugins/connection/napalm.py diff --git a/lib/ansible/plugins/connection/__init__.py b/lib/ansible/plugins/connection/__init__.py index 90265398da0..cd1998f9289 100644 --- a/lib/ansible/plugins/connection/__init__.py +++ b/lib/ansible/plugins/connection/__init__.py @@ -360,7 +360,8 @@ class NetworkConnectionBase(ConnectionBase): initialize implementation plugin options ''' for plugin in self._implementation_plugins: - plugin.set_options(task_keys=task_keys, var_options=var_options, direct=direct) + if hasattr(plugin, 'set_options'): + plugin.set_options(task_keys=task_keys, var_options=var_options, direct=direct) def _update_connection_state(self): ''' diff --git a/lib/ansible/plugins/connection/napalm.py b/lib/ansible/plugins/connection/napalm.py new file mode 100644 index 00000000000..83d7237b0c5 --- /dev/null +++ b/lib/ansible/plugins/connection/napalm.py @@ -0,0 +1,198 @@ +# (c) 2018 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = """ +--- +author: Ansible Networking Team +connection: napalm +short_description: Provides persistent connection using NAPALM +description: + - This connection plugin provides connectivity to network devices using + the NAPALM network device abstraction library. This library requires + certain features to be enabled on network devices depending on the + destination device operating system. The connection plugin requires + C(napalm) to be installed locally on the Ansible controller. +version_added: "2.8" +requirements: + - napalm +options: + host: + description: + - Specifies the remote device FQDN or IP address to establish the SSH + connection to. + default: inventory_hostname + vars: + - name: ansible_host + port: + type: int + description: + - Specifies the port on the remote device that listens for connections + when establishing the SSH connection. + default: 22 + ini: + - section: defaults + key: remote_port + env: + - name: ANSIBLE_REMOTE_PORT + vars: + - name: ansible_port + network_os: + description: + - Configures the device platform network operating system. This value is + used to load a napalm device abstraction. + vars: + - name: ansible_network_os + remote_user: + description: + - The username used to authenticate to the remote device when the SSH + connection is first established. If the remote_user is not specified, + the connection will use the username of the logged in user. + - Can be configured from the CLI via the C(--user) or C(-u) options. + ini: + - section: defaults + key: remote_user + env: + - name: ANSIBLE_REMOTE_USER + vars: + - name: ansible_user + password: + description: + - Configures the user password used to authenticate to the remote device + when first establishing the SSH connection. + vars: + - name: ansible_password + - name: ansible_ssh_pass + private_key_file: + description: + - The private SSH key or certificate file used to authenticate to the + remote device when first establishing the SSH connection. + ini: + - section: defaults + key: private_key_file + env: + - name: ANSIBLE_PRIVATE_KEY_FILE + vars: + - name: ansible_private_key_file + timeout: + type: int + description: + - Sets the connection time, in seconds, for communicating with the + remote device. This timeout is used as the default timeout value for + commands when issuing a command to the network CLI. If the command + does not return in timeout seconds, an error is generated. + default: 120 + host_key_auto_add: + type: boolean + description: + - By default, Ansible will prompt the user before adding SSH keys to the + known hosts file. By enabling this option, unknown host keys will + automatically be added to the known hosts file. + - Be sure to fully understand the security implications of enabling this + option on production systems as it could create a security vulnerability. + default: False + ini: + - section: paramiko_connection + key: host_key_auto_add + env: + - name: ANSIBLE_HOST_KEY_AUTO_ADD + persistent_connect_timeout: + type: int + description: + - Configures, in seconds, the amount of time to wait when trying to + initially establish a persistent connection. If this value expires + before the connection to the remote device is completed, the connection + will fail. + default: 30 + ini: + - section: persistent_connection + key: connect_timeout + env: + - name: ANSIBLE_PERSISTENT_CONNECT_TIMEOUT + persistent_command_timeout: + type: int + description: + - Configures, in seconds, the amount of time to wait for a command to + return from the remote device. If this timer is exceeded before the + command returns, the connection plugin will raise an exception and + close. + default: 10 + ini: + - section: persistent_connection + key: command_timeout + env: + - name: ANSIBLE_PERSISTENT_COMMAND_TIMEOUT + vars: + - name: ansible_command_timeout +""" + +from ansible.errors import AnsibleConnectionFailure, AnsibleError +from ansible.plugins.connection import NetworkConnectionBase + +try: + from napalm import get_network_driver + from napalm.base import ModuleImportError + HAS_NAPALM = True +except ImportError: + raise AnsibleError( + 'Napalm is required to use the napalm connection type.\n' + 'Please run pip install napalm' + ) + +try: + from __main__ import display +except ImportError: + from ansible.utils.display import Display + display = Display() + + +class Connection(NetworkConnectionBase): + """Napalm connections""" + + transport = 'napalm' + has_pipelining = False + + def __init__(self, play_context, new_stdin, *args, **kwargs): + super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) + + self.napalm = None + + def _connect(self): + super(Connection, self)._connect() + + if not self.connected: + if not self._network_os: + raise AnsibleConnectionFailure( + 'Unable to automatically determine host network os. Please ' + 'manually configure ansible_network_os value for this host' + ) + display.display('network_os is set to %s' % self._network_os, log_only=True) + + try: + driver = get_network_driver(self._network_os) + except ModuleImportError: + raise AnsibleConnectionFailure('Failed to import napalm driver for {0}'.format(self._network_os)) + + host = self.get_option('host') + self.napalm = driver( + hostname=host, + username=self.get_option('remote_user'), + password=self.get_option('password'), + timeout=self.get_option('persistent_command_timeout'), + ) + + self.napalm.open() + + self._implementation_plugins.append(self.napalm) + display.vvvv('created napalm device for network_os %s' % self._network_os, host=host) + self._connected = True + + def close(self): + if self.napalm: + self.napalm.close() + self.napalm = None + + super(Connection, self).close()