From c93313ea2c97e98d6e266dcdb0932c0eab689d9e Mon Sep 17 00:00:00 2001 From: Paul Greenberg Date: Sat, 30 Jun 2018 13:19:07 -0400 Subject: [PATCH] demo: add cisco ucs and meraki api (#15) More info: This commit adds the files necessary for the data collection from Cisco UCS switches. Additionally, it makes this Ansible module compatible with Ansible 2.6.0. Resolves: #12, #13, #14 --- demo/devnet-cisco-ucs/README.md | 16 ++ demo/devnet-cisco-ucs/ansible.cfg | 8 + demo/devnet-cisco-ucs/ansible.vault.key | 1 + demo/devnet-cisco-ucs/ansible.vault.yml | 22 ++ demo/devnet-cisco-ucs/hosts | 14 ++ .../playbooks/collect_all.yml | 14 ++ demo/devnet-meraki/README.md | 16 ++ demo/devnet-meraki/ansible.cfg | 8 + demo/devnet-meraki/ansible.vault.key | 1 + demo/devnet-meraki/ansible.vault.yml | 22 ++ demo/devnet-meraki/hosts | 14 ++ demo/devnet-meraki/playbooks/collect_all.yml | 14 ++ .../playbooks/configure_ospf.yml | 3 +- .../plugins/action/files/cli/os/cisco_ucs.yml | 201 ++++++++++++++++++ ndmtk/plugins/action/ndmtk.j2 | 7 +- ndmtk/plugins/action/ndmtk.py | 90 +++++++- ndmtk/plugins/action/ndmtk.yml | 2 + ndmtk/plugins/callback/ndmtk.py | 11 +- setup.py | 48 ++--- 19 files changed, 467 insertions(+), 45 deletions(-) create mode 100644 demo/devnet-cisco-ucs/README.md create mode 100644 demo/devnet-cisco-ucs/ansible.cfg create mode 100644 demo/devnet-cisco-ucs/ansible.vault.key create mode 100644 demo/devnet-cisco-ucs/ansible.vault.yml create mode 100644 demo/devnet-cisco-ucs/hosts create mode 100644 demo/devnet-cisco-ucs/playbooks/collect_all.yml create mode 100644 demo/devnet-meraki/README.md create mode 100644 demo/devnet-meraki/ansible.cfg create mode 100644 demo/devnet-meraki/ansible.vault.key create mode 100644 demo/devnet-meraki/ansible.vault.yml create mode 100644 demo/devnet-meraki/hosts create mode 100644 demo/devnet-meraki/playbooks/collect_all.yml create mode 100644 ndmtk/plugins/action/files/cli/os/cisco_ucs.yml diff --git a/demo/devnet-cisco-ucs/README.md b/demo/devnet-cisco-ucs/README.md new file mode 100644 index 0000000..b2a76f0 --- /dev/null +++ b/demo/devnet-cisco-ucs/README.md @@ -0,0 +1,16 @@ +# Cisco DevNet Sandbox - Meraki API + +This demo shows how to collect data from [Meraki API](https://learninglabs.cisco.com/modules/dnc-2017-meraki/meraki-dashboard-api/step/1). + +## Data Collection + +A user collects data by browsing to this directory and invoking a specific +playbook. + +``` +cd devnet-meraki/ +ansible-playbook playbooks/collect_all.yml -v +``` + +Once the toolkit collected the data, the user may use `ndmtk-git` to check +that data into source code repository. diff --git a/demo/devnet-cisco-ucs/ansible.cfg b/demo/devnet-cisco-ucs/ansible.cfg new file mode 100644 index 0000000..ef18081 --- /dev/null +++ b/demo/devnet-cisco-ucs/ansible.cfg @@ -0,0 +1,8 @@ +[defaults] +inventory = ./hosts +forks = 100 +local_tmp = $HOME/.ansible/tmp +retry_files_enabled = True +retry_files_save_path = $HOME/.ansible/retries/ +log_path = $HOME/.ansible/ansible.log +transport = local diff --git a/demo/devnet-cisco-ucs/ansible.vault.key b/demo/devnet-cisco-ucs/ansible.vault.key new file mode 100644 index 0000000..f63fc1e --- /dev/null +++ b/demo/devnet-cisco-ucs/ansible.vault.key @@ -0,0 +1 @@ +cisco diff --git a/demo/devnet-cisco-ucs/ansible.vault.yml b/demo/devnet-cisco-ucs/ansible.vault.yml new file mode 100644 index 0000000..43df1b9 --- /dev/null +++ b/demo/devnet-cisco-ucs/ansible.vault.yml @@ -0,0 +1,22 @@ +$ANSIBLE_VAULT;1.1;AES256 +65333536326365313533313235376363323463373239633463353036383066383631613066353731 +6639663963363938623235356435383231393361333031630a326166633439373734366364376336 +36316233353662653663666636666536353838323535363339613864323764353564396535303231 +6239383364656335640a383532303531313337346363366538616438333434353862633565363330 +38383665626131376561393435646235383962623061643430383165656461346166653939656239 +32366662366631373662373630383930653233363465643335343139633566376562643338643133 +32653538336433363732313537663631356332333362613835323065353262663334626439643436 +30303663356435386537656162303734666165363735336433373931376431366461373739393664 +37383564366232343661393864313731353233353162346666653261343762616533363933653338 +32633239316163643332643462323632656331383730656230366233346265353733356663613065 +62373064353436323262633966373736356566353561636136316164363333633830303464656337 +34613939353935656462303862356638353033326139333762376665363335613931653939383832 +30363465346539343033666334323730633237643737323231323138343464363963393663386266 +36646239376537356534616435653438333336633633386565643265376565653139353530336562 +33393130336534613537353963623963333466656466336464343362626131323561343333616537 +65616232313634613430616263306333666632623132656435383566373630356135303561656364 +62333762643463653865313938396331653261613439623738613239383462643961316464353239 +39396566383962346338343330393239363231396162313837333563303036343330313164313736 +31333163303066336162373336356464326566323166653035333762333139636434663632373834 +63616132643362376632633763333436383433353662366664396531393331303064623830366461 +3937 diff --git a/demo/devnet-cisco-ucs/hosts b/demo/devnet-cisco-ucs/hosts new file mode 100644 index 0000000..69c14fa --- /dev/null +++ b/demo/devnet-cisco-ucs/hosts @@ -0,0 +1,14 @@ +# +# DevNet Sandbox +# + +controller ansible_connection=local + +[cisco-ucs:children] +cisco-ucs-shell + +[cisco-ucs-shell] +cisco-ucs-manager os=cisco_ucs host_overwrite=10.199.0.219 host_port=5901 + +[cisco-ucs:vars] +ansible_connection=local diff --git a/demo/devnet-cisco-ucs/playbooks/collect_all.yml b/demo/devnet-cisco-ucs/playbooks/collect_all.yml new file mode 100644 index 0000000..8a99e75 --- /dev/null +++ b/demo/devnet-cisco-ucs/playbooks/collect_all.yml @@ -0,0 +1,14 @@ +--- +- name: data collection from Cisco UCS + hosts: + - 'cisco-ucs' + strategy: free + gather_facts: no + tasks: + - name: collect all available data from Cisco UCS Manager managed fabric + action: ndmtk + output="/opt/data/devnet-cisco-ucs/cisco-ucs-%Y%m%d%H%M%S" + no_host_key_check=yes + on_error=continue + safe="./ansible.vault.yml" + lockpick="./ansible.vault.key" diff --git a/demo/devnet-meraki/README.md b/demo/devnet-meraki/README.md new file mode 100644 index 0000000..b2a76f0 --- /dev/null +++ b/demo/devnet-meraki/README.md @@ -0,0 +1,16 @@ +# Cisco DevNet Sandbox - Meraki API + +This demo shows how to collect data from [Meraki API](https://learninglabs.cisco.com/modules/dnc-2017-meraki/meraki-dashboard-api/step/1). + +## Data Collection + +A user collects data by browsing to this directory and invoking a specific +playbook. + +``` +cd devnet-meraki/ +ansible-playbook playbooks/collect_all.yml -v +``` + +Once the toolkit collected the data, the user may use `ndmtk-git` to check +that data into source code repository. diff --git a/demo/devnet-meraki/ansible.cfg b/demo/devnet-meraki/ansible.cfg new file mode 100644 index 0000000..ef18081 --- /dev/null +++ b/demo/devnet-meraki/ansible.cfg @@ -0,0 +1,8 @@ +[defaults] +inventory = ./hosts +forks = 100 +local_tmp = $HOME/.ansible/tmp +retry_files_enabled = True +retry_files_save_path = $HOME/.ansible/retries/ +log_path = $HOME/.ansible/ansible.log +transport = local diff --git a/demo/devnet-meraki/ansible.vault.key b/demo/devnet-meraki/ansible.vault.key new file mode 100644 index 0000000..f63fc1e --- /dev/null +++ b/demo/devnet-meraki/ansible.vault.key @@ -0,0 +1 @@ +cisco diff --git a/demo/devnet-meraki/ansible.vault.yml b/demo/devnet-meraki/ansible.vault.yml new file mode 100644 index 0000000..df55419 --- /dev/null +++ b/demo/devnet-meraki/ansible.vault.yml @@ -0,0 +1,22 @@ +$ANSIBLE_VAULT;1.1;AES256 +63393532663966663530643432313839343231303836653661303335303434316535626238646135 +3438333662353363363439353838643736636263343763640a623730316639646535366262373731 +34333666626639396663383464613762303135366539356438373130363033353931386631306333 +3566303664393632390a353637653363303865626237663339653833363662303761633363643262 +32303537373061356130653461303865386338663233313137396238663537323831336264636338 +37386539646563623162303530313131633833623937613033356363653966396265626366393033 +62373233376336616438386338323238636236343435346463643535366135386332336630313736 +33303466346531303366363537346331376265363462646462316538343538336163363335306331 +64666230613665653866356365323533633033336565663831636562613534396532316265323730 +34343661636365323838633366303439393736343264366334346535313361633330326338383838 +37333834306135656135396461316134353830653036313162306631383065393438396561646331 +39613162653966393537393636373166393236656331636538623231623662363131623362393339 +62396638313966633935323663633137623639353631653434353836353633333135396431373538 +31346465656636363530353339386534343736663833353831316161323663653535666163306436 +32323832326235663362313534633530613939326439626234643733623038323364326166396535 +32323333323339646133343764336632313334666534643333323039653636383563363534666131 +35326134393039336633373966353032313230336364373430306536316135383132653535356466 +33633235646238376430653463346639363331643336356565383862346362303563383532653130 +34323936663031313862653535326366326236616136623165663837366639366437643263633433 +62333064386633613132376365613665363935646434653630326161346565666563646338616531 +65333965386664333939623335386432643964316265613162313536643361333262 diff --git a/demo/devnet-meraki/hosts b/demo/devnet-meraki/hosts new file mode 100644 index 0000000..f0539e6 --- /dev/null +++ b/demo/devnet-meraki/hosts @@ -0,0 +1,14 @@ +# +# DevNet Sandbox +# + +controller ansible_connection=local + +[meraki:children] +meraki-api + +[meraki-api] +meraki-endpoint os=rest api_endpoint="https://dashboard.meraki.com/api/v0" + +[meraki:vars] +ansible_connection=local diff --git a/demo/devnet-meraki/playbooks/collect_all.yml b/demo/devnet-meraki/playbooks/collect_all.yml new file mode 100644 index 0000000..8efe81e --- /dev/null +++ b/demo/devnet-meraki/playbooks/collect_all.yml @@ -0,0 +1,14 @@ +--- +- name: data collection from Meraki API + hosts: + - meraki + strategy: free + gather_facts: no + tasks: + - name: collect all available data from Meraki API nodes + action: ndmtk + output="/opt/data/devnet-meraki/meraki-%Y%m%d%H%M%S" + no_host_key_check=yes + on_error=continue + safe="./ansible.vault.yml" + lockpick="./ansible.vault.key" diff --git a/demo/devnet-nxapi-virl/playbooks/configure_ospf.yml b/demo/devnet-nxapi-virl/playbooks/configure_ospf.yml index ced9c3c..4e980c5 100644 --- a/demo/devnet-nxapi-virl/playbooks/configure_ospf.yml +++ b/demo/devnet-nxapi-virl/playbooks/configure_ospf.yml @@ -1,8 +1,7 @@ --- - name: OSPF configuration hosts: - #- sandbox - - nx-osv-2 + - sandbox strategy: free gather_facts: no tasks: diff --git a/ndmtk/plugins/action/files/cli/os/cisco_ucs.yml b/ndmtk/plugins/action/files/cli/os/cisco_ucs.yml new file mode 100644 index 0000000..a0e97fc --- /dev/null +++ b/ndmtk/plugins/action/files/cli/os/cisco_ucs.yml @@ -0,0 +1,201 @@ +--- +# +# ndmtk - Network Discovery and Management Toolkit +# Copyright (C) 2016 Paul Greenberg @greenpau +# See LICENSE.txt for licensing details +# +# File: plugins/action/files/cli/os/cisco_ucs.yml +# + +ndmtk: +- cli: 'show configuration all' + tags: 'configuration' + paging: 'terminal length 0' +- cli: 'show version' + tags: 'version' +- description: 'Collect UCS Chassis information' + cli: 'show chassis detail' + tags: ['version'] +- description: 'Collect firmware information' + cli: 'show chassis firmware detail' + tags: ['management'] +- description: 'Collect version information' + cli: 'show chassis version detail' + tags: ['management'] +- description: 'Collect fabric information' + cli: 'show chassis fabric detail' + tags: ['management'] +- description: 'Collect chassis health information' + cli: 'show chassis environment detail' + tags: ['management'] +- description: 'Collect chassis health information (IO Module)' + cli: 'show chassis environment iom detail' + tags: ['management'] +- description: 'Collect chassis health information (Power Supplies)' + cli: 'show chassis environment psu detail' + tags: ['management'] +- description: 'Collect chassis health information (Fans)' + cli: 'show chassis environment fan detail' + tags: ['management'] +- description: 'Collect chassis health information (Servers)' + cli: 'show chassis environment server detail' + tags: ['management'] +- description: 'Collect chassis inventory information' + cli: 'show chassis inventory detail' + tags: ['inventory'] +- description: 'Collect chassis inventory information (IO Module)' + cli: 'show chassis inventory iom detail' + tags: ['inventory'] +- description: 'Collect chassis inventory information (Power Supplies)' + cli: 'show chassis inventory psu detail' + tags: ['inventory'] +- description: 'Collect chassis inventory information (Fans)' + cli: 'show chassis inventory fan detail' + tags: ['inventory'] +- description: 'Collect chassis inventory information (Servers)' + cli: 'show chassis inventory server detail' + tags: ['inventory'] +- cli: 'show clock detail' + tags: ['clock'] +- cli: 'show cluster state' + tags: ['high_availability'] +- cli: 'show cluster extended-state' + tags: ['high_availability'] +- cli: 'show eth-uplink detail' + tags: ['network'] +- cli: 'show eth-uplink detail expand' + tags: ['network'] +- cli: 'show fabric-interconnect detail' + tags: ['network'] +- cli: 'show fabric-interconnect environment expand detail' + tags: ['network'] +- cli: 'show fabric-interconnect inventory expand detail' + tags: ['network'] +- cli: 'show fabric-interconnect mode' + tags: ['network'] +- cli: 'show fabric-interconnect mac-aging' + tags: ['network'] +- cli: 'show fabric-interconnect version' + tags: ['network'] +- cli: 'show fabric-interconnect firmware' + tags: ['network'] +- cli: 'show fault' + tags: ['management'] +- cli: 'show security detail' + tags: ['servers'] +- cli: 'show server inventory expand detail' + tags: ['servers'] +- cli: 'show server assoc' + tags: ['servers'] +- cli: 'show server boot-order detail' + tags: ['servers'] +- cli: 'show server firmware detail' + tags: ['servers'] +- cli: 'show server identity' + tags: ['servers'] +- cli: 'show server status' + tags: ['servers'] +- cli: 'show server status detail' + tags: ['servers'] +- cli: 'show server storage detail' + tags: ['servers'] +- cli: 'show server version detail' + tags: ['servers'] +- cli: 'show service-profile assoc' + tags: ['sevice_profile'] +- cli: 'show service-profile assoc detail' + tags: ['sevice_profile'] +- cli: 'show service-profile circuit' + tags: ['sevice_profile'] +- cli: 'show service-profile circuit detail' + tags: ['sevice_profile'] +- cli: 'show service-profile connectivity' + tags: ['sevice_profile'] +- cli: 'show service-profile connectivity detail' + tags: ['sevice_profile'] +- cli: 'show service-profile identity' + tags: ['sevice_profile'] +- cli: 'show service-profile inventory' + tags: ['sevice_profile'] +- cli: 'show service-profile inventory expand detail' + tags: ['sevice_profile'] +- cli: 'show service-profile path' + tags: ['sevice_profile'] +- cli: 'show service-profile path detail' + tags: ['sevice_profile'] +- cli: 'show service-profile status' + tags: ['sevice_profile'] +- cli: 'show service-profile status expand detail' + tags: ['sevice_profile'] +- description: 'Collect configuration from Fabric Interconnect A' + pre: 'connect nxos a' + cli: 'show running-config' + saveas: '%h.connect.nxos.a.show.running-config.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect version from Fabric Interconnect A' + pre: 'connect nxos a' + cli: 'show version' + saveas: '%h.connect.nxos.a.show.version.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect interface information from Fabric Interconnect A' + pre: 'connect nxos a' + cli: 'show interface' + saveas: '%h.connect.nxos.a.show.interface.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect interface SNMP index information from Fabric Interconnect A' + pre: 'connect nxos a' + cli: 'show interface snmp-ifindex' + saveas: '%h.connect.nxos.a.show.interface.snmp-ifindex.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect MAC address table from Fabric Interconnect A' + pre: 'connect nxos a' + cli: 'show mac address-table' + saveas: '%h.connect.nxos.a.show.mac.address-table.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect VLAN information from Fabric Interconnect A' + pre: 'connect nxos a' + cli: 'show vlan' + saveas: '%h.connect.nxos.a.show.vlan.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect configuration from Fabric Interconnect B' + pre: 'connect nxos b' + cli: 'show running-config' + saveas: '%h.connect.nxos.b.show.running-config.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect version from Fabric Interconnect B' + pre: 'connect nxos b' + cli: 'show version' + saveas: '%h.connect.nxos.b.show.version.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect interface information from Fabric Interconnect B' + pre: 'connect nxos b' + cli: 'show interface' + saveas: '%h.connect.nxos.b.show.interface.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect interface SNMP index information from Fabric Interconnect B' + pre: 'connect nxos b' + cli: 'show interface snmp-ifindex' + saveas: '%h.connect.nxos.b.show.interface.snmp-ifindex.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect MAC address table from Fabric Interconnect B' + pre: 'connect nxos b' + cli: 'show mac address-table' + saveas: '%h.connect.nxos.b.show.mac.address-table.txt' + post: 'exit' + tags: ['network'] +- description: 'Collect VLAN information from Fabric Interconnect B' + pre: 'connect nxos b' + cli: 'show vlan' + saveas: '%h.connect.nxos.b.show.vlan.txt' + post: 'exit' + tags: ['network'] diff --git a/ndmtk/plugins/action/ndmtk.j2 b/ndmtk/plugins/action/ndmtk.j2 index f8ef969..1d6d72e 100644 --- a/ndmtk/plugins/action/ndmtk.j2 +++ b/ndmtk/plugins/action/ndmtk.j2 @@ -272,8 +272,8 @@ proc parse_cli_prompt {cli_output req} { "?" { set line_char_flt "\?"; } "*" { set line_char_flt "\\*"; } "+" { set line_char_flt "\+"; } - "(" { set line_char_flt "\("; } - ")" { set line_char_flt "\)"; } + "(" { set line_char_flt "\\("; } + ")" { set line_char_flt "\\)"; } "{" { set line_char_flt "\{"; } "}" { set line_char_flt "\}"; } default { set line_char_flt $line_char; } @@ -521,7 +521,7 @@ while 1 { } } -regexp {[#]\s?$} { - send_status "pount_prompt:yes"; + send_status "pound_prompt:yes"; if { $session_connected == 0 } { send_status "connected:yes"; } @@ -692,7 +692,6 @@ while {1} { } else { send -h "$clitask\n"; } - if { $host_operating_system == "generic_linux" || $host_operating_system == "opengear_linux" } { expect -re "\r\n|\n|\r"; expect -re $; diff --git a/ndmtk/plugins/action/ndmtk.py b/ndmtk/plugins/action/ndmtk.py index 9024b9b..446cae5 100644 --- a/ndmtk/plugins/action/ndmtk.py +++ b/ndmtk/plugins/action/ndmtk.py @@ -256,7 +256,17 @@ from collections import OrderedDict; from ansible.errors import AnsibleError; from ansible.plugins.action import ActionBase; -from ansible.utils.boolean import boolean; +try: + from ansible.module_utils.parsing.convert_bool import boolean +except ImportError: + try: + from ansible.utils.boolean import boolean + except ImportError: + try: + from ansible.utils import boolean + except ImportError: + from ansible import constants + boolean = constants.mk_boolean from datetime import date; import jinja2; import json; @@ -408,8 +418,8 @@ def run(self, tmp=None, task_vars=None): self.errors.append('the \'' + str(self.conf['on_' + i]) + '\' is not a valid option for \'' + self.plugin_name + '\' plugin'); return dict(msg='\n'.join(self.errors), log_dir=self.conf['temp_dir'], failed=True); - self.conf['disable_defaults'] = boolean(self._task.args.get('disable_defaults')); - self.conf['no_host_key_check'] = boolean(self._task.args.get('no_host_key_check')); + self.conf['disable_defaults'] = boolean(self._task.args.get('disable_defaults', 'no')); + self.conf['no_host_key_check'] = boolean(self._task.args.get('no_host_key_check', 'no')); self.conf['allowed_sections'] = self._task.args.get('sections', None); if self.conf['allowed_sections'] is not None: self.conf['allowed_sections'] = [s.strip() for s in self.conf['allowed_sections'].split(",")] @@ -429,6 +439,26 @@ def run(self, tmp=None, task_vars=None): self.errors.append('\'' + self.plugin_name + '_' + i + '\' inventory attribute must be associated with ' + self.info['host']); return dict(msg='\n'.join(self.errors), log_dir=self.conf['temp_dir'], failed=True); + ''' + Determine the interface to interract with a remote system. + Currently, there are two types: shell and api + ''' + api_interfaces = [ + 'rest', + ]; + if self.info['os'] in api_interfaces: + self._is_api_driven = True; + else: + self._is_api_driven = False; + + if self._is_api_driven: + for i in ['api_endpoint']: + v = task_vars.get(i, None); + if v is None: + self.errors.append('interraction with API-driven operating systems requires defining ' + i + ' ' + self.info['host']); + return dict(msg='\n'.join(self.errors), log_dir=self.conf['temp_dir'], failed=True); + self.info[i] = v; + ''' Set default session timeout. ''' @@ -550,7 +580,7 @@ def run(self, tmp=None, task_vars=None): j = 0; for c in self.keystore: for k in c: - if k not in ['password', 'password_enable']: + if k not in ['password', 'password_enable', 'api_auth_value']: display.vvv('credentials (' + str(j) + '): ' + str(k) + ': ' + str(c[k]), host=self.info['host']); j += 1; @@ -1933,6 +1963,9 @@ def _parse_cli_output(self, fn, cli_id): self._remove_non_ascii(fn); self.conf['cliset'][cli_id]['lines'] = self._remove_ltr_blanks(fn); if self.conf['cliset'][cli_id]['lines'] == 0: + #display.v('<' + self.info['host'] + '> testing id: ' + str(cli_id)); + #display.v('<' + self.info['host'] + '> testing file: ' + str(fn)); + #display.v('<' + self.info['host'] + '> testing: ' + str(self.conf['cliset'][cli_id])); if self.conf['cliset'][cli_id]['allow_empty_response'] == True: return False; if self.conf['cliset'][cli_id]['mode'] == 'analytics': @@ -2189,6 +2222,12 @@ def _parse_cli_output(self, fn, cli_id): @staticmethod def _remove_non_ascii(fn): + if not os.path.exists(fn): + return; + if not os.path.isfile(fn): + return; + if not os.access(fn, os.R_OK): + return; with open(fn, "r+") as f: data = f.read(); buffer = []; @@ -2218,8 +2257,16 @@ def _remove_non_ascii(fn): return; - @staticmethod - def _remove_ltr_blanks(fn): + def _remove_ltr_blanks(self, fn): + if not os.path.exists(fn): + display.v('<' + self.info['host'] + '> file does not exist: ' + str(fn)); + return 0; + if not os.path.isfile(fn): + display.v('<' + self.info['host'] + '> is not a file: ' + str(fn)); + return 0; + if not os.access(fn, os.R_OK): + display.v('<' + self.info['host'] + '> is not readable: ' + str(fn)); + return 0; ''' This function removes leading and trailing blank lines from a file. Additionally, it returns the number of lines in the file. @@ -2353,12 +2400,33 @@ def _load_credentials(self, db=dict()): credentials = []; rgx_credentials = {}; dft_credentials = {}; + allowed_credentials_fields = [ + 'regex', + 'username', + 'password', + 'password_enable', + 'priority', + 'description', + 'default', + 'token', + 'pin', + 'api_auth_key', + 'api_auth_value', + ]; + #self.errors.append('XXXX: ' + str(db)); for c in db: for k in c: - if k not in ['regex', 'username', 'password', 'password_enable', 'priority', 'description', 'default', 'token', 'pin']: + if k not in allowed_credentials_fields: self.errors.append('access credentials dictionary contains invalid key: ' + k); - required_keys = ['username', 'password', 'priority']; + required_keys = None; + required_keys_shell = ['username', 'password', 'priority']; + required_keys_api = ['api_auth_key', 'api_auth_value']; + if self._is_api_driven: + required_keys = copy.deepcopy(required_keys_api); + else: + required_keys = copy.deepcopy(required_keys_shell); for k in required_keys: + break; if k not in c: self.errors.append('the "' + str(c) + '" access credentials entry is missing mandatory key "' + k + '"'); return None; @@ -2640,6 +2708,12 @@ def _load_cliset(self, fn, src, commit=True): if self.conf['cliset'][c]['mode'] != entry_mode: if entry_mode == 'noop' or self.conf['cliset'][c]['mode'] == 'noop': continue; + if 'pre' in entry and 'post' in entry: + _is_duplicate_cli = False; + continue; + if 'saveas' in entry: + _is_duplicate_cli = False; + continue; self.errors.append('the plugin does not support the mixing of \'configure\' and \'analytics\' modes in the same run'); return False; if _is_duplicate_cli: diff --git a/ndmtk/plugins/action/ndmtk.yml b/ndmtk/plugins/action/ndmtk.yml index 382c564..3f313fb 100644 --- a/ndmtk/plugins/action/ndmtk.yml +++ b/ndmtk/plugins/action/ndmtk.yml @@ -45,6 +45,7 @@ allowed_os: - name: 'cisco_nxos' - name: 'cisco_nxos_cmp' - name: 'cisco_nxos_mds' +- name: 'cisco_ucs' - name: 'citrix_netscaler' - name: 'junos_qfx' - name: 'junos_srx' @@ -80,6 +81,7 @@ allowed_os: - 'environment no more' exit_sequence: - 'logout' +- name: 'rest' allowed_ref_tags: - 'configuration' diff --git a/ndmtk/plugins/callback/ndmtk.py b/ndmtk/plugins/callback/ndmtk.py index 445a4a3..bb15118 100644 --- a/ndmtk/plugins/callback/ndmtk.py +++ b/ndmtk/plugins/callback/ndmtk.py @@ -160,9 +160,14 @@ def _load_auth_secrets(self, hosts=[], secrets=[]): for _safe, _lockpick in secrets: try: _safe_loader = DataLoader(); - _safe_lockpick = CLI.read_vault_password_file(_lockpick, loader=_safe_loader); - _safe_loader.set_vault_password(_safe_lockpick); - _safe_contents = _safe_loader.load_from_file(_safe); + _safe_lockpick = None + try: + _safe_lockpick = CLI.read_vault_password_file(_lockpick, loader=_safe_loader); + _safe_loader.set_vault_password(_safe_lockpick); + _safe_contents = _safe_loader.load_from_file(_safe); + except: + _safe_lockpick = CLI.setup_vault_secrets(_safe_loader, [_lockpick]) + _safe_contents = _safe_loader.load_from_file(_safe); if 'credentials' not in _safe_contents: return dict(); #display.display(pprint.pformat(_safe_contents, indent=4), color='green'); diff --git a/setup.py b/setup.py index f251183..2f5566a 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,24 @@ def _load_test_suite(): test_suite = test_loader.discover(os.path.join(pkg_dir, pkg_name, 'tests'), pattern='test_*.py'); return test_suite; +def remove_ansible_files(ansible_dirs): + for ansible_dir in ansible_dirs: + for suffix in ['.py', '.pyc']: + for plugin_type in ['plugins/action', 'plugins/callback']: + plugin_file = os.path.join(ansible_dir, plugin_type , pkg_name + suffix); + if os.path.isfile(plugin_file) or os.path.islink(plugin_file): + print("[INFO] found '%s'" % plugin_file); + try: + if os.path.islink(plugin_file): + os.unlink(plugin_file); + else: + os.remove(plugin_file); + print("[INFO] removed '%s'" % plugin_file); + except: + exc_type, exc_value, exc_traceback = sys.exc_info(); + print("[ERROR] failed to remove %s %s" % (plugin_file, ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))); + return + def pre_build_toolkit(): for ts in _load_test_suite(): tsr=unittest.TextTestRunner(); @@ -53,22 +71,7 @@ def pre_build_toolkit(): print("[ERROR] 'ansible' python package was not found"); return []; print("[INFO] the path to 'ansible' python package is: " + str(ansible_dirs)); - for ansible_dir in ansible_dirs: - for suffix in ['.py', '.pyc']: - for plugin_type in ['plugins/action', 'plugins/callback']: - plugin_file = os.path.join(ansible_dir, plugin_type , pkg_name + suffix); - try: - os.unlink(plugin_file); - except: - pass; - try: - os.remove(plugin_file); - except: - pass; - if os.path.exists(plugin_file): - print("[ERROR] 'ansible' python package contains traces '" + pkg_name + "' package ("+ plugin_file +"), failed to delete, aborting!"); - else: - print("[INFO] 'ansible' python package contains traces '" + pkg_name + "' package ("+ plugin_file +"), deleted!"); + remove_ansible_files(ansible_dirs); return ansible_dirs; def _find_utility(name): @@ -180,18 +183,7 @@ def run(self): if len(ansible_dirs) == 0: print("[ERROR] 'ansible' python package was not found"); return; - for ansible_dir in ansible_dirs: - for suffix in ['.py', '.pyc']: - for plugin_type in ['plugins/action', 'plugins/callback']: - plugin_file = os.path.join(ansible_dir, plugin_type , pkg_name + suffix); - try: - os.unlink(plugin_file); - except: - pass; - try: - os.remove(plugin_file); - except: - pass; + remove_ansible_files(ansible_dirs); return;