Repository: ansible/ansible
Pull request: facts - properly report virtualization facts for Linux guests on bhyve
##### SUMMARY
<!--- Describe the change below, including rationale and design decisions -->
Fixes #73167

Add unit tests for this specific change. We did not have any tests for this file prior, so more could be added.
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
- Bugfix Pull Request

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
`lib/ansible/module_utils/facts/virtual/linux.py`

Problem statement:
Virtualization detection fails for Bhyve guests
##### SUMMARY
When i collect facts from FreeBSD bhyve target, with the command : ansible xxx -m setup | grep "ansible_virtualization_type"
the variable "ansible_virtualization_type" and "ansible_virtualization_role" is "NA".
Result of dmidecode -s system-product-name
```
dmidecode -s system-product-name
BHYVE
```

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
module_utils/facts/virtual/linux.py

##### ANSIBLE VERSION
```
ansible 2.10.4
  config file = /srv/ansible/ansible.cfg
  configured module search path = ['/srv/ansible/library']
  ansible python module location = /srv/ansible-venv/lib/python3.7/site-packages/ansible
  executable location = /srv/ansible-venv/bin/ansible
  python version = 3.7.3 (default, Dec 20 2019, 18:57:59) [GCC 8.3.0]

```

##### CONFIGURATION
```
ANSIBLE_FORCE_COLOR(/srv/ansible/ansible.cfg) = True
DEFAULT_HOST_LIST(/srv/ansible/ansible.cfg) = ['/srv/ansible/site.host']
DEFAULT_JINJA2_EXTENSIONS(/srv/ansible/ansible.cfg) = jinja2.ext.do
DEFAULT_MANAGED_STR(/srv/ansible/ansible.cfg) = Gestion par Ansible: {file} modifie le %Y-%m-%d %H:%M:%S par {uid} depuis {host}
DEFAULT_MODULE_PATH(/srv/ansible/ansible.cfg) = ['/srv/ansible/library']
DEFAULT_ROLES_PATH(/srv/ansible/ansible.cfg) = ['/srv/ansible/roles']
DEFAULT_STDOUT_CALLBACK(/srv/ansible/ansible.cfg) = yaml
DISPLAY_SKIPPED_HOSTS(/srv/ansible/ansible.cfg) = False
INTERPRETER_PYTHON(/srv/ansible/ansible.cfg) = auto_silent
RETRY_FILES_ENABLED(/srv/ansible/ansible.cfg) = False
```

##### OS / ENVIRONMENT
OS Target : Debian 10 in BHYVE hardware hosted on TrueNAS.

##### STEPS TO REPRODUCE
Create a VM in bhyve host, run the commande `ansible -m setup`



##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
        "ansible_virtualization_role": "guest",
        "ansible_virtualization_type": "bhyve",


##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
        "ansible_virtualization_role": "NA",
        "ansible_virtualization_type": "NA",

Hints from issue discussion:
Files identified in the description:
* [`lib/ansible/module_utils/facts/virtual/linux.py`](https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/virtual/linux.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Commit message: facts -properly report virtualization facts for Linux guests on bhyve

Patch to review:
diff --git a/changelogs/fragments/73167-bhyve-facts.yml b/changelogs/fragments/73167-bhyve-facts.yml
new file mode 100644
index 00000000000000..d9f6d0fc9048f6
--- /dev/null
+++ b/changelogs/fragments/73167-bhyve-facts.yml
@@ -0,0 +1,2 @@
+bugfixes:
+  - facts - properly report virtualization facts for Linux guests running on bhyve (https://github.com/ansible/ansible/issues/73167)
diff --git a/lib/ansible/module_utils/facts/virtual/linux.py b/lib/ansible/module_utils/facts/virtual/linux.py
index 46a7cfaddc3c98..6365f518adc171 100644
--- a/lib/ansible/module_utils/facts/virtual/linux.py
+++ b/lib/ansible/module_utils/facts/virtual/linux.py
@@ -352,6 +352,11 @@ def get_virtual_facts(self):
                         virtual_facts['virtualization_role'] = 'guest'
                         found_virt = True
 
+                if 'BHYVE' in out:
+                    virtual_facts['virtualization_type'] = 'bhyve'
+                    virtual_facts['virtualization_role'] = 'guest'
+                    found_virt = True
+
         if os.path.exists('/dev/kvm'):
             host_tech.add('kvm')
             if not found_virt:
diff --git a/test/units/module_utils/facts/virtual/__init__.py b/test/units/module_utils/facts/virtual/__init__.py
new file mode 100644
index 00000000000000..8b137891791fe9
--- /dev/null
+++ b/test/units/module_utils/facts/virtual/__init__.py
@@ -0,0 +1 @@
+
diff --git a/test/units/module_utils/facts/virtual/test_linux.py b/test/units/module_utils/facts/virtual/test_linux.py
new file mode 100644
index 00000000000000..4f7b05f21fb055
--- /dev/null
+++ b/test/units/module_utils/facts/virtual/test_linux.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import pytest
+
+from ansible.module_utils.facts.virtual import linux
+
+
+def test_get_virtual_facts_bhyve(mocker):
+    mocker.patch('os.path.exists', return_value=False)
+    mocker.patch('ansible.module_utils.facts.virtual.linux.get_file_content', return_value='')
+    mocker.patch('ansible.module_utils.facts.virtual.linux.get_file_lines', return_value=[])
+
+    module = mocker.Mock()
+    module.run_command.return_value = (0, 'BHYVE\n', '')
+    inst = linux.LinuxVirtual(module)
+
+    facts = inst.get_virtual_facts()
+    expected = {
+        'virtualization_role': 'guest',
+        'virtualization_tech_host': set(),
+        'virtualization_type': 'bhyve',
+        'virtualization_tech_guest': set(),
+    }
+
+    assert facts == expected


Code review comments:
diff_hunk: @@ -352,6 +352,11 @@ def get_virtual_facts(self):
                         virtual_facts['virtualization_role'] = 'guest'
                         found_virt = True
 
+                if 'BHYVE' in out:
+                    virtual_facts['virtualization_type'] = 'bhyve'
+                    virtual_facts['virtualization_role'] = 'guest'
+                    found_virt = True
path: lib/ansible/module_utils/facts/virtual/linux.py
text: @user1:
I think we'll need something like:

```suggestion
                if 'BHYVE' in out:
                    guest_tech.add('bhyve')
                    if not found_virt:
                        virtual_facts['virtualization_type'] = 'bhyve'
                        virtual_facts['virtualization_role'] = 'guest'
                        found_virt = True
```

Basically: If there's nested or multiple virt techs at play and we found something earlier, go with that, but add bhyve to the list of guest techs no matter what.

@author:
Will do.