Skip to content

Commit

Permalink
Add mount and unmount for volumes
Browse files Browse the repository at this point in the history
Fix containers#603
Signed-off-by: Sagi Shnaidman <[email protected]>
  • Loading branch information
sshnaidm committed May 28, 2024
1 parent 9142efd commit ef7daf9
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 4 deletions.
52 changes: 48 additions & 4 deletions plugins/modules/podman_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
'''
# noqa: F402
import json # noqa: F402
import os # noqa: F402

from ansible.module_utils.basic import AnsibleModule # noqa: F402
from ansible.module_utils._text import to_bytes, to_native # noqa: F402
Expand Down Expand Up @@ -160,7 +161,7 @@ def construct_command_from_params(self):
Returns:
list -- list of byte strings for Popen command
"""
if self.action in ['delete']:
if self.action in ['delete', 'mount', 'unmount']:
return self._simple_action()
if self.action in ['create']:
return self._create_action()
Expand All @@ -169,6 +170,12 @@ def _simple_action(self):
if self.action == 'delete':
cmd = ['rm', '-f', self.params['name']]
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
if self.action == 'mount':
cmd = ['mount', self.params['name']]
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
if self.action == 'unmount':
cmd = ['unmount', self.params['name']]
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]

def _create_action(self):
cmd = [self.action, self.params['name']]
Expand Down Expand Up @@ -326,6 +333,7 @@ def __init__(self, module, name):
self.module = module
self.name = name
self.stdout, self.stderr = '', ''
self.mount_point = None
self.info = self.get_info()
self.version = self._get_podman_version()
self.diff = {}
Expand Down Expand Up @@ -380,7 +388,7 @@ def _perform_action(self, action):
"""Perform action with volume.
Arguments:
action {str} -- action to perform - create, stop, delete
action {str} -- action to perform - create, delete, mount, unmout
"""
b_command = PodmanVolumeModuleParams(action,
self.module.params,
Expand All @@ -389,18 +397,24 @@ def _perform_action(self, action):
).construct_command_from_params()
full_cmd = " ".join([self.module.params['executable'], 'volume']
+ [to_native(i) for i in b_command])
# check if running not from root
if os.getuid() != 0 and action == 'mount':
full_cmd = f"{self.module.params['executable']} unshare {full_cmd}"
self.module.log("PODMAN-VOLUME-DEBUG: %s" % full_cmd)
self.actions.append(full_cmd)
if not self.module.check_mode:
rc, out, err = self.module.run_command(
[self.module.params['executable'], b'volume'] + b_command,
full_cmd,
expand_user_and_vars=False)
self.stdout = out
self.stderr = err
if rc != 0:
self.module.fail_json(
msg="Can't %s volume %s" % (action, self.name),
stdout=out, stderr=err)
# in case of mount/unmount, return path to the volume from stdout
if action in ['mount', 'unmount']:
self.mount_point = out.strip()

def delete(self):
"""Delete the volume."""
Expand All @@ -410,6 +424,14 @@ def create(self):
"""Create the volume."""
self._perform_action('create')

def mount(self):
"""Delete the volume."""
self._perform_action('mount')

def unmount(self):
"""Create the volume."""
self._perform_action('unmount')

def recreate(self):
"""Recreate the volume."""
self.delete()
Expand Down Expand Up @@ -468,6 +490,8 @@ def execute(self):
states_map = {
'present': self.make_present,
'absent': self.make_absent,
'mounted': self.make_mount,
'unmounted': self.make_unmount,
'quadlet': self.make_quadlet,
}
process_action = states_map[self.state]
Expand Down Expand Up @@ -501,6 +525,26 @@ def make_absent(self):
'podman_actions': self.volume.actions})
self.module.exit_json(**self.results)

def make_mount(self):
"""Run actions if desired state is 'mounted'."""
if not self.volume.exists:
self.volume.create()
self.results['actions'].append('created %s' % self.volume.name)
self.volume.mount()
self.results['actions'].append('mounted %s' % self.volume.name)
if self.volume.mount_point:
self.results.update({'mount_point': self.volume.mount_point})
self.update_volume_result()

def make_unmount(self):
"""Run actions if desired state is 'unmounted'."""
if self.volume.exists:
self.volume.unmount()
self.results['actions'].append('unmounted %s' % self.volume.name)
self.update_volume_result()
else:
self.module.fail_json(msg="Volume %s does not exist!" % self.name)

def make_quadlet(self):
results_update = create_quadlet_state(self.module, "volume")
self.results.update(results_update)
Expand All @@ -511,7 +555,7 @@ def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(type='str', default="present",
choices=['present', 'absent', 'quadlet']),
choices=['present', 'absent', 'mounted', 'unmounted', 'quadlet']),
name=dict(type='str', required=True),
label=dict(type='dict', required=False),
driver=dict(type='str', required=False),
Expand Down
73 changes: 73 additions & 0 deletions tests/integration/targets/podman_volume/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,79 @@
- info10 is failed
- delete.volume == {}

- name: Mount non existing volume
containers.podman.podman_volume:
executable: "{{ test_executable | default('podman') }}"
name: nonexistent
state: mounted
register: mount1

- name: Check results
assert:
that:
- mount1 is success
- "'mount_point' in mount1.volume"

- name: Create volume for mount
containers.podman.podman_volume:
executable: "{{ test_executable | default('podman') }}"
name: mountme
state: present

- name: Mount existing volume
containers.podman.podman_volume:
executable: "{{ test_executable | default('podman') }}"
name: mountme
state: mounted
register: mount2

- name: Check results
assert:
that:
- mount2 is success
- "'mount_point' in mount2.volume"

- name: Unmount volume
containers.podman.podman_volume:
executable: "{{ test_executable | default('podman') }}"
name: mountme
state: unmounted
register: unmount

- name: Check results
assert:
that:
- unmount is success
- "'mount_point' not in unmount.volume"

- name: Mount as root
become: true
containers.podman.podman_volume:
executable: "{{ test_executable | default('podman') }}"
name: rootmount
state: mounted
register: mount3

- name: Check results
assert:
that:
- mount3 is success
- "'mount_point' in mount3.volume"

- name: Unmount as root
become: true
containers.podman.podman_volume:
executable: "{{ test_executable | default('podman') }}"
name: rootmount
state: unmounted
register: unmount2

- name: Check results
assert:
that:
- unmount2 is success
- "'mount_point' not in unmount2.volume"

- name: Create a Quadlet for volume with filename
containers.podman.podman_volume:
executable: "{{ test_executable | default('podman') }}"
Expand Down

0 comments on commit ef7daf9

Please sign in to comment.