mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
New module: iso_customize (#5190)
* add ansible module iso_customize.py * rerun CI testing due to "Failed to send request to https://api.github.com/repos/ansible/ansible/issues/23642: HTTP Error 403: rate limit exceeded" * Rerun CI testing due to "Failed to send request to https://api.github....." * rerun CI testing due to failure "Unknown error when attempting to call Galaxy at 'https://galaxy.ansible.com/api/v2/collections/netbox/netbox/versions/3.1.0/': The read operation timed out" * change document part as felixfontein's careful review * modify test file as russoz's comments * modify comment part of module * add comment for the example * add more tests: check the files are deleted / added in customized ISO * fix it: failed to run ansible.posix.mount in ubuntu * fix it: ansible.posix.mount is not working well in some OS. * change DOCUMENTATION part * change files according to the comment from code review * fix issue: E231: missing whitespace after ':' * modify the description of Document * modify code for code review * delete extra blank line in yml file * Try to fix CI testing issue: "Caught \"'foo' is undefined. 'foo' is undefined\" while evaluating 'b' with item == {'a': 1}" * delete extra blank line in the end of file * change code as the comment from code review * change code from code review * change type: str to type: path * change type:str to type:path * delete unused variable * fix CI testing error: return-syntax-error: RETURN.dest_iso.type: not a valid value for dictionary value @ data['dest_iso']['type']. Got 'path' * add testcase: test add files / delete files separately * add more testcases: test if we can catch exception from error input of users * change code from code review * fix issue: E231: missing whitespace after ',' * change code from code review * add notes to document * modify notes in document part * /rebuild_failed /rebuild_failed * Try to support running testcases not only in MAC but also in other OS. * modify document * change mount to ansible.posix.mount * skip the test platform which report "Error mounting" * fix mount failed: Operation not permitted * change code from code review * change document from code review * fix CI testing issue in some platforms * Update plugins/modules/files/iso_customize.py * change code from code review 1) change testcase 2) try to fix "mount: not permitted" * modify aliases file * change document and rerun CI testing * add skip/docker as suggested * add debug task * fix issue in redhat 7.9: occurred while running the lookup plugin 'file'. ..could not locate file in lookup.. * change code from the code review * modify function "iso_rr_check_file_exist" to "iso_check_file_exists" to make it works in all types of ISO 1. modify function "iso_rr_check_file_exist" to "iso_check_file_exists" to make it works in all types of ISO 2. run main.yml with newer python 3.10.6 ansible [core 2.13.4] config file = None configured module search path = ['/Users/zouy/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/local/Cellar/ansible/6.4.0/libexec/lib/python3.10/site-packages/ansible ansible collection location = /Users/zouy/.ansible/collections:/usr/share/ansible/collections executable location = /usr/local/bin/ansible python version = 3.10.6 (main, Aug 30 2022, 05:12:36) [Clang 13.1.6 (clang-1316.0.21.2.5)] jinja version = 3.1.2 libyaml = True * delete blank * simply the code as suggested. * Two small docs updates. Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
8c04133284
commit
d76392ed2a
12 changed files with 764 additions and 0 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -560,6 +560,8 @@ files:
|
||||||
maintainers: jpmens noseka1
|
maintainers: jpmens noseka1
|
||||||
$modules/files/iso_create.py:
|
$modules/files/iso_create.py:
|
||||||
maintainers: Tomorrow9
|
maintainers: Tomorrow9
|
||||||
|
$modules/files/iso_customize.py:
|
||||||
|
maintainers: ZouYuhua
|
||||||
$modules/files/iso_extract.py:
|
$modules/files/iso_extract.py:
|
||||||
maintainers: dagwieers jhoekx ribbons
|
maintainers: dagwieers jhoekx ribbons
|
||||||
$modules/files/read_csv.py:
|
$modules/files/read_csv.py:
|
||||||
|
|
|
@ -560,6 +560,8 @@ plugin_routing:
|
||||||
redirect: community.general.files.iso_create
|
redirect: community.general.files.iso_create
|
||||||
iso_extract:
|
iso_extract:
|
||||||
redirect: community.general.files.iso_extract
|
redirect: community.general.files.iso_extract
|
||||||
|
iso_customize:
|
||||||
|
redirect: community.general.files.iso_customize
|
||||||
jabber:
|
jabber:
|
||||||
redirect: community.general.notification.jabber
|
redirect: community.general.notification.jabber
|
||||||
java_cert:
|
java_cert:
|
||||||
|
|
343
plugins/modules/files/iso_customize.py
Normal file
343
plugins/modules/files/iso_customize.py
Normal file
|
@ -0,0 +1,343 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2022, Ansible Project
|
||||||
|
# Copyright (c) 2022, VMware, Inc. All Rights Reserved.
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: iso_customize
|
||||||
|
short_description: Add/remove/change files in ISO file
|
||||||
|
description:
|
||||||
|
- This module is used to add/remove/change files in ISO file.
|
||||||
|
- The file inside ISO will be overwritten if it exists by option I(add_files).
|
||||||
|
author:
|
||||||
|
- Yuhua Zou (@ZouYuhua) <zouy@vmware.com>
|
||||||
|
requirements:
|
||||||
|
- "pycdlib"
|
||||||
|
- "python >= 2.7"
|
||||||
|
version_added: '5.8.0'
|
||||||
|
|
||||||
|
options:
|
||||||
|
src_iso:
|
||||||
|
description:
|
||||||
|
- This is the path of source ISO file.
|
||||||
|
type: path
|
||||||
|
required: true
|
||||||
|
dest_iso:
|
||||||
|
description:
|
||||||
|
- The path of the customized ISO file.
|
||||||
|
type: path
|
||||||
|
required: true
|
||||||
|
delete_files:
|
||||||
|
description:
|
||||||
|
- Absolute paths for files inside the ISO file that should be removed.
|
||||||
|
type: list
|
||||||
|
required: false
|
||||||
|
elements: str
|
||||||
|
add_files:
|
||||||
|
description:
|
||||||
|
- Allows to add and replace files in the ISO file.
|
||||||
|
- Will create intermediate folders inside the ISO file when they do not exist.
|
||||||
|
type: list
|
||||||
|
required: false
|
||||||
|
elements: dict
|
||||||
|
suboptions:
|
||||||
|
src_file:
|
||||||
|
description:
|
||||||
|
- The path with file name on the machine the module is executed on.
|
||||||
|
type: path
|
||||||
|
required: true
|
||||||
|
dest_file:
|
||||||
|
description:
|
||||||
|
- The absolute path of the file inside the ISO file.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
notes:
|
||||||
|
- The C(pycdlib) library states it supports Python 2.7 and 3.4 only.
|
||||||
|
- >
|
||||||
|
The function I(add_file) in pycdlib will overwrite the existing file in ISO with type ISO9660 / Rock Ridge 1.12 / Joliet / UDF.
|
||||||
|
But it will not overwrite the existing file in ISO with Rock Ridge 1.09 / 1.10.
|
||||||
|
So we take workaround "delete the existing file and then add file for ISO with Rock Ridge".
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = r'''
|
||||||
|
- name: "Customize ISO file"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "/path/to/ubuntu-22.04-desktop-amd64.iso"
|
||||||
|
dest_iso: "/path/to/ubuntu-22.04-desktop-amd64-customized.iso"
|
||||||
|
delete_files:
|
||||||
|
- "/boot.catalog"
|
||||||
|
add_files:
|
||||||
|
- src_file: "/path/to/grub.cfg"
|
||||||
|
dest_file: "/boot/grub/grub.cfg"
|
||||||
|
- src_file: "/path/to/ubuntu.seed"
|
||||||
|
dest_file: "/preseed/ubuntu.seed"
|
||||||
|
register: customize_iso_result
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = r'''
|
||||||
|
src_iso:
|
||||||
|
description: Path of source ISO file.
|
||||||
|
returned: on success
|
||||||
|
type: str
|
||||||
|
sample: "/path/to/file.iso"
|
||||||
|
dest_iso:
|
||||||
|
description: Path of the customized ISO file.
|
||||||
|
returned: on success
|
||||||
|
type: str
|
||||||
|
sample: "/path/to/customized.iso"
|
||||||
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
PYCDLIB_IMP_ERR = None
|
||||||
|
try:
|
||||||
|
import pycdlib
|
||||||
|
HAS_PYCDLIB = True
|
||||||
|
except ImportError:
|
||||||
|
PYCDLIB_IMP_ERR = traceback.format_exc()
|
||||||
|
HAS_PYCDLIB = False
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||||
|
from ansible.module_utils.common.text.converters import to_native
|
||||||
|
|
||||||
|
|
||||||
|
# The upper dir exist, we only add subdirectoy
|
||||||
|
def iso_add_dir(module, opened_iso, iso_type, dir_path):
|
||||||
|
parent_dir, check_dirname = dir_path.rsplit("/", 1)
|
||||||
|
if not parent_dir.strip():
|
||||||
|
parent_dir = "/"
|
||||||
|
check_dirname = check_dirname.strip()
|
||||||
|
|
||||||
|
for dirname, dirlist, dummy_filelist in opened_iso.walk(iso_path=parent_dir.upper()):
|
||||||
|
if dirname == parent_dir.upper():
|
||||||
|
if check_dirname.upper() in dirlist:
|
||||||
|
return
|
||||||
|
|
||||||
|
if parent_dir == "/":
|
||||||
|
current_dirpath = "/%s" % check_dirname
|
||||||
|
else:
|
||||||
|
current_dirpath = "%s/%s" % (parent_dir, check_dirname)
|
||||||
|
|
||||||
|
current_dirpath_upper = current_dirpath.upper()
|
||||||
|
try:
|
||||||
|
if iso_type == "iso9660":
|
||||||
|
opened_iso.add_directory(current_dirpath_upper)
|
||||||
|
elif iso_type == "rr":
|
||||||
|
opened_iso.add_directory(current_dirpath_upper, rr_name=check_dirname)
|
||||||
|
elif iso_type == "joliet":
|
||||||
|
opened_iso.add_directory(current_dirpath_upper, joliet_path=current_dirpath)
|
||||||
|
elif iso_type == "udf":
|
||||||
|
opened_iso.add_directory(current_dirpath_upper, udf_path=current_dirpath)
|
||||||
|
except Exception as err:
|
||||||
|
msg = "Failed to create dir %s with error: %s" % (current_dirpath, to_native(err))
|
||||||
|
module.fail_json(msg=msg)
|
||||||
|
|
||||||
|
|
||||||
|
def iso_add_dirs(module, opened_iso, iso_type, dir_path):
|
||||||
|
dirnames = dir_path.strip().split("/")
|
||||||
|
|
||||||
|
current_dirpath = "/"
|
||||||
|
for item in dirnames:
|
||||||
|
if not item.strip():
|
||||||
|
continue
|
||||||
|
if current_dirpath == "/":
|
||||||
|
current_dirpath = "/%s" % item
|
||||||
|
else:
|
||||||
|
current_dirpath = "%s/%s" % (current_dirpath, item)
|
||||||
|
|
||||||
|
iso_add_dir(module, opened_iso, iso_type, current_dirpath)
|
||||||
|
|
||||||
|
|
||||||
|
def iso_check_file_exists(opened_iso, dest_file):
|
||||||
|
file_dir = os.path.dirname(dest_file).strip()
|
||||||
|
file_name = os.path.basename(dest_file)
|
||||||
|
dirnames = file_dir.strip().split("/")
|
||||||
|
|
||||||
|
parent_dir = "/"
|
||||||
|
for item in dirnames:
|
||||||
|
if not item.strip():
|
||||||
|
continue
|
||||||
|
|
||||||
|
for dirname, dirlist, dummy_filelist in opened_iso.walk(iso_path=parent_dir.upper()):
|
||||||
|
if dirname != parent_dir.upper():
|
||||||
|
break
|
||||||
|
|
||||||
|
if item.upper() not in dirlist:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if parent_dir == "/":
|
||||||
|
parent_dir = "/%s" % item
|
||||||
|
else:
|
||||||
|
parent_dir = "%s/%s" % (parent_dir, item)
|
||||||
|
|
||||||
|
if '.' not in file_name:
|
||||||
|
file_in_iso_path = file_name.upper() + '.;1'
|
||||||
|
else:
|
||||||
|
file_in_iso_path = file_name.upper() + ';1'
|
||||||
|
|
||||||
|
for dirname, dummy_dirlist, filelist in opened_iso.walk(iso_path=parent_dir.upper()):
|
||||||
|
if dirname != parent_dir.upper():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return file_name.upper() in filelist or file_in_iso_path in filelist
|
||||||
|
|
||||||
|
|
||||||
|
def iso_add_file(module, opened_iso, iso_type, src_file, dest_file):
|
||||||
|
dest_file = dest_file.strip()
|
||||||
|
if dest_file[0] != "/":
|
||||||
|
dest_file = "/%s" % dest_file
|
||||||
|
|
||||||
|
file_local = src_file.strip()
|
||||||
|
|
||||||
|
file_dir = os.path.dirname(dest_file).strip()
|
||||||
|
file_name = os.path.basename(dest_file)
|
||||||
|
if '.' not in file_name:
|
||||||
|
file_in_iso_path = dest_file.upper() + '.;1'
|
||||||
|
else:
|
||||||
|
file_in_iso_path = dest_file.upper() + ';1'
|
||||||
|
|
||||||
|
if file_dir and file_dir != "/":
|
||||||
|
iso_add_dirs(module, opened_iso, iso_type, file_dir)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if iso_type == "iso9660":
|
||||||
|
opened_iso.add_file(file_local, iso_path=file_in_iso_path)
|
||||||
|
elif iso_type == "rr":
|
||||||
|
# For ISO with Rock Ridge 1.09 / 1.10, it won't overwrite the existing file
|
||||||
|
# So we take workaround here: delete the existing file and then add file
|
||||||
|
if iso_check_file_exists(opened_iso, dest_file):
|
||||||
|
opened_iso.rm_file(iso_path=file_in_iso_path)
|
||||||
|
opened_iso.add_file(file_local, iso_path=file_in_iso_path, rr_name=file_name)
|
||||||
|
elif iso_type == "joliet":
|
||||||
|
opened_iso.add_file(file_local, iso_path=file_in_iso_path, joliet_path=dest_file)
|
||||||
|
elif iso_type == "udf":
|
||||||
|
# For ISO with UDF, it won't always succeed to overwrite the existing file
|
||||||
|
# So we take workaround here: delete the existing file and then add file
|
||||||
|
if iso_check_file_exists(opened_iso, dest_file):
|
||||||
|
opened_iso.rm_file(udf_path=dest_file)
|
||||||
|
opened_iso.add_file(file_local, iso_path=file_in_iso_path, udf_path=dest_file)
|
||||||
|
except Exception as err:
|
||||||
|
msg = "Failed to add local file %s to ISO with error: %s" % (file_local, to_native(err))
|
||||||
|
module.fail_json(msg=msg)
|
||||||
|
|
||||||
|
|
||||||
|
def iso_delete_file(module, opened_iso, iso_type, dest_file):
|
||||||
|
dest_file = dest_file.strip()
|
||||||
|
if dest_file[0] != "/":
|
||||||
|
dest_file = "/%s" % dest_file
|
||||||
|
file_name = os.path.basename(dest_file)
|
||||||
|
|
||||||
|
if not iso_check_file_exists(opened_iso, dest_file):
|
||||||
|
module.fail_json(msg="The file %s does not exist." % dest_file)
|
||||||
|
|
||||||
|
if '.' not in file_name:
|
||||||
|
file_in_iso_path = dest_file.upper() + '.;1'
|
||||||
|
else:
|
||||||
|
file_in_iso_path = dest_file.upper() + ';1'
|
||||||
|
|
||||||
|
try:
|
||||||
|
if iso_type == "iso9660":
|
||||||
|
opened_iso.rm_file(iso_path=file_in_iso_path)
|
||||||
|
elif iso_type == "rr":
|
||||||
|
opened_iso.rm_file(iso_path=file_in_iso_path)
|
||||||
|
elif iso_type == "joliet":
|
||||||
|
opened_iso.rm_file(joliet_path=dest_file)
|
||||||
|
elif iso_type == "udf":
|
||||||
|
opened_iso.rm_file(udf_path=dest_file)
|
||||||
|
except Exception as err:
|
||||||
|
msg = "Failed to delete iso file %s with error: %s" % (dest_file, to_native(err))
|
||||||
|
module.fail_json(msg=msg)
|
||||||
|
|
||||||
|
|
||||||
|
def iso_rebuild(module, src_iso, dest_iso, delete_files_list, add_files_list):
|
||||||
|
iso = None
|
||||||
|
iso_type = "iso9660"
|
||||||
|
|
||||||
|
try:
|
||||||
|
iso = pycdlib.PyCdlib(always_consistent=True)
|
||||||
|
iso.open(src_iso)
|
||||||
|
if iso.has_rock_ridge():
|
||||||
|
iso_type = "rr"
|
||||||
|
elif iso.has_joliet():
|
||||||
|
iso_type = "joliet"
|
||||||
|
elif iso.has_udf():
|
||||||
|
iso_type = "udf"
|
||||||
|
|
||||||
|
for item in delete_files_list:
|
||||||
|
iso_delete_file(module, iso, iso_type, item)
|
||||||
|
|
||||||
|
for item in add_files_list:
|
||||||
|
iso_add_file(module, iso, iso_type, item['src_file'], item['dest_file'])
|
||||||
|
|
||||||
|
iso.write(dest_iso)
|
||||||
|
except Exception as err:
|
||||||
|
msg = "Failed to rebuild ISO %s with error: %s" % (src_iso, to_native(err))
|
||||||
|
module.fail_json(msg=msg)
|
||||||
|
finally:
|
||||||
|
if iso:
|
||||||
|
iso.close()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
argument_spec = dict(
|
||||||
|
src_iso=dict(type='path', required=True),
|
||||||
|
dest_iso=dict(type='path', required=True),
|
||||||
|
delete_files=dict(type='list', elements='str', default=[]),
|
||||||
|
add_files=dict(
|
||||||
|
type='list', elements='dict', default=[],
|
||||||
|
options=dict(
|
||||||
|
src_file=dict(type='path', required=True),
|
||||||
|
dest_file=dict(type='str', required=True),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
module = AnsibleModule(
|
||||||
|
argument_spec=argument_spec,
|
||||||
|
required_one_of=[('delete_files', 'add_files'), ],
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
if not HAS_PYCDLIB:
|
||||||
|
module.fail_json(
|
||||||
|
missing_required_lib('pycdlib'), exception=PYCDLIB_IMP_ERR)
|
||||||
|
|
||||||
|
src_iso = module.params['src_iso']
|
||||||
|
if not os.path.exists(src_iso):
|
||||||
|
module.fail_json(msg="ISO file %s does not exist." % src_iso)
|
||||||
|
|
||||||
|
dest_iso = module.params['dest_iso']
|
||||||
|
dest_iso_dir = os.path.dirname(dest_iso)
|
||||||
|
if dest_iso_dir and not os.path.exists(dest_iso_dir):
|
||||||
|
module.fail_json(msg="The dest directory %s does not exist" % dest_iso_dir)
|
||||||
|
|
||||||
|
delete_files_list = [s.strip() for s in module.params['delete_files']]
|
||||||
|
add_files_list = module.params['add_files']
|
||||||
|
if add_files_list:
|
||||||
|
for item in add_files_list:
|
||||||
|
if not os.path.exists(item['src_file']):
|
||||||
|
module.fail_json(msg="The file %s does not exist." % item['src_file'])
|
||||||
|
|
||||||
|
result = dict(
|
||||||
|
src_iso=src_iso,
|
||||||
|
customized_iso=dest_iso,
|
||||||
|
delete_files=delete_files_list,
|
||||||
|
add_files=add_files_list,
|
||||||
|
changed=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not module.check_mode:
|
||||||
|
iso_rebuild(module, src_iso, dest_iso, delete_files_list, add_files_list)
|
||||||
|
|
||||||
|
result['changed'] = True
|
||||||
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
14
tests/integration/targets/iso_customize/aliases
Normal file
14
tests/integration/targets/iso_customize/aliases
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Copyright (c) 2022, Ansible Project
|
||||||
|
# Copyright (c) 2022, VMware, Inc. All Rights Reserved.
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
shippable/posix/group1
|
||||||
|
destructive
|
||||||
|
skip/aix
|
||||||
|
skip/freebsd
|
||||||
|
skip/alpine
|
||||||
|
skip/python2.6
|
||||||
|
skip/docker
|
||||||
|
needs/root
|
||||||
|
|
7
tests/integration/targets/iso_customize/meta/main.yml
Normal file
7
tests/integration/targets/iso_customize/meta/main.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
# Copyright (c) Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- setup_remote_tmp_dir
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Copyright (c) 2022, Ansible Project
|
||||||
|
# Copyright (c) 2022, VMware, Inc. All Rights Reserved.
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- name: Add a line to the file test02.cfg and make sure it succeed
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ test_dir }}/test02.cfg"
|
||||||
|
regexp: "^test"
|
||||||
|
line: "test"
|
||||||
|
|
||||||
|
- name: "Customize ISO file: add file, delete file and change file"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test.iso"
|
||||||
|
dest_iso: "{{ test_dir }}/{{ dest_iso_name }}"
|
||||||
|
delete_files:
|
||||||
|
- "/test01.cfg"
|
||||||
|
add_files:
|
||||||
|
- src_file: "{{ test_dir }}/test01.cfg"
|
||||||
|
dest_file: "/preseed/ubuntu.seed"
|
||||||
|
- src_file: "{{ test_dir }}/test02.cfg"
|
||||||
|
dest_file: "/test02.cfg"
|
||||||
|
|
||||||
|
- include_tasks: iso_mount.yml
|
||||||
|
vars:
|
||||||
|
iso_name: "{{ dest_iso_name }}"
|
||||||
|
|
||||||
|
- debug: var=mount_root_dir
|
||||||
|
|
||||||
|
- name: Check the file test01.cfg is deleted
|
||||||
|
stat:
|
||||||
|
path: "{{ mount_root_dir }}/test01.cfg"
|
||||||
|
register: check_file
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- check_file.stat.exists == False
|
||||||
|
|
||||||
|
- name: Check the file /preseed/ubuntu.seed is added
|
||||||
|
stat:
|
||||||
|
path: "{{ mount_root_dir }}/preseed/ubuntu.seed"
|
||||||
|
register: check_file
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- check_file.stat.exists == True
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: Get the content of file test02.cfg
|
||||||
|
command: "cat {{ mount_root_dir }}/test02.cfg"
|
||||||
|
register: get_file_content
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
file_contents: "{{ get_file_content.stdout }}"
|
||||||
|
when: ansible_distribution == 'RedHat' and ansible_distribution_version is version('7.9', '==')
|
||||||
|
|
||||||
|
- name: Get the content of file test02.cfg
|
||||||
|
set_fact:
|
||||||
|
file_contents: "{{ lookup('file', mount_root_dir + '/test02.cfg') }}"
|
||||||
|
when: not (ansible_distribution == 'RedHat' and ansible_distribution_version is version('7.9', '=='))
|
||||||
|
|
||||||
|
- fail: msg="Failed to replace the file test02.cfg"
|
||||||
|
when: file_contents != "test"
|
||||||
|
|
||||||
|
- name: Umount ISO
|
||||||
|
mount:
|
||||||
|
path: "{{ mount_root_dir }}"
|
||||||
|
fstab: "{{ test_dir }}/temp.fstab"
|
||||||
|
state: unmounted
|
||||||
|
|
||||||
|
- name: Delete line of file test02.cfg
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ test_dir }}/test02.cfg"
|
||||||
|
regexp: "test"
|
||||||
|
state: absent
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright (c) 2022, Ansible Project
|
||||||
|
# Copyright (c) 2022, VMware, Inc. All Rights Reserved.
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- name: "Customize ISO file: add file"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test1.iso"
|
||||||
|
dest_iso: "{{ test_dir }}/{{ dest_iso_name }}"
|
||||||
|
add_files:
|
||||||
|
- src_file: "{{ test_dir }}/test01.cfg"
|
||||||
|
dest_file: "preseed/ubuntu.seed"
|
||||||
|
|
||||||
|
|
||||||
|
- include_tasks: iso_mount.yml
|
||||||
|
vars:
|
||||||
|
iso_name: "{{ dest_iso_name }}"
|
||||||
|
|
||||||
|
- debug: var=mount_root_dir
|
||||||
|
|
||||||
|
- name: Check the file /preseed/ubuntu.seed is added
|
||||||
|
stat:
|
||||||
|
path: "{{ mount_root_dir }}/preseed/ubuntu.seed"
|
||||||
|
register: check_file
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- check_file.stat.exists == True
|
||||||
|
|
||||||
|
- name: Umount ISO
|
||||||
|
mount:
|
||||||
|
path: "{{ mount_root_dir }}"
|
||||||
|
fstab: "{{ test_dir }}/temp.fstab"
|
||||||
|
state: unmounted
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright (c) 2022, Ansible Project
|
||||||
|
# Copyright (c) 2022, VMware, Inc. All Rights Reserved.
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- name: "Customize ISO file: delete file"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test1.iso"
|
||||||
|
dest_iso: "{{ test_dir }}/{{ dest_iso_name }}"
|
||||||
|
delete_files:
|
||||||
|
- "test01.cfg"
|
||||||
|
|
||||||
|
- debug: var=ansible_distribution
|
||||||
|
|
||||||
|
- include_tasks: iso_mount.yml
|
||||||
|
vars:
|
||||||
|
iso_name: "{{ dest_iso_name }}"
|
||||||
|
|
||||||
|
- debug: var=mount_root_dir
|
||||||
|
|
||||||
|
- name: Check the file test01.cfg is deleted
|
||||||
|
stat:
|
||||||
|
path: "{{ mount_root_dir }}/test01.cfg"
|
||||||
|
register: check_file
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- check_file.stat.exists == False
|
||||||
|
|
||||||
|
- name: Umount ISO
|
||||||
|
mount:
|
||||||
|
path: "{{ mount_root_dir }}"
|
||||||
|
fstab: "{{ test_dir }}/temp.fstab"
|
||||||
|
state: unmounted
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Copyright (c) 2022, Ansible Project
|
||||||
|
# Copyright (c) 2022, VMware, Inc. All Rights Reserved.
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- name: "Testcase: local resource ISO does not exists"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test11.iso"
|
||||||
|
dest_iso: "{{ test_dir }}/{{ dest_iso_name }}"
|
||||||
|
register: customized_result
|
||||||
|
failed_when: customized_result.msg.find('does not exist') == -1
|
||||||
|
|
||||||
|
- name: "Testcase:: dest dir does not exists"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test1.iso"
|
||||||
|
dest_iso: "/aaa/{{ dest_iso_name }}"
|
||||||
|
register: customized_result
|
||||||
|
failed_when: customized_result.msg.find('does not exist') == -1
|
||||||
|
|
||||||
|
# Test: Get MODULE FAILURE when no add files data and no delete files data
|
||||||
|
- name: "Testcase:: no add files data and no delete files data"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test1.iso"
|
||||||
|
dest_iso: "{{ test_dir }}/iso_customize_nodata.iso"
|
||||||
|
delete_files:
|
||||||
|
add_files:
|
||||||
|
register: customized_result
|
||||||
|
failed_when: customized_result.msg.find("MODULE FAILURE") == -1
|
||||||
|
|
||||||
|
# Test: nothing is changed when no options "add files" and "delete files"
|
||||||
|
- block:
|
||||||
|
- name: "Testcase: no options 'add files' and 'delete files'"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test1.iso"
|
||||||
|
dest_iso: "{{ test_dir }}/iso_customize_nochanged.iso"
|
||||||
|
|
||||||
|
- name: Get stats of a file test1.iso
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ test_dir }}/test1.iso"
|
||||||
|
register: iso_orginal
|
||||||
|
|
||||||
|
- name: Get stats of a file iso_customize_nochanged.iso
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ test_dir }}/iso_customize_nochanged.iso"
|
||||||
|
register: iso_customized
|
||||||
|
|
||||||
|
- name: compare size
|
||||||
|
fail: msg="Check we have nothing changed for customized ISO"
|
||||||
|
when: iso_orginal.stat.size != iso_customized.stat.size
|
||||||
|
|
||||||
|
- name: "Testcase: delete the non-existing file in ISO"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test1.iso"
|
||||||
|
dest_iso: "{{ test_dir }}/{{ dest_iso_name }}"
|
||||||
|
delete_files:
|
||||||
|
- "/test03.cfg"
|
||||||
|
register: customized_result
|
||||||
|
failed_when: customized_result.msg.find("does not exist") == -1
|
||||||
|
|
||||||
|
# Test: failed when local src file does not exists
|
||||||
|
- name: "Testcase: local src file does not exists"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test.iso"
|
||||||
|
dest_iso: "{{ test_dir }}/{{ dest_iso_name }}"
|
||||||
|
add_files:
|
||||||
|
- src_file: "{{ test_dir }}/test03.cfg"
|
||||||
|
dest_file: "/preseed/ubuntu.seed"
|
||||||
|
register: customized_result
|
||||||
|
failed_when: customized_result.msg.find("does not exist") == -1
|
||||||
|
|
||||||
|
# Test: filenames with whitespaces
|
||||||
|
# We report error: the user should be reponsible for the it
|
||||||
|
- name: "Testcase: filenames with whitespaces"
|
||||||
|
community.general.iso_customize:
|
||||||
|
src_iso: "{{ test_dir }}/test.iso"
|
||||||
|
dest_iso: "{{ test_dir }}/{{ dest_iso_name }}"
|
||||||
|
add_files:
|
||||||
|
- src_file: " {{ test_dir }}/test01.cfg "
|
||||||
|
dest_file: "/preseed/ubuntu.seed"
|
||||||
|
register: customized_result
|
||||||
|
failed_when: customized_result.msg.find("does not exist") == -1
|
39
tests/integration/targets/iso_customize/tasks/iso_mount.yml
Normal file
39
tests/integration/targets/iso_customize/tasks/iso_mount.yml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# Copyright (c) 2022, Ansible Project
|
||||||
|
# Copyright (c) 2022, VMware, Inc. All Rights Reserved.
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- debug: var=ansible_distribution
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: "Mount customized ISO on MAC"
|
||||||
|
command: "hdiutil attach {{ test_dir }}/{{ iso_name }} -mountroot {{ test_dir }}/iso_mount"
|
||||||
|
|
||||||
|
# For MAC, we have different root directory for different type of ISO
|
||||||
|
- set_fact:
|
||||||
|
mount_root_dir: "{{ test_dir }}/iso_mount/disk_image"
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
mount_root_dir: "{{ test_dir }}/iso_mount/AUTOINSTALL"
|
||||||
|
when: iso_name.find('joliet') != -1
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
mount_root_dir: "{{ test_dir }}/iso_mount/CDROM"
|
||||||
|
when: iso_name.find('udf') != -1
|
||||||
|
when: ansible_distribution == "MacOSX"
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: "Mount {{ iso_name }} to {{ test_dir }}/iso_mount on localhost"
|
||||||
|
become: true
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: "{{ test_dir }}/iso_mount"
|
||||||
|
src: "{{ test_dir }}/{{ iso_name }}"
|
||||||
|
opts: "ro,noauto"
|
||||||
|
fstab: "{{ test_dir }}/temp.fstab"
|
||||||
|
fstype: "iso9660"
|
||||||
|
state: mounted
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
mount_root_dir: "{{ test_dir }}/iso_mount"
|
||||||
|
when:
|
||||||
|
- ansible_distribution != "MacOSX"
|
94
tests/integration/targets/iso_customize/tasks/main.yml
Normal file
94
tests/integration/targets/iso_customize/tasks/main.yml
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
####################################################################
|
||||||
|
# WARNING: These are designed specifically for Ansible tests #
|
||||||
|
# and should not be used as examples of how to write Ansible roles #
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
# Copyright (c) 2022, Ansible Project
|
||||||
|
# Copyright (c) 2022, VMware, Inc. All Rights Reserved.
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- name: Skip some platforms which does not support ansible.posix.mount
|
||||||
|
meta: end_play
|
||||||
|
when: ansible_distribution in ['Alpine']
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
test_dir: '{{ remote_tmp_dir }}/test_iso_customize'
|
||||||
|
|
||||||
|
- include_tasks: prepare.yml
|
||||||
|
|
||||||
|
- name: Create iso file with a specified file and directory
|
||||||
|
community.general.iso_create:
|
||||||
|
src_files:
|
||||||
|
- "{{ test_dir }}/test01.cfg"
|
||||||
|
- "{{ test_dir }}/test02.cfg"
|
||||||
|
dest_iso: "{{ test_dir }}/test.iso"
|
||||||
|
interchange_level: 3
|
||||||
|
|
||||||
|
- include_tasks: iso_customize.yml
|
||||||
|
vars:
|
||||||
|
dest_iso_name: "iso_customize.iso"
|
||||||
|
|
||||||
|
- name: Create an ISO file with Rock Ridge extension
|
||||||
|
community.general.iso_create:
|
||||||
|
src_files:
|
||||||
|
- "{{ test_dir }}/test01.cfg"
|
||||||
|
- "{{ test_dir }}/test02.cfg"
|
||||||
|
dest_iso: "{{ test_dir }}/test.iso"
|
||||||
|
rock_ridge: "1.09"
|
||||||
|
|
||||||
|
- include_tasks: iso_customize.yml
|
||||||
|
vars:
|
||||||
|
dest_iso_name: "iso_customize_rr.iso"
|
||||||
|
|
||||||
|
- name: Create an ISO file with Joliet support
|
||||||
|
community.general.iso_create:
|
||||||
|
src_files:
|
||||||
|
- "{{ test_dir }}/test01.cfg"
|
||||||
|
- "{{ test_dir }}/test02.cfg"
|
||||||
|
dest_iso: "{{ test_dir }}/test.iso"
|
||||||
|
interchange_level: 3
|
||||||
|
joliet: 3
|
||||||
|
vol_ident: AUTOINSTALL
|
||||||
|
|
||||||
|
- include_tasks: iso_customize.yml
|
||||||
|
vars:
|
||||||
|
dest_iso_name: "iso_customize_joliet.iso"
|
||||||
|
|
||||||
|
- name: Create iso file with UDF enabled
|
||||||
|
community.general.iso_create:
|
||||||
|
src_files:
|
||||||
|
- "{{ test_dir }}/test01.cfg"
|
||||||
|
- "{{ test_dir }}/test02.cfg"
|
||||||
|
dest_iso: "{{ test_dir }}/test.iso"
|
||||||
|
udf: True
|
||||||
|
|
||||||
|
- include_tasks: iso_customize.yml
|
||||||
|
vars:
|
||||||
|
dest_iso_name: "iso_customize_udf.iso"
|
||||||
|
|
||||||
|
# Create initial iso for customzing with only option add_files/delete_files
|
||||||
|
- name: Create iso file with a specified file and directory
|
||||||
|
community.general.iso_create:
|
||||||
|
src_files:
|
||||||
|
- "{{ test_dir }}/test01.cfg"
|
||||||
|
dest_iso: "{{ test_dir }}/test1.iso"
|
||||||
|
interchange_level: 3
|
||||||
|
|
||||||
|
- include_tasks: iso_customize_add_files.yml
|
||||||
|
vars:
|
||||||
|
dest_iso_name: "iso_customize_add.iso"
|
||||||
|
|
||||||
|
- include_tasks: iso_customize_delete_files.yml
|
||||||
|
vars:
|
||||||
|
dest_iso_name: "iso_customize_delete.iso"
|
||||||
|
|
||||||
|
# Test: misc exception
|
||||||
|
- include_tasks: iso_customize_exception.yml
|
||||||
|
vars:
|
||||||
|
dest_iso_name: "iso_customize_exception.iso"
|
||||||
|
|
||||||
|
- name: Delete testing sub-directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: '{{ test_dir }}'
|
||||||
|
state: absent
|
39
tests/integration/targets/iso_customize/tasks/prepare.yml
Normal file
39
tests/integration/targets/iso_customize/tasks/prepare.yml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# Copyright (c) 2022, Ansible Project
|
||||||
|
# Copyright (c) 2022, VMware, Inc. All Rights Reserved.
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- name: install pycdlib
|
||||||
|
ansible.builtin.pip:
|
||||||
|
name: pycdlib
|
||||||
|
|
||||||
|
- name: Make sure the previous testing sub-directory is deleted
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: '{{ test_dir }}'
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Create our testing sub-directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: '{{ test_dir }}'
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Create sub directory to mount customized ISO
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: '{{ test_dir }}/iso_mount'
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Create temporary file test01.cfg for testing
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ test_dir }}/test01.cfg"
|
||||||
|
state: touch
|
||||||
|
|
||||||
|
- name: Add a line to the file test01.cfg and make sure it succeed
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ test_dir }}/test01.cfg"
|
||||||
|
regexp: "^aaa"
|
||||||
|
line: "aaa"
|
||||||
|
|
||||||
|
- name: Create temporary file test02.cfg for testing
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ test_dir }}/test02.cfg"
|
||||||
|
state: touch
|
Loading…
Reference in a new issue