Repository: ansible-collections/community.general

Issue:
Support Incus in lxd_container module
### Summary

When I try to use https://docs.ansible.com/ansible/latest/collections/community/general/lxd_container_module.html to create a container, I get an error that `/1.0/instances` does not exist.

Incus is a fork of LXD by the original maintainers. According to them, this API path is deprecated even in LXD proper: https://discuss.linuxcontainers.org/t/ansible-guidance-receiving-errors-when-using-lxd-container-plugin/18734

### Issue Type

Bug Report

### Component Name

lxd_container

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.11.7]
  config file = None
  configured module search path = ['/Users/jmaguire/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/homebrew/lib/python3.9/site-packages/ansible
  ansible collection location = /Users/jmaguire/.ansible/collections:/usr/share/ansible/collections
  executable location = /opt/homebrew/bin/ansible
  python version = 3.9.18 (main, Aug 24 2023, 21:19:58) [Clang 14.0.3 (clang-1403.0.22.14.1)]
  jinja version = 3.0.3
  libyaml = True
```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general

# /opt/homebrew/lib/python3.9/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 3.8.0

# /Users/jmaguire/.ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 6.2.0

```


### Configuration

I do not have it handy.

### OS / Environment

Arch Linux, Incus 0.4-2

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
community.general.lxd_container:
   name: docker
   state: started
   type: container
   config:
     security.nesting: "true"
     security.syscalls.intercept.mknod: "true"
     security.syscalls.intercept.setxattr: "true"
   devices:
     docker:
       path: /var/lib/docker
       pool: default
       type: disk
       source: docker
   source:
     protocol: simplestreams
     type: image
     mode: pull
     server: https://images.linuxcontainers.org
     alias: debian/bookworm
   profiles: ["default"]
   wait_for_ipv4_addresses: true
   timeout: 600
   url: "unix:/var/lib/incus/unix.socket"
```


### Expected Results

A new LXD container named docker should be created.

### Actual Results

I do not have it handy, but I received a 404 error from the daemon.

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/lxd_container.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/lxd_container.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/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Resolving patch:
diff --git a/plugins/modules/lxd_container.py b/plugins/modules/lxd_container.py
--- a/plugins/modules/lxd_container.py
+++ b/plugins/modules/lxd_container.py
@@ -436,12 +436,12 @@
 
 # CONFIG_PARAMS is a list of config attribute names.
 CONFIG_PARAMS = [
-    'architecture', 'config', 'devices', 'ephemeral', 'profiles', 'source'
+    'architecture', 'config', 'devices', 'ephemeral', 'profiles', 'source', 'type'
 ]
 
 # CONFIG_CREATION_PARAMS is a list of attribute names that are only applied
 # on instance creation.
-CONFIG_CREATION_PARAMS = ['source']
+CONFIG_CREATION_PARAMS = ['source', 'type']
 
 
 class LXDContainerManagement(object):
@@ -467,13 +467,6 @@ def __init__(self, module):
 
         self.type = self.module.params['type']
 
-        # LXD Rest API provides additional endpoints for creating containers and virtual-machines.
-        self.api_endpoint = None
-        if self.type == 'container':
-            self.api_endpoint = '/1.0/containers'
-        elif self.type == 'virtual-machine':
-            self.api_endpoint = '/1.0/virtual-machines'
-
         self.key_file = self.module.params.get('client_key')
         if self.key_file is None:
             self.key_file = '{0}/.config/lxc/client.key'.format(os.environ['HOME'])
@@ -499,6 +492,18 @@ def __init__(self, module):
             )
         except LXDClientException as e:
             self.module.fail_json(msg=e.msg)
+
+        # LXD (3.19) Rest API provides instances endpoint, failback to containers and virtual-machines
+        # https://documentation.ubuntu.com/lxd/en/latest/rest-api/#instances-containers-and-virtual-machines
+        self.api_endpoint = '/1.0/instances'
+        check_api_endpoint = self.client.do('GET', '{0}?project='.format(self.api_endpoint), ok_error_codes=[404])
+
+        if check_api_endpoint['error_code'] == 404:
+            if self.type == 'container':
+                self.api_endpoint = '/1.0/containers'
+            elif self.type == 'virtual-machine':
+                self.api_endpoint = '/1.0/virtual-machines'
+
         self.trust_password = self.module.params.get('trust_password', None)
         self.actions = []
         self.diff = {'before': {}, 'after': {}}
@@ -551,6 +556,8 @@ def _create_instance(self):
             url = '{0}?{1}'.format(url, urlencode(url_params))
         config = self.config.copy()
         config['name'] = self.name
+        if self.type not in self.api_endpoint:
+            config['type'] = self.type
         if not self.module.check_mode:
             self.client.do('POST', url, config, wait_for_container=self.wait_for_container)
         self.actions.append('create')


Test patch: