Skip to content

Commit

Permalink
Pick 2.6 nxos bugfixes (#44105)
Browse files Browse the repository at this point in the history
* Add md5sum check in nxos_file_copy module (#43423)

* Add md5sum check in nxos_file_copy module

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* address review comment

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit fee4c24)

* nxos_vlan refactor to support non structured output (#43805)

* nxos_vlan refactor to support non structured output

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* unittest fix

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* minor fixes

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* use check_rc

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* address review comment

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* remove additional return statement

* address Nate's review

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 9634693)

* nxos_facts test lldp feature and fix nxapi check_rc (#44104)

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 43ae240)

* nxos bugfix 2.6 changelog

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* nxos_interface port-channel idempotence fix for mode (#44248)

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 6af6e80)

* changelog

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* check_mode in nxos_static_route module (#44252)

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 7e39c5b)

* changelog nxos_static_route

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* nxos_linkagg mode fix (#44294)

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 6090802)

* changelog nxos_linkagg

* nxos_system idempotence fix (#44752)

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 8a79d94)

* nxos_system changelog

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* use retry_json nxos_banner (#44376)

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 9c4ed4d)

* nxos_banner changelog

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* fix Python 2.6 regex bug terminal plugin nxos, iosxr (#45135)

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit ab3cd10)

* terminal plugin changelog

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
  • Loading branch information
trishnaguha authored and mattclay committed Sep 4, 2018
1 parent b0678e7 commit 7b840f3
Show file tree
Hide file tree
Showing 26 changed files with 299 additions and 171 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/nxos_banner_text.yaml
@@ -0,0 +1,2 @@
bugfixes:
- use retry_json nxos_banner (https://github.com/ansible/ansible/pull/44376).
2 changes: 2 additions & 0 deletions changelogs/fragments/nxos_file_copy_md5sum.yaml
@@ -0,0 +1,2 @@
bugfixes:
- Add md5sum check in nxos_file_copy module (https://github.com/ansible/ansible/pull/43423).
2 changes: 2 additions & 0 deletions changelogs/fragments/nxos_interface.yaml
@@ -0,0 +1,2 @@
bugfixes:
- nxos_interface port-channel idempotence fix for mode (https://github.com/ansible/ansible/pull/44248).
2 changes: 2 additions & 0 deletions changelogs/fragments/nxos_linkagg.yaml
@@ -0,0 +1,2 @@
bugfixes:
- nxos_linkagg mode fix (https://github.com/ansible/ansible/pull/44294).
3 changes: 3 additions & 0 deletions changelogs/fragments/nxos_non_structured_output_fix.yaml
@@ -0,0 +1,3 @@
bugfixes:
- nxos_vlan refactor to support non structured output (https://github.com/ansible/ansible/pull/43805).
- nxos_facts test lldp feature and fix nxapi check_rc (https://github.com/ansible/ansible/pull/44104).
2 changes: 2 additions & 0 deletions changelogs/fragments/nxos_static_route_fix.yaml
@@ -0,0 +1,2 @@
bugfixes:
- Fix check_mode in nxos_static_route module (https://github.com/ansible/ansible/pull/44252).
2 changes: 2 additions & 0 deletions changelogs/fragments/nxos_system.yaml
@@ -0,0 +1,2 @@
bugfixes:
- nxos_system idempotence fix (https://github.com/ansible/ansible/pull/44752).
2 changes: 2 additions & 0 deletions changelogs/fragments/terminal_plugin.yaml
@@ -0,0 +1,2 @@
bugfixes:
- Fix Python2.6 regex bug terminal plugin nxos, iosxr (https://github.com/ansible/ansible/pull/45135).
19 changes: 17 additions & 2 deletions lib/ansible/module_utils/network/nxos/nxos.py
Expand Up @@ -147,7 +147,22 @@ def run_commands(self, commands, check_rc=True):
"""Run list of commands on remote device and return results
"""
connection = self._get_connection()
return connection.run_commands(commands, check_rc)

try:
out = connection.run_commands(commands, check_rc)
if check_rc == 'retry_json':
capabilities = self.get_capabilities()
network_api = capabilities.get('network_api')

if network_api == 'cliconf' and out:
for index, resp in enumerate(out):
if 'Invalid command at' in resp and 'json' in resp:
if commands[index]['output'] == 'json':
commands[index]['output'] = 'text'
out = connection.run_commands(commands, check_rc)
return out
except ConnectionError as exc:
self._module.fail_json(msg=to_text(exc))

def load_config(self, config, return_error=False, opts=None):
"""Sends configuration commands to the remote device
Expand Down Expand Up @@ -309,7 +324,7 @@ def send_request(self, commands, output='text', check_status=True,
if response['ins_api'].get('outputs'):
output = response['ins_api']['outputs']['output']
for item in to_list(output):
if check_status and item['code'] != '200':
if check_status is True and item['code'] != '200':
if return_error:
result.append(item)
else:
Expand Down
11 changes: 2 additions & 9 deletions lib/ansible/modules/network/nxos/nxos_banner.py
Expand Up @@ -98,14 +98,7 @@ def execute_show_command(module, command):
'command': command,
'output': format,
}]
output = run_commands(module, cmds, False)
if len(output) == 0 or len(output[0]) == 0:
# If we get here the platform does not
# support structured output. Resend as
# text.
cmds[0]['output'] = 'text'
output = run_commands(module, cmds, False)

output = run_commands(module, cmds, check_rc='retry_json')
return output


Expand All @@ -130,7 +123,7 @@ def map_config_to_obj(module):
output = execute_show_command(module, command)[0]

if "Invalid command" in output:
module.fail_json(msg="banner: exec may not be supported on this platform. Possible values are : exec | motd")
module.fail_json(msg="banner: %s may not be supported on this platform. Possible values are : exec | motd" % module.params['banner'])

if isinstance(output, dict):
output = list(output.values())
Expand Down
60 changes: 15 additions & 45 deletions lib/ansible/modules/network/nxos/nxos_facts.py
Expand Up @@ -194,7 +194,7 @@ def run(self, command, output='text'):
'command': command,
'output': output
}
resp = run_commands(self.module, [command], check_rc=False)
resp = run_commands(self.module, [command], check_rc='retry_json')
try:
return resp[0]
except IndexError:
Expand Down Expand Up @@ -234,10 +234,8 @@ class Default(FactsBase):
def populate(self):
data = None

try:
data = self.run('show version', output='json')
except ConnectionError:
data = self.run('show version')
data = self.run('show version', output='json')

if data:
if isinstance(data, dict):
if data.get('sys_ver_str'):
Expand Down Expand Up @@ -300,10 +298,8 @@ def populate(self):
self.facts['filesystems'] = self.parse_filesystems(data)

data = None
try:
data = self.run('show system resources', output='json')
except ConnectionError:
data = self.run('show system resources')
data = self.run('show system resources', output='json')

if data:
if isinstance(data, dict):
self.facts['memtotal_mb'] = int(data['memory_usage_total']) / 1024
Expand Down Expand Up @@ -380,10 +376,8 @@ def populate(self):
self.facts['all_ipv6_addresses'] = list()
data = None

try:
data = self.run('show interface', output='json')
except ConnectionError:
data = self.run('show interface')
data = self.run('show interface', output='json')

if data:
if isinstance(data, dict):
self.facts['interfaces'] = self.populate_structured_interfaces(data)
Expand All @@ -392,10 +386,7 @@ def populate(self):
self.facts['interfaces'] = self.populate_interfaces(interfaces)

if self.ipv6_structure_op_supported():
try:
data = self.run('show ipv6 interface', output='json')
except ConnectionError:
data = self.run('show ipv6 interface')
data = self.run('show ipv6 interface', output='json')
else:
data = None
if data:
Expand All @@ -409,10 +400,7 @@ def populate(self):
if data:
self.facts['neighbors'] = self.populate_neighbors(data)

try:
data = self.run('show cdp neighbors detail', output='json')
except ConnectionError:
data = self.run('show cdp neighbors detail')
data = self.run('show cdp neighbors detail', output='json')
if data:
if isinstance(data, dict):
self.facts['neighbors'] = self.populate_structured_neighbors_cdp(data)
Expand Down Expand Up @@ -718,10 +706,7 @@ class Legacy(FactsBase):
def populate(self):
data = None

try:
data = self.run('show version')
except ConnectionError:
data = self.run('show version', output='json')
data = self.run('show version', output='json')
if data:
if isinstance(data, dict):
self.facts.update(self.transform_dict(data, self.VERSION_MAP))
Expand All @@ -730,50 +715,35 @@ def populate(self):
self.facts['_os'] = self.parse_os(data)
self.facts['_platform'] = self.parse_platform(data)

try:
data = self.run('show interface', output='json')
except ConnectionError:
data = self.run('show interface')
data = self.run('show interface', output='json')
if data:
if isinstance(data, dict):
self.facts['_interfaces_list'] = self.parse_structured_interfaces(data)
else:
self.facts['_interfaces_list'] = self.parse_interfaces(data)

try:
data = self.run('show vlan brief', output='json')
except ConnectionError:
data = self.run('show vlan brief')
data = self.run('show vlan brief', output='json')
if data:
if isinstance(data, dict):
self.facts['_vlan_list'] = self.parse_structured_vlans(data)
else:
self.facts['_vlan_list'] = self.parse_vlans(data)

try:
data = self.run('show module', output='json')
except ConnectionError:
data = self.run('show module')
data = self.run('show module', output='json')
if data:
if isinstance(data, dict):
self.facts['_module'] = self.parse_structured_module(data)
else:
self.facts['_module'] = self.parse_module(data)

try:
data = self.run('show environment fan', output='json')
except ConnectionError:
data = self.run('show environment fan')
data = self.run('show environment fan', output='json')
if data:
if isinstance(data, dict):
self.facts['_fan_info'] = self.parse_structured_fan_info(data)
else:
self.facts['_fan_info'] = self.parse_fan_info(data)

try:
data = self.run('show environment power', output='json')
except ConnectionError:
data = self.run('show environment power')
data = self.run('show environment power', output='json')
if data:
if isinstance(data, dict):
self.facts['_power_supply_info'] = self.parse_structured_power_supply_info(data)
Expand Down
26 changes: 25 additions & 1 deletion lib/ansible/modules/network/nxos/nxos_file_copy.py
Expand Up @@ -86,13 +86,15 @@
sample: '/path/to/remote/file'
'''

import hashlib
import os
import re
import time

from ansible.module_utils.network.nxos.nxos import run_commands
from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native, to_text, to_bytes

try:
import paramiko
Expand All @@ -107,12 +109,34 @@
HAS_SCP = False


def md5sum_check(module, dst, file_system):
command = 'show file {0}{1} md5sum'.format(file_system, dst)
remote_filehash = run_commands(module, {'command': command, 'output': 'text'})[0]
remote_filehash = to_bytes(remote_filehash, errors='surrogate_or_strict')

local_file = module.params['local_file']
try:
with open(local_file, 'r') as f:
filecontent = f.read()
except (OSError, IOError) as exc:
module.fail_json(msg="Error reading the file: %s" % to_text(exc))

filecontent = to_bytes(filecontent, errors='surrogate_or_strict')
local_filehash = hashlib.md5(filecontent).hexdigest()

if local_filehash == remote_filehash:
return True
else:
return False


def remote_file_exists(module, dst, file_system='bootflash:'):
command = 'dir {0}/{1}'.format(file_system, dst)
body = run_commands(module, {'command': command, 'output': 'text'})[0]
if 'No such file' in body:
return False
return True
else:
return md5sum_check(module, dst, file_system)


def verify_remote_file_exists(module, dst, file_system='bootflash:'):
Expand Down
6 changes: 5 additions & 1 deletion lib/ansible/modules/network/nxos/nxos_interface.py
Expand Up @@ -578,7 +578,11 @@ def map_config_to_obj(want, module):
obj['name'] = normalize_interface(interface_table.get('interface'))
obj['admin_state'] = interface_table.get('admin_state')
obj['description'] = interface_table.get('desc')
obj['mode'] = interface_table.get('eth_mode')
mode = interface_table.get('eth_mode')
if mode == 'access':
obj['mode'] = 'layer2'
else:
obj['mode'] = 'layer3'

objs.append(obj)

Expand Down
38 changes: 36 additions & 2 deletions lib/ansible/modules/network/nxos/nxos_linkagg.py
Expand Up @@ -139,6 +139,19 @@ def search_obj_in_list(group, lst):
return o


def get_diff(w, obj):
c = deepcopy(w)
o = deepcopy(obj)

if o['group'] == c['group'] and o.get('members') == c.get('members'):
if 'members' in o:
del o['members']
if 'members' in c:
del c['members']
diff_dict = dict(set(c.items()) - set(o.items()))
return diff_dict


def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
Expand Down Expand Up @@ -208,6 +221,18 @@ def map_obj_to_commands(updates, module):
commands.append('exit')
commands.append('interface {0}'.format(m))
commands.append('no channel-group {0}'.format(group))

else:
diff = get_diff(w, obj_in_have)
if diff and 'mode' in diff:
mode = diff['mode']
for i in members:
commands.append('interface {0}'.format(i))
if force:
commands.append('channel-group {0} force mode {1}'.format(group, mode))
else:
commands.append('channel-group {0} mode {1}'.format(group, mode))

if purge:
for h in have:
obj_in_want = search_obj_in_list(h['group'], want)
Expand Down Expand Up @@ -310,7 +335,7 @@ def parse_channel_options(module, output, channel):

group = channel['group']
obj['group'] = group
obj['min-links'] = parse_min_links(module, group)
obj['min_links'] = parse_min_links(module, group)
members = parse_members(output, group)
obj['members'] = members
for m in members:
Expand Down Expand Up @@ -389,7 +414,16 @@ def main():

if commands:
if not module.check_mode:
load_config(module, commands)
resp = load_config(module, commands, True)
if resp:
for item in resp:
if item:
if isinstance(item, dict):
err_str = item['clierror']
else:
err_str = item
if 'cannot add' in err_str.lower():
module.fail_json(msg=err_str)
result['changed'] = True

module.exit_json(**result)
Expand Down
4 changes: 1 addition & 3 deletions lib/ansible/modules/network/nxos/nxos_static_route.py
Expand Up @@ -280,13 +280,11 @@ def main():
candidate = CustomNetworkConfig(indent=3)
reconcile_candidate(module, candidate, prefix, w)

if candidate:
if not module.check_mode and candidate:
candidate = candidate.items_text()
load_config(module, candidate)
result['commands'].extend(candidate)
result['changed'] = True
else:
result['commands'] = []

module.exit_json(**result)

Expand Down
4 changes: 1 addition & 3 deletions lib/ansible/modules/network/nxos/nxos_system.py
Expand Up @@ -279,10 +279,8 @@ def parse_name_servers(config, vrf_config, vrfs):
objects = list()

match = re.search('^ip name-server (.+)$', config, re.M)
if match:
if match and 'use-vrf' not in match.group(1):
for addr in match.group(1).split(' '):
if addr == 'use-vrf' or addr in vrfs:
continue
objects.append({'server': addr, 'vrf': None})

for vrf, cfg in iteritems(vrf_config):
Expand Down

0 comments on commit 7b840f3

Please sign in to comment.