[stable-2.9] Stabilize nxos initiated copy for nxos_file_copy plugin (#62355)
* Retry spawn connection on failure * Add debug logs * Additional debug logs * Close session before respawn attempt * More debug info and increase loops * Remove debug info and reset error dict on reconnect * Add epdb debuger * Add epdb debuger * Add epdb debuger * Wait before sending password and close pexpect session * Fix comment typo * Scrub error logs * Scrub error logs * Add more specific initial connect pattern * Fix shippable errors * Dont make remote_scp_server_password a hard requirement * Add saftey check
This commit is contained in:
parent
fd7e156ccd
commit
97b15e9f0c
2 changed files with 44 additions and 14 deletions
|
@ -21,6 +21,7 @@ import copy
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
from ansible.module_utils._text import to_text, to_bytes
|
from ansible.module_utils._text import to_text, to_bytes
|
||||||
|
@ -98,14 +99,12 @@ class ActionModule(ActionBase):
|
||||||
raise AnsibleError('Playbook parameter <remote_scp_server> required when <file_pull> is True')
|
raise AnsibleError('Playbook parameter <remote_scp_server> required when <file_pull> is True')
|
||||||
|
|
||||||
if playvals['remote_scp_server'] or \
|
if playvals['remote_scp_server'] or \
|
||||||
playvals['remote_scp_server_user'] or \
|
playvals['remote_scp_server_user']:
|
||||||
playvals['remote_scp_server_password']:
|
|
||||||
|
|
||||||
if None in (playvals['remote_scp_server'],
|
if None in (playvals['remote_scp_server'],
|
||||||
playvals['remote_scp_server_user'],
|
playvals['remote_scp_server_user']):
|
||||||
playvals['remote_scp_server_password']):
|
params = '<remote_scp_server>, <remote_scp_server_user>'
|
||||||
params = '<remote_scp_server>, <remote_scp_server_user>, ,remote_scp_server_password>'
|
raise AnsibleError('Playbook parameters {0} must be set together'.format(params))
|
||||||
raise AnsibleError('Playbook parameters {0} must all be set together'.format(params))
|
|
||||||
|
|
||||||
return playvals
|
return playvals
|
||||||
|
|
||||||
|
@ -285,8 +284,8 @@ class ActionModule(ActionBase):
|
||||||
# 14) - Copy completed without issues
|
# 14) - Copy completed without issues
|
||||||
# 15) - nxos_router_prompt#
|
# 15) - nxos_router_prompt#
|
||||||
# 16) - pexpect timeout
|
# 16) - pexpect timeout
|
||||||
possible_outcomes = ['yes',
|
possible_outcomes = [r'sure you want to continue connecting \(yes/no\)\? ',
|
||||||
'(?i)Password',
|
'(?i)Password: ',
|
||||||
'file existing with this name',
|
'file existing with this name',
|
||||||
'timed out',
|
'timed out',
|
||||||
'(?i)No space.*#',
|
'(?i)No space.*#',
|
||||||
|
@ -341,27 +340,41 @@ class ActionModule(ActionBase):
|
||||||
# Spawn pexpect connection to NX-OS device.
|
# Spawn pexpect connection to NX-OS device.
|
||||||
nxos_session = pexpect.spawn('ssh ' + nxos_username + '@' + nxos_hostname + ' -p' + str(port))
|
nxos_session = pexpect.spawn('ssh ' + nxos_username + '@' + nxos_hostname + ' -p' + str(port))
|
||||||
# There might be multiple user_response_required prompts or intermittent timeouts
|
# There might be multiple user_response_required prompts or intermittent timeouts
|
||||||
# spawning the expect session so loop up to 5 times during the spwan process.
|
# spawning the expect session so loop up to 24 times during the spawn process.
|
||||||
for connect_attempt in range(6):
|
max_attempts = 24
|
||||||
|
for connect_attempt in range(max_attempts):
|
||||||
outcome = process_outcomes(nxos_session)
|
outcome = process_outcomes(nxos_session)
|
||||||
if outcome['user_response_required']:
|
if outcome['user_response_required']:
|
||||||
nxos_session.sendline('yes')
|
nxos_session.sendline('yes')
|
||||||
continue
|
continue
|
||||||
if outcome['password_prompt_detected']:
|
if outcome['password_prompt_detected']:
|
||||||
|
time.sleep(3)
|
||||||
nxos_session.sendline(nxos_password)
|
nxos_session.sendline(nxos_password)
|
||||||
continue
|
continue
|
||||||
if outcome['final_prompt_detected']:
|
if outcome['final_prompt_detected']:
|
||||||
break
|
break
|
||||||
if outcome['error'] or outcome['expect_timeout']:
|
if outcome['error'] or outcome['expect_timeout']:
|
||||||
|
# Error encountered, try to spawn expect session n more times up to max_attempts - 1
|
||||||
|
if connect_attempt < max_attempts:
|
||||||
|
outcome['error'] = False
|
||||||
|
outcome['expect_timeout'] = False
|
||||||
|
nxos_session.close()
|
||||||
|
nxos_session = pexpect.spawn('ssh ' + nxos_username + '@' + nxos_hostname + ' -p' + str(port))
|
||||||
|
continue
|
||||||
self.results['failed'] = True
|
self.results['failed'] = True
|
||||||
|
outcome['error_data'] = re.sub(nxos_password, '', outcome['error_data'])
|
||||||
self.results['error_data'] = 'Failed to spawn expect session! ' + outcome['error_data']
|
self.results['error_data'] = 'Failed to spawn expect session! ' + outcome['error_data']
|
||||||
|
nxos_session.close()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# The before property will contain all text up to the expected string pattern.
|
# The before property will contain all text up to the expected string pattern.
|
||||||
# The after string will contain the text that was matched by the expected pattern.
|
# The after string will contain the text that was matched by the expected pattern.
|
||||||
msg = 'After {0} attempts, failed to spawn pexpect session to {1}'
|
msg = 'After {0} attempts, failed to spawn pexpect session to {1}'
|
||||||
msg += 'BEFORE: {2}, AFTER: {3}'
|
msg += 'BEFORE: {2}, AFTER: {3}'
|
||||||
raise AnsibleError(msg.format(connect_attempt, nxos_hostname, nxos_session.before, nxos_session.before))
|
error_msg = msg.format(connect_attempt, nxos_hostname, nxos_session.before, nxos_session.after)
|
||||||
|
re.sub(nxos_password, '', error_msg)
|
||||||
|
nxos_session.close()
|
||||||
|
raise AnsibleError(error_msg)
|
||||||
|
|
||||||
# Create local file directory under NX-OS filesystem if
|
# Create local file directory under NX-OS filesystem if
|
||||||
# local_file_directory playbook parameter is set.
|
# local_file_directory playbook parameter is set.
|
||||||
|
@ -375,6 +388,7 @@ class ActionModule(ActionBase):
|
||||||
if outcome['error'] or outcome['expect_timeout']:
|
if outcome['error'] or outcome['expect_timeout']:
|
||||||
self.results['mkdir_cmd'] = mkdir_cmd
|
self.results['mkdir_cmd'] = mkdir_cmd
|
||||||
self.results['failed'] = True
|
self.results['failed'] = True
|
||||||
|
outcome['error_data'] = re.sub(nxos_password, '', outcome['error_data'])
|
||||||
self.results['error_data'] = outcome['error_data']
|
self.results['error_data'] = outcome['error_data']
|
||||||
return
|
return
|
||||||
local_dir_root += each + '/'
|
local_dir_root += each + '/'
|
||||||
|
@ -389,7 +403,12 @@ class ActionModule(ActionBase):
|
||||||
nxos_session.sendline('yes')
|
nxos_session.sendline('yes')
|
||||||
continue
|
continue
|
||||||
if outcome['password_prompt_detected']:
|
if outcome['password_prompt_detected']:
|
||||||
nxos_session.sendline(self.playvals['remote_scp_server_password'])
|
if self.playvals.get('remote_scp_server_password'):
|
||||||
|
nxos_session.sendline(self.playvals['remote_scp_server_password'])
|
||||||
|
else:
|
||||||
|
err_msg = 'Remote scp server {0} requires a password.'.format(rserver)
|
||||||
|
err_msg += ' Set the <remote_scp_server_password> playbook parameter or configure nxos device for passwordless scp'
|
||||||
|
raise AnsibleError(err_msg)
|
||||||
continue
|
continue
|
||||||
if outcome['existing_file_with_same_name']:
|
if outcome['existing_file_with_same_name']:
|
||||||
nxos_session.sendline('y')
|
nxos_session.sendline('y')
|
||||||
|
@ -399,14 +418,25 @@ class ActionModule(ActionBase):
|
||||||
break
|
break
|
||||||
if outcome['error'] or outcome['expect_timeout']:
|
if outcome['error'] or outcome['expect_timeout']:
|
||||||
self.results['failed'] = True
|
self.results['failed'] = True
|
||||||
|
outcome['error_data'] = re.sub(nxos_password, '', outcome['error_data'])
|
||||||
|
if self.playvals.get('remote_scp_server_password'):
|
||||||
|
outcome['error_data'] = re.sub(self.playvals['remote_scp_server_password'], '', outcome['error_data'])
|
||||||
self.results['error_data'] = outcome['error_data']
|
self.results['error_data'] = outcome['error_data']
|
||||||
|
nxos_session.close()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# The before property will contain all text up to the expected string pattern.
|
# The before property will contain all text up to the expected string pattern.
|
||||||
# The after string will contain the text that was matched by the expected pattern.
|
# The after string will contain the text that was matched by the expected pattern.
|
||||||
msg = 'After {0} attempts, failed to copy file to {1}'
|
msg = 'After {0} attempts, failed to copy file to {1}'
|
||||||
msg += 'BEFORE: {2}, AFTER: {3}, CMD: {4}'
|
msg += 'BEFORE: {2}, AFTER: {3}, CMD: {4}'
|
||||||
raise AnsibleError(msg.format(copy_attempt, nxos_hostname, nxos_session.before, nxos_session.before, copy_cmd))
|
error_msg = msg.format(copy_attempt, nxos_hostname, nxos_session.before, nxos_session.before, copy_cmd)
|
||||||
|
re.sub(nxos_password, '', error_msg)
|
||||||
|
if self.playvals.get('remote_scp_server_password'):
|
||||||
|
re.sub(self.playvals['remote_scp_server_password'], '', error_msg)
|
||||||
|
nxos_session.close()
|
||||||
|
raise AnsibleError(error_msg)
|
||||||
|
|
||||||
|
nxos_session.close()
|
||||||
|
|
||||||
def file_pull(self):
|
def file_pull(self):
|
||||||
local_file = self.playvals['local_file']
|
local_file = self.playvals['local_file']
|
||||||
|
|
|
@ -60,6 +60,6 @@
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
that:
|
that:
|
||||||
- result is search('Playbook parameters <remote_scp_server>, <remote_scp_server_user>, ,remote_scp_server_password> must all be set together')
|
- result is search('Playbook parameters <remote_scp_server>, <remote_scp_server_user> must be set together')
|
||||||
|
|
||||||
- debug: msg="END nxos_file_copy input_validation test"
|
- debug: msg="END nxos_file_copy input_validation test"
|
||||||
|
|
Loading…
Reference in a new issue