Repository: ansible/ansible

Issue:
git module needs better error messages for timeout related errors
##### ISSUE TYPE
 - Documentation Report

##### COMPONENT NAME
git

##### ANSIBLE VERSION
```
ansible 2.3.0 (devel f1b5dde4cb) last updated 2016/12/16 05:20:05 (GMT -400)
  config file = /home/ecms/horizons_v2/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
N/A

##### SUMMARY
Ansible's git module reports a hostkey error rather than a timeout when in fact it simply couldn't make a connection at all. Sure, now that I've traced it to a firewall restriction and the ensuing timeout, that makes sense, but this could be a much more insightful and accurate error message.

##### STEPS TO REPRODUCE
Set a security group egress restriction on the ssh port (22), and attempt to do a git clone...

```yaml
- name: checkout application from repository
  git:
    repo: ssh://AAAAAAAAAAAAAAAAAAAA@git-codecommit.us-east-1.amazonaws.com/v1/repos/somerepo
    dest: "{{ app_home }}"
    key_file: /home/ec2-user/deploy.pem
    accept_hostkey: yes
    force: yes
```

##### EXPECTED RESULTS
```
2016-12-16 06:47:20,756 p=11741 u=user |  fatal: [host.domain.internal]: FAILED! => {"changed": false, "failed": true, "msg": "git connection timed out, waited 60s"}
```

##### ACTUAL RESULTS
Instead of a timeout related error, the error message refers to failing to get the hostkey.  
```
2016-12-16 06:47:20,756 p=11741 u=user |  fatal: [host.domain.internal]: FAILED! => {"changed": false, "failed": true, "msg": "failed to get the hostkey for git-codecommit.us-east-1.amazonaws.com"}
```



Discussion:
cc @robinro
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
This was introduced in https://github.com/ansible/ansible/commit/0d9afa84d50e02c912e90ff6bbe5a89cea30b8be 

One could think about moving the error from the hostkey module back to the git module, maybe via an exception?
Or the hostkey check could report more accurately that the machine is not reachable instead of the "hostkey" message.

@abadger What's your view on that?

Does ssh-keyscan tell us why it failed?  If so, then we could include it in the error message.  If not, then it seems like we'd be as in the dark as the user as to why it failed.
looks like 'ssh-keyscan -v' sometimes shows slightly more info, but nothing particularly consistent:

```
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ ssh-keyscan -t rsa1 192.168.1.1
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ ssh-keyscan -t rsa1 localhost
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ ssh-keyscan -v -t rsa1 localhost
debug1: match: OpenSSH_7.4 pat OpenSSH* compat 0x04000000
debug1: localhost doesn't support ssh1
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ ssh-keyscan -v -t eczxczx localhost
unknown key type eczxczx
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ ssh-keyscan -t eczxczx localhost
unknown key type eczxczx
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ man ssh-keyscan
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ ssh-keyscan -t ed25519 localhost
# localhost:22 SSH-2.0-OpenSSH_7.4
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ ssh-keyscan -t dsa localhost
# localhost:22 SSH-2.0-OpenSSH_7.4
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ ssh-keyscan -v -t dsa localhost
debug1: match: OpenSSH_7.4 pat OpenSSH* compat 0x04000000
# localhost:22 SSH-2.0-OpenSSH_7.4
debug1: Enabling compatibility mode for protocol 2.0
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: (no match)
[fedora-25:ansible (vault_yaml_repr_23846 %)]$ ssh-keyscan -v -t ed25519 localhost
debug1: match: OpenSSH_7.4 pat OpenSSH* compat 0x04000000
# localhost:22 SSH-2.0-OpenSSH_7.4
debug1: Enabling compatibility mode for protocol 2.0
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: (no match)
```
In general ssh-keyscan is not very communicative. I get an error (that looks config/distro specific) when the hostname can not be resolved, but if there is no answer on port 22 ssh-keyscan stays silent. I guess those two are the most common cases.
Okay.  I'm in favour of better error messages but it doesn't seem like ssh-keyscan is giving us the information that we need to make a good message in this case.  Maybe we can make it a little more specific about what we were doing when it failed?  "Failed to retrieve hostname.  ssh-keyscan -p PORT HOSTNAME returned no matches" ?  It's not much but it's the only thing I can think to do.

Resolving patch:
diff --git a/lib/ansible/module_utils/known_hosts.py b/lib/ansible/module_utils/known_hosts.py
--- a/lib/ansible/module_utils/known_hosts.py
+++ b/lib/ansible/module_utils/known_hosts.py
@@ -200,9 +200,19 @@ def add_host_key(module, fqdn, port=22, key_type="rsa", create_dir=False):
         this_cmd = "%s -t %s %s" % (keyscan_cmd, key_type, fqdn)
 
     rc, out, err = module.run_command(this_cmd)
-    # ssh-keyscan gives a 0 exit code and prints nothins on timeout
+    # ssh-keyscan gives a 0 exit code and prints nothing on timeout
     if rc != 0 or not out:
-        module.fail_json(msg='failed to get the hostkey for %s' % fqdn)
+        msg = 'failed to retrieve hostkey'
+        if not out:
+            msg += '. "%s" returned no matches.'  % this_cmd
+        else:
+            msg += ' using command "%s". [stdout]: %s' % (this_cmd, out)
+
+        if err:
+            msg += ' [stderr]: %s' % err
+
+        module.fail_json(msg=msg)
+
     module.append_to_file(user_host_file, out)
 
     return rc, out, err


Test patch: