From aeece5a107eea5cf743c5331b6f52ca243ec7cb5 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 22 Apr 2022 22:49:29 +0200 Subject: [PATCH] Add project support for lxd_container and lxd_profile module (#4479) (#4561) * add project support for lxd modules * fix lxd_container yaml format error * add changelog fragement add version_add entry * fix LXD spelling * complete lxd_profile example (cherry picked from commit 552db0d3533a59c79206e3576d3535db639e3f8b) Co-authored-by: Raymond Chang --- ...port-for-lxd_container-and-lxd_profile.yml | 3 + plugins/modules/cloud/lxd/lxd_container.py | 75 +++++++++++++++---- plugins/modules/cloud/lxd/lxd_profile.py | 53 +++++++++++-- 3 files changed, 108 insertions(+), 23 deletions(-) create mode 100644 changelogs/fragments/4479-add-project-support-for-lxd_container-and-lxd_profile.yml diff --git a/changelogs/fragments/4479-add-project-support-for-lxd_container-and-lxd_profile.yml b/changelogs/fragments/4479-add-project-support-for-lxd_container-and-lxd_profile.yml new file mode 100644 index 0000000000..5ea1448c0b --- /dev/null +++ b/changelogs/fragments/4479-add-project-support-for-lxd_container-and-lxd_profile.yml @@ -0,0 +1,3 @@ +minor_changes: + - lxd_container - adds ``project`` option to allow selecting project for LXD instance (https://github.com/ansible-collections/community.general/pull/4479). + - lxd_profile - adds ``project`` option to allow selecting project for LXD profile (https://github.com/ansible-collections/community.general/pull/4479). diff --git a/plugins/modules/cloud/lxd/lxd_container.py b/plugins/modules/cloud/lxd/lxd_container.py index bd2326684b..27f8409bc8 100644 --- a/plugins/modules/cloud/lxd/lxd_container.py +++ b/plugins/modules/cloud/lxd/lxd_container.py @@ -21,6 +21,13 @@ options: - Name of an instance. type: str required: true + project: + description: + - 'Project of an instance. + See U(https://github.com/lxc/lxd/blob/master/doc/projects.md).' + required: false + type: str + version_added: 4.8.0 architecture: description: - 'The architecture for the instance (for example C(x86_64) or C(i686)). @@ -248,6 +255,26 @@ EXAMPLES = ''' wait_for_ipv4_addresses: true timeout: 600 +# An example for creating container in project other than default +- hosts: localhost + connection: local + tasks: + - name: Create a started container in project mytestproject + community.general.lxd_container: + name: mycontainer + project: mytestproject + ignore_volatile_options: true + state: started + source: + protocol: simplestreams + type: image + mode: pull + server: https://images.linuxcontainers.org + alias: ubuntu/20.04/cloud + profiles: ["default"] + wait_for_ipv4_addresses: true + timeout: 600 + # An example for deleting a container - hosts: localhost connection: local @@ -412,6 +439,7 @@ class LXDContainerManagement(object): """ self.module = module self.name = self.module.params['name'] + self.project = self.module.params['project'] self._build_config() self.state = self.module.params['state'] @@ -468,16 +496,16 @@ class LXDContainerManagement(object): self.config[attr] = param_val def _get_instance_json(self): - return self.client.do( - 'GET', '{0}/{1}'.format(self.api_endpoint, self.name), - ok_error_codes=[404] - ) + url = '{0}/{1}'.format(self.api_endpoint, self.name) + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) + return self.client.do('GET', url, ok_error_codes=[404]) def _get_instance_state_json(self): - return self.client.do( - 'GET', '{0}/{1}/state'.format(self.api_endpoint, self.name), - ok_error_codes=[404] - ) + url = '{0}/{1}/state'.format(self.api_endpoint, self.name) + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) + return self.client.do('GET', url, ok_error_codes=[404]) @staticmethod def _instance_json_to_module_state(resp_json): @@ -486,18 +514,26 @@ class LXDContainerManagement(object): return ANSIBLE_LXD_STATES[resp_json['metadata']['status']] def _change_state(self, action, force_stop=False): + url = '{0}/{1}/state'.format(self.api_endpoint, self.name) + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) body_json = {'action': action, 'timeout': self.timeout} if force_stop: body_json['force'] = True - return self.client.do('PUT', '{0}/{1}/state'.format(self.api_endpoint, self.name), body_json=body_json) + return self.client.do('PUT', url, body_json=body_json) def _create_instance(self): + url = self.api_endpoint + url_params = dict() + if self.target: + url_params['target'] = self.target + if self.project: + url_params['project'] = self.project + if url_params: + url = '{0}?{1}'.format(url, urlencode(url_params)) config = self.config.copy() config['name'] = self.name - if self.target: - self.client.do('POST', '{0}?{1}'.format(self.api_endpoint, urlencode(dict(target=self.target))), config, wait_for_container=self.wait_for_container) - else: - self.client.do('POST', self.api_endpoint, config, wait_for_container=self.wait_for_container) + self.client.do('POST', url, config, wait_for_container=self.wait_for_container) self.actions.append('create') def _start_instance(self): @@ -513,7 +549,10 @@ class LXDContainerManagement(object): self.actions.append('restart') def _delete_instance(self): - self.client.do('DELETE', '{0}/{1}'.format(self.api_endpoint, self.name)) + url = '{0}/{1}'.format(self.api_endpoint, self.name) + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) + self.client.do('DELETE', url) self.actions.append('delete') def _freeze_instance(self): @@ -666,7 +705,10 @@ class LXDContainerManagement(object): if self._needs_to_change_instance_config('profiles'): body_json['profiles'] = self.config['profiles'] - self.client.do('PUT', '{0}/{1}'.format(self.api_endpoint, self.name), body_json=body_json) + url = '{0}/{1}'.format(self.api_endpoint, self.name) + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) + self.client.do('PUT', url, body_json=body_json) self.actions.append('apply_instance_configs') def run(self): @@ -715,6 +757,9 @@ def main(): type='str', required=True ), + project=dict( + type='str', + ), architecture=dict( type='str', ), diff --git a/plugins/modules/cloud/lxd/lxd_profile.py b/plugins/modules/cloud/lxd/lxd_profile.py index 3094898f2c..82244f0bac 100644 --- a/plugins/modules/cloud/lxd/lxd_profile.py +++ b/plugins/modules/cloud/lxd/lxd_profile.py @@ -21,6 +21,13 @@ options: - Name of a profile. required: true type: str + project: + description: + - 'Project of a profile. + See U(https://github.com/lxc/lxd/blob/master/doc/projects.md).' + type: str + required: false + version_added: 4.8.0 description: description: - Description of the profile. @@ -129,6 +136,19 @@ EXAMPLES = ''' parent: br0 type: nic +# An example for creating a profile in project mytestproject +- hosts: localhost + connection: local + tasks: + - name: Create a profile + community.general.lxd_profile: + name: testprofile + project: mytestproject + state: present + config: {} + description: test profile in project mytestproject + devices: {} + # An example for creating a profile via http connection - hosts: localhost connection: local @@ -208,6 +228,7 @@ actions: import os from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.general.plugins.module_utils.lxd import LXDClient, LXDClientException +from ansible.module_utils.six.moves.urllib.parse import urlencode # ANSIBLE_LXD_DEFAULT_URL is a default value of the lxd endpoint ANSIBLE_LXD_DEFAULT_URL = 'unix:/var/lib/lxd/unix.socket' @@ -232,6 +253,7 @@ class LXDProfileManagement(object): """ self.module = module self.name = self.module.params['name'] + self.project = self.module.params['project'] self._build_config() self.state = self.module.params['state'] self.new_name = self.module.params.get('new_name', None) @@ -272,10 +294,10 @@ class LXDProfileManagement(object): self.config[attr] = param_val def _get_profile_json(self): - return self.client.do( - 'GET', '/1.0/profiles/{0}'.format(self.name), - ok_error_codes=[404] - ) + url = '/1.0/profiles/{0}'.format(self.name) + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) + return self.client.do('GET', url, ok_error_codes=[404]) @staticmethod def _profile_json_to_module_state(resp_json): @@ -307,14 +329,20 @@ class LXDProfileManagement(object): changed=False) def _create_profile(self): + url = '/1.0/profiles' + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) config = self.config.copy() config['name'] = self.name - self.client.do('POST', '/1.0/profiles', config) + self.client.do('POST', url, config) self.actions.append('create') def _rename_profile(self): + url = '/1.0/profiles/{0}'.format(self.name) + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) config = {'name': self.new_name} - self.client.do('POST', '/1.0/profiles/{0}'.format(self.name), config) + self.client.do('POST', url, config) self.actions.append('rename') self.name = self.new_name @@ -421,11 +449,17 @@ class LXDProfileManagement(object): config = self._generate_new_config(config) # upload config to lxd - self.client.do('PUT', '/1.0/profiles/{0}'.format(self.name), config) + url = '/1.0/profiles/{0}'.format(self.name) + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) + self.client.do('PUT', url, config) self.actions.append('apply_profile_configs') def _delete_profile(self): - self.client.do('DELETE', '/1.0/profiles/{0}'.format(self.name)) + url = '/1.0/profiles/{0}'.format(self.name) + if self.project: + url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) + self.client.do('DELETE', url) self.actions.append('delete') def run(self): @@ -469,6 +503,9 @@ def main(): type='str', required=True ), + project=dict( + type='str', + ), new_name=dict( type='str', ),