Repository: ansible/ansible-modules-extras

Issue:
lxc-create fails when using lxc_path parameter
Hi,

There is an issue when I use the lxc_path parameter where ansible will mark the task as fail, although the actual container itself is correctly created in the target host. It seems like the module tries to recreate the container twice.

Note that before every run, I ensure that the container has been deleted by running lxc-destroy -n <container-name>  -P /mnt/lxc2 on the target host

Here's my task to create the lxc container in my Ansible Role file

-name: Create LXC container
  lxc_container:
   name: test
   container_log: true
   lxc_path: /mnt/lxc2/
   template: centos
   state: stopped

Ansible Playbook run output
TASK: [lxc | Create LXC container] *******************************************\* 
failed: [x.x.x.x] => {"command": "/usr/bin/lxc-create --name test --quiet --config /etc/lxc/default.conf --template centos --lxcpath /mnt/lxc2 --bdev dir --logfile /var/log/lxc/lxc-test.log --logpriority INFO", "err": "Container already exists\n", "failed": true, "rc": 1}
msg: Failed executing lxc-create.



Discussion:
Hi @quattro1982 ,

Couple of questions: 
- What version of ansible were you using and how was it installed?
- is the container content being dropped into the `/mnt/lxc2/` directory or somewhere else?
- is this same failure happening with other templates?

Thanks for the report and with a bit more data I'd be happy to dive in and see what I can do to make it better. 

Seeing the same problem here. Ansible 2.0.0.2-2, installed from Ubuntu 16.04 repos.
Container is being dropped in the path specified with lxc_path.
Happens with the download template, for --dist ubuntu --release trusty --arch amd64 and --dist centos --release --arch amd64.

```
TASK [lxc : Create LXC container] **********************************************
task path: /home/stijn/ansible/roles/lxc/tasks/main.yml:14
ESTABLISH LOCAL CONNECTION FOR USER: stijn
localhost EXEC ( umask 22 && mkdir -p "$( echo $HOME/.ansible/tmp/ansible-tmp-1459958697.73-151870222614858 )" && echo "$( echo $HOME/.ansible/tmp/ansible-tmp-1459958697.73-151870222614858 )" )
localhost PUT /tmp/tmpNKENOX TO /home/stijn/.ansible/tmp/ansible-tmp-1459958697.73-151870222614858/lxc_container
localhost EXEC LANG=nl_BE.UTF-8 LC_ALL=nl_BE.UTF-8 LC_MESSAGES=nl_BE.UTF-8 /usr/bin/python /home/stijn/.ansible/tmp/ansible-tmp-1459958697.73-151870222614858/lxc_container; rm -rf "/home/stijn/.ansible/tmp/ansible-tmp-1459958697.73-151870222614858/" > /dev/null 2>&1
failed: [localhost] => (item={u'template_options': u'--dist centos --release 7 --arch amd64', u'state': u'started', u'name': u'foreman-master', u'template': u'download'}) => {"command": "/usr/bin/lxc-create --name foreman-master --quiet --config /etc/lxc/default.conf --template download --lxcpath /home/stijn/Containers --bdev dir -- --dist centos --release 7 --arch amd64", "err": "Container already exists\n", "failed": true, "invocation": {"module_args": {"archive": "false", "archive_compression": "gzip", "archive_path": "/tmp", "backing_store": "dir", "clone_name": null, "clone_snapshot": "false", "config": "/etc/lxc/default.conf", "container_command": null, "container_config": null, "container_log": false, "container_log_level": "DEBUG", "directory": null, "fs_size": "5G", "fs_type": "ext4", "lv_name": "foreman-master", "lxc_path": "/home/stijn/Containers", "name": "foreman-master", "state": "started", "template": "download", "template_options": "--dist centos --release 7 --arch amd64", "thinpool": null, "vg_name": "lxc", "zfs_root": null}, "module_name": "lxc_container"}, "item": {"name": "foreman-master", "state": "started", "template": "download", "template_options": "--dist centos --release 7 --arch amd64"}, "msg": "Failed executing lxc-create.", "rc": 1}
```

Thanks @stintel for the additional data. I will try and reproduce this and figure out the root cause in the coming days provided I find a bit more time to do so. 

@cloudnull thanks. FYI, I have just tried with devel fd9838f1a0b2cef54b9ba84df6db03248e7d5c58 and hacking/env-setup, and the problem exists there as well. Without lxc_path, it works just fine.

Hi all,

I am actually running 2.1.0.0. Problem seems to be still there. Any way to circumvent this ?

```
- hosts: 127.0.0.1 ansible_connection=local
  become: yes

  tasks:

    - name: Create a Alpine container
      lxc_container:
        name: Alpine-3.3.x
        backing_store: zfs
        zfs_root: bacinella
        lxc_path: /bacinella
        template: download
        template_options: --dist alpine --release 3.3 --arch amd64
        container_command: |
          adduser -h /home/sysop -s /bin/bash -G wheel -S sysop
      tags: Alpine
```

```
fatal: [127.0.0.1]: FAILED! => {"changed": false, "command": "/usr/bin/lxc-create --name Alpine-3.3.x --quiet --zfsroot bacinella --template download --lxcpath /bacinella --bdev zfs -- --dist alpine --release 3.3 --arch amd64", "err": "Container already exists\n", "failed": true, "msg": "Failed executing lxc-create.", "rc": 1}
```

Container is placed in the given `lxc_path` and is bootable but the rest (`container_command`) is not applied which is a bit inconvenient because it is impossible to provision the LXC.

The use of the CLI

```
# lxc-create --name Alpine-3.3.x --zfsroot bacinella --template download --lxcpath /bacinella --bdev zfs -- --dist alpine --release 3.3 --arch amd64
```

works fine.

kind regards

Thanks for he update. Sorry for the reply delay, I'm currently on vacation without a laptop but I've added this to my todo list and will try resolve it as soon as I'm back in action near the end of the month (assuming it's not fixed sooner by someone else). 


Resolving patch:
diff --git a/cloud/lxc/lxc_container.py b/cloud/lxc/lxc_container.py
--- a/cloud/lxc/lxc_container.py
+++ b/cloud/lxc/lxc_container.py
@@ -603,6 +603,7 @@ def __init__(self, module):
         self.state = self.module.params.get('state', None)
         self.state_change = False
         self.lxc_vg = None
+        self.lxc_path = self.module.params.get('lxc_path', None)
         self.container_name = self.module.params['name']
         self.container = self.get_container_bind()
         self.archive_info = None
@@ -627,7 +628,7 @@ def _roundup(num):
         return num
 
     @staticmethod
-    def _container_exists(container_name):
+    def _container_exists(container_name, lxc_path=None):
         """Check if a container exists.
 
         :param container_name: Name of the container.
@@ -635,7 +636,7 @@ def _container_exists(container_name):
         :returns: True or False if the container is found.
         :rtype: ``bol``
         """
-        if [i for i in lxc.list_containers() if i == container_name]:
+        if [i for i in lxc.list_containers(config_path=lxc_path) if i == container_name]:
             return True
         else:
             return False
@@ -944,7 +945,7 @@ def _get_state(self):
         :rtype: ``str``
         """
 
-        if self._container_exists(container_name=self.container_name):
+        if self._container_exists(container_name=self.container_name, lxc_path=self.lxc_path):
             return str(self.container.state).lower()
         else:
             return str('absent')
@@ -1009,7 +1010,7 @@ def _check_clone(self):
 
         clone_name = self.module.params.get('clone_name')
         if clone_name:
-            if not self._container_exists(container_name=clone_name):
+            if not self._container_exists(container_name=clone_name, lxc_path=self.lxc_path):
                 self.clone_info = {
                     'cloned': self._container_create_clone()
                 }
@@ -1026,7 +1027,7 @@ def _destroyed(self, timeout=60):
         """
 
         for _ in xrange(timeout):
-            if not self._container_exists(container_name=self.container_name):
+            if not self._container_exists(container_name=self.container_name, lxc_path=self.lxc_path):
                 break
 
             # Check if the container needs to have an archive created.
@@ -1065,7 +1066,7 @@ def _frozen(self, count=0):
         """
 
         self.check_count(count=count, method='frozen')
-        if self._container_exists(container_name=self.container_name):
+        if self._container_exists(container_name=self.container_name, lxc_path=self.lxc_path):
             self._execute_command()
 
             # Perform any configuration updates
@@ -1102,7 +1103,7 @@ def _restarted(self, count=0):
         """
 
         self.check_count(count=count, method='restart')
-        if self._container_exists(container_name=self.container_name):
+        if self._container_exists(container_name=self.container_name, lxc_path=self.lxc_path):
             self._execute_command()
 
             # Perform any configuration updates
@@ -1135,7 +1136,7 @@ def _stopped(self, count=0):
         """
 
         self.check_count(count=count, method='stop')
-        if self._container_exists(container_name=self.container_name):
+        if self._container_exists(container_name=self.container_name, lxc_path=self.lxc_path):
             self._execute_command()
 
             # Perform any configuration updates
@@ -1165,7 +1166,7 @@ def _started(self, count=0):
         """
 
         self.check_count(count=count, method='start')
-        if self._container_exists(container_name=self.container_name):
+        if self._container_exists(container_name=self.container_name, lxc_path=self.lxc_path):
             container_state = self._get_state()
             if container_state == 'running':
                 pass


Test patch: