Repository: ansible/ansible

Issue:
detecting virtualization facts fails with host xenial and guest trusty
<!--- Verify first that your issue/request is not already reported in GitHub -->
##### ISSUE TYPE

<!--- Pick one below and delete the rest: -->
- Bug Report
##### ANSIBLE VERSION

<!--- Paste verbatim output from “ansible --version” between quotes below -->

```
ansible 2.0.0.2
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```
##### 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.
-->

Ubuntu 16.04
##### SUMMARY

<!--- Explain the problem briefly -->

Ansible is not able to detect the virtualization correctly if the host is an Ubuntu 16.04 Xenial and the guest is an Ubuntu 14.04 Trusty using LXC. On guests with 16.04 it is detecting everything correctly though.
##### STEPS TO REPRODUCE

<!---
For bugs, show exactly how to reproduce the problem.
For new features, show how the feature would be used.
-->

Setup a host with Ubuntu 16.04. Install 2 LXC guest systems on this server. One with 16.04, the other with 14.04.

After that just try to gather facts on the 3 machines like the following:

<!--- Paste example playbooks or commands between quotes below -->

```
# ansible xenial_host -m setup -a "filter=ansible_virtualization_*"
xenial_host | SUCCESS => {
    "ansible_facts": {
        "ansible_virtualization_role": "host",
        "ansible_virtualization_type": "kvm"
    },
    "changed": false
}
```

```
# ansible xenial_guest -m setup -a "filter=ansible_virtualization_*"
xenial_guest | SUCCESS => {
    "ansible_facts": {
        "ansible_virtualization_role": "guest",
        "ansible_virtualization_type": "lxc"
    },
    "changed": false
}
```

```
# ansible trusty_guest -m setup -a "filter=ansible_virtualization_*"
trusty_guest | SUCCESS => {
    "ansible_facts": {
        "ansible_virtualization_role": "host",
        "ansible_virtualization_type": "kvm"
    },
    "changed": false
}
```

<!--- You can also paste gist.github.com links for larger files -->
##### EXPECTED RESULTS

<!--- What did you expect to happen when running the steps above? -->

The trusty_guest should have the same result as the xenial_guest. both are LXC guest systems on xenial_host. The result should be

```
# ansible trusty_guest -m setup -a "filter=ansible_virtualization_*"
trusty_guest | SUCCESS => {
    "ansible_facts": {
        "ansible_virtualization_role": "guest",
        "ansible_virtualization_type": "lxc"
    },
    "changed": false
}
```
##### ACTUAL RESULTS

<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->

```
# ansible trusty_guest -m setup -a "filter=ansible_virtualization_*"
trusty_guest | SUCCESS => {
    "ansible_facts": {
        "ansible_virtualization_role": "host",
        "ansible_virtualization_type": "kvm"
    },
    "changed": false
}
```

Maybe this one is related to #15165, but not exactly the same setup or environment.



Discussion:
I double checked the results on a LXC guest running 14.04, but this time the host was an Ubuntu 14.04 as well. In this case the virtualization detection works. Only if the host system is a 16.04 it is not working.

So the current detection uses the existence of `/proc/1/cgroup` AND the existence of `/lxc/` inside that file as the way to detect virtualization, has this changed on Ubuntu 16?

In any case that should not set `kvm`, this happens through other checks, it is not a default

yes, it has changed.

if the host is a 14.04 the content looks like

```
# cat /proc/1/cgroup
11:hugetlb:/lxc/abbe
10:perf_event:/lxc/abbe
9:blkio:/lxc/abbe
8:freezer:/lxc/abbe
7:devices:/lxc/abbe
6:memory:/lxc/abbe
5:cpuacct:/lxc/abbe
4:cpu:/lxc/abbe
3:cpuset:/lxc/abbe
2:name=systemd:/lxc/abbe
```

BUT if the host is a 16.04 the content looks like

```
$ cat /proc/1/cgroup
11:pids:/
10:memory:/
9:cpu,cpuacct:/
8:hugetlb:/
7:net_cls,net_prio:/
6:blkio:/
5:devices:/
4:perf_event:/
3:freezer:/
2:cpuset:/
1:name=systemd:/
```

under ubuntu 16.04 you might be able to detect LXC guests by looking into /proc/1/mounts, because some /procs are mounted there:

```
$ cat /proc/1/mounts | grep lxc
lxcfs /proc/cpuinfo fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/diskstats fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/meminfo fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/stat fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/swaps fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
lxcfs /proc/uptime fuse.lxcfs rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other 0 0
devpts /dev/lxc/console devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
devpts /dev/lxc/tty1 devpts rw,relatime,gid=5,mode=620,ptmxmode=666 0 0
devpts /dev/lxc/tty2 devpts rw,relatime,gid=5,mode=620,ptmxmode=666 0 0
devpts /dev/lxc/tty3 devpts rw,relatime,gid=5,mode=620,ptmxmode=666 0 0
devpts /dev/lxc/tty4 devpts rw,relatime,gid=5,mode=620,ptmxmode=666 0 0
```

How do you detect a guest in an Ubuntu 16.04 guest? Because the detection is working there, even though the cgroup file has no LXC in it.

This is the output of an Ubuntu 16.04 guest running on an 16.04 host:

```
# cat /proc/1/cgroup
11:pids:/
10:memory:/
9:cpu,cpuacct:/
8:hugetlb:/
7:net_cls,net_prio:/
6:blkio:/
5:devices:/init.scope
4:perf_event:/
3:freezer:/
2:cpuset:/
1:name=systemd:/init.scope
```

So the problem for ansible is, that the container detection is not based on the guest OS, but on the underlying host OS. 

Some people suggest to look for the existance of /dev/lxc/console, which do exists on all our LXC guests no matter of the underlying OS/LXC version.


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
@@ -2877,6 +2877,7 @@ def populate(self):
 
     # For more information, check: http://people.redhat.com/~rjones/virt-what/
     def get_virtual_facts(self):
+        # old 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):
@@ -2888,6 +2889,14 @@ def get_virtual_facts(self):
                     self.facts['virtualization_role'] = 'guest'
                     return
 
+        # newer lxc does not appear in cgroups anymore but sets 'container=lxc' environment var
+        if os.path.exists('/proc/1/environ'):
+            for line in get_file_lines('/proc/1/environ'):
+                if re.search('container=lxc', line):
+                    self.facts['virtualization_type'] = 'lxc'
+                    self.facts['virtualization_role'] = 'guest'
+                    return
+
         if os.path.exists('/proc/vz'):
             self.facts['virtualization_type'] = 'openvz'
             if os.path.exists('/proc/bc'):


Test patch: