Code Monkey home page Code Monkey logo

napalm-ansible's Introduction

napalm-ansible

Collection of ansible modules that use napalm to retrieve data or modify configuration on networking devices.

Modules

The following modules are currently available:

  • napalm_cli
  • napalm_diff_yang
  • napalm_get_facts
  • napalm_install_config
  • napalm_parse_yang
  • napalm_ping
  • napalm_translate_yang
  • napalm_validate

Action-Plugins

Action-Plugins should be used to make napalm-ansible more consistent with the behavior of other Ansible modules (eliminate the need of a provider and of individual task arguments for hostname, username, password, and timeout).

They provide default parameters for the hostname, username, password and timeout paramters.

  • hostname is set to the first of provider {{ hostname }}, provider {{ host }}, play-context remote_addr.
  • username is set to the first of provider {{ username }}, play-context connection_user.
  • password is set to the first of provider {{ password }}, play-context password (-k argument).
  • timeout is set to the provider {{ timeout }}, or else defaults to 60 seconds (can't be passed via command-line).

Installing

You can install the napalm-ansible collection with the Ansible Galaxy CLI:

ansible-galaxy collection install napalm.napalm

You can also include it in a requirements.yml file and install it with ansible-galaxy collection install -r requirements.yml, using the format:

---
collections:
  - name: napalm.napalm

You will also need to install napalm via Pip as it is a dependency

pip install napalm

Alternatively, you can install it via Pip

pip install napalm napalm-ansible

Or:

git clone https://github.com/napalm-automation/napalm-ansible
pip install napalm

Configuring Ansible

If you have installed napalm-ansible via Pip you will need to add library and actions_plugins paths in ansible.cfg. Instructions can be found by running napalm-ansible

$ cat .ansible.cfg

[defaults]
library = ~/napalm-ansible/napalm_ansible/modules
action_plugins = ~/napalm-ansible/napalm_ansible/plugins/action
...

For more details on ansible's configuration file visit:
https://docs.ansible.com/ansible/latest/intro_configuration.html

Dependencies

Examples

Cisco IOS

Inventory (IOS)

[cisco]
cisco5 ansible_host=cisco5.domain.com

[cisco:vars]
# Must match Python that NAPALM is installed into.
ansible_python_interpreter=/path/to/venv/bin/python
ansible_network_os=ios
ansible_connection=network_cli
ansible_user=admin
ansible_ssh_pass=my_password

Playbook (IOS)

---
- name: NAPALM get_facts and get_interfaces
  hosts: cisco5
  gather_facts: False
  tasks:
    - name: napalm get_facts
      napalm_get_facts:
        filter: facts,interfaces

    - debug:
        var: napalm_facts

Playbook Output (IOS)

$ ansible-playbook napalm_get_ios.yml

PLAY [NAPALM get_facts and get_interfaces] *********

TASK [napalm get_facts] ****************************
ok: [cisco5]

TASK [debug] ***************************************
ok: [cisco5] => {
    "napalm_facts": {
        "fqdn": "cisco5.domain.com",
        "hostname": "cisco5",
        "interface_list": [
            "GigabitEthernet1",
            "GigabitEthernet2",
            "GigabitEthernet3",
            "GigabitEthernet4",
            "GigabitEthernet5",
            "GigabitEthernet6",
            "GigabitEthernet7"
        ],
        "model": "CSR1000V",
        "os_version": "Virtual XE Software, Version 16.9.3, RELEASE SOFTWARE (fc2)",
        "serial_number": "9700000000P",
        "uptime": 13999500,
        "vendor": "Cisco"
    }
}

PLAY RECAP *****************************************
cisco5 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0   

Arista EOS

Inventory (EOS)

[arista]
arista5 ansible_host=arista5.domain.com

[arista:vars]
# Must match Python that NAPALM is installed into.
ansible_python_interpreter=/path/to/venv/bin/python
ansible_network_os=eos
# Continue using 'network_cli' (NAPALM module itself will use eAPI)
ansible_connection=network_cli
ansible_user=admin
ansible_ssh_pass=my_password

Playbook (EOS)

---
- name: NAPALM get_facts and get_interfaces
  hosts: arista5
  gather_facts: False
  tasks:
    - name: napalm get_facts
      napalm_get_facts:
        filter: facts,interfaces

    - debug:
        var: napalm_facts

Playbook Output (EOS)

$ ansible-playbook napalm_get_arista.yml

PLAY [NAPALM get_facts and get_interfaces] *********

TASK [napalm get_facts] ****************************
ok: [arista5]

TASK [debug] ***************************************
ok: [arista5] => {
    "napalm_facts": {
        "fqdn": "arista5",
        "hostname": "arista5",
        "interface_list": [
            "Ethernet1",
            "Ethernet2",
            "Ethernet3",
            "Ethernet4",
            "Ethernet5",
            "Ethernet6",
            "Ethernet7",
            "Management1",
            "Vlan1"
        ],
        "model": "vEOS",
        "os_version": "4.20.10M-10040268.42010M",
        "serial_number": "",
        "uptime": 12858220,
        "vendor": "Arista"
    }
}

PLAY RECAP ****************************************
arista5 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0   

Cisco NX-OS

Inventory (NX-OS)

[nxos]
nxos1 ansible_host=nxos1.domain.com

[nxos:vars]
# Must match Python that NAPALM is installed into.
ansible_python_interpreter=/path/to/venv/bin/python
ansible_network_os=nxos
# Continue using 'network_cli' (NAPALM module itself will use NX-API)
ansible_connection=network_cli
ansible_user=admin
ansible_ssh_pass=my_password

Playbook (NX-OS NX-API)

---
- name: napalm 
  hosts: nxos1
  gather_facts: False
  tasks:
    - name: Retrieve get_facts, get_interfaces
      napalm_get_facts:
        filter: facts,interfaces
        # Specify NX-API Port
        optional_args:
          port: 8443

    - debug:
        var: napalm_facts

Playbook Output (NX-OS NX-API)

$ ansible-playbook napalm_get_nxos.yml

PLAY [napalm] ***************************************

TASK [Retrieve get_facts, get_interfaces] ***********
ok: [nxos1]

TASK [debug] ****************************************
ok: [nxos1] => {
    "napalm_facts": {
        "fqdn": "nxos1.domain.com",
        "hostname": "nxos1",
        "interface_list": [
            "mgmt0",
            "Ethernet1/1",
            "Ethernet1/2",
            "Ethernet1/3",
            "Ethernet1/4",
            "Vlan1"
        ],
        "model": "Nexus9000 9000v Chassis",
        "os_version": "",
        "serial_number": "9B00000000S",
        "uptime": 12767664,
        "vendor": "Cisco"
    }
}

PLAY RECAP ******************************************
nxos1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0   

Playbook (NX-OS SSH)

---
- name: napalm nxos_ssh
  hosts: nxos1
  tasks:
    - name: Retrieve get_facts, get_interfaces
      napalm_get_facts:
        filter: facts,interfaces
        # Instruct NAPALM module to use SSH
        dev_os: nxos_ssh

    - debug:
        var: napalm_facts

Playbook Output (NX-OS SSH)

$ ansible-playbook napalm_get_nxos_ssh.yml

PLAY [napalm nxos_ssh] ********************************

TASK [Retrieve get_facts, get_interfaces] *************
ok: [nxos1]

TASK [debug] ******************************************
ok: [nxos1] => {
    "napalm_facts": {
        "fqdn": "nxos1.domain.com",
        "hostname": "nxos1",
        "interface_list": [
            "mgmt0",
            "Ethernet1/1",
            "Ethernet1/2",
            "Ethernet1/3",
            "Ethernet1/4",
            "Vlan1"
        ],
        "model": "Nexus9000 9000v Chassis",
        "os_version": "9.2(3)",
        "serial_number": "9000000000S",
        "uptime": 12767973,
        "vendor": "Cisco"
    }
}

PLAY RECAP ********************************************
nxos1 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0   

Juniper Junos

Inventory (Junos)

[juniper]
juniper1 ansible_host=juniper1.domain.com

[juniper:vars]
# Must match Python that NAPALM is installed into.
ansible_python_interpreter=/path/to/venv/bin/python
ansible_network_os=junos
# Continue using 'network_cli' (NAPALM module itself will use NETCONF)
ansible_connection=network_cli
ansible_user=admin
ansible_ssh_pass=my_password

Playbook (Junos)

---
- name: napalm 
  hosts: juniper
  gather_facts: False
  tasks:
    - name: Retrieve get_facts, get_interfaces
      napalm_get_facts:
        filter: facts,interfaces

    - debug:
        var: napalm_facts

Playbook Output (Junos)

$ ansible-playbook napalm_get_junos.yml -i

PLAY [napalm] *****************************************

TASK [Retrieve get_facts, get_interfaces] *************
ok: [juniper1]

TASK [debug] ******************************************
ok: [juniper1] => {
    "napalm_facts": {
        "fqdn": "juniper1",
        "hostname": "juniper1",
        "interface_list": [
            "fe-0/0/0",
            "gr-0/0/0",
            "ip-0/0/0",
            "lt-0/0/0",
            "mt-0/0/0",
            "sp-0/0/0",
            "fe-0/0/1",
            "fe-0/0/2",
            "fe-0/0/3",
            "fe-0/0/4",
            "fe-0/0/5",
            "fe-0/0/6",
            "fe-0/0/7",
            "gre",
            "ipip",
            "irb",
            "lo0",
            "lsi",
            "mtun",
            "pimd",
            "pime",
            "pp0",
            "ppd0",
            "ppe0",
            "st0",
            "tap",
            "vlan"
        ],
        "model": "SRX100H2",
        "os_version": "12.1X44-D35.5",
        "serial_number": "BZ0000000008",
        "uptime": 119586097,
        "vendor": "Juniper"
    }
}

PLAY RECAP *******************************************
juniper1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0   

Example to install config on a device

- assemble:
    src=../compiled/{{ inventory_hostname }}/
    dest=../compiled/{{ inventory_hostname }}/running.conf

 - napalm_install_config:
    hostname={{ inventory_hostname }}
    username={{ user }}
    dev_os={{ os }}
    password={{ passwd }}
    config_file=../compiled/{{ inventory_hostname }}/running.conf
    commit_changes={{ commit_changes }}
    replace_config={{ replace_config }}
    get_diffs=True
    diff_file=../compiled/{{ inventory_hostname }}/diff

Example to get compliance report

- name: GET VALIDATION REPORT
  napalm_validate:
    username: "{{ user }}"
    password: "{{ passwd }}"
    hostname: "{{ inventory_hostname }}"
    dev_os: "{{ dev_os }}"
    validation_file: validate.yml

napalm-ansible's People

Contributors

aaronjohnson avatar benmaddison avatar costasd avatar cspeidel avatar dbarrosop avatar decoupca avatar ebeahan avatar fooelisa avatar ggabriele avatar itdependsnetworks avatar jedelman8 avatar ktbyers avatar lindsayhill avatar mzbenami avatar ogenstad avatar targuan avatar vincentbernat avatar xionox avatar yeled avatar yzguy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

napalm-ansible's Issues

napam-ansible: config replace not working ( candidate.txt "%The input file is not a valid config file")

Hi All
Need help with this issue. A similar issues #209 was opened in the past. It is closed but i am not sure whether a fix is available.

Problem: candidate.txt file created for config replace operation with ansible napalm module is considered not valid by c6k ios 12.2(33)SXJ4,

Here are the files created on the router after executing the playbook.

CAT6KLAB05#dir
Directory of sup-bootdisk:/
1 -rw- 143315812 Mar 19 2017 13:05:24 +00:00 s72033-advipservicesk9_wan-mz.122-33.SXJ4.bin
--snip----
10 -rw- 21 Apr 29 2017 14:09:18 +00:00 candidate.txt
5 -rw- 0 Apr 29 2017 14:05:58 +00:00 merge_config.txt
--snip----
17 -rw- 6268 Apr 29 2017 14:09:30 +00:00 rollback_config.txt
--snip----
13 -rw- 6250 Apr 29 2017 05:08:20 +00:00 ArchiveCfg-Apr-29-05-08-20.040-8318

CAT6KLAB05# configure replace sup-bootdisk:candidate.txt
This will apply all necessary additions and deletions
to replace the current running configuration with the
contents of the specified configuration file, which is
assumed to be a complete configuration, not a partial
configuration. Enter Y if you are sure you want to proceed. ? [no]: Y

%The input file is not a valid config file.


Here is my playbook.

  • name: NAPALM Playbook
    hosts: all
    connection: local
    gather_facts: False

    tasks:

    • napalm_install_config:
      hostname={{ inventory_hostname }}
      username={{ username }}
      dev_os=ios
      password={{ password }}
      config_file=initial.conf
      commit_changes=True
      replace_config=True
      diff_file=initial.diff

Here is my initial.conf. I am trying to config replace with just one cli ( yes with only hostname to see the diff)
$ more initial.conf
hostname TESTMYSCRIPT

Here is the diff generated. And it is as expected.

$ more initial.diff
+hostname TESTMYSCRIPT-service timestamps debug datetime msec
-service timestamps log datetime msec
-no service password-encryption
-service counters max age 5
-hostname CAT6KLAB05
-boot-start-marker
-boot-end-marker
--snip----

Thanks for the help.

Create a meta/mail.yml file

- src: https://github.com/napalm-automation/napalm-ansible/
  version: master
  name: napalm
  path: roles

In requirements.yml
then:
ansible-galaxy install -r requirements.yml --force

Returns
[WARNING]: - napalm was NOT installed successfully: ERROR! this role does not appear to have a meta/main.yml file.

Trying to compare non-existend diff

task:

- name: Push configuration with napalm
  napalm_install_config:
    hostname: "{{ inventory_hostname }}"
    dev_os: ios
    username: "{{ cisco_admins.0.username }}"
    password: "{{ cisco_admins.0.password }}"
    config_file: cisco-configs/{{ inventory_hostname }}/running-config
    commit_changes: False
    replace_config: False
    diff_file: cisco-config/{{ inventory_hostname }}/diff

ouput:

TASK: [cisco-ios | Push configuration with napalm] **************************** 
failed: [gw01ram1rls.ram1.rls.io] => {"failed": true, "parsed": false}
SSH connection established to gw01ram1rls.ram1.rls.io:22
Interactive SSH session established
+ service timestamps debug datetime msec
...
+ end
Traceback (most recent call last):
  File "/home/t/.ansible/tmp/ansible-tmp-1454334065.32-270842437256163/napalm_install_config", line 1792, in <module>
    main()
  File "/home/t/.ansible/tmp/ansible-tmp-1454334065.32-270842437256163/napalm_install_config", line 163, in main
    diff = device.compare_config().encode('utf-8')
AttributeError: 'NoneType' object has no attribute 'encode'


FATAL: all hosts have already failed -- aborting

Module "napalm_install_config" fails when the parameter "config_file" is located in a home directory (i.e. '~/.compiled/`)

As stated in the title, this appears to be an problem with just napalm_install_config module due to the fact that the stat module is able to locate it fine.

Running the install role:

TASK [install : Ensure assembled configuration file exists] ******************************************                                                                                               [32/966]
ok: [rtp-vba-a]

TASK [install : debug] *******************************************************************************
ok: [rtp-vba-a] => {
    "result.stat": {
        "atime": 1499900564.0014858,
        "attr_flags": "e",
        "attributes": [
            "extents"
        ],
        "block_size": 4096,
        "blocks": 152,
        "charset": "us-ascii",
        "checksum": "05a37e3fdb9a92e7efd17e3210bdde77d23bb729",
        "ctime": 1499900527.9213126,
        "dev": 64512,
        "device_type": 0,
        "executable": false,
        "exists": true,
        "gid": 901000001,
        "gr_name": "coc-users",
        "inode": 147520,
        "isblk": false,
        "ischr": false,
        "isdir": false,
        "isfifo": false,
        "isgid": false,
        "islnk": false,
        "isreg": true,
        "issock": false,
        "isuid": false,
        "md5": "345db65692def774e6af2e24f9d60bdb",
        "mimetype": "text/plain",
        "mode": "0644",
        "mtime": 1499900527.9213126,
        "nlink": 1,
        "path": "/home/bl839s/.compiled/rtp-vba-a/assembled.conf",
        "pw_name": "bl839s",
        "readable": true,
        "rgrp": true,
        "roth": true,
        "rusr": true,
        "size": 77357,
        "uid": 901000002,
        "version": "18446744072107431939",
        "wgrp": false,
        "woth": false,
        "writeable": true,
        "wusr": true,
        "xgrp": false,
        "xoth": false,
        "xusr": false
    }
}

TASK [install : Fail is the configuration file cannot be located] ************************************
skipping: [rtp-vba-a]

TASK [install : Push configuration onto the device] **************************************************
fatal: [rtp-vba-a]: FAILED! => {"changed": false, "failed": true, "msg": "cannot load config: [Errno 2] No such file or directory: '~/.compiled/rtp-vba-a/assembled.conf'"}

PLAY RECAP *******************************************************************************************
rtp-vba-a                  : ok=2    changed=0    unreachable=0    failed=1

Contents of the install role:

$ cat roles/install/tasks/main.yml
- name: Ensure assembled configuration file exists
  stat:
    path: "{{ host_dir }}/assembled.conf"
  register: result

- debug:
    var: result.stat

- name: Fail is the configuration file cannot be located
  fail:
    msg: "Cannot locate configuration file to push!"
  when: result.stat.exists == False

- name: Push configuration onto the device
  napalm_install_config:
    provider: "{{ napalm }}"
    config_file: "{{ host_dir }}/assembled.conf"
    commit_changes: "{{ not ansible_check_mode }}"
    get_diffs: true
    diff_file: "{{ host_dir }}/config.diff"

device.commit_config()

Hi all,
Im working with Napalm with python and Im executing the following lines. This is a Cisco-xe. (ref link http://networktocode.com/labs/tutorials/how-to-use-napalm-python-library-to-manage-ios-devices/)

from napalm import get_network_driver
driver = get_network_driver('ios')
device = driver('csr1', 'ntc', 'ntc123')
device.open()
device.load_replace_candidate(filename='newconfig.cfg')
device.commit_config()

After I commit I get the following error
device.commit_config()
Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python2.7/dist-packages/napalm/ios/ios.py", line 447, in commit_config
raise ReplaceConfigException(msg)
napalm.base.exceptions.ReplaceConfigException: Candidate config could not be applied

Failed to apply command Last configuration change at 13:41:24 UTC Thu Jan 18 2018
Aborting Rollback.

Rollback failed.Reverting back to the original configuration: bootflash:archive-Jan-18-17-54-15.366-0 ...

Total number of passes: 1
Rollback Done

The original configuration has been successfully restored.

Thanks.

No such file or directory: 'initial.conf'

Hi,
I just trying napalm-ansible, but the playbook give error No such file or directory: 'initial.conf'
What steps I miss

tq

GOAL:
-change cisco router hostname from R1 to R11

# ansible-playbook playbooks/napalm.yml
PLAY [cisco1] ******************************************************************
TASK [Gathering Facts] *********************************************************
ok: [cisco1]
TASK [create a diff] ***********************************************************
fatal: [cisco1]: FAILED! => {"changed": false, "msg": "cannot load config: [Errno 2] No such file or directory: 'initial.conf'"}
PLAY RECAP *********************************************************************
cisco1                     : ok=1    changed=0    unreachable=0    failed=1

# pip freeze | grep napalm
napalm==2.3.2
napalm-ansible==0.10.0
napalm-base==1.0.0
napalm-fortios==0.4.1
napalm-ios==0.8.1
napalm-junos==0.12.1

# cat playbooks/napalm.yml
---
- hosts: cisco1
  connection: local
  tasks:
  - name: "create a diff"
    napalm_install_config:
      hostname: "{{ansible_host}}"
      username: "{{ansible_user}}"
      password: "{{ansible_ssh_pass}}"
      optional_args: {'secret': '{{ansible_become_pass}}'}
      dev_os: "{{ansible_network_os}}"
      config_file: initial.conf
      commit_changes: false
      replace_config: false
      diff_file: initial.diff

# cat playbooks/initial.conf
hostname R11

# cat group_vars/cisco.yml
---
ansible_connection: network_cli
ansible_network_os: ios
ansible_user: cisco
ansible_ssh_pass: cisco
ansbile_become: yes
ansible_become_method: enable
ansible_become_pass: cisco

route_to on junos - missing parameters

I'm trying to use the 'route_to' filter on get_facts and I'm failing:

  tasks:
   - name: get facts from device
     napalm_get_facts:
       provider: "{{ junos_provider }}"
       filter: 'route_to'
     register: result

$ ansible-playbook -i ./inventory/ ./getdata.yaml

PLAY [fetch info] ****************************************************************************************************************************************************************************

TASK [get facts from device] *****************************************************************************************************************************************************************
fatal: [10.69.0.208]: FAILED! => {"changed": false, "failed": true, "msg": "[route_to] cannot retrieve device data: Unknown protocol: destination"}
        to retry, use: --limit @/home/ubuntu/2d-automation/ansible/getdata.retry

PLAY RECAP ***********************************************************************************************************************************************************************************
10.69.0.208               : ok=0    changed=0    unreachable=0    failed=1

Looking at the code in napalm-junos the function expects two parameters, but I don't think there is a way to pass them.

Mechanism to install modules

@ktbyers I was thinking in a more "pythonic" way of installing these modules. We could have a regular develop -> master -> release lifecycle like we have with regular drivers and have the setup.py provide a simple cli tool whereis-napalm-ansible that returns the path to where the modules where installed so users can add that path to the ansible.cfg file.

This would give us a simple mechanism to deploy the modules with all the pip goodies (pin versions, different venv with different versions of the modules, easy rollback if a newer version breaks something, etc).

Thoughts?

Add namespace to the facts returned by napalm_get_facts

The module napalm_get_facts currently set the result in ansible variables that are have quite generic name (for instance interfaces). I run into an issue where I wished to retrieve the list of interfaces on an equipements and that erased the content of my inventory variable interfaces. I had to rework all my inventory in order to avoid collision of the variables names.

In the network core modules, the facts gethered by the differents *_facts put the results in a variable called ansible_net_*. I'd like to know if this behavour would be accepted in napalm ?
Either by putting all the variables in a dictionnary napalm_facts: {interfaces: []} or by prepending napalm_ to all the variables names.

In order not to break compatibility with the current names, it could also be implements with a new argument to the module namespace.

ios issue - can't determine file system

when using ios for ansible, encounter below issue -

fatal: [router1]: FAILED! => {"changed": false, "failed": true, "msg": "cannot connect to device: An error occurred in dynamically determining remote file system: dir router1#\nrouter1#\nrouter1#"}

my user is priviledge 15 (tacacs), not sure why this happens

Python 2.7.12+ (default, Sep 17 2016, 12:08:02)
[GCC 6.2.0 20160914] on linux2
Type "help", "copyright", "credits" or "license" for more information.

import napalm_base
import napalm_ios
napalm_ios.version
'0.6.2'
netmiko.version
u'1.4.0'

my playbook

  • name: Test Inventory #The Task Name
    hosts: router1 #This will be in your ansible inventory file
    connection: local #Required
    gather_facts: no

    tasks: #Begin Tasks

    • name: get facts from device #Task Name
      napalm_get_facts: #Call the napalm module, in this case napal_get_facts
      hostname: "{{ hostname }}" #This is a parameter and is derived from your ansible inventory file
      username: 'user1' #The username to ssh with
      dev_os: 'ios' #The hardware operating system
      password: 'pass1' #The line level password
      optional_args: {'secret': 'pass2'}
      filter: 'interfaces, bgp_neighbors' #The list of items you want to retrieve. The filter keyword is inclusive of what you want
      register: result #Ansible function for collecting output

    • name: print results #Task Name
      debug: msg= "{{ result }}" #Display the collected output

actually my user is already pri 15 so I don't think secret is needed.

same playbook work for junos.

Without `serial: 1` napalm_install_config fails due to disappearing candidate.cfg

Hi!

I use napalm from the pip repository, the current napalm-ansible development branch from github together with the napalm-aos community driver (development version) with Python 3.

When I run a playbook without serial: 1 I almost randomly (i. e. not for every task) encounter errors like

failed: [myswitchname] (item=/xxx/ansible-networking/configs/myswitchname/020-mst-merge.txt) => {"changed": false, "item": "/xxx/ansible-networking/configs/myswitchname/020-mst-merge.txt", "msg": "cannot load config: [Errno 2] No such file or directory: '/var/folders/1b/zt9ydwjj26l57xlrrn0hgjj80000gn/T/candidate.cfg'"}

as soon as I run several "napalm_install_config" tasks. This particular playbook currently runs for two hosts. If I add serial: 1 to the playbook, everything works as expected. I think that the problem is that "candidate.cfg" is not a unique filename for each host and I believe that the "napalm_install_config" tasks that run in parallel probably delete "candidate.cfg" as soon as they finished while another task might expect to be working on its own "candidate.cfg". I was trying to figure out which part of napalm-ansible actually creates the "candidate.cfg" file. I suspect that it is the driver but I'm not entirely sure -- could you help me out? In any case, an easy fix would be to add some randomness to the name for each creation of the file to make it unique. I hope to be able to help with a fix as soon as I understand where the filename is set.

Add support for passing of command line arguments (in a manner consistent with Ansible core modules)

---
- name: NAPALM on IOS 
  hosts: pynet-rtr1
  tasks:
    - name: NAPALM facts
      napalm_get_facts: 
        hostname: "{{ ansible_host }}"
        dev_os: "ios"
$ ansible-playbook napalm_w_cli.yml -u pyclass -k
SSH password: 

PLAY [NAPALM on IOS] *************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************************************************************************
ok: [pynet-rtr1]

TASK [NAPALM facts] **************************************************************************************************************************************************************************************************
fatal: [pynet-rtr1]: FAILED! => {"changed": false, "failed": true, "msg": "username is required"}

PLAY RECAP ***********************************************************************************************************************************************************************************************************
pynet-rtr1                 : ok=1    changed=0    unreachable=0    failed=1   

Also should support if using a provider.

Argument `filter` in napalm_get_facts is not a list

Despite the documentation claiming that is a list the filter argument is in fact a string. It turns out you can pass a comma separated list of filters but it's not clear in the documentation and it's very fragile as facts,bgp_neighbors will work but facts, bgp_neighbors will not. Why not make it a real list instead?

Should give a better error when IOS file transfer fails due to no file-system

Virtual devices on IOS might not have a file system which might fail with this vague message:

fatal: [r2]: FAILED! => {"changed": false, "msg": "cannot load config: 'NoneType' object has no attribute 'group'"}
fatal: [r1]: FAILED! => {"changed": false, "msg": "cannot load config: 'NoneType' object has no attribute 'group'"}

Extend a driver and reference it with napalm-ansible

Dear napalm developers,

It is not clear to me how to reference a custom method deployed for a network driver into the napalm-ansible module.

Reading the documentation : http://napalm.readthedocs.io/en/latest/tutorials/extend_driver.html it is stated

โ€˜The positive side effect is that tools such as Salt, Ansible, and Netbox implicitly have access to these methods.โ€™

So far what I was able to obtain is to leverage my method (get_service_policies) from a standard python script. I have created in the script working directory the custom_napalm folder with the ios.py file and I have expanded the IOSDriver class.

To my understanding I have to write an ansible task as this:

  • name: get facts from device
    napalm_get_facts:
    hostname={{ ansible_host }}
    username={{ ansible_user }}
    dev_os={{ os }}
    password={{ ansible_password }}
    filter='service_policies'
    register: result

I believe I have to move this folder somewhere else in order to be visible from the ansible module. Can you clarify ?

Error with napalm_install_config for nxos_ssh OS - Search pattern never detected in send_command_expect: [hostname]

I am using napalm-ansible to install configuration on various equipment and I am getting an error on a specific Nexus7k host.

Everything works fine on my other three Nexus (and on other types of equipment and on all equipment in my dev environment), so this should not be a problem with the way I use the module.

Here is the part in my playbook where I use the napalm_install_config module:

---

- name: "{{ equipment_family }} - Install the configuration using the napalm-ansible library"
  napalm_install_config:
    hostname: "{{ ansible_ssh_host }}"
    username: "{{ smi_username }}"
    password: "{{ smi_password }}"
    dev_os: "{{ napalm_install_config_dev_os }}"
    config_file: "{{ master_config_file }}"
    commit_changes: "{{ not ansible_check_mode }}"
    replace_config: False
    get_diffs: True
    diff_file: "{{ diff_file }}"
    timeout: "{{ napalm_install_config_timeout }}"
  any_errors_fatal: true

And here is the verbose output I get when this part runs on the problematic host:

The full traceback is:
  File "/tmp/ansible_gJc7b8/ansible_module_napalm_install_config.py", line 311, in main
    device.commit_config()
  File "/usr/lib/python2.7/site-packages/napalm/nxos/nxos.py", line 54, in commit_config
    self._save_to_checkpoint(self.backup_file)
  File "/usr/lib/python2.7/site-packages/napalm/nxos_ssh/nxos_ssh.py", line 645, in _save_to_checkpoint
    self.device.send_command(command)
  File "/usr/lib/python2.7/site-packages/netmiko/base_connection.py", line 1112, in send_command
    search_pattern))

fatal: [QCDRVLS202]: FAILED! => {
    "changed": false, 
    "invocation": {
        "module_args": {
            "archive_file": null, 
            "candidate_file": null, 
            "commit_changes": true, 
            "config": null, 
            "config_file": "/var/lib/awx/projects/_79__customer_provisioning_v72/playbooks/../files/configs/2018-08-24_11:35:30/QCDRVLS202/___master.conf", 
            "dev_os": "nxos_ssh", 
            "diff_file": "/var/lib/awx/projects/_79__customer_provisioning_v72/playbooks/../files/diffs/2018-08-24_11:35:30/QCDRVLS202.diff", 
            "get_diffs": true, 
            "hostname": "10.199.254.47", 
            "optional_args": null, 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "provider": {
                "hostname": "10.199.254.47", 
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "timeout": 60, 
                "username": "removed"
            }, 
            "replace_config": false, 
            "timeout": 60, 
            "username": "removed"
        }
    }, 
    "msg": "cannot install config: Search pattern never detected in send_command_expect: QCDRVLS202\\#"
}

As I said, it always happens on the QCDRVLS202 Nexus even though the configuration that I send to this host is the same as the one I send to other hosts.

This error doesn't happen in check_mode. From what I gathered while searching on Google, this might be due to the fact that this specific host might be slower to respond than the three other similar hosts, which makes the prompt "time-out" while waiting for the "QCMTRLS202#" text on the command line, but I can't seem to find a way to make it wait longer.

If you require additional information, I will be glad to provide it as it is very important to me to solve this issue .

Thank you!

Issue with IOS module after upgrading to napalm-ansible 0.9.0

After upgrading to the new napalm-ansible 0.9.0. Any play I run for ios fails with the below. EOS and NXOS are fine. It was installed via the setup command sudo python ./setup.py install

FAILED! => {"changed": false, "failed": true, "msg": "cannot connect to device: Cannot import "napalm_ios". Is the library installed?"}

sudo pip freeze | grep napalm
napalm==2.3.0
napalm-ansible==0.9.0
napalm-base==0.25.0
napalm-eos==0.5.2
napalm-fortios==0.3.1
napalm-ibm==0.1.6
napalm-ios==0.8.1
napalm-iosxr==0.4.6
napalm-junos==0.6.2
napalm-nxos==0.7.0
napalm-panos==0.3.0
napalm-pluribus==0.5.0
napalm-ros==0.2.0
napalm-vyos==0.1.2
napalm-yang==0.0.7

jvill@mgt1:~/ansible> python -c "from napalm.ios import IOSDriver"
Traceback (most recent call last):
File "", line 1, in
File "/opt/ixea/lib/python2.7/site-packages/napalm/ios/init.py", line 16, in
from napalm.ios.ios import IOSDriver
File "/opt/ixea/lib/python2.7/site-packages/napalm/ios/ios.py", line 28, in
from netmiko import ConnectHandler, FileTransfer, InLineTransfer
File "/opt/ixea/lib/python2.7/site-packages/netmiko/init.py", line 8, in
from netmiko.ssh_dispatcher import ConnectHandler
File "/opt/ixea/lib/python2.7/site-packages/netmiko/ssh_dispatcher.py", line 4, in
from netmiko.a10 import A10SSH
File "/opt/ixea/lib/python2.7/site-packages/netmiko/a10/init.py", line 2, in
from netmiko.a10.a10_ssh import A10SSH
File "/opt/ixea/lib/python2.7/site-packages/netmiko/a10/a10_ssh.py", line 4, in
from netmiko.cisco_base_connection import CiscoSSHConnection
File "/opt/ixea/lib/python2.7/site-packages/netmiko/cisco_base_connection.py", line 3, in
from netmiko.base_connection import BaseConnection
File "/opt/ixea/lib/python2.7/site-packages/netmiko/base_connection.py", line 24, in
from netmiko.utilities import write_bytes, check_serial_port, get_structured_data
File "/opt/ixea/lib/python2.7/site-packages/netmiko/utilities.py", line 8, in
import serial.tools.list_ports

        napalm.get_network_driver('ios')
        Traceback (most recent call last):
        File "", line 1, in
        File "/opt/jons/lib/python2.7/site-packages/napalm/base/init.py", line 95, in get_network_driver
        install_name=module_install_name
        napalm.base.exceptions.ModuleImportError: Cannot import "napalm.ios". Is the library installed?

Testing NAPALM

Hi All,

I've been trying to test napalm with the following setup and keep getting the error as shown below.

update: I also tested connectivity

[rbotham@ernie napalmpb]$ sh conn_napalm.sh 
Successfully connected to the device.
[rbotham@ernie napalmpb]$ 

Any assistance would be a great help

versions below

[rbotham@ernie napalmpb]$ ansible --version
[WARNING]: Optional dependency 'cryptography' raised an exception, falling back to 'Crypto'
ansible 2.1.1.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides

[rbotham@ernie napalmpb]$ python --version
Python 2.7.8

napalm (1.1.0)
napalm-base (0.15.0)
napalm-eos (0.3.0)
napalm-fortios (0.1.1)
napalm-ibm (0.1.1)
napalm-ios (0.2.0)
napalm-iosxr (0.2.2)
napalm-junos (0.3.0)
napalm-nxos (0.3.0)
napalm-panos (0.1.0)
napalm-pluribus (0.3.0)

[rbotham@ernie napalmpb]$ ansible --version
[WARNING]: Optional dependency 'cryptography' raised an exception, falling back to 'Crypto'
ansible 2.1.1.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides

host file contents below

[junos]
junipervm3

test_napalm2.yml file contents below


---

- name: Test NAPALM on JUNOS
  hosts: junos

  tasks:
    - name: Test Napalm on Junos
      napalm_install_config:
            hostname: junipervm3
            username: rich
            password: password
            config_file: new_config.conf


```Error
[rbotham@ernie napalmpb]$ ansible-playbook test_napalm2.yml -i ../../ansible-hosts 
 [WARNING]: Optional dependency 'cryptography' raised an exception, falling back to 'Crypto'
ERROR! no action detected in task. This often indicates a misspelled module name, or incorrect module path.

The error appears to have been in '/home/rbotham/playbooks/playbookdb/napalmpb/test_napalm2.yml': line 7, column 7, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  tasks:
    - name: Test Napalm on Junos
      ^ here


The error appears to have been in '/home/rbotham/playbooks/playbookdb/napalmpb/test_napalm2.yml': line 7, column 7, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

  tasks:
    - name: Test Napalm on Junos
      ^ here

Optional Args Not working for Arista

When attempting to push a config to Arista using napalm_install_config the subsystem fails to recognize the optional_args being passed.

According to the readthedocs entry here, https://media.readthedocs.org/pdf/napalmsdf/latest/napalmsdf.pdf, there is a list of optional args expected and on page 36 it says we can add to them but it isn't clear where the model exists to add the optional args needed for our use case.

In either case I lack the confidence that the subsystem will operate as needed if the model is updated accordingly. Please advise.

Below is sample yaml

- name: Load configuration into the device and diff
  napalm_install_config:
    hostname: "{{ ansible_host }}"
    username: test_user
    dev_os: "{{ ansible_os_family }}"
    optional_args:
      commit_session: "{{ ansible_host }}"
      commit: True
      commit_timer: 00:02:00
      commit_comments: "word"
    password: test_password
    config_file: "{{ config_file_name }}"
    commit_changeas: True
    replace_config: False
    get_diffs: False
    diff_file: "{{ ansible_os_family }}.{{ state }}.conf.diff"
  when:  has_pending_config == false
  tags: [print_action]`

Below is output error s you can see optional_args is null
fatal: [arista7508e.cvr]: FAILED! => {"changed": false, "failed": true, "invocation": {"module_args": {"archive_file": null, "candidate_file": null, "commit_changes": false, "config": null, "config_file": "/arista7508e.cvr.eos.present.conf", "dev_os": "eos", "diff_file": "/arista7508e.cvr.eos.present.conf.diff", "get_diffs": true, "hostname": "arista7508e.cvr.d0cdn.net", "optional_args": null, "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "provider": null, "replace_config": false, "timeout": 60, "username": "fjone"}, "module_name": "napalm_install_config"}, "msg": "cannot load config: Error [1002]: CLI command 3 of 4510 'commit timer ' failed: invalid command [incomplete command (at token 2: None)]"}

I can manually edit the eos.py class however we want dynamic variable input rather than static.

virtualenv

without ansible_python_interpreter set napalm module cannot be found. all other ansible modules work when a python virtualenv is activate.

python complains that module napalm cannot be found without the ansible_python_interpreter knob set.

Does napalm-ansible support network_cli

Does the modules provided by napalm-ansible support network_cli connection plugin provided by ansible (recommended in 2.5) ?

This would allow pipelining and avoid providing connection parameters such as "dev_os".

No license defined

There isn't currently a license defined for this project, which makes things difficult for external contributors. It appears most of the other projects within the Napalm project are using Apache 2.0, so I'd suggest doing the same here.

napalm_get_facts broken in 228789c6c2e33d437662675c3122ade31087381a

David already knows what the bug is, this is just a reminder he asked for ;)

Workaround: pass args: {}

The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_bEwtxf/ansible_module_napalm_get_facts.py", line 263, in <module>
    main()
  File "/tmp/ansible_bEwtxf/ansible_module_napalm_get_facts.py", line 234, in main
    result = get_func(**args.get(getter, {}))
AttributeError: 'NoneType' object has no attribute 'get'

fatal: [E1]: FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_bEwtxf/ansible_module_napalm_get_facts.py\", line 263, in <module>\n    main()\n  File \"/tmp/ansible_bEwtxf/ansible_module_napalm_get_facts.py\", line 234, in main\n    result = get_func(**args.get(getter, {}))\nAttributeError: 'NoneType' object has no attribute 'get'\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE",
    "rc": 0
}

Fix napalm-ansible python3 compatibility

Add to unit tests.

This was testing Ansible 2.4.0 'devel' branch as of August29.

The full traceback is:
Traceback (most recent call last):
  File "/home/kbyers/VENV/ansible_test3/lib/python3.5/site-packages/ansible-2.4.0-py3.5.egg/ansible/executor/task_executor.py", line 125, in run
    res = self._execute()
  File "/home/kbyers/VENV/ansible_test3/lib/python3.5/site-packages/ansible-2.4.0-py3.5.egg/ansible/executor/task_executor.py", line 526, in _execute
    result = self._handler.run(task_vars=variables)
  File "/home/kbyers/VENV/ansible_test3/lib/python3.5/site-packages/ansible-2.4.0-py3.5.egg/ansible/plugins/action/normal.py", line 45, in run
    results = merge_hash(results, self._execute_module(tmp=tmp, task_vars=task_vars, wrap_async=wrap_async))
  File "/home/kbyers/VENV/ansible_test3/lib/python3.5/site-packages/ansible-2.4.0-py3.5.egg/ansible/plugins/action/__init__.py", line 630, in _execute_module
    (module_style, shebang, module_data, module_path) = self._configure_module(module_name=module_name, module_args=module_args, task_vars=task_vars)
  File "/home/kbyers/VENV/ansible_test3/lib/python3.5/site-packages/ansible-2.4.0-py3.5.egg/ansible/plugins/action/__init__.py", line 165, in _configure_module
    environment=final_environment)
  File "/home/kbyers/VENV/ansible_test3/lib/python3.5/site-packages/ansible-2.4.0-py3.5.egg/ansible/executor/module_common.py", line 869, in modify_module
    environment=environment)
  File "/home/kbyers/VENV/ansible_test3/lib/python3.5/site-packages/ansible-2.4.0-py3.5.egg/ansible/executor/module_common.py", line 695, in _find_module_utils
    recursive_finder(module_name, b_module_data, py_module_names, py_module_cache, zf)
  File "/home/kbyers/VENV/ansible_test3/lib/python3.5/site-packages/ansible-2.4.0-py3.5.egg/ansible/executor/module_common.py", line 466, in recursive_finder
    tree = ast.parse(data)
  File "/usr/lib64/python3.5/ast.py", line 35, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "<unknown>", line 259
    except Exception, e:

"the python module napalm is required"

Got "the python module napalm is required" when calling napalm_install_config, confirmed all the required python modules are installed along with the correct path in ansible.cfg.

$ pip freeze|grep -iE 'napalm|ansible'
ansible==2.6.3
napalm==2.3.2
napalm-ansible==0.10.0
$ grep -iE 'library|action_plugins' ansible.cfg 
library           = /usr/local/lib/python2.7/site-packages/napalm_ansible/modules
action_plugins    = /usr/local/lib/python2.7/site-packages/napalm_ansible/plugins/action
The full traceback is:
  File "/var/folders/hp/zbcbp7wd5zqgcph8b5bbr3f42vxdjf/T/ansible_Maungo/ansible_module_napalm_install_config.py", line 171, in <module>
    from napalm_base import get_network_driver   # noqa

fatal: [mgmtsw2-bos1]: FAILED! => {
    "changed": false, 
    "invocation": {
        "module_args": {
            "archive_file": null, 
            "candidate_file": null, 
            "commit_changes": false, 
            "config": null, 
            "config_file": "configs/mgmtsw2-bos1", 
            "dev_os": "ios", 
            "diff_file": "configs/mgmtsw2-bos1.diff", 
            "get_diffs": true, 
            "hostname": "mgmtsw2-bos1", 
            "optional_args": null, 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "provider": {
                "hostname": "mgmtsw2-bos1", 
                "password": null, 
                "timeout": 60, 
                "username": "ansible"
            }, 
            "replace_config": false, 
            "timeout": 60, 
            "username": "labuser"
        }
    }, 
    "msg": "the python module napalm is required"
}

"msg": "the python module napalm is required"

Hi team,

OSX=10.12.5
python=2.7.13
ansible=2.3.1.0
napalm=1.2.0.0
napalm-eos=0.5.6

I'm trying to run a basic playbook :

- hosts: pod1
  connection: local
  gather_facts: no

  tasks:
  - name: BUILD
    template:
       src=templates/bgp.j2
       dest=configs/{{ inventory_hostname }}.conf
    tags: build
  - name: PUSH
    napalm_install_config:
       hostname={{ inventory_hostname }}
       username=ccompain
       password=arista
       dev_os=eos
       config_file=configs/{{ inventory_hostname }}.conf
       commit_changes=true
       replace_config=false
       get_diffs=false
    tags: push

The directory where I'm executing the code is :

~/vEOS/ansible/test4$ ls -aF
./                   configure_eos.retry  host_vars/           misc/
../                  configure_eos.yaml   hosts                templates/
configs/             group_vars/          library/

I've put the ansible-napalm (get from github) library in library/ directory.

But it doesn't work when I run the playbook - The '-vvvv' extract is :

...
TASK [PUSH] **********************************************************************************************************
task path: /Users/ccompain/vEOS/ansible/test4/configure_eos.yaml:12
Using module file /Users/ccompain/vEOS/ansible/test4/library/napalm_install_config.py
<vEOS-1> ESTABLISH LOCAL CONNECTION FOR USER: ccompain
<vEOS-1> EXEC /bin/sh -c 'echo ~ && sleep 0'
Using module file /Users/ccompain/vEOS/ansible/test4/library/napalm_install_config.py
<vEOS-2> ESTABLISH LOCAL CONNECTION FOR USER: ccompain
<vEOS-2> EXEC /bin/sh -c 'echo ~ && sleep 0'
<vEOS-1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-187657461983830 `" && echo ansible-tmp-1498484651.53-187657461983830="` echo /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-187657461983830 `" ) && sleep 0'
<vEOS-2> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-154559222962668 `" && echo ansible-tmp-1498484651.53-154559222962668="` echo /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-154559222962668 `" ) && sleep 0'
<vEOS-1> PUT /var/folders/kl/wxylqm7s0ybf9c8w9z51bdkm0000gn/T/tmpMMUrzr TO /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-187657461983830/napalm_install_config.py
<vEOS-1> EXEC /bin/sh -c 'chmod u+x /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-187657461983830/ /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-187657461983830/napalm_install_config.py && sleep 0'
<vEOS-2> PUT /var/folders/kl/wxylqm7s0ybf9c8w9z51bdkm0000gn/T/tmp0fjhAT TO /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-154559222962668/napalm_install_config.py
<vEOS-2> EXEC /bin/sh -c 'chmod u+x /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-154559222962668/ /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-154559222962668/napalm_install_config.py && sleep 0'
<vEOS-1> EXEC /bin/sh -c '/usr/bin/python /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-187657461983830/napalm_install_config.py; rm -rf "/Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-187657461983830/" > /dev/null 2>&1 && sleep 0'
<vEOS-2> EXEC /bin/sh -c '/usr/bin/python /Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-154559222962668/napalm_install_config.py; rm -rf "/Users/ccompain/.ansible/tmp/ansible-tmp-1498484651.53-154559222962668/" > /dev/null 2>&1 && sleep 0'
fatal: [vEOS-1]: FAILED! => {
    "changed": false,
    "failed": true,
    "invocation": {
        "module_args": {
            "archive_file": null,
            "commit_changes": true,
            "config": null,
            "config_file": "configs/vEOS-1.conf",
            "dev_os": "eos",
            "diff_file": null,
            "get_diffs": false,
            "hostname": "vEOS-1",
            "optional_args": null,
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "provider": null,
            "replace_config": false,
            "timeout": 60,
            "username": "ccompain"
        }
    },
    "msg": "the python module napalm is required"
}
fatal: [vEOS-2]: FAILED! => {
    "changed": false,
    "failed": true,
    "invocation": {
        "module_args": {
            "archive_file": null,
            "commit_changes": true,
            "config": null,
            "config_file": "configs/vEOS-2.conf",
            "dev_os": "eos",
            "diff_file": null,
            "get_diffs": false,
            "hostname": "vEOS-2",
            "optional_args": null,
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "provider": null,
            "replace_config": false,
            "timeout": 60,
            "username": "ccompain"
        }
    },
    "msg": "the python module napalm is required"
}
...

Any idea for solving this ?

Thanks in advance,

Christophe

PS :

sudo apt-get install python-pip python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev
sudo -H pip install napalm

napalm_install_config.py shouldn't hide errors

It's good we are catching errors to fail with the fail_json method but we shouldn't hide the errors as it frustrates uses. We should do something like:

try:
   whatever
except Exception as e:
  module.fail_json(e.msg)

Example using inventory_hostname

Hello guys,

When we follow examples (http://napalm.readthedocs.io/en/latest/tutorials/ansible-napalm.html or https://github.com/napalm-automation/napalm-ansible#examples), the hostname is using the {{ inventory_hostname }}

But a classic host file is :

[cisco]
spine1 ansible_host=10.0.100.101
spine2 ansible_host=10.0.100.102
leaf1 ansible_host=10.0.100.103
leaf2 ansible_host=10.0.100.104
leaf3 ansible_host=10.0.100.105
leaf4 ansible_host=10.0.100.106

[cisco:vars]
ansible_python_interpreter= "/usr/bin/env python"

Here's the playbook example :

- name: Test Inventory #The Task Name
  hosts: cisco         #This will be in your ansible inventory file
  connection: local    #Required
  gather_facts: no     #Do not gather facts

  tasks:                                     #Begin Tasks
    - name: get facts from device            #Task Name
      napalm_get_facts:                      #Call the napalm module, in this case napal_get_facts
        hostname: "{{ inventory_hostname }}" #This is a parameter and is derived from your ansible inventory file
        username: 'admin'                     #The username to ssh with
        dev_os: 'nxos_ssh'                        #The hardware operating system
        password: 'xxxx'                 #The line level password
        filter: 'facts'                      #The list of items you want to retrieve. The filter keyword is _inclusive_ of what you want
      register: result                       #Ansible function for collecting output

    - name: print results                    #Task Name
      debug: msg="{{ result }}"              #Display the collected output

The playbook does not work

(flacommare_venv2.7) florian@ubuntuvm:~/ansible$ ansible-playbook playbooks/get_facts.yaml -i inventory/host.ini

PLAY [Test Inventory] ************************************************************************************************************************************************************************************************************************

TASK [get facts from device] *****************************************************************************************************************************************************************************************************************
fatal: [leaf2]: FAILED! => {"changed": false, "msg": "cannot connect to device: Cannot connect to leaf2"}
fatal: [leaf1]: FAILED! => {"changed": false, "msg": "cannot connect to device: Cannot connect to leaf1"}
fatal: [leaf3]: FAILED! => {"changed": false, "msg": "cannot connect to device: Cannot connect to leaf3"}
fatal: [spine2]: FAILED! => {"changed": false, "msg": "cannot connect to device: Cannot connect to spine2"}
fatal: [spine1]: FAILED! => {"changed": false, "msg": "cannot connect to device: Cannot connect to spine1"}
fatal: [leaf4]: FAILED! => {"changed": false, "msg": "cannot connect to device: Cannot connect to leaf4"}
	to retry, use: --limit @/home/florian/ansible/playbooks/get_facts.retry

PLAY RECAP ***********************************************************************************************************************************************************************************************************************************
leaf1                      : ok=0    changed=0    unreachable=0    failed=1
leaf2                      : ok=0    changed=0    unreachable=0    failed=1
leaf3                      : ok=0    changed=0    unreachable=0    failed=1
leaf4                      : ok=0    changed=0    unreachable=0    failed=1
spine1                     : ok=0    changed=0    unreachable=0    failed=1
spine2                     : ok=0    changed=0    unreachable=0    failed=1

Indeed, the module code set the local params to override provider. (

# allow local params to override provider
)
So my IP is not use but the "name" of the equipment, which is not a dns entry.

Is there some mistake in my inventory file ? Or maybe the example have a mistake and should use {{ ansible_host }} instead ?

Can't install via ansible-galaxy because meta/main.yml is missing

Steps to reproduce:

Place the following in requirements.yml:

---
- src: https://github.com/napalm-automation/napalm-ansible/
  version: master
  name: napalm
  path: roles

then run ansible-galaxy install -r requirements.yml --force

Actual results:
[WARNING]: - napalm was NOT installed successfully: this role does not appear to have a meta/main.yml file.

This file used to exist in napalm: it was added in 4b06902

And was removed again in dc34cfc 6 days ago.

napalm-ansible need to add separate develop branch

Also need to add a set of unit tests to it.

Depending on the frequency of changes we also might to periodically make a release to that people can use a specific version of napalm-ansible without it being changed.

Eliminate os_choices option from napalm-ansible

From #94:

Allow nxos_ssh to be used

Is there a reason not to rely on napalm get_network_driver and getter exceptions to filter which driver can or can't be used ?
It seems cumbersome to track the drivers for any addition in the implementation when their is already something we can accurately rely on to do so.

Configure replace error message didn't show up in Ansible

Loading and merging the candidate config works using napalm cli:

Enter password: 
2017-10-17 11:53:32,031 - napalm - DEBUG - Starting napalm's debugging tool
2017-10-17 11:53:32,032 - napalm - DEBUG - Gathering napalm packages
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-base==0.25.0
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-eos==0.6.1
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-fortios==0.4.0
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-ios==0.6.2
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-iosxr==0.5.5
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-junos==0.12.0
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-nxos==0.7.0
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-panos==0.4.0
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-pluribus==0.5.1
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-ros==0.2.2
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm-vyos==0.1.3
2017-10-17 11:53:32,037 - napalm - DEBUG - napalm==1.2.0
2017-10-17 11:53:32,037 - napalm - DEBUG - get_network_driver - Calling with args: ('ios',), {}
2017-10-17 11:53:32,115 - napalm - DEBUG - get_network_driver - Successful
2017-10-17 11:53:32,115 - napalm - DEBUG - __init__ - Calling with args: (<class 'napalm_ios.ios.IOSDriver'>, 'test-2960.domain', 'ansible'), {'password': u'*******', 'optional_args': {}, 'timeout': 60}
2017-10-17 11:53:32,115 - napalm - DEBUG - __init__ - Successful
2017-10-17 11:53:32,115 - napalm - DEBUG - pre_connection_tests - Calling with args: (<napalm_ios.ios.IOSDriver object at 0x24f7810>,), {}
2017-10-17 11:53:32,115 - napalm - DEBUG - open - Calling with args: (<napalm_ios.ios.IOSDriver object at 0x24f7810>,), {}
2017-10-17 11:53:39,137 - napalm - DEBUG - open - Successful
2017-10-17 11:53:39,137 - napalm - DEBUG - connection_tests - Calling with args: (<napalm_ios.ios.IOSDriver object at 0x24f7810>,), {}
2017-10-17 11:53:39,137 - napalm - DEBUG - get_facts - Calling with args: (<napalm_ios.ios.IOSDriver object at 0x24f7810>,), {}
2017-10-17 11:53:40,943 - napalm - DEBUG - Gathered facts:
{
    "os_version": "C2960 Software (C2960-LANBASEK9-M), Version 15.0(2)SE11, RELEASE SOFTWARE (fc3)", 
    "uptime": 960, 
    "interface_list": [
        "Vlan1", 
        "Vlan3", 
        "FastEthernet0/1", 
        "FastEthernet0/2", 
        "FastEthernet0/3", 
        "FastEthernet0/4", 
        "FastEthernet0/5", 
        "FastEthernet0/6", 
        "FastEthernet0/7", 
        "FastEthernet0/8", 
        "FastEthernet0/9", 
        "FastEthernet0/10", 
        "FastEthernet0/11", 
        "FastEthernet0/12", 
        "FastEthernet0/13", 
        "FastEthernet0/14", 
        "FastEthernet0/15", 
        "FastEthernet0/16", 
        "FastEthernet0/17", 
        "FastEthernet0/18", 
        "FastEthernet0/19", 
        "FastEthernet0/20", 
        "FastEthernet0/21", 
        "FastEthernet0/22", 
        "FastEthernet0/23", 
        "FastEthernet0/24", 
        "FastEthernet0/25", 
        "FastEthernet0/26", 
        "FastEthernet0/27", 
        "FastEthernet0/28", 
        "FastEthernet0/29", 
        "FastEthernet0/30", 
        "FastEthernet0/31", 
        "FastEthernet0/32", 
        "FastEthernet0/33", 
        "FastEthernet0/34", 
        "FastEthernet0/35", 
        "FastEthernet0/36", 
        "FastEthernet0/37", 
        "FastEthernet0/38", 
        "FastEthernet0/39", 
        "FastEthernet0/40", 
        "FastEthernet0/41", 
        "FastEthernet0/42", 
        "FastEthernet0/43", 
        "FastEthernet0/44", 
        "FastEthernet0/45", 
        "FastEthernet0/46", 
        "FastEthernet0/47", 
        "FastEthernet0/48", 
        "GigabitEthernet0/1", 
        "GigabitEthernet0/2"
    ], 
    "vendor": "Cisco", 
    "serial_number": "FOC1247X10Z", 
    "model": "WS-C2960-48TT-L", 
    "hostname": "test-2960", 
    "fqdn": "test-2960.domain"
}
{
    "os_version": "C2960 Software (C2960-LANBASEK9-M), Version 15.0(2)SE11, RELEASE SOFTWARE (fc3)", 
    "uptime": 960, 
    "interface_list": [
        "Vlan1", 
        "Vlan3", 
        "FastEthernet0/1", 
        "FastEthernet0/2", 
        "FastEthernet0/3", 
        "FastEthernet0/4", 
        "FastEthernet0/5", 
        "FastEthernet0/6", 
        "FastEthernet0/7", 
        "FastEthernet0/8", 
        "FastEthernet0/9", 
        "FastEthernet0/10", 
        "FastEthernet0/11", 
        "FastEthernet0/12", 
        "FastEthernet0/13", 
        "FastEthernet0/14", 
        "FastEthernet0/15", 
        "FastEthernet0/16", 
        "FastEthernet0/17", 
        "FastEthernet0/18", 
        "FastEthernet0/19", 
        "FastEthernet0/20", 
        "FastEthernet0/21", 
        "FastEthernet0/22", 
        "FastEthernet0/23", 
        "FastEthernet0/24", 
        "FastEthernet0/25", 
        "FastEthernet0/26", 
        "FastEthernet0/27", 
        "FastEthernet0/28", 
        "FastEthernet0/29", 
        "FastEthernet0/30", 
        "FastEthernet0/31", 
        "FastEthernet0/32", 
        "FastEthernet0/33", 
        "FastEthernet0/34", 
        "FastEthernet0/35", 
        "FastEthernet0/36", 
        "FastEthernet0/37", 
        "FastEthernet0/38", 
        "FastEthernet0/39", 
        "FastEthernet0/40", 
        "FastEthernet0/41", 
        "FastEthernet0/42", 
        "FastEthernet0/43", 
        "FastEthernet0/44", 
        "FastEthernet0/45", 
        "FastEthernet0/46", 
        "FastEthernet0/47", 
        "FastEthernet0/48", 
        "GigabitEthernet0/1", 
        "GigabitEthernet0/2"
    ], 
    "vendor": "Cisco", 
    "serial_number": "FOC1247X10Z", 
    "model": "WS-C2960-48TT-L", 
    "hostname": "test-2960", 
    "fqdn": "test-2960.domain"
}
2017-10-17 11:53:40,943 - napalm - DEBUG - get_facts - Successful
2017-10-17 11:53:40,943 - napalm - DEBUG - load_merge_candidate - Calling with args: (<napalm_ios.ios.IOSDriver object at 0x24f7810>,), {'filename': '.compiled/test-2960/candidate_config.conf'}
2017-10-17 11:53:50,624 - napalm - DEBUG - load_merge_candidate - Successful
2017-10-17 11:53:50,625 - napalm - DEBUG - compare_config - Calling with args: (<napalm_ios.ios.IOSDriver object at 0x24f7810>,), {}
2017-10-17 11:53:54,230 - napalm - DEBUG - Gathered diff:
+snmp-server location test
+banner motd ^C[3] test^C
2017-10-17 11:53:54,231 - napalm - DEBUG - compare_config - Successful
2017-10-17 11:53:54,231 - napalm - DEBUG - commit_config - Calling with args: (<napalm_ios.ios.IOSDriver object at 0x24f7810>,), {}
2017-10-17 11:54:17,827 - napalm - DEBUG - commit_config - Successful
2017-10-17 11:54:17,827 - napalm - DEBUG - close - Calling with args: (<napalm_ios.ios.IOSDriver object at 0x24f7810>,), {}
2017-10-17 11:54:17,842 - napalm - DEBUG - close - Successful
2017-10-17 11:54:17,842 - napalm - DEBUG - post_connection_tests - Calling with args: (<napalm_ios.ios.IOSDriver object at 0x24f7810>,), {}

This same operation called in a playbook as follows fails:

Playbook:

---
- name: Check switch config using templates and yaml source config.
  hosts: all
  connection: local
  gather_facts: no
  roles:
    - access_switch
    - cisco-ios-passwords
  vars:
    host_dir: ".compiled/{{ inventory_hostname_short }}"
    commit_changes: False

  pre_tasks:
    - name: Remove diffs from previous runs
      file:
        path: "{{ host_dir }}"
        state: absent
      changed_when: False
      check_mode: no
    - name: Create folder to store configurations and diffs for/from the devices
      file:
        path: "{{ host_dir }}"
        state: directory
      changed_when: False
      check_mode: no

  tasks:
    - name: Create configuration from jinja2 templates
      template:
        src: "templates/{{ os }}/{{ switch_model }}/global.j2"
        dest: "{{ host_dir }}/candidate_config.conf"
        backup: yes
      changed_when: False
      check_mode: no
    - name: Load configuration into the device and diff
      napalm_install_config:
        hostname: "{{ inventory_hostname }}"
        username: "{{ user }}"
        dev_os: "{{ os }}"
        password: "{{ password }}"
        config_file: "{{ host_dir }}/candidate_config.conf"
        commit_changes: "{{ commit_changes }}"
        replace_config: True
        get_diffs: True
        diff_file: "{{ host_dir }}/diff"
      tags: [print_action]

Example running:

(venv)[oli@ansible-d0 ansible]$ ansible-playbook -i hosts-testing --vault-password-file .vault_pass.txt check_switch_config.yml -vv --extra-vars "commit_changes=True"
ansible-playbook 2.4.0.0
  config file = /home/oli/git/ansible/ansible.cfg
  configured module search path = [u'/home/oli/git/ansible/library', u'/home/oli/git/napalm-ansible/library']
  ansible python module location = /home/oli/git/venv/lib/python2.7/site-packages/ansible
  executable location = /home/oli/git/venv/bin/ansible-playbook
  python version = 2.7.5 (default, Aug  4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
Using /home/oli/git/ansible/ansible.cfg as config file

PLAYBOOK: check_switch_config.yml ******************************************************************************************************
1 plays in check_switch_config.yml

PLAY [Check switch config using templates and yaml source config.] *********************************************************************

TASK [Remove diffs from previous runs] *************************************************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:14
ok: [test-2960.domain] => {"changed": false, "failed": false, "path": ".compiled/test-2960", "state": "absent"}

TASK [Create folder to store configurations and diffs for/from the devices] ************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:20
ok: [test-2960.domain] => {"changed": false, "failed": false, "gid": 4640, "group": "isys", "mode": "0755", "owner": "oli", "path": ".compiled/test-2960", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 4096, "state": "directory", "uid": 20577}
META: ran handlers

TASK [Create configuration from jinja2 templates] **************************************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:28
ok: [test-2960.domain] => {"changed": false, "checksum": "f5d67562daf3c2732611d6a7048194afc365ca9e", "dest": ".compiled/test-2960/candidate_config.conf", "failed": false, "gid": 4640, "group": "isys", "md5sum": "c5f72646e9c1fe6afb34353e6141aa8e", "mode": "0644", "owner": "oli", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 18345, "src": "/home/oli/.ansible/tmp/ansible-tmp-1508237886.24-90961176918724/source", "state": "file", "uid": 20577}

TASK [Load configuration into the device and diff] *************************************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:35
changed: [test-2960.domain] => {"changed": true, "failed": false, "msg": "-vstack"}

TASK [Print options] *******************************************************************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:47
ok: [test-2960.domain] => {
    "msg": "Commit Changes True"
}
META: ran handlers
META: ran handlers

PLAY RECAP *****************************************************************************************************************************
test-2960.domain  : ok=5    changed=1    unreachable=0    failed=0   

(venv)[oli@ansible-d0 ansible]$ ansible-playbook -i hosts-testing --vault-password-file .vault_pass.txt check_switch_config.yml -vvvv --extra-vars "commit_changes=True"
ansible-playbook 2.4.0.0
  config file = /home/oli/git/ansible/ansible.cfg
  configured module search path = [u'/home/oli/git/ansible/library', u'/home/oli/git/napalm-ansible/library']
  ansible python module location = /home/oli/git/venv/lib/python2.7/site-packages/ansible
  executable location = /home/oli/git/venv/bin/ansible-playbook
  python version = 2.7.5 (default, Aug  4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
Using /home/oli/git/ansible/ansible.cfg as config file
setting up inventory plugins
Parsed /home/oli/git/ansible/hosts-testing inventory source with ini plugin
Trying secret FileVaultSecret(filename='/home/oli/git/ansible/.vault_pass.txt') for vault_id=default
Loading callback plugin default of type stdout, v2.0 from /home/oli/git/venv/lib/python2.7/site-packages/ansible/plugins/callback/__init__.pyc

PLAYBOOK: check_switch_config.yml ******************************************************************************************************
1 plays in check_switch_config.yml

PLAY [Check switch config using templates and yaml source config.] *********************************************************************

TASK [Remove diffs from previous runs] *************************************************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:14
Using module file /home/oli/git/venv/lib/python2.7/site-packages/ansible/modules/files/file.py
<test-2960.domain> ESTABLISH LOCAL CONNECTION FOR USER: oli
<test-2960.domain> EXEC /bin/sh -c 'echo ~ && sleep 0'
<test-2960.domain> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/oli/.ansible/tmp/ansible-tmp-1508237947.07-281025839920352 `" && echo ansible-tmp-1508237947.07-281025839920352="` echo /home/oli/.ansible/tmp/ansible-tmp-1508237947.07-281025839920352 `" ) && sleep 0'
<test-2960.domain> PUT /tmp/tmpyMNq_R TO /home/oli/.ansible/tmp/ansible-tmp-1508237947.07-281025839920352/file.py
<test-2960.domain> EXEC /bin/sh -c 'chmod u+x /home/oli/.ansible/tmp/ansible-tmp-1508237947.07-281025839920352/ /home/oli/.ansible/tmp/ansible-tmp-1508237947.07-281025839920352/file.py && sleep 0'
<test-2960.domain> EXEC /bin/sh -c '../venv/bin/python /home/oli/.ansible/tmp/ansible-tmp-1508237947.07-281025839920352/file.py; rm -rf "/home/oli/.ansible/tmp/ansible-tmp-1508237947.07-281025839920352/" > /dev/null 2>&1 && sleep 0'
ok: [test-2960.domain] => {
    "changed": false, 
    "diff": {
        "after": {
            "path": ".compiled/test-2960", 
            "state": "absent"
        }, 
        "before": {
            "path": ".compiled/test-2960", 
            "state": "directory"
        }
    }, 
    "failed": false, 
    "invocation": {
        "module_args": {
            "attributes": null, 
            "backup": null, 
            "content": null, 
            "delimiter": null, 
            "diff_peek": null, 
            "directory_mode": null, 
            "follow": false, 
            "force": false, 
            "group": null, 
            "mode": null, 
            "original_basename": null, 
            "owner": null, 
            "path": ".compiled/test-2960", 
            "recurse": false, 
            "regexp": null, 
            "remote_src": null, 
            "selevel": null, 
            "serole": null, 
            "setype": null, 
            "seuser": null, 
            "src": null, 
            "state": "absent", 
            "unsafe_writes": null, 
            "validate": null
        }
    }, 
    "path": ".compiled/test-2960", 
    "state": "absent"
}

TASK [Create folder to store configurations and diffs for/from the devices] ************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:20
Using module file /home/oli/git/venv/lib/python2.7/site-packages/ansible/modules/files/file.py
<test-2960.domain> ESTABLISH LOCAL CONNECTION FOR USER: oli
<test-2960.domain> EXEC /bin/sh -c 'echo ~ && sleep 0'
<test-2960.domain> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/oli/.ansible/tmp/ansible-tmp-1508237947.21-9039621643107 `" && echo ansible-tmp-1508237947.21-9039621643107="` echo /home/oli/.ansible/tmp/ansible-tmp-1508237947.21-9039621643107 `" ) && sleep 0'
<test-2960.domain> PUT /tmp/tmpPP9xkq TO /home/oli/.ansible/tmp/ansible-tmp-1508237947.21-9039621643107/file.py
<test-2960.domain> EXEC /bin/sh -c 'chmod u+x /home/oli/.ansible/tmp/ansible-tmp-1508237947.21-9039621643107/ /home/oli/.ansible/tmp/ansible-tmp-1508237947.21-9039621643107/file.py && sleep 0'
<test-2960.domain> EXEC /bin/sh -c '../venv/bin/python /home/oli/.ansible/tmp/ansible-tmp-1508237947.21-9039621643107/file.py; rm -rf "/home/oli/.ansible/tmp/ansible-tmp-1508237947.21-9039621643107/" > /dev/null 2>&1 && sleep 0'
ok: [test-2960.domain] => {
    "changed": false, 
    "diff": {
        "after": {
            "path": ".compiled/test-2960", 
            "state": "directory"
        }, 
        "before": {
            "path": ".compiled/test-2960", 
            "state": "absent"
        }
    }, 
    "failed": false, 
    "gid": 4640, 
    "group": "isys", 
    "invocation": {
        "module_args": {
            "attributes": null, 
            "backup": null, 
            "content": null, 
            "delimiter": null, 
            "diff_peek": null, 
            "directory_mode": null, 
            "follow": false, 
            "force": false, 
            "group": null, 
            "mode": null, 
            "original_basename": null, 
            "owner": null, 
            "path": ".compiled/test-2960", 
            "recurse": false, 
            "regexp": null, 
            "remote_src": null, 
            "selevel": null, 
            "serole": null, 
            "setype": null, 
            "seuser": null, 
            "src": null, 
            "state": "directory", 
            "unsafe_writes": null, 
            "validate": null
        }
    }, 
    "mode": "0755", 
    "owner": "oli", 
    "path": ".compiled/test-2960", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 4096, 
    "state": "directory", 
    "uid": 20577
}
META: ran handlers

TASK [Create configuration from jinja2 templates] **************************************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:28
<test-2960.domain> ESTABLISH LOCAL CONNECTION FOR USER: oli
<test-2960.domain> EXEC /bin/sh -c 'echo ~ && sleep 0'
<test-2960.domain> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405 `" && echo ansible-tmp-1508237947.4-265171165173405="` echo /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405 `" ) && sleep 0'
Using module file /home/oli/git/venv/lib/python2.7/site-packages/ansible/modules/files/stat.py
<test-2960.domain> PUT /tmp/tmpRIj53I TO /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/stat.py
<test-2960.domain> EXEC /bin/sh -c 'chmod u+x /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/ /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/stat.py && sleep 0'
<test-2960.domain> EXEC /bin/sh -c '../venv/bin/python /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/stat.py && sleep 0'
<test-2960.domain> PUT /tmp/tmp84AWcf/global.j2 TO /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/source
<test-2960.domain> EXEC /bin/sh -c 'chmod u+x /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/ /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/source && sleep 0'
Using module file /home/oli/git/venv/lib/python2.7/site-packages/ansible/modules/files/copy.py
<test-2960.domain> PUT /tmp/tmp4jxjGR TO /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/copy.py
<test-2960.domain> EXEC /bin/sh -c 'chmod u+x /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/ /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/copy.py && sleep 0'
<test-2960.domain> EXEC /bin/sh -c '../venv/bin/python /home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/copy.py; rm -rf "/home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/" > /dev/null 2>&1 && sleep 0'
ok: [test-2960.domain] => {
    "changed": false, 
    "checksum": "f5d67562daf3c2732611d6a7048194afc365ca9e", 
    "dest": ".compiled/test-2960/candidate_config.conf", 
    "diff": [], 
    "failed": false, 
    "gid": 4640, 
    "group": "isys", 
    "invocation": {
        "module_args": {
            "attributes": null, 
            "backup": true, 
            "content": null, 
            "delimiter": null, 
            "dest": ".compiled/test-2960/candidate_config.conf", 
            "directory_mode": null, 
            "follow": false, 
            "force": true, 
            "group": null, 
            "local_follow": null, 
            "mode": null, 
            "original_basename": "global.j2", 
            "owner": null, 
            "regexp": null, 
            "remote_src": null, 
            "selevel": null, 
            "serole": null, 
            "setype": null, 
            "seuser": null, 
            "src": "/home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/source", 
            "unsafe_writes": null, 
            "validate": null
        }
    }, 
    "md5sum": "c5f72646e9c1fe6afb34353e6141aa8e", 
    "mode": "0644", 
    "owner": "oli", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 18345, 
    "src": "/home/oli/.ansible/tmp/ansible-tmp-1508237947.4-265171165173405/source", 
    "state": "file", 
    "uid": 20577
}

TASK [Load configuration into the device and diff] *************************************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:35
Using module file /home/oli/git/napalm-ansible/napalm_ansible/napalm_install_config.py
<test-2960.domain> ESTABLISH LOCAL CONNECTION FOR USER: oli
<test-2960.domain> EXEC /bin/sh -c 'echo ~ && sleep 0'
<test-2960.domain> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/oli/.ansible/tmp/ansible-tmp-1508237948.03-127512870432814 `" && echo ansible-tmp-1508237948.03-127512870432814="` echo /home/oli/.ansible/tmp/ansible-tmp-1508237948.03-127512870432814 `" ) && sleep 0'
<test-2960.domain> PUT /tmp/tmpSj225O TO /home/oli/.ansible/tmp/ansible-tmp-1508237948.03-127512870432814/napalm_install_config.py
<test-2960.domain> EXEC /bin/sh -c 'chmod u+x /home/oli/.ansible/tmp/ansible-tmp-1508237948.03-127512870432814/ /home/oli/.ansible/tmp/ansible-tmp-1508237948.03-127512870432814/napalm_install_config.py && sleep 0'
<test-2960.domain> EXEC /bin/sh -c '../venv/bin/python /home/oli/.ansible/tmp/ansible-tmp-1508237948.03-127512870432814/napalm_install_config.py; rm -rf "/home/oli/.ansible/tmp/ansible-tmp-1508237948.03-127512870432814/" > /dev/null 2>&1 && sleep 0'
changed: [test-2960.domain] => {
    "changed": true, 
    "failed": false, 
    "invocation": {
        "module_args": {
            "archive_file": null, 
            "commit_changes": true, 
            "config": null, 
            "config_file": ".compiled/test-2960/candidate_config.conf", 
            "dev_os": "ios", 
            "diff_file": ".compiled/test-2960/diff", 
            "get_diffs": true, 
            "hostname": "test-2960.domain", 
            "optional_args": null, 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "provider": null, 
            "replace_config": true, 
            "timeout": 60, 
            "username": "ansible"
        }
    }, 
    "msg": "+snmp-server location test\n+banner motd ^C[3] test^C\n-snmp-server location Commsdev\n-vstack\n-banner motd ^C[3] Commsdev^C"
}

TASK [Print options] *******************************************************************************************************************
task path: /home/oli/git/ansible/check_switch_config.yml:47
ok: [test-2960.domain] => {
    "msg": "Commit Changes True"
}
META: ran handlers
META: ran handlers

PLAY RECAP *****************************************************************************************************************************
test-2960.domain  : ok=5    changed=1    unreachable=0    failed=0   

Switch logs during the playbook run:

Oct 17 11:58:31 BST: %SYS-5-CONFIG_I: Configured from console by ansible on vty0 (1.2.3.4)
Oct 17 11:59:25 BST: %SYS-5-CONFIG_I: Configured from console by ansible on vty0 (1.2.3.4)
Oct 17 11:59:32 BST: %SYS-5-CONFIG_I: Configured from console by ansible on vty0 (1.2.3.4)

Files on switch after this run match the files generated by the ansible tasks.

ansible==2.4.0.0
ansible-lint==3.4.15
asn1crypto==0.23.0
backports.ssl-match-hostname==3.5.0.1
bcrypt==3.1.4
bigsuds==1.0.4
certifi==2017.7.27.1
cffi==1.11.2
chainmap==1.0.2
chardet==3.0.4
ciscoconfparse==1.2.47
colorama==0.3.9
cryptography==2.0.3
dnspython==1.15.0
docker-py==1.10.6
docker-pycreds==0.2.1
enum34==1.1.6
future==0.16.0
idna==2.6
ipaddr==2.2.0
ipaddress==1.0.18
Jinja2==2.9.6
jmespath==0.9.2
jtextfsm==0.3.1
junos-eznc==2.1.7
librouteros==1.0.4
lxml==4.1.0
MarkupSafe==1.0
napalm==1.2.0
napalm-base==0.25.0
napalm-eos==0.6.1
napalm-fortios==0.4.0
napalm-ios==0.6.2
napalm-iosxr==0.5.5
napalm-junos==0.12.0
napalm-nxos==0.7.0
napalm-panos==0.4.0
napalm-pluribus==0.5.1
napalm-ros==0.2.2
napalm-vyos==0.1.3
ncclient==0.5.3
netaddr==0.7.19
netmiko==1.4.3
pan-python==0.12.0
paramiko==2.3.1
pathspec==0.5.5
pyasn1==0.3.7
pycparser==2.18
pycrypto==2.6.1
pyeapi==0.8.1
pyfg==0.50
pyIOSXR==0.52
PyNaCl==1.1.2
pynxos==0.0.3
pyPluribus==0.3.1
pyserial==3.4
PyYAML==3.12
requests==2.18.4
requests-toolbelt==0.8.0
scp==0.10.2
six==1.11.0
suds==0.4
urllib3==1.22
VyattaConfParser==0.5.1
websocket-client==0.44.0
xmltodict==0.11.0
yamllint==1.8.1

Instances of '30' in IOS interface description replaced with eight asterisks with napalm_get_facts, interfaces

Example output:
ansible_facts:
interfaces:
GigabitEthernet0/0:
description: '>ACC Ethernet AT&T ********M EDBK6CHS0009< [********000:********000]'
is_enabled: true
is_up: true
last_flapped: -1.0
mac_address: 24:E9:B3:2E:36:E1
speed: 100
changed: false
not_implemented: []

Should read:
description: '>ACC Ethernet AT&T 30M EDBK6CHS0009< [30000:30000]'

Versions in use:
ansible 2.3.1.0
napalm (1.2.0)
napalm-base (0.24.1)
napalm-eos (0.6.0)
napalm-fortios (0.4.0)
napalm-ios (0.7.0)
napalm-iosxr (0.5.1)
napalm-junos (0.10.3)
napalm-nxos (0.5.3)
napalm-panos (0.4.0)
napalm-pluribus (0.5.1)
napalm-ros (0.2.2)
napalm-vyos (0.1.3)
netmiko (1.4.1)

Support enable mode and passing in of secret argument

I'm also having this problem when calling napalm via Ansible. Had to set privilege mode of user to 15 in order to get this module to work.

Also have to delete the secret variable as it breaks the playbook.

Netmiko version is 1.1.0.

Here is my playbook.yml
` - name: Push config to switch group.
hosts: test
connection: local
gather_facts: no

tasks:

  • name: get facts from device
    napalm_get_facts:
    hostname={{ inventory_hostname }}
    username=theusername
    dev_os=ios
    password=thepassword
    secret=thesecret
    filter='interfaces'
    register: result

  • debug: msg= "{{ result.stdout }}"`

napalm-ansible should not fail the module on a failed connection close

https://github.com/napalm-automation/napalm-ansible/blob/develop/napalm_ansible/napalm_get_facts.py#L270

This should be a pass.

There are timing issues here on SSH connection closures:

<sw01> EXEC /bin/sh -c '/usr/bin/python /home/austindcc/.ansible/tmp/ansible-tmp-1510176054.16-142312610962492/napalm_get_facts.py; rm -rf "/home/austindcc/.ansible/tmp/ansible-tmp-1510176054.16-142312610962492/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
  File "/tmp/ansible_onTZIK/ansible_module_napalm_get_facts.py", line 260, in main
    device.close()
  File "/usr/local/lib/python2.7/dist-packages/napalm_ios/ios.py", line 154, in close
    self.device.disconnect()
  File "/usr/local/lib/python2.7/dist-packages/netmiko/base_connection.py", line 1053, in disconnect
    self.remote_conn_pre.close()
  File "/usr/local/lib/python2.7/dist-packages/paramiko/client.py", line 395, in close
    self._transport.close()
  File "/usr/local/lib/python2.7/dist-packages/paramiko/transport.py", line 658, in close
    self.sock.close()
  File "/usr/local/lib/python2.7/dist-packages/paramiko/proxy.py", line 110, in close
    os.kill(self.process.pid, signal.SIGTERM)

setup.x in repo

There is a long outstanding issue with ansible with setup.x files and gather_facts. If gather_facts: is not set for False, it will search for any file with setup.x in it under the ansible.cfg library path. This ends up failing for playbooks as there is a name space issue.

In ntc-modules there has been confusion and issues because of this, and I would recommend to remove the setup.py and setup.cfg file.

See ansible/ansible#20702 and ansible/ansible#20717 for further details.

install config change failed

I have attached the playbook and the config file that is trying to be updated.
fortigate.config-update.yml.txt

new.conf.txt

This is the output that I am getting from the playbook:

ansible-tst:~/fortigate# ansible-playbook fortigate.config-update.yml

PLAY [firewall] ***************************************************************

GATHERING FACTS ***************************************************************
ok: [homefw]

TASK: [update config] *********************************************************
failed: [homefw] => {"failed": true}
msg: cannot install config:

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
to retry, use: --limit @/root/fortigate.config-update.retry

homefw : ok=1 changed=0 unreachable=0 failed=1


Many Thanks!

napalm_ping RETURN VALUES

Documentation says that when echo request fails there should be a response like this:
results:

    # when echo request fails
    {"error": "connect: Network is unreachable"}

I've tested this and get response like this:

"results": {
    "success": {
       "packet_loss": -2,
       "probes_sent": 0,
       "results": [],
       "rtt_avg": 0.0,
       "rtt_max": 0.0,
       "rtt_min": 0.0,
       "rtt_stddev": 0.0
        }

My task:

  - name: Ping hosts
      napalm_ping:
        destination: "{{ item }}"
        dev_os: ios
        count: "{{ ping_count | default(2) }}"
        provider: "{{ provider }}"
        optional_args: {'port': "{{ port | default(22) }}"}
      with_items: "{{ pinghosts }}"

Error to use get_facts in XRV

I am having some problem using napalm_get_facts with filter facts under specific plataform related Cisco.

The issue was found under Cisco XRV IOS version 6.1.2.

When executed this feature I received a message related below:

fatal: [XRV]: FAILED! => {"changed": false, "msg": "[facts] cannot retrieve device data: list index out of range"}

If you executed another filters identified we can get correct output like interfaces, arp_table, interfaces_ip.

napalm_install_config module: Privilege Denied w/enable Optl Argu

When trying to use the enable optional argument, this is what I get. However, if I go to the switch and set my privilege to 15 I do not get this error and my command is playbook is accepted. Does anybody have insight on to what I may be running into?

fatal: [SW-AS-07]: FAILED! => {"changed": false, "failed": true, "invocation": {"module_args": {"commit_changes": true, "config_file": "test/running.conf", "dev_os": "ios", "diff_file": "test/initial.diff", "get_diffs": true, "hostname": "SW-AS-07", "optional_args": {"secret": "thesecret"}, "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "replace_config": false, "timeout": 60, "username": "theuser"}, "module_name": "napalm_install_config"}, "msg": "cannot load config: Privilege denied.\n"}
---
- name: Change hostname test
  hosts: SW-AS
  connection: local
  tasks:

    - assemble:
        src: test/
        dest: test/running.conf

    - napalm_install_config:
        hostname: "{{ inventory_hostname }}"
        username: theuser
        password: thepassword
        optional_args: {'secret': 'thesecret'}
        dev_os: ios
        config_file: test/running.conf
        commit_changes: True
        replace_config: False
        get_diffs: True
        diff_file: test/initial.diff
      register: result

    - name: Debugging
      debug: var=result

running.conf

hostname test

move as much code to napalm itself

The plugin system of ansible is terrible, that's why ansible itself doesn't use it and their modules are basically intertwined in the codebase. The reason I am saying this is because it encourages duplicating a lot of code. For this reason, I was wondering if we should move as much code as possible to napalm itself so we don't have to repeat the same code over and over.

@ktbyers thoughts?

Error using napalm_parse_yang

Hi all, thanks for this awesome piece of code. I'm trying to figure out how to use napalm_parse_yang module within napalm-ansible. napalm_get_facts is working well showing that connectivity is OK with inventory_hosts.

I took your example and change dev_os and profile to "junos" value.

However when running the playbook I have this error:

fatal: [gn4-net-core-1]: FAILED! => {"changed": false, "module_stderr": "No handlers could be found for logger \"napalm-yang\"
Traceback (most recent call last):
  File \"/tmp/ansible_OGsNB8/ansible_module_napalm_parse_yang.py\", line 297, in <module>
    main()
  File \"/tmp/ansible_OGsNB8/ansible_module_napalm_parse_yang.py\", line 291, in main
    yang_model = parse_from_device(module, os_choices)
  File \"/tmp/ansible_OGsNB8/ansible_module_napalm_parse_yang.py\", line 247, in parse_from_device
    root.parse_config(device=device, profile=profiles or device.profile)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/base.py\", line 225, in parse_config
    parser.parse()
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 85, in parse
    self._parse(self._yang_name, self.model, self.mapping[self._yang_name])
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 91, in _parse
    self._parse_container(attribute, model, mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 132, in _parse_container
    self._parse(k, v, mapping[v._yang_name])
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 93, in _parse
    self._parse_list(attribute, model, mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 178, in _parse_list
    self._parse(key, obj, element_mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 91, in _parse
    self._parse_container(attribute, model, mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 132, in _parse_container
    self._parse(k, v, mapping[v._yang_name])
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 91, in _parse
    self._parse_container(attribute, model, mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 132, in _parse_container
    self._parse(k, v, mapping[v._yang_name])
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 93, in _parse
    self._parse_list(attribute, model, mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 178, in _parse_list
    self._parse(key, obj, element_mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 91, in _parse
    self._parse_container(attribute, model, mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 130, in _parse_container
    parser.parse()
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 85, in parse
    self._parse(self._yang_name, self.model, self.mapping[self._yang_name])
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 91, in _parse
    self._parse_container(attribute, model, mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 132, in _parse_container
    self._parse(k, v, mapping[v._yang_name])
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 91, in _parse
    self._parse_container(attribute, model, mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 132, in _parse_container
    self._parse(k, v, mapping[v._yang_name])
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 95, in _parse
    self._parse_leaf(attribute, model, mapping)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/parser.py\", line 199, in _parse_leaf
    setter(value)
  File \"/usr/local/lib/python2.7/dist-packages/napalm_yang/models/openconfig/interfaces/interface/subinterfaces/subinterface/vlan/config/__init__.py\", line 90, in _set_vlan_id
    'generated-type': \"\"\"YANGDynClass(base=[RestrictedClassType(base_type=RestrictedClassType(base_type=int, restriction_dict={'range': ['0..65535']},int_size=16), restriction_dict={'range': [u'1..4094']}),RestrictedClassType(base_type=unicode, restriction_dict={'pattern': u'(409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{1,2}|[1-9])\\\\.((409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{1,2}|[1-9])|\\\\*)'}),], is_leaf=True, yang_name=\"vlan-id\", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, namespace='http://openconfig.net/yang/vlan', defining_module='openconfig-vlan', yang_type='union', is_config=True)\"\"\",
ValueError: {'error-string': 'vlan_id must be of a type compatible with union', 'generated-type': 'YANGDynClass(base=[RestrictedClassType(base_type=RestrictedClassType(base_type=int, restriction_dict={\\'range\\': [\\'0..65535\\']},int_size=16), restriction_dict={\\'range\\': [u\\'1..4094\\']}),RestrictedClassType(base_type=unicode, restriction_dict={\\'pattern\\': u\\'(409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{1,2}|[1-9])\\\\.((409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{1,2}|[1-9])|\\\\*)\\'}),], is_leaf=True, yang_name=\"vlan-id\", parent=self, path_helper=self._path_helper, extmethods=self._extmethods, register_paths=True, namespace=\\'http://openconfig.net/yang/vlan\\', defining_module=\\'openconfig-vlan\\', yang_type=\\'union\\', is_config=True)', 'defined-type': 'openconfig-vlan:union'}
", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 0}

My playbook:

- name: Test napalm-parse-yang 
  hosts: gn4-net-core         
  connection: local    
  gather_facts: no     

  tasks:                            
    - name: get facts from device    
      napalm_get_facts:             
        hostname: "{{ inventory_hostname }}" 
        username: "{{ user }}"              
        dev_os: "{{ os }}"                 
        password: "{{ passwd }}"          
        filter: ['facts']    
      register: result                  

    - name: print results                    
      debug: msg="{{ result }}"          


    - name: Parse from device
      napalm_parse_yang:
        hostname: "{{ inventory_hostname }}"
        username: "{{ user }}"
        dev_os: "{{ os }}"
        password: "{{ passwd }}"
        mode: "config"
        profiles: ['junos']
        models:
          - models.openconfig_interfaces
      register: running

    - name: print running                    
      debug: msg="{{ running }}"              #Display the collected output

Anything I migh miss or misunderstood ?

The router is a vMX with basic PE config. (ISIS, BGP, MPLS activated) I can provide it if needed with interface and subinterface.

Any hints/Tips & tricks would be appreciated... :-)
Thanks for your help !

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.