Changes from Brian Coca's review of this module

These are all the code changes from Brian's review:
* change #! line
* rename "host" to "name" [keep as alias]
* make documentation clearer
* imports 1 per line
* use get_bin_path to find ssh-keygen
* key not actually required when removing host
This commit is contained in:
Matthew Vernon 2015-03-06 18:22:28 +00:00 committed by Matt Clay
parent e76ad916ef
commit 1e21e34e0f

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/python
""" """
Ansible module to manage the ssh known_hosts file. Ansible module to manage the ssh known_hosts file.
@ -28,15 +28,16 @@ description:
If you have a very large number of host keys to manage, you will find the M(template) module more useful. If you have a very large number of host keys to manage, you will find the M(template) module more useful.
version_added: "1.6" version_added: "1.6"
options: options:
host: name:
aliases: [ 'host' ]
description: description:
- The host to add or remove (must match a host specified in key) - The host to add or remove (must match a host specified in key)
required: true required: true
default: null default: null
key: key:
description: description:
- The SSH public host key, as a string (optional if removing the host) - The SSH public host key, as a string (required if state=present, optional when state=absent, in which case all keys for the host are removed)
required: true required: false
default: null default: null
path: path:
description: description:
@ -66,12 +67,15 @@ EXAMPLES = '''
# #
# Arguments # Arguments
# ========= # =========
# host = hostname whose key should be added # name = hostname whose key should be added (alias: host)
# key = line(s) to add to known_hosts file # key = line(s) to add to known_hosts file
# path = the known_hosts file to edit (default: ~/.ssh/known_hosts) # path = the known_hosts file to edit (default: ~/.ssh/known_hosts)
# state = absent|present (default: present) # state = absent|present (default: present)
import os,os.path,tempfile,errno import os
import os.path
import tempfile
import errno
def enforce_state(module, params): def enforce_state(module, params):
""" """
@ -83,9 +87,10 @@ def enforce_state(module, params):
port = params.get("port",None) port = params.get("port",None)
#expand the path parameter; otherwise module.add_path_info #expand the path parameter; otherwise module.add_path_info
#(called by exit_json) unhelpfully says the unexpanded path is absent. #(called by exit_json) unhelpfully says the unexpanded path is absent.
params["path"]=os.path.expanduser(params.get("path")) path = os.path.expanduser(params.get("path"))
path = params.get("path") state = params.get("state")
state = params.get("state","present") #Find the ssh-keygen binary
sshkeygen = module.get_bin_path("ssh-keygen",True)
#trailing newline in files gets lost, so re-add if necessary #trailing newline in files gets lost, so re-add if necessary
if key is not None and key[-1]!='\n': if key is not None and key[-1]!='\n':
@ -94,9 +99,9 @@ def enforce_state(module, params):
if key is None and state != "absent": if key is None and state != "absent":
module.fail_json(msg="No key specified when adding a host") module.fail_json(msg="No key specified when adding a host")
sanity_check(module,host,key) sanity_check(module,host,key,sshkeygen)
current,replace=search_for_host_key(module,host,key,path) current,replace=search_for_host_key(module,host,key,path,sshkeygen)
#We will change state if current==True & state!="present" #We will change state if current==True & state!="present"
#or current==False & state=="present" #or current==False & state=="present"
@ -109,7 +114,7 @@ def enforce_state(module, params):
#First, remove an extant entry if required #First, remove an extant entry if required
if replace==True or (current==True and state=="absent"): if replace==True or (current==True and state=="absent"):
module.run_command(['ssh-keygen','-R',host,'-f',path], module.run_command([sshkeygen,'-R',host,'-f',path],
check_rc=True) check_rc=True)
params['changed'] = True params['changed'] = True
#Next, add a new (or replacing) entry #Next, add a new (or replacing) entry
@ -139,12 +144,13 @@ def enforce_state(module, params):
return params return params
def sanity_check(module,host,key): def sanity_check(module,host,key,sshkeygen):
'''Check supplied key is sensible '''Check supplied key is sensible
host and key are parameters provided by the user; If the host host and key are parameters provided by the user; If the host
provided is inconsistent with the key supplied, then this function provided is inconsistent with the key supplied, then this function
quits, providing an error to the user. quits, providing an error to the user.
sshkeygen is the path to ssh-keygen, found earlier with get_bin_path
''' '''
#If no key supplied, we're doing a removal, and have nothing to check here. #If no key supplied, we're doing a removal, and have nothing to check here.
if key is None: if key is None:
@ -162,26 +168,27 @@ def sanity_check(module,host,key):
except IOError,e: except IOError,e:
module.fail_json(msg="Failed to write to temporary file %s: %s" % \ module.fail_json(msg="Failed to write to temporary file %s: %s" % \
(outf.name,str(e))) (outf.name,str(e)))
rc,stdout,stderr=module.run_command(['ssh-keygen','-F',host, rc,stdout,stderr=module.run_command([sshkeygen,'-F',host,
'-f',outf.name], '-f',outf.name],
check_rc=True) check_rc=True)
os.remove(outf.name) os.remove(outf.name)
if stdout=='': #host not found if stdout=='': #host not found
module.fail_json(msg="Host parameter does not match hashed host field in supplied key") module.fail_json(msg="Host parameter does not match hashed host field in supplied key")
def search_for_host_key(module,host,key,path): def search_for_host_key(module,host,key,path,sshkeygen):
'''search_for_host_key(module,host,key,path) -> (current,replace) '''search_for_host_key(module,host,key,path,sshkeygen) -> (current,replace)
Looks up host in the known_hosts file path; if it's there, looks to see Looks up host in the known_hosts file path; if it's there, looks to see
if one of those entries matches key. Returns: if one of those entries matches key. Returns:
current (Boolean): is host found in path? current (Boolean): is host found in path?
replace (Boolean): is the key in path different to that supplied by user? replace (Boolean): is the key in path different to that supplied by user?
if current=False, then replace is always False. if current=False, then replace is always False.
sshkeygen is the path to ssh-keygen, found earlier with get_bin_path
''' '''
replace=False replace=False
if os.path.exists(path)==False: if os.path.exists(path)==False:
return False, False return False, False
rc,stdout,stderr=module.run_command(['ssh-keygen','-F',host,'-f',path], rc,stdout,stderr=module.run_command([sshkeygen,'-F',host,'-f',path],
check_rc=True) check_rc=True)
if stdout=='': #host not found if stdout=='': #host not found
return False, False return False, False
@ -218,8 +225,8 @@ def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec = dict(
host = dict(required=True, type='str'), name = dict(required=True, type='str', aliases=['host']),
key = dict(required=True, type='str'), key = dict(required=False, type='str'),
path = dict(default="~/.ssh/known_hosts", type='str'), path = dict(default="~/.ssh/known_hosts", type='str'),
state = dict(default='present', choices=['absent','present']), state = dict(default='present', choices=['absent','present']),
), ),