mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #28967 from cro/fx2_switch
Fix some issues with password changes
This commit is contained in:
commit
bcec8d8608
6 changed files with 194 additions and 100 deletions
|
@ -1202,7 +1202,7 @@ DEFAULT_PROXY_MINION_OPTS = {
|
|||
# salt.vt will have trouble with our forking model.
|
||||
# Proxies with non-persistent (mostly REST API) connections
|
||||
# can change this back to True
|
||||
'multiprocessing': False
|
||||
'multiprocessing': True
|
||||
}
|
||||
|
||||
# ----- Salt Cloud Configuration Defaults ----------------------------------->
|
||||
|
|
|
@ -30,28 +30,47 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def _find_credentials():
|
||||
'''
|
||||
Cycle through all the possible credentials and return the first one that
|
||||
works
|
||||
'''
|
||||
usernames = []
|
||||
usernames.append(__pillar__['proxy'].get('admin_username', 'root'))
|
||||
if 'fallback_admin_username' in __pillar__.get('proxy'):
|
||||
usernames.append(__pillar__['proxy'].get('fallback_admin_username'))
|
||||
|
||||
for u in usernames:
|
||||
for p in __pillar__['proxy']['passwords']:
|
||||
r = salt.modules.dracr.get_chassis_name(host=__pillar__['proxy']['host'],
|
||||
admin_username=u,
|
||||
admin_password=p)
|
||||
# Retcode will be present if the chassis_name call failed
|
||||
try:
|
||||
if r.get('retcode', None) is None:
|
||||
return (u, p)
|
||||
except AttributeError:
|
||||
# Then the above was a string, and we can return the username
|
||||
# and password
|
||||
return (u, p)
|
||||
|
||||
logger.debug('grains fx2._find_credentials found no valid credentials, using Dell default')
|
||||
return ('root', 'calvin')
|
||||
|
||||
|
||||
def _grains():
|
||||
'''
|
||||
Get the grains from the proxied device
|
||||
'''
|
||||
(username, password) = _find_credentials()
|
||||
r = salt.modules.dracr.system_info(host=__pillar__['proxy']['host'],
|
||||
admin_username=__pillar__['proxy']['admin_username'],
|
||||
admin_password=__pillar__['proxy']['admin_password'])
|
||||
admin_username=username,
|
||||
admin_password=password)
|
||||
|
||||
if r.get('retcode', 0) == 0:
|
||||
GRAINS_CACHE = r
|
||||
username = __pillar__['proxy']['admin_username']
|
||||
password = __pillar__['proxy']['admin_password']
|
||||
else:
|
||||
r = salt.modules.dracr.system_info(host=__pillar__['proxy']['host'],
|
||||
admin_username=__pillar__['proxy']['fallback_admin_username'],
|
||||
admin_password=__pillar__['proxy']['fallback_admin_password'])
|
||||
if r.get('retcode', 0) == 0:
|
||||
GRAINS_CACHE = r
|
||||
username = __pillar__['proxy']['fallback_admin_username']
|
||||
password = __pillar__['proxy']['fallback_admin_password']
|
||||
else:
|
||||
GRAINS_CACHE = {}
|
||||
GRAINS_CACHE = {}
|
||||
|
||||
GRAINS_CACHE.update(salt.modules.dracr.inventory(host=__pillar__['proxy']['host'],
|
||||
admin_username=username,
|
||||
|
|
|
@ -37,8 +37,9 @@ def __virtual__():
|
|||
|
||||
def cmd(cmd, *args, **kwargs):
|
||||
proxyprefix = __opts__['proxy']['proxytype']
|
||||
kwargs['admin_username'] = __proxy__[proxyprefix+'.admin_username']()
|
||||
kwargs['admin_password'] = __proxy__[proxyprefix+'.admin_password']()
|
||||
(username, password) = __proxy__[proxyprefix+'.find_credentials']()
|
||||
kwargs['admin_username'] = username
|
||||
kwargs['admin_password'] = password
|
||||
kwargs['host'] = __proxy__[proxyprefix+'.host']()
|
||||
proxycmd = __opts__['proxy']['proxytype'] + '.chconfig'
|
||||
return __proxy__[proxycmd](cmd, *args, **kwargs)
|
||||
|
|
|
@ -243,6 +243,12 @@ def network_info(host=None,
|
|||
|
||||
inv = inventory(host=host, admin_username=admin_username,
|
||||
admin_password=admin_password)
|
||||
if inv is None:
|
||||
cmd = {}
|
||||
cmd['retcode'] = -1
|
||||
cmd['stdout'] = 'Problem getting switch inventory'
|
||||
return cmd
|
||||
|
||||
if module not in inv.get('switch'):
|
||||
cmd = {}
|
||||
cmd['retcode'] = -1
|
||||
|
@ -1076,9 +1082,9 @@ def get_chassis_name(host=None, admin_username=None, admin_password=None):
|
|||
admin_username=root admin_password=secret
|
||||
|
||||
'''
|
||||
return system_info(host=host, admin_username=admin_username,
|
||||
admin_password=
|
||||
admin_password)['Chassis Information']['Chassis Name']
|
||||
return bare_rac_cmd('getchassisname', host=host,
|
||||
admin_username=admin_username,
|
||||
admin_password=admin_password)
|
||||
|
||||
|
||||
def inventory(host=None, admin_username=None, admin_password=None):
|
||||
|
@ -1301,3 +1307,16 @@ def get_general(cfg_sec, cfg_var, host=None,
|
|||
return ret['stdout']
|
||||
else:
|
||||
return ret
|
||||
|
||||
|
||||
def bare_rac_cmd(cmd, host=None,
|
||||
admin_username=None, admin_password=None):
|
||||
ret = __execute_ret('{0}'.format(cmd),
|
||||
host=host,
|
||||
admin_username=admin_username,
|
||||
admin_password=admin_password)
|
||||
|
||||
if ret['retcode'] == 0:
|
||||
return ret['stdout']
|
||||
else:
|
||||
return ret
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-,
|
||||
'''
|
||||
Dell FX2 chassis
|
||||
================
|
||||
|
@ -50,13 +50,46 @@ look like this:
|
|||
proxy:
|
||||
host: <ip or dns name of chassis controller>
|
||||
admin_username: <iDRAC username for the CMC, usually 'root'>
|
||||
admin_password: <iDRAC password. Dell default is 'calvin'>
|
||||
fallback_admin_username: <username to try if the first fails>
|
||||
passwords:
|
||||
- first_password
|
||||
- second_password
|
||||
- third-password
|
||||
proxytype: fx2
|
||||
|
||||
The ``proxytype`` line above is critical, it tells Salt which interface to load
|
||||
from the ``proxy`` directory in Salt's install hierarchy, or from ``/srv/salt/_proxy``
|
||||
on the salt-master (if you have created your own proxy module, for example).
|
||||
|
||||
The proxy integration will try the passwords listed in order. It is
|
||||
configured this way so you can have a regular password, a potential
|
||||
fallback password, and the third password can be the one you intend
|
||||
to change the chassis to use. This way, after it is changed, you
|
||||
should not need to restart the proxy minion--it should just pick up the
|
||||
third password in the list. You can then change pillar at will to
|
||||
move that password to the front and retire the unused ones.
|
||||
|
||||
Beware, many Dell CMC and iDRAC units are configured to lockout
|
||||
IP addresses or users after too many failed password attempts. This can
|
||||
generate user panic in the form of "I no longer know what the password is!!!".
|
||||
To mitigate panic try the web interface from a different IP, or setup a
|
||||
emergency administrator user in the CMC before doing a wholesale
|
||||
password rotation.
|
||||
|
||||
The automatic lockout can be disabled via Salt with the following:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt <cmc> chassis.cmd set_general cfgRacTuning cfgRacTuneIpBlkEnable 0
|
||||
|
||||
and then verified with
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt <cmc> chassis.cmd get_general cfgRacTuning cfgRacTuneIpBlkEnable
|
||||
|
||||
|
||||
|
||||
salt-proxy
|
||||
----------
|
||||
|
||||
|
@ -186,42 +219,27 @@ def init(opts):
|
|||
|
||||
DETAILS['host'] = opts['proxy']['host']
|
||||
|
||||
first_user = opts['proxy']['admin_username']
|
||||
first_password = opts['proxy']['admin_password']
|
||||
if 'fallback_admin_username' in opts['proxy'] \
|
||||
and 'fallback_admin_password' in opts['proxy']:
|
||||
fallback_available = True
|
||||
fallback_user = opts['proxy']['fallback_admin_username']
|
||||
fallback_password = opts['proxy']['fallback_admin_password']
|
||||
else:
|
||||
fallback_available = False
|
||||
|
||||
check_grains = _grains(DETAILS['host'], first_user, first_password)
|
||||
if check_grains:
|
||||
DETAILS['admin_username'] = opts['proxy']['admin_username']
|
||||
DETAILS['admin_password'] = opts['proxy']['admin_password']
|
||||
return True
|
||||
elif fallback_available and _grains(DETAILS['host'],
|
||||
fallback_user,
|
||||
fallback_password):
|
||||
log.info('Fallback credentials used'
|
||||
' to access chassis {0}'.format(opts['proxy']['host']))
|
||||
DETAILS['admin_username'] = opts['proxy']['fallback_admin_username']
|
||||
DETAILS['admin_password'] = opts['proxy']['fallback_admin_password']
|
||||
return True
|
||||
else:
|
||||
log.critical('Neither the primary nor the fallback credentials'
|
||||
' were able to access the chassis '
|
||||
' at {0}'.format(opts['proxy']['host']))
|
||||
return False
|
||||
(username, password) = find_credentials()
|
||||
|
||||
|
||||
def admin_username():
|
||||
return DETAILS['admin_username']
|
||||
'''
|
||||
Return the admin_username in the DETAILS dictionary, or root if there
|
||||
is none present
|
||||
'''
|
||||
return DETAILS.get('admin_username', 'root')
|
||||
|
||||
|
||||
def admin_password():
|
||||
return DETAILS['admin_password']
|
||||
'''
|
||||
Return the admin_password in the DETAILS dictionary, or 'calvin'
|
||||
(the Dell default) if there is none present
|
||||
'''
|
||||
if 'admin_password' not in DETAILS:
|
||||
log.info('proxy.fx2: No admin_password in DETAILS, returning Dell default')
|
||||
return 'calvin'
|
||||
|
||||
return DETAILS.get('admin_password', 'calvin')
|
||||
|
||||
|
||||
def host():
|
||||
|
@ -262,6 +280,42 @@ def grains_refresh():
|
|||
return grains()
|
||||
|
||||
|
||||
def find_credentials():
|
||||
'''
|
||||
Cycle through all the possible credentials and return the first one that
|
||||
works
|
||||
'''
|
||||
usernames = []
|
||||
usernames.append(__pillar__['proxy'].get('admin_username', 'root'))
|
||||
if 'fallback_admin_username' in __pillar__.get('proxy'):
|
||||
usernames.append(__pillar__['proxy'].get('fallback_admin_username'))
|
||||
|
||||
for u in usernames:
|
||||
for p in __pillar__['proxy']['passwords']:
|
||||
r = __salt__['dracr.get_chassis_name'](host=__pillar__['proxy']['host'],
|
||||
admin_username=u,
|
||||
admin_password=p)
|
||||
# Retcode will be present if the chassis_name call failed
|
||||
try:
|
||||
if r.get('retcode', None) is None:
|
||||
DETAILS['admin_username'] = u
|
||||
DETAILS['admin_password'] = p
|
||||
__opts__['proxy']['admin_username'] = u
|
||||
__opts__['proxy']['admin_password'] = p
|
||||
return (u, p)
|
||||
except AttributeError:
|
||||
# Then the above was a string, and we can return the username
|
||||
# and password
|
||||
DETAILS['admin_username'] = u
|
||||
DETAILS['admin_password'] = p
|
||||
__opts__['proxy']['admin_username'] = u
|
||||
__opts__['proxy']['admin_password'] = p
|
||||
return (u, p)
|
||||
|
||||
log.debug('proxy fx2.find_credentials found no valid credentials, using Dell default')
|
||||
return ('root', 'calvin')
|
||||
|
||||
|
||||
def chconfig(cmd, *args, **kwargs):
|
||||
'''
|
||||
This function is called by the :doc:`salt.modules.chassis.cmd </ref/modules/all/salt.modules.chassis>`
|
||||
|
@ -281,15 +335,20 @@ def chconfig(cmd, *args, **kwargs):
|
|||
kwargs.pop(k)
|
||||
|
||||
# Catch password reset
|
||||
if 'dracr.'+cmd not in __salt__:
|
||||
ret = {'retcode': -1, 'message': 'dracr.' + cmd + ' is not available'}
|
||||
else:
|
||||
ret = __salt__['dracr.'+cmd](*args, **kwargs)
|
||||
|
||||
if cmd == 'change_password':
|
||||
if 'username' in kwargs:
|
||||
__opts__['proxy']['admin_username'] = kwargs['username']
|
||||
DETAILS['admin_username'] = kwargs['username']
|
||||
if 'password' in kwargs:
|
||||
__opts__['proxy']['admin_password'] = kwargs['password']
|
||||
DETAILS['admin_password'] = kwargs['password']
|
||||
if 'dracr.'+cmd not in __salt__:
|
||||
return {'retcode': -1, 'message': 'dracr.' + cmd + ' is not available'}
|
||||
else:
|
||||
return __salt__['dracr.'+cmd](*args, **kwargs)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def ping():
|
||||
|
|
|
@ -31,9 +31,10 @@ data in pillar. Here's an example pillar structure:
|
|||
proxy:
|
||||
host: 10.27.20.18
|
||||
admin_username: root
|
||||
admin_password: super-secret
|
||||
fallback_admin_username: root
|
||||
fallback_admin_password: old-secret
|
||||
passwords:
|
||||
- super-secret
|
||||
- old-secret
|
||||
proxytype: fx2
|
||||
|
||||
chassis:
|
||||
|
@ -105,17 +106,6 @@ pillar stated above:
|
|||
- {{ k }}: {{ v }}
|
||||
{% endfor %}
|
||||
|
||||
{% for k, v in details['switches'].iteritems() %}
|
||||
standup-switches-{{ k }}:
|
||||
dellchassis.switch:
|
||||
- name: {{ k }}
|
||||
- ip: {{ v['ip'] }}
|
||||
- netmask: {{ v['netmask'] }}
|
||||
- gateway: {{ v['gateway'] }}
|
||||
- password: {{ v['password'] }}
|
||||
- snmp: {{ v['snmp'] }}
|
||||
{% endfor %}
|
||||
|
||||
blade_powercycle:
|
||||
dellchassis.chassis:
|
||||
- blade_power_states:
|
||||
|
@ -277,6 +267,7 @@ def chassis(name, chassis_name=None, password=None, datacenter=None,
|
|||
- server-3: powercycle
|
||||
'''
|
||||
ret = {'name': chassis_name,
|
||||
'chassis_name': chassis_name,
|
||||
'result': True,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
|
@ -487,43 +478,48 @@ def switch(name, ip=None, netmask=None, gateway=None, dhcp=None,
|
|||
'comment': ''}
|
||||
|
||||
current_nic = __salt__['chassis.cmd']('network_info', module=name)
|
||||
if current_nic.get('retcode', 0) != 0:
|
||||
ret['result'] = False
|
||||
ret['comment'] = current_nic['stdout']
|
||||
return ret
|
||||
try:
|
||||
if current_nic.get('retcode', 0) != 0:
|
||||
ret['result'] = False
|
||||
ret['comment'] = current_nic['stdout']
|
||||
return ret
|
||||
|
||||
if ip or netmask or gateway:
|
||||
if not ip:
|
||||
ip = current_nic['Network']['IP Address']
|
||||
if not netmask:
|
||||
ip = current_nic['Network']['Subnet Mask']
|
||||
if not gateway:
|
||||
ip = current_nic['Network']['Gateway']
|
||||
if ip or netmask or gateway:
|
||||
if not ip:
|
||||
ip = current_nic['Network']['IP Address']
|
||||
if not netmask:
|
||||
ip = current_nic['Network']['Subnet Mask']
|
||||
if not gateway:
|
||||
ip = current_nic['Network']['Gateway']
|
||||
|
||||
if current_nic['Network']['DHCP Enabled'] == '0' and dhcp:
|
||||
ret['changes'].update({'DHCP': {'Old': {'DHCP Enabled': current_nic['Network']['DHCP Enabled']},
|
||||
'New': {'DHCP Enabled': dhcp}}})
|
||||
if current_nic['Network']['DHCP Enabled'] == '0' and dhcp:
|
||||
ret['changes'].update({'DHCP': {'Old': {'DHCP Enabled': current_nic['Network']['DHCP Enabled']},
|
||||
'New': {'DHCP Enabled': dhcp}}})
|
||||
|
||||
if ((ip or netmask or gateway) and not dhcp and (ip != current_nic['Network']['IP Address'] or
|
||||
netmask != current_nic['Network']['Subnet Mask'] or
|
||||
gateway != current_nic['Network']['Gateway'])):
|
||||
ret['changes'].update({'IP': {'Old': current_nic['Network'],
|
||||
'New': {'IP Address': ip,
|
||||
'Subnet Mask': netmask,
|
||||
'Gateway': gateway}}})
|
||||
if ((ip or netmask or gateway) and not dhcp and (ip != current_nic['Network']['IP Address'] or
|
||||
netmask != current_nic['Network']['Subnet Mask'] or
|
||||
gateway != current_nic['Network']['Gateway'])):
|
||||
ret['changes'].update({'IP': {'Old': current_nic['Network'],
|
||||
'New': {'IP Address': ip,
|
||||
'Subnet Mask': netmask,
|
||||
'Gateway': gateway}}})
|
||||
|
||||
if password:
|
||||
if 'New' not in ret['changes']:
|
||||
ret['changes']['New'] = {}
|
||||
ret['changes']['New'].update({'Password': '*****'})
|
||||
if password:
|
||||
if 'New' not in ret['changes']:
|
||||
ret['changes']['New'] = {}
|
||||
ret['changes']['New'].update({'Password': '*****'})
|
||||
|
||||
if snmp:
|
||||
if 'New' not in ret['changes']:
|
||||
ret['changes']['New'] = {}
|
||||
ret['changes']['New'].update({'SNMP': '*****'})
|
||||
if snmp:
|
||||
if 'New' not in ret['changes']:
|
||||
ret['changes']['New'] = {}
|
||||
ret['changes']['New'].update({'SNMP': '*****'})
|
||||
|
||||
if ret['changes'] == {}:
|
||||
ret['comment'] = 'Switch ' + name + ' is already in desired state'
|
||||
if ret['changes'] == {}:
|
||||
ret['comment'] = 'Switch ' + name + ' is already in desired state'
|
||||
return ret
|
||||
except AttributeError:
|
||||
ret['changes'] = {}
|
||||
ret['comment'] = 'Something went wrong retrieving the switch details'
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
|
@ -541,7 +537,7 @@ def switch(name, ip=None, netmask=None, gateway=None, dhcp=None,
|
|||
password_ret = __salt__['chassis.cmd']('deploy_password', 'root', password, module=name)
|
||||
|
||||
if snmp:
|
||||
snmp_ret = __salt__['chassis.cmd']('deploy_snmp', password, module=name)
|
||||
snmp_ret = __salt__['chassis.cmd']('deploy_snmp', snmp, module=name)
|
||||
|
||||
if any([password_ret, snmp_ret, net_ret, dhcp_ret]) is False:
|
||||
ret['result'] = False
|
||||
|
|
Loading…
Add table
Reference in a new issue