Skip to content

Commit f5fbcd4

Browse files
[Enabler] [zos_backup_restore] Add support for GDG and special characters (#1527)
* Added tests for gds * Added changelog and docs * Fixed issue * fixed unit tests * Added changlog for zos_backup * Reverted repr --------- Co-authored-by: André Marcel Gutiérrez Benítez <[email protected]>
1 parent 45c5561 commit f5fbcd4

File tree

3 files changed

+133
-14
lines changed

3 files changed

+133
-14
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
minor_changes:
2+
- zos_data_set - Added support for GDS relative name notation to include or exclude data sets when
3+
operation is backup. Added support for data set names with special characters
4+
like $, /#, and @.
5+
(https://github.com/ansible-collections/ibm_zos_core/pull/1527).

plugins/modules/zos_backup_restore.py

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
description:
5252
- When I(operation=backup), specifies a list of data sets or data set patterns
5353
to include in the backup.
54+
- When I(operation=backup) GDS relative names are supported.
5455
- When I(operation=restore), specifies a list of data sets or data set patterns
5556
to include when restoring from a backup.
5657
- The single asterisk, C(*), is used in place of exactly one qualifier.
@@ -68,6 +69,7 @@
6869
description:
6970
- When I(operation=backup), specifies a list of data sets or data set patterns
7071
to exclude from the backup.
72+
- When I(operation=backup) GDS relative names are supported.
7173
- When I(operation=restore), specifies a list of data sets or data set patterns
7274
to exclude when restoring from a backup.
7375
- The single asterisk, C(*), is used in place of exactly one qualifier.
@@ -117,6 +119,7 @@
117119
- There are no enforced conventions for backup names.
118120
However, using a common extension like C(.dzp) for UNIX files and C(.DZP) for data sets will
119121
improve readability.
122+
- GDS relative names are supported when I(operation=restore).
120123
type: str
121124
required: True
122125
recover:
@@ -217,6 +220,15 @@
217220
exclude: user.private.*
218221
backup_name: MY.BACKUP.DZP
219222
223+
- name: Backup a list of GDDs to data set my.backup.dzp
224+
zos_backup_restore:
225+
operation: backup
226+
data_sets:
227+
include:
228+
- user.gdg(-1)
229+
- user.gdg(0)
230+
backup_name: my.backup.dzp
231+
220232
- name: Backup all datasets matching the pattern USER.** to UNIX file /tmp/temp_backup.dzp, ignore recoverable errors.
221233
zos_backup_restore:
222234
operation: backup
@@ -312,18 +324,18 @@
312324
sms_management_class: DB2SMS10
313325
"""
314326

315-
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.better_arg_parser import (
316-
BetterArgParser,
317-
)
318-
from ansible.module_utils.basic import AnsibleModule
327+
import traceback
328+
from os import path
329+
from re import IGNORECASE, match, search
319330

320-
from re import match, search, IGNORECASE
331+
from ansible.module_utils.basic import AnsibleModule
332+
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.better_arg_parser import \
333+
BetterArgParser
334+
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.data_set import \
335+
DataSet
336+
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.import_handler import \
337+
ZOAUImportError
321338

322-
from ansible_collections.ibm.ibm_zos_core.plugins.module_utils.import_handler import (
323-
ZOAUImportError,
324-
)
325-
from os import path
326-
import traceback
327339
try:
328340
from zoautil_py import datasets
329341
from zoautil_py import exceptions as zoau_exceptions
@@ -386,8 +398,8 @@ def main():
386398
if operation == "backup":
387399
backup(
388400
backup_name=backup_name,
389-
include_data_sets=data_sets.get("include"),
390-
exclude_data_sets=data_sets.get("exclude"),
401+
include_data_sets=resolve_gds_name_if_any(data_sets.get("include")),
402+
exclude_data_sets=resolve_gds_name_if_any(data_sets.get("exclude")),
391403
volume=volume,
392404
full_volume=full_volume,
393405
temp_volume=temp_volume,
@@ -423,6 +435,26 @@ def main():
423435
module.exit_json(**result)
424436

425437

438+
def resolve_gds_name_if_any(data_set_list):
439+
"""Resolve all gds names in a list, if no gds relative name is found then
440+
the original name will be kept.
441+
Parameters
442+
----------
443+
data_set_list : list
444+
List of data set names.
445+
446+
Returns
447+
-------
448+
list
449+
List of data set names with resolved gds names.
450+
"""
451+
if isinstance(data_set_list, list):
452+
for index, name in enumerate(data_set_list):
453+
if DataSet.is_gds_relative_name(name):
454+
data_set_list[index] = DataSet.resolve_gds_absolute_name(name)
455+
return data_set_list
456+
457+
426458
def parse_and_validate_args(params):
427459
"""Parse and validate arguments to be used by remainder of module.
428460
@@ -662,7 +694,7 @@ def data_set_pattern_type(contents, dependencies):
662694
)
663695
for pattern in contents:
664696
if not match(
665-
r"^(?:(?:[A-Za-z$#@\?\*]{1}[A-Za-z0-9$#@\-\?\*]{0,7})(?:[.]{1})){1,21}[A-Za-z$#@\*\?]{1}[A-Za-z0-9$#@\-\*\?]{0,7}$",
697+
r"^(?:(?:[A-Za-z$#@\?\*]{1}[A-Za-z0-9$#@\-\?\*]{0,7})(?:[.]{1})){1,21}[A-Za-z$#@\*\?]{1}[A-Za-z0-9$#@\-\*\?]{0,7}(?:\(([-+]?[0-9]+)\)){0,1}$",
666698
str(pattern),
667699
IGNORECASE,
668700
):
@@ -832,7 +864,7 @@ def backup_name_type(contents, dependencies):
832864
if contents is None:
833865
return None
834866
if not match(
835-
r"^(?:(?:[A-Za-z$#@\?\*]{1}[A-Za-z0-9$#@\-\?\*]{0,7})(?:[.]{1})){1,21}[A-Za-z$#@\*\?]{1}[A-Za-z0-9$#@\-\*\?]{0,7}$",
867+
r"^(?:(?:[A-Za-z$#@\?\*]{1}[A-Za-z0-9$#@\-\?\*]{0,7})(?:[.]{1})){1,21}[A-Za-z$#@\*\?]{1}[A-Za-z0-9$#@\-\*\?]{0,7}(?:\(([-+]?[0-9]+)\)){0,1}$",
836868
str(contents),
837869
IGNORECASE,
838870
):

tests/functional/modules/test_zos_backup_restore.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,3 +713,85 @@ def test_restore_of_data_set_when_volume_does_not_exist(ansible_zos_module):
713713
# finally:
714714
# delete_data_set_or_file(hosts, data_set_name)
715715
# delete_data_set_or_file(hosts, DATA_SET_BACKUP_LOCATION)
716+
717+
718+
@pytest.mark.parametrize("dstype", ["seq", "pds", "pdse"])
719+
def test_backup_gds(ansible_zos_module, dstype):
720+
try:
721+
hosts = ansible_zos_module
722+
# We need to replace hyphens because of NAZARE-10614: dzip fails archiving data set names with '-'
723+
data_set_name = get_tmp_ds_name(symbols=True).replace("-", "")
724+
backup_dest = get_tmp_ds_name(symbols=True).replace("-", "")
725+
results = hosts.all.zos_data_set(name=data_set_name, state="present", type="gdg", limit=3)
726+
for result in results.contacted.values():
727+
assert result.get("changed") is True
728+
assert result.get("module_stderr") is None
729+
results = hosts.all.zos_data_set(name=f"{data_set_name}(+1)", state="present", type=dstype)
730+
for result in results.contacted.values():
731+
assert result.get("changed") is True
732+
assert result.get("module_stderr") is None
733+
results = hosts.all.zos_data_set(name=f"{data_set_name}(+1)", state="present", type=dstype)
734+
for result in results.contacted.values():
735+
assert result.get("changed") is True
736+
assert result.get("module_stderr") is None
737+
results = hosts.all.zos_backup_restore(
738+
operation="backup",
739+
data_sets=dict(include=[f"{data_set_name}(-1)", f"{data_set_name}(0)"]),
740+
backup_name=backup_dest,
741+
)
742+
for result in results.contacted.values():
743+
assert result.get("changed") is True
744+
assert result.get("module_stderr") is None
745+
finally:
746+
hosts.all.shell(cmd=f"drm ANSIBLE.* ")
747+
748+
749+
@pytest.mark.parametrize("dstype", ["seq", "pds", "pdse"])
750+
def test_backup_into_gds(ansible_zos_module, dstype):
751+
"""This test will create a dataset and backup it into a new generation of
752+
backup data sets.
753+
"""
754+
try:
755+
hosts = ansible_zos_module
756+
# We need to replace hyphens because of NAZARE-10614: dzip fails archiving data set names with '-'
757+
data_set_name = get_tmp_ds_name(symbols=True).replace("-", "")
758+
ds_name = get_tmp_ds_name(symbols=True).replace("-", "")
759+
results = hosts.all.zos_data_set(name=data_set_name, state="present", type="gdg", limit=3)
760+
for result in results.contacted.values():
761+
assert result.get("changed") is True
762+
assert result.get("module_stderr") is None
763+
results = hosts.all.zos_data_set(name=f"{data_set_name}(+1)", state="present", type=dstype)
764+
for result in results.contacted.values():
765+
assert result.get("changed") is True
766+
assert result.get("module_stderr") is None
767+
results = hosts.all.zos_data_set(name=ds_name, state="present", type=dstype)
768+
for result in results.contacted.values():
769+
assert result.get("changed") is True
770+
assert result.get("module_stderr") is None
771+
ds_to_write = f"{ds_name}(MEM)" if dstype in ['pds', 'pdse'] else ds_name
772+
results = hosts.all.shell(cmd=f"decho 'test line' \"{ds_to_write}\"")
773+
for result in results.contacted.values():
774+
assert result.get("changed") is True
775+
assert result.get("module_stderr") is None
776+
results = hosts.all.zos_backup_restore(
777+
operation="backup",
778+
data_sets=dict(include=[ds_name]),
779+
backup_name=f"{data_set_name}.G0002V00",
780+
)
781+
for result in results.contacted.values():
782+
assert result.get("changed") is True
783+
assert result.get("module_stderr") is None
784+
results = hosts.all.shell(cmd=f"drm \"{ds_name}\"")
785+
for result in results.contacted.values():
786+
assert result.get("changed") is True
787+
assert result.get("module_stderr") is None
788+
results = hosts.all.zos_backup_restore(
789+
operation="restore",
790+
backup_name=f"{data_set_name}(0)",
791+
)
792+
for result in results.contacted.values():
793+
assert result.get("changed") is True
794+
assert result.get("module_stderr") is None
795+
finally:
796+
hosts.all.shell(cmd=f"drm ANSIBLE.* ")
797+

0 commit comments

Comments
 (0)