Switch from admin_password and fallback_admin_password to a list of passwords to try.

This commit is contained in:
C. R. Oldham 2015-11-17 15:20:15 -07:00
parent 4b382e977d
commit e42100cf8a
5 changed files with 165 additions and 89 deletions

View file

@ -30,30 +30,48 @@ 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)
log.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 = __pillar__['proxy']['admin_username']
password = __pillar__['proxy']['admin_password']
(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,

View file

@ -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)

View file

@ -1076,9 +1076,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 +1301,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

View file

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-,
'''
Dell FX2 chassis
================
@ -50,13 +50,25 @@ 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.
salt-proxy
----------
@ -186,42 +198,29 @@ 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
(username, password) = find_credentials()
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
check_grains = _grains(DETAILS['host'], username, password)
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 +261,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,6 +316,11 @@ 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']
@ -288,10 +328,8 @@ def chconfig(cmd, *args, **kwargs):
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():

View file

@ -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:
@ -452,43 +453,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']:
@ -506,7 +512,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