mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 08:58:59 +00:00
Merge pull request #39896 from cloudflare/not-always-alive
Add always_alive option for napalm proxy
This commit is contained in:
commit
1e200b92e2
@ -95,6 +95,25 @@ def _filter_dict(input_dict, search_key, search_value):
|
||||
return output_dict
|
||||
|
||||
|
||||
def _explicit_close(napalm_device):
|
||||
'''
|
||||
Will explicitely close the config session with the network device,
|
||||
when running in a now-always-alive proxy minion or regular minion.
|
||||
This helper must be used in configuration-related functions,
|
||||
as the session is preserved and not closed before making any changes.
|
||||
'''
|
||||
if salt.utils.napalm.not_always_alive(__opts__):
|
||||
# force closing the configuration session
|
||||
# when running in a non-always-alive proxy
|
||||
# or regular minion
|
||||
try:
|
||||
napalm_device['DRIVER'].close()
|
||||
except Exception as err:
|
||||
log.error('Unable to close the temp connection with the device:')
|
||||
log.error(err)
|
||||
log.error('Please report.')
|
||||
|
||||
|
||||
def _config_logic(napalm_device,
|
||||
loaded_result,
|
||||
test=False,
|
||||
@ -131,7 +150,6 @@ def _config_logic(napalm_device,
|
||||
loaded_result.pop('out', '') # not needed
|
||||
|
||||
_loaded_res = loaded_result.get('result', False)
|
||||
|
||||
if not _loaded_res or test:
|
||||
# if unable to load the config (errors / warnings)
|
||||
# or in testing mode,
|
||||
@ -147,11 +165,13 @@ def _config_logic(napalm_device,
|
||||
loaded_result['result'] = False
|
||||
# make sure it notifies
|
||||
# that something went wrong
|
||||
_explicit_close(napalm_device)
|
||||
return loaded_result
|
||||
|
||||
loaded_result['comment'] += 'Configuration discarded.'
|
||||
# loaded_result['result'] = False not necessary
|
||||
# as the result can be true when test=True
|
||||
_explicit_close(napalm_device)
|
||||
return loaded_result
|
||||
|
||||
if not test and commit_config:
|
||||
@ -181,10 +201,11 @@ def _config_logic(napalm_device,
|
||||
else 'Unable to discard config.'
|
||||
loaded_result['result'] = False
|
||||
# notify if anything goes wrong
|
||||
_explicit_close(napalm_device)
|
||||
return loaded_result
|
||||
loaded_result['already_configured'] = True
|
||||
loaded_result['comment'] = 'Already configured.'
|
||||
|
||||
_explicit_close(napalm_device)
|
||||
return loaded_result
|
||||
|
||||
|
||||
@ -841,6 +862,15 @@ def load_config(filename=None,
|
||||
fun = 'load_merge_candidate'
|
||||
if replace:
|
||||
fun = 'load_replace_candidate'
|
||||
if salt.utils.napalm.not_always_alive(__opts__):
|
||||
# if a not-always-alive proxy
|
||||
# or regular minion
|
||||
# do not close the connection after loading the config
|
||||
# this will be handled in _config_logic
|
||||
# after running the other features:
|
||||
# compare_config, discard / commit
|
||||
# which have to be over the same session
|
||||
napalm_device['CLOSE'] = False # pylint: disable=undefined-variable
|
||||
_loaded = salt.utils.napalm.call(
|
||||
napalm_device, # pylint: disable=undefined-variable
|
||||
fun,
|
||||
@ -1207,6 +1237,15 @@ def load_template(template_name,
|
||||
fun = 'load_merge_candidate'
|
||||
if replace: # replace requested
|
||||
fun = 'load_replace_candidate'
|
||||
if salt.utils.napalm.not_always_alive(__opts__):
|
||||
# if a not-always-alive proxy
|
||||
# or regular minion
|
||||
# do not close the connection after loading the config
|
||||
# this will be handled in _config_logic
|
||||
# after running the other features:
|
||||
# compare_config, discard / commit
|
||||
# which have to be over the same session
|
||||
napalm_device['CLOSE'] = False # pylint: disable=undefined-variable
|
||||
_loaded = salt.utils.napalm.call(
|
||||
napalm_device, # pylint: disable=undefined-variable
|
||||
fun,
|
||||
@ -1228,12 +1267,21 @@ def load_template(template_name,
|
||||
'opts': __opts__ # inject opts content
|
||||
}
|
||||
)
|
||||
if salt.utils.napalm.not_always_alive(__opts__):
|
||||
# if a not-always-alive proxy
|
||||
# or regular minion
|
||||
# do not close the connection after loading the config
|
||||
# this will be handled in _config_logic
|
||||
# after running the other features:
|
||||
# compare_config, discard / commit
|
||||
# which have to be over the same session
|
||||
# so we'll set the CLOSE global explicitely as False
|
||||
napalm_device['CLOSE'] = False # pylint: disable=undefined-variable
|
||||
_loaded = salt.utils.napalm.call(
|
||||
napalm_device, # pylint: disable=undefined-variable
|
||||
'load_template',
|
||||
**load_templates_params
|
||||
)
|
||||
|
||||
return _config_logic(napalm_device, # pylint: disable=undefined-variable
|
||||
_loaded,
|
||||
test=test,
|
||||
|
@ -129,6 +129,9 @@ def alive(opts):
|
||||
|
||||
.. versionadded:: Nitrogen
|
||||
'''
|
||||
if salt.utils.napalm.not_always_alive(opts):
|
||||
return True # don't force reconnection for not-always alive proxies
|
||||
# or regular minion
|
||||
is_alive_ret = call('is_alive', **{})
|
||||
if not is_alive_ret.get('result', False):
|
||||
log.debug('[{proxyid}] Unable to execute `is_alive`: {comment}'.format(
|
||||
|
@ -19,6 +19,7 @@ from __future__ import absolute_import
|
||||
|
||||
import traceback
|
||||
import logging
|
||||
from functools import wraps
|
||||
log = logging.getLogger(__file__)
|
||||
|
||||
import salt.utils
|
||||
@ -44,6 +45,20 @@ def is_proxy(opts):
|
||||
return salt.utils.is_proxy() and opts.get('proxy', {}).get('proxytype') == 'napalm'
|
||||
|
||||
|
||||
def is_always_alive(opts):
|
||||
'''
|
||||
Is always alive required?
|
||||
'''
|
||||
return opts.get('proxy', {}).get('always_alive', True)
|
||||
|
||||
|
||||
def not_always_alive(opts):
|
||||
'''
|
||||
Should this proxy be always alive?
|
||||
'''
|
||||
return (is_proxy(opts) and not is_always_alive(opts)) or is_minion(opts)
|
||||
|
||||
|
||||
def is_minion(opts):
|
||||
'''
|
||||
Is this a NAPALM straight minion?
|
||||
@ -113,6 +128,7 @@ def call(napalm_device, method, *args, **kwargs):
|
||||
'''
|
||||
result = False
|
||||
out = None
|
||||
opts = napalm_device.get('__opts__', {})
|
||||
try:
|
||||
if not napalm_device.get('UP', False):
|
||||
raise Exception('not connected')
|
||||
@ -153,6 +169,13 @@ def call(napalm_device, method, *args, **kwargs):
|
||||
'comment': comment,
|
||||
'traceback': err_tb
|
||||
}
|
||||
finally:
|
||||
if opts and not_always_alive(opts) and napalm_device.get('CLOSE', True):
|
||||
# either running in a not-always-alive proxy
|
||||
# either running in a regular minion
|
||||
# close the connection when the call is over
|
||||
# unless the CLOSE is explicitely set as False
|
||||
napalm_device['DRIVER'].close()
|
||||
return {
|
||||
'out': out,
|
||||
'result': result,
|
||||
@ -172,7 +195,7 @@ def get_device(opts, salt_obj=None):
|
||||
device_dict = opts.get('proxy', {}) or opts.get('napalm', {})
|
||||
if salt_obj and not device_dict:
|
||||
# get the connection details from the opts
|
||||
device_dict = salt_obj['config.option']('napalm')
|
||||
device_dict = salt_obj['config.merge']('napalm')
|
||||
if not device_dict:
|
||||
# still not able to setup
|
||||
log.error('Incorrect minion config. Please specify at least the napalm driver name!')
|
||||
@ -183,8 +206,9 @@ def get_device(opts, salt_obj=None):
|
||||
network_device['PASSWORD'] = device_dict.get('passwd') or device_dict.get('password') or device_dict.get('pass')
|
||||
network_device['TIMEOUT'] = device_dict.get('timeout', 60)
|
||||
network_device['OPTIONAL_ARGS'] = device_dict.get('optional_args', {})
|
||||
network_device['ALWAYS_ALIVE'] = device_dict.get('always_alive', True)
|
||||
network_device['UP'] = False
|
||||
# get driver object form NAPALM
|
||||
# get driver object from NAPALM
|
||||
if 'config_lock' not in list(network_device['OPTIONAL_ARGS'].keys()):
|
||||
network_device['OPTIONAL_ARGS']['config_lock'] = False
|
||||
_driver_ = napalm_base.get_network_driver(network_device.get('DRIVER_NAME'))
|
||||
@ -226,20 +250,25 @@ def proxy_napalm_wrap(func):
|
||||
:param func:
|
||||
:return:
|
||||
'''
|
||||
@wraps(func)
|
||||
def func_wrapper(*args, **kwargs):
|
||||
wrapped_global_namespace = func.__globals__
|
||||
# get __proxy__ from func_globals
|
||||
# get __opts__ and __proxy__ from func_globals
|
||||
proxy = wrapped_global_namespace.get('__proxy__')
|
||||
|
||||
opts = wrapped_global_namespace.get('__opts__')
|
||||
# in any case, will inject the `napalm_device` global
|
||||
# the execution modules will make use of this variable from now on
|
||||
# previously they were accessing the device properties through the __proxy__ object
|
||||
if salt.utils.is_proxy():
|
||||
always_alive = opts.get('proxy', {}).get('always_alive', True)
|
||||
if salt.utils.is_proxy() and always_alive:
|
||||
# if it is running in a proxy and it's using the default always alive behaviour,
|
||||
# will get the cached copy of the network device
|
||||
wrapped_global_namespace['napalm_device'] = proxy['napalm.get_device']()
|
||||
else:
|
||||
# get __opts__ and __salt__ from func_globals
|
||||
opts = wrapped_global_namespace.get('__opts__')
|
||||
_salt_obj = wrapped_global_namespace.get('__salt__')
|
||||
elif salt.utils.is_proxy() and not always_alive:
|
||||
# if still proxy, but the user does not want the SSH session always alive
|
||||
# get a new device instance
|
||||
# which establishes a new connection
|
||||
# which is closed just before the call() function defined above returns
|
||||
if 'inherit_napalm_device' not in kwargs or ('inherit_napalm_device' in kwargs and
|
||||
not kwargs['inherit_napalm_device']):
|
||||
# try to open a new connection
|
||||
@ -247,8 +276,9 @@ def proxy_napalm_wrap(func):
|
||||
# for configuration management this is very important,
|
||||
# in order to make sure we are editing the same session.
|
||||
try:
|
||||
wrapped_global_namespace['napalm_device'] = get_device(opts, salt_obj=_salt_obj)
|
||||
wrapped_global_namespace['napalm_device'] = get_device(opts)
|
||||
except napalm_base.exceptions.ConnectionException as nce:
|
||||
log.error(nce)
|
||||
return '{base_msg}. See log for details.'.format(
|
||||
base_msg=str(nce.msg)
|
||||
)
|
||||
@ -260,5 +290,35 @@ def proxy_napalm_wrap(func):
|
||||
# as all actions must be issued within the same configuration session
|
||||
# otherwise we risk to open multiple sessions
|
||||
wrapped_global_namespace['napalm_device'] = kwargs['inherit_napalm_device']
|
||||
else:
|
||||
# if no proxy
|
||||
# thus it is running on a regular minion, directly on the network device
|
||||
# get __salt__ from func_globals
|
||||
_salt_obj = wrapped_global_namespace.get('__salt__')
|
||||
if 'inherit_napalm_device' not in kwargs or ('inherit_napalm_device' in kwargs and
|
||||
not kwargs['inherit_napalm_device']):
|
||||
# try to open a new connection
|
||||
# but only if the function does not inherit the napalm driver
|
||||
# for configuration management this is very important,
|
||||
# in order to make sure we are editing the same session.
|
||||
try:
|
||||
wrapped_global_namespace['napalm_device'] = get_device(opts, salt_obj=_salt_obj)
|
||||
except napalm_base.exceptions.ConnectionException as nce:
|
||||
log.error(nce)
|
||||
return '{base_msg}. See log for details.'.format(
|
||||
base_msg=str(nce.msg)
|
||||
)
|
||||
else:
|
||||
# in case the `inherit_napalm_device` is set
|
||||
# and it also has a non-empty value,
|
||||
# the global var `napalm_device` will be overriden.
|
||||
# this is extremely important for configuration-related features
|
||||
# as all actions must be issued within the same configuration session
|
||||
# otherwise we risk to open multiple sessions
|
||||
wrapped_global_namespace['napalm_device'] = kwargs['inherit_napalm_device']
|
||||
if not_always_alive(opts):
|
||||
# inject the __opts__ only when not always alive
|
||||
# otherwise, we don't want to overload the always-alive proxies
|
||||
wrapped_global_namespace['napalm_device']['__opts__'] = opts
|
||||
return func(*args, **kwargs)
|
||||
return func_wrapper
|
||||
|
Loading…
Reference in New Issue
Block a user