Repository: ansible/ansible

Issue:
ansible_distribution_major_version missing on Solaris
##### ISSUE TYPE
- Bug Report
##### COMPONENT NAME

setup module (facts.py)
##### ANSIBLE VERSION

```
ansible 2.2.0.0 (detached HEAD eafb4043c9) last updated 2016/10/26 09:57:34 (GMT +200)
  lib/ansible/modules/core: (detached HEAD c51ced56cc) last updated 2016/10/26 09:56:37 (GMT +200)
  lib/ansible/modules/extras: (detached HEAD 8ffe314ea5) last updated 2016/10/26 09:56:37 (GMT +200)

ansible v2.2.0.0-0.3.rc3
```
##### OS / ENVIRONMENT

running on MacOS 10.12.1 
managing "Solaris 10 10/09 s10s_u8wos_08a SPARC"
##### SUMMARY

We are using 
`group_by: key={{ ansible_os_family }}-{{ ansible_distribution_major_version }}`
in our playbooks to include group_vars based on distro and major_version. Playbooks are used for Solaris, RHEL, SLES, and Debian at the same time.
With ansible-2.1.2 on Solaris we get `"ansible_distribution_major_version": "NA"` and therefore our grouping is working.
With ansible-2.2.0.0-0.3.rc3 "ansible_distribution_major_version" is missing an Solaris hosts and breaks grouping.
##### STEPS TO REPRODUCE

Run ansible-playbook with following playbook:

```
- hosts:
  - solaris-host
  - linux-host
  pre_tasks:
        - name: group by os-version
          group_by: key={{ ansible_os_family }}-{{ ansible_distribution_major_version }}
          changed_when: False
          tags: always

```
##### EXPECTED RESULTS

group_by pre_task still works for all OS
##### ACTUAL RESULTS

```
fatal: [solaris-host]: FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'ansible_distribution_major_version' is undefined
```

For backwards compatibility  `"ansible_distribution_major_version": "NA"` should be reported for Solaris. 
Or it should get the real number (9,10,11).



Discussion:
Looks like 'ansible_distribution_major_version' for Solaris was missed in 5626c540ce408ac19ebe4d6ac8321ee11ce356c2. 

It probably fell into the 'other' category for 2.1 which would default to the first segment of the platform.dist()[1] versopm, but 2.2 has support for SunOS/Solaris specifically but that doesn't set the ansible_distribution_major_version

Same issue with macOS:

```
ansible -i 'localhost,' -c local -m setup -a 'gather_subset=!facter,!ohai filter=ansible_distribution*' localhost
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_distribution": "MacOSX",
        "ansible_distribution_release": "16.1.0",
        "ansible_distribution_version": "10.12.1"
    },
    "changed": false
}
```

bot_broken as this is not windows-related at all.
Agreed this should not be labeled for windows.  For some reason bot broken doesn't seem to be picked up.  will try putting it on a separate line

bot_broken
Hmm, so bot instructions now have to be on their own line ? That wasn't the case earlier though.
@dagwieers only for bot broken.
Manually removing windows label as the bot doesn't want to.  To be fair since setup is used on all host platforms it would be hard for the bot to figure out which setup implementation needed attention.
Is this still a problem in 2.3 or 2.4?
If you are running from an ansible source check out, could you try running

```
hacking/tests/gen_distribution_version_testcase.py
```

On the solaris box and posting the results.

It would be nice if some active Ansible Solaris people would form a Solaris WG and think about how to improve Solaris in general, and fixing Solaris-related issues.
Still occurs with Ansible 2.6.2

Unable to resolve ansible_distribution_major_version 

Resolving patch:
diff --git a/lib/ansible/module_utils/facts/system/distribution.py b/lib/ansible/module_utils/facts/system/distribution.py
--- a/lib/ansible/module_utils/facts/system/distribution.py
+++ b/lib/ansible/module_utils/facts/system/distribution.py
@@ -26,8 +26,12 @@
 from ansible.module_utils.facts.collector import BaseFactCollector
 
 
-def get_uname_version(module):
-    rc, out, err = module.run_command(['uname', '-v'])
+def get_uname(module, flags=('-v')):
+    if isinstance(flags, str):
+        flags = flags.split()
+    command = ['uname']
+    command.extend(flags)
+    rc, out, err = module.run_command(command)
     if rc == 0:
         return out
     return None
@@ -587,6 +591,8 @@ def get_distribution_SunOS(self):
         data = get_file_content('/etc/release').splitlines()[0]
 
         if 'Solaris' in data:
+            # for solaris 10 uname_r will contain 5.10, for solaris 11 it will have 5.11
+            uname_r = get_uname(self.module, flags=['-r'])
             ora_prefix = ''
             if 'Oracle Solaris' in data:
                 data = data.replace('Oracle ', '')
@@ -594,9 +600,10 @@ def get_distribution_SunOS(self):
             sunos_facts['distribution'] = data.split()[0]
             sunos_facts['distribution_version'] = data.split()[1]
             sunos_facts['distribution_release'] = ora_prefix + data
+            sunos_facts['distribution_major_version'] = uname_r.split('.')[1].rstrip()
             return sunos_facts
 
-        uname_v = get_uname_version(self.module)
+        uname_v = get_uname(self.module, flags=['-v'])
         distribution_version = None
 
         if 'SmartOS' in data:


Test patch:
diff --git a/test/units/module_utils/test_distribution_version.py b/test/units/module_utils/test_distribution_version.py
--- a/test/units/module_utils/test_distribution_version.py
+++ b/test/units/module_utils/test_distribution_version.py
@@ -844,10 +844,12 @@
     {
         "name": "Solaris 10",
         "uname_v": "Generic_141445-09",
+        "uname_r": "5.10",
         "result": {
             "distribution_release": "Solaris 10 10/09 s10x_u8wos_08a X86",
             "distribution": "Solaris",
             "os_family": "Solaris",
+            "distribution_major_version": "10",
             "distribution_version": "10"
         },
         "platform.dist": [
@@ -865,10 +867,12 @@
     {
         "name": "Solaris 11",
         "uname_v": "11.0",
+        "uname_r": "5.11",
         "result": {
             "distribution_release": "Oracle Solaris 11 11/11 X86",
             "distribution": "Solaris",
             "os_family": "Solaris",
+            "distribution_major_version": "11",
             "distribution_version": "11"
         },
         "platform.dist": [
@@ -884,6 +888,7 @@
     },
     {
         "name": "Solaris 11.3",
+        "uname_r": "5.11",
         "platform.dist": [
             "",
             "",
@@ -891,8 +896,8 @@
         ],
         "input": {
             "/etc/release": (
-                "                             Oracle Solaris 11.3 X86\n  Copyright (c) 1983, 2015, Oracle and/or its affiliates.  "
-                "All rights reserved.\n                            Assembled 06 October 2015\n"
+                "                             Oracle Solaris 11.3 X86\n  Copyright (c) 1983, 2018, Oracle and/or its affiliates.  "
+                "All rights reserved.\n                              Assembled 09 May 2018\n"
             )
         },
         "platform.system": "SunOS",
@@ -900,11 +905,36 @@
             "distribution_release": "Oracle Solaris 11.3 X86",
             "distribution": "Solaris",
             "os_family": "Solaris",
+            "distribution_major_version": "11",
             "distribution_version": "11.3"
         }
     },
+    {
+        "name": "Solaris 11.4",
+        "uname_r": "5.11",
+        "platform.dist": [
+            "",
+            "",
+            ""
+        ],
+        "input": {
+            "/etc/release": (
+                "                            Oracle Solaris 11.4 SPARC\n    Copyright (c) 1983, 2018, Oracle and/or its affiliates."
+                "  All rights reserved.\n                           Assembled 14 September 2018\n"
+            )
+        },
+        "platform.system": "SunOS",
+        "result": {
+            "distribution_release": "Oracle Solaris 11.4 SPARC",
+            "distribution": "Solaris",
+            "os_family": "Solaris",
+            "distribution_major_version": "11",
+            "distribution_version": "11.4"
+        }
+    },
     {
         "name": "Solaris 10",
+        "uname_r": "5.10",
         "platform.dist": [
             "",
             "",
@@ -919,6 +949,7 @@
             "distribution_release": "Oracle Solaris 10 1/13 s10x_u11wos_24a X86",
             "distribution": "Solaris",
             "os_family": "Solaris",
+            "distribution_major_version": "10",
             "distribution_version": "10"
         }
     },
@@ -1109,8 +1140,13 @@ def mock_get_file_content(fname, default=None, strip=True):
             data = data.strip()
         return data
 
-    def mock_get_uname_version(am):
-        return testcase.get('uname_v', None)
+    def mock_get_uname(am, flags):
+        if '-v' in flags:
+            return testcase.get('uname_v', None)
+        elif '-r' in flags:
+            return testcase.get('uname_r', None)
+        else:
+            return None
 
     def mock_file_exists(fname, allow_empty=False):
         if fname not in testcase['input']:
@@ -1152,7 +1188,7 @@ def mock_os_path_is_file(filename):
         return False
 
     mocker.patch('ansible.module_utils.facts.system.distribution.get_file_content', mock_get_file_content)
-    mocker.patch('ansible.module_utils.facts.system.distribution.get_uname_version', mock_get_uname_version)
+    mocker.patch('ansible.module_utils.facts.system.distribution.get_uname', mock_get_uname)
     mocker.patch('ansible.module_utils.facts.system.distribution._file_exists', mock_file_exists)
     mocker.patch('ansible.module_utils.distro.name', mock_distro_name)
     mocker.patch('ansible.module_utils.distro.id', mock_distro_name)