|
|
@ -21,10 +21,7 @@ import copy
|
|
|
|
import hashlib
|
|
|
|
import hashlib
|
|
|
|
import os
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import re
|
|
|
|
import sys
|
|
|
|
|
|
|
|
import time
|
|
|
|
import time
|
|
|
|
import traceback
|
|
|
|
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
@ -106,14 +103,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
|
|
|
|
|
|
|
|
|
|
|
@ -294,8 +289,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.*#',
|
|
|
@ -350,27 +345,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.
|
|
|
@ -384,6 +393,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 + '/'
|
|
|
@ -398,7 +408,12 @@ class ActionModule(ActionBase):
|
|
|
|
nxos_session.sendline('yes')
|
|
|
|
nxos_session.sendline('yes')
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
if outcome['password_prompt_detected']:
|
|
|
|
if outcome['password_prompt_detected']:
|
|
|
|
|
|
|
|
if self.playvals.get('remote_scp_server_password'):
|
|
|
|
nxos_session.sendline(self.playvals['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')
|
|
|
@ -408,14 +423,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']
|
|
|
|