1""" 2Netmiko Execution Module 3======================== 4 5.. versionadded:: 2019.2.0 6 7Execution module to interface the connection with a remote network device. It is 8flexible enough to execute the commands both when running under a Netmiko Proxy 9Minion, as well as running under a Regular Minion by specifying the connection 10arguments, i.e., ``device_type``, ``ip``, ``username``, ``password`` etc. 11 12:codeauthor: Mircea Ulinic <ping@mirceaulinic.net> & Kirk Byers <ktbyers@twb-tech.com> 13:maturity: new 14:depends: netmiko 15:platform: unix 16 17Dependencies 18------------ 19 20The ``netmiko`` proxy modules requires Netmiko to be installed: ``pip install netmiko``. 21 22Usage 23----- 24 25This module can equally be used via the :mod:`netmiko <salt.proxy.netmiko_px>` 26Proxy module (check documentation), or directly from an arbitrary (Proxy) Minion 27that is running on a server (computer) having access to the network device, and 28has the ``netmiko`` library installed. 29 30When running outside of the :mod:`netmiko Proxy <salt.proxy.netmiko_px>` (i.e., 31from another Proxy Minion type, or regular Minion), the netmiko connection 32arguments can be either specified from the CLI when executing the command, or 33in a configuration block under the ``netmiko`` key in the configuration opts 34(i.e., (Proxy) Minion configuration file), or Pillar. The module supports these 35simultaneously. These fields are the exact same supported by the ``netmiko`` 36Proxy Module: 37 38- ``device_type`` - Class selection based on device type. Supported options: 39 40 - ``a10``: A10 Networks 41 - ``accedian``: Accedian Networks 42 - ``alcatel_aos``: Alcatel AOS 43 - ``alcatel_sros``: Alcatel SROS 44 - ``apresia_aeos``: Apresia AEOS 45 - ``arista_eos``: Arista EOS 46 - ``aruba_os``: Aruba 47 - ``avaya_ers``: Avaya ERS 48 - ``avaya_vsp``: Avaya VSP 49 - ``brocade_fastiron``: Brocade Fastiron 50 - ``brocade_netiron``: Brocade Netiron 51 - ``brocade_nos``: Brocade NOS 52 - ``brocade_vdx``: Brocade NOS 53 - ``brocade_vyos``: VyOS 54 - ``checkpoint_gaia``: Check Point GAiA 55 - ``calix_b6``: Calix B6 56 - ``ciena_saos``: Ciena SAOS 57 - ``cisco_asa``: Cisco SA 58 - ``cisco_ios``: Cisco IOS 59 - ``cisco_nxos``: Cisco NX-oS 60 - ``cisco_s300``: Cisco S300 61 - ``cisco_tp``: Cisco TpTcCe 62 - ``cisco_wlc``: Cisco WLC 63 - ``cisco_xe``: Cisco IOS 64 - ``cisco_xr``: Cisco XR 65 - ``coriant``: Coriant 66 - ``dell_force10``: Dell Force10 67 - ``dell_os10``: Dell OS10 68 - ``dell_powerconnect``: Dell PowerConnect 69 - ``eltex``: Eltex 70 - ``enterasys``: Enterasys 71 - ``extreme``: Extreme 72 - ``extreme_wing``: Extreme Wing 73 - ``f5_ltm``: F5 LTM 74 - ``fortinet``: Fortinet 75 - ``generic_termserver``: TerminalServer 76 - ``hp_comware``: HP Comware 77 - ``hp_procurve``: HP Procurve 78 - ``huawei``: Huawei 79 - ``huawei_vrpv8``: Huawei VRPV8 80 - ``juniper``: Juniper Junos 81 - ``juniper_junos``: Juniper Junos 82 - ``linux``: Linux 83 - ``mellanox``: Mellanox 84 - ``mrv_optiswitch``: MrvOptiswitch 85 - ``netapp_cdot``: NetAppcDot 86 - ``netscaler``: Netscaler 87 - ``ovs_linux``: OvsLinux 88 - ``paloalto_panos``: PaloAlto Panos 89 - ``pluribus``: Pluribus 90 - ``quanta_mesh``: Quanta Mesh 91 - ``ruckus_fastiron``: Ruckus Fastiron 92 - ``ubiquiti_edge``: Ubiquiti Edge 93 - ``ubiquiti_edgeswitch``: Ubiquiti Edge 94 - ``vyatta_vyos``: VyOS 95 - ``vyos``: VyOS 96 - ``brocade_fastiron_telnet``: Brocade Fastiron over Telnet 97 - ``brocade_netiron_telnet``: Brocade Netiron over Telnet 98 - ``cisco_ios_telnet``: Cisco IOS over Telnet 99 - ``apresia_aeos_telnet``: Apresia AEOS over Telnet 100 - ``arista_eos_telnet``: Arista EOS over Telnet 101 - ``hp_procurve_telnet``: HP Procurve over Telnet 102 - ``hp_comware_telnet``: HP Comware over Telnet 103 - ``juniper_junos_telnet``: Juniper Junos over Telnet 104 - ``calix_b6_telnet``: Calix B6 over Telnet 105 - ``dell_powerconnect_telnet``: Dell PowerConnect over Telnet 106 - ``generic_termserver_telnet``: TerminalServer over Telnet 107 - ``extreme_telnet``: Extreme Networks over Telnet 108 - ``ruckus_fastiron_telnet``: Ruckus Fastiron over Telnet 109 - ``cisco_ios_serial``: Cisco IOS over serial port 110 111- ``ip`` - IP address of target device (not required if ``host`` is provided) 112 113- ``host`` - Hostname of target device (not required if ``ip`` is provided) 114 115- ``username`` - Username to authenticate against target device, if required 116 117- ``password`` - Password to authenticate against target device, if required 118 119- ``secret`` - The enable password if target device requires one 120 121- ``port`` - The destination port used to connect to the target device 122 123- ``global_delay_factor`` - Multiplication factor affecting Netmiko delays 124 (default: ``1``) 125 126- ``use_keys`` - Connect to target device using SSH keys (default: ``False``) 127 128- ``key_file`` - Filename path of the SSH key file to use 129 130- ``allow_agent`` - Enable use of SSH key-agent 131 132- ``ssh_strict`` - Automatically reject unknown SSH host keys (default: 133 ``False``, which means unknown SSH host keys will be accepted) 134 135- ``system_host_keys`` - Load host keys from the user's "known_hosts" file 136 (default: ``False``) 137 138- ``alt_host_keys`` - If ``True``, host keys will be loaded from the file 139 specified in ``alt_key_file`` (default: ``False``) 140 141- ``alt_key_file`` - SSH host key file to use (if ``alt_host_keys=True``) 142 143- ``ssh_config_file`` - File name of OpenSSH configuration file 144 145- ``timeout`` - Connection timeout, in seconds (default: ``90``) 146 147- ``session_timeout`` - Set a timeout for parallel requests, in seconds 148 (default: ``60``) 149 150- ``keepalive`` - Send SSH keepalive packets at a specific interval, in 151 seconds. Currently defaults to ``0``, for backwards compatibility (it will 152 not attempt to keep the connection alive using the KEEPALIVE packets). 153 154- ``default_enter`` - Character(s) to send to correspond to enter key (default: 155 ``\\n``) 156 157- ``response_return`` - Character(s) to use in normalized return data to 158 represent enter key (default: ``\\n``) 159 160Example (when not running in a ``netmiko`` Proxy Minion): 161 162.. code-block:: yaml 163 164 netmiko: 165 username: test 166 password: test 167 168In case the ``username`` and ``password`` are the same on any device you are 169targeting, the block above (besides other parameters specific to your 170environment you might need) should suffice to be able to execute commands from 171outside a ``netmiko`` Proxy, e.g.: 172 173.. code-block:: bash 174 175 salt '*' netmiko.send_command 'show version' host=router1.example.com device_type=juniper 176 salt '*' netmiko.send_config https://bit.ly/2sgljCB host=sw2.example.com device_type=cisco_ios 177 178.. note:: 179 180 Remember that the above applies only when not running in a ``netmiko`` Proxy 181 Minion. If you want to use the :mod:`<salt.proxy.netmiko_px>`, please follow 182 the documentation notes for a proper setup. 183""" 184 185import logging 186 187import salt.utils.platform 188from salt.exceptions import CommandExecutionError 189from salt.utils.args import clean_kwargs 190 191try: 192 from netmiko import ConnectHandler 193 from netmiko import BaseConnection 194 195 HAS_NETMIKO = True 196except ImportError: 197 HAS_NETMIKO = False 198 199# ----------------------------------------------------------------------------- 200# execution module properties 201# ----------------------------------------------------------------------------- 202 203__proxyenabled__ = ["*"] 204# Any Proxy Minion should be able to execute these (not only netmiko) 205 206__virtualname__ = "netmiko" 207# The Execution Module will be identified as ``netmiko`` 208 209# ----------------------------------------------------------------------------- 210# globals 211# ----------------------------------------------------------------------------- 212 213log = logging.getLogger(__name__) 214 215# ----------------------------------------------------------------------------- 216# propery functions 217# ----------------------------------------------------------------------------- 218 219 220def __virtual__(): 221 """ 222 Execution module available only if Netmiko is installed. 223 """ 224 if not HAS_NETMIKO: 225 return ( 226 False, 227 "The netmiko execution module requires netmiko library to be installed.", 228 ) 229 if ( 230 salt.utils.platform.is_proxy() 231 and __opts__["proxy"]["proxytype"] == "deltaproxy" 232 ): 233 return ( 234 False, 235 "Unsupported proxy minion type.", 236 ) 237 238 return __virtualname__ 239 240 241# ----------------------------------------------------------------------------- 242# helper functions 243# ----------------------------------------------------------------------------- 244 245 246def _prepare_connection(**kwargs): 247 """ 248 Prepare the connection with the remote network device, and clean up the key 249 value pairs, removing the args used for the connection init. 250 """ 251 init_args = {} 252 fun_kwargs = {} 253 netmiko_kwargs = __salt__["config.get"]("netmiko", {}) 254 netmiko_kwargs.update(kwargs) # merge the CLI args with the opts/pillar 255 netmiko_init_args, _, _, netmiko_defaults = __utils__["args.get_function_argspec"]( 256 BaseConnection.__init__ 257 ) 258 check_self = netmiko_init_args.pop(0) 259 for karg, warg in netmiko_kwargs.items(): 260 if karg not in netmiko_init_args: 261 if warg is not None: 262 fun_kwargs[karg] = warg 263 continue 264 if warg is not None: 265 init_args[karg] = warg 266 conn = ConnectHandler(**init_args) 267 return conn, fun_kwargs 268 269 270# ----------------------------------------------------------------------------- 271# callable functions 272# ----------------------------------------------------------------------------- 273 274 275def get_connection(**kwargs): 276 """ 277 Return the Netmiko connection object. 278 279 .. warning:: 280 281 This function returns an unserializable object, hence it is not meant 282 to be used on the CLI. This should mainly be used when invoked from 283 other modules for the low level connection with the network device. 284 285 kwargs 286 Key-value dictionary with the authentication details. 287 288 USAGE Example: 289 290 .. code-block:: python 291 292 conn = __salt__['netmiko.get_connection'](host='router1.example.com', 293 username='example', 294 password='example') 295 show_if = conn.send_command('show interfaces') 296 conn.disconnect() 297 """ 298 kwargs = clean_kwargs(**kwargs) 299 if "netmiko.conn" in __proxy__: 300 return __proxy__["netmiko.conn"]() 301 conn, kwargs = _prepare_connection(**kwargs) 302 return conn 303 304 305def call(method, *args, **kwargs): 306 """ 307 Invoke an arbitrary Netmiko method. 308 309 method 310 The name of the Netmiko method to invoke. 311 312 args 313 A list of arguments to send to the method invoked. 314 315 kwargs 316 Key-value dictionary to send to the method invoked. 317 """ 318 kwargs = clean_kwargs(**kwargs) 319 if "netmiko.call" in __proxy__: 320 return __proxy__["netmiko.call"](method, *args, **kwargs) 321 conn, kwargs = _prepare_connection(**kwargs) 322 ret = getattr(conn, method)(*args, **kwargs) 323 conn.disconnect() 324 return ret 325 326 327def multi_call(*methods, **kwargs): 328 """ 329 Invoke multiple Netmiko methods at once, and return their output, as list. 330 331 methods 332 A list of dictionaries with the following keys: 333 334 - ``name``: the name of the Netmiko method to be executed. 335 - ``args``: list of arguments to be sent to the Netmiko method. 336 - ``kwargs``: dictionary of arguments to be sent to the Netmiko method. 337 338 kwargs 339 Key-value dictionary with the connection details (when not running 340 under a Proxy Minion). 341 """ 342 kwargs = clean_kwargs(**kwargs) 343 if "netmiko.conn" in __proxy__: 344 conn = __proxy__["netmiko.conn"]() 345 else: 346 conn, kwargs = _prepare_connection(**kwargs) 347 ret = [] 348 for method in methods: 349 # Explicit unpacking 350 method_name = method["name"] 351 method_args = method.get("args", []) 352 method_kwargs = method.get("kwargs", []) 353 ret.append(getattr(conn, method_name)(*method_args, **method_kwargs)) 354 if "netmiko.conn" not in __proxy__: 355 conn.disconnect() 356 return ret 357 358 359def send_command(command_string, **kwargs): 360 """ 361 Execute command_string on the SSH channel using a pattern-based mechanism. 362 Generally used for show commands. By default this method will keep waiting 363 to receive data until the network device prompt is detected. The current 364 network device prompt will be determined automatically. 365 366 command_string 367 The command to be executed on the remote device. 368 369 expect_string 370 Regular expression pattern to use for determining end of output. 371 If left blank will default to being based on router prompt. 372 373 delay_factor: ``1`` 374 Multiplying factor used to adjust delays (default: ``1``). 375 376 max_loops: ``500`` 377 Controls wait time in conjunction with delay_factor. Will default to be 378 based upon self.timeout. 379 380 auto_find_prompt: ``True`` 381 Whether it should try to auto-detect the prompt (default: ``True``). 382 383 strip_prompt: ``True`` 384 Remove the trailing router prompt from the output (default: ``True``). 385 386 strip_command: ``True`` 387 Remove the echo of the command from the output (default: ``True``). 388 389 normalize: ``True`` 390 Ensure the proper enter is sent at end of command (default: ``True``). 391 392 use_textfsm: ``False`` 393 Process command output through TextFSM template (default: ``False``). 394 395 CLI Example: 396 397 .. code-block:: bash 398 399 salt '*' netmiko.send_command 'show version' 400 salt '*' netmiko.send_command 'show_version' host='router1.example.com' username='example' device_type='cisco_ios' 401 """ 402 return call("send_command", command_string, **kwargs) 403 404 405def send_command_timing(command_string, **kwargs): 406 """ 407 Execute command_string on the SSH channel using a delay-based mechanism. 408 Generally used for show commands. 409 410 command_string 411 The command to be executed on the remote device. 412 413 delay_factor: ``1`` 414 Multiplying factor used to adjust delays (default: ``1``). 415 416 max_loops: ``500`` 417 Controls wait time in conjunction with delay_factor. Will default to be 418 based upon self.timeout. 419 420 strip_prompt: ``True`` 421 Remove the trailing router prompt from the output (default: ``True``). 422 423 strip_command: ``True`` 424 Remove the echo of the command from the output (default: ``True``). 425 426 normalize: ``True`` 427 Ensure the proper enter is sent at end of command (default: ``True``). 428 429 use_textfsm: ``False`` 430 Process command output through TextFSM template (default: ``False``). 431 432 CLI Example: 433 434 .. code-block:: bash 435 436 salt '*' netmiko.send_command_timing 'show version' 437 salt '*' netmiko.send_command_timing 'show version' host='router1.example.com' username='example' device_type='arista_eos' 438 """ 439 return call("send_command_timing", command_string, **kwargs) 440 441 442def enter_config_mode(**kwargs): 443 """ 444 Enter into config mode. 445 446 config_command 447 Configuration command to send to the device. 448 449 pattern 450 Pattern to terminate reading of channel. 451 452 CLI Example: 453 454 .. code-block:: bash 455 456 salt '*' netmiko.enter_config_mode 457 salt '*' netmiko.enter_config_mode device_type='juniper_junos' ip='192.168.0.1' username='example' 458 """ 459 return call("config_mode", **kwargs) 460 461 462def exit_config_mode(**kwargs): 463 """ 464 Exit from configuration mode. 465 466 exit_config 467 Command to exit configuration mode. 468 469 pattern 470 Pattern to terminate reading of channel. 471 472 CLI Example: 473 474 .. code-block:: bash 475 476 salt '*' netmiko.exit_config_mode 477 salt '*' netmiko.exit_config_mode device_type='juniper' ip='192.168.0.1' username='example' 478 """ 479 return call("exit_config_mode", **kwargs) 480 481 482def send_config( 483 config_file=None, 484 config_commands=None, 485 template_engine="jinja", 486 commit=False, 487 context=None, 488 defaults=None, 489 saltenv="base", 490 **kwargs 491): 492 """ 493 Send configuration commands down the SSH channel. 494 Return the configuration lines sent to the device. 495 496 The function is flexible to send the configuration from a local or remote 497 file, or simply the commands as list. 498 499 config_file 500 The source file with the configuration commands to be sent to the 501 device. 502 503 The file can also be a template that can be rendered using the template 504 engine of choice. 505 506 This can be specified using the absolute path to the file, or using one 507 of the following URL schemes: 508 509 - ``salt://``, to fetch the file from the Salt fileserver. 510 - ``http://`` or ``https://`` 511 - ``ftp://`` 512 - ``s3://`` 513 - ``swift://`` 514 515 config_commands 516 Multiple configuration commands to be sent to the device. 517 518 .. note:: 519 520 This argument is ignored when ``config_file`` is specified. 521 522 template_engine: ``jinja`` 523 The template engine to use when rendering the source file. Default: 524 ``jinja``. To simply fetch the file without attempting to render, set 525 this argument to ``None``. 526 527 commit: ``False`` 528 Commit the configuration changes before exiting the config mode. This 529 option is by default disabled, as many platforms don't have this 530 capability natively. 531 532 context 533 Variables to add to the template context. 534 535 defaults 536 Default values of the context_dict. 537 538 exit_config_mode: ``True`` 539 Determines whether or not to exit config mode after complete. 540 541 delay_factor: ``1`` 542 Factor to adjust delays. 543 544 max_loops: ``150`` 545 Controls wait time in conjunction with delay_factor (default: ``150``). 546 547 strip_prompt: ``False`` 548 Determines whether or not to strip the prompt (default: ``False``). 549 550 strip_command: ``False`` 551 Determines whether or not to strip the command (default: ``False``). 552 553 config_mode_command 554 The command to enter into config mode. 555 556 CLI Example: 557 558 .. code-block:: bash 559 560 salt '*' netmiko.send_config config_commands="['interface GigabitEthernet3', 'no ip address']" 561 salt '*' netmiko.send_config config_commands="['snmp-server location {{ grains.location }}']" 562 salt '*' netmiko.send_config config_file=salt://config.txt 563 salt '*' netmiko.send_config config_file=https://bit.ly/2sgljCB device_type='cisco_ios' ip='1.2.3.4' username='example' 564 """ 565 if config_file: 566 file_str = __salt__["cp.get_file_str"](config_file, saltenv=saltenv) 567 if file_str is False: 568 raise CommandExecutionError("Source file {} not found".format(config_file)) 569 elif config_commands: 570 if isinstance(config_commands, ((str,), str)): 571 config_commands = [config_commands] 572 file_str = "\n".join(config_commands) 573 # unify all the commands in a single file, to render them in a go 574 if template_engine: 575 file_str = __salt__["file.apply_template_on_contents"]( 576 file_str, template_engine, context, defaults, saltenv 577 ) 578 # whatever the source of the commands would be, split them line by line 579 config_commands = [line for line in file_str.splitlines() if line.strip()] 580 kwargs = clean_kwargs(**kwargs) 581 if "netmiko.conn" in __proxy__: 582 conn = __proxy__["netmiko.conn"]() 583 if not conn or not conn.is_alive(): 584 conn, _ = _prepare_connection(**__proxy__["netmiko.args"]()) 585 else: 586 conn, kwargs = _prepare_connection(**kwargs) 587 if commit: 588 kwargs["exit_config_mode"] = False # don't exit config mode after 589 # loading the commands, wait for explicit commit 590 ret = conn.send_config_set(config_commands=config_commands, **kwargs) 591 if commit: 592 ret += conn.commit() 593 return ret 594 595 596def commit(**kwargs): 597 """ 598 Commit the configuration changes. 599 600 .. warning:: 601 602 This function is supported only on the platforms that support the 603 ``commit`` operation. 604 605 CLI Example: 606 607 .. code-block:: bash 608 609 salt '*' netmiko.commit 610 """ 611 return call("commit", **kwargs) 612