diff options
| author | Ivan Halomi <ivan.halomi@tietoevery.com> | 2021-05-31 11:57:28 +0200 |
|---|---|---|
| committer | Martin Hiner <m.hiner@partner.samsung.com> | 2023-10-20 17:51:52 +0200 |
| commit | 9a3f46334506d655f302ebe4abcd23df7540bd80 (patch) | |
| tree | 1049d14b3e66c381d599489bad71fd92e5936ed8 /ansible/library | |
| parent | 13ba75cccf71373f7cb3315edd2182c05d4ec2ec (diff) | |
| download | kolla-ansible-9a3f46334506d655f302ebe4abcd23df7540bd80.tar.gz kolla-ansible-9a3f46334506d655f302ebe4abcd23df7540bd80.zip | |
Add support of podman deployment
This change adds basic deployment based on Podman
container manager as an alternative to Docker.
Signed-off-by: Ivan Halomi <i.halomi@partner.samsung.com>
Signed-off-by: Martin Hiner <m.hiner@partner.samsung.com>
Signed-off-by: Petr Tuma <p.tuma@partner.samsung.com>
Change-Id: I2b52964906ba8b19b8b1098717b9423ab954fa3d
Depends-On: Ie4b4c1cf8fe6e7ce41eaa703b423dedcb41e3afc
Diffstat (limited to 'ansible/library')
| -rw-r--r-- | ansible/library/kolla_container_facts.py | 63 | ||||
| -rw-r--r-- | ansible/library/kolla_container_volume_facts.py | 31 | ||||
| -rw-r--r-- | ansible/library/kolla_docker.py | 29 | ||||
| -rw-r--r-- | ansible/library/kolla_toolbox.py | 104 |
4 files changed, 184 insertions, 43 deletions
diff --git a/ansible/library/kolla_container_facts.py b/ansible/library/kolla_container_facts.py index d9626097c..3f75a4478 100644 --- a/ansible/library/kolla_container_facts.py +++ b/ansible/library/kolla_container_facts.py @@ -13,10 +13,9 @@ # limitations under the License. -import docker - from ansible.module_utils.basic import AnsibleModule + DOCUMENTATION = ''' --- module: kolla_container_facts @@ -41,6 +40,11 @@ options: - Name or names of the containers required: False type: str or list + container_engine: + description: + - Name of container engine to use + required: True + type: str author: Jeffrey Zhang ''' @@ -49,6 +53,7 @@ EXAMPLES = ''' tasks: - name: Gather docker facts kolla_container_facts: + container_engine: docker - name: Gather glance container facts kolla_container_facts: @@ -56,24 +61,18 @@ EXAMPLES = ''' name: - glance_api - glance_registry + container_engine: podman ''' def get_docker_client(): + import docker return docker.APIClient -def main(): - argument_spec = dict( - name=dict(required=False, type='list', default=[]), - api_version=dict(required=False, type='str', default='auto'), - container_engine=dict(required=True, type='str') - ) - - module = AnsibleModule(argument_spec=argument_spec) - - results = dict(changed=False, _containers=[]) +def use_docker(module, results): client = get_docker_client()(version=module.params.get('api_version')) + containers = client.containers() names = module.params.get('name') if names and not isinstance(names, list): @@ -86,6 +85,46 @@ def main(): continue results['_containers'].append(container) results[container_name] = container + + +def use_podman(module, results): + import podman.errors as pe + from podman import PodmanClient + + client = PodmanClient(base_url="http+unix:/run/podman/podman.sock") + + try: + containers = client.containers.list(all=True, ignore_removed=True) + except pe.APIError as e: + module.fail_json(failed=True, msg=f"Internal error: {e.explanation}") + names = module.params.get('name') + if names and not isinstance(names, list): + names = [names] + + for container in containers: + container.reload() + container_name = container.attrs['Name'] + if container_name not in names: + continue + results['_containers'].append(container.attrs) + results[container_name] = container.attrs + + +def main(): + argument_spec = dict( + name=dict(required=False, type='list', default=[]), + api_version=dict(required=False, type='str', default='auto'), + container_engine=dict(required=True, type='str') + ) + + module = AnsibleModule(argument_spec=argument_spec) + + results = dict(changed=False, _containers=[]) + if module.params['container_engine'] == 'podman': + use_podman(module, results) + else: + use_docker(module, results) + module.exit_json(**results) diff --git a/ansible/library/kolla_container_volume_facts.py b/ansible/library/kolla_container_volume_facts.py index b1f471d22..a5aba0c9e 100644 --- a/ansible/library/kolla_container_volume_facts.py +++ b/ansible/library/kolla_container_volume_facts.py @@ -13,16 +13,14 @@ # limitations under the License. -import docker - from ansible.module_utils.basic import AnsibleModule DOCUMENTATION = ''' --- module: kolla_container_volume_facts -short_description: Module for collecting Docker container volume facts +short_description: Module for collecting container volume facts description: - - A module targeted at collecting Docker container volume facts. It is used + - A module targeted at collecting container volume facts. It is used for detecting whether the container volume exists on a host. options: container_engine: @@ -60,9 +58,25 @@ EXAMPLES = ''' def get_docker_client(): + import docker return docker.APIClient +def get_docker_volumes(api_version): + client = get_docker_client()(version=api_version) + return client.volumes()['Volumes'] + + +def get_podman_volumes(): + from podman import PodmanClient + + client = PodmanClient(base_url="http+unix:/run/podman/podman.sock") + volumes = [] + for volume in client.volumes.list(): + volumes.append(volume.attrs) + return volumes + + def main(): argument_spec = dict( name=dict(required=False, type='list', default=[]), @@ -73,12 +87,15 @@ def main(): module = AnsibleModule(argument_spec=argument_spec) results = dict(changed=False, _volumes=[]) - client = get_docker_client()(version=module.params.get('api_version')) - volumes = client.volumes() + if module.params.get('container_engine') == 'docker': + volumes = get_docker_volumes(module.params.get('api_version')) + else: + volumes = get_podman_volumes() + names = module.params.get('name') if names and not isinstance(names, list): names = [names] - for volume in volumes['Volumes']: + for volume in volumes: volume_name = volume['Name'] if names and volume_name not in names: continue diff --git a/ansible/library/kolla_docker.py b/ansible/library/kolla_docker.py index 93788e35f..c19b7eb4f 100644 --- a/ansible/library/kolla_docker.py +++ b/ansible/library/kolla_docker.py @@ -16,22 +16,25 @@ # a hacky way to seed most usages of kolla_docker in kolla-ansible ansible # playbooks - caution has to be exerted when setting "common_options" -import traceback +# FIXME(yoctozepto): restart_policy is *not* checked in the container from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.kolla_docker_worker import DockerWorker +import traceback + +from ansible.module_utils.kolla_container_worker import ContainerWorker + DOCUMENTATION = ''' --- module: kolla_docker -short_description: Module for controlling Docker +short_description: Module for controlling containers description: - - A module targeting at controlling Docker as used by Kolla. + - A module targeting at controlling container engine as used by Kolla. options: common_options: description: - A dict containing common params such as login info - required: False + required: True type: dict default: dict() action: @@ -397,17 +400,23 @@ def generate_module(): def main(): module = generate_module() - dw = None + cw: ContainerWorker = None try: - dw = DockerWorker(module) + if module.params.get('container_engine') == 'docker': + from ansible.module_utils.kolla_docker_worker import DockerWorker + cw = DockerWorker(module) + else: + from ansible.module_utils.kolla_podman_worker import PodmanWorker + cw = PodmanWorker(module) + # TODO(inc0): We keep it bool to have ansible deal with consistent # types. If we ever add method that will have to return some # meaningful data, we need to refactor all methods to return dicts. - result = bool(getattr(dw, module.params.get('action'))()) - module.exit_json(changed=dw.changed, result=result, **dw.result) + result = bool(getattr(cw, module.params.get('action'))()) + module.exit_json(changed=cw.changed, result=result, **cw.result) except Exception: module.fail_json(changed=True, msg=repr(traceback.format_exc()), - **getattr(dw, 'result', {})) + **getattr(cw, 'result', {})) if __name__ == '__main__': diff --git a/ansible/library/kolla_toolbox.py b/ansible/library/kolla_toolbox.py index 388d678ad..acfc8a15a 100644 --- a/ansible/library/kolla_toolbox.py +++ b/ansible/library/kolla_toolbox.py @@ -13,7 +13,6 @@ # limitations under the License. from distutils.version import StrictVersion -import docker import json import re @@ -21,6 +20,7 @@ from ansible.module_utils.ansible_release import __version__ as ansible_version from ansible.module_utils.basic import AnsibleModule from ast import literal_eval +from shlex import split DOCUMENTATION = ''' --- @@ -120,10 +120,19 @@ def gen_commandline(params): if StrictVersion(ansible_version) < StrictVersion('2.11.0'): module_args = params.get('module_args') else: - module_args = literal_eval(params.get('module_args')) + try: + module_args = literal_eval(params.get('module_args')) + except SyntaxError: + if not isinstance(params.get('module_args'), str): + raise + + # account for string arguments + module_args = split(params.get('module_args')) if isinstance(module_args, dict): module_args = ' '.join("{}='{}'".format(key, value) for key, value in module_args.items()) + if isinstance(module_args, list): + module_args = ' '.join(module_args) command.extend(['-a', module_args]) if params.get('module_extra_vars'): extra_vars = params.get('module_extra_vars') @@ -134,6 +143,7 @@ def gen_commandline(params): def get_docker_client(): + import docker return docker.APIClient @@ -142,17 +152,7 @@ def docker_supports_environment_in_exec(client): return docker_version >= StrictVersion('1.25') -def main(): - specs = dict( - container_engine=dict(required=True, type='str'), - module_name=dict(required=True, type='str'), - module_args=dict(type='str'), - module_extra_vars=dict(type='json'), - api_version=dict(required=False, type='str', default='auto'), - timeout=dict(required=False, type='int', default=180), - user=dict(required=False, type='str'), - ) - module = AnsibleModule(argument_spec=specs, bypass_checks=True) +def use_docker(module): client = get_docker_client()( version=module.params.get('api_version'), timeout=module.params.get('timeout')) @@ -240,7 +240,83 @@ def main(): # No way to know whether changed - assume yes. ret['changed'] = True - module.exit_json(**ret) + return ret + + +def get_kolla_toolbox(): + from podman import PodmanClient + + with PodmanClient(base_url="http+unix:/run/podman/podman.sock") as client: + for cont in client.containers.list(all=True): + cont.reload() + if cont.name == 'kolla_toolbox' and cont.status == 'running': + return cont + + +def use_podman(module): + from podman.errors.exceptions import APIError + + try: + kolla_toolbox = get_kolla_toolbox() + if not kolla_toolbox: + module.fail_json(msg='kolla_toolbox container is not running.') + + kwargs = {} + if 'user' in module.params: + kwargs['user'] = module.params['user'] + environment = {"ANSIBLE_STDOUT_CALLBACK": "json", + "ANSIBLE_LOAD_CALLBACK_PLUGINS": "True"} + command_line = gen_commandline(module.params) + + _, raw_output = kolla_toolbox.exec_run( + command_line, + environment=environment, + tty=True, + **kwargs + ) + except APIError as e: + module.fail_json(msg=f'Encountered Podman API error: {e.explanation}') + + try: + json_output = raw_output.decode('utf-8') + output = json.loads(json_output) + except Exception: + module.fail_json( + msg='Can not parse the inner module output: %s' % json_output) + + try: + ret = output['plays'][0]['tasks'][0]['hosts']['localhost'] + except (KeyError, IndexError): + module.fail_json( + msg='Ansible JSON output has unexpected format: %s' % output) + + # Remove Ansible's internal variables from returned fields. + ret.pop('_ansible_no_log', None) + + return ret + + +def main(): + specs = dict( + container_engine=dict(required=True, type='str'), + module_name=dict(required=True, type='str'), + module_args=dict(type='str'), + module_extra_vars=dict(type='json'), + api_version=dict(required=False, type='str', default='auto'), + timeout=dict(required=False, type='int', default=180), + user=dict(required=False, type='str'), + ) + module = AnsibleModule(argument_spec=specs, bypass_checks=True) + + container_engine = module.params.get('container_engine').lower() + if container_engine == 'docker': + result = use_docker(module) + elif container_engine == 'podman': + result = use_podman(module) + else: + module.fail_json(msg='Missing or invalid container engine.') + + module.exit_json(**result) if __name__ == "__main__": |
