diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 462155a..d84033e 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -2,115 +2,133 @@
**Topics**
-- v1\.28\.0
+- v1\.29\.0
- Release Summary
+ - New Modules
+- v1\.28\.0
+ - Release Summary
- Major Changes
- Minor Changes
- v1\.27\.0
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.26\.0
- - Release Summary
-- v1\.25\.0
- Release Summary
-- v1\.24\.0
+- v1\.25\.0
- Release Summary
-- v1\.23\.1
+- v1\.24\.0
- Release Summary
+- v1\.23\.1
+ - Release Summary
- Major Changes
- v1\.23\.0
- - Release Summary
+ - Release Summary
- Major Changes
- v1\.22\.1
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.22\.0
- - Release Summary
+ - Release Summary
- v1\.21\.0
- v1\.20\.0
- - Release Summary
+ - Release Summary
- Major Changes
- v1\.19\.0
- - Release Summary
+ - Release Summary
- Major Changes
- v1\.18\.0
- v1\.17\.0
- - Release Summary
+ - Release Summary
- Major Changes
- v1\.16\.0
- v1\.15\.1
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.15\.0
- - Release Summary
+ - Release Summary
- Major Changes
- v1\.14\.1
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.14\.0
- v1\.13\.0
- - Release Summary
+ - Release Summary
- Major Changes
- v1\.12\.0
- - Release Summary
-- v1\.11\.0
- Release Summary
+- v1\.11\.0
+ - Release Summary
- Minor Changes
- v1\.10\.0
- - Release Summary
-- v1\.9\.1
- Release Summary
+- v1\.9\.1
+ - Release Summary
- Minor Changes
- v1\.9\.0
- - Release Summary
+ - Release Summary
- Major Changes
- v1\.3\.2
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.3\.1
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.3\.0
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.2\.2
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.2\.1
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.2\.0
- - Release Summary
+ - Release Summary
- Major Changes
- v1\.1\.2
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.1\.1
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.1\.0
- - Release Summary
-- v1\.0\.5
- Release Summary
+- v1\.0\.5
+ - Release Summary
- Minor Changes
- v1\.0\.4
- - Release Summary
+ - Release Summary
- Major Changes
- Minor Changes
- v1\.0\.3
- - Release Summary
+ - Release Summary
- Minor Changes
- v1\.0\.2
- - Release Summary
+ - Release Summary
- Major Changes
- v1\.0\.0
- - Release Summary
+ - Release Summary
- Major Changes
+
+## v1\.29\.0
+
+
+### Release Summary
+
+Feature release
+
+
+### New Modules
+
+* sap\.sap\_operations\.enq\_admin\_info \- Get information from enq\_admin tool for ENSA2 systems
+* sap\.sap\_operations\.enq\_admin\_lock \- Manage enque server locks for ENSA2 SAP instances
+* sap\.sap\_operations\.enq\_admin\_locks\_info \- Get information about enque server locks for ENSA2 SAP instances
+
## v1\.28\.0
-
+
### Release Summary
Feature and bugfix release
@@ -131,7 +149,7 @@ Feature and bugfix release
## v1\.27\.0
-
+
### Release Summary
Feature release
@@ -145,7 +163,7 @@ Feature release
## v1\.26\.0
-
+
### Release Summary
Feature release
@@ -153,7 +171,7 @@ Feature release
## v1\.25\.0
-
+
### Release Summary
Feature release
@@ -161,7 +179,7 @@ Feature release
## v1\.24\.0
-
+
### Release Summary
Feature release
@@ -169,7 +187,7 @@ Feature release
## v1\.23\.1
-
+
### Release Summary
Documentation release
@@ -182,7 +200,7 @@ Documentation release
## v1\.23\.0
-
+
### Release Summary
Maintenance release
@@ -195,7 +213,7 @@ Maintenance release
## v1\.22\.1
-
+
### Release Summary
Documentation release
@@ -209,7 +227,7 @@ Documentation release
## v1\.22\.0
-
+
### Release Summary
Feature release
@@ -220,7 +238,7 @@ Feature release
## v1\.20\.0
-
+
### Release Summary
Feature release
@@ -233,7 +251,7 @@ Feature release
## v1\.19\.0
-
+
### Release Summary
Feature release
@@ -249,7 +267,7 @@ Feature release
## v1\.17\.0
-
+
### Release Summary
Feature release
@@ -265,7 +283,7 @@ Feature release
## v1\.15\.1
-
+
### Release Summary
Bugfix release
@@ -278,7 +296,7 @@ Bugfix release
## v1\.15\.0
-
+
### Release Summary
Feature release
@@ -291,7 +309,7 @@ Feature release
## v1\.14\.1
-
+
### Release Summary
Bug fix release
@@ -308,7 +326,7 @@ Bug fix release
## v1\.13\.0
-
+
### Release Summary
Feature release
@@ -321,7 +339,7 @@ Feature release
## v1\.12\.0
-
+
### Release Summary
Feature release
@@ -329,7 +347,7 @@ Feature release
## v1\.11\.0
-
+
### Release Summary
Feature release
@@ -342,7 +360,7 @@ Feature release
## v1\.10\.0
-
+
### Release Summary
Feature release
@@ -350,7 +368,7 @@ Feature release
## v1\.9\.1
-
+
### Release Summary
Bug fix release
@@ -364,7 +382,7 @@ Bug fix release
## v1\.9\.0
-
+
### Release Summary
Lifecycle release
@@ -382,7 +400,7 @@ Lifecycle release
## v1\.3\.2
-
+
### Release Summary
Bugfix release
@@ -395,7 +413,7 @@ Bugfix release
## v1\.3\.1
-
+
### Release Summary
Bugfix release
@@ -408,7 +426,7 @@ Bugfix release
## v1\.3\.0
-
+
### Release Summary
Feature release
@@ -423,7 +441,7 @@ Feature release
## v1\.2\.2
-
+
### Release Summary
Feature release
@@ -436,7 +454,7 @@ Feature release
## v1\.2\.1
-
+
### Release Summary
Feature release
@@ -449,7 +467,7 @@ Feature release
## v1\.2\.0
-
+
### Release Summary
Feature release
@@ -462,7 +480,7 @@ Feature release
## v1\.1\.2
-
+
### Release Summary
Bug Fix Release
@@ -475,7 +493,7 @@ Bug Fix Release
## v1\.1\.1
-
+
### Release Summary
Bug Fix Release
@@ -491,7 +509,7 @@ Bug Fix Release
## v1\.1\.0
-
+
### Release Summary
Feature Release
@@ -499,7 +517,7 @@ Feature Release
## v1\.0\.5
-
+
### Release Summary
Bug fix release
@@ -512,7 +530,7 @@ Bug fix release
## v1\.0\.4
-
+
### Release Summary
Two roles are added hana\_update and prepare
@@ -531,7 +549,7 @@ Two roles are added hana\_update and
## v1\.0\.3
-
+
### Release Summary
Using changelog fragments to build collection changelog\.
@@ -545,7 +563,7 @@ Using changelog fragments to build collection changelog\.
## v1\.0\.2
-
+
### Release Summary
First release of SAP Operations collection\.
@@ -562,7 +580,7 @@ First release of SAP Operations collection\.
## v1\.0\.0
-
+
### Release Summary
First release of SAP Operations collection\.
diff --git a/galaxy.yml b/galaxy.yml
index 7af77c9..c12e3d5 100644
--- a/galaxy.yml
+++ b/galaxy.yml
@@ -24,7 +24,7 @@ namespace: sap
name: sap_operations
-version: 1.28.0
+version: 1.29.0
readme: README.md
diff --git a/meta/runtime.yml b/meta/runtime.yml
index 3d4b6cb..b188618 100644
--- a/meta/runtime.yml
+++ b/meta/runtime.yml
@@ -22,8 +22,7 @@
---
requires_ansible: ">=2.15.0"
action_groups:
- "sap.sap_operations.cf":
- # TODO: action groups do not work - reason not clear
+ cf:
- cf_marketplace_info
- cf_service_instance
- cf_service_instance_info
@@ -32,3 +31,7 @@ action_groups:
- cf_spaces_info
- cf_service_instance_keys_info
- cf_service_instance_key
+ enq_admin:
+ - enq_admin_info
+ - enq_admin_locks_info
+ - enq_admin_lock
diff --git a/plugins/doc_fragments/enq_admin.py b/plugins/doc_fragments/enq_admin.py
new file mode 100644
index 0000000..d623ec5
--- /dev/null
+++ b/plugins/doc_fragments/enq_admin.py
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-3.0-only
+# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere
+#
+# Copyright 2024 Red Hat, Project Atmosphere
+#
+# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License along with this program.
+# If not, see .
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = r"""
+---
+requirements:
+ - only for ENSA2 instances with enq_admin tool installed
+
+author:
+ - Kirill Satarin (@kksat)
+
+options:
+ profile_filepath:
+ description:
+ - A path to the profile file
+ - Either I(profile_filepath) or I(sid), I(hostname) and I(port) required
+ - I(sid), I(hostname) and I(port) are required together
+ required: false
+ type: path
+ aliases:
+ - pf
+ - profile
+ sid:
+ description: SAP system id
+ required: false
+ type: str
+ hostname:
+ description: hostname where enque process is running
+ required: false
+ type: str
+ aliases:
+ - host
+ port:
+ description: Port where enque process is running
+ required: false
+ type: int
+
+seealso:
+ - name: Architecture of the Standalone Enqueue Server 2
+ description: Architecture of the Standalone Enqueue Server 2
+ link: https://help.sap.com/docs/latest/e458064e3077486994caaf9a85c4aa23/902412f09e134f5bb875adb6db585c92.html
+ - name: Parameter Reference of Standalone Enqueue Server 2
+ description: Parameter Reference of Standalone Enqueue Server 2
+ link: https://help.sap.com/docs/latest/e458064e3077486994caaf9a85c4aa23/1ca2eab4fca04d2696b7185f470b51aa.html
+"""
diff --git a/plugins/module_utils/enq_admin.py b/plugins/module_utils/enq_admin.py
new file mode 100644
index 0000000..f4f1065
--- /dev/null
+++ b/plugins/module_utils/enq_admin.py
@@ -0,0 +1,228 @@
+# SPDX-License-Identifier: GPL-3.0-only
+# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere
+#
+# Copyright 2024 Red Hat, Project Atmosphere
+#
+# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License along with this program.
+# If not, see .
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+class AnsibleModuleEnqAdmin(AnsibleModule):
+ def __init__(
+ self,
+ argument_spec=None,
+ required_together=None,
+ mutually_exclusive=None,
+ required_one_of=None,
+ supports_check_mode=False,
+ required_if=None,
+ required_by=None,
+ ):
+ """Custom AnsibleModule for enq_admin tool."""
+ enq_admin_argument_spec = dict(
+ profile_filepath=dict(
+ type="path", required=False, aliases=["pf", "profile"]
+ ),
+ sid=dict(type="str", required=False),
+ hostname=dict(type="str", required=False, aliases=["host"]),
+ port=dict(type="int", required=False),
+ )
+ enq_admin_required_together = [["sid", "hostname", "port"]]
+ enq_admin_mutually_exclusive = [
+ ["profile_filepath", "sid"],
+ ["profile_filepath", "hostname"],
+ ["profile_filepath", "port"],
+ ]
+ enq_admin_required_one_of = [["profile_filepath", "sid", "hostname", "port"]]
+
+ if argument_spec is not None:
+ argument_spec = {
+ k: v
+ for d in (argument_spec, enq_admin_argument_spec)
+ for k, v in d.items()
+ }
+ else:
+ argument_spec = enq_admin_argument_spec
+ required_together = [] if required_together is None else required_together
+ mutually_exclusive = [] if mutually_exclusive is None else mutually_exclusive
+ required_one_of = [] if required_one_of is None else required_one_of
+ required_if = [] if required_if is None else required_if
+ required_by = {} if required_by is None else required_by
+
+ super(AnsibleModuleEnqAdmin, self).__init__(
+ argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive + enq_admin_mutually_exclusive,
+ required_together=required_together + enq_admin_required_together,
+ required_one_of=required_one_of + enq_admin_required_one_of,
+ supports_check_mode=supports_check_mode,
+ required_if=required_if,
+ required_by=required_by,
+ )
+
+
+def run_enq_admin(args, module=None):
+ if module is None:
+ return (0, "", "module is required")
+
+ profile_filepath = module.params.get("profile_filepath")
+ sid = module.params.get("sid")
+ hostname = module.params.get("hostname")
+ port = module.params.get("port")
+
+ enq_admin_path = module.get_bin_path("enq_admin")
+
+ if profile_filepath:
+ default_args = [enq_admin_path, "pf={0}".format(profile_filepath)]
+ else:
+ default_args = [
+ enq_admin_path,
+ "--sid={0}".format(sid),
+ "--host={0}".format(hostname),
+ "--port={0}".format(port),
+ ]
+
+ return module.run_command(default_args + args)
+
+
+def convert_string2bool(value):
+ if value.lower() == "true":
+ return True
+ if value.lower() == "false":
+ return False
+ return value
+
+
+def get_table_from_csv_data(data):
+ data_lines = data.split("\n")
+ table_name = data_lines[0].strip().strip(":")
+ column_names = data_lines[1].split(";")
+ column_names = [name.strip() for name in column_names if name != ""]
+ table = []
+ for row in data_lines[2:]:
+ values = row.split(";")
+ values = [convert_string2bool(value) for value in values]
+ table.append(dict(zip(column_names, values)))
+
+ return dict(
+ table=table,
+ table_name=table_name,
+ )
+
+
+def enq_admin_process_stdout(stdout):
+ [header, data, footer] = stdout.split("\n\n")
+ table = get_table_from_csv_data(data)
+ return dict(
+ header=header,
+ data=data,
+ footer=footer,
+ table=table["table"],
+ table_name=table["table_name"],
+ )
+
+
+long_lock_type = {
+ "X": "Exclusive",
+ "E": "Write",
+ "S": "Read",
+ "O": "Optimistic",
+}
+
+
+def lock_has_same_attributes(lock, lock_type, owner1, owner2, name, argument):
+ return (
+ lock["Type"] == long_lock_type[lock_type] and
+ lock["Owner 1"] == owner1 and
+ lock["Owner 2"] == owner2 and
+ lock["Name"] == name and
+ lock["Argument"] == argument
+ )
+
+
+def exclusive_lock_with_same_attributes_exists(
+ lock, lock_type, owner1, owner2, name, argument
+):
+ return (
+ lock["Type"] == long_lock_type["X"] and
+ lock["Owner 1"] == owner1 and
+ lock["Owner 2"] == owner2 and
+ lock["Name"] == name and
+ lock["Argument"] == argument
+ )
+
+
+def find_locks_in_locks_list(locks, lock_type, owner1, owner2, name, argument):
+ return [
+ lock
+ for lock in locks
+ if lock_has_same_attributes(lock, lock_type, owner1, owner2, name, argument) or
+ exclusive_lock_with_same_attributes_exists(
+ lock, lock_type, owner1, owner2, name, argument
+ )
+ ]
+
+
+def lock_exists_in_locks_list(locks, lock_type, owner1, owner2, name, argument):
+ return (
+ len(find_locks_in_locks_list(locks, lock_type, owner1, owner2, name, argument)) > 0
+ )
+
+
+def get_enq_admin_locks_info(
+ module, run_enq_admin, n="*", client="*", user="*", name="*", argument="*"
+):
+ args = [
+ "--csv",
+ "--no_color",
+ "--locks={0}:{1}:{2}:{3}:{4}".format(n, client, user, name, argument),
+ ]
+
+ rc, stdout, stderr = run_enq_admin(module=module, args=args)
+
+ if rc == 0:
+ data = enq_admin_process_stdout(stdout)
+ return dict(changed=False, stdout=stdout, enq_admin_locks_info=data["table"])
+ return dict(
+ failed=True,
+ msg="Failed to get information from enq_admin tool",
+ stderr=stderr,
+ stdout=stdout,
+ rc=rc,
+ )
+
+
+def ensure_locks(
+ state, lock_type, owner1, owner2, name, argument, run_enq_admin, module
+):
+ lock_command = "--set_locks" if state == "present" else "--release_locks"
+ args = [
+ "--csv",
+ "--no_color",
+ "{0}=1:{1}:{2}:{3}:{4}:{5}".format(
+ lock_command, lock_type, owner1, owner2, name, argument
+ ),
+ ]
+
+ rc, stdout, stderr = run_enq_admin(module=module, args=args)
+ return rc, stdout, stderr
diff --git a/plugins/modules/enq_admin_info.py b/plugins/modules/enq_admin_info.py
new file mode 100644
index 0000000..1c0ba48
--- /dev/null
+++ b/plugins/modules/enq_admin_info.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python
+
+# SPDX-License-Identifier: GPL-3.0-only
+# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere
+#
+# Copyright 2024 Red Hat, Project Atmosphere
+#
+# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License along with this program.
+# If not, see .
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: enq_admin_info
+
+extends_documentation_fragment:
+- sap.sap_operations.community
+- sap.sap_operations.enq_admin
+
+author:
+ - Kirill Satarin (@kksat)
+
+short_description: Get information from enq_admin tool for ENSA2 systems
+
+description: Get information from enq_admin tool for ENSA2 systems
+
+version_added: 1.29.0
+
+options: {}
+"""
+
+EXAMPLES = r"""
+---
+- name: Get information from enq_admin tool for ENSA2 systems
+ sap.sap_operations.enq_admin_info:
+ pf: /usr/sap/S4H/SYS/profile/S4H_ASCS20_s4ascsa
+"""
+
+RETURN = r"""
+---
+stdout:
+ description: Standard output from enq_admin tool
+ returned: success
+ type: str
+ sample: |
+ Enqueue Server 2
+ Server Status : STATUS_RUNNING
+ Repl.State : REPLICATION_ON
+ Repl.Type : REPLICATION_REPLICATOR
+ Process ID : 17591
+ Start Time : 2024-04-30 11:12:13
+ Release : 789
+ Patch Level : 100
+ Compiled On : Linux GNU SLES-15 x86_64 cc10.3.0 use-pr230217
+ Compiled At : Feb 17 2023 18:43:20
+ Number Clients: 38
+ Dev.Trc. Level: 1
+ Req.Trc. Level: 0
+
+ 2024-05-03 15:35:11; OK; 'Process Information'; Response=281 usec
+ ===================================================================================================
+"""
+
+
+from ansible_collections.sap.sap_operations.plugins.module_utils.enq_admin import (
+ AnsibleModuleEnqAdmin,
+ run_enq_admin,
+)
+
+
+def run_module(module, run_enq_admin):
+ rc, stdout, stderr = run_enq_admin(module=module, args=["--info", "--no_color"])
+
+ if rc == 0:
+ return dict(changed=False, stdout=stdout)
+ return dict(
+ failed=True,
+ msg="Failed to get information from enq_admin tool",
+ stderr=stderr,
+ )
+
+
+def main():
+
+ module = AnsibleModuleEnqAdmin(
+ argument_spec={},
+ supports_check_mode=True,
+ )
+ result = run_module(module, run_enq_admin=run_enq_admin)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/enq_admin_lock.py b/plugins/modules/enq_admin_lock.py
new file mode 100644
index 0000000..185690d
--- /dev/null
+++ b/plugins/modules/enq_admin_lock.py
@@ -0,0 +1,274 @@
+#!/usr/bin/python
+
+# SPDX-License-Identifier: GPL-3.0-only
+# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere
+#
+# Copyright 2024 Red Hat, Project Atmosphere
+#
+# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License along with this program.
+# If not, see .
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: enq_admin_lock
+
+extends_documentation_fragment:
+ - sap.sap_operations.community
+ - sap.sap_operations.enq_admin
+
+author:
+ - Kirill Satarin (@kksat)
+
+short_description: Manage enque server locks for ENSA2 SAP instances
+version_added: 1.29.0
+description:
+ - Manage enque server locks for ENSA2 SAP instances
+ - This module will set/release only one lock at a time (parameter n for enq_admin equals 1, see documentation)
+ - If several locks are needed, use this module several times or in loop, see examples
+ - If exclusive lock is set, other locks will not be created, information about existing exclusive lock will be created
+ - Module is idempotent, new locks will not be created if this type of lock or exclusive lock already exists with the same parameters
+ - |
+ Value '%u' for parameters is not supported - because only one lock is set/released at a time,
+ this is not handled by this module. Use ansible loops for this.
+
+options:
+ state:
+ description: State of the lock (present or absent)
+ required: false
+ type: str
+ default: "present"
+ choices:
+ - absent
+ - present
+ lock_type:
+ description:
+ - Lock type
+ - X - Exclusive lock ("Exclusive")
+ - E - Exclusive non cumulative lock ("Write")
+ - S - Shared lock ("Read")
+ - O - Optimistic lock ("Optimistic")
+ - if exclusive lock is set, other locks are not allowed
+ required: true
+ type: str
+ aliases:
+ - type
+ choices:
+ - X
+ - E
+ - S
+ - O
+ owner1:
+ description: Pattern for dialog lock owner
+ required: false
+ type: str
+ default: ""
+ owner2:
+ description: Pattern for update lock owner
+ required: false
+ type: str
+ default: ""
+ name:
+ description: Pattern for lock name
+ required: false
+ type: str
+ default: ""
+ argument:
+ description:
+ - Pattern for lock argument
+ required: false
+ type: str
+ default: ""
+"""
+
+EXAMPLES = r"""
+---
+- name: Set lock
+ sap.sap_operations.enq_admin_lock:
+ pf: /usr/sap/S4H/SYS/profile/S4H_ASCS20_s4ascsa
+ state: present
+ lock_type: E
+ owner1: DIAG
+ owner2: ""
+ name: ANSIBLE
+ argument: ANSIBLE
+
+- name: Release lock
+ sap.sap_operations.enq_admin_lock:
+ pf: /usr/sap/S4H/SYS/profile/S4H_ASCS20_s4ascsa
+ state: absent
+ lock_type: E
+ owner1: DIAG
+ owner2: ""
+ name: ANSIBLE
+ argument: ANSIBLE
+"""
+
+RETURN = r"""
+---
+enq_admin_lock:
+ description: Lock information
+ returned: if state is present and execution was successful
+ type: dict
+ sample:
+ Argument: ANSIBLE
+ BCK: false
+ Client: '000'
+ Count 1: '1'
+ Count 2: '0'
+ Name: ANSIBLE
+ Object: E_FILL
+ Owner 1: DIAG
+ Owner 2: ''
+ TCODE: SFILL
+ Type: Write
+ User: FILL_USER
+"""
+
+
+from ansible_collections.sap.sap_operations.plugins.module_utils.enq_admin import (
+ AnsibleModuleEnqAdmin,
+ find_locks_in_locks_list,
+ run_enq_admin,
+ get_enq_admin_locks_info,
+ ensure_locks,
+)
+
+
+def run_module(module, run_enq_admin):
+ lock_type = module.params.get("lock_type")
+ owner1 = module.params.get("owner1")
+ owner2 = module.params.get("owner2")
+ name = module.params.get("name")
+ argument = module.params.get("argument")
+ state = module.params.get("state", "present")
+
+ existing_locks = get_enq_admin_locks_info(
+ module=module, run_enq_admin=run_enq_admin
+ )
+ if existing_locks.get("failed"):
+ return dict(
+ failed=True,
+ msg="Failed to get existing locks from enq_admin tool",
+ stdout=existing_locks.get("stdout"),
+ stderr=existing_locks.get("stderr"),
+ rc=existing_locks.get("rc"),
+ )
+
+ existing_locks = find_locks_in_locks_list(
+ locks=existing_locks.get("enq_admin_locks_info", []),
+ lock_type=lock_type,
+ owner1=owner1,
+ owner2=owner2,
+ name=name,
+ argument=argument,
+ )
+ if state == "present" and existing_locks:
+ return dict(
+ changed=False,
+ enq_admin_lock=existing_locks[0],
+ )
+ elif state == "present" and not existing_locks:
+ rc, stdout, stderr = ensure_locks(
+ state, lock_type, owner1, owner2, name, argument, run_enq_admin, module
+ )
+
+ if rc == 0:
+ created_locks = get_enq_admin_locks_info(
+ module=module, run_enq_admin=run_enq_admin
+ )
+ if created_locks.get("failed"):
+ return dict(
+ failed=True,
+ msg="Failed to get locks from enq_admin tool",
+ stdout=created_locks.get("stdout"),
+ stderr=created_locks.get("stderr"),
+ rc=created_locks.get("rc"),
+ )
+
+ created_locks = find_locks_in_locks_list(
+ locks=created_locks.get("enq_admin_locks_info", []),
+ lock_type=lock_type,
+ owner1=owner1,
+ owner2=owner2,
+ name=name,
+ argument=argument,
+ )
+ if not created_locks:
+ return dict(
+ failed=True,
+ msg="Failed to get created lock from enq_admin tool",
+ stdout=created_locks.get("stdout"),
+ stderr=created_locks.get("stderr"),
+ rc=created_locks.get("rc"),
+ )
+
+ return dict(changed=True, enq_admin_lock=created_locks[0])
+ else:
+ return dict(
+ failed=True,
+ msg="Failed to get information from enq_admin tool",
+ stderr=stderr,
+ )
+
+ if state == "absent" and not existing_locks:
+ return dict(
+ changed=False,
+ enq_admin_lock=[],
+ )
+
+ if state == "absent" and existing_locks:
+ rc, stdout, stderr = ensure_locks(
+ state, lock_type, owner1, owner2, name, argument, run_enq_admin, module
+ )
+ if rc == 0:
+ return dict(changed=True, enq_admin_lock=[])
+ else:
+ return dict(
+ failed=True,
+ msg="Failed to release enq_admin tool",
+ stderr=stderr,
+ stdout=stdout,
+ rc=rc,
+ )
+
+
+def main():
+ argument_spec = dict(
+ state=dict(type="str", choices=["absent", "present"], default="present"),
+ lock_type=dict(
+ type="str", choices=["X", "E", "S", "O"], aliases=["type"], required=True
+ ),
+ owner1=dict(type="str", default=""),
+ owner2=dict(type="str", default=""),
+ name=dict(type="str", default=""),
+ argument=dict(type="str", default=""),
+ )
+
+ module = AnsibleModuleEnqAdmin(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+ result = run_module(module, run_enq_admin=run_enq_admin)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/enq_admin_locks_info.py b/plugins/modules/enq_admin_locks_info.py
new file mode 100644
index 0000000..199ff74
--- /dev/null
+++ b/plugins/modules/enq_admin_locks_info.py
@@ -0,0 +1,173 @@
+#!/usr/bin/python
+
+# SPDX-License-Identifier: GPL-3.0-only
+# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere
+#
+# Copyright 2024 Red Hat, Project Atmosphere
+#
+# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License along with this program.
+# If not, see .
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: enq_admin_locks_info
+
+extends_documentation_fragment:
+- sap.sap_operations.community
+- sap.sap_operations.enq_admin
+
+author:
+ - Kirill Satarin (@kksat)
+
+short_description: Get information about enque server locks for ENSA2 SAP instances
+
+description: Get information about enque server locks for ENSA2 SAP instances
+
+version_added: 1.29.0
+
+options:
+ n:
+ description: Number of locks to retrieve
+ required: false
+ type: str
+ default: '*'
+ aliases:
+ - number
+ client:
+ description: Pattern for client
+ required: false
+ type: str
+ default: '*'
+ user:
+ description: Pattern for user name
+ required: false
+ type: str
+ default: '*'
+ name:
+ description: Pattern for lock name
+ required: false
+ type: str
+ default: '*'
+ argument:
+ description: Pattern for lock argument
+ required: false
+ type: str
+ default: '*'
+"""
+
+EXAMPLES = r"""
+---
+- name: Get information about locks from enq_admin tool for ENSA2 systems
+ sap.sap_operations.enq_admin_locks_info:
+ pf: /usr/sap/S4H/SYS/profile/S4H_ASCS20_s4ascsa
+"""
+
+RETURN = r"""
+---
+enq_admin_locks_info:
+ description: List of locks, filtered by provided parameters
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - Argument: "0"
+ BCK: false
+ Client: "000"
+ Count 1: "1"
+ Count 2: "0"
+ Name: ANSIBLE2
+ Object: E_FILL
+ Owner 1: DIAG
+ Owner 2: ""
+ TCODE: SFILL
+ Type: Exclusive
+ User: FILL_USER
+ - Argument: O
+ BCK: false
+ Client: "000"
+ Count 1: "1"
+ Count 2: "0"
+ Name: ANSIBLE
+ Object: E_FILL
+ Owner 1: DIAG
+ Owner 2: ""
+ TCODE: SFILL
+ Type: Optimistic
+ User: FILL_USER
+stdout:
+ description: Standard output of the enq_admin tool
+ returned: always
+ type: str
+ sample: |
+ Enqueue Server 2
+ LID=(17591/1714468346); RN=(300151); Current=20; Peak=30; Maximum=250000
+
+ Locks:
+ Type;Count 1;Count 2;BCK;Object;User;Client;TCODE;Owner 1;Owner 2;Name;Argument;
+ Exclusive;1;0;false;E_FILL;FILL_USER;000;SFILL;DIAG;;ANSIBLE2;0;
+ Optimistic;1;0;false;E_FILL;FILL_USER;000;SFILL;DIAG;;ANSIBLE;O;
+
+ 2024-05-03 15:36:25; OK; 'Lock List'; Response=578 usec
+ ===================================================================================================
+"""
+
+
+from ansible_collections.sap.sap_operations.plugins.module_utils.enq_admin import (
+ AnsibleModuleEnqAdmin,
+ run_enq_admin,
+ get_enq_admin_locks_info,
+)
+
+
+def main():
+ argument_spec = dict(
+ n=dict(type="str", default="*", aliases=["number"]),
+ client=dict(type="str", default="*"),
+ user=dict(type="str", default="*"),
+ name=dict(type="str", default="*"),
+ argument=dict(type="str", default="*"),
+ )
+
+ module = AnsibleModuleEnqAdmin(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ n = module.params.get("n", "*")
+ client = module.params.get("client", "*")
+ user = module.params.get("user", "*")
+ name = module.params.get("name", "*")
+ argument = module.params.get("argument", "*")
+
+ result = get_enq_admin_locks_info(
+ module=module,
+ run_enq_admin=run_enq_admin,
+ n=n,
+ client=client,
+ user=user,
+ name=name,
+ argument=argument,
+ )
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()