Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b8c982b
First iteration
Oct 29, 2025
411f8d0
Fix bad return
Oct 29, 2025
400c79a
Generate pytest test
Oct 29, 2025
cc2bc29
Add documentation and full test suite
Oct 29, 2025
491c971
Fix comments
Oct 31, 2025
2580b14
Fix dataset
Oct 31, 2025
98c9410
Fix bandit
Oct 31, 2025
3d1b82b
Fix typo
Oct 31, 2025
c07d41f
Move files for dependency finder
Nov 3, 2025
c63ded2
Rename filter to filters
Nov 3, 2025
8ae1f98
Return name
Nov 3, 2025
97069f2
Add to dependency finder for filters
Nov 3, 2025
eff0c5e
Add to ansible config to ensure work properly
Nov 3, 2025
0577d52
Update to use middle and last level qualifier
Nov 7, 2025
196f559
tests/functional/filters/test_generate_data_set_name.py
Nov 7, 2025
1f8f9d0
Complete role
Nov 7, 2025
e7bd73f
Fix sanity
Nov 7, 2025
3002125
Merge branch 'dev' into filter/2165/Filter_for_data_set_name
AndreMarcel99 Nov 7, 2025
65e19a3
Fix sanity f
Nov 7, 2025
4042f97
Merged from main
fernandofloresg Nov 10, 2025
31f455b
Update plugins/filter/generate_data_set_name.py
AndreMarcel99 Nov 11, 2025
bfb4709
Update plugins/filter/generate_data_set_name.py
AndreMarcel99 Nov 11, 2025
31b0a9d
Update documentation
Nov 11, 2025
8ddc145
Fix Examples documentation
Nov 11, 2025
5be31c4
Avoid extra space
Nov 11, 2025
13cc71b
Added documentation for filters
fernandofloresg Nov 11, 2025
c36eb5e
updated source
fernandofloresg Nov 11, 2025
9d183fd
Added source
fernandofloresg Nov 11, 2025
5785fac
Modified doc generation makefile
fernandofloresg Nov 11, 2025
de45adf
Updated documentation
fernandofloresg Nov 11, 2025
40924ee
Updated rst
fernandofloresg Nov 11, 2025
2cbc016
Modifiy from last to low
Nov 11, 2025
e6b71d2
Update error message test case
Nov 11, 2025
77064ae
Merge branch 'dev' into filter/2165/Filter_for_data_set_name
AndreMarcel99 Nov 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ansible.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
[defaults]
forks = 25
action_plugins=~/.ansible/collections/ansible_collections/ibm/ibm_zos_core/plugins/action
filter_plugins=~/.ansible/collections/ansible_collections/ibm/ibm_zos_core/plugins/filter
# remote_tmp = /u/ansible/tmp
# remote_port = 22
# debug = True
Expand Down
77 changes: 77 additions & 0 deletions plugins/filter/generate_data_set_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright (c) IBM Corporation 2025
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import secrets
import string
import re
from ansible.errors import AnsibleFilterError


def generate_data_set_name(value, generations=1):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first sight generations might seem to be referring to a different thing, it will be better to use something like count or num_names

"""Filter to generate valid data set names

Args:
value {str} -- value of high level qualifier to use on data set names
generations {int, optional} -- number of dataset names to generate. Defaults to 1.

Returns:
list -- the total dataset names valid
"""
if len(value) > 8:
raise AnsibleFilterError("The high level qualifier is too long for the data set name")

if generations > 1:
dataset_names = []
for generation in range(generations):
name = value + get_tmp_ds_name()
dataset_names.append(name)
else:
dataset_names = value + get_tmp_ds_name()

return dataset_names


def get_tmp_ds_name():
"""Unify the random qualifiers generate in one name.

Returns:
str: valid data set name
"""
ds = "."
ds += "P" + get_random_q() + "."
ds += "T" + get_random_q() + "."
ds += "C" + get_random_q()
return ds


def get_random_q():
""" Function or test to ensure random hlq of datasets"""
# Generate the first random hlq of size pass as parameter
letters = string.ascii_uppercase + string.digits
random_q = ''.join(secrets.choice(letters)for iteration in range(7))
count = 0
# Generate a random HLQ and verify if is valid, if not, repeat the process
while count < 5 and not re.fullmatch(
r"^(?:[A-Z$#@]{1}[A-Z0-9$#@-]{0,7})",
random_q,
re.IGNORECASE,
):
random_q = ''.join(secrets.choice(letters)for iteration in range(7))
count += 1
return random_q


class FilterModule(object):
""" Jinja2 filter for the returned list or string by the collection module. """
def filters(self):
return {
'generate_data_set_name': generate_data_set_name
}
39 changes: 39 additions & 0 deletions plugins/filter/generate_data_set_name.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (c) IBM Corporation 2025
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
DOCUMENTATION:
name: generate_data_set_name
author: Marcel Gutierrez (@AndreMarcel99)
version_added: "2.0.0"
short_description: Filter returned valid data set names
description:
- Provide a valid temporary data set name.
options:
value:
description:
- High level qualifier.
type: str
required: true
generations:
description:
- Number of data set names that you require to generate.
type: int
required: false

EXAMPLES: |
# Get only one data set name.
clean_output: "{{ hlq | ibm.ibm_zos_core.generate_data_set_name}}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the result if I do this ?

{{ ibm.ibm_zos_core.generate_data_set_name }}

Asking because I would expect to have either an error or a randomly generated hlq, when using mvstmp it uses the result of whoami command

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case we cannot generate a valid HLQ even having the ansible user, so we could fail in case no HLQ is provided

# Get 10 data set names.
clean_output: "{{ hlq | ibm.ibm_zos_core.generate_data_set_name(10)}}"

RETURN:
_value:
description: Name or names generated by the filter.
type: list
6 changes: 6 additions & 0 deletions tests/dependencyfinder.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this!!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update the copyright year

Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,12 @@ def get_changed_plugins(path, branch="origin/dev"):
path_corrected_line = line.split("|", 1)[0].strip()
if "plugins/modules/" in line:
path_corrected_line = line.split("|", 1)[0].strip()
if "plugins/filter/" in line:
path_corrected_line = line.split("|", 1)[0].strip()
if "functional/filters/" in line:
if re.match('..', line):
line = line.replace("..", "tests")
path_corrected_line = line.split("|", 1)[0].strip()
if "functional/modules/" in line:
if re.match('...', line):
line = line.replace("...", "tests")
Expand Down
49 changes: 49 additions & 0 deletions tests/functional/filters/test_generate_data_set_name.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, would only add one testing when is empty (if possible even)

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-

# Copyright (c) IBM Corporation 2025
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import, division, print_function

__metaclass__ = type

import pytest

def test_generate_data_set_name_filter(ansible_zos_module):
hosts = ansible_zos_module
input_string = "OMVSADM"
hosts.all.set_fact(input_string=input_string)
results = hosts.all.debug(msg="{{ input_string | generate_data_set_name }}")

for result in results.contacted.values():
assert result.get('msg') is not None
assert input_string in result.get('msg')

def test_generate_data_set_name_filter_multiple_generations(ansible_zos_module):
hosts = ansible_zos_module
input_string = "OMVSADM"
hosts.all.set_fact(input_string=input_string)
results = hosts.all.debug(msg="{{ input_string | generate_data_set_name(10) }}")

for result in results.contacted.values():
assert result.get('msg') is not None
assert input_string in result.get('msg')[0]
assert len(result.get('msg')) == 10

def test_generate_data_set_name_filter_bad_hl1(ansible_zos_module):
hosts = ansible_zos_module
input_string = "OMVSADMONE"
hosts.all.set_fact(input_string=input_string)
results = hosts.all.debug(msg="{{ input_string | generate_data_set_name }}")

for result in results.contacted.values():
assert result.get('failed') is True
assert result.get('msg') == "The high level qualifier is too long for the data set name"