Init: mediaserver

This commit is contained in:
2023-02-08 12:13:28 +01:00
parent 848bc9739c
commit f7c23d4ba9
31914 changed files with 6175775 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
===========================
Community SAP Release Notes
===========================
.. contents:: Topics
v1.4.0
======
Release Summary
---------------
This is the 1.3.0 minor release of the ``community.sap_libs`` collection.
This changelog contains all changes to the modules and plugins in this collection
that have been made after the previous release.
Bugfixes
--------
- fix a bug where some commands produces no output which cause to crash the module.
- modules - fix a "variable used before assignment" that cannot be reached but causes sanity test failures.
v1.3.0
======
Release Summary
---------------
This is the 1.3.0 minor release of the ``community.sap_libs`` collection. This changelog contains all changes to the modules and plugins in this collection that have been made after the previous release.
Minor Changes
-------------
- License requirements are updated.
- The modules purposes are described clearer.
- The namespaces of the modules are removed to provide a flatter design.
- hana_query - module is moved to sap_hdbsql.
- sapcontrol - module is moved to sap_control_exec to have a clearer separation to other roles and references.
v1.2.0
======
Release Summary
---------------
This is the minor release of the ``community.sap_libs`` collection.
This changelog contains all changes to the modules and plugins in this collection
that have been made after the previous release.
Bugfixes
--------
- syp_system_facts - fix a typo in the usage example which lead to an error if it used as supposed.
New Modules
-----------
- sap_pyrfc - Ansible Module for use of SAP PyRFC to execute SAP RFCs (Remote Function Calls) to SAP remote-enabled function modules
v1.1.0
======
Release Summary
---------------
This is the minor release of the ``community.sap_libs`` collection.
This changelog contains all changes to the modules and plugins in this collection
that have been made after the previous release.
New Modules
-----------
System
~~~~~~
- sapcontrol - Ansible Module to execute SAPCONTROL
v1.0.0
======
Release Summary
---------------
This is the minor release of the ``community.sap`` collection. It is the initial relase for the ``community.sap`` collection
New Modules
-----------
Database
~~~~~~~~
saphana
^^^^^^^
- hana_query - Ansible Module to execute SQL on SAP HANA
Files
~~~~~
- sapcar_extract - Manages SAP SAPCAR archives
Identity
~~~~~~~~
- sap_company - This module will manage a company entities in a SAP S4HANA environment
- sap_user - This module will manage a user entities in a SAP S4/HANA environment
System
~~~~~~
- sap_snote - This module will upload and (de)implements C(SNOTES) in a SAP S4HANA environment.
- sap_system_facts - Gathers SAP facts in a host
- sap_task_list_execute - Perform SAP Task list execution

View File

@@ -0,0 +1,3 @@
# Community Code of Conduct
Please see the official [Ansible Community Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html).

View File

@@ -0,0 +1,3 @@
# Contributing
Refer to the [Contributing guidelines](https://github.com/ansible/community-docs/blob/main/contributing.rst).

View File

@@ -0,0 +1,614 @@
{
"files": [
{
"name": ".",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": ".github",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": ".github/ISSUE_TEMPLATE",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": ".github/ISSUE_TEMPLATE/bug_report.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "575804259b5332b40427800e6e11bd676b392c2267f569c713f4ebbe29cfbbe0",
"format": 1
},
{
"name": ".github/ISSUE_TEMPLATE/config.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "2e5f08c57601d637ec507daec616f993993d16f51892ca62214932b4fad0dcd9",
"format": 1
},
{
"name": ".github/ISSUE_TEMPLATE/documentation_report.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "6d775ce8659e9e8b73b442ba1b64c406d9ab022c181e7de40967a9ad17c8943b",
"format": 1
},
{
"name": ".github/ISSUE_TEMPLATE/feature_request.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "b441852aab4cb82226dd1e17a34d78b1ddd32e394e1daac91b8fd758e200123c",
"format": 1
},
{
"name": ".github/workflows",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": ".github/workflows/ansible-test.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "6151f0e825ee6c6fcbc7efb6d05ee1ad77e9fe287a795530ed0282b000582dc6",
"format": 1
},
{
"name": ".vscode",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": ".vscode/extensions.json",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "f72b83e1aa1301adb18ce0fe71ce6613d3cfb148f881b3e39c55359d41d3277f",
"format": 1
},
{
"name": "changelogs",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "changelogs/fragments",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "changelogs/fragments/.keep",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "changelogs/changelog.yaml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "3b44c5d2bef81e7a8d6698115abbb50c17bfe29c63052957f46ef8800fb3a895",
"format": 1
},
{
"name": "changelogs/config.yaml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "5f253a9432287fcdfe3100b556bf39d442a5e8b5a5fd3ec4a971f37f954ebac0",
"format": 1
},
{
"name": "meta",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "meta/runtime.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "592bc6961db54832e62eeebe83912aa3041c6bcfaf3df3a1a7a867cfbc27c55d",
"format": 1
},
{
"name": "plugins",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "plugins/doc_fragments",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "plugins/doc_fragments/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "plugins/module_utils",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "plugins/module_utils/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "plugins/module_utils/pyrfc_handler.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "ab0ba29a768b4478f3fa606a530712b845d3f68fb3e9b1bdf6a3988e0ce283cc",
"format": 1
},
{
"name": "plugins/module_utils/swpm2_parameters_inifile_generate.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "4320fb963ddfb7d2434da4426e09c57feded076c241ba44a3de18c57eee043c3",
"format": 1
},
{
"name": "plugins/modules",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "plugins/modules/sap_company.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "a3641687701245c9fbadb1fba2ce4bfbcdda33b3ce64c134d3e27916688ab37b",
"format": 1
},
{
"name": "plugins/modules/sap_control_exec.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "28c36eb33e6f2753019d3f4ade67f62de67dc612373fb6cdf41b0347c1737e1d",
"format": 1
},
{
"name": "plugins/modules/sap_hdbsql.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "21686c309fcdcc1be98f802112ade272e136875f6143ab8844495142ea3b58d2",
"format": 1
},
{
"name": "plugins/modules/sap_pyrfc.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "dbf07502b4466dc67e2afaf02e9dfd0b01441337e4ac7b29df473c6e5e31ed4c",
"format": 1
},
{
"name": "plugins/modules/sap_snote.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "598913d72aece5f981b343789704f799daa0be0fee21b6b6865c5721c4730bce",
"format": 1
},
{
"name": "plugins/modules/sap_system_facts.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "4dbf86ef447a3a021c832addcb2c001521d837ec1e13c4d4f3725b1be046a514",
"format": 1
},
{
"name": "plugins/modules/sap_task_list_execute.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "a6a6f58647718e88edf7d7bb852053b4cf297ec8afc0fd330a3e9792378cb420",
"format": 1
},
{
"name": "plugins/modules/sap_user.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "3ed1c980af0df1b349171ecb830a3596d44f7875094c7b1db9356635fae56cd4",
"format": 1
},
{
"name": "plugins/modules/sapcar_extract.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "9e96b03379bdd6c96b7246753233758703bfe383b3f935d56d14f84ddfdb5ead",
"format": 1
},
{
"name": "tests",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "tests/integration",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "tests/integration/targets",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "tests/integration/targets/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "tests/integration/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "tests/sanity",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "tests/sanity/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "tests/sanity/ignore-2.10.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "d3912e2808d50e5b6d67317db83cd5ab3b253d6a08aabcdb08c895a72f0ae163",
"format": 1
},
{
"name": "tests/sanity/ignore-2.11.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "d3912e2808d50e5b6d67317db83cd5ab3b253d6a08aabcdb08c895a72f0ae163",
"format": 1
},
{
"name": "tests/sanity/ignore-2.12.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "4b96e6fd9dd5ffdf1d5a0860ea5407249eecc1fcd4505693e040ef70ba6b2d97",
"format": 1
},
{
"name": "tests/sanity/ignore-2.13.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "4b96e6fd9dd5ffdf1d5a0860ea5407249eecc1fcd4505693e040ef70ba6b2d97",
"format": 1
},
{
"name": "tests/sanity/ignore-2.14.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "4b96e6fd9dd5ffdf1d5a0860ea5407249eecc1fcd4505693e040ef70ba6b2d97",
"format": 1
},
{
"name": "tests/sanity/ignore-2.15.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "4b96e6fd9dd5ffdf1d5a0860ea5407249eecc1fcd4505693e040ef70ba6b2d97",
"format": 1
},
{
"name": "tests/sanity/ignore-2.9.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "d3912e2808d50e5b6d67317db83cd5ab3b253d6a08aabcdb08c895a72f0ae163",
"format": 1
},
{
"name": "tests/unit",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "tests/unit/compat",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "tests/unit/compat/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "tests/unit/compat/builtins.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "0ca4cac919e166b25e601e11acb01f6957dddd574ff0a62569cb994a5ecb63e1",
"format": 1
},
{
"name": "tests/unit/compat/mock.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "0af958450cf6de3fbafe94b1111eae8ba5a8dbe1d785ffbb9df81f26e4946d99",
"format": 1
},
{
"name": "tests/unit/compat/unittest.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "5401a046e5ce71fa19b6d905abd0f9bdf816c0c635f7bdda6730b3ef06e67096",
"format": 1
},
{
"name": "tests/unit/mock",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "tests/unit/mock/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "tests/unit/mock/loader.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "3452ac615f89c99a76d1df4ab1ad84d1aff546e5b5fde18034a241239690d05a",
"format": 1
},
{
"name": "tests/unit/mock/path.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "f048a12629a6297a676ea56529ecf766cff30bcaa873c6659ac5b7f6e29472b1",
"format": 1
},
{
"name": "tests/unit/mock/procenv.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e79b2fe520af92318da175c231296e16bf047842a93b1bfa4e1a5afc453baa03",
"format": 1
},
{
"name": "tests/unit/mock/vault_helper.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "0562db7b9972e6378701e3492c623e5f881732c4792e096032b72c2e54d22298",
"format": 1
},
{
"name": "tests/unit/mock/yaml_helper.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "cd95a4807e52e9123a8d40132a5f52b75cbc1496e1a32b104b2655bf631cfee4",
"format": 1
},
{
"name": "tests/unit/plugins",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "tests/unit/plugins/modules",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
"name": "tests/unit/plugins/modules/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_sap_company.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "41198ca748183d2c0539e35332b5649e55a12010a8ce5b0abe77cfd6ec0611c4",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_sap_control_exec.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "10881167f5b5993f99b21eefc7984d91b7ed1104dd61b69a3f4b461c4129264a",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_sap_hdbsql.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "dc0de20c35c780cb80163b0de97fd5887068f1abde7f70037ffa7606d2c9a5fb",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_sap_pyrfc.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "b96dd15fee5fd6ff51e84fd428a767d98b2698cd07d7789fd21ce134154f3029",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_sap_snote.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "f520fcc506ed73656432d0428d1de39c28e6207840c41c5aa735e4ebed72b247",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_sap_system_facts.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "f06591bc4afcbeaf90d54b35796980f325f1e8b544bbc448dcd86c731a59a69d",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_sap_task_list_execute.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "f8deb1ca8741c89271c26fb97f208b95dabac2e5f1d42cbfe763c43658ca29fd",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_sap_user.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "6e5cea8169a2a94a179f4b79760514377480d5c432debc215878eaa9ae05ce98",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_sapcar_extract.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "9993318f661724e663e5bb0c9cea1b96ffc8dc4379ac80b8b32475fb90bfc011",
"format": 1
},
{
"name": "tests/unit/plugins/modules/utils.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "da523bad72795557d39c3dbc0527ebeca4c4b27e1f5009c88c81add458ebdfd2",
"format": 1
},
{
"name": "tests/unit/plugins/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "tests/unit/__init__.py",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"format": 1
},
{
"name": "tests/unit/requirements.txt",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "57ca135f60a45a67968cae026a738c74d287bd1a69cf99b20e6c0b02ebc72b35",
"format": 1
},
{
"name": "CHANGELOG.rst",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "362f880f89812864b92195442accf889fa4d62a12bc71ac63cf97af6c8a73eee",
"format": 1
},
{
"name": "CODE_OF_CONDUCT.md",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "14ec928234a7ed52bf8b458d31e4862335111e477e4dbe7fb543686c24115140",
"format": 1
},
{
"name": "CONTRIBUTING.md",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "eec219dbc09446ed3e9938c8f66378621a0548b056426df33e3ea32e26bb4dc8",
"format": 1
},
{
"name": "LICENSE",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
"format": 1
},
{
"name": "MAINTAINERS",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "bc751d90416fb1652ef9a5d35d8f60f5e050bc9735eb2fe26378d1695fd1ac46",
"format": 1
},
{
"name": "MAINTAINING.md",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "2435665a6562d5f3841fff1631970f95f0466c498e949d2b8579ccc2a0b810ad",
"format": 1
},
{
"name": "README.md",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "feb05211584ede33d61c331bfe223f9bbb19579cb73068ca1624c17bde8e9069",
"format": 1
},
{
"name": "REVIEW_CHECKLIST.md",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "91ad4aff2cc14b98f81fbe2d90609c5a69ed96b6d836387a9c697c1112e603c0",
"format": 1
},
{
"name": "codecov.yml",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "9745c317e22e0efb4d13c8240bfd094dad77784004d3564310209b8fc037ed5a",
"format": 1
}
],
"format": 1
}

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -0,0 +1,2 @@
rainerleber
rkpobe

View File

@@ -0,0 +1,3 @@
# Maintaining this collection
Refer to the [Maintainer guidelines](https://github.com/ansible/community-docs/blob/main/maintaining.rst).

View File

@@ -0,0 +1,32 @@
{
"collection_info": {
"namespace": "community",
"name": "sap_libs",
"version": "1.4.0",
"authors": [
"Rainer Leber (github.com/rainerleber)",
"Robert Kraemer (github.com/rkpobe)",
"Sean Freeman (github.com/sean-freeman)"
],
"readme": "README.md",
"tags": [
"sap"
],
"description": "SAP Module community collection for Ansible",
"license": [],
"license_file": "LICENSE",
"dependencies": {},
"repository": "https://github.com/sap-linuxlab/community.sap_libs",
"documentation": "https://github.com/sap-linuxlab/community.sap_libs",
"homepage": "https://github.com/sap-linuxlab/community.sap_libs",
"issues": "https://github.com/sap-linuxlab/community.sap_libs"
},
"file_manifest_file": {
"name": "FILES.json",
"ftype": "file",
"chksum_type": "sha256",
"chksum_sha256": "f5e3ebf25e7614316525747cddb7209fdc2d2f1bc7ea0e8cfe49b48adb68780d",
"format": 1
},
"format": 1
}

View File

@@ -0,0 +1,182 @@
# Community SAP_LIBS Collection
This repository contains the community.sap_libs Ansible Collection. The collection includes modules and plugins supported by the Ansible SAP community to help SAP landscape management.
**This collection is migrated from ansbile-collections/community.sap to sap-linuxlab/community.sap_libs.**
# SAP Module Collection for Ansible
<!-- Add CI and code coverage badges here. Samples included below. -->
[![CI](https://github.com/sap-linuxlab/community.sap_libs/workflows/CI/badge.svg)](https://github.com/sap-linuxlab/community.sap_libs/actions) [![Codecov](https://img.shields.io/codecov/c/github/sap-linuxlab/community.sap_libs)](https://codecov.io/gh/sap-linuxlab/community.sap_libs)
<!-- Describe the collection and why a user would want to use it. What does the collection do? -->
## Code of Conduct
We follow the [Ansible Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html) in all our interactions within this project.
If you encounter abusive behavior, please refer to the [policy violations](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html#policy-violations) section of the Code for information on how to raise a complaint.
## Communication
<!--List available communication channels. In addition to channels specific to your collection, we also recommend to use the following ones.-->
We announce releases and important changes through Ansible's [The Bullhorn newsletter](https://github.com/ansible/community/wiki/News#the-bullhorn). Be sure you are [subscribed](https://eepurl.com/gZmiEP).
Join us in the `#ansible` (general use questions and support), `#ansible-community` (community and collection development questions), and other [Matrix/LiberaChat IRC channels](https://docs.ansible.com/ansible/devel/community/communication.html#real-time-chat).
We take part in the global quarterly [Ansible Contributor Summit](https://github.com/ansible/community/wiki/Contributor-Summit) virtually or in-person. Track [The Bullhorn newsletter](https://eepurl.com/gZmiEP) and join us.
For more information about communication, refer to the [Ansible Communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
## Contributing to this collection
<!--Describe how the community can contribute to your collection. At a minimum, fill up and include the CONTRIBUTING.md file containing how and where users can create issues to report problems or request features for this collection. List contribution requirements, including preferred workflows and necessary testing, so you can benefit from community PRs. If you are following general Ansible contributor guidelines, you can link to - [Ansible Community Guide](https://docs.ansible.com/ansible/devel/community/index.html). List the current maintainers (contributors with write or higher access to the repository). The following can be included:-->
The content of this collection is made by people like you, a community of individuals collaborating on making the world better through developing automation software.
We are actively accepting new contributors.
Any kind of contribution is very welcome.
You don't know how to start? Refer to our [contribution guide](CONTRIBUTING.md)!
We use the following guidelines:
* [CONTRIBUTING.md](CONTRIBUTING.md)
* [REVIEW_CHECKLIST.md](REVIEW_CHECKLIST.md)
* [Ansible Community Guide](https://docs.ansible.com/ansible/latest/community/index.html)
* [Ansible Development Guide](https://docs.ansible.com/ansible/devel/dev_guide/index.html)
* [Ansible Collection Development Guide](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#contributing-to-collections)
## Collection maintenance
The current maintainers are listed in the [MAINTAINERS](MAINTAINERS) file. If you have questions or need help, feel free to mention them in the proposals.
To learn how to maintain / become a maintainer of this collection, refer to the [Maintainer guidelines](MAINTAINING.md).
## Governance
<!--Describe how the collection is governed. Here can be the following text:-->
The process of decision making in this collection is based on discussing and finding consensus among participants.
Every voice is important. If you have something on your mind, create an issue or dedicated discussion and let's discuss it!
## Tested with Ansible and the following Python versions
Tested Ansible versions:
- 2.9
- 2.10
- 2.11
- 2.12
- 2.13
- 2.14
- devel
Tested Python versions:
- 2.6
- 2.7
- 3.5
- 3.6
- 3.7
- 3.8
- 3.9
- 3.10
- 3.11
Due to SAP licensing and hardware requirements, integration tests are momentarily not feasible.
The modules are tested manually against SAP systems until we found a solution or have some
modules where we are able to execute integration test we decided to disable these tests.
## External requirements
For some modules the below requirements are needed on the host that executes a module.
- pyrfc >= 2.4.0
- SAPCAR
- SAPCONTROL
### Supported connections
<!-- Optional. If your collection supports only specific connection types (such as HTTPAPI, netconf, or others), list them here. -->
## Included content
- **Modules**:
- [sap_hdbsql](https://docs.ansible.com/ansible/latest/collections/community/sap_libs/sap_hdbsql.html)
- [sap_task_list_execute](https://docs.ansible.com/ansible/latest/collections/community/sap_libs/sap_task_list_execute.html)
- [sapcar_extract](https://docs.ansible.com/ansible/latest/collections/community/sap_libs/sapcar_extract.html)
- [sap_company](https://docs.ansible.com/ansible/latest/collections/community/sap_libs/sap_company.html)
- [sap_snote](https://docs.ansible.com/ansible/latest/collections/community/sap_libs/sap_snote.html)
- [sap_user](https://docs.ansible.com/ansible/latest/collections/community/sap_libs/sap_user.html)
- [sap_system_facts](https://docs.ansible.com/ansible/latest/collections/community/sap_libs/sap_system_facts.html)
- [sap_control_exec](https://docs.ansible.com/ansible/latest/collections/community/sap_libs/sap_control_exec.html)
## Using this collection
<!--Include some quick examples that cover the most common use cases for your collection content. It can include the following examples of installation and upgrade (change NAMESPACE.COLLECTION_NAME correspondingly):-->
### Installing the Collection from Ansible Galaxy
Before using this collection, you need to install it with the Ansible Galaxy command-line tool:
```bash
ansible-galaxy collection install community.sap_libs
```
You can also include it in a `requirements.yml` file and install it with `ansible-galaxy collection install -r requirements.yml`, using the format:
```yaml
---
collections:
- name: community.sap_libs
```
Note that if you install the collection from Ansible Galaxy, it will not be upgraded automatically when you upgrade the `ansible` package. To upgrade the collection to the latest available version, run the following command:
```bash
ansible-galaxy collection install community.sap_libs --upgrade
```
You can also install a specific version of the collection, for example, if you need to downgrade when something is broken in the latest version (please report an issue in this repository). Use the following syntax to install version `1.0.0`:
```bash
ansible-galaxy collection install community.sap_libs:==1.0.0
```
See [Ansible Using collections](https://docs.ansible.com/ansible/devel/user_guide/collections_using.html) for more details.
## Release notes
See the [changelog](https://github.com/sap-linuxlab/community.sap_libs/tree/main/CHANGELOG.rst).
## Releasing, Versioning and Deprecation
This collection follows Semantic Versioning. More details on versioning can be found in the Ansible docs.
We plan to regularly release new minor or bugfix versions once new features or bugfixes have been implemented.
Releasing the current major version happens from the main branch. We will create a stable-1 branch for 1.x.y versions once we start working on a 2.0.0 release, to allow backporting bugfixes and features from the 2.0.0 branch (main) to stable-1.
For reference have a look at the issue [Releasing, Versioning and Deprecation](https://github.com/sap-linuxlab/community.sap_libs/issues/1).
## Roadmap
Please have a look at the project board.
## More information
<!-- List out where the user can find additional information, such as working group meeting times, slack/IRC channels, or documentation for the product this collection automates. At a minimum, link to: -->
- [Ansible Collection overview](https://github.com/ansible-collections/overview)
- [Ansible User guide](https://docs.ansible.com/ansible/devel/user_guide/index.html)
- [Ansible Developer guide](https://docs.ansible.com/ansible/devel/dev_guide/index.html)
- [Ansible Collections Checklist](https://github.com/ansible-collections/overview/blob/master/collection_requirements.rst)
- [Ansible Community Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html)
- [The Bullhorn (the Ansible Contributor newsletter)](https://us19.campaign-archive.com/home/?u=56d874e027110e35dea0e03c1&id=d6635f5420)
- [News for Maintainers](https://github.com/ansible-collections/news-for-maintainers)
## Licensing
<!-- Include the appropriate license information here and a pointer to the full licensing details. If the collection contains modules migrated from the ansible/ansible repo, you must use the same license that existed in the ansible/ansible repo. See the GNU license example below. -->
Apache License, Version 2.0
See [LICENSE](http://www.apache.org/licenses/LICENSE-2.0) to see the full text.

View File

@@ -0,0 +1,3 @@
# Review Checklist
Refer to the [Collection review checklist](https://github.com/ansible/community-docs/blob/main/review_checklist.rst).

View File

@@ -0,0 +1,97 @@
ancestor: null
releases:
1.0.0:
changes:
release_summary: This is the minor release of the ``community.sap`` collection.
It is the initial relase for the ``community.sap`` collection
fragments:
- 1.0.0.yml
modules:
- description: Ansible Module to execute SQL on SAP HANA
name: hana_query
namespace: database.saphana
- description: This module will manage a company entities in a SAP S4HANA environment
name: sap_company
namespace: identity
- description: This module will upload and (de)implements C(SNOTES) in a SAP S4HANA
environment.
name: sap_snote
namespace: system
- description: Gathers SAP facts in a host
name: sap_system_facts
namespace: system
- description: Perform SAP Task list execution
name: sap_task_list_execute
namespace: system
- description: This module will manage a user entities in a SAP S4/HANA environment
name: sap_user
namespace: identity
- description: Manages SAP SAPCAR archives
name: sapcar_extract
namespace: files
1.1.0:
changes:
release_summary: 'This is the minor release of the ``community.sap_libs`` collection.
This changelog contains all changes to the modules and plugins in this collection
that have been made after the previous release.'
fragments:
- 1.1.0.yml
modules:
- description: Ansible Module to execute SAPCONTROL
name: sapcontrol
namespace: system
release_date: '2022-05-17'
1.2.0:
changes:
bugfixes:
- syp_system_facts - fix a typo in the usage example which lead to an error
if it used as supposed.
release_summary: 'This is the minor release of the ``community.sap_libs`` collection.
This changelog contains all changes to the modules and plugins in this collection
that have been made after the previous release.'
fragments:
- 1.2.0.yml
- 11-sap_system_facts_fix_typo.yml
modules:
- description: Ansible Module for use of SAP PyRFC to execute SAP RFCs (Remote
Function Calls) to SAP remote-enabled function modules
name: sap_pyrfc
namespace: ''
release_date: '2022-07-18'
1.3.0:
changes:
minor_changes:
- License requirements are updated.
- The modules purposes are described clearer.
- The namespaces of the modules are removed to provide a flatter design.
- hana_query - module is moved to sap_hdbsql.
- sapcontrol - module is moved to sap_control_exec to have a clearer separation
to other roles and references.
release_summary: This is the 1.3.0 minor release of the ``community.sap_libs``
collection. This changelog contains all changes to the modules and plugins
in this collection that have been made after the previous release.
fragments:
- 1.3.0.yml
release_date: '2022-09-09'
1.4.0:
changes:
bugfixes:
- fix a bug where some commands produces no output which cause to crash the
module.
- modules - fix a "variable used before assignment" that cannot be reached but
causes sanity test failures.
release_summary: 'This is the 1.3.0 minor release of the ``community.sap_libs``
collection.
This changelog contains all changes to the modules and plugins in this collection
that have been made after the previous release.'
fragments:
- 0020-fix_sap_control_exec.yml
- 1.4.0.yml
- 22-use-before-assignment.yml
release_date: '2022-12-05'

View File

@@ -0,0 +1,30 @@
changelog_filename_template: ../CHANGELOG.rst
changelog_filename_version_depth: 0
changes_file: changelog.yaml
changes_format: combined
keep_fragments: false
mention_ancestor: true
flatmap: true
new_plugins_after_name: removed_features
notesdir: fragments
prelude_section_name: release_summary
prelude_section_title: Release Summary
sections:
- - major_changes
- Major Changes
- - minor_changes
- Minor Changes
- - breaking_changes
- Breaking Changes / Porting Guide
- - deprecated_features
- Deprecated Features
- - removed_features
- Removed Features (previously deprecated)
- - security_fixes
- Security Fixes
- - bugfixes
- Bugfixes
- - known_issues
- Known Issues
title: Community SAP
trivial_section_name: trivial

View File

@@ -0,0 +1,2 @@
fixes:
- "/sap-linuxlab/community/sap_libs/::"

View File

@@ -0,0 +1,15 @@
---
requires_ansible: ">=2.9.10"
plugin_routing:
modules:
hana_query:
redirect: community.sap_libs.sap_hdbsql
deprecation:
removal_version: 2.0.0
warning_text: Use community.sap_libs.sap_hdbsql instead.
sapcontrol:
redirect: community.sap_libs.sap_control_exec
deprecation:
removal_version: 2.0.0
warning_text: Use community.sap_libs.sap_control_exec instead.

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2022, Sean Freeman ,
# Rainer Leber <rainerleber@gmail.com> <rainer.leber@sva.de>
# 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
from ansible.module_utils.basic import missing_required_lib
import traceback
PYRFC_LIBRARY_IMPORT_ERROR = None
try:
import pyrfc
except ImportError:
PYRFC_LIBRARY_IMPORT_ERROR = traceback.format_exc()
HAS_PYRFC_LIBRARY = False
else:
HAS_PYRFC_LIBRARY = True
def get_connection(module, conn_params):
if not HAS_PYRFC_LIBRARY:
module.fail_json(msg=missing_required_lib(
"pyrfc"), exception=PYRFC_LIBRARY_IMPORT_ERROR)
module.warn('Connecting ... %s' % conn_params['ashost'])
if "saprouter" in conn_params:
module.warn("...via SAPRouter to SAP System")
elif "gwhost" in conn_params:
module.warn("...via Gateway to SAP System")
else:
module.warn("...direct to SAP System")
conn = pyrfc.Connection(**conn_params)
module.warn("Verifying connection is open/alive: %s" % conn.alive)
return conn

View File

@@ -0,0 +1,271 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright: (c) 2022, Sean Freeman ,
# Rainer Leber <rainerleber@gmail.com> <rainer.leber@sva.de>
# 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
from ansible.module_utils.basic import missing_required_lib
import traceback
import sys
import os
BS4_LIBRARY_IMPORT_ERROR = None
try:
from bs4 import BeautifulSoup
except ImportError:
BS4_LIBRARY_IMPORT_ERROR = traceback.format_exc()
HAS_BS4_LIBRARY = False
else:
HAS_BS4_LIBRARY = True
LXML_LIBRARY_IMPORT_ERROR = None
try:
from lxml import etree
except ImportError:
LXML_LIBRARY_IMPORT_ERROR = traceback.format_exc()
HAS_LXML_LIBRARY = False
else:
HAS_LXML_LIBRARY = True
def debug_bs4(module):
# Diagnose XML file parsing errors in Beautiful Soup
# https://stackoverflow.com/questions/56942892/cannot-parse-iso-8859-15-encoded-xml-with-bs4/56947172#56947172
if not HAS_BS4_LIBRARY:
module.fail_json(msg=missing_required_lib(
"bs4"), exception=BS4_LIBRARY_IMPORT_ERROR)
from bs4.diagnose import diagnose
with open('control.xml', 'rb') as f:
diagnose(f)
# SWPM2 control.xml conversion to utf8
def control_xml_utf8(filepath, module):
if not HAS_LXML_LIBRARY:
module.fail_json(msg=missing_required_lib(
"lxml"), exception=LXML_LIBRARY_IMPORT_ERROR)
source = filepath + "/control.xml"
# Convert control.xml from iso-8859-1 to UTF-8, so it can be used with Beautiful Soup lxml-xml parser
# https://stackoverflow.com/questions/64629600/how-can-you-convert-a-xml-iso-8859-1-to-utf-8-using-python-3-7-7/64634454#64634454
with open(source, 'rb') as source:
parser = etree.XMLParser(encoding="iso-8859-1", strip_cdata=False)
root = etree.parse(source, parser)
string = etree.tostring(root, xml_declaration=True, encoding="UTF-8",
pretty_print=True).decode('utf8').encode('iso-8859-1')
# string1 = etree.tostring(root, xml_declaration=True, encoding="UTF-8",
# pretty_print=True).decode('utf8').encode('utf-8').strip()
with open('control_utf8.xml', 'wb') as target:
target.write(string)
# SWPM2 Component and Parameters extract all as CSV
def control_xml_to_csv(filepath, module):
if not HAS_BS4_LIBRARY:
module.fail_json(msg=missing_required_lib(
"bs4"), exception=BS4_LIBRARY_IMPORT_ERROR)
infile = open(filepath + "/control_utf8.xml", "r")
contents = infile.read()
soup = BeautifulSoup(markup=contents, features='lxml-xml')
space = soup.find('components')
component_list = space.findChildren("component", recursive=False)
csv_output = open('control_output.csv', 'w')
csv_header = '"' + 'Component Name' + '","' + 'Component Display Name' + '","' + 'Parameter Name' + '","' + 'Parameter Inifile Key' + \
'","' + 'Parameter Access' + '","' + 'Parameter Encode' + '","' + \
'Parameter Default Value' + '","' + 'Parameter Inifile description' + '"'
csv_output.write("%s\n" % csv_header)
for component in component_list:
for parameter in component.findChildren("parameter"):
component_key = parameter.findParent("component")
component_key_name_text = component_key["name"]
for child in component_key.findChildren("display-name"):
component_key_display_name_text = child.get_text().replace('\n', '')
component_parameter_key_name = parameter["name"]
component_parameter_key_inifile_name = parameter.get(
"defval-for-inifile-generation", "")
component_parameter_key_access = parameter.get("access", "")
component_parameter_key_encode = parameter.get("encode", "")
component_parameter_key_defval = parameter.get("defval", "")
component_parameter_contents_doclong_text = parameter.get_text().replace('\n', '')
component_parameter_contents_doclong_text_quote_replacement = component_parameter_contents_doclong_text.replace(
'"', '\'')
csv_string = '"' + component_key_name_text + '","' + component_key_display_name_text + '","' + \
component_parameter_key_name + '","' + component_parameter_key_inifile_name + '","' + \
component_parameter_key_access + '","' + component_parameter_key_encode + '","' + \
component_parameter_key_defval + '","' + \
component_parameter_contents_doclong_text_quote_replacement + '"'
csv_output.write("%s\n" % csv_string)
csv_output.close()
# SWPM2 Component and Parameters extract all and generate template inifile.params
def control_xml_to_inifile_params(filepath, module):
if not HAS_BS4_LIBRARY:
module.fail_json(msg=missing_required_lib(
"bs4"), exception=BS4_LIBRARY_IMPORT_ERROR)
infile = open(filepath + "/control_utf8.xml", "r")
contents = infile.read()
soup = BeautifulSoup(markup=contents, features='lxml-xml')
space = soup.find('components')
component_list = space.findChildren("component", recursive=False)
inifile_output = open('generated_inifile_params', 'w')
inifile_params_header = """############
# SWPM Unattended Parameters inifile.params generated export
#
#
# Export of all SWPM Component and the SWPM Unattended Parameters. Not all components have SWPM Unattended Parameters.
#
# All parameters are commented-out, each hash # before the parameter is removed to activate the parameter.
# When running SWPM in Unattended Mode, the activated parameters will create a new SWPM file in the sapinst directory.
# If any parameter is marked as 'encode', the plaintext value will be coverted to DES hash
# for this parameter in the new SWPM file (in the sapinst directory).
#
# An inifile.params is otherwise obtained after running SWPM as GUI or Unattended install,
# and will be generated for a specific Product ID (such as 'NW_ABAP_OneHost:S4HANA1809.CORE.HDB.CP').
############
############
# MANUAL
############
# The folder containing all archives that have been downloaded from http://support.sap.com/swdc and are supposed to be used in this procedure
# archives.downloadBasket =
"""
inifile_output.write(inifile_params_header)
for component in component_list:
component_key_name_text = component["name"]
component_key_display_name = component.find("display-name")
if component_key_display_name is not None:
component_key_display_name_text = component_key_display_name.get_text()
inifile_output.write("\n\n\n\n############\n# Component: %s\n# Component Display Name: %s\n############\n" % (
component_key_name_text, component_key_display_name_text))
for parameter in component.findChildren("parameter"):
# component_key=parameter.findParent("component")
component_parameter_key_encode = parameter.get("encode", None)
component_parameter_key_inifile_name = parameter.get(
"defval-for-inifile-generation", None)
component_parameter_key_defval = parameter.get("defval", "")
component_parameter_contents_doclong_text = parameter.get_text().replace('\n', '')
# component_parameter_contents_doclong_text_quote_replacement=component_parameter_contents_doclong_text.replace('"','\'')
if component_parameter_key_inifile_name is not None:
inifile_output.write("\n# %s" % (
component_parameter_contents_doclong_text))
if component_parameter_key_encode == "true":
inifile_output.write(
"\n# Encoded parameter. Plaintext values will be coverted to DES hash")
inifile_output.write("\n# %s = %s\n" % (
component_parameter_key_inifile_name, component_parameter_key_defval))
inifile_output.close()
# SWPM2 product.catalog conversion to utf8
def product_catalog_xml_utf8(filepath, module):
if not HAS_LXML_LIBRARY:
module.fail_json(msg=missing_required_lib(
"lxml"), exception=LXML_LIBRARY_IMPORT_ERROR)
source = filepath + "/product.catalog"
# Convert control.xml from iso-8859-1 to UTF-8, so it can be used with Beautiful Soup lxml-xml parser
# https://stackoverflow.com/questions/64629600/how-can-you-convert-a-xml-iso-8859-1-to-utf-8-using-python-3-7-7/64634454#64634454
with open(source, 'rb') as source:
parser = etree.XMLParser(encoding="iso-8859-1", strip_cdata=False)
root = etree.parse(source, parser)
string = etree.tostring(root, xml_declaration=True, encoding="UTF-8",
pretty_print=True).decode('utf8').encode('iso-8859-1')
with open('product_catalog_utf8.xml', 'wb') as target:
target.write(string)
# SWPM2 Product Catalog entries to CSV
# Each Product Catalog entry is part of a components group, which may have attributes:
# output-dir, control-file, product-dir (link to SWPM directory of param file etc)
# Attributes possible for each entry = control-file, db, id, name, os, os-type, output-dir,
# ppms-component, ppms-component-release, product, product-dir, release, table
def product_catalog_xml_to_csv(filepath, module):
if not HAS_BS4_LIBRARY:
module.fail_json(msg=missing_required_lib(
"bs4"), exception=BS4_LIBRARY_IMPORT_ERROR)
infile = open(filepath + "/product_catalog_utf8.xml", "r")
contents = infile.read()
soup = BeautifulSoup(markup=contents, features='lxml-xml')
space = soup.find_all('component')
csv_output = open('product_catalog_output.csv', 'w')
csv_header = '"' + 'Product Catalog Component Name' + '","' + 'Product Catalog Component ID' + '","' + 'Product Catalog Component Table' + '","' + \
'Product Catalog Component Output Dir' + '","' + 'Product Catalog Component Display Name' + \
'","' + 'Product Catalog Component UserInfo' + '"'
csv_output.write("%s\n" % csv_header)
for component in space:
component_name = component.get("name", "")
component_id = component.get("id", "")
component_table = component.get("table", "")
component_output_dir = component.get("output-dir", "")
for displayname in component.findChildren("display-name"):
component_displayname = displayname.get_text().strip()
for userinfo in component.findChildren("user-info"):
html_raw = userinfo.get_text().strip()
html_parsed = BeautifulSoup(html_raw, 'html.parser')
component_userinfo = html_parsed.get_text().replace('"', '\'')
csv_string = '"' + component_name + '","' + component_id + '","' + component_table + '","' + \
component_output_dir + '","' + component_displayname + \
'","' + component_userinfo + '"'
csv_output.write("%s\n" % csv_string)
csv_output.close()
# Get arguments passed to Python script session
# Define path to control.xml, else assume in /tmp directory
if len(sys.argv) > 1:
control_xml_path = sys.argv[1]
else:
control_xml_path = "/tmp"
if control_xml_path == "":
control_xml_path = os.getcwd()
if os.path.exists(control_xml_path + '/control.xml'):
control_xml_utf8(control_xml_path, '')
control_xml_to_csv(control_xml_path, '')
control_xml_to_inifile_params(control_xml_path, '')

View File

@@ -0,0 +1,335 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Rainer Leber <rainerleber@gmail.com> <rainer.leber@sva.de>
# 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
DOCUMENTATION = r'''
---
module: sap_company
short_description: This module will manage a company entities in a SAP S4HANA environment
version_added: "1.0.0"
description:
- The M(community.sap_libs.sap_user) module depends on C(pyrfc) Python library (version 2.4.0 and upwards).
Depending on distribution you are using, you may need to install additional packages to
have these available.
- This module will use the company BAPIs C(BAPI_COMPANY_CLONE) and C(BAPI_COMPANY_DELETE) to manage company entities.
options:
state:
description:
- The decision what to do with the company.
default: 'present'
choices:
- 'present'
- 'absent'
required: false
type: str
conn_username:
description: The required username for the SAP system.
required: true
type: str
conn_password:
description: The required password for the SAP system.
required: true
type: str
host:
description: The required host for the SAP system. Can be either an FQDN or IP Address.
required: true
type: str
sysnr:
description:
- The system number of the SAP system.
- You must quote the value to ensure retaining the leading zeros.
required: false
default: '01'
type: str
client:
description:
- The client number to connect to.
- You must quote the value to ensure retaining the leading zeros.
required: false
default: '000'
type: str
company_id:
description: The company id.
required: true
type: str
name:
description: The company name.
required: false
type: str
name_2:
description: Additional company name.
required: false
type: str
country:
description: The country code for the company. For example, C('DE').
required: false
type: str
time_zone:
description: The timezone.
required: false
type: str
city:
description: The city where the company is located.
required: false
type: str
post_code:
description: The post code from the city.
required: false
type: str
street:
description: Street where the company is located.
required: false
type: str
street_no:
description: Street number.
required: false
type: str
e_mail:
description: General E-Mail address.
required: false
type: str
requirements:
- pyrfc >= 2.4.0
author:
- Rainer Leber (@rainerleber)
notes:
- Does not support C(check_mode).
'''
EXAMPLES = r'''
- name: Create SAP Company
community.sap_libs.sap_company:
conn_username: 'DDIC'
conn_password: 'HECtna2021#'
host: 100.0.201.20
sysnr: '01'
client: '000'
state: present
company_id: "Comp_ID"
name: "Test_comp"
name_2: "LTD"
country: "DE"
time_zone: "UTC"
city: "City"
post_code: "12345"
street: "test_street"
street_no: "1"
e_mail: "test@test.de"
# pass in a message and have changed true
- name: Delete SAP Company
community.sap_libs.sap_company:
conn_username: 'DDIC'
conn_password: 'HECtna2021#'
host: 100.0.201.20
sysnr: '01'
client: '000'
state: absent
company_id: "Comp_ID"
name: "Test_comp"
name_2: "LTD"
country: "DE"
time_zone: "UTC"
city: "City"
post_code: "12345"
street: "test_street"
street_no: "1"
e_mail: "test@test.de"
'''
RETURN = r'''
# These are examples of possible return values, and in general should use other names for return values.
msg:
description: A small execution description.
type: str
returned: always
sample: 'Company address COMP_ID created'
out:
description: A complete description of the executed tasks. If this is available.
type: list
elements: dict
returned: always
sample: '{
"RETURN": [
{
"FIELD": "",
"ID": "01",
"LOG_MSG_NO": "000000",
"LOG_NO": "",
"MESSAGE": "Company address COMP_ID created",
"MESSAGE_V1": "COMP_ID",
"MESSAGE_V2": "",
"MESSAGE_V3": "",
"MESSAGE_V4": "",
"NUMBER": "078",
"PARAMETER": "",
"ROW": 0,
"SYSTEM": "",
"TYPE": "S"
}
]
}
}'
'''
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
import traceback
try:
from pyrfc import Connection
except ImportError:
HAS_PYRFC_LIBRARY = False
ANOTHER_LIBRARY_IMPORT_ERROR = traceback.format_exc()
else:
ANOTHER_LIBRARY_IMPORT_ERROR = None
HAS_PYRFC_LIBRARY = True
def call_rfc_method(connection, method_name, kwargs):
# PyRFC call function
return connection.call(method_name, **kwargs)
def build_company_params(name, name_2, country, time_zone, city, post_code, street, street_no, e_mail):
# Creates RFC parameters for creating organizations
# define dicts in batch
params = dict()
# define company name
params['NAME'] = name
params['NAME_2'] = name_2
# define location
params['COUNTRY'] = country
params['TIME_ZONE'] = time_zone
params['CITY'] = city
params['POSTL_COD1'] = post_code
params['STREET'] = street
params['STREET_NO'] = street_no
# define communication
params['E_MAIL'] = e_mail
# return dict
return params
def return_analysis(raw):
change = False
failed = False
msg = raw['RETURN'][0]['MESSAGE']
for state in raw['RETURN']:
if state['TYPE'] == "E":
if state['NUMBER'] == '081':
change = False
else:
failed = True
if state['TYPE'] == "S":
if state['NUMBER'] != '079':
change = True
else:
msg = "No changes where made."
return [{"change": change}, {"failed": failed}, {"msg": msg}]
def run_module():
module = AnsibleModule(
argument_spec=dict(
state=dict(default='present', choices=['absent', 'present']),
conn_username=dict(type='str', required=True),
conn_password=dict(type='str', required=True, no_log=True),
host=dict(type='str', required=True),
sysnr=dict(type='str', default="01"),
client=dict(type='str', default="000"),
company_id=dict(type='str', required=True),
name=dict(type='str', required=False),
name_2=dict(type='str', required=False),
country=dict(type='str', required=False),
time_zone=dict(type='str', required=False),
city=dict(type='str', required=False),
post_code=dict(type='str', required=False),
street=dict(type='str', required=False),
street_no=dict(type='str', required=False),
e_mail=dict(type='str', required=False),
),
supports_check_mode=False,
)
result = dict(changed=False, msg='', out={})
raw = ""
params = module.params
state = params['state']
conn_username = (params['conn_username']).upper()
conn_password = params['conn_password']
host = params['host']
sysnr = params['sysnr']
client = params['client']
company_id = (params['company_id']).upper()
name = params['name']
name_2 = params['name_2']
country = params['country']
time_zone = params['time_zone']
city = params['city']
post_code = params['post_code']
street = params['street']
street_no = params['street_no']
e_mail = params['e_mail']
if not HAS_PYRFC_LIBRARY:
module.fail_json(
msg=missing_required_lib('pyrfc'),
exception=ANOTHER_LIBRARY_IMPORT_ERROR)
# basic RFC connection with pyrfc
try:
conn = Connection(user=conn_username, passwd=conn_password, ashost=host, sysnr=sysnr, client=client)
except Exception as err:
result['error'] = str(err)
result['msg'] = 'Something went wrong connecting to the SAP system.'
module.fail_json(**result)
# build parameter dict of dict
company_params = build_company_params(name, name_2, country, time_zone, city, post_code, street, street_no, e_mail)
if state == "absent":
raw = call_rfc_method(conn, 'BAPI_COMPANY_DELETE', {'COMPANY': company_id})
if state == "present":
raw = call_rfc_method(conn, 'BAPI_COMPANY_CLONE',
{'METHOD': {'USMETHOD': 'COMPANY_CLONE'}, 'COMPANY': company_id, 'COMP_DATA': company_params})
analysed = return_analysis(raw)
result['out'] = raw
result['changed'] = analysed[0]['change']
result['msg'] = analysed[2]['msg']
if analysed[1]['failed']:
module.fail_json(**result)
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,401 @@
#!/usr/bin/python
# Copyright: (c) 2022, Rainer Leber rainerleber@gmail.com, rainer.leber@sva.de,
# Robert Kraemer @rkpobe, robert.kraemer@sva.de
# 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
DOCUMENTATION = r'''
---
module: sap_control_exec
short_description: Ansible Module to execute SAPCONTROL
version_added: "1.1.0"
description:
- Provides support for sapstartsrv formaly known as sapcontrol
- A complete information of all functions and the parameters can be found here
U(https://www.sap.com/documents/2016/09/0a40e60d-8b7c-0010-82c7-eda71af511fa.html)
options:
sysnr:
description:
- The system number of the instance.
required: false
type: str
port:
description:
- The port number of the sapstartsrv.
required: false
type: int
username:
description:
- The username to connect to the sapstartsrv.
required: false
type: str
password:
description:
- The password to connect to the sapstartsrv.
required: false
type: str
hostname:
description:
- The hostname to connect to the sapstartsrv.
- Could be an IP address, FQDN or hostname.
required: false
default: localhost
type: str
function:
description:
- The function to execute.
required: true
choices:
- Start
- Stop
- RestartInstance
- Shutdown
- InstanceStart
- GetProcessList
- Bootstrap
- InstanceStop
- StopService
- StartService
- RestartService
- ParameterValue
- GetStartProfile
- GetTraceFile
- GetAlertTree
- GetAlerts
- GetEnvironment
- GetVersionInfo
- GetQueueStatistic
- GetInstanceProperties
- ListDeveloperTraces
- ReadDeveloperTrace
- ListLogFiles
- ReadLogFile
- AnalyseLogFiles
- ConfigureLogFileList
- GetLogFileList
- CreateSnapshot
- ReadSnapshot
- ListSnapshots
- DeleteSnapshots
- GetAccessPointList
- GetProcessParameter
- SetProcessParameter
- SetProcessParameter2
- CheckParameter
- OSExecute
- SendSignal
- GetCallstack
- GetSystemInstanceList
- StartSystem
- StopSystem
- RestartSystem
- GetSystemUpdateList
- UpdateSystem
- UpdateSCSInstance
- CheckUpdateSystem
- AccessCheck
- GetSecNetworkId
- GetNetworkId
- RequestLogonFile
- UpdateSystemPKI
- UpdateInstancePSE
- StorePSE
- DeletePSE
- CheckPSE
- CreatePSECredential
- HACheckConfig
- HACheckFailoverConfig
- HAGetFailoverConfig
- HAFailoverToNode
- HASetMaintenanceMode
- HACheckMaintenanceMode
- ABAPReadSyslog
- ABAPReadRawSyslog
- ABAPGetWPTable
- ABAPGetComponentList
- ABAPCheckRFCDestinations
- ABAPGetSystemWPTable
- J2EEControlProcess
- J2EEControlCluster
- J2EEEnableDbgSession
- J2EEDisableDbgSession
- J2EEGetProcessList
- J2EEGetProcessList2
- J2EEGetThreadList
- J2EEGetThreadList2
- J2EEGetThreadCallStack
- J2EEGetThreadTaskStack
- J2EEGetSessionList
- J2EEGetCacheStatistic
- J2EEGetCacheStatistic2
- J2EEGetApplicationAliasList
- J2EEGetComponentList
- J2EEControlComponents
- J2EEGetWebSessionList
- J2EEGetWebSessionList2
- J2EEGetEJBSessionList
- J2EEGetRemoteObjectList
- J2EEGetVMGCHistory
- J2EEGetVMGCHistory2
- J2EEGetVMHeapInfo
- J2EEGetClusterMsgList
- J2EEGetSharedTableInfo
- ICMGetThreadList
- ICMGetConnectionList
- ICMGetProxyConnectionList
- ICMGetCacheEntries
- WebDispGetServerList
- WebDispGetGroupList
- WebDispGetVirtHostList
- WebDispGetUrlPrefixList
- EnqGetStatistic
- EnqGetLockTable
- EnqRemoveUserLocks
- StartWait
- StopWait
- WaitforStarted
- WaitforStopped
- RestartServiceWait
- WaitforServiceStarted
- CheckHostAgent
type: str
parameter:
description:
- The parameter to pass to the function.
required: false
type: str
force:
description:
- Forces the execution of the function C(Stop).
required: false
default: false
type: bool
author:
- Rainer Leber (@RainerLeber)
- Robert Kraemer (@rkpobe)
notes:
- Does not support C(check_mode).
'''
EXAMPLES = r"""
- name: GetProcessList with sysnr
community.sap_libs.sap_control_exec:
hostname: 192.168.8.15
sysnr: "01"
function: GetProcessList
- name: GetProcessList with custom port
community.sap_libs.sap_control_exec:
hostname: 192.168.8.15
function: GetProcessList
port: 50113
- name: ParameterValue
community.sap_libs.sap_control_exec:
hostname: 192.168.8.15
sysnr: "01"
username: hdbadm
password: test1234#
function: ParameterValue
parameter: ztta
"""
RETURN = r'''
msg:
description: Success-message with functionname.
type: str
returned: always
sample: 'Succesful execution of: GetProcessList'
out:
description: The full output of the required function.
type: list
elements: dict
returned: always
sample: [{
"item": [
{
"description": "MessageServer",
"dispstatus": "SAPControl-GREEN",
"elapsedtime": "412:30:50",
"name": "msg_server",
"pid": 70643,
"starttime": "2022 03 13 15:22:42",
"textstatus": "Running"
},
{
"description": "EnqueueServer",
"dispstatus": "SAPControl-GREEN",
"elapsedtime": "412:30:50",
"name": "enserver",
"pid": 70644,
"starttime": "2022 03 13 15:22:42",
"textstatus": "Running"
},
{
"description": "Gateway",
"dispstatus": "SAPControl-GREEN",
"elapsedtime": "412:30:50",
"name": "gwrd",
"pid": 70645,
"starttime": "2022 03 13 15:22:42",
"textstatus": "Running"
}
]
}]
'''
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
import traceback
try:
from suds.client import Client
from suds.sudsobject import asdict
except ImportError:
HAS_SUDS_LIBRARY = False
SUDS_LIBRARY_IMPORT_ERROR = traceback.format_exc()
else:
SUDS_LIBRARY_IMPORT_ERROR = None
HAS_SUDS_LIBRARY = True
def choices():
retlist = ["Start", "Stop", "RestartInstance", "Shutdown", "InstanceStart", 'GetProcessList',
'Bootstrap', 'InstanceStop', 'StopService', 'StartService', 'RestartService', 'ParameterValue',
'GetStartProfile', 'GetTraceFile', 'GetAlertTree', 'GetAlerts', 'GetEnvironment', 'GetVersionInfo',
'GetQueueStatistic', 'GetInstanceProperties', 'ListDeveloperTraces', 'ReadDeveloperTrace',
'ListLogFiles', 'ReadLogFile', 'AnalyseLogFiles', 'ConfigureLogFileList', 'GetLogFileList', 'CreateSnapshot', 'ReadSnapshot',
'ListSnapshots', 'DeleteSnapshots', 'GetAccessPointList', 'GetProcessParameter', 'SetProcessParameter',
'SetProcessParameter2', 'CheckParameter', 'OSExecute', 'SendSignal', 'GetCallstack', 'GetSystemInstanceList',
'StartSystem', 'StopSystem', 'RestartSystem', 'GetSystemUpdateList', 'UpdateSystem', 'UpdateSCSInstance',
'CheckUpdateSystem', 'AccessCheck', 'GetSecNetworkId', 'GetNetworkId', 'RequestLogonFile',
'UpdateSystemPKI', 'UpdateInstancePSE', 'StorePSE', 'DeletePSE', 'CheckPSE', 'CreatePSECredential',
'HACheckConfig', 'HACheckFailoverConfig', 'HAGetFailoverConfig', 'HAFailoverToNode',
'HASetMaintenanceMode', 'HACheckMaintenanceMode', 'ABAPReadSyslog', 'ABAPReadRawSyslog',
'ABAPGetWPTable', 'ABAPGetComponentList', 'ABAPCheckRFCDestinations',
'ABAPGetSystemWPTable', 'J2EEControlProcess', 'J2EEControlCluster', 'J2EEEnableDbgSession',
'J2EEDisableDbgSession', 'J2EEGetProcessList', 'J2EEGetProcessList2', 'J2EEGetThreadList', 'J2EEGetThreadList2',
'J2EEGetThreadCallStack', 'J2EEGetThreadTaskStack', 'J2EEGetSessionList', 'J2EEGetCacheStatistic',
'J2EEGetCacheStatistic2', 'J2EEGetApplicationAliasList', 'J2EEGetComponentList',
'J2EEControlComponents', 'J2EEGetWebSessionList', 'J2EEGetWebSessionList2', 'J2EEGetEJBSessionList', 'J2EEGetRemoteObjectList',
'J2EEGetVMGCHistory', 'J2EEGetVMGCHistory2', 'J2EEGetVMHeapInfo', 'J2EEGetClusterMsgList', 'J2EEGetSharedTableInfo',
'ICMGetThreadList', 'ICMGetConnectionList', 'ICMGetProxyConnectionList', 'ICMGetCacheEntries', 'WebDispGetServerList',
'WebDispGetGroupList', 'WebDispGetVirtHostList', 'WebDispGetUrlPrefixList', 'EnqGetStatistic', 'EnqGetLockTable',
'EnqRemoveUserLocks', 'StartWait', 'StopWait', 'WaitforStarted', 'WaitforStopped', 'RestartServiceWait',
'WaitforServiceStarted', 'CheckHostAgent']
return retlist
# converts recursively the suds object to a dictionary e.g. {'item': [{'name': hdbdaemon, 'value': '1'}]}
def recursive_dict(suds_object):
out = {}
if isinstance(suds_object, str):
return suds_object
for k, v in asdict(suds_object).items():
if hasattr(v, '__keylist__'):
out[k] = recursive_dict(v)
elif isinstance(v, list):
out[k] = []
for item in v:
if hasattr(item, '__keylist__'):
out[k].append(recursive_dict(item))
else:
out[k].append(item)
else:
out[k] = v
return out
def connection(hostname, port, username, password, function, parameter):
url = 'http://{0}:{1}/sapcontrol?wsdl'.format(hostname, port)
client = Client(url, username=username, password=password)
_function = getattr(client.service, function)
if parameter is not None:
result = _function(parameter)
else:
result = _function()
return result
def main():
module = AnsibleModule(
argument_spec=dict(
sysnr=dict(type='str', required=False),
port=dict(type='int', required=False),
username=dict(type='str', required=False),
password=dict(type='str', no_log=True, required=False),
hostname=dict(type='str', default="localhost"),
function=dict(type='str', required=True, choices=choices()),
parameter=dict(type='str', required=False),
force=dict(type='bool', default=False),
),
required_one_of=[('sysnr', 'port')],
mutually_exclusive=[('sysnr', 'port')],
supports_check_mode=False,
)
result = dict(changed=False, msg='', out={}, error='')
params = module.params
sysnr = params['sysnr']
port = params['port']
username = params['username']
password = params['password']
hostname = params['hostname']
function = params['function']
parameter = params['parameter']
force = params['force']
if not HAS_SUDS_LIBRARY:
module.fail_json(
msg=missing_required_lib('suds'),
exception=SUDS_LIBRARY_IMPORT_ERROR)
if function == "Stop":
if force is False:
module.fail_json(msg="Stop function requires force: True")
if port is None:
try:
try:
conn = connection(hostname, "5{0}14".format((sysnr).zfill(2)), username, password, function, parameter)
except Exception:
conn = connection(hostname, "5{0}13".format((sysnr).zfill(2)), username, password, function, parameter)
except Exception as err:
result['error'] = str(err)
else:
try:
conn = connection(hostname, port, username, password, function, parameter)
except Exception as err:
result['error'] = str(err)
if result['error'] != '':
result['msg'] = 'Something went wrong connecting to the SAPCONTROL SOAP API.'
module.fail_json(**result)
if conn is not None:
returned_data = recursive_dict(conn)
else:
returned_data = conn
result['changed'] = True
result['msg'] = "Succesful execution of: " + function
result['out'] = [returned_data]
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,246 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Rainer Leber <rainerleber@gmail.com>
# 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
DOCUMENTATION = r'''
---
module: sap_hdbsql
short_description: Ansible Module to execute SQL on SAP HANA
version_added: "1.0.0"
description: This module executes SQL statements on HANA with hdbsql.
options:
sid:
description: The system ID.
type: str
required: false
bin_path:
description: The path to the hdbsql binary.
type: str
required: false
instance:
description: The instance number.
type: str
required: true
user:
description: A dedicated username. The user could be also in hdbuserstore.
type: str
default: SYSTEM
userstore:
description: If C(true), the user must be in hdbuserstore.
type: bool
default: false
password:
description:
- The password to connect to the database.
- "B(Note:) Since the passwords have to be passed as command line arguments, I(userstore=true) should
be used whenever possible, as command line arguments can be seen by other users
on the same machine."
type: str
autocommit:
description: Autocommit the statement.
type: bool
default: true
host:
description: The Host IP address. The port can be defined as well.
type: str
database:
description: Define the database on which to connect.
type: str
encrypted:
description: Use encrypted connection.
type: bool
default: false
filepath:
description:
- One or more files each containing one SQL query to run.
- Must be a string or list containing strings.
type: list
elements: path
query:
description:
- SQL query to run.
- Must be a string or list containing strings. Please note that if you supply a string, it will be split by commas (C(,)) to a list.
It is better to supply a one-element list instead to avoid mangled input.
type: list
elements: str
notes:
- Does not support C(check_mode). Always reports that the state has changed even if no changes have been made.
author:
- Rainer Leber (@rainerleber)
'''
EXAMPLES = r'''
- name: Simple select query
community.sap_libs.sap_hdbsql:
sid: "hdb"
instance: "01"
password: "Test123"
query: select user_name from users
- name: RUN select query with host port
community.sap_libs.sap_hdbsql:
sid: "hdb"
instance: "01"
password: "Test123"
host: "10.10.2.4:30001"
query: select user_name from users
- name: Run several queries
community.sap_libs.sap_hdbsql:
sid: "hdb"
instance: "01"
password: "Test123"
query:
- select user_name from users
- select * from SYSTEM
host: "localhost"
autocommit: False
- name: Run several queries with path
community.sap_libs.sap_hdbsql:
bin_path: "/usr/sap/HDB/HDB01/exe/hdbsql"
instance: "01"
password: "Test123"
query:
- select user_name from users
- select * from users
host: "localhost"
autocommit: False
- name: Run several queries from file
community.sap_libs.sap_hdbsql:
sid: "hdb"
instance: "01"
password: "Test123"
filepath:
- /tmp/HANA_CPU_UtilizationPerCore_2.00.020+.txt
- /tmp/HANA.txt
host: "localhost"
- name: Run several queries from user store
community.sap_libs.sap_hdbsql:
sid: "hdb"
instance: "01"
user: hdbstoreuser
userstore: true
query:
- select user_name from users
- select * from users
autocommit: False
'''
RETURN = r'''
query_result:
description: List containing results of all queries executed (one sublist for every query).
returned: on success
type: list
elements: list
sample: [[{"Column": "Value1"}, {"Column": "Value2"}], [{"Column": "Value1"}, {"Column": "Value2"}]]
'''
import csv
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import StringIO
from ansible.module_utils.common.text.converters import to_native
def csv_to_list(rawcsv):
reader_raw = csv.DictReader(StringIO(rawcsv))
reader = [dict((k, v.strip()) for k, v in row.items()) for row in reader_raw]
return list(reader)
def main():
module = AnsibleModule(
argument_spec=dict(
sid=dict(type='str', required=False),
bin_path=dict(type='str', required=False),
instance=dict(type='str', required=True),
encrypted=dict(type='bool', default=False),
host=dict(type='str', required=False),
user=dict(type='str', default="SYSTEM"),
userstore=dict(type='bool', default=False),
password=dict(type='str', no_log=True),
database=dict(type='str', required=False),
query=dict(type='list', elements='str', required=False),
filepath=dict(type='list', elements='path', required=False),
autocommit=dict(type='bool', default=True),
),
required_one_of=[('query', 'filepath'), ('sid', 'instance')],
required_if=[('userstore', False, ['password'])],
supports_check_mode=False,
)
rc, out, err, out_raw = [0, [], "", ""]
params = module.params
sid = params['sid']
bin_path = params['bin_path']
instance = params['instance']
user = params['user']
userstore = params['userstore']
password = params['password']
autocommit = params['autocommit']
host = params['host']
database = params['database']
encrypted = params['encrypted']
filepath = params['filepath']
query = params['query']
if bin_path is None:
bin_path = "/usr/sap/{sid}/HDB{instance}/exe/hdbsql".format(sid=sid.upper(), instance=instance)
try:
command = [module.get_bin_path(bin_path, required=True)]
except Exception as e:
module.fail_json(msg='Failed to find hdbsql at the expected path "{0}".Please check SID and instance number: "{1}"'.format(bin_path, to_native(e)))
if encrypted is True:
command.extend(['-attemptencrypt'])
if autocommit is False:
command.extend(['-z'])
if host is not None:
command.extend(['-n', host])
if database is not None:
command.extend(['-d', database])
# -x Suppresses additional output, such as the number of selected rows in a result set.
if userstore:
command.extend(['-x', '-U', user])
else:
command.extend(['-x', '-i', instance, '-u', user, '-p', password])
if filepath is not None:
command.extend(['-I'])
for p in filepath:
# makes a command like hdbsql -i 01 -u SYSTEM -p secret123# -I /tmp/HANA_CPU_UtilizationPerCore_2.00.020+.txt,
# iterates through files and append the output to var out.
query_command = command + [p]
(rc, out_raw, err) = module.run_command(query_command)
out.append(csv_to_list(out_raw))
if query is not None:
for q in query:
# makes a command like hdbsql -i 01 -u SYSTEM -p secret123# "select user_name from users",
# iterates through multiple commands and append the output to var out.
query_command = command + [q]
(rc, out_raw, err) = module.run_command(query_command)
out.append(csv_to_list(out_raw))
changed = True
module.exit_json(changed=changed, rc=rc, query_result=out, stderr=err)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,187 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2022, Sean Freeman ,
# Rainer Leber <rainerleber@gmail.com> <rainer.leber@sva.de>
# 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
DOCUMENTATION = r'''
---
module: sap_pyrfc
short_description: Ansible Module for use of SAP PyRFC to execute SAP RFCs (Remote Function Calls) to SAP remote-enabled function modules
version_added: "1.2.0"
description:
- This module will executes rfc calls on a sap system.
- It is a generic approach to call rfc functions on a SAP System.
- This module should be used where no module or role is provided.
options:
function:
description: The SAP RFC function to call.
required: true
type: str
parameters:
description: The parameters which are needed by the function.
required: true
type: dict
connection:
description: The required connection details.
required: true
type: dict
suboptions:
ashost:
description: The required host for the SAP system. Can be either an FQDN or IP Address.
type: str
required: true
sysid:
description: The systemid of the SAP system.
type: str
required: false
sysnr:
description:
- The system number of the SAP system.
- You must quote the value to ensure retaining the leading zeros.
type: str
required: true
client:
description:
- The client number to connect to.
- You must quote the value to ensure retaining the leading zeros.
type: str
required: true
user:
description: The required username for the SAP system.
type: str
required: true
passwd:
description: The required password for the SAP system.
type: str
required: true
lang:
description: The used language to execute.
type: str
required: false
requirements:
- pyrfc >= 2.4.0
author:
- Sean Freeman (@seanfreeman)
- Rainer Leber (@rainerleber)
'''
EXAMPLES = '''
- name: test the pyrfc module
community.sap_libs.sap_pyrfc:
function: STFC_CONNECTION
parameters:
REQUTEXT: "Hello SAP!"
connection:
ashost: s4hana.poc.cloud
sysid: TDT
sysnr: "01"
client: "400"
user: DDIC
passwd: Password1
lang: EN
'''
RETURN = r'''
result:
description: The execution description.
type: dict
returned: always
sample: {"ECHOTEXT": "Hello SAP!",
"RESPTEXT": "SAP R/3 Rel. 756 Sysid: TST Date: 20220710 Time: 140717 Logon_Data: 000/DDIC/E"}
'''
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ..module_utils.pyrfc_handler import get_connection
try:
from pyrfc import ABAPApplicationError, ABAPRuntimeError, CommunicationError, Connection, LogonError
except ImportError:
HAS_PYRFC_LIBRARY = False
PYRFC_LIBRARY_IMPORT_ERROR = traceback.format_exc()
else:
PYRFC_LIBRARY_IMPORT_ERROR = None
HAS_PYRFC_LIBRARY = True
def main():
msg = None
params_spec = dict(
ashost=dict(type='str', required=True),
sysid=dict(type='str', required=False),
sysnr=dict(type='str', required=True),
client=dict(type='str', required=True),
user=dict(type='str', required=True),
passwd=dict(type='str', required=True, no_log=True),
lang=dict(type='str', required=False),
)
argument_spec = dict(function=dict(required=True, type='str'),
parameters=dict(required=True, type='dict'),
connection=dict(
required=True, type='dict', options=params_spec),
)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)
function = module.params.get('function')
func_params = module.params.get('parameters')
conn_params = module.params.get('connection')
if not HAS_PYRFC_LIBRARY:
module.fail_json(
msg=missing_required_lib('pyrfc'),
exception=PYRFC_LIBRARY_IMPORT_ERROR)
# Check mode
if module.check_mode:
msg = "function: %s; params: %s; login: %s" % (
function, func_params, conn_params)
module.exit_json(msg=msg, changed=True)
try:
conn = get_connection(module, conn_params)
result = conn.call(function, **func_params)
error_msg = None
except CommunicationError as err:
msg = "Could not connect to server"
error_msg = err.message
except LogonError as err:
msg = "Could not log in"
error_msg = err.message
except (ABAPApplicationError, ABAPRuntimeError) as err:
msg = "ABAP error occurred"
error_msg = err.message
except Exception as err:
msg = "Something went wrong."
error_msg = err
else:
module.exit_json(changed=True, result=result)
if msg:
module.fail_json(msg=msg, exception=error_msg)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,267 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Rainer Leber <rainerleber@gmail.com> <rainer.leber@sva.de>
# 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
DOCUMENTATION = r'''
---
module: sap_snote
short_description: This module will upload and (de)implements C(SNOTES) in a SAP S4HANA environment.
version_added: "1.0.0"
description:
- The C(sap_snote) module depends on C(pyrfc) Python library (version 2.4.0 and upwards).
Depending on distribution you are using, you may need to install additional packages to
have these available.
- This module will use the Function Group C(SCWB_API).
- The C(TMS) must be configured at first.
- Integrating SNOTES cannot be done via C(DDIC)- or C(SAP*)-User.
options:
state:
description:
- The decision what to do with the SNOTE.
- Could be C('present'), C('absent')
default: 'present'
choices:
- 'present'
- 'absent'
required: false
type: str
conn_username:
description: The required username for the SAP system.
required: true
type: str
conn_password:
description: The required password for the SAP system.
required: true
type: str
host:
description: The required host for the SAP system. Can be either an FQDN or IP Address.
required: true
type: str
sysnr:
description:
- The system number of the SAP system.
- You must quote the value to ensure retaining the leading zeros.
required: false
default: '01'
type: str
client:
description:
- The client number to connect to.
- You must quote the value to ensure retaining the leading zeros.
required: false
default: '000'
type: str
snote_path:
description:
- The path to the extracted SNOTE txt file.
- The File could be extracted from SAR package.
- If C(snote_path) is not provided, the C(snote) parameter must be defined.
- The SNOTE txt file must be at a place where the SAP System is authorized for. For example C(/usr/sap/trans/files).
required: false
type: str
snote:
description:
- With the C(snote) paramter only implementation and deimplementation will work.
- Upload SNOTES to the System is only available if C(snote_path) is provided.
required: false
type: str
requirements:
- pyrfc >= 2.4.0
author:
- Rainer Leber (@rainerleber)
'''
EXAMPLES = r'''
- name: test snote module
hosts: localhost
tasks:
- name: implement SNOTE
community.sap_libs.sap_snote:
conn_username: 'DDIC'
conn_password: 'Passwd1234'
host: 192.168.1.100
sysnr: '01'
client: '000'
state: present
snote_path: /usr/sap/trans/tmp/0002949148.txt
- name: test snote module without path
hosts: localhost
tasks:
- name: deimplement SNOTE
community.sap_libs.sap_snote:
conn_username: 'DDIC'
conn_password: 'Passwd1234'
host: 192.168.1.100
sysnr: '01'
client: '000'
state: absent
snote: 0002949148
'''
RETURN = r'''
msg:
description: A small execution description.
type: str
returned: always
sample: 'SNOTE 000298026 implemented.'
out:
description: A complete description of the SNOTE implementation. If this is available.
type: list
elements: dict
returned: always
sample: '{
"RETURN": [{"ES_MSG": { "MSGNO": "000", "MSGTY": "", "MSGTXT": "", "MSGV1": "" },
"ET_MSG": [],
"EV_RC": 0,
"ET_MISSING_NOTES": [],
"IT_FILENAME": [{"FILENAME": "/usr/sap/trans/tmp/0002980265.txt"}],
"IT_NOTES": [{"NUMM": "0002980265", "VERSNO": "0000"}]
}]}'
'''
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from os import path as os_path
import traceback
try:
from pyrfc import Connection
except ImportError:
HAS_PYRFC_LIBRARY = False
ANOTHER_LIBRARY_IMPORT_ERROR = traceback.format_exc()
else:
ANOTHER_LIBRARY_IMPORT_ERROR = None
HAS_PYRFC_LIBRARY = True
def call_rfc_method(connection, method_name, kwargs):
# PyRFC call function
return connection.call(method_name, **kwargs)
def check_implementation(conn, snote):
check_implemented = call_rfc_method(conn, 'SCWB_API_GET_NOTES_IMPLEMENTED', {})
for snote_list in check_implemented['ET_NOTES_IMPL']:
if snote in snote_list['NUMM']:
return True
return False
def run_module():
module = AnsibleModule(
argument_spec=dict(
state=dict(default='present', choices=['absent', 'present']),
conn_username=dict(type='str', required=True),
conn_password=dict(type='str', required=True, no_log=True),
host=dict(type='str', required=True),
sysnr=dict(type='str', default="01"),
client=dict(type='str', default="000"),
snote_path=dict(type='str', required=False),
snote=dict(type='str', required=False),
),
required_one_of=[('snote_path', 'snote')],
supports_check_mode=False,
)
result = dict(changed=False, msg='', out={}, error='')
raw = ""
post_check = False
params = module.params
state = params['state']
conn_username = (params['conn_username']).upper()
conn_password = params['conn_password']
host = params['host']
sysnr = (params['sysnr']).zfill(2)
client = params['client']
path = params['snote_path']
snote = params['snote']
if not HAS_PYRFC_LIBRARY:
module.fail_json(
msg=missing_required_lib('pyrfc'),
exception=ANOTHER_LIBRARY_IMPORT_ERROR)
if conn_username == "DDIC" or conn_username == "SAP*":
result['msg'] = 'User C(DDIC) or C(SAP*) not allowed for this operation.'
module.fail_json(**result)
# basic RFC connection with pyrfc
try:
conn = Connection(user=conn_username, passwd=conn_password, ashost=host, sysnr=sysnr, client=client)
except Exception as err:
result['error'] = str(err)
result['msg'] = 'Something went wrong connecting to the SAP system.'
module.fail_json(**result)
# pre evaluation of parameters
if path is not None:
if path.endswith('.txt'):
# splits snote number from path and txt extension
snote = os_path.basename(os_path.normpath(path)).split('.')[0]
else:
result['msg'] = 'The path must include the extracted snote file and ends with txt.'
module.fail_json(**result)
pre_check = check_implementation(conn, snote)
if state == "absent" and pre_check:
raw = call_rfc_method(conn, 'SCWB_API_NOTES_DEIMPLEMENT', {'IT_NOTES': [snote]})
if state == "present" and not pre_check:
if path:
raw_upload = call_rfc_method(conn, 'SCWB_API_UPLOAD_NOTES', {'IT_FILENAME': [path], 'IT_NOTES': [snote]})
if raw_upload['EV_RC'] != 0:
result['out'] = raw_upload
result['msg'] = raw_upload['ES_MSG']['MSGTXT']
module.fail_json(**result)
raw = call_rfc_method(conn, 'SCWB_API_NOTES_IMPLEMENT', {'IT_NOTES': [snote]})
queued = call_rfc_method(conn, 'SCWB_API_CINST_QUEUE_GET', {})
if queued['ET_MANUAL_ACTIVITIES']:
raw = call_rfc_method(conn, 'SCWB_API_CONFIRM_MAN_ACTIVITY', {})
if raw:
if raw['EV_RC'] == 0:
post_check = check_implementation(conn, snote)
if post_check and state == "present":
result['changed'] = True
result['msg'] = 'SNOTE "{0}" implemented.'.format(snote)
if not post_check and state == "absent":
result['changed'] = True
result['msg'] = 'SNOTE "{0}" deimplemented.'.format(snote)
else:
result['msg'] = "Something went wrong."
module.fail_json(**result)
result['out'] = raw
else:
result['msg'] = "Nothing to do."
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,213 @@
#!/usr/bin/python
# Copyright: (c) 2022, Rainer Leber rainerleber@gmail.com>
# 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
DOCUMENTATION = r'''
---
module: sap_system_facts
short_description: Gathers SAP facts in a host
version_added: "1.0.0"
description:
- This facts module gathers SAP system facts about the running instance.
author:
- Rainer Leber (@rainerleber)
notes:
- Supports C(check_mode).
'''
EXAMPLES = r'''
- name: Return SAP system ansible_facts
community.sap_libs.sap_system_facts:
'''
RETURN = r'''
# These are examples of possible return values,
# and in general should use other names for return values.
ansible_facts:
description: Facts about the running SAP systems.
returned: always
type: dict
contains:
sap:
description: Facts about the running SAP systems.
type: list
elements: dict
returned: When SAP system fact is present
sample: [
{
"InstanceType": "NW",
"NR": "00",
"SID": "ABC",
"TYPE": "ASCS"
},
{
"InstanceType": "NW",
"NR": "01",
"SID": "ABC",
"TYPE": "PAS"
},
{
"InstanceType": "HANA",
"NR": "02",
"SID": "HDB",
"TYPE": "HDB"
},
{
"InstanceType": "NW",
"NR": "80",
"SID": "WEB",
"TYPE": "WebDisp"
}
]
'''
from ansible.module_utils.basic import AnsibleModule
import os
import re
def get_all_hana_sid():
hana_sid = list()
if os.path.isdir("/hana/shared"):
# /hana/shared directory exists
for sid in os.listdir('/hana/shared'):
if os.path.isdir("/usr/sap/" + sid):
hana_sid = hana_sid + [sid]
if hana_sid:
return hana_sid
def get_all_nw_sid():
nw_sid = list()
if os.path.isdir("/sapmnt"):
# /sapmnt directory exists
for sid in os.listdir('/sapmnt'):
if os.path.isdir("/usr/sap/" + sid):
nw_sid = nw_sid + [sid]
else:
# Check to see if /sapmnt/SID/sap_bobj exists
if os.path.isdir("/sapmnt/" + sid + "/sap_bobj"):
# is a bobj system
nw_sid = nw_sid + [sid]
if nw_sid:
return nw_sid
def get_hana_nr(sids, module):
hana_list = list()
for sid in sids:
for instance in os.listdir('/usr/sap/' + sid):
if 'HDB' in instance:
instance_nr = instance[-2:]
# check if instance number exists
command = [module.get_bin_path('/usr/sap/hostctrl/exe/sapcontrol', required=True)]
command.extend(['-nr', instance_nr, '-function', 'GetProcessList'])
check_instance = module.run_command(command, check_rc=False)
# sapcontrol returns c(0 - 5) exit codes only c(1) is unavailable
if check_instance[0] != 1:
hana_list.append({'NR': instance_nr, 'SID': sid, 'TYPE': 'HDB', 'InstanceType': 'HANA'})
return hana_list
def get_nw_nr(sids, module):
nw_list = list()
type = ""
for sid in sids:
for instance in os.listdir('/usr/sap/' + sid):
instance_nr = instance[-2:]
command = [module.get_bin_path('/usr/sap/hostctrl/exe/sapcontrol', required=True)]
# check if returned instance_nr is a number because sapcontrol returns all if a random string is provided
if instance_nr.isdigit():
command.extend(['-nr', instance_nr, '-function', 'GetInstanceProperties'])
check_instance = module.run_command(command, check_rc=False)
if check_instance[0] != 1:
for line in check_instance[1].splitlines():
if re.search('INSTANCE_NAME', line):
# convert to list and extract last
type_raw = (line.strip('][').split(', '))[-1]
# split instance number
type = type_raw[:-2]
nw_list.append({'NR': instance_nr, 'SID': sid, 'TYPE': get_instance_type(type), 'InstanceType': 'NW'})
return nw_list
def get_instance_type(raw_type):
if raw_type[0] == "D":
# It's a PAS
type = "PAS"
elif raw_type[0] == "A":
# It's an ASCS
type = "ASCS"
elif raw_type[0] == "W":
# It's a Webdisp
type = "WebDisp"
elif raw_type[0] == "J":
# It's a Java
type = "Java"
elif raw_type[0] == "S":
# It's an SCS
type = "SCS"
elif raw_type[0] == "E":
# It's an ERS
type = "ERS"
else:
# Unknown instance type
type = "XXX"
return type
def run_module():
module_args = dict()
system_result = list()
result = dict(
changed=False,
ansible_facts=dict(),
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True,
)
hana_sid = get_all_hana_sid()
if hana_sid:
system_result = system_result + get_hana_nr(hana_sid, module)
nw_sid = get_all_nw_sid()
if nw_sid:
system_result = system_result + get_nw_nr(nw_sid, module)
if system_result:
result['ansible_facts'] = {'sap': system_result}
else:
result['ansible_facts']
if module.check_mode:
module.exit_json(**result)
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,350 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Rainer Leber <rainerleber@gmail.com>
# 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
DOCUMENTATION = r'''
---
module: sap_task_list_execute
short_description: Perform SAP Task list execution
version_added: "0.1.0"
description:
- The M(community.sap_libs.sap_task_list_execute) module depends on C(pyrfc) Python library (version 2.4.0 and upwards).
Depending on distribution you are using, you may need to install additional packages to
have these available.
- Tasks in the task list which requires manual activities will be confirmed automatically.
- This module will use the RFC package C(STC_TM_API).
requirements:
- pyrfc >= 2.4.0
- xmltodict
options:
conn_username:
description: The required username for the SAP system.
required: true
type: str
conn_password:
description: The required password for the SAP system.
required: true
type: str
host:
description: The required host for the SAP system. Can be either an FQDN or IP Address.
required: true
type: str
sysnr:
description:
- The system number of the SAP system.
- You must quote the value to ensure retaining the leading zeros.
default: '00'
type: str
client:
description:
- The client number to connect to.
- You must quote the value to ensure retaining the leading zeros.
default: '000'
type: str
task_to_execute:
description: The task list which will be executed.
required: true
type: str
task_parameters:
description:
- The tasks and the parameters for execution.
- If the task list does not need any parameters, this could be empty.
- If only specific tasks from the task list should be executed,
the tasks even when no parameter is needed must be provided
alongside with the module parameter I(task_skip=true).
type: list
elements: dict
suboptions:
TASKNAME:
description: The name of the task in the task list.
type: str
required: true
FIELDNAME:
description: The name of the field of the task.
type: str
VALUE:
description: The value which have to be set.
type: raw
task_settings:
description:
- Setting for the execution of the task list. This can be the following as in TCODE SE80 described.
Check Mode C(CHECKRUN), Background Processing Active C(BATCH) (this is the default value),
Asynchronous Execution C(ASYNC), Trace Mode C(TRACE), Server Name C(BATCH_TARGET).
default: ['BATCH']
type: list
elements: str
task_skip:
description:
- If this parameter is C(true), not defined tasks in I(task_parameters) are skipped.
- This could be the case when only certain tasks should run from the task list.
default: false
type: bool
notes:
- Does not support C(check_mode). Always returns that the state has changed.
author:
- Rainer Leber (@rainerleber)
'''
EXAMPLES = r'''
# Pass in a message
- name: Test task execution
community.sap_libs.sap_task_list_execute:
conn_username: DDIC
conn_password: Passwd1234
host: 10.1.8.10
sysnr: '01'
client: '000'
task_to_execute: SAP_BASIS_SSL_CHECK
task_settings: batch
- name: Pass in input parameters
community.sap_libs.sap_task_list_execute:
conn_username: DDIC
conn_password: Passwd1234
host: 10.1.8.10
sysnr: '00'
client: '000'
task_to_execute: SAP_BASIS_SSL_CHECK
task_parameters :
- { 'TASKNAME': 'CL_STCT_CHECK_SEC_CRYPTO', 'FIELDNAME': 'P_OPT2', 'VALUE': 'X' }
- TASKNAME: CL_STCT_CHECK_SEC_CRYPTO
FIELDNAME: P_OPT3
VALUE: X
task_settings: batch
# Exported environment variables
- name: Hint if module will fail with error message like ImportError libsapnwrfc.so...
community.sap_libs.sap_task_list_execute:
conn_username: DDIC
conn_password: Passwd1234
host: 10.1.8.10
sysnr: '00'
client: '000'
task_to_execute: SAP_BASIS_SSL_CHECK
task_settings: batch
environment:
SAPNWRFC_HOME: /usr/local/sap/nwrfcsdk
LD_LIBRARY_PATH: /usr/local/sap/nwrfcsdk/lib
'''
RETURN = r'''
msg:
description: A small execution description.
type: str
returned: always
sample: 'Successful'
out:
description: A complete description of the executed tasks. If this is available.
type: list
elements: dict
returned: on success
sample: [...,{
"LOG": {
"STCTM_S_LOG": [
{
"ACTIVITY": "U_CONFIG",
"ACTIVITY_DESCR": "Configuration changed",
"DETAILS": null,
"EXEC_ID": "20210728184903.815739",
"FIELD": null,
"ID": "STC_TASK",
"LOG_MSG_NO": "000000",
"LOG_NO": null,
"MESSAGE": "For radiobutton group ICM too many options are set; choose only one option",
"MESSAGE_V1": "ICM",
"MESSAGE_V2": null,
"MESSAGE_V3": null,
"MESSAGE_V4": null,
"NUMBER": "048",
"PARAMETER": null,
"PERIOD": "M",
"PERIOD_DESCR": "Maintenance",
"ROW": "0",
"SRC_LINE": "170",
"SRC_OBJECT": "CL_STCTM_REPORT_UI IF_STCTM_UI_TASK~SET_PARAMETERS",
"SYSTEM": null,
"TIMESTMP": "20210728184903",
"TSTPNM": "DDIC",
"TYPE": "E"
},...
]}}]
'''
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
import traceback
try:
from pyrfc import Connection
except ImportError:
HAS_PYRFC_LIBRARY = False
PYRFC_LIBRARY_IMPORT_ERROR = traceback.format_exc()
else:
PYRFC_LIBRARY_IMPORT_ERROR = None
HAS_PYRFC_LIBRARY = True
try:
import xmltodict
except ImportError:
HAS_XMLTODICT_LIBRARY = False
XMLTODICT_LIBRARY_IMPORT_ERROR = traceback.format_exc()
else:
XMLTODICT_LIBRARY_IMPORT_ERROR = None
HAS_XMLTODICT_LIBRARY = True
def call_rfc_method(connection, method_name, kwargs):
# PyRFC call function
return connection.call(method_name, **kwargs)
def process_exec_settings(task_settings):
# processes task settings to objects
exec_settings = {}
for settings in task_settings:
temp_dict = {settings.upper(): 'X'}
for key, value in temp_dict.items():
exec_settings[key] = value
return exec_settings
def xml_to_dict(xml_raw):
try:
xml_parsed = xmltodict.parse(xml_raw, dict_constructor=dict)
xml_dict = xml_parsed['asx:abap']['asx:values']['SESSION']['TASKLIST']
except KeyError:
xml_dict = "No logs available."
return xml_dict
def run_module():
params_spec = dict(
TASKNAME=dict(type='str', required=True),
FIELDNAME=dict(type='str'),
VALUE=dict(type='raw'),
)
# define available arguments/parameters a user can pass to the module
module = AnsibleModule(
argument_spec=dict(
# values for connection
conn_username=dict(type='str', required=True),
conn_password=dict(type='str', required=True, no_log=True),
host=dict(type='str', required=True),
sysnr=dict(type='str', default="00"),
client=dict(type='str', default="000"),
# values for execution tasks
task_to_execute=dict(type='str', required=True),
task_parameters=dict(type='list', elements='dict', options=params_spec),
task_settings=dict(type='list', elements='str', default=['BATCH']),
task_skip=dict(type='bool', default=False),
),
supports_check_mode=False,
)
result = dict(changed=False, msg='', out={})
params = module.params
username = params['conn_username'].upper()
password = params['conn_password']
host = params['host']
sysnr = params['sysnr']
client = params['client']
task_parameters = params['task_parameters']
task_to_execute = params['task_to_execute']
task_settings = params['task_settings']
task_skip = params['task_skip']
if not HAS_PYRFC_LIBRARY:
module.fail_json(
msg=missing_required_lib('pyrfc'),
exception=PYRFC_LIBRARY_IMPORT_ERROR)
if not HAS_XMLTODICT_LIBRARY:
module.fail_json(
msg=missing_required_lib('xmltodict'),
exception=XMLTODICT_LIBRARY_IMPORT_ERROR)
# basic RFC connection with pyrfc
try:
conn = Connection(user=username, passwd=password, ashost=host, sysnr=sysnr, client=client)
except Exception as err:
result['error'] = str(err)
result['msg'] = 'Something went wrong connecting to the SAP system.'
module.fail_json(**result)
try:
raw_params = call_rfc_method(conn, 'STC_TM_SCENARIO_GET_PARAMETERS',
{'I_SCENARIO_ID': task_to_execute})
except Exception as err:
result['error'] = str(err)
result['msg'] = 'The task list does not exist.'
module.fail_json(**result)
exec_settings = process_exec_settings(task_settings)
# initialize session task
session_init = call_rfc_method(conn, 'STC_TM_SESSION_BEGIN',
{'I_SCENARIO_ID': task_to_execute,
'I_INIT_ONLY': 'X'})
# Confirm Tasks which requires manual activities from Task List Run
for task in raw_params['ET_PARAMETER']:
call_rfc_method(conn, 'STC_TM_TASK_CONFIRM',
{'I_SESSION_ID': session_init['E_SESSION_ID'],
'I_TASKNAME': task['TASKNAME']})
if task_skip:
for task in raw_params['ET_PARAMETER']:
call_rfc_method(conn, 'STC_TM_TASK_SKIP',
{'I_SESSION_ID': session_init['E_SESSION_ID'],
'I_TASKNAME': task['TASKNAME'], 'I_SKIP_DEP_TASKS': 'X'})
# unskip defined tasks and set parameters
if task_parameters is not None:
for task in task_parameters:
call_rfc_method(conn, 'STC_TM_TASK_UNSKIP',
{'I_SESSION_ID': session_init['E_SESSION_ID'],
'I_TASKNAME': task['TASKNAME'], 'I_UNSKIP_DEP_TASKS': 'X'})
call_rfc_method(conn, 'STC_TM_SESSION_SET_PARAMETERS',
{'I_SESSION_ID': session_init['E_SESSION_ID'],
'IT_PARAMETER': task_parameters})
# start the task
try:
session_start = call_rfc_method(conn, 'STC_TM_SESSION_RESUME',
{'I_SESSION_ID': session_init['E_SESSION_ID'],
'IS_EXEC_SETTINGS': exec_settings})
except Exception as err:
result['error'] = str(err)
result['msg'] = 'Something went wrong. See error.'
module.fail_json(**result)
# get task logs because the execution may successfully but the tasks shows errors or warnings
# returned value is ABAPXML https://help.sap.com/doc/abapdocu_755_index_htm/7.55/en-US/abenabap_xslt_asxml_general.htm
session_log = call_rfc_method(conn, 'STC_TM_SESSION_GET_LOG',
{'I_SESSION_ID': session_init['E_SESSION_ID']})
task_list = xml_to_dict(session_log['E_LOG'])
result['changed'] = True
result['msg'] = session_start['E_STATUS_DESCR']
result['out'] = task_list
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,508 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Rainer Leber <rainerleber@gmail.com> <rainer.leber@sva.de>
# 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
DOCUMENTATION = r'''
---
module: sap_user
short_description: This module will manage a user entities in a SAP S4/HANA environment
version_added: "1.0.0"
description:
- The M(community.sap_libs.sap_user) module depends on C(pyrfc) Python library (version 2.4.0 and upwards).
Depending on distribution you are using, you may need to install additional packages to
have these available.
- This module will use the following user BAPIs to manage user entities.
- C(BAPI_USER_GET_DETAIL)
- C(BAPI_USER_DELETE)
- C(BAPI_USER_CREATE1)
- C(BAPI_USER_CHANGE)
- C(BAPI_USER_ACTGROUPS_ASSIGN)
- C(BAPI_USER_PROFILES_ASSIGN)
- C(BAPI_USER_UNLOCK)
- C(BAPI_USER_LOCK)
options:
state:
description:
- The decision what to do with the user.
default: 'present'
choices:
- 'present'
- 'absent'
- 'lock'
- 'unlock'
required: false
type: str
force:
description:
- Must be C('True') if the password or type should be overwritten.
default: False
required: false
type: bool
conn_username:
description: The required username for the SAP system.
required: true
type: str
conn_password:
description: The required password for the SAP system.
required: true
type: str
host:
description: The required host for the SAP system. Can be either an FQDN or IP Address.
required: true
type: str
sysnr:
description:
- The system number of the SAP system.
- You must quote the value to ensure retaining the leading zeros.
default: '00'
type: str
client:
description:
- The client number to connect to.
- You must quote the value to ensure retaining the leading zeros.
default: '000'
type: str
username:
description:
- The username.
type: str
required: true
firstname:
description:
- The Firstname of the user in the SAP system.
type: str
required: false
lastname:
description:
- The lastname of the user in the SAP system.
type: str
required: false
email:
description:
- The email address of the user in the SAP system.
type: str
required: false
password:
description:
- The password for the user in the SAP system.
type: str
required: false
useralias:
description:
- The alias for the user in the SAP system.
type: str
required: false
user_type:
description:
- The type for the user in the SAP system.
- C('A') Dialog user, C('B') System User, C('C') Communication User,
C('S') Service User, C('L') Reference User.
- Must be in uppercase.
type: str
required: false
default: 'A'
choices: ['A', 'B', 'C', 'S', 'L']
company:
description:
- The specific company the user belongs to.
- The company name must be available in the SAP system.
type: str
required: false
profiles:
description:
- Assign profiles to the user.
- Should be in uppercase, for example C('SAP_NEW') or C('SAP_ALL').
type: list
elements: str
default: ['']
required: false
roles:
description:
- Assign roles to the user.
type: list
elements: str
default: ['']
required: false
requirements:
- pyrfc >= 2.4.0
author:
- Rainer Leber (@rainerleber)
notes:
- Does not support C(check_mode).
'''
EXAMPLES = r'''
- name: Create SAP User
community.sap_libs.sap_user:
conn_username: 'DDIC'
conn_password: 'Test123'
host: 192.168.1.150
sysnr: '01'
client: '000'
state: present
username: ADMIN
firstname: first_admin
lastname: last_admin
email: admin@test.de
password: Test123456
useralias: ADMIN
company: DEFAULT_COMPANY
roles:
- "SAP_ALL"
- name: Force change SAP User
community.sap_libs.sap_user:
conn_username: 'DDIC'
conn_password: 'Test123'
host: 192.168.1.150
sysnr: '01'
client: '000'
state: present
force: true
username: ADMIN
firstname: first_admin
lastname: last_admin
email: admin@test.de
password: Test123456
useralias: ADMIN
company: DEFAULT_COMPANY
roles:
- "SAP_ALL"
- name: Delete SAP User
community.sap_libs.sap_user:
conn_username: 'DDIC'
conn_password: 'Test123'
host: 192.168.1.150
sysnr: '01'
client: '000'
state: absent
force: true
username: ADMIN
- name: Unlock SAP User
community.sap_libs.sap_user:
conn_username: 'DDIC'
conn_password: 'Test123'
host: 192.168.1.150
sysnr: '01'
client: '000'
state: unlock
force: true
username: ADMIN
'''
RETURN = r'''
msg:
description: A small execution description about the user action.
type: str
returned: always
sample: 'User ADMIN created'
out:
description: A detailed description about the user action.
type: list
elements: dict
returned: on success
sample: [...,{
"RETURN": [
{
"FIELD": "BNAME",
"ID": "01",
"LOG_MSG_NO": "000000",
"LOG_NO": "",
"MESSAGE": "User ADMIN created",
"MESSAGE_V1": "ADMIN",
"MESSAGE_V2": "",
"MESSAGE_V3": "",
"MESSAGE_V4": "",
"NUMBER": "102",
"PARAMETER": "",
"ROW": 0,
"SYSTEM": "",
"TYPE": "S"
}
],
"SAPUSER_UUID_HIST": []}]
'''
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
import traceback
import datetime
try:
from pyrfc import Connection
except ImportError:
HAS_PYRFC_LIBRARY = False
PYRFC_LIBRARY_IMPORT_ERROR = traceback.format_exc()
else:
PYRFC_LIBRARY_IMPORT_ERROR = None
HAS_PYRFC_LIBRARY = True
def add_to_dict(target_dict, target_key, value):
# Adds the given value to a dict as the key
# check if the given key is in the given dict yet
if target_key in target_dict:
return False
target_dict[target_key] = value
return True
def call_rfc_method(connection, method_name, kwargs):
# PyRFC call function
return connection.call(method_name, **kwargs)
def build_rfc_user_params(username, firstname, lastname, email, raw_password,
useralias, user_type, raw_company, user_change, force):
"""Creates RFC parameters for Creating users"""
# define dicts in batch
params = dict()
address = dict()
password = dict()
alias = dict()
logondata = dict()
company = dict()
# for change parameters
addressx = dict()
passwordx = dict()
logondatax = dict()
companyx = dict()
# define username
add_to_dict(params, 'USERNAME', username)
# define Address
add_to_dict(address, 'FIRSTNAME', firstname)
add_to_dict(address, 'LASTNAME', lastname)
add_to_dict(address, 'E_MAIL', email)
# define Password
add_to_dict(password, 'BAPIPWD', raw_password)
# define Alias
add_to_dict(alias, 'USERALIAS', useralias)
# define LogonData
add_to_dict(logondata, 'GLTGV', datetime.date.today())
add_to_dict(logondata, 'GLTGB', '20991231')
add_to_dict(logondata, 'USTYP', user_type)
# define company
add_to_dict(company, 'COMPANY', raw_company)
params['LOGONDATA'] = logondata
params['ADDRESS'] = address
params['COMPANY'] = company
params['ALIAS'] = alias
params['PASSWORD'] = password
# add change if user exists
if user_change and force:
add_to_dict(addressx, 'FIRSTNAME', 'X')
add_to_dict(addressx, 'LASTNAME', 'X')
add_to_dict(addressx, 'E_MAIL', 'X')
# define Password
add_to_dict(passwordx, 'BAPIPWD', 'X')
# define LogonData
add_to_dict(logondatax, 'USTYP', 'X')
# define company
add_to_dict(companyx, 'COMPANY', 'X')
params['LOGONDATAX'] = logondatax
params['ADDRESSX'] = addressx
params['COMPANYX'] = companyx
params['PASSWORDX'] = passwordx
return params
def user_role_assignment_build_rfc_params(roles, username):
rfc_table = []
for role_name in roles:
table_row = {'AGR_NAME': role_name}
add_to_dict(table_row, 'FROM_DAT', datetime.date.today())
add_to_dict(table_row, 'TO_DAT', '20991231')
rfc_table.append(table_row)
return {
'USERNAME': username,
'ACTIVITYGROUPS': rfc_table
}
def user_profile_assignment_build_rfc_params(profiles, username):
rfc_table = []
for profile_name in profiles:
table_row = {'BAPIPROF': profile_name}
rfc_table.append(table_row)
return {
'USERNAME': username,
'PROFILES': rfc_table
}
def check_user(user_detail):
if len(user_detail['RETURN']) > 0:
for sub in user_detail['RETURN']:
if sub['NUMBER'] == '124':
return False
return True
def return_analysis(raw):
change = False
failed = False
for state in raw['RETURN']:
if state['TYPE'] == "E":
if state['NUMBER'] == '224' or state['NUMBER'] == '124':
change = False
else:
failed = True
if state['TYPE'] == "S":
if state['NUMBER'] != '029':
change = True
if state['TYPE'] == "W":
if state['NUMBER'] == '049' or state['NUMBER'] == '047':
change = True
if state['NUMBER'] == '255':
change = True
return [{"change": change}, {"failed": failed}]
def run_module():
module = AnsibleModule(
argument_spec=dict(
# logical values
state=dict(default='present', choices=[
'absent', 'present', 'lock', 'unlock']),
force=dict(type='bool', default=False),
# values for connection
conn_username=dict(type='str', required=True),
conn_password=dict(type='str', required=True, no_log=True),
host=dict(type='str', required=True),
sysnr=dict(type='str', default="00"),
client=dict(type='str', default="000"),
# values for the new or existing user
username=dict(type='str', required=True),
firstname=dict(type='str', required=False),
lastname=dict(type='str', required=False),
email=dict(type='str', required=False),
password=dict(type='str', required=False, no_log=True),
useralias=dict(type='str', required=False),
user_type=dict(default="A",
choices=['A', 'B', 'C', 'S', 'L']),
company=dict(type='str', required=False),
# values for profile must a list
# Example ["SAP_NEW", "SAP_ALL"]
profiles=dict(type='list', elements='str', default=[""]),
# values for roles must a list
roles=dict(type='list', elements='str', default=[""]),
),
supports_check_mode=False,
required_if=[('state', 'present', ['useralias', 'company'])]
)
result = dict(changed=False, msg='', out='')
count = 0
raw = ""
params = module.params
state = params['state']
conn_username = (params['conn_username']).upper()
conn_password = params['conn_password']
host = params['host']
sysnr = params['sysnr']
client = params['client']
username = (params['username']).upper()
firstname = params['firstname']
lastname = params['lastname']
email = params['email']
password = params['password']
force = params['force']
if not params['useralias'] is None:
useralias = (params['useralias']).upper()
user_type = (params['user_type']).upper()
company = params['company']
profiles = params['profiles']
roles = params['roles']
if not HAS_PYRFC_LIBRARY:
module.fail_json(
msg=missing_required_lib('pyrfc'),
exception=PYRFC_LIBRARY_IMPORT_ERROR)
# basic RFC connection with pyrfc
try:
conn = Connection(user=conn_username, passwd=conn_password, ashost=host, sysnr=sysnr, client=client)
except Exception as err:
result['error'] = str(err)
result['msg'] = 'Something went wrong connecting to the SAP system.'
module.fail_json(**result)
# user details
user_detail = call_rfc_method(conn, 'BAPI_USER_GET_DETAIL', {'USERNAME': username})
user_exists = check_user(user_detail)
if state == "absent":
if user_exists:
raw = call_rfc_method(conn, 'BAPI_USER_DELETE', {'USERNAME': username})
if state == "present":
user_params = build_rfc_user_params(username, firstname, lastname, email, password, useralias, user_type, company, user_exists, force)
if not user_exists:
raw = call_rfc_method(conn, 'BAPI_USER_CREATE1', user_params)
if user_exists:
# check for address changes when user exists
user_no_changes = all((user_detail.get('ADDRESS')).get(k) == v for k, v in (user_params.get('ADDRESS')).items())
if not user_no_changes or force:
raw = call_rfc_method(conn, 'BAPI_USER_CHANGE', user_params)
call_rfc_method(conn, 'BAPI_USER_ACTGROUPS_ASSIGN', user_role_assignment_build_rfc_params(roles, username))
call_rfc_method(conn, 'BAPI_USER_PROFILES_ASSIGN', user_profile_assignment_build_rfc_params(profiles, username))
if state == "unlock":
if user_exists:
raw = call_rfc_method(conn, 'BAPI_USER_UNLOCK', {'USERNAME': username})
if state == "lock":
if user_exists:
raw = call_rfc_method(conn, 'BAPI_USER_LOCK', {'USERNAME': username})
# analyse return value
if raw != '':
analysed = return_analysis(raw)
result['out'] = raw
result['changed'] = analysed[0]['change']
for msgs in raw['RETURN']:
if count > 0:
result['msg'] = result['msg'] + '\n'
result['msg'] = result['msg'] + msgs['MESSAGE']
count = count + 1
if analysed[1]['failed']:
module.fail_json(**result)
else:
result['msg'] = "No changes where made."
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,228 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Rainer Leber <rainerleber@gmail.com>
# 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
DOCUMENTATION = r'''
---
module: sapcar_extract
short_description: Manages SAP SAPCAR archives
version_added: "1.0.0"
description:
- Provides support for unpacking C(sar)/C(car) files with the SAPCAR binary from SAP and pulling
information back into Ansible.
options:
path:
description: The path to the SAR/CAR file.
type: path
required: true
dest:
description:
- The destination where SAPCAR extracts the SAR file. Missing folders will be created.
If this parameter is not provided, it will unpack in the same folder as the SAR file.
type: path
binary_path:
description:
- The path to the SAPCAR binary, for example, C(/home/dummy/sapcar) or C(https://myserver/SAPCAR).
If this parameter is not provided, the module will look in C(PATH).
type: path
signature:
description:
- If C(true), the signature will be extracted.
default: false
type: bool
security_library:
description:
- The path to the security library, for example, C(/usr/sap/hostctrl/exe/libsapcrytp.so), for signature operations.
type: path
manifest:
description:
- The name of the manifest.
default: "SIGNATURE.SMF"
type: str
remove:
description:
- If C(true), the SAR/CAR file will be removed. B(This should be used with caution!)
default: false
type: bool
author:
- Rainer Leber (@RainerLeber)
notes:
- Always returns C(changed=true) in C(check_mode).
'''
EXAMPLES = r"""
- name: Extract SAR file
community.sap_libs.sapcar_extract:
path: "~/source/hana.sar"
- name: Extract SAR file with destination
community.sap_libs.sapcar_extract:
path: "~/source/hana.sar"
dest: "~/test/"
- name: Extract SAR file with destination and download from webserver can be a fileshare as well
community.sap_libs.sapcar_extract:
path: "~/source/hana.sar"
dest: "~/dest/"
binary_path: "https://myserver/SAPCAR"
- name: Extract SAR file and delete SAR after extract
community.sap_libs.sapcar_extract:
path: "~/source/hana.sar"
remove: true
- name: Extract SAR file with manifest
community.sap_libs.sapcar_extract:
path: "~/source/hana.sar"
signature: true
- name: Extract SAR file with manifest and rename it
community.sap_libs.sapcar_extract:
path: "~/source/hana.sar"
manifest: "MyNewSignature.SMF"
signature: true
"""
import os
from tempfile import NamedTemporaryFile
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import open_url
from ansible.module_utils.common.text.converters import to_native
def get_list_of_files(dir_name):
# create a list of file and directories
# names in the given directory
list_of_file = os.listdir(dir_name)
allFiles = list()
# Iterate over all the entries
for entry in list_of_file:
# Create full path
fullPath = os.path.join(dir_name, entry)
# If entry is a directory then get the list of files in this directory
if os.path.isdir(fullPath):
allFiles = allFiles + [fullPath]
allFiles = allFiles + get_list_of_files(fullPath)
else:
allFiles.append(fullPath)
return allFiles
def download_SAPCAR(binary_path, module):
bin_path = None
# download sapcar binary if url is provided otherwise path is returned
if binary_path is not None:
if binary_path.startswith('https://') or binary_path.startswith('http://'):
random_file = NamedTemporaryFile(delete=False)
with open_url(binary_path) as response:
with random_file as out_file:
data = response.read()
out_file.write(data)
os.chmod(out_file.name, 0o700)
bin_path = out_file.name
module.add_cleanup_file(bin_path)
else:
bin_path = binary_path
return bin_path
def check_if_present(command, path, dest, signature, manifest, module):
# manipulating output from SAR file for compare with already extracted files
iter_command = [command, '-tvf', path]
sar_out = module.run_command(iter_command)[1]
sar_raw = sar_out.split("\n")[1:]
if dest[-1] != "/":
dest = dest + "/"
sar_files = [dest + x.split(" ")[-1] for x in sar_raw if x]
# remove any SIGNATURE.SMF from list because it will not unpacked if signature is false
if not signature:
sar_files = [item for item in sar_files if not item.endswith('.SMF')]
# if signature is renamed manipulate files in list of sar file for compare.
if manifest != "SIGNATURE.SMF":
sar_files = [item for item in sar_files if not item.endswith('.SMF')]
sar_files = sar_files + [manifest]
# get extracted files if present
files_extracted = get_list_of_files(dest)
# compare extracted files with files in sar file
present = all(elem in files_extracted for elem in sar_files)
return present
def main():
module = AnsibleModule(
argument_spec=dict(
path=dict(type='path', required=True),
dest=dict(type='path'),
binary_path=dict(type='path'),
signature=dict(type='bool', default=False),
security_library=dict(type='path'),
manifest=dict(type='str', default="SIGNATURE.SMF"),
remove=dict(type='bool', default=False),
),
supports_check_mode=True,
)
rc, out, err = [0, "", ""]
params = module.params
check_mode = module.check_mode
path = params['path']
dest = params['dest']
signature = params['signature']
security_library = params['security_library']
manifest = params['manifest']
remove = params['remove']
bin_path = download_SAPCAR(params['binary_path'], module)
if dest is None:
dest_head_tail = os.path.split(path)
dest = dest_head_tail[0] + '/'
else:
if not os.path.exists(dest):
os.makedirs(dest, 0o755)
if bin_path is not None:
command = [module.get_bin_path(bin_path, required=True)]
else:
try:
command = [module.get_bin_path('sapcar', required=True)]
except Exception as e:
module.fail_json(msg='Failed to find SAPCAR at the expected path or URL "{0}". Please check whether it is available: {1}'
.format(bin_path, to_native(e)))
present = check_if_present(command[0], path, dest, signature, manifest, module)
if not present:
command.extend(['-xvf', path, '-R', dest])
if security_library:
command.extend(['-L', security_library])
if signature:
command.extend(['-manifest', manifest])
if not check_mode:
(rc, out, err) = module.run_command(command, check_rc=True)
changed = True
else:
changed = False
out = "already unpacked"
if remove:
os.remove(path)
module.exit_json(changed=changed, message=rc, stdout=out,
stderr=err, command=' '.join(command))
if __name__ == '__main__':
main()