From 4e55b936132c0363e23708cfbbe5e5f53e2b5cdf Mon Sep 17 00:00:00 2001 From: Rick Elrod Date: Thu, 6 Aug 2020 09:45:37 -0500 Subject: [PATCH] virt facts: allow guest/host to have >1 virt tech (#70832) Change: - Allow systems to declare multiple virt techs. For example if a system is both a docker container, but virtualized on KVM, show both. If a system is set up to run virtualbox and KVM VMs, show both. - This is done by introducing new facts keys: - virtualization_tech_guest - virtualization_tech_host - Backwards compatibility is preserved by keeping track of the previous return-points and refusing to update those keys after we would have returned, but now returning them at the end, so that the new keys can accumulate their data. Test Plan: - Local - CI Tickets: - Refs #66304 - Refs #17151 - Refs #17058 - Probably others Signed-off-by: Rick Elrod --- changelogs/fragments/multivirt.yml | 2 + .../porting_guide_base_2.11.rst | 3 +- .../module_utils/facts/virtual/base.py | 16 +- .../module_utils/facts/virtual/freebsd.py | 30 +- .../module_utils/facts/virtual/hpux.py | 10 + .../module_utils/facts/virtual/linux.py | 268 ++++++++++++------ .../module_utils/facts/virtual/netbsd.py | 27 +- .../module_utils/facts/virtual/openbsd.py | 12 +- .../module_utils/facts/virtual/sunos.py | 27 +- .../module_utils/facts/virtual/sysctl.py | 86 ++++-- 10 files changed, 356 insertions(+), 125 deletions(-) create mode 100644 changelogs/fragments/multivirt.yml diff --git a/changelogs/fragments/multivirt.yml b/changelogs/fragments/multivirt.yml new file mode 100644 index 00000000000..759d9ea662f --- /dev/null +++ b/changelogs/fragments/multivirt.yml @@ -0,0 +1,2 @@ +minor_changes: + - New virtualization facts, ``virtualization_tech_guest`` and ``virtualization_tech_host`` now allow for conveying when a system is a host or guest of multiple virtualization technologies. diff --git a/docs/docsite/rst/porting_guides/porting_guide_base_2.11.rst b/docs/docsite/rst/porting_guides/porting_guide_base_2.11.rst index 6bbafbeedab..5bd423cdf0e 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_base_2.11.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_base_2.11.rst @@ -131,7 +131,8 @@ No notable changes Noteworthy module changes ------------------------- -* facts - ``ansible_virtualization_type`` now tries to report a more accurate result than ``xen`` when virtualized and not running on Xen. +* facts - On NetBSD, ``ansible_virtualization_type`` now tries to report a more accurate result than ``xen`` when virtualized and not running on Xen. +* facts - Virtualization facts now include ``virtualization_tech_guest`` and ``virtualization_tech_host`` keys. These are lists of virtualization technologies that a guest is a part of, or that a host provides, respectively. As an example, a host may be set up to provide both KVM and VirtualBox, and these will be included in ``virtualization_tech_host``, and a podman container running on a VM powered by KVM will have a ``virtualization_tech_guest`` of ``["kvm", "podman", "container"]``. Plugins diff --git a/lib/ansible/module_utils/facts/virtual/base.py b/lib/ansible/module_utils/facts/virtual/base.py index 02da049e34c..cb725b6ac88 100644 --- a/lib/ansible/module_utils/facts/virtual/base.py +++ b/lib/ansible/module_utils/facts/virtual/base.py @@ -46,16 +46,24 @@ class Virtual: return virtual_facts def get_virtual_facts(self): - virtual_facts = {'virtualization_type': '', - 'virtualization_role': ''} + virtual_facts = { + 'virtualization_type': '', + 'virtualization_role': '', + 'virtualization_tech_guest': set(), + 'virtualization_tech_host': set(), + } return virtual_facts class VirtualCollector(BaseFactCollector): name = 'virtual' _fact_class = Virtual - _fact_ids = set(['virtualization_type', - 'virtualization_role']) + _fact_ids = set([ + 'virtualization_type', + 'virtualization_role', + 'virtualization_tech_guest', + 'virtualization_tech_host', + ]) def collect(self, module=None, collected_facts=None): collected_facts = collected_facts or {} diff --git a/lib/ansible/module_utils/facts/virtual/freebsd.py b/lib/ansible/module_utils/facts/virtual/freebsd.py index cfaf880ee87..7062d019843 100644 --- a/lib/ansible/module_utils/facts/virtual/freebsd.py +++ b/lib/ansible/module_utils/facts/virtual/freebsd.py @@ -32,23 +32,45 @@ class FreeBSDVirtual(Virtual, VirtualSysctlDetectionMixin): def get_virtual_facts(self): virtual_facts = {} + host_tech = set() + guest_tech = set() + # Set empty values as default virtual_facts['virtualization_type'] = '' virtual_facts['virtualization_role'] = '' if os.path.exists('/dev/xen/xenstore'): + guest_tech.add('xen') virtual_facts['virtualization_type'] = 'xen' virtual_facts['virtualization_role'] = 'guest' + kern_vm_guest = self.detect_virt_product('kern.vm_guest') + guest_tech.update(kern_vm_guest['virtualization_tech_guest']) + host_tech.update(kern_vm_guest['virtualization_tech_host']) + + hw_hv_vendor = self.detect_virt_product('hw.hv_vendor') + guest_tech.update(hw_hv_vendor['virtualization_tech_guest']) + host_tech.update(hw_hv_vendor['virtualization_tech_host']) + + sec_jail_jailed = self.detect_virt_product('security.jail.jailed') + guest_tech.update(sec_jail_jailed['virtualization_tech_guest']) + host_tech.update(sec_jail_jailed['virtualization_tech_host']) + if virtual_facts['virtualization_type'] == '': - virtual_product_facts = self.detect_virt_product('kern.vm_guest') or self.detect_virt_product( - 'hw.hv_vendor') or self.detect_virt_product('security.jail.jailed') - virtual_facts.update(virtual_product_facts) + sysctl = kern_vm_guest or hw_hv_vendor or sec_jail_jailed + # We call update here, then re-set virtualization_tech_host/guest + # later. + virtual_facts.update(sysctl) + + virtual_vendor_facts = self.detect_virt_vendor('hw.model') + guest_tech.update(virtual_vendor_facts['virtualization_tech_guest']) + host_tech.update(virtual_vendor_facts['virtualization_tech_host']) if virtual_facts['virtualization_type'] == '': - virtual_vendor_facts = self.detect_virt_vendor('hw.model') virtual_facts.update(virtual_vendor_facts) + virtual_facts['virtualization_tech_guest'] = guest_tech + virtual_facts['virtualization_tech_host'] = host_tech return virtual_facts diff --git a/lib/ansible/module_utils/facts/virtual/hpux.py b/lib/ansible/module_utils/facts/virtual/hpux.py index 94ea6a1a4f2..10574827e44 100644 --- a/lib/ansible/module_utils/facts/virtual/hpux.py +++ b/lib/ansible/module_utils/facts/virtual/hpux.py @@ -32,28 +32,38 @@ class HPUXVirtual(Virtual): def get_virtual_facts(self): virtual_facts = {} + host_tech = set() + guest_tech = set() + if os.path.exists('/usr/sbin/vecheck'): rc, out, err = self.module.run_command("/usr/sbin/vecheck") if rc == 0: + guest_tech.add('HP vPar') virtual_facts['virtualization_type'] = 'guest' virtual_facts['virtualization_role'] = 'HP vPar' if os.path.exists('/opt/hpvm/bin/hpvminfo'): rc, out, err = self.module.run_command("/opt/hpvm/bin/hpvminfo") if rc == 0 and re.match('.*Running.*HPVM vPar.*', out): + guest_tech.add('HPVM vPar') virtual_facts['virtualization_type'] = 'guest' virtual_facts['virtualization_role'] = 'HPVM vPar' elif rc == 0 and re.match('.*Running.*HPVM guest.*', out): + guest_tech.add('HPVM IVM') virtual_facts['virtualization_type'] = 'guest' virtual_facts['virtualization_role'] = 'HPVM IVM' elif rc == 0 and re.match('.*Running.*HPVM host.*', out): + guest_tech.add('HPVM') virtual_facts['virtualization_type'] = 'host' virtual_facts['virtualization_role'] = 'HPVM' if os.path.exists('/usr/sbin/parstatus'): rc, out, err = self.module.run_command("/usr/sbin/parstatus") if rc == 0: + guest_tech.add('HP nPar') virtual_facts['virtualization_type'] = 'guest' virtual_facts['virtualization_role'] = 'HP nPar' + virtual_facts['virtualization_tech_guest'] = guest_tech + virtual_facts['virtualization_tech_host'] = host_tech return virtual_facts diff --git a/lib/ansible/module_utils/facts/virtual/linux.py b/lib/ansible/module_utils/facts/virtual/linux.py index e19193cfd73..d7a9f73eb0a 100644 --- a/lib/ansible/module_utils/facts/virtual/linux.py +++ b/lib/ansible/module_utils/facts/virtual/linux.py @@ -35,141 +35,218 @@ class LinuxVirtual(Virtual): # For more information, check: http://people.redhat.com/~rjones/virt-what/ def get_virtual_facts(self): virtual_facts = {} + + # We want to maintain compatibility with the old "virtualization_type" + # and "virtualization_role" entries, so we need to track if we found + # them. We won't return them until the end, but if we found them early, + # we should avoid updating them again. + found_virt = False + + # But as we go along, we also want to track virt tech the new way. + host_tech = set() + guest_tech = set() + # lxc/docker if os.path.exists('/proc/1/cgroup'): for line in get_file_lines('/proc/1/cgroup'): if re.search(r'/docker(/|-[0-9a-f]+\.scope)', line): - virtual_facts['virtualization_type'] = 'docker' - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + guest_tech.add('docker') + if not found_virt: + virtual_facts['virtualization_type'] = 'docker' + virtual_facts['virtualization_role'] = 'guest' + found_virt = True if re.search('/lxc/', line) or re.search('/machine.slice/machine-lxc', line): - virtual_facts['virtualization_type'] = 'lxc' - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + guest_tech.add('lxc') + if not found_virt: + virtual_facts['virtualization_type'] = 'lxc' + virtual_facts['virtualization_role'] = 'guest' + found_virt = True # lxc does not always appear in cgroups anymore but sets 'container=lxc' environment var, requires root privs if os.path.exists('/proc/1/environ'): for line in get_file_lines('/proc/1/environ', line_sep='\x00'): if re.search('container=lxc', line): - virtual_facts['virtualization_type'] = 'lxc' - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + guest_tech.add('lxc') + if not found_virt: + virtual_facts['virtualization_type'] = 'lxc' + virtual_facts['virtualization_role'] = 'guest' + found_virt = True if re.search('container=podman', line): - virtual_facts['virtualization_type'] = 'podman' - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + guest_tech.add('podman') + if not found_virt: + virtual_facts['virtualization_type'] = 'podman' + virtual_facts['virtualization_role'] = 'guest' + found_virt = True if re.search('^container=.', line): - virtual_facts['virtualization_type'] = 'container' - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + guest_tech.add('container') + if not found_virt: + virtual_facts['virtualization_type'] = 'container' + virtual_facts['virtualization_role'] = 'guest' + found_virt = True if os.path.exists('/proc/vz') and not os.path.exists('/proc/lve'): virtual_facts['virtualization_type'] = 'openvz' if os.path.exists('/proc/bc'): - virtual_facts['virtualization_role'] = 'host' + host_tech.add('openvz') + if not found_virt: + virtual_facts['virtualization_role'] = 'host' else: - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + guest_tech.add('openvz') + if not found_virt: + virtual_facts['virtualization_role'] = 'guest' + found_virt = True systemd_container = get_file_content('/run/systemd/container') if systemd_container: - virtual_facts['virtualization_type'] = systemd_container - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + guest_tech.add(systemd_container) + if not found_virt: + virtual_facts['virtualization_type'] = systemd_container + virtual_facts['virtualization_role'] = 'guest' + found_virt = True if os.path.exists("/proc/xen"): - virtual_facts['virtualization_type'] = 'xen' - virtual_facts['virtualization_role'] = 'guest' + is_xen_host = False try: for line in get_file_lines('/proc/xen/capabilities'): if "control_d" in line: - virtual_facts['virtualization_role'] = 'host' + is_xen_host = True except IOError: pass - return virtual_facts + + if is_xen_host: + host_tech.add('xen') + if not found_virt: + virtual_facts['virtualization_type'] = 'xen' + virtual_facts['virtualization_role'] = 'host' + else: + if not found_virt: + virtual_facts['virtualization_type'] = 'xen' + virtual_facts['virtualization_role'] = 'guest' + found_virt = True # assume guest for this block - virtual_facts['virtualization_role'] = 'guest' + if not found_virt: + virtual_facts['virtualization_role'] = 'guest' product_name = get_file_content('/sys/devices/virtual/dmi/id/product_name') if product_name in ('KVM', 'KVM Server', 'Bochs', 'AHV'): - virtual_facts['virtualization_type'] = 'kvm' - return virtual_facts + guest_tech.add('kvm') + if not found_virt: + virtual_facts['virtualization_type'] = 'kvm' + found_virt = True if product_name == 'RHEV Hypervisor': - virtual_facts['virtualization_type'] = 'RHEV' - return virtual_facts + guest_tech.add('RHEV') + if not found_virt: + virtual_facts['virtualization_type'] = 'RHEV' + found_virt = True if product_name in ('VMware Virtual Platform', 'VMware7,1'): - virtual_facts['virtualization_type'] = 'VMware' - return virtual_facts + guest_tech.add('VMware') + if not found_virt: + virtual_facts['virtualization_type'] = 'VMware' + found_virt = True if product_name in ('OpenStack Compute', 'OpenStack Nova'): - virtual_facts['virtualization_type'] = 'openstack' - return virtual_facts + guest_tech.add('openstack') + if not found_virt: + virtual_facts['virtualization_type'] = 'openstack' + found_virt = True bios_vendor = get_file_content('/sys/devices/virtual/dmi/id/bios_vendor') if bios_vendor == 'Xen': - virtual_facts['virtualization_type'] = 'xen' - return virtual_facts + guest_tech.add('xen') + if not found_virt: + virtual_facts['virtualization_type'] = 'xen' + found_virt = True if bios_vendor == 'innotek GmbH': - virtual_facts['virtualization_type'] = 'virtualbox' - return virtual_facts + guest_tech.add('virtualbox') + if not found_virt: + virtual_facts['virtualization_type'] = 'virtualbox' + found_virt = True if bios_vendor in ('Amazon EC2', 'DigitalOcean', 'Hetzner'): - virtual_facts['virtualization_type'] = 'kvm' - return virtual_facts + guest_tech.add('kvm') + if not found_virt: + virtual_facts['virtualization_type'] = 'kvm' + found_virt = True sys_vendor = get_file_content('/sys/devices/virtual/dmi/id/sys_vendor') KVM_SYS_VENDORS = ('QEMU', 'oVirt', 'Amazon EC2', 'DigitalOcean', 'Google', 'Scaleway', 'Nutanix') if sys_vendor in KVM_SYS_VENDORS: - virtual_facts['virtualization_type'] = 'kvm' - return virtual_facts + guest_tech.add('kvm') + if not found_virt: + virtual_facts['virtualization_type'] = 'kvm' + found_virt = True # FIXME: This does also match hyperv if sys_vendor == 'Microsoft Corporation': - virtual_facts['virtualization_type'] = 'VirtualPC' - return virtual_facts + guest_tech.add('VirtualPC') + if not found_virt: + virtual_facts['virtualization_type'] = 'VirtualPC' + found_virt = True if sys_vendor == 'Parallels Software International Inc.': - virtual_facts['virtualization_type'] = 'parallels' - return virtual_facts + guest_tech.add('parallels') + if not found_virt: + virtual_facts['virtualization_type'] = 'parallels' + found_virt = True if sys_vendor == 'OpenStack Foundation': - virtual_facts['virtualization_type'] = 'openstack' - return virtual_facts + guest_tech.add('openstack') + if not found_virt: + virtual_facts['virtualization_type'] = 'openstack' + found_virt = True # unassume guest - del virtual_facts['virtualization_role'] + if not found_virt: + del virtual_facts['virtualization_role'] if os.path.exists('/proc/self/status'): for line in get_file_lines('/proc/self/status'): if re.match(r'^VxID:\s+\d+', line): - virtual_facts['virtualization_type'] = 'linux_vserver' + if not found_virt: + virtual_facts['virtualization_type'] = 'linux_vserver' if re.match(r'^VxID:\s+0', line): - virtual_facts['virtualization_role'] = 'host' + host_tech.add('linux_vserver') + if not found_virt: + virtual_facts['virtualization_role'] = 'host' else: - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + guest_tech.add('linux_vserver') + if not found_virt: + virtual_facts['virtualization_role'] = 'guest' + found_virt = True if os.path.exists('/proc/cpuinfo'): for line in get_file_lines('/proc/cpuinfo'): if re.match('^model name.*QEMU Virtual CPU', line): - virtual_facts['virtualization_type'] = 'kvm' + guest_tech.add('kvm') + if not found_virt: + virtual_facts['virtualization_type'] = 'kvm' elif re.match('^vendor_id.*User Mode Linux', line): - virtual_facts['virtualization_type'] = 'uml' + guest_tech.add('uml') + if not found_virt: + virtual_facts['virtualization_type'] = 'uml' elif re.match('^model name.*UML', line): - virtual_facts['virtualization_type'] = 'uml' + guest_tech.add('uml') + if not found_virt: + virtual_facts['virtualization_type'] = 'uml' elif re.match('^machine.*CHRP IBM pSeries .emulated by qemu.', line): - virtual_facts['virtualization_type'] = 'kvm' + guest_tech.add('kvm') + if not found_virt: + virtual_facts['virtualization_type'] = 'kvm' elif re.match('^vendor_id.*PowerVM Lx86', line): - virtual_facts['virtualization_type'] = 'powervm_lx86' + guest_tech.add('powervm_lx86') + if not found_virt: + virtual_facts['virtualization_type'] = 'powervm_lx86' elif re.match('^vendor_id.*IBM/S390', line): - virtual_facts['virtualization_type'] = 'PR/SM' + guest_tech.add('PR/SM') + if not found_virt: + virtual_facts['virtualization_type'] = 'PR/SM' lscpu = self.module.get_bin_path('lscpu') if lscpu: rc, out, err = self.module.run_command(["lscpu"]) @@ -178,16 +255,24 @@ class LinuxVirtual(Virtual): data = line.split(":", 1) key = data[0].strip() if key == 'Hypervisor': - virtual_facts['virtualization_type'] = data[1].strip() + tech = data[1].strip() + guest_tech.add(tech) + if not found_virt: + virtual_facts['virtualization_type'] = tech else: - virtual_facts['virtualization_type'] = 'ibm_systemz' + guest_tech.add('ibm_systemz') + if not found_virt: + virtual_facts['virtualization_type'] = 'ibm_systemz' else: continue if virtual_facts['virtualization_type'] == 'PR/SM': - virtual_facts['virtualization_role'] = 'LPAR' + if not found_virt: + virtual_facts['virtualization_role'] = 'LPAR' else: - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + if not found_virt: + virtual_facts['virtualization_role'] = 'guest' + if not found_virt: + found_virt = True # Beware that we can have both kvm and virtualbox running on a single system if os.path.exists("/proc/modules") and os.access('/proc/modules', os.R_OK): @@ -197,8 +282,10 @@ class LinuxVirtual(Virtual): modules.append(data[0]) if 'kvm' in modules: - virtual_facts['virtualization_type'] = 'kvm' - virtual_facts['virtualization_role'] = 'host' + host_tech.add('kvm') + if not found_virt: + virtual_facts['virtualization_type'] = 'kvm' + virtual_facts['virtualization_role'] = 'host' if os.path.isdir('/rhev/'): # Check whether this is a RHEV hypervisor (is vdsm running ?) @@ -206,23 +293,32 @@ class LinuxVirtual(Virtual): try: with open(f) as virt_fh: comm_content = virt_fh.read().rstrip() + if comm_content in ('vdsm', 'vdsmd'): - virtual_facts['virtualization_type'] = 'RHEV' + # We add both kvm and RHEV to host_tech in this case. + # It's accurate. RHEV uses KVM. + host_tech.add('RHEV') + if not found_virt: + virtual_facts['virtualization_type'] = 'RHEV' break except Exception: pass - return virtual_facts + found_virt = True if 'vboxdrv' in modules: - virtual_facts['virtualization_type'] = 'virtualbox' - virtual_facts['virtualization_role'] = 'host' - return virtual_facts + host_tech.add('virtualbox') + if not found_virt: + virtual_facts['virtualization_type'] = 'virtualbox' + virtual_facts['virtualization_role'] = 'host' + found_virt = True if 'virtio' in modules: - virtual_facts['virtualization_type'] = 'kvm' - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + host_tech.add('kvm') + if not found_virt: + virtual_facts['virtualization_type'] = 'kvm' + virtual_facts['virtualization_role'] = 'guest' + found_virt = True # In older Linux Kernel versions, /sys filesystem is not available # dmidecode is the safest option to parse virtualization related values @@ -234,20 +330,28 @@ class LinuxVirtual(Virtual): # Strip out commented lines (specific dmidecode output) vendor_name = ''.join([line.strip() for line in out.splitlines() if not line.startswith('#')]) if vendor_name.startswith('VMware'): - virtual_facts['virtualization_type'] = 'VMware' - virtual_facts['virtualization_role'] = 'guest' - return virtual_facts + guest_tech.add('VMware') + if not found_virt: + virtual_facts['virtualization_type'] = 'VMware' + virtual_facts['virtualization_role'] = 'guest' + found_virt = True if os.path.exists('/dev/kvm'): - virtual_facts['virtualization_type'] = 'kvm' - virtual_facts['virtualization_role'] = 'host' - return virtual_facts + host_tech.add('kvm') + if not found_virt: + virtual_facts['virtualization_type'] = 'kvm' + virtual_facts['virtualization_role'] = 'host' + found_virt = True # If none of the above matches, return 'NA' for virtualization_type # and virtualization_role. This allows for proper grouping. - virtual_facts['virtualization_type'] = 'NA' - virtual_facts['virtualization_role'] = 'NA' + if not found_virt: + virtual_facts['virtualization_type'] = 'NA' + virtual_facts['virtualization_role'] = 'NA' + found_virt = True + virtual_facts['virtualization_tech_guest'] = guest_tech + virtual_facts['virtualization_tech_host'] = host_tech return virtual_facts diff --git a/lib/ansible/module_utils/facts/virtual/netbsd.py b/lib/ansible/module_utils/facts/virtual/netbsd.py index eb37b595af4..b4ef14ed046 100644 --- a/lib/ansible/module_utils/facts/virtual/netbsd.py +++ b/lib/ansible/module_utils/facts/virtual/netbsd.py @@ -27,29 +27,44 @@ class NetBSDVirtual(Virtual, VirtualSysctlDetectionMixin): def get_virtual_facts(self): virtual_facts = {} + host_tech = set() + guest_tech = set() + # Set empty values as default virtual_facts['virtualization_type'] = '' virtual_facts['virtualization_role'] = '' virtual_product_facts = self.detect_virt_product('machdep.dmi.system-product') + guest_tech.update(virtual_product_facts['virtualization_tech_guest']) + host_tech.update(virtual_product_facts['virtualization_tech_host']) virtual_facts.update(virtual_product_facts) + virtual_vendor_facts = self.detect_virt_vendor('machdep.dmi.system-vendor') + guest_tech.update(virtual_vendor_facts['virtualization_tech_guest']) + host_tech.update(virtual_vendor_facts['virtualization_tech_host']) + if virtual_facts['virtualization_type'] == '': - virtual_vendor_facts = self.detect_virt_vendor('machdep.dmi.system-vendor') virtual_facts.update(virtual_vendor_facts) # The above logic is tried first for backwards compatibility. If # something above matches, use it. Otherwise if the result is still # empty, try machdep.hypervisor. + virtual_vendor_facts = self.detect_virt_vendor('machdep.hypervisor') + guest_tech.update(virtual_vendor_facts['virtualization_tech_guest']) + host_tech.update(virtual_vendor_facts['virtualization_tech_host']) + if virtual_facts['virtualization_type'] == '': - virtual_vendor_facts = self.detect_virt_vendor('machdep.hypervisor') virtual_facts.update(virtual_vendor_facts) - if (virtual_facts['virtualization_type'] == '' and - os.path.exists('/dev/xencons')): - virtual_facts['virtualization_type'] = 'xen' - virtual_facts['virtualization_role'] = 'guest' + if os.path.exists('/dev/xencons'): + guest_tech.add('xen') + + if virtual_facts['virtualization_type'] == '': + virtual_facts['virtualization_type'] = 'xen' + virtual_facts['virtualization_role'] = 'guest' + virtual_facts['virtualization_tech_guest'] = guest_tech + virtual_facts['virtualization_tech_host'] = host_tech return virtual_facts diff --git a/lib/ansible/module_utils/facts/virtual/openbsd.py b/lib/ansible/module_utils/facts/virtual/openbsd.py index 42daa3375ef..c449028d42d 100644 --- a/lib/ansible/module_utils/facts/virtual/openbsd.py +++ b/lib/ansible/module_utils/facts/virtual/openbsd.py @@ -35,16 +35,23 @@ class OpenBSDVirtual(Virtual, VirtualSysctlDetectionMixin): def get_virtual_facts(self): virtual_facts = {} + host_tech = set() + guest_tech = set() # Set empty values as default virtual_facts['virtualization_type'] = '' virtual_facts['virtualization_role'] = '' virtual_product_facts = self.detect_virt_product('hw.product') + guest_tech.update(virtual_product_facts['virtualization_tech_guest']) + host_tech.update(virtual_product_facts['virtualization_tech_host']) virtual_facts.update(virtual_product_facts) + virtual_vendor_facts = self.detect_virt_vendor('hw.vendor') + guest_tech.update(virtual_vendor_facts['virtualization_tech_guest']) + host_tech.update(virtual_vendor_facts['virtualization_tech_host']) + if virtual_facts['virtualization_type'] == '': - virtual_vendor_facts = self.detect_virt_vendor('hw.vendor') virtual_facts.update(virtual_vendor_facts) # Check the dmesg if vmm(4) attached, indicating the host is @@ -53,9 +60,12 @@ class OpenBSDVirtual(Virtual, VirtualSysctlDetectionMixin): for line in dmesg_boot.splitlines(): match = re.match('^vmm0 at mainbus0: (SVM/RVI|VMX/EPT)$', line) if match: + host_tech.add('vmm') virtual_facts['virtualization_type'] = 'vmm' virtual_facts['virtualization_role'] = 'host' + virtual_facts['virtualization_tech_guest'] = guest_tech + virtual_facts['virtualization_tech_host'] = host_tech return virtual_facts diff --git a/lib/ansible/module_utils/facts/virtual/sunos.py b/lib/ansible/module_utils/facts/virtual/sunos.py index 06ce661a025..1e92677e4ce 100644 --- a/lib/ansible/module_utils/facts/virtual/sunos.py +++ b/lib/ansible/module_utils/facts/virtual/sunos.py @@ -32,19 +32,27 @@ class SunOSVirtual(Virtual): def get_virtual_facts(self): virtual_facts = {} - # Check if it's a zone + host_tech = set() + guest_tech = set() + # Check if it's a zone zonename = self.module.get_bin_path('zonename') if zonename: rc, out, err = self.module.run_command(zonename) - if rc == 0 and out.rstrip() != "global": - virtual_facts['container'] = 'zone' + if rc == 0: + if out.rstrip() == "global": + host_tech.add('zone') + else: + guest_tech.add('zone') + virtual_facts['container'] = 'zone' + # Check if it's a branded zone (i.e. Solaris 8/9 zone) if os.path.isdir('/.SUNWnative'): + guest_tech.add('zone') virtual_facts['container'] = 'zone' + # If it's a zone check if we can detect if our global zone is itself virtualized. # Relies on the "guest tools" (e.g. vmware tools) to be installed - if 'container' in virtual_facts and virtual_facts['container'] == 'zone': modinfo = self.module.get_bin_path('modinfo') if modinfo: @@ -52,13 +60,16 @@ class SunOSVirtual(Virtual): if rc == 0: for line in out.splitlines(): if 'VMware' in line: + guest_tech.add('vmware') virtual_facts['virtualization_type'] = 'vmware' virtual_facts['virtualization_role'] = 'guest' if 'VirtualBox' in line: + guest_tech.add('virtualbox') virtual_facts['virtualization_type'] = 'virtualbox' virtual_facts['virtualization_role'] = 'guest' if os.path.exists('/proc/vz'): + guest_tech.add('virtuozzo') virtual_facts['virtualization_type'] = 'virtuozzo' virtual_facts['virtualization_role'] = 'guest' @@ -77,6 +88,7 @@ class SunOSVirtual(Virtual): for line in out.splitlines(): fields = line.split('|') if fields[0] == 'DOMAINROLE' and fields[1] == 'impl=LDoms': + guest_tech.add('ldom') virtual_facts['virtualization_type'] = 'ldom' virtual_facts['virtualization_role'] = 'guest' hostfeatures = [] @@ -97,21 +109,28 @@ class SunOSVirtual(Virtual): if rc == 0: for line in out.splitlines(): if 'VMware' in line: + guest_tech.add('vmware') virtual_facts['virtualization_type'] = 'vmware' virtual_facts['virtualization_role'] = 'guest' elif 'Parallels' in line: + guest_tech.add('parallels') virtual_facts['virtualization_type'] = 'parallels' virtual_facts['virtualization_role'] = 'guest' elif 'VirtualBox' in line: + guest_tech.add('virtualbox') virtual_facts['virtualization_type'] = 'virtualbox' virtual_facts['virtualization_role'] = 'guest' elif 'HVM domU' in line: + guest_tech.add('xen') virtual_facts['virtualization_type'] = 'xen' virtual_facts['virtualization_role'] = 'guest' elif 'KVM' in line: + guest_tech.add('kvm') virtual_facts['virtualization_type'] = 'kvm' virtual_facts['virtualization_role'] = 'guest' + virtual_facts['virtualization_tech_guest'] = guest_tech + virtual_facts['virtualization_tech_host'] = host_tech return virtual_facts diff --git a/lib/ansible/module_utils/facts/virtual/sysctl.py b/lib/ansible/module_utils/facts/virtual/sysctl.py index 62bd8d406d7..1c7b2b34848 100644 --- a/lib/ansible/module_utils/facts/virtual/sysctl.py +++ b/lib/ansible/module_utils/facts/virtual/sysctl.py @@ -25,48 +25,88 @@ class VirtualSysctlDetectionMixin(object): def detect_virt_product(self, key): virtual_product_facts = {} + host_tech = set() + guest_tech = set() + + # We do similar to what we do in linux.py -- We want to allow multiple + # virt techs to show up, but maintain compatibility, so we have to track + # when we would have stopped, even though now we go through everything. + found_virt = False + self.detect_sysctl() if self.sysctl_path: rc, out, err = self.module.run_command("%s -n %s" % (self.sysctl_path, key)) if rc == 0: if re.match('(KVM|kvm|Bochs|SmartDC).*', out): - virtual_product_facts['virtualization_type'] = 'kvm' - virtual_product_facts['virtualization_role'] = 'guest' - elif re.match('.*VMware.*', out): - virtual_product_facts['virtualization_type'] = 'VMware' - virtual_product_facts['virtualization_role'] = 'guest' - elif out.rstrip() == 'VirtualBox': - virtual_product_facts['virtualization_type'] = 'virtualbox' - virtual_product_facts['virtualization_role'] = 'guest' - elif re.match('(HVM domU|XenPVH|XenPV|XenPVHVM).*', out): - virtual_product_facts['virtualization_type'] = 'xen' - virtual_product_facts['virtualization_role'] = 'guest' - elif out.rstrip() == 'Hyper-V': - virtual_product_facts['virtualization_type'] = 'Hyper-V' - virtual_product_facts['virtualization_role'] = 'guest' - elif out.rstrip() == 'Parallels': - virtual_product_facts['virtualization_type'] = 'parallels' - virtual_product_facts['virtualization_role'] = 'guest' - elif out.rstrip() == 'RHEV Hypervisor': - virtual_product_facts['virtualization_type'] = 'RHEV' - virtual_product_facts['virtualization_role'] = 'guest' - elif (key == 'security.jail.jailed') and (out.rstrip() == '1'): - virtual_product_facts['virtualization_type'] = 'jails' - virtual_product_facts['virtualization_role'] = 'guest' + guest_tech.add('kvm') + if not found_virt: + virtual_product_facts['virtualization_type'] = 'kvm' + virtual_product_facts['virtualization_role'] = 'guest' + found_virt = True + if re.match('.*VMware.*', out): + guest_tech.add('VMware') + if not found_virt: + virtual_product_facts['virtualization_type'] = 'VMware' + virtual_product_facts['virtualization_role'] = 'guest' + found_virt = True + if out.rstrip() == 'VirtualBox': + guest_tech.add('virtualbox') + if not found_virt: + virtual_product_facts['virtualization_type'] = 'virtualbox' + virtual_product_facts['virtualization_role'] = 'guest' + found_virt = True + if re.match('(HVM domU|XenPVH|XenPV|XenPVHVM).*', out): + guest_tech.add('xen') + if not found_virt: + virtual_product_facts['virtualization_type'] = 'xen' + virtual_product_facts['virtualization_role'] = 'guest' + found_virt = True + if out.rstrip() == 'Hyper-V': + guest_tech.add('Hyper-V') + if not found_virt: + virtual_product_facts['virtualization_type'] = 'Hyper-V' + virtual_product_facts['virtualization_role'] = 'guest' + found_virt = True + if out.rstrip() == 'Parallels': + guest_tech.add('parallels') + if not found_virt: + virtual_product_facts['virtualization_type'] = 'parallels' + virtual_product_facts['virtualization_role'] = 'guest' + found_virt = True + if out.rstrip() == 'RHEV Hypervisor': + guest_tech.add('RHEV') + if not found_virt: + virtual_product_facts['virtualization_type'] = 'RHEV' + virtual_product_facts['virtualization_role'] = 'guest' + found_virt = True + if (key == 'security.jail.jailed') and (out.rstrip() == '1'): + guest_tech.add('jails') + if not found_virt: + virtual_product_facts['virtualization_type'] = 'jails' + virtual_product_facts['virtualization_role'] = 'guest' + found_virt = True + virtual_product_facts['virtualization_tech_guest'] = guest_tech + virtual_product_facts['virtualization_tech_host'] = host_tech return virtual_product_facts def detect_virt_vendor(self, key): virtual_vendor_facts = {} + host_tech = set() + guest_tech = set() self.detect_sysctl() if self.sysctl_path: rc, out, err = self.module.run_command("%s -n %s" % (self.sysctl_path, key)) if rc == 0: if out.rstrip() == 'QEMU': + guest_tech.add('kvm') virtual_vendor_facts['virtualization_type'] = 'kvm' virtual_vendor_facts['virtualization_role'] = 'guest' if out.rstrip() == 'OpenBSD': + guest_tech.add('vmm') virtual_vendor_facts['virtualization_type'] = 'vmm' virtual_vendor_facts['virtualization_role'] = 'guest' + virtual_vendor_facts['virtualization_tech_guest'] = guest_tech + virtual_vendor_facts['virtualization_tech_host'] = host_tech return virtual_vendor_facts