Refactor CLI prompt mode check for network plugins (#63945)
* Refactor CLI prompt mode check for network plugins * Move the CLI prompt mode check logic from action plugin to the controller side with the cliconf plugins. * This refactor also allows the network modules to initialise the persistent connection with remote device only when it is required. * Fix review comments
This commit is contained in:
parent
73526b9d65
commit
c27e47327f
33 changed files with 209 additions and 239 deletions
|
@ -305,8 +305,9 @@ def main():
|
|||
pc_data = to_text(init_data)
|
||||
try:
|
||||
conn.update_play_context(pc_data)
|
||||
conn.set_cli_prompt_context()
|
||||
except Exception as exc:
|
||||
# Only network_cli has update_play context, so missing this is
|
||||
# Only network_cli has update_play context and set_cli_prompt_context, so missing this is
|
||||
# not fatal e.g. netconf
|
||||
if isinstance(exc, ConnectionError) and getattr(exc, 'code', None) == -32601:
|
||||
pass
|
||||
|
|
|
@ -23,8 +23,6 @@ import sys
|
|||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.aireos.aireos import aireos_provider_spec
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
|
@ -69,14 +67,6 @@ class ActionModule(ActionNetworkModule):
|
|||
'msg': 'unable to open shell. Please see: ' +
|
||||
'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell'}
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
if to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
|
||||
task_vars['ansible_socket'] = socket_path
|
||||
|
||||
if self._play_context.become_method == 'enable':
|
||||
|
|
|
@ -23,8 +23,6 @@ import sys
|
|||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.aruba.aruba import aruba_provider_spec
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
|
@ -70,14 +68,6 @@ class ActionModule(ActionNetworkModule):
|
|||
'msg': 'unable to open shell. Please see: ' +
|
||||
'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell'}
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
if to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
|
||||
task_vars['ansible_socket'] = socket_path
|
||||
|
||||
if self._play_context.become_method == 'enable':
|
||||
|
|
|
@ -10,8 +10,6 @@ import sys
|
|||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.cloudengine.ce import ce_provider_spec
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
|
@ -85,22 +83,5 @@ class ActionModule(ActionNetworkModule):
|
|||
return {'failed': True, 'msg': "Connection type '%s' is not valid for '%s' module."
|
||||
% (self._play_context.connection, self._task.action)}
|
||||
|
||||
if (self._play_context.connection == 'local' and transport == 'cli' and self._task.action in CLI_SUPPORTED_MODULES) \
|
||||
or self._play_context.connection == 'network_cli':
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
prompt = to_text(out, errors='surrogate_then_replace').strip()
|
||||
while prompt.endswith(']'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
if prompt.startswith('[*'):
|
||||
conn.exec_command('clear configuration candidate')
|
||||
conn.exec_command('return')
|
||||
out = conn.get_prompt()
|
||||
prompt = to_text(out, errors='surrogate_then_replace').strip()
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -24,8 +24,6 @@ from ansible import constants as C
|
|||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.cnos.cnos import cnos_provider_spec
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.utils.display import Display
|
||||
|
||||
display = Display()
|
||||
|
@ -37,7 +35,6 @@ class ActionModule(ActionNetworkModule):
|
|||
del tmp # tmp no longer has any effect
|
||||
|
||||
self._config_module = True if self._task.action == 'cnos_config' else False
|
||||
socket_path = None
|
||||
|
||||
if self._play_context.connection == 'local':
|
||||
provider = load_provider(cnos_provider_spec, self._task.args)
|
||||
|
@ -67,18 +64,5 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
task_vars['ansible_socket'] = socket_path
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module or exec mode
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
if to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
display.vvvv('In Config mode, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
else:
|
||||
conn.send_command('enable')
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -25,8 +25,6 @@ import sys
|
|||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.network.dellos10.dellos10 import dellos10_provider_spec
|
||||
|
@ -41,7 +39,6 @@ class ActionModule(ActionNetworkModule):
|
|||
del tmp # tmp no longer has any effect
|
||||
|
||||
self._config_module = True if self._task.action == 'dellos10_config' else False
|
||||
socket_path = None
|
||||
|
||||
if self._play_context.connection == 'network_cli':
|
||||
provider = self._task.args.get('provider', {})
|
||||
|
@ -77,17 +74,5 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
task_vars['ansible_socket'] = socket_path
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
while to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
out = conn.get_prompt()
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -25,8 +25,6 @@ import sys
|
|||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.dellos6.dellos6 import dellos6_provider_spec
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
|
@ -77,17 +75,5 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
task_vars['ansible_socket'] = socket_path
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
while to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
out = conn.get_prompt()
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -25,8 +25,6 @@ import sys
|
|||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.network.dellos9.dellos9 import dellos9_provider_spec
|
||||
|
@ -77,17 +75,5 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
task_vars['ansible_socket'] = socket_path
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
while to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
out = conn.get_prompt()
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -24,8 +24,6 @@ from ansible import constants as C
|
|||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.enos.enos import enos_provider_spec
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.utils.display import Display
|
||||
|
||||
display = Display()
|
||||
|
@ -37,7 +35,7 @@ class ActionModule(ActionNetworkModule):
|
|||
del tmp # tmp no longer has any effect
|
||||
|
||||
self._config_module = True if self._task.action == 'enos_config' else False
|
||||
socket_path = None
|
||||
|
||||
if self._play_context.connection == 'local':
|
||||
provider = load_provider(enos_provider_spec, self._task.args)
|
||||
pc = copy.deepcopy(self._play_context)
|
||||
|
@ -66,18 +64,5 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
task_vars['ansible_socket'] = socket_path
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module or exec mode
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
if to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
display.vvvv('In Config mode, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
else:
|
||||
conn.send_command('enable')
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -23,8 +23,6 @@ import sys
|
|||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.module_utils.network.eos.eos import eos_provider_spec
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
|
@ -40,7 +38,6 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
module_name = self._task.action.split('.')[-1]
|
||||
self._config_module = True if module_name == 'eos_config' else False
|
||||
socket_path = None
|
||||
|
||||
if self._play_context.connection in ('network_cli', 'httpapi'):
|
||||
provider = self._task.args.get('provider', {})
|
||||
|
@ -90,19 +87,6 @@ class ActionModule(ActionNetworkModule):
|
|||
else:
|
||||
return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection}
|
||||
|
||||
if (self._play_context.connection == 'local' and transport == 'cli') or self._play_context.connection == 'network_cli':
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
while '(config' in to_text(out, errors='surrogate_then_replace').strip():
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('abort')
|
||||
out = conn.get_prompt()
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
||||
|
|
|
@ -19,12 +19,9 @@
|
|||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
import sys
|
||||
import copy
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection, ConnectionError
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.network.ios.ios import ios_provider_spec
|
||||
|
@ -40,7 +37,6 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
module_name = self._task.action.split('.')[-1]
|
||||
self._config_module = True if module_name == 'ios_config' else False
|
||||
socket_path = None
|
||||
|
||||
if self._play_context.connection == 'network_cli':
|
||||
provider = self._task.args.get('provider', {})
|
||||
|
@ -79,19 +75,5 @@ class ActionModule(ActionNetworkModule):
|
|||
else:
|
||||
return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection}
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
try:
|
||||
out = conn.get_prompt()
|
||||
if re.search(r'config.*\)#', to_text(out, errors='surrogate_then_replace').strip()):
|
||||
display.vvvv('wrong context, sending end to device', self._play_context.remote_addr)
|
||||
conn.send_command('end')
|
||||
except ConnectionError as exc:
|
||||
return {'failed': True, 'msg': to_text(exc)}
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -22,9 +22,6 @@ __metaclass__ = type
|
|||
import sys
|
||||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.module_utils.network.iosxr.iosxr import iosxr_provider_spec
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
|
@ -40,7 +37,6 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
module_name = self._task.action.split('.')[-1]
|
||||
self._config_module = True if module_name == 'iosxr_config' else False
|
||||
socket_path = None
|
||||
force_cli = module_name in ('iosxr_netconf', 'iosxr_config', 'iosxr_command', 'iosxr_facts')
|
||||
|
||||
if self._play_context.connection == 'local':
|
||||
|
@ -86,18 +82,5 @@ class ActionModule(ActionNetworkModule):
|
|||
else:
|
||||
return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection}
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if (self._play_context.connection == 'local' and pc.connection == 'network_cli') or self._play_context.connection == 'network_cli':
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
while to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('abort')
|
||||
out = conn.get_prompt()
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -22,9 +22,6 @@ __metaclass__ = type
|
|||
import sys
|
||||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection, ConnectionError
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.network.ironware.ironware import ironware_provider_spec
|
||||
|
@ -39,7 +36,6 @@ class ActionModule(ActionNetworkModule):
|
|||
del tmp # tmp no longer has any effect
|
||||
|
||||
self._config_module = True if self._task.action == 'ironware_config' else False
|
||||
socket_path = None
|
||||
|
||||
if self._play_context.connection == 'network_cli':
|
||||
provider = self._task.args.get('provider', {})
|
||||
|
@ -78,20 +74,5 @@ class ActionModule(ActionNetworkModule):
|
|||
else:
|
||||
return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection}
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
try:
|
||||
out = conn.get_prompt()
|
||||
while to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
out = conn.get_prompt()
|
||||
except ConnectionError as exc:
|
||||
return {'failed': True, 'msg': to_text(exc)}
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -22,8 +22,6 @@ __metaclass__ = type
|
|||
import sys
|
||||
import copy
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.network.junos.junos import junos_provider_spec
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
|
@ -41,7 +39,6 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
module_name = self._task.action.split('.')[-1]
|
||||
self._config_module = True if module_name == 'junos_config' else False
|
||||
socket_path = None
|
||||
|
||||
if self._play_context.connection == 'local':
|
||||
provider = load_provider(junos_provider_spec, self._task.args)
|
||||
|
@ -94,18 +91,5 @@ class ActionModule(ActionNetworkModule):
|
|||
"Please see https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html"
|
||||
% (self._play_context.connection, module_name)}
|
||||
|
||||
if (self._play_context.connection == 'local' and pc.connection == 'network_cli') or self._play_context.connection == 'network_cli':
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
while to_text(out, errors='surrogate_then_replace').strip().endswith('#'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
out = conn.get_prompt()
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -24,8 +24,6 @@ import re
|
|||
import sys
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.network.nxos.nxos import nxos_provider_spec
|
||||
|
@ -56,8 +54,6 @@ class ActionModule(ActionNetworkModule):
|
|||
self._task.args['username'] = self._play_context.connection_user
|
||||
|
||||
if module_name == 'nxos_install_os':
|
||||
persistent_command_timeout = 0
|
||||
persistent_connect_timeout = 0
|
||||
connection = self._connection
|
||||
if connection.transport == 'local':
|
||||
persistent_command_timeout = C.PERSISTENT_COMMAND_TIMEOUT
|
||||
|
@ -124,21 +120,6 @@ class ActionModule(ActionNetworkModule):
|
|||
else:
|
||||
return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection}
|
||||
|
||||
if (self._play_context.connection == 'local' and transport == 'cli') or self._play_context.connection == 'network_cli':
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
# Match prompts ending in )# except those with (maint-mode)#
|
||||
config_prompt = re.compile(r'^.*\((?!maint-mode).*\)#$')
|
||||
out = conn.get_prompt()
|
||||
while config_prompt.match(to_text(out, errors='surrogate_then_replace').strip()):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit')
|
||||
out = conn.get_prompt()
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
||||
|
|
|
@ -22,10 +22,7 @@ __metaclass__ = type
|
|||
import sys
|
||||
import copy
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.network.vyos.vyos import vyos_provider_spec
|
||||
from ansible.utils.display import Display
|
||||
|
@ -40,7 +37,6 @@ class ActionModule(ActionNetworkModule):
|
|||
|
||||
module_name = self._task.action.split('.')[-1]
|
||||
self._config_module = True if module_name == 'vyos_config' else False
|
||||
socket_path = None
|
||||
|
||||
if self._play_context.connection == 'network_cli':
|
||||
provider = self._task.args.get('provider', {})
|
||||
|
@ -75,16 +71,5 @@ class ActionModule(ActionNetworkModule):
|
|||
else:
|
||||
return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection}
|
||||
|
||||
# make sure we are in the right cli context which should be
|
||||
# enable mode and not config module
|
||||
if socket_path is None:
|
||||
socket_path = self._connection.socket_path
|
||||
|
||||
conn = Connection(socket_path)
|
||||
out = conn.get_prompt()
|
||||
if to_text(out, errors='surrogate_then_replace').strip().endswith('#'):
|
||||
display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr)
|
||||
conn.send_command('exit discard')
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
return result
|
||||
|
|
|
@ -447,3 +447,31 @@ class CliconfBase(AnsiblePlugin):
|
|||
|
||||
if replace and not operations.get('supports_replace', False):
|
||||
raise ValueError("configuration replace is not supported")
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Ensure the command prompt on device is in right mode
|
||||
:return: None
|
||||
"""
|
||||
pass
|
||||
|
||||
def _update_cli_prompt_context(self, config_context=None, exit_command='exit'):
|
||||
"""
|
||||
Update the cli prompt context to ensure it is in operational mode
|
||||
:param config_context: It is string value to identify if the current cli prompt ends with config mode prompt
|
||||
:param exit_command: Command to execute to exit the config mode
|
||||
:return: None
|
||||
"""
|
||||
out = self._connection.get_prompt()
|
||||
if out is None:
|
||||
raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received'
|
||||
u' response window: %s' % self._connection._last_recv_window)
|
||||
|
||||
while True:
|
||||
out = to_text(out, errors='surrogate_then_replace').strip()
|
||||
if config_context and out.endswith(config_context):
|
||||
self._connection.queue_message('vvvv', 'wrong context, sending exit to device')
|
||||
self.send_command(exit_command)
|
||||
out = self._connection.get_prompt()
|
||||
else:
|
||||
break
|
||||
|
|
|
@ -34,6 +34,7 @@ import json
|
|||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase, enable_mode
|
||||
|
@ -85,3 +86,11 @@ class Cliconf(CliconfBase):
|
|||
def get_capabilities(self):
|
||||
result = super(Cliconf, self).get_capabilities()
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context=')#')
|
||||
|
|
|
@ -86,3 +86,11 @@ class Cliconf(CliconfBase):
|
|||
def get_capabilities(self):
|
||||
result = super(Cliconf, self).get_capabilities()
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context=')#')
|
||||
|
|
|
@ -34,6 +34,7 @@ import json
|
|||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase, enable_mode
|
||||
|
@ -98,3 +99,24 @@ class Cliconf(CliconfBase):
|
|||
def get_capabilities(self):
|
||||
result = super(Cliconf, self).get_capabilities()
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
out = self._connection.get_prompt()
|
||||
|
||||
if out is None:
|
||||
raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received'
|
||||
u' response window: %s' % self._connection._last_recv_window)
|
||||
|
||||
prompt = to_text(out, errors='surrogate_then_replace').strip()
|
||||
while prompt.endswith(']'):
|
||||
self._connection.queue_message('vvvv', 'wrong context, sending return to device')
|
||||
if prompt.startswith('[*'):
|
||||
self._connection.exec_command('clear configuration candidate')
|
||||
self._connection.exec_command('return')
|
||||
out = self._connection.get_prompt()
|
||||
prompt = to_text(out, errors='surrogate_then_replace').strip()
|
||||
|
|
|
@ -29,7 +29,7 @@ version_added: 2.6
|
|||
import re
|
||||
import json
|
||||
|
||||
from itertools import chain
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.module_utils.common._collections_compat import Mapping
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
|
@ -116,3 +116,21 @@ class Cliconf(CliconfBase):
|
|||
def get_capabilities(self):
|
||||
result = super(Cliconf, self).get_capabilities()
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
out = self._connection.get_prompt()
|
||||
|
||||
if out is None:
|
||||
raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received'
|
||||
u' response window: %s' % self._connection._last_recv_window)
|
||||
|
||||
if to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
self._connection.queue_message('vvvv', 'In Config mode, sending exit to device')
|
||||
self._connection.send_command('exit')
|
||||
else:
|
||||
self._connection.send_command('enable')
|
||||
|
|
|
@ -113,3 +113,11 @@ class Cliconf(CliconfBase):
|
|||
responses.append(out)
|
||||
|
||||
return responses
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context=')#')
|
||||
|
|
|
@ -113,3 +113,11 @@ class Cliconf(CliconfBase):
|
|||
responses.append(out)
|
||||
|
||||
return responses
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context=')#')
|
||||
|
|
|
@ -113,3 +113,11 @@ class Cliconf(CliconfBase):
|
|||
responses.append(out)
|
||||
|
||||
return responses
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context=')#')
|
||||
|
|
|
@ -31,7 +31,8 @@ import json
|
|||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase, enable_mode
|
||||
|
||||
|
@ -83,3 +84,21 @@ class Cliconf(CliconfBase):
|
|||
def get_capabilities(self):
|
||||
result = super(Cliconf, self).get_capabilities()
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
out = self._connection.get_prompt()
|
||||
|
||||
if out is None:
|
||||
raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received'
|
||||
u' response window: %s' % self._connection._last_recv_window)
|
||||
|
||||
if to_text(out, errors='surrogate_then_replace').strip().endswith(')#'):
|
||||
self._connection.queue_message('vvvv', 'In Config mode, sending exit to device')
|
||||
self._connection.send_command('exit')
|
||||
else:
|
||||
self._connection.send_command('enable')
|
||||
|
|
|
@ -286,6 +286,14 @@ class Cliconf(CliconfBase):
|
|||
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context='(config', exit_command='abort')
|
||||
|
||||
def _get_command_with_output(self, command, output):
|
||||
options_values = self.get_option_values()
|
||||
if output not in options_values['output']:
|
||||
|
|
|
@ -34,8 +34,6 @@ import re
|
|||
import time
|
||||
import json
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.common._collections_compat import Mapping
|
||||
|
@ -333,6 +331,22 @@ class Cliconf(CliconfBase):
|
|||
else:
|
||||
return 'full'
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
out = self._connection.get_prompt()
|
||||
|
||||
if out is None:
|
||||
raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received'
|
||||
u' response window: %s' % self._connection._last_recv_window)
|
||||
|
||||
if re.search(r'config.*\)#', to_text(out, errors='surrogate_then_replace').strip()):
|
||||
self._connection.queue_message('vvvv', 'wrong context, sending end to device')
|
||||
self._connection.send_command('end')
|
||||
|
||||
def _extract_banners(self, config):
|
||||
banners = {}
|
||||
banner_cmds = re.findall(r'^banner (\w+)', config, re.M)
|
||||
|
|
|
@ -266,3 +266,11 @@ class Cliconf(CliconfBase):
|
|||
result['device_operations'] = self.get_device_operations()
|
||||
result.update(self.get_option_values())
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context=')#', exit_command='abort')
|
||||
|
|
|
@ -86,3 +86,11 @@ class Cliconf(CliconfBase):
|
|||
def get_capabilities(self):
|
||||
result = super(Cliconf, self).get_capabilities()
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context=')#')
|
||||
|
|
|
@ -246,6 +246,14 @@ class Cliconf(CliconfBase):
|
|||
result.update(self.get_option_values())
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context='#')
|
||||
|
||||
def _get_command_with_output(self, command, output):
|
||||
options_values = self.get_option_values()
|
||||
if output not in options_values['output']:
|
||||
|
|
|
@ -258,6 +258,24 @@ class Cliconf(CliconfBase):
|
|||
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli context
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
out = self._connection.get_prompt()
|
||||
if out is None:
|
||||
raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received'
|
||||
u' response window: %s' % self._connection._last_recv_window)
|
||||
# Match prompts ending in )# except those with (maint-mode)#
|
||||
config_prompt = re.compile(r'^.*\((?!maint-mode).*\)#$')
|
||||
|
||||
while config_prompt.match(to_text(out, errors='surrogate_then_replace').strip()):
|
||||
self._connection.queue_message('vvvv', 'wrong context, sending exit to device')
|
||||
self._connection.send_command('exit')
|
||||
out = self._connection.get_prompt()
|
||||
|
||||
def _get_command_with_output(self, command, output):
|
||||
options_values = self.get_option_values()
|
||||
if output not in options_values['output']:
|
||||
|
|
|
@ -266,3 +266,11 @@ class Cliconf(CliconfBase):
|
|||
result['device_operations'] = self.get_device_operations()
|
||||
result.update(self.get_option_values())
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context='#', exit_command='exit discard')
|
||||
|
|
|
@ -314,6 +314,7 @@ class Connection(NetworkConnectionBase):
|
|||
self._last_response = None
|
||||
self._history = list()
|
||||
self._command_response = None
|
||||
self._last_recv_window = None
|
||||
|
||||
self._terminal = None
|
||||
self.cliconf = None
|
||||
|
@ -541,6 +542,7 @@ class Connection(NetworkConnectionBase):
|
|||
recv.seek(offset)
|
||||
|
||||
window = self._strip(recv.read())
|
||||
self._last_recv_window = window
|
||||
window_count += 1
|
||||
|
||||
if prompts and not handled:
|
||||
|
|
Loading…
Reference in a new issue