Repository: ansible/ansible

Issue:
Gathering facts, zero division error in get_cpu_facts
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
module setup (ansible/module_utils/facts.py)

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.2.1.0
Python 2.7.10 (host)

Python 2.7.3 (remote)
```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
I have a lot of hosts and problem only with one. I updated ansible couple of time and didn't test changes on this host.

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
-->
```
# host
tried on MacOS and CentOS 6.8
# remote
Linux hostname 3.8.0-32-generic #47~precise1-Ubuntu SMP Wed Oct 2 16:19:35 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
```

##### SUMMARY
<!--- Explain the problem briefly -->
When I run command:
```
ansible hostname -m setup -a 'gather_subset=!all'
```
everything work fine, but when i run playbook or just try to gather facts I have a module failure:
```
ansible hostname -m setup
hostname | FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Shared connection to hostnameIP closed.\r\n",
    "module_stdout": "Traceback (most recent call last):\r\n  File \"/tmp/ansible_Z9fwoS/ansible_module_setup.py\", line 134, in <module>\r\n    main()\r\n  File \"/tmp/ansible_Z9fwoS/ansible_module_setup.py\", line 126, in main\r\n    data = get_all_facts(module)\r\n  File \"/tmp/ansible_Z9fwoS/ansible_modlib.zip/ansible/module_utils/facts.py\", line 3518, in get_all_facts\r\n  File \"/tmp/ansible_Z9fwoS/ansible_modlib.zip/ansible/module_utils/facts.py\", line 3461, in ansible_facts\r\n  File \"/tmp/ansible_Z9fwoS/ansible_modlib.zip/ansible/module_utils/facts.py\", line 987, in populate\r\n  File \"/tmp/ansible_Z9fwoS/ansible_modlib.zip/ansible/module_utils/facts.py\", line 1132, in get_cpu_facts\r\nZeroDivisionError: integer division or modulo by zero\r\n",
    "msg": "MODULE FAILURE"
}
```
I don't understand how I can fix facts.py or find problem with zero division on sources...
When I run on remote host:
```
cat /proc/cpuinfo
```
it shows info about CPU without problem.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->
I can reproduce it only on my single host with other ones everything works fine.
I tried to update all packages on remote host but it didn't help me.



Discussion:
Mind pasting the entire 'cat /proc/cpuinfo' ?

I could maybe mock it to looks like yours in order to reproduce.

Thanks
Hi, yes sure. 

```
$ cat /proc/cpuinfo
processor	: 0
vendor_id	: GenuineIntel
cpu family	: 15
model		: 4
model name	: Intel(R) Pentium(R) 4 CPU 3.40GHz
stepping	: 3
microcode	: 0x5
cpu MHz		: 3400.347
cache size	: 2048 KB
physical id	: 0
siblings	: 1
core id		: 0
cpu cores	: 1
apicid		: 0
initial apicid	: 0
fpu		: yes
fpu_exception	: yes
cpuid level	: 5
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc pebs bts nopl pni dtes64 monitor ds_cpl est cid cx16 xtpr
bogomips	: 6800.69
clflush size	: 64
cache_alignment	: 128
address sizes	: 36 bits physical, 48 bits virtual
power management:

processor	: 1
vendor_id	: GenuineIntel
cpu family	: 15
model		: 4
model name	: Intel(R) Pentium(R) 4 CPU 3.40GHz
stepping	: 3
microcode	: 0x5
cpu MHz		: 3400.347
cache size	: 2048 KB
physical id	: 0
siblings	: 1
core id		: 0
cpu cores	: 0
apicid		: 1
initial apicid	: 1
fpu		: yes
fpu_exception	: yes
cpuid level	: 5
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc pebs bts nopl pni dtes64 monitor ds_cpl est cid cx16 xtpr
bogomips	: 6800.69
clflush size	: 64
cache_alignment	: 128
address sizes	: 36 bits physical, 48 bits virtual
power management:
```
guessing that the 'cpu_cores: 0' on the second entry is the problem.

The code around line 1121

``` python
                socket_values = list(sockets.values())
                if socket_values:
                    self.facts['processor_cores'] = socket_values[0]
                else:
                    self.facts['processor_cores'] = 1

                core_values = list(cores.values())
                if core_values:
                    self.facts['processor_threads_per_core'] = core_values[0] // self.facts['processor_cores']
                else:
                    self.facts['processor_threads_per_core'] = 1 // self.facts['processor_cores']
```

sockets[physid] can end up being the last value read for 'cpu_cores', which
would be 0 here and cause the error.

Resolving patch:
diff --git a/lib/ansible/module_utils/facts.py b/lib/ansible/module_utils/facts.py
--- a/lib/ansible/module_utils/facts.py
+++ b/lib/ansible/module_utils/facts.py
@@ -1204,7 +1204,7 @@ def get_cpu_facts(self):
                     self.facts['processor_count'] = i
 
                 socket_values = list(sockets.values())
-                if socket_values:
+                if socket_values and socket_values[0]:
                     self.facts['processor_cores'] = socket_values[0]
                 else:
                     self.facts['processor_cores'] = 1


Test patch: