diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
commit | 975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch) | |
tree | 89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/cisco/ucs | |
parent | Initial commit. (diff) | |
download | ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip |
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/cisco/ucs')
79 files changed, 10566 insertions, 0 deletions
diff --git a/ansible_collections/cisco/ucs/.gitignore b/ansible_collections/cisco/ucs/.gitignore new file mode 100644 index 000000000..3f711c898 --- /dev/null +++ b/ansible_collections/cisco/ucs/.gitignore @@ -0,0 +1,135 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Editor +.vscode/ +collections/ +ansible.cfg +args/ + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/ansible_collections/cisco/ucs/Development.md b/ansible_collections/cisco/ucs/Development.md new file mode 100644 index 000000000..d4596d0fb --- /dev/null +++ b/ansible_collections/cisco/ucs/Development.md @@ -0,0 +1,61 @@ +# cisco.ucs Collection Development Notes + +### Current Development Status + +These object specific modules cover a very small set of UCS Manager managed objects. For UCS objects that do not +have a specific module below use `ucs_managed_objects`. This module accepts either JSON or YAML +when used as a task in a playbook. Review `playbooks/ucs_managed_objects` playbook for examples. + +| Configuration Category | Configuration Task | Module Name | +| ---------------------- | ------------------ | ----------- | +| Objects | | | +| | Any UCS Object | cisco.ucs.ucs_managed_objects | +| Query | | | +| | Query Classes or DNs | cisco.ucs.ucs_query | +| | VLAN Find | cisco.ucs.ucs_vlan_find +| Organizations | | | +| | Organizations | cisco.ucs.ucs_org | +| Servers | | | +| | Graphics Card Policy | cisco.ucs.ucs_graphics_card_policy | +| | Scrub Policy | cisco.ucs.ucs_scrub_policy | +| | Serial Over Lan Policy | cisco.ucs.ucs_serial_over_lan_policy | +| | Service Profile Template | cisco.ucs.ucs_service_profile_template | +| | Service Profile from Template | cisco.ucs.ucs_service_profile_from_template | +| | UUID Suffix Pool | cisco.ucs.ucs_uuid_pool | +| LAN | | | +| | IP Addresses for KVM Access | cisco.ucs.ucs_ip_pool | +| | LAN Connectivity Policy | cisco.ucs.ucs_lan_connectivity | +| | MAC Address Pools | cisco.ucs.ucs_mac_pool | +| | System QOS | cisco.ucs.ucs_system_qos | +| | vNIC Template | cisco.ucs.ucs_vnic_template | +| | VLANs | cisco.ucs.ucs_vlans | +| SAN | | | +| | SAN Connectivity Policy | cisco.ucs.ucs_san_connectivity | +| | vHBA Template | cisco.ucs.ucs_vhba_template | +| | VSANs | cisco.ucs.ucs_vsans | +| | WWN Pool | cisco.ucs.ucs_wwn_pool | +| Storage | | | +| | Disk Group Policy | cisco.ucs.ucs_disk_group_policy | +| | Storage Profile | cisco.ucs.ucs_storage_profile | +| Admin | | | +| | DNS Server | cisco.ucs.ucs_dns_server | +| | NTP Server | cisco.ucs.ucs_ntp_server | +| | Time Zone | cisco.ucs.ucs_timezone | + +### Ansible Development Notes + +Modules in development follow processes documented at http://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html. The modules support ansible-doc and should eventually have integration tests. + +When developing modules in this repository, here are a few helpful commands to sanity check the code and documentation (replace module_name with your module (e.g., intersight_objects)). Ansible modules won't generally be pylint or pycodestyle (PEP8) clean without disabling several of the checks: + ``` + pylint --disable=invalid-name,no-member,too-many-nested-blocks,redefined-variable-type,too-many-statements,too-many-branches,broad-except,line-too-long,missing-docstring,wrong-import-position,too-many-locals,import-error <module_name>.py + + pycodestyle --max-line-length 160 --config /dev/null --ignore E402 <module_name>.py + + ansible-doc <module_name> + ``` + +# Community: + +* We are on Slack (https://ciscoucs.slack.com/) - Slack requires registration, but the ucspython team is open invitation to + anyone. Click [here](https://ucspython.herokuapp.com) to register diff --git a/ansible_collections/cisco/ucs/FILES.json b/ansible_collections/cisco/ucs/FILES.json new file mode 100644 index 000000000..639a02b6f --- /dev/null +++ b/ansible_collections/cisco/ucs/FILES.json @@ -0,0 +1,670 @@ +{ + "files": [ + { + "name": ".", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "misc", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "misc/README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e61dd07deb821f7d7de15f2667c7b8c0a636efab8ad63484badfeb37ad6d7796", + "format": 1 + }, + { + "name": "misc/Impact DNW07 UCS Ansible Collection Lab Guide.pdf", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8cce3d380ece9706c078a39e2ab729a555d585e546c12acd4d247d78ff3dfb7f", + "format": 1 + }, + { + "name": "requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "aa3e8ff6c09414944bff4bf66cd0d1b885333c1ea305c1aa7b4dde48a1abee1e", + "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/ucs.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9a91b29311e81b8c9db22d6fa74e6aebea262e57ec4b3e8912103e5efbe90328", + "format": 1 + }, + { + "name": "plugins/README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c82ee692702ec1dd604cdbc38ff252114e5204e1b0627045a66c9451e7a918ac", + "format": 1 + }, + { + "name": "plugins/module_utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/ucs.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1c2bb177e2a14aada0ef9921533ed78813e7969b77b5e5aa25f383e21e94f5e1", + "format": 1 + }, + { + "name": "plugins/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules/ucs_vsans.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8919b0519d1083b08e27d8df96f3823612359c9bb059547794b38272962ba242", + "format": 1 + }, + { + "name": "plugins/modules/ucs_lan_connectivity.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fbdbaf175df169e342ce705a9bb681341a65e7bff4bb56b9c4d33d5203f1252c", + "format": 1 + }, + { + "name": "plugins/modules/ucs_system_qos.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "80b949ac3deb50592098a49ce517b962a3972c85f9732961431e5c667440d366", + "format": 1 + }, + { + "name": "plugins/modules/ucs_managed_objects.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b60efa21bcd6162f8f2b30e21e8456e1babfa40d9d22e90c4a5737c7be5d71d0", + "format": 1 + }, + { + "name": "plugins/modules/ucs_san_connectivity.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1a33a74d1ab46735686d97ab5abfdac74965f9dcf3f442a7a222e1519a6a2b64", + "format": 1 + }, + { + "name": "plugins/modules/ucs_serial_over_lan_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2fc8fca5ddd9a070486e6507a26e6fb3286de264cc1ffcf9548637109da23e77", + "format": 1 + }, + { + "name": "plugins/modules/ucs_wwn_pool.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "64fd4588ad5f970d9ebb7326d0cbde40c38a7dc243252e0fd55a8118e162f670", + "format": 1 + }, + { + "name": "plugins/modules/ucs_vhba_template.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2f49fb8de46c89971bf97d201a790f63c3e90e962f05260b65b4e05d76013f33", + "format": 1 + }, + { + "name": "plugins/modules/ucs_vlans.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "44833390c6dca5c4d84ea741b011ebdd8048a8d3ae08f56b092f94f407a2819d", + "format": 1 + }, + { + "name": "plugins/modules/ucs_sp_vnic_order.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3b1f5b666900e81ed47c0fc49c864d9ca3e325ae83e123110f1f95a34b0d110e", + "format": 1 + }, + { + "name": "plugins/modules/ucs_disk_group_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b2079621e3ec6b509006acd22ac9a422191926cfd34315ca8c0af843cdb96870", + "format": 1 + }, + { + "name": "plugins/modules/ucs_ntp_server.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c87d20311dc4520fef184a523657bddecc1659912bcba747da49551c77c64dba", + "format": 1 + }, + { + "name": "plugins/modules/ucs_scrub_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "efc721b747f2955a998ea67c595286a94ef983b71cced9eb10ca16378a488099", + "format": 1 + }, + { + "name": "plugins/modules/ucs_uuid_pool.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "937ed44aca4af4f9cc3795850d0b6faa283d0f511461e7a9842b1b08d1723e0e", + "format": 1 + }, + { + "name": "plugins/modules/ucs_dns_server.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "737d20161c466848d0d4fef11cf989a70c37a8b1255c76ed77f74cdc56748458", + "format": 1 + }, + { + "name": "plugins/modules/ucs_vlan_find.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "efcc0bd315ecfc7404b4f50bfa8416d7e8bc2329278b5e5343dc63c71c5af760", + "format": 1 + }, + { + "name": "plugins/modules/ucs_graphics_card_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a635e301c5a42ab7d3b93d107638bdfa13256926f7507a4386ba5afee2a569e3", + "format": 1 + }, + { + "name": "plugins/modules/ucs_org.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8b50c9af02932abe59b7dc3044a8146ea2393de435932ba83450ca4174608b58", + "format": 1 + }, + { + "name": "plugins/modules/ucs_vnic_template.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "da4b4e7c608e690d4b53f6e1972c3b3bbd3782245b303a847dee6db8ce8ecd71", + "format": 1 + }, + { + "name": "plugins/modules/ucs_storage_profile.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "60ed97f411860171f579fe1b1d31716db66cce08a69e9e9752fb3484bd97990f", + "format": 1 + }, + { + "name": "plugins/modules/ucs_service_profile_association.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cb306f31e4e0f23519d6af4de72a1eda5382c6d5bc79b697c4c140978a7635dc", + "format": 1 + }, + { + "name": "plugins/modules/ucs_vlan_to_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e34928b3c067dae6da852ec16183db1f4464f4ec4c6ebf06e6e2f5c7607777ef", + "format": 1 + }, + { + "name": "plugins/modules/ucs_timezone.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ac8518370ee6f0f6d768f9464e13011269d55d4032a9b767228b0d27a7f0fb03", + "format": 1 + }, + { + "name": "plugins/modules/ucs_ip_pool.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9990d779de27b83f6449e06d933d7d9a2d0344ad0b871ee83f01cdc71f7cb1cb", + "format": 1 + }, + { + "name": "plugins/modules/ucs_service_profile_from_template.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2c15389a8e9aa4fb284e328a49e0b311eb5d13670bbd82322aca19ef4c2c16a1", + "format": 1 + }, + { + "name": "plugins/modules/ucs_query.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c41609719a3501d378dee7c3d64ac436c5b7b58459ec4151820b9b6e9f0c640b", + "format": 1 + }, + { + "name": "plugins/modules/ucs_server_maintenance.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7909713ae7541e07c14a47fdeef30741b35424a49be8688a582aee60d80e2c07", + "format": 1 + }, + { + "name": "plugins/modules/ucs_service_profile_template.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "945abd9f0855f640afccdba195a9b0e8ad15f75adf7e8c8ebfda15adbb87d4c0", + "format": 1 + }, + { + "name": "plugins/modules/ucs_mac_pool.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "13a00a3f340c16e71a3f907f499db455b9c10e12034db6dc8536203898f99afd", + "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": "aa2e34142e05cac25ebdf95790050adccca8011fdeb0aec65425c2547fd80f39", + "format": 1 + }, + { + "name": "roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/ucs_storage_profile.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3777b58c91917d04a3ec4dc9d279489dd865c2fcb269aa43ceb23bea968937c8", + "format": 1 + }, + { + "name": "playbooks/ucs_wwn_pool.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "191db96eb7a6adb4a2bc82c95aaff7cd20fdba0a532d1dd1e2e50dc97f65feb2", + "format": 1 + }, + { + "name": "playbooks/ucs_serial_over_lan_policy.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6dcfa1cbcb46ca123dd97ef08c5c434753da47c7ed685991b77ea6464f743370", + "format": 1 + }, + { + "name": "playbooks/ucs_vhba_template.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "00cda823af22cc1603f448616cac0c76fbb6ca2bde70a2ffbb4b69a346c4d43b", + "format": 1 + }, + { + "name": "playbooks/ucs_lan_connectivity.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "859208b19078ba8862402da21e574e7878b03a0ad941110962e153472a19ca25", + "format": 1 + }, + { + "name": "playbooks/ucs_mac_pool.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1b08ecac7d4b2391bc679199a2048ef21e4c530bde0657540c30c18c69f2b6cf", + "format": 1 + }, + { + "name": "playbooks/ucs_vsans.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "79074b78bc316bae4834d7bf6e24e6483cb7d5cb1008f4989ef2cbcb37b30280", + "format": 1 + }, + { + "name": "playbooks/ucs_system_qos.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d4d6c5b5c72bcd66ad8dcba66c8453b395fb3bfa9fe0fc72a6bc3b0251c46dfa", + "format": 1 + }, + { + "name": "playbooks/ucs_ntp_server.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "77299c4830b65606abc39d720469981362df6dd498e88baee7cc989616982317", + "format": 1 + }, + { + "name": "playbooks/ucs_service_profile_from_template.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9e7cf62de403f1ca24e6048ecab5a4de7f9fbcc366a65e457e85b6f530b23937", + "format": 1 + }, + { + "name": "playbooks/ucs_vlan_find.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4314b028607d257cd71018545d70198f0514ed237f6338b9c66ee8363691629b", + "format": 1 + }, + { + "name": "playbooks/ucs_vlan_to_group.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "96d7f24656cbd38da33aef2570ae605a64340152396bfa63cd09d43faf3b3c59", + "format": 1 + }, + { + "name": "playbooks/ucs_uuid_pool.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7241264e900011016678b997bbf831f1d6bc779108e67634f3b51e2e368d2556", + "format": 1 + }, + { + "name": "playbooks/ucs_service_profile_association.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "14dac7f4aaacfafe69fda6574416fa6fd37ca2d84df4880b8b9f2cca85513c82", + "format": 1 + }, + { + "name": "playbooks/example_playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7510073bfa856bd46084f6f64817f4a4dc918eb41b06305cf6441697be4700ae", + "format": 1 + }, + { + "name": "playbooks/roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/roles/servers", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/roles/servers/service_profile_templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/roles/servers/service_profile_templates/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/roles/servers/service_profile_templates/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "76a596b849386fd161f9a0404954c6a84ce372e13ffbbc9cd8184a8f4c844599", + "format": 1 + }, + { + "name": "playbooks/roles/servers/service_profiles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/roles/servers/service_profiles/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/roles/servers/service_profiles/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b03e8aa16fddd905499d0bb4fcbe84f95dfadf9cce767d321f33205b64419645", + "format": 1 + }, + { + "name": "playbooks/roles/servers/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/roles/servers/defaults/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/roles/servers/defaults/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bbe7b86819348adaf8076cc9633535a80b858b957860d97cddc71acf0018e7c3", + "format": 1 + }, + { + "name": "playbooks/ucs_managed_objects.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3c6b7a20e2c330a5cade1a53f67534296714456abd99f1371aac62fad178219e", + "format": 1 + }, + { + "name": "playbooks/ucs_vnic_template.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cbc6add894923e8a6246a94f1f2381de211b1483f733c5a7c881f52ad859197e", + "format": 1 + }, + { + "name": "playbooks/ucs_scrub_policy.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3e03124d0180685b351cc39dbcb4d2c7070204ce812753150ccfd49c7bf5ed8e", + "format": 1 + }, + { + "name": "playbooks/ucs_san_connectivity.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bea00638d91b6ed884bad08f631643894a6336dd85c9ea594d089c52017fd60c", + "format": 1 + }, + { + "name": "playbooks/ucs_server_maintenance.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f66d3bafb2811978d47b31d9148ace000c70045809e6ba70c1882cc587677f42", + "format": 1 + }, + { + "name": "playbooks/fw_download_config_hfp.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "17cd79e71470587a70d01b6f3d80afc8d3e4fe51daa4dd1f72b2bc2f9e8ee6c2", + "format": 1 + }, + { + "name": "playbooks/ucs_vlans.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0525339a713095f1cd72688203c1bf7e09af518ff34449f9dfa6c150d0754155", + "format": 1 + }, + { + "name": "playbooks/ucs_timezone.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e9ae6a7429ee3030b96caee58a8e417a23b919023d892ad00143dc5206d0ff87", + "format": 1 + }, + { + "name": "playbooks/inventory", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fb8cee99e65566c3554036f8954a343bbeb1028c0400fb39f13d446745eb15f3", + "format": 1 + }, + { + "name": "playbooks/ucs_service_profile_template.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a6fea5f68845578d69aaf34883be9d6f5bd846f0f22815bdbbd9b39e91c40d48", + "format": 1 + }, + { + "name": "playbooks/ucs_query.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f67b361a468838e509c252e739b37c85978821ce4dafef6fb5a0645321bf769e", + "format": 1 + }, + { + "name": "playbooks/ucs_ip_pool.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "705bff291c1ef759c19110c2c941269470a0e40b1e8ea0831a15dcc7611c37b3", + "format": 1 + }, + { + "name": "playbooks/ucs_graphics_card_policy.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "65ef05804963545ee353e13395751b63177949b6713003f4124502a40109e9a4", + "format": 1 + }, + { + "name": "playbooks/ucs_sp_vnic_order.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4732febd59e04f510c13372427c2351dc7d681cc4ea9281d17efa1311d6f47c3", + "format": 1 + }, + { + "name": "playbooks/ucs_org.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8d66cd58db0b4ad70f2ff4ba8e0dd33061b2d78ad7830a680d917e309cefc892", + "format": 1 + }, + { + "name": "playbooks/sandbox_inventory", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f39d48e2ce1d77a8a2c7c48580e474470793757faa3f6b49adc5382277ba1182", + "format": 1 + }, + { + "name": "playbooks/ucs_dns_server.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1d6622fbe1de63bc74e280c556ea2c1047b8dcb76f3fc2c3e808647f142c4c2d", + "format": 1 + }, + { + "name": "playbooks/ucs_disk_group_policy.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ae1f622dfcb5d1c1a31244432ea35b1b1fbba0da585ad19d2ba7321a52168c75", + "format": 1 + }, + { + "name": "playbooks/server_deploy.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "087cece69b2bd12dcbe7d4b98b5783a94bd440c24ba88e9dcfe01fdd8b4790d5", + "format": 1 + }, + { + "name": "docs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "eebb8fd73b6f1040b24bb9176c6c28484dab8833149bff13abb25455419eb01f", + "format": 1 + }, + { + "name": ".gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fbbde23b486e876ee1aab3a0053c76df8b996c6e0f15502013fff9c8ecdb4fcc", + "format": 1 + }, + { + "name": "LICENSE.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1babfc8408a4d1d6f39b9c01c763e313b3e0f764bf90a11fc9151a2ae556b12d", + "format": 1 + }, + { + "name": "Development.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9877e1ab8e3670990bf466609609750a6f38c54f8e5ddb9761fe5f7966bfd93d", + "format": 1 + } + ], + "format": 1 +}
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/LICENSE.txt b/ansible_collections/cisco/ucs/LICENSE.txt new file mode 100644 index 000000000..02f660920 --- /dev/null +++ b/ansible_collections/cisco/ucs/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Cisco Systems, Inc. and/or its affiliates + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ansible_collections/cisco/ucs/MANIFEST.json b/ansible_collections/cisco/ucs/MANIFEST.json new file mode 100644 index 000000000..83036def8 --- /dev/null +++ b/ansible_collections/cisco/ucs/MANIFEST.json @@ -0,0 +1,34 @@ +{ + "collection_info": { + "namespace": "cisco", + "name": "ucs", + "version": "1.8.0", + "authors": [ + "David Soper (@dsoper2)", + "John McDonough (@movinalot)" + ], + "readme": "README.md", + "tags": [ + "cisco", + "ucs" + ], + "description": "modules for Cisco UCS Manager", + "license": [ + "GPL-3.0-or-later" + ], + "license_file": null, + "dependencies": {}, + "repository": "https://github.com/CiscoDevNet/ansible-ucs", + "documentation": "https://developer.cisco.com/site/ucs-dev-center", + "homepage": "https://github.com/CiscoDevNet/ansible-ucs", + "issues": "https://github.com/CiscoDevNet/ansible-ucs" + }, + "file_manifest_file": { + "name": "FILES.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "aa96b36e3d9e7dde362cd2a55ef8ed616efc0a72203cd68c86281fc5042732ae", + "format": 1 + }, + "format": 1 +}
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/README.md b/ansible_collections/cisco/ucs/README.md new file mode 100644 index 000000000..354f46476 --- /dev/null +++ b/ansible_collections/cisco/ucs/README.md @@ -0,0 +1,101 @@ +# Ansible Collection - cisco.ucs + +Ansible collection for managing and automing Cisco UCS Manager envrionments. Modules and roles are provided for common Cisco UCS Manager tasks. + +* Note: This collection is not compatible with versions of Ansible before v2.8. + +## Requirements + +- Ansible v2.8 or newer +- UCSM Python SDK (ucsmsdk) + +## Install +- ansible must be installed +``` +sudo pip install ansible +``` +- ucsmsdk must be installed +``` +sudo pip install ucsmsdk +``` +We recommend verifying the ucsmsdk can connect to the domains you want to manage with Ansible. Here is an example connection test using python: +``` +# python +Python 2.7.14 (default, Apr 27 2018, 14:31:56) +[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux2 +Type "help", "copyright", "credits" or "license" for more information. +>>> from ucsmsdk import ucshandle +>>> handle = ucshandle.UcsHandle(ip='172.22.250.236', username='admin', password='password') +>>> handle.login() +True +``` + +## Usage +Once Ansible is installed you can create inventory files and playbooks to manage your UCS domains. Each module supports ansible-doc which includes example usage: +``` +# ansible-doc cisco.ucs.ucs_vlans +<snip> +EXAMPLES: +- name: Configure VLAN + cisco.ucs.ucs_vlans: + hostname: 172.16.143.150 + username: admin + password: password + name: vlan2 + id: '2' + native: 'yes' +``` +This repository includes a playbooks directory with examples including an inventory file that can be edited with information for the UCSM domain you want to configure: +``` +# vi inventory +[ucs] +13.58.22.56 + +[ucs:vars] +username=admin +password=password +``` +An example_playbook.yml playbook is included to test VLAN configuration on the UCSM domain given in the inventory file: +``` +# vi example_playbook.yml + +--- +# Example Playbook: VLAN configuration using the [ucs] hosts group +- hosts: ucs + connection: local + collections: + - cisco.ucs + gather_facts: false + tasks: + - name: Configure VLAN + ucs_vlans: + hostname: "{{ inventory_hostname }}" + username: "{{ username | default(omit) }}" + password: "{{ password }}" + state: "{{ state | default(omit) }}" + name: vlan2 + id: '2' + native: 'no' + delegate_to: localhost +``` +Ansible will use data from the inventory file for the hostname and other variables above. Multiple UCSM domains can be listed in the inventory file and Ansible will configure all the listed domains in parallel using host specific data. + +The ansible-playbook command can be used to run the above playbook and inventory file: +``` +# ansible-playbook -i inventory example_playbook.yml + +PLAY [ucs] ********************************************************************* + +TASK [Configure VLAN] ********************************************************** +ok: [13.58.22.56 -> localhost] + +PLAY RECAP ********************************************************************* +13.58.22.56 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +``` + +A more detailed configuration example is provided in the server_deploy.yml playbook. + +# Community: + +* We are on Slack (https://ciscoucs.slack.com/) - Slack requires registration, but the ucspython team is open invitation to + anyone. Click [here](https://ucspython.herokuapp.com) to register. diff --git a/ansible_collections/cisco/ucs/meta/runtime.yml b/ansible_collections/cisco/ucs/meta/runtime.yml new file mode 100644 index 000000000..aba42e9b2 --- /dev/null +++ b/ansible_collections/cisco/ucs/meta/runtime.yml @@ -0,0 +1 @@ +requires_ansible: ">=2.9" diff --git a/ansible_collections/cisco/ucs/misc/Impact DNW07 UCS Ansible Collection Lab Guide.pdf b/ansible_collections/cisco/ucs/misc/Impact DNW07 UCS Ansible Collection Lab Guide.pdf Binary files differnew file mode 100644 index 000000000..6c7f30408 --- /dev/null +++ b/ansible_collections/cisco/ucs/misc/Impact DNW07 UCS Ansible Collection Lab Guide.pdf diff --git a/ansible_collections/cisco/ucs/misc/README.md b/ansible_collections/cisco/ucs/misc/README.md new file mode 100644 index 000000000..934c8a1d8 --- /dev/null +++ b/ansible_collections/cisco/ucs/misc/README.md @@ -0,0 +1,3 @@ +# Miscellaneous Items + +This folder contains miscellaneous items related to the collection. Trainings, DevNet Workshop giudes, etc.
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/playbooks/example_playbook.yml b/ansible_collections/cisco/ucs/playbooks/example_playbook.yml new file mode 100644 index 000000000..36e728378 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/example_playbook.yml @@ -0,0 +1,18 @@ +--- +# Example Playbook: VLAN configuration using the [ucs] hosts group +- hosts: ucs + connection: local + collections: + - cisco.ucs + gather_facts: false + tasks: + - name: Configure VLAN + ucs_vlans: + hostname: "{{ inventory_hostname }}" + username: "{{ username | default(omit) }}" + password: "{{ password }}" + state: "{{ state | default(omit) }}" + name: vlan2 + id: '2' + native: 'no' + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/fw_download_config_hfp.yml b/ansible_collections/cisco/ucs/playbooks/fw_download_config_hfp.yml new file mode 100644 index 000000000..32ab0abf7 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/fw_download_config_hfp.yml @@ -0,0 +1,83 @@ +--- +# +# Download firmware to the FI and configure Host Firmware Packages (HFP). +# Uses remote fileshare and scp for FW download (server, remote_path, etc. provided through variables): +# Example vars: +# [ucs:vars] +# remote_ip_address=172.28.224.77 +# remote_fw_path=/mnt/SHARE/ISOS/UCS_Code/4.1 +# remote_username=... +# +# The fw_bundles variable is a list that can be passed on the command line. +# Example: +# ansible-playbook ... -e '{"fw_bundles": ["ucs-k9-bundle-b-series.4.1.2b.B.bin"]}' +# +- hosts: ucs + connection: local + gather_facts: false + vars: + login_info: &login_info + hostname: "{{ inventory_hostname }}" + username: "{{ username | default(omit) }}" + password: "{{ password }}" + tasks: + - name: Download FW to FI + ucs_managed_objects: + <<: *login_info + objects: + - module: ucsmsdk.mometa.firmware.FirmwareCatalogue + class: FirmwareCatalogue + properties: + parent_mo_or_dn: sys + children: + - module: ucsmsdk.mometa.firmware.FirmwareDownloader + class: FirmwareDownloader + properties: + protocol: scp + server: "{{ remote_ip_address }}" + remote_path: "{{ remote_fw_path }}" + file_name: "{{ item }}" + user: "{{ remote_username }}" + pwd: "{{ remote_password }}" + loop: "{{ fw_bundles }}" + delegate_to: localhost + register: download_result + - name: Query and wait for download if needed + cisco.ucs.ucs_query: + <<: *login_info + distinguished_names: "{{ fw_download_dn }}" + loop: "{{ fw_bundles }}" + vars: + fw_download_dn: "sys/fw-catalogue/dnld-{{ item }}" + delegate_to: localhost + register: query_response + # works with warnings: + # until: query_response['objects']["{{ fw_download_dn }}"]['transfer_state'] == 'downloaded' + until: query_response.objects is search('downloaded') + # retry every 60 seconds for 20 minutes + delay: 60 + retries: 20 + # regular escapes in a set variable + - set_fact: + match_str: 'ucs-.*?\.(?P<rel>\d\.\d)\.(?P<patch>.*)\.' + # escape the escapes when used directly in strings + - set_fact: + blade_version: "{{ item | regex_replace(match_str + 'B\\.bin', '\\g<rel>(\\g<patch>)B') }}" + loop: "{{ fw_bundles }}" + when: item | regex_search(match_str + 'B\\.bin') + - set_fact: + rack_version: "{{ item | regex_replace(match_str + 'C\\.bin', '\\g<rel>(\\g<patch>)C') }}" + loop: "{{ fw_bundles }}" + when: item | regex_search(match_str + 'C\\.bin') + - name: Config Host FW Package + ucs_managed_objects: + <<: *login_info + objects: + - module: ucsmsdk.mometa.firmware.FirmwareComputeHostPack + class: FirmwareComputeHostPack + properties: + parent_mo_or_dn: org-root + name: ansible-latest + blade_bundle_version: "{{ blade_version | default(omit) }}" + rack_bundle_version: "{{ rack_version | default(omit) }}" + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/inventory b/ansible_collections/cisco/ucs/playbooks/inventory new file mode 100644 index 000000000..aed6af36e --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/inventory @@ -0,0 +1,6 @@ +[ucs] +13.58.22.56 + +[ucs:vars] +username=admin +password=password diff --git a/ansible_collections/cisco/ucs/playbooks/roles/servers/defaults/tasks/main.yml b/ansible_collections/cisco/ucs/playbooks/roles/servers/defaults/tasks/main.yml new file mode 100644 index 000000000..ab28cd57a --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/roles/servers/defaults/tasks/main.yml @@ -0,0 +1,136 @@ +--- +- name: "Configure default IP Pool" + vars: + # Create an anchor for login_info that can be used throughout the file + login_info: &login_info + hostname: "{{ hostname }}" + username: "{{ username | default(omit) }}" + password: "{{ password | default('password') }}" + state: "{{ state | default(omit) }}" + cisco.ucs.ucs_ip_pool: + <<: *login_info + name: ext-mgmt + ipv4_blocks: + - first_addr: 198.18.0.20 + last_addr: 198.18.0.40 + subnet_mask: 255.255.255.0 + default_gw: 198.18.0.1 + tags: ip_pool +- name: "Configure default MAC Pool" + cisco.ucs.ucs_mac_pool: + <<: *login_info + name: default + first_addr: 00:25:B5:DE:30:00 + last_addr: 00:25:B5:DE:32:FF + tags: mac_pool +- name: "Configure default UUID Pool" + cisco.ucs.ucs_uuid_pool: + <<: *login_info + name: default + first_uuid: 0000-000000000001 + last_uuid: 0000-000000000078 + tags: uuid_pool +- name: "Configure default Virtual Media Policy" + cisco.ucs.ucs_managed_objects: + <<: *login_info + objects: + - module: ucsmsdk.mometa.cimcvmedia.CimcvmediaMountConfigPolicy + class: CimcvmediaMountConfigPolicy + properties: + parent_mo_or_dn: org-root + name: "{{ vmedia_policy }}" + children: + - module: ucsmsdk.mometa.cimcvmedia.CimcvmediaConfigMountEntry + class: CimcvmediaConfigMountEntry + properties: + device_type: cdd + image_file_name: "{{ image_file_name | default('centos7.2-boot-ks.iso') }}" + image_path: "{{ image_path | default('/home/public') }}" + mapping_name: cdd-nfs + mount_protocol: nfs + remote_ip_address: "{{ remote_ip_address | default('198.18.134.242') }}" + tags: virtual_media +- name: "Configure default Boot Order Policy" + cisco.ucs.ucs_managed_objects: + <<: *login_info + objects: + - module: ucsmsdk.mometa.lsboot.LsbootPolicy + class: LsbootPolicy + properties: + parent_mo_or_dn: org-root + boot_mode: legacy + enforce_vnic_name: 'yes' + name: vmedia-local + reboot_on_update: 'no' + children: + - module: ucsmsdk.mometa.lsboot.LsbootVirtualMedia + class: LsbootVirtualMedia + properties: + access: read-only-remote-cimc + lun_id: '0' + order: '1' + - module: ucsmsdk.mometa.lsboot.LsbootStorage + class: LsbootStorage + properties: + order: '2' + children: + - module: ucsmsdk.mometa.lsboot.LsbootLocalStorage + class: LsbootLocalStorage + properties: {} + children: + - module: ucsmsdk.mometa.lsboot.LsbootDefaultLocalImage + class: LsbootDefaultLocalImage + properties: + order: '2' + tags: boot_order +- name: "Configure default Server Pool" + cisco.ucs.ucs_managed_objects: + <<: *login_info + objects: + - module: ucsmsdk.mometa.compute.ComputePool + class: ComputePool + properties: + parent_mo_or_dn: org-root + name: default + children: + - module: ucsmsdk.mometa.compute.ComputePooledSlot + class: ComputePooledSlot + properties: + chassis_id: '1' + slot_id: '1' + - module: ucsmsdk.mometa.compute.ComputePooledSlot + class: ComputePooledSlot + properties: + chassis_id: '1' + slot_id: '2' + - module: ucsmsdk.mometa.compute.ComputePooledSlot + class: ComputePooledSlot + properties: + chassis_id: '1' + slot_id: '3' + - module: ucsmsdk.mometa.compute.ComputePooledSlot + class: ComputePooledSlot + properties: + chassis_id: '1' + slot_id: '4' + - module: ucsmsdk.mometa.compute.ComputePooledSlot + class: ComputePooledSlot + properties: + chassis_id: '1' + slot_id: '5' + - module: ucsmsdk.mometa.compute.ComputePooledSlot + class: ComputePooledSlot + properties: + chassis_id: '1' + slot_id: '6' + - module: ucsmsdk.mometa.compute.ComputePooledSlot + class: ComputePooledSlot + properties: + chassis_id: '1' + slot_id: '7' + - module: ucsmsdk.mometa.compute.ComputePooledSlot + class: ComputePooledSlot + properties: + chassis_id: '1' + slot_id: '8' + tags: server_pool diff --git a/ansible_collections/cisco/ucs/playbooks/roles/servers/service_profile_templates/tasks/main.yml b/ansible_collections/cisco/ucs/playbooks/roles/servers/service_profile_templates/tasks/main.yml new file mode 100644 index 000000000..c61432807 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/roles/servers/service_profile_templates/tasks/main.yml @@ -0,0 +1,18 @@ +--- +- name: "Configure {{ template_name }} Service Profile Template" + vars: + # Create an anchor for login_info that can be used throughout the file + login_info: &login_info + hostname: "{{ hostname }}" + username: "{{ username | default(omit) }}" + password: "{{ password | default('password') }}" + state: "{{ state | default(omit) }}" + cisco.ucs.ucs_service_profile_template: + <<: *login_info + name: "{{ template_name }}" + template_type: "{{ template_type }}" + host_firmware_package: "{{ host_firmware_package }}" + server_pool: default + vmedia_policy: "{{ vmedia_policy }}" + boot_policy: vmedia-local + maintenance_policy: "{{ maintenance_policy }}" diff --git a/ansible_collections/cisco/ucs/playbooks/roles/servers/service_profiles/tasks/main.yml b/ansible_collections/cisco/ucs/playbooks/roles/servers/service_profiles/tasks/main.yml new file mode 100644 index 000000000..ac1f920b4 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/roles/servers/service_profiles/tasks/main.yml @@ -0,0 +1,14 @@ +--- +- name: "Configure {{ profile_name }} Service Profiles from template {{ template_name }}" + vars: + # Create an anchor for login_info that can be used throughout the file + login_info: &login_info + hostname: "{{ hostname }}" + username: "{{ username | default(omit) }}" + password: "{{ password | default('password') }}" + state: "{{ state | default(omit) }}" + cisco.ucs.ucs_service_profile_from_template: + <<: *login_info + name: "{{ profile_name }}-{{ '%d' | format(item) }}" + source_template: "{{ template_name }}" + loop: "{{ range(1, num_profiles|int + 1) | list }}" diff --git a/ansible_collections/cisco/ucs/playbooks/sandbox_inventory b/ansible_collections/cisco/ucs/playbooks/sandbox_inventory new file mode 100644 index 000000000..4a3a2b26f --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/sandbox_inventory @@ -0,0 +1,6 @@ +[ucs] +ucs1 ucs_hostname=sandbox-ucsm1.cisco.com + +[ucs:vars] +ucs_username=admin +ucs_password=C1sco12345 diff --git a/ansible_collections/cisco/ucs/playbooks/server_deploy.yml b/ansible_collections/cisco/ucs/playbooks/server_deploy.yml new file mode 100644 index 000000000..a7d62439c --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/server_deploy.yml @@ -0,0 +1,39 @@ +--- +# +# Configure UCS, Associate Service Profiles, and Install OS +# +# The hosts group used is provided by the group variable or defaulted to 'ucs'. +# You can specify a specific host (or host group) on the command line: +# ansible-playbook ... -e group=<your host group> +# e.g., ansible-playbook server_profiles.yml -e group=TME_Demo +# +- hosts: "{{ group | default('ucs') }}" + connection: local + gather_facts: false + vars: + # The UCS domain hostname can be set in the inventory or on the command line as needed + hostname: "{{ inventory_hostname }}" + # Names for Service Profiles, Policies, and number of Profiles + template_name: auto-template + template_type: updating-template + host_firmware_package: ansible-latest + maintenance_policy: user-ack + vmedia_policy: cdd-nfs + profile_name: auto-profile + num_profiles: 2 + tasks: + - block: + # Configure default pools and other settings + - import_role: + name: servers/defaults + tags: defaults + # Configure Service Profile Template with default settings + - import_role: + name: servers/service_profile_templates + tags: templates + # Create Service Profiles from template and associate + - import_role: + name: servers/service_profiles + tags: profiles + # Use the localhost's environment and Python + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_disk_group_policy.yml b/ansible_collections/cisco/ucs/playbooks/ucs_disk_group_policy.yml new file mode 100644 index 000000000..877bdb93c --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_disk_group_policy.yml @@ -0,0 +1,74 @@ +--- +# Example Playbook: cisco.ucs.ucs_disk_group_policy +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure Disk Group Policy + cisco.ucs.ucs_disk_group_policy: + <<: *login_info + name: DEE-DG + raid_level: mirror + configuration_mode: manual + manual_disks: + - slot_num: '1' + role: normal + - slot_num: '2' + role: normal + org_dn: org-root/org-level1 + + - name: Remove Disk from Policy + cisco.ucs.ucs_disk_group_policy: + <<: *login_info + name: DEE-DG + description: Testing Ansible + raid_level: stripe + configuration_mode: manual + manual_disks: + - slot_num: '1' + role: normal + - slot_num: '2' + role: normal + state: absent + virtual_drive: + access_policy: platform-default + io_policy: direct + strip_size: 64KB + org_dn: org-root/org-level1 + + - name: Remove Disk Group Policy + cisco.ucs.ucs_disk_group_policy: + <<: *login_info + name: DEE-DG + state: absent + org_dn: org-root/org-level1 + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_dns_server.yml b/ansible_collections/cisco/ucs/playbooks/ucs_dns_server.yml new file mode 100644 index 000000000..5ded54b24 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_dns_server.yml @@ -0,0 +1,33 @@ +--- +# Example Playbook: cisco.ucs.ucs_dns_server +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Configure DNS server + cisco.ucs.ucs_dns_server: + <<: *login_info + dns_server: 10.10.10.10 + description: DNS Server IP address + state: present + delegate_to: localhost + + - name: Remove DNS server + cisco.ucs.ucs_dns_server: + <<: *login_info + dns_server: 10.10.10.10 + state: absent + delegate_to: localhost
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_graphics_card_policy.yml b/ansible_collections/cisco/ucs/playbooks/ucs_graphics_card_policy.yml new file mode 100644 index 000000000..9a2cab855 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_graphics_card_policy.yml @@ -0,0 +1,105 @@ +--- +# Example Playbook: cisco.ucs_graphics_card_policy +- hosts: ucs + connection: local + gather_facts: false + + vars: + org: AnsibleOrg + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: "{{ org }}" + parent_org_path: root + description: "Org {{ org }}" + state: present + delegate_to: localhost + + - name: Add Graphics Card Policy + cisco.ucs.ucs_graphics_card_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + description: Any Graphics Mode Policy + name: prod_graphics + graphics_card_mode: any-configuration + delegate_to: localhost + + - name: Check Graphics Card Policy + cisco.ucs.ucs_graphics_card_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + description: Any Graphics Mode Policy + name: prod_graphics + graphics_card_mode: any-configuration + delegate_to: localhost + check_mode: True + + - name: Add Graphics Card Policy + cisco.ucs.ucs_graphics_card_policy: + <<: *login_info + state: present + description: Any Graphics Mode Policy + name: any_graphics + graphics_card_mode: any-configuration + delegate_to: localhost + + - name: Idempotent Graphics Card Policy + cisco.ucs.ucs_graphics_card_policy: + <<: *login_info + state: present + description: Any Graphics Mode Policy + name: any_graphics + graphics_card_mode: any-configuration + delegate_to: localhost + + - name: Check Create/Update Graphics Card Policy + cisco.ucs.ucs_graphics_card_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + description: Compute Graphics Mode Policy + name: prod_graphics + graphics_card_mode: compute + delegate_to: localhost + check_mode: true + + - name: Create/Update Graphics Card Policy + cisco.ucs.ucs_graphics_card_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + description: Compute Graphics Mode Policy + name: prod_graphics + graphics_card_mode: compute + delegate_to: localhost + + - name: Delete Graphics Card Policy + cisco.ucs.ucs_graphics_card_policy: + <<: *login_info + state: absent + name: any_graphics + delegate_to: localhost + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: "{{ org }}" + parent_org_path: root + state: absent + delegate_to: localhost
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_ip_pool.yml b/ansible_collections/cisco/ucs/playbooks/ucs_ip_pool.yml new file mode 100644 index 000000000..f1d9da989 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_ip_pool.yml @@ -0,0 +1,78 @@ +--- +# Example Playbook: cisco.ucs.ucs_ip_pool +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure IPv4 and IPv6 address pool + cisco.ucs.ucs_ip_pool: + <<: *login_info + name: ip-pool-01 + org_dn: org-root/org-level1 + ipv4_blocks: + - first_addr: 192.168.10.1 + last_addr: 192.168.10.20 + subnet_mask: 255.255.255.128 + default_gw: 192.168.10.2 + - first_addr: 192.168.11.1 + last_addr: 192.168.11.20 + subnet_mask: 255.255.255.128 + default_gw: 192.168.11.2 + ipv6_blocks: + - ipv6_first_addr: fe80::1cae:7992:d7a1:ed07 + ipv6_last_addr: fe80::1cae:7992:d7a1:edfe + ipv6_default_gw: fe80::1cae:7992:d7a1:ecff + - ipv6_first_addr: fe80::1cae:7992:d7a1:ec07 + ipv6_last_addr: fe80::1cae:7992:d7a1:ecfe + ipv6_default_gw: fe80::1cae:7992:d7a1:ecff + + - name: Remove IPv4 and IPv6 address pool blocks + cisco.ucs.ucs_ip_pool: + <<: *login_info + name: ip-pool-01 + org_dn: org-root/org-level1 + ipv4_blocks: + - first_addr: 192.168.10.1 + last_addr: 192.168.10.20 + state: absent + ipv6_blocks: + - ipv6_first_addr: fe80::1cae:7992:d7a1:ec07 + ipv6_last_addr: fe80::1cae:7992:d7a1:ecfe + state: absent + + - name: Remove IPv4 and IPv6 address pool + cisco.ucs.ucs_ip_pool: + <<: *login_info + name: ip-pool-01 + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_lan_connectivity.yml b/ansible_collections/cisco/ucs/playbooks/ucs_lan_connectivity.yml new file mode 100644 index 000000000..09903bac7 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_lan_connectivity.yml @@ -0,0 +1,67 @@ +--- +# Example Playbook: cisco.ucs.ucs_lan_connectivity +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure LAN Connectivity Policy + cisco.ucs.ucs_lan_connectivity: + <<: *login_info + name: Cntr-FC-Boot + org_dn: org-root/org-level1 + vnic_list: + - name: eno1 + vnic_template: Cntr-Template + adapter_policy: Linux + - name: eno2 + vnic_template: Container-NFS-A + adapter_policy: Linux + - name: eno3 + vnic_template: Container-NFS-B + adapter_policy: Linux + iscsi_vnic_list: + - name: iSCSIa + overlay_vnic: eno1 + iscsi_adapter_policy: default + vlan_name: Container-MGMT-VLAN + - name: iSCSIb + overlay_vnic: eno3 + iscsi_adapter_policy: default + vlan_name: Container-TNT-A-NFS + + - name: Remove LAN Connectivity Policy + cisco.ucs.ucs_lan_connectivity: + <<: *login_info + name: Cntr-FC-Boot + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_mac_pool.yml b/ansible_collections/cisco/ucs/playbooks/ucs_mac_pool.yml new file mode 100644 index 000000000..a590bdfb3 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_mac_pool.yml @@ -0,0 +1,51 @@ +--- +# Example Playbook: cisco.ucs.ucs_mac_pool +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure MAC address pool + cisco.ucs.ucs_mac_pool: + <<: *login_info + name: mac-A + org_dn: org-root/org-level1 + first_addr: 00:25:B5:00:66:00 + last_addr: 00:25:B5:00:67:F3 + order: sequential + + - name: Remove MAC address pool + cisco.ucs.ucs_mac_pool: + <<: *login_info + name: mac-A + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_managed_objects.yml b/ansible_collections/cisco/ucs/playbooks/ucs_managed_objects.yml new file mode 100644 index 000000000..f36d2b261 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_managed_objects.yml @@ -0,0 +1,124 @@ +--- +# Example Playbook: cisco.ucs.ucs_managed_objects +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure Network Control Policy + cisco.ucs.ucs_managed_objects: + <<: *login_info + objects: + - module: ucsmsdk.mometa.nwctrl.NwctrlDefinition + class: NwctrlDefinition + properties: + parent_mo_or_dn: org-root/org-level1 + cdp: enabled + descr: '' + lldp_receive: enabled + lldp_transmit: enabled + name: Enable-CDP-LLDP + + - name: Remove Network Control Policy + cisco.ucs.ucs_managed_objects: + <<: *login_info + objects: + - module: ucsmsdk.mometa.nwctrl.NwctrlDefinition + class: NwctrlDefinition + properties: + parent_mo_or_dn: org-root/org-level1 + name: Enable-CDP-LLDP + state: absent + + - name: Configure Boot Policy Using JSON objects list with children + cisco.ucs.ucs_managed_objects: + <<: *login_info + objects: + - { + "module": "ucsmsdk.mometa.lsboot.LsbootPolicy", + "class": "LsbootPolicy", + "properties": { + "parent_mo_or_dn": "org-root/org-level1", + "name": "Python_SDS", + "enforce_vnic_name": "yes", + "boot_mode": "legacy", + "reboot_on_update": "no" + }, + "children": [ + { + "module": "ucsmsdk.mometa.lsboot.LsbootVirtualMedia", + "class": "LsbootVirtualMedia", + "properties": { + "access": "read-only-local", + "lun_id": "0", + "order": "2" + } + }, + { + "module": "ucsmsdk.mometa.lsboot.LsbootStorage", + "class": "LsbootStorage", + "properties": { + "order": "1" + }, + "children": [ + { + "module": "ucsmsdk.mometa.lsboot.LsbootLocalStorage", + "class": "LsbootLocalStorage", + "properties": {}, + "children": [ + { + "module": "ucsmsdk.mometa.lsboot.LsbootDefaultLocalImage", + "class": "LsbootDefaultLocalImage", + "properties": { + "order": "1" + } + } + ] + } + ] + } + ] + } + + - name: Remove Boot Policy Using JSON objects list + cisco.ucs.ucs_managed_objects: + <<: *login_info + objects: + - { + "module": "ucsmsdk.mometa.lsboot.LsbootPolicy", + "class": "LsbootPolicy", + "properties": { + "parent_mo_or_dn": "org-root/org-level1", + "name": "Python_SDS" + } + } + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_ntp_server.yml b/ansible_collections/cisco/ucs/playbooks/ucs_ntp_server.yml new file mode 100644 index 000000000..be8f049eb --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_ntp_server.yml @@ -0,0 +1,44 @@ +--- +# Example Playbook: cisco.ucs.ucs_dns_server +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Configure NTP server + cisco.ucs.ucs_ntp_server: + <<: *login_info + ntp_server: 10.10.10.10 + description: Internal NTP Server by IP address + state: present + + - name: Configure NTP server + cisco.ucs.ucs_ntp_server: + <<: *login_info + ntp_server: pool.ntp.org + description: External NTP Server by hostname + state: present + + - name: Remove NTP server + cisco.ucs.ucs_ntp_server: + <<: *login_info + ntp_server: 10.10.10.10 + state: absent + + - name: Remove NTP server + cisco.ucs.ucs_ntp_server: + <<: *login_info + ntp_server: pool.ntp.org + state: absent
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_org.yml b/ansible_collections/cisco/ucs/playbooks/ucs_org.yml new file mode 100644 index 000000000..367beb7d8 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_org.yml @@ -0,0 +1,73 @@ +--- +# Example Playbook: cisco.ucs.ucs_org +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: test + description: testing org + state: present + delegate_to: localhost + + - name: Update UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: test + description: Testing org + state: present + delegate_to: localhost + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: test + state: absent + delegate_to: localhost + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level2 + parent_org_path: root/level1 + description: level2 org + state: present + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level3 + parent_org_path: root/level1/level2 + description: level3 org + state: present + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level2 + parent_org_path: root/level1 + state: absent diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_query.yml b/ansible_collections/cisco/ucs/playbooks/ucs_query.yml new file mode 100644 index 000000000..be9d70e87 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_query.yml @@ -0,0 +1,62 @@ +--- +# Example Playbook: cisco.ucs.ucs_query +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Query UCS Class ID + cisco.ucs.ucs_query: + <<: *login_info + class_ids: computeBlade + delegate_to: localhost + register: response + + - name: show response + debug: + msg: "{{ response }}" + + - name: Query UCS Class IDs + cisco.ucs.ucs_query: + <<: *login_info + class_ids: computeBlade, fabricVlan + delegate_to: localhost + register: response + + - name: show response + debug: + msg: "{{ response }}" + + - name: Query UCS Distinguished Name + cisco.ucs.ucs_query: + <<: *login_info + distinguished_names: org-root + delegate_to: localhost + register: response + + - name: show response + debug: + msg: "{{ response }}" + + - name: Query UCS Distinguished Names + cisco.ucs.ucs_query: + <<: *login_info + distinguished_names: org-root, sys/rack-unit-1, sys/chassis-1/blade-2 + delegate_to: localhost + register: response + + - name: show response + debug: + msg: "{{ response }}"
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_san_connectivity.yml b/ansible_collections/cisco/ucs/playbooks/ucs_san_connectivity.yml new file mode 100644 index 000000000..7958af126 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_san_connectivity.yml @@ -0,0 +1,56 @@ +--- +# Example Playbook: cisco.ucs.ucs_san_connectivity +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure SAN Connectivity Policy + cisco.ucs.ucs_san_connectivity: + <<: *login_info + name: Cntr-FC-Boot + org_dn: org-root/org-level1 + wwnn_pool: WWNN-Pool + vhba_list: + - name: Fabric-A + vhba_template: vHBA-Template-A + adapter_policy: Linux + - name: Fabric-B + vhba_template: vHBA-Template-B + adapter_policy: Linux + + - name: Remove SAN Connectivity Policy + cisco.ucs.ucs_san_connectivity: + <<: *login_info + name: Cntr-FC-Boot + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_scrub_policy.yml b/ansible_collections/cisco/ucs/playbooks/ucs_scrub_policy.yml new file mode 100644 index 000000000..96e9477ad --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_scrub_policy.yml @@ -0,0 +1,123 @@ +--- +# Example Playbook: cisco.ucs.ucs_scrub_policy +- hosts: ucs + connection: local + gather_facts: false + + vars: + org: AnsibleOrg + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: "{{ org }}" + parent_org_path: root + description: "Org {{ org }}" + state: present + delegate_to: localhost + + - name: Add Scrub Policy + cisco.ucs.ucs_scrub_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + description: Scrub All Policy + name: all_scrub + bios_settings_scrub: "yes" + disk_scrub: "yes" + flex_flash_scrub: "yes" + persistent_memory_scrub: "yes" + delegate_to: localhost + + - name: Check Scrub Policy + cisco.ucs.ucs_scrub_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + description: Scrub All Policy + name: all_scrub + bios_settings_scrub: "yes" + disk_scrub: "yes" + flex_flash_scrub: "yes" + persistent_memory_scrub: "yes" + delegate_to: localhost + check_mode: True + + - name: Add Scrub Policy + cisco.ucs.ucs_scrub_policy: + <<: *login_info + state: present + description: Scrub All Policy + name: all_scrub + bios_settings_scrub: "yes" + disk_scrub: "yes" + flex_flash_scrub: "yes" + persistent_memory_scrub: "yes" + delegate_to: localhost + + - name: Idempotent Scrub Policy + cisco.ucs.ucs_scrub_policy: + <<: *login_info + state: present + description: Scrub All Policy + name: all_scrub + bios_settings_scrub: "yes" + disk_scrub: "yes" + flex_flash_scrub: "yes" + persistent_memory_scrub: "yes" + delegate_to: localhost + + - name: Check Create/Update Scrub Policy + cisco.ucs.ucs_scrub_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + name: BD_scrub + description: Scrub BIOS and Disk Policy + bios_settings_scrub: "yes" + disk_scrub: "yes" + flex_flash_scrub: "no" + persistent_memory_scrub: "no" + delegate_to: localhost + check_mode: true + + - name: Create/Update Scrub Policy + cisco.ucs.ucs_scrub_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + name: BD_scrub + description: Scrub BIOS and Disk Policy + bios_settings_scrub: "yes" + disk_scrub: "yes" + flex_flash_scrub: "no" + persistent_memory_scrub: "no" + delegate_to: localhost + + - name: Delete Scrub Policy + cisco.ucs.ucs_scrub_policy: + <<: *login_info + state: absent + name: all_scrub + delegate_to: localhost + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: "{{ org }}" + parent_org_path: root + state: absent + delegate_to: localhost
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_serial_over_lan_policy.yml b/ansible_collections/cisco/ucs/playbooks/ucs_serial_over_lan_policy.yml new file mode 100644 index 000000000..7ed640df4 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_serial_over_lan_policy.yml @@ -0,0 +1,109 @@ +--- +# Example Playbook: cisco.ucs.ucs_serial_over_lan_policy +- hosts: ucs + connection: local + gather_facts: false + + vars: + org: AnsibleOrg + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: "{{ org }}" + parent_org_path: root + description: "Org {{ org }}" + state: present + delegate_to: localhost + + - name: Add UCS Serial Over Lan Policy + cisco.ucs.ucs_serial_over_lan_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + name: sol_pol + description: "Serial Over Lan for Org {{ org }} servers" + admin_state: enable + speed: "38400" + delegate_to: localhost + + - name: Check Serial Over Lan + cisco.ucs.ucs_serial_over_lan_policy: + <<: *login_info + state: present + org_dn: "org-root/org-{{ org }}" + name: sol_pol + description: "Serial Over Lan for Org {{ org }} servers" + admin_state: enable + speed: "38400" + delegate_to: localhost + check_mode: True + + - name: Add Serial Over Lan + cisco.ucs.ucs_serial_over_lan_policy: + <<: *login_info + state: present + name: sol_pol + description: Serial Over Lan for Org Root servers + admin_state: enable + speed: "57600" + delegate_to: localhost + + - name: Idempotent Serial Over Lan + cisco.ucs.ucs_serial_over_lan_policy: + <<: *login_info + state: present + name: sol_pol + description: Serial Over Lan for Org Root servers + admin_state: enable + speed: "57600" + delegate_to: localhost + + - name: Check Update Serial Over Lan + cisco.ucs.ucs_serial_over_lan_policy: + <<: *login_info + state: present + name: sol_pol + description: Serial Over Lan for Org Root servers + admin_state: enable + speed: "57600" + delegate_to: localhost + check_mode: true + + - name: Update Serial Over Lan + cisco.ucs.ucs_serial_over_lan_policy: + <<: *login_info + state: present + name: sol_pol + description: Serial Over Lan for Org Root servers + admin_state: enable + speed: "57600" + delegate_to: localhost + + - name: Delete Serial Over Lan + cisco.ucs.ucs_serial_over_lan_policy: + <<: *login_info + state: absent + name: sol_pol + delegate_to: localhost + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: "{{ org }}" + parent_org_path: root + state: absent + delegate_to: localhost
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_server_maintenance.yml b/ansible_collections/cisco/ucs/playbooks/ucs_server_maintenance.yml new file mode 100644 index 000000000..84a58da8d --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_server_maintenance.yml @@ -0,0 +1,43 @@ +--- +# Example Playbook: cisco.ucs.ucs_server_maintenance +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add Server Maintenance Policy (check_mode) + cisco.ucs.ucs_server_maintenance: + <<: *login_info + name: user-ack + uptime_disr: user-ack + trigger_config: on-next-boot + delegate_to: localhost + check_mode: true + + - name: Add Server Maintenance Policy + cisco.ucs.ucs_server_maintenance: + <<: *login_info + name: user-ack + uptime_disr: user-ack + trigger_config: on-next-boot + delegate_to: localhost + + - name: Idempotent Add Server Maintenance Policy + cisco.ucs.ucs_server_maintenance: + <<: *login_info + name: user-ack + uptime_disr: user-ack + trigger_config: on-next-boot + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_service_profile_association.yml b/ansible_collections/cisco/ucs/playbooks/ucs_service_profile_association.yml new file mode 100644 index 000000000..96d1ca789 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_service_profile_association.yml @@ -0,0 +1,53 @@ +--- +# Example Playbook: cisco.ucs.ucs_ucs_service_profile_association +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Change Service Profile Association to Existing Server + cisco.ucs.ucs_service_profile_association: + <<: *login_info + service_profile_name: auto-profile-2 + server_assignment: server + server_dn: sys/chassis-6/blade-1 + delegate_to: localhost + register: result + until: result.assign_state == 'assigned' and result.assoc_state == 'associated' + retries: 10 + delay: 60 + + - name: Change Service Profile Association to Pool + cisco.ucs.ucs_service_profile_association: + <<: *login_info + service_profile_name: auto-profile-2 + server_assignment: pool + server_pool_name: default + delegate_to: localhost + register: result + until: result.assign_state == 'assigned' and result.assoc_state == 'associated' + retries: 10 + delay: 60 + + - name: Disassociate Service Profile + cisco.ucs.ucs_service_profile_association: + <<: *login_info + service_profile_name: auto-profile-2 + state: absent + delegate_to: localhost + register: result + until: result.assign_state == 'unassigned' and result.assoc_state == 'unassociated' + retries: 10 + delay: 60 diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_service_profile_from_template.yml b/ansible_collections/cisco/ucs/playbooks/ucs_service_profile_from_template.yml new file mode 100644 index 000000000..ba05b116c --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_service_profile_from_template.yml @@ -0,0 +1,68 @@ +--- +# Example Playbook: cisco.ucs.ucs_service_profile_from_template +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure Service Profile Template with LAN/SAN Connectivity and all other options defaulted + cisco.ucs.ucs_service_profile_template: + <<: *login_info + name: sp-template + org_dn: org-root/org-level1 + template_type: updating-template + uuid_pool: UUID-Pool + storage_profile: DEE-StgProf + lan_connectivity_policy: Cntr-FC-Boot + iqn_pool: iSCSI-Boot-A + san_connectivity_policy: Cntr-FC-Boot + boot_policy: DEE-vMedia + maintenance_policy: default + server_pool: Container-Pool + host_firmware_package: 3.1.2b + bios_policy: Docker + + - name: Configure Service Profile from Template + cisco.ucs.ucs_service_profile_from_template: + <<: *login_info + name: sp-instance-1 + org_dn: org-root/org-level1 + user_label: SP Instance + power_state: down + source_template: sp-template + + - name: Remove Service Profile Template + cisco.ucs.ucs_service_profile_template: + <<: *login_info + name: sp-template + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_service_profile_template.yml b/ansible_collections/cisco/ucs/playbooks/ucs_service_profile_template.yml new file mode 100644 index 000000000..a63330c37 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_service_profile_template.yml @@ -0,0 +1,59 @@ +--- +# Example Playbook: cisco.ucs.ucs_service_profile_template +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure Service Profile Template with LAN/SAN Connectivity and all other options defaulted + cisco.ucs.ucs_service_profile_template: + <<: *login_info + name: sp-template + org_dn: org-root/org-level1 + template_type: updating-template + uuid_pool: UUID-Pool + storage_profile: DEE-StgProf + lan_connectivity_policy: Cntr-FC-Boot + iqn_pool: iSCSI-Boot-A + san_connectivity_policy: Cntr-FC-Boot + boot_policy: DEE-vMedia + maintenance_policy: default + server_pool: Container-Pool + host_firmware_package: 3.1.2b + bios_policy: Docker + + - name: Remove Service Profile Template + cisco.ucs.ucs_service_profile_template: + <<: *login_info + name: sp-template + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_sp_vnic_order.yml b/ansible_collections/cisco/ucs/playbooks/ucs_sp_vnic_order.yml new file mode 100644 index 000000000..9c511c87f --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_sp_vnic_order.yml @@ -0,0 +1,29 @@ +--- +# Example Playbook: cisco.ucs.ucs_sp_vnic_order +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Configure vnic order + cisco.ucs.ucs_sp_vnic_order: + <<: *login_info + sp_name: DEE-Ctrl-02 + vnics: + - name: eno1 + admin_vcon: '1' + order: '1' + transport: ethernet + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_storage_profile.yml b/ansible_collections/cisco/ucs/playbooks/ucs_storage_profile.yml new file mode 100644 index 000000000..aaeac8740 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_storage_profile.yml @@ -0,0 +1,64 @@ +--- +# Example Playbook: cisco.ucs.ucs_storage_profile +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure Storage Profile + cisco.ucs.ucs_storage_profile: + <<: *login_info + name: DEE-StgProf + org_dn: org-root/org-level1 + local_luns: + - name: Boot-LUN + size: '60' + disk_policy_name: DEE-DG + - name: Data-LUN + size: '200' + disk_policy_name: DEE-DG + + - name: Remove Local LUN from Storage Profile + cisco.ucs.ucs_storage_profile: + <<: *login_info + name: DEE-StgProf + org_dn: org-root/org-level1 + local_luns: + - name: Data-LUN + state: absent + + - name: Remove Storage Profile + cisco.ucs.ucs_storage_profile: + <<: *login_info + name: DEE-StgProf + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_system_qos.yml b/ansible_collections/cisco/ucs/playbooks/ucs_system_qos.yml new file mode 100644 index 000000000..2dbfb61f3 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_system_qos.yml @@ -0,0 +1,71 @@ +--- +# Example Playbook: cisco.ucs.ucs_system_qos +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Check QoS System Class + cisco.ucs.ucs_system_qos: + <<: *login_info + priority: platinum + cos: '5' + weight: '10' + delegate_to: localhost + check_mode: true + + - name: Add QoS System Class + cisco.ucs.ucs_system_qos: + <<: *login_info + priority: platinum + cos: '5' + weight: '10' + delegate_to: localhost + + - name: Idempotent Add QoS System Class + cisco.ucs.ucs_system_qos: + <<: *login_info + priority: platinum + cos: '5' + weight: '10' + delegate_to: localhost + + - name: Check Update QoS System Class + cisco.ucs.ucs_system_qos: + <<: *login_info + priority: platinum + cos: '5' + weight: '10' + mtu: '9216' + delegate_to: localhost + check_mode: true + + - name: Update QoS System Class + cisco.ucs.ucs_system_qos: + <<: *login_info + priority: platinum + cos: '5' + weight: '10' + mtu: '9216' + delegate_to: localhost + + - name: Idempotent Update QoS System Class + cisco.ucs.ucs_system_qos: + <<: *login_info + priority: platinum + cos: '5' + weight: '10' + mtu: '9216' + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_timezone.yml b/ansible_collections/cisco/ucs/playbooks/ucs_timezone.yml new file mode 100644 index 000000000..15a5c3b62 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_timezone.yml @@ -0,0 +1,31 @@ +--- +# Example Playbook: cisco.ucs.ucs_timezone +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Configure Time Zone + cisco.ucs.ucs_timezone: + <<: *login_info + state: present + admin_state: enabled + timezone: America/Phoenix + + - name: Unconfigure Time Zone + cisco.ucs.ucs_timezone: + <<: *login_info + state: absent + admin_state: disabled
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_uuid_pool.yml b/ansible_collections/cisco/ucs/playbooks/ucs_uuid_pool.yml new file mode 100644 index 000000000..a0b3798a9 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_uuid_pool.yml @@ -0,0 +1,51 @@ +--- +# Example Playbook: cisco.ucs.ucs_uuid_pool +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure UUID address pool + cisco.ucs.ucs_uuid_pool: + <<: *login_info + name: UUID-Pool + org_dn: org-root/org-level1 + order: sequential + first_uuid: 0000-000000000001 + last_uuid: 0000-000000000078 + + - name: Remove UUID address pool + cisco.ucs.ucs_uuid_pool: + <<: *login_info + name: UUID-Pool + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_vhba_template.yml b/ansible_collections/cisco/ucs/playbooks/ucs_vhba_template.yml new file mode 100644 index 000000000..e830147c0 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_vhba_template.yml @@ -0,0 +1,51 @@ +--- +# Example Playbook: cisco.ucs.ucs_vhba_template +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure vHBA template + cisco.ucs.ucs_vhba_template: + <<: *login_info + name: vHBA-A + org_dn: org-root/org-level1 + fabric: A + vsan: VSAN-A + wwpn_pool: WWPN-Pool-A + + - name: Remote vHBA template + cisco.ucs.ucs_vhba_template: + <<: *login_info + name: vHBA-A + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_vlan_find.yml b/ansible_collections/cisco/ucs/playbooks/ucs_vlan_find.yml new file mode 100644 index 000000000..b2568b19a --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_vlan_find.yml @@ -0,0 +1,39 @@ +--- +# Example Playbook: cisco.ucs.ucs_vlan_find +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Get all vlans in fabric A + cisco.ucs.ucs_vlan_find: + <<: *login_info + fabric: 'common' + pattern: '.' + register: response + + - name: show response + debug: + msg: "{{ response }}" + + - name: Confirm if vlan 15 is present + cisco.ucs.ucs_vlan_find: + <<: *login_info + vlanid: '200' + register: response + + - name: show response + debug: + msg: "{{ response }}" diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_vlan_to_group.yml b/ansible_collections/cisco/ucs/playbooks/ucs_vlan_to_group.yml new file mode 100644 index 000000000..4470b075a --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_vlan_to_group.yml @@ -0,0 +1,25 @@ +--- +# Example Playbook: cisco.ucs.ucs_vlan_to_group +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add VLAN to existing VLAN group + cisco.ucs.ucs_vlan_to_group: + <<: *login_info + vlangroup: inband + vlanname: vlan244 + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_vlans.yml b/ansible_collections/cisco/ucs/playbooks/ucs_vlans.yml new file mode 100644 index 000000000..d7796ac26 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_vlans.yml @@ -0,0 +1,31 @@ +--- +# Example Playbook: cisco.ucs.ucs_vlans +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Configure VLAN + cisco.ucs.ucs_vlans: + <<: *login_info + name: vlan300 + id: '300' + native: 'no' + + - name: Remove VLAN + cisco.ucs.ucs_vlans: + <<: *login_info + name: vlan300 + state: absent diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_vnic_template.yml b/ansible_collections/cisco/ucs/playbooks/ucs_vnic_template.yml new file mode 100644 index 000000000..ff98822d1 --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_vnic_template.yml @@ -0,0 +1,98 @@ +--- +# Example Playbook: cisco.ucs.ucs_vnic_template +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure vNIC template + cisco.ucs.ucs_vnic_template: + <<: *login_info + name: vNIC-A + org_dn: org-root/org-level1 + fabric: A + vlans_list: + - name: default + native: 'yes' + - name: vlan200 + native: 'no' + + - name: Remove VLAN from template + cisco.ucs.ucs_vnic_template: + <<: *login_info + name: vNIC-A + org_dn: org-root/org-level1 + fabric: A + vlans_list: + - name: vlan200 + state: absent + + - name: Configure vNIC template for failover + cisco.ucs.ucs_vnic_template: + <<: *login_info + name: vNIC-A-B + org_dn: org-root/org-level1 + fabric: A-B + redundancy_type: primary + vlans_list: + - name: default + native: 'yes' + - name: vlan200 + native: 'no' + + - name: Configure vNIC template for failover + cisco.ucs.ucs_vnic_template: + <<: *login_info + name: vNIC-B-A + org_dn: org-root/org-level1 + fabric: B-A + redundancy_type: secondary + vlans_list: + - name: default + native: 'yes' + - name: vlan200 + native: 'no' + + + - name: Remove vNIC template + cisco.ucs.ucs_vnic_template: + <<: *login_info + name: vNIC-A-B + org_dn: org-root/org-level1 +# state: absent + + - name: Remove vNIC template + cisco.ucs.ucs_vnic_template: + <<: *login_info + name: vNIC-B-A + org_dn: org-root/org-level1 +# state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root +# state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_vsans.yml b/ansible_collections/cisco/ucs/playbooks/ucs_vsans.yml new file mode 100644 index 000000000..5868690fa --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_vsans.yml @@ -0,0 +1,32 @@ +--- +# Example Playbook: cisco.ucs.ucs_vsans +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Configure VSAN + cisco.ucs.ucs_vsans: + <<: *login_info + name: vsan110 + fabric: common + vsan_id: '110' + vlan_id: '110' + + - name: Remove VSAN + cisco.ucs.ucs_vsans: + <<: *login_info + name: vsan110 + state: absent diff --git a/ansible_collections/cisco/ucs/playbooks/ucs_wwn_pool.yml b/ansible_collections/cisco/ucs/playbooks/ucs_wwn_pool.yml new file mode 100644 index 000000000..17ccb62dc --- /dev/null +++ b/ansible_collections/cisco/ucs/playbooks/ucs_wwn_pool.yml @@ -0,0 +1,85 @@ +--- +# Example Playbook: cisco.ucs.ucs_wwn_pool +- hosts: ucs + connection: local + gather_facts: false + + tasks: + - name: Test that we have a UCS hostname, UCS username, and UCS password + fail: + msg: 'Please define the following variables: ucs_hostname, ucs_username and ucs_password.' + when: ucs_hostname is not defined or ucs_username is not defined or ucs_password is not defined + vars: + # use "<<: *login_info" to substite the information below in each task + # this is not required, however it makes the playbook shorter. + login_info: &login_info + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + + - name: Add UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + + - name: Configure WWNN pool + cisco.ucs.ucs_wwn_pool: + <<: *login_info + name: WWNN-Pool + org_dn: org-root/org-level1 + purpose: node + first_addr: 20:00:00:25:B5:48:00:00 + last_addr: 20:00:00:25:B5:48:00:0F + + - name: Configure WWPN A pool + cisco.ucs.ucs_wwn_pool: + <<: *login_info + name: WWPN-Pool-A + org_dn: org-root/org-level1 + purpose: port + order: sequential + first_addr: 20:00:00:25:B5:48:0A:00 + last_addr: 20:00:00:25:B5:48:0A:0F + + - name: Configure WWPN Bpool + cisco.ucs.ucs_wwn_pool: + <<: *login_info + name: WWPN-Pool-B + org_dn: org-root/org-level1 + purpose: port + order: sequential + first_addr: 20:00:00:25:B5:48:0B:00 + last_addr: 20:00:00:25:B5:48:0B:0F + + - name: Remove WWNN pool + cisco.ucs.ucs_wwn_pool: + <<: *login_info + org_dn: org-root/org-level1 + name: WWNN-Pool + state: absent + + - name: Remove WWPN pool + cisco.ucs.ucs_wwn_pool: + <<: *login_info + name: WWPN-Pool-A + org_dn: org-root/org-level1 + state: absent + + - name: Remove WWPN pool + cisco.ucs.ucs_wwn_pool: + <<: *login_info + name: WWPN-Pool-B + org_dn: org-root/org-level1 + state: absent + + - name: Remove UCS Organization + cisco.ucs.ucs_org: + <<: *login_info + org_name: level1 + parent_org_path: root + state: absent + delegate_to: localhost diff --git a/ansible_collections/cisco/ucs/plugins/README.md b/ansible_collections/cisco/ucs/plugins/README.md new file mode 100644 index 000000000..6541cf7cf --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/README.md @@ -0,0 +1,31 @@ +# Collections Plugins Directory + +This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that +is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that +would contain module utils and modules respectively. + +Here is an example directory of the majority of plugins currently supported by Ansible: + +``` +└── plugins + ├── action + ├── become + ├── cache + ├── callback + ├── cliconf + ├── connection + ├── filter + ├── httpapi + ├── inventory + ├── lookup + ├── module_utils + ├── modules + ├── netconf + ├── shell + ├── strategy + ├── terminal + ├── test + └── vars +``` + +A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/2.9/plugins/plugins.html).
\ No newline at end of file diff --git a/ansible_collections/cisco/ucs/plugins/doc_fragments/ucs.py b/ansible_collections/cisco/ucs/plugins/doc_fragments/ucs.py new file mode 100644 index 000000000..5e03a772e --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/doc_fragments/ucs.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# (c) 2016 Red Hat Inc. +# (c) 2017 Cisco Systems Inc. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +class ModuleDocFragment(object): + # Cisco UCS doc fragment + DOCUMENTATION = ''' +options: + hostname: + description: + - IP address or hostname of Cisco UCS Manager. + - Modules can be used with the UCS Platform Emulator U(https://cs.co/ucspe) + type: str + required: yes + username: + description: + - Username for Cisco UCS Manager authentication. + type: str + default: admin + password: + description: + - Password for Cisco UCS Manager authentication. + type: str + required: yes + port: + description: + - Port number to be used during connection (by default uses 443 for https and 80 for http connection). + type: int + use_ssl: + description: + - If C(no), an HTTP connection will be used instead of the default HTTPS connection. + type: bool + default: yes + use_proxy: + description: + - If C(no), will not use the proxy as defined by system environment variable. + type: bool + default: yes + proxy: + description: + - If use_proxy is no, specfies proxy to be used for connection. + e.g. 'http://proxy.xy.z:8080' + type: str +''' diff --git a/ansible_collections/cisco/ucs/plugins/module_utils/ucs.py b/ansible_collections/cisco/ucs/plugins/module_utils/ucs.py new file mode 100644 index 000000000..bbb0a407a --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/module_utils/ucs.py @@ -0,0 +1,97 @@ +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# (c) 2016 Red Hat Inc. +# (c) 2019 Cisco Systems Inc. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +import traceback + +from ansible.module_utils.basic import missing_required_lib + +UCSMSDK_IMP_ERR = None +try: + import ucsmsdk + HAS_UCSMSDK = True +except Exception: + UCSMSDK_IMP_ERR = traceback.format_exc() + HAS_UCSMSDK = False + +ucs_argument_spec = dict( + hostname=dict(type='str', required=True), + username=dict(type='str', default='admin'), + password=dict(type='str', required=True, no_log=True), + port=dict(type='int', default=None), + use_ssl=dict(type='bool', default=True), + use_proxy=dict(type='bool', default=True), + proxy=dict(type='str', default=None), +) + + +class UCSModule(): + + def __init__(self, module): + self.module = module + self.result = {} + if not HAS_UCSMSDK: + self.module.fail_json(msg=missing_required_lib('ucsmsdk'), exception=UCSMSDK_IMP_ERR) + self.login() + + def __del__(self): + self.logout() + + def login(self): + from ucsmsdk.ucshandle import UcsHandle + + # use_proxy=yes (default) and proxy=None (default) should be using the system defined proxy + # use_proxy=yes (default) and proxy=value should use the provided proxy + # use_proxy=no (user) should not be using a proxy + if self.module.params['use_proxy']: + proxy = self.module.params['proxy'] + else: + # force no proxy to be used. Note that proxy=None in UcsHandle will + # use the system proxy so we must set to something else + proxy = {} + + try: + handle = UcsHandle( + ip=self.module.params['hostname'], + username=self.module.params['username'], + password=self.module.params['password'], + port=self.module.params['port'], + secure=self.module.params['use_ssl'], + proxy=proxy + ) + handle.login() + except Exception as e: + self.result['msg'] = str(e) + self.module.fail_json(**self.result) + self.login_handle = handle + + def logout(self): + if hasattr(self, 'login_handle'): + self.login_handle.logout() + return True + return False diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_disk_group_policy.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_disk_group_policy.py new file mode 100644 index 000000000..86d7eaca1 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_disk_group_policy.py @@ -0,0 +1,428 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_disk_group_policy +short_description: Configures disk group policies on Cisco UCS Manager +description: +- Configures disk group policies on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - Desired state of the disk group policy. + - If C(present), will verify that the disk group policy is present and will create if needed. + - If C(absent), will verify that the disk group policy is absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the disk group policy. + - This name can be between 1 and 16 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the policy is created. + required: yes + description: + description: + - The user-defined description of the storage profile. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + raid_level: + description: + - "The RAID level for the disk group policy. This can be one of the following:" + - "stripe - UCS Manager shows RAID 0 Striped" + - "mirror - RAID 1 Mirrored" + - "mirror-stripe - RAID 10 Mirrored and Striped" + - "stripe-parity - RAID 5 Striped Parity" + - "stripe-dual-parity - RAID 6 Striped Dual Parity" + - "stripe-parity-stripe - RAID 50 Striped Parity and Striped" + - "stripe-dual-parity-stripe - RAID 60 Striped Dual Parity and Striped" + choices: [stripe, mirror, mirror-stripe, stripe-parity, stripe-dual-parity, stripe-parity-stripe, stripe-dual-parity-stripe] + default: stripe + configuration_mode: + description: + - "Disk group configuration mode. Choose one of the following:" + - "automatic - Automatically configures the disks in the disk group." + - "manual - Enables you to manually configure the disks in the disk group." + choices: [automatic, manual] + default: automatic + num_drives: + description: + - Specify the number of drives for the disk group. + - This can be from 0 to 24. + - Option only applies when configuration mode is automatic. + default: 1 + drive_type: + description: + - Specify the drive type to use in the drive group. + - "This can be one of the following:" + - "unspecified — Selects the first available drive type, and applies that to all drives in the group." + - "HDD — Hard disk drive" + - "SSD — Solid state drive" + - Option only applies when configuration mode is automatic. + choices: [unspecified, HDD, SSD] + default: unspecified + num_ded_hot_spares: + description: + - Specify the number of hot spares for the disk group. + - This can be from 0 to 24. + - Option only applies when configuration mode is automatic. + default: unspecified + num_glob_hot_spares: + description: + - Specify the number of global hot spares for the disk group. + - This can be from 0 to 24. + - Option only applies when configuration mode is automatic. + default: unspecified + min_drive_size: + description: + - Specify the minimum drive size or unspecified to allow all drive sizes. + - This can be from 0 to 10240 GB. + - Option only applies when configuration mode is automatic. + default: 'unspecified' + use_remaining_disks: + description: + - Specifies whether you can use all the remaining disks in the disk group or not. + - Option only applies when configuration mode is automatic. + choices: ['yes', 'no'] + default: 'no' + manual_disks: + description: + - List of manually configured disks. + - Options are only used when you choose manual configuration_mode. + suboptions: + name: + description: + - The name of the local LUN. + required: yes + slot_num: + description: + - The slot number of the specific disk. + role: + description: + - "The role of the disk. This can be one of the following:" + - "normal - Normal" + - "ded-hot-spare - Dedicated Hot Spare" + - "glob-hot-spare - Glob Hot Spare" + span_id: + description: + - The Span ID of the specific disk. + default: 'unspecified' + state: + description: + - If C(present), will verify disk slot is configured within policy. + If C(absent), will verify disk slot is absent from policy. + choices: [ present, absent ] + default: present + virtual_drive: + description: + - Configuration of virtual drive options. + suboptions: + access_policy: + description: + - Configure access policy to virtual drive. + choices: [blocked, hidden, platform-default, read-only, read-write, transport-ready] + default: platform-default + drive_cache: + description: + - Configure drive caching. + choices: [disable, enable, no-change, platform-default] + default: platform-default + io_policy: + description: + - Direct or Cached IO path. + choices: [cached, direct, platform-default] + default: platform-default + read_policy: + description: + - Read access policy to virtual drive. + choices: [normal, platform-default, read-ahead] + default: platform-default + strip_size: + description: + - Virtual drive strip size. + choices: [ present, absent ] + default: platform-default + write_cache_policy: + description: + - Write back cache policy. + choices: [always-write-back, platform-default, write-back-good-bbu, write-through] + default: platform-default + org_dn: + description: + - The distinguished name (dn) of the organization where the resource is assigned. + default: org-root +requirements: +- ucsmsdk +author: +- Sindhu Sudhir (@sisudhir) +- David Soper (@dsoper2) +- CiscoUcs (@CiscoUcs) +- Brett Johnson (@sdbrett) +- John McDonough (@movinalot) +version_added: '2.8' +''' + +EXAMPLES = r''' +- name: Configure Disk Group Policy + cisco.ucs.ucs_disk_group_policy: + hostname: 172.16.143.150 + username: admin + password: password + name: DEE-DG + raid_level: mirror + configuration_mode: manual + manual_disks: + - slot_num: '1' + role: normal + - slot_num: '2' + role: normal + +- name: Remove Disk Group Policy + cisco.ucs.ucs_disk_group_policy: + name: DEE-DG + hostname: 172.16.143.150 + username: admin + password: password + state: absent + +- name: Remove Disk from Policy + cisco.ucs.ucs_disk_group_policy: + hostname: 172.16.143.150 + username: admin + password: password + name: DEE-DG + description: Testing Ansible + raid_level: stripe + configuration_mode: manual + manual_disks: + - slot_num: '1' + role: normal + - slot_num: '2' + role: normal + state: absent + virtual_drive: + access_policy: platform-default + io_policy: direct + strip_size: 64KB +''' + +RETURN = r''' +# +''' + +from ucsmsdk.mometa.lstorage.LstorageDiskGroupConfigPolicy import LstorageDiskGroupConfigPolicy +from ucsmsdk.mometa.lstorage.LstorageDiskGroupQualifier import LstorageDiskGroupQualifier +from ucsmsdk.mometa.lstorage.LstorageLocalDiskConfigRef import LstorageLocalDiskConfigRef +from ucsmsdk.mometa.lstorage.LstorageVirtualDriveDef import LstorageVirtualDriveDef + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def configure_disk_policy(ucs, module, dn): + # from ucsmsdk.mometa.lstorage.LstorageDiskGroupConfigPolicy import LstorageDiskGroupConfigPolicy + # from ucsmsdk.mometa.lstorage.LstorageDiskGroupQualifier import LstorageDiskGroupQualifier + # from ucsmsdk.mometa.lstorage.LstorageLocalDiskConfigRef import LstorageLocalDiskConfigRef + + if not module.check_mode: + try: + # create if mo does not already exist + mo = LstorageDiskGroupConfigPolicy( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['name'], + descr=module.params['description'], + raid_level=module.params['raid_level'], + ) + if module.params['configuration_mode'] == 'automatic': + LstorageDiskGroupQualifier( + parent_mo_or_dn=mo, + num_drives=module.params['num_drives'], + drive_type=module.params['drive_type'], + use_remaining_disks=module.params['use_remaining_disks'], + num_ded_hot_spares=module.params['num_ded_hot_spares'], + num_glob_hot_spares=module.params['num_glob_hot_spares'], + min_drive_size=module.params['min_drive_size'], + ) + else: # configuration_mode == 'manual' + for disk in module.params['manual_disks']: + if disk['state'] == 'absent': + child_dn = dn + '/slot-' + disk['slot_num'] + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + ucs.login_handle.remove_mo(mo_1) + else: # state == 'present' + LstorageLocalDiskConfigRef( + parent_mo_or_dn=mo, + slot_num=disk['slot_num'], + role=disk['role'], + span_id=disk['span_id'], + ) + + if module.params['virtual_drive']: + _configure_virtual_drive(module, mo) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + except Exception as e: # generic Exception handling because SDK can throw a variety + ucs.result['msg'] = "setup error: %s " % str(e) + module.fail_json(**ucs.result) + + ucs.result['changed'] = True + + +def check_disk_policy_props(ucs, module, mo, dn): + props_match = True + + # check top-level mo props + kwargs = dict(descr=module.params['description']) + kwargs['raid_level'] = module.params['raid_level'] + if mo.check_prop_match(**kwargs): + # top-level props match, check next level mo/props + if module.params['configuration_mode'] == 'automatic': + child_dn = dn + '/disk-group-qual' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(num_drives=module.params['num_drives']) + kwargs['drive_type'] = module.params['drive_type'] + kwargs['use_remaining_disks'] = module.params['use_remaining_disks'] + kwargs['num_ded_hot_spares'] = module.params['num_ded_hot_spares'] + kwargs['num_glob_hot_spares'] = module.params['num_glob_hot_spares'] + kwargs['min_drive_size'] = module.params['min_drive_size'] + props_match = mo_1.check_prop_match(**kwargs) + + else: # configuration_mode == 'manual' + for disk in module.params['manual_disks']: + child_dn = dn + '/slot-' + disk['slot_num'] + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + if disk['state'] == 'absent': + props_match = False + else: # state == 'present' + kwargs = dict(slot_num=disk['slot_num']) + kwargs['role'] = disk['role'] + kwargs['span_id'] = disk['span_id'] + if not mo_1.check_prop_match(**kwargs): + props_match = False + break + if props_match: + if module.params['virtual_drive']: + props_match = check_virtual_drive_props(ucs, module, dn) + else: + props_match = False + return props_match + + +def check_virtual_drive_props(ucs, module, dn): + child_dn = dn + '/virtual-drive-def' + mo_1 = ucs.login_handle.query_dn(child_dn) + return mo_1.check_prop_match(**module.params['virtual_drive']) + + +def _configure_virtual_drive(module, mo): + # from ucsmsdk.mometa.lstorage.LstorageVirtualDriveDef import LstorageVirtualDriveDef + LstorageVirtualDriveDef(parent_mo_or_dn=mo, **module.params['virtual_drive']) + + +def _virtual_drive_argument_spec(): + return dict( + access_policy=dict(type='str', default='platform-default', + choices=["blocked", "hidden", "platform-default", "read-only", "read-write", + "transport-ready"]), + drive_cache=dict(type='str', default='platform-default', + choices=["disable", "enable", "no-change", "platform-default"]), + io_policy=dict(type='str', default='platform-default', + choices=["cached", "direct", "platform-default"]), + read_policy=dict(type='str', default='platform-default', + choices=["normal", "platform-default", "read-ahead"]), + strip_size=dict(type='str', default='platform-default', + choices=["1024KB", "128KB", "16KB", "256KB", "32KB", "512KB", "64KB", "8KB", + "platform-default"]), + write_cache_policy=dict(type='str', default='platform-default', + choices=["always-write-back", "platform-default", "write-back-good-bbu", + "write-through"]), + ) + + +def main(): + manual_disk = dict( + slot_num=dict(type='str', required=True), + role=dict(type='str', default='normal', choices=['normal', 'ded-hot-spare', 'glob-hot-spare']), + span_id=dict(type='str', default='unspecified'), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str', required=True), + description=dict(type='str', aliases=['descr'], default=''), + raid_level=dict( + type='str', + default='stripe', + choices=[ + 'stripe', + 'mirror', + 'mirror-stripe', + 'stripe-parity', + 'stripe-dual-parity', + 'stripe-parity-stripe', + 'stripe-dual-parity-stripe', + ], + ), + num_drives=dict(type='str', default='1'), + configuration_mode=dict(type='str', default='automatic', choices=['automatic', 'manual']), + num_ded_hot_spares=dict(type='str', default='unspecified'), + num_glob_hot_spares=dict(type='str', default='unspecified'), + drive_type=dict(type='str', default='unspecified', choices=['unspecified', 'HDD', 'SSD']), + use_remaining_disks=dict(type='str', default='no', choices=['yes', 'no']), + min_drive_size=dict(type='str', default='unspecified'), + manual_disks=dict(type='list', elements='dict', options=manual_disk), + state=dict(type='str', default='present', choices=['present', 'absent']), + virtual_drive=dict(type='dict', options=_virtual_drive_argument_spec()), + ) + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + ucs = UCSModule(module) + # UCSModule creation above verifies ucsmsdk is present and exits on failure. + # Additional imports are done below or in called functions. + + ucs.result['changed'] = False + props_match = False + # dn is <org_dn>/disk-group-config-<name> + dn = module.params['org_dn'] + '/disk-group-config-' + module.params['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + ucs.result['changed'] = True + else: # state == 'present' + props_match = check_disk_policy_props(ucs, module, mo, dn) + + if module.params['state'] == 'present' and not props_match: + configure_disk_policy(ucs, module, dn) + + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_dns_server.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_dns_server.py new file mode 100644 index 000000000..5271cacc5 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_dns_server.py @@ -0,0 +1,165 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_dns_server +short_description: Configure DNS servers on Cisco UCS Manager +description: +- Configure DNS servers on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(absent), will remove a DNS server. + - If C(present), will add or update a DNS server. + choices: [absent, present] + default: present + type: str + + dns_server: + description: + - DNS server IP address. + - Enter a valid IPV4 Address. + - UCS Manager supports up to 4 DNS Servers + aliases: [ name ] + type: str + + description: + description: + - A user-defined description of the DNS server. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + type: str + + delegate_to: + description: + - Where the module will be run + default: localhost + type: str + +requirements: +- ucsmsdk + +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) + +version_added: "2.8" +''' + +EXAMPLES = r''' +- name: Configure DNS server + cisco.ucs.ucs_dns_server: + hostname: 172.16.143.150 + username: admin + password: password + dns_server: 10.10.10.10 + description: DNS Server IP address + state: present + delegate_to: localhost + +- name: Remove DNS server + cisco.ucs.ucs_dns_server: + hostname: 172.16.143.150 + username: admin + password: password + dns_server: 10.10.10.10 + state: absent + delegate_to: localhost +''' + +RETURN = r''' +# +''' +from ucsmsdk.mometa.comm.CommDnsProvider import CommDnsProvider + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def run_module(): + argument_spec = ucs_argument_spec + argument_spec.update( + dns_server=dict(type='str', aliases=['name']), + description=dict(type='str', aliases=['descr'], default=''), + state=dict(type='str', default='present', choices=['present', 'absent']), + delegate_to=dict(type='str', default='localhost'), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['dns_server']], + ], + ) + # UCSModule verifies ucsmsdk is present and exits on failure. + # Imports are below for UCS object creation. + ucs = UCSModule(module) + # from ucsmsdk.mometa.comm.CommDnsProvider import CommDnsProvider + + err = False + changed = False + + try: + mo_exists = False + props_match = False + + dn = 'sys/svc-ext/dns-svc/dns-' + module.params['dns_server'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(descr=module.params['description']) + if mo.check_prop_match(**kwargs): + props_match = True + + if not props_match: + if not module.check_mode: + # update/add mo + mo = CommDnsProvider(parent_mo_or_dn='sys/svc-ext/dns-svc', + name=module.params['dns_server'], + descr=module.params['description']) + ucs.login_handle.add_mo(mo, modify_present=True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_graphics_card_policy.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_graphics_card_policy.py new file mode 100644 index 000000000..ccc8c5838 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_graphics_card_policy.py @@ -0,0 +1,261 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_graphics_card_policy + +short_description: Manages UCS Graphics Card Policies on UCS Manager + +description: + - Manages UCS Graphics Card Policies on UCS Manager. + +extends_documentation_fragment: cisco.ucs.ucs + +options: + state: + description: + - If C(absent), will remove organization. + - If C(present), will create or update organization. + choices: [absent, present] + default: present + type: str + + name: + description: + - The name of the organization. + - Enter up to 16 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote)" + - "= (equal sign), > (greater than), < (less than), ' (single quote)." + required: true + type: str + + description: + description: + - A user-defined description of the organization. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote)" + - "= (equal sign), > (greater than), < (less than), ' (single quote)." + aliases: [ descr ] + type: str + + graphics_card_mode: + description: + - Set the Graphics Card Mode. + choices: [any-configuration, compute, graphics] + type: str + + org_dn: + description: + - Org dn (distinguished name) + default: org-root + type: str + +requirements: +- ucsmsdk + +author: +- John McDonough (@movinalot) +version_added: "2.9" +''' + +EXAMPLES = r''' +- name: Add UCS Graphics Card Policy + cisco.ucs.ucs_graphics_card_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + description: Any Graphics Mode Policy + name: any_graphics + graphics_card_mode: any-configuration + delegate_to: localhost + +- name: Add UCS Graphics Card Policy in an Organization + cisco.ucs.ucs_graphics_card_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + org_dn: org-root/org-prod + description: Any Graphics Mode Policy + name: prod_graphics + graphics_card_mode: any-configuration + delegate_to: localhost + +- name: Update UCS Graphics Card Policy in an Organization + cisco.ucs.ucs_graphics_card_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + org_dn: org-root/org-prod + description: Graphics Mode Policy + name: prod_graphics + graphics_card_mode: graphics + delegate_to: localhost + +- name: Update UCS Graphics Card Policy in an Organization + cisco.ucs.ucs_graphics_card_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + org_dn: org-root/org-prod + description: Compute Mode Policy + name: prod_graphics + graphics_card_mode: compute + delegate_to: localhost + +- name: Delete UCS Graphics Card Policy in an Organization + cisco.ucs.ucs_graphics_card_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: absent + org_dn: org-root/org-prod + name: prod_graphics + delegate_to: localhost + +- name: Delete UCS Graphics Card Policy + cisco.ucs.ucs_graphics_card_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: absent + name: any_graphics + delegate_to: localhost +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import ( + UCSModule, + ucs_argument_spec +) + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(required=True, type='str'), + descr=dict(type='str'), + description=dict(type='str', aliases=['descr']), + graphics_card_mode=dict(type='str', choices=[ + 'any-configuration', + 'compute', + 'graphics' + ]), + state=dict( + type='str', default='present', + choices=['present', 'absent'] + ), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['name']], + ], + ) + + # UCSModule verifies ucsmsdk is present and exits on failure. + # Imports are below for UCS object creation. + ucs = UCSModule(module) + from importlib import import_module + from ucsmsdk.ucscoreutils import get_meta_info + + # The Class(es) this module is managing + module_file = 'ucsmsdk.mometa.compute.ComputeGraphicsCardPolicy' + module_class = 'ComputeGraphicsCardPolicy' + mo_module = import_module(module_file) + mo_class = getattr(mo_module, module_class) + + META = get_meta_info(class_id=module_class) + + err = False + changed = False + requested_state = module.params['state'] + + kwargs = dict() + + # Manage Aliased Attributes + for attribute in ['descr:description']: + attribute_alias = attribute.split(':') + if module.params[attribute_alias[1]] is not None: + kwargs[attribute_alias[0]] = module.params[attribute_alias[1]] + + # Manage Attributes + for attribute in [ + 'graphics_card_mode', 'descr']: + if module.params[attribute] is not None: + kwargs[attribute] = module.params[attribute] + + try: + dn = ( + module.params['org_dn'] + '/' + + META.rn[0:META.rn.rindex('-') + 1] + + module.params['name'] + ) + mo = ucs.login_handle.query_dn(dn) + + # Determine state change + if mo: + # Object exists, if it should exist has anything changed? + if requested_state == 'present': + # Do some or all Object properties not match, that is a change + + if not mo.check_prop_match(**kwargs): + changed = True + + # Object does not exist but should, that is a change + else: + if requested_state == 'present': + changed = True + + # Object exists but should not, that is a change + if mo and requested_state == 'absent': + changed = True + + # Apply state if not check_mode + if changed and not module.check_mode: + if requested_state == 'absent': + ucs.login_handle.remove_mo(mo) + else: + kwargs['parent_mo_or_dn'] = module.params['org_dn'] + kwargs['name'] = module.params['name'] + + mo = mo_class(**kwargs) + ucs.login_handle.add_mo(mo, modify_present=True) + ucs.login_handle.commit() + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_ip_pool.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_ip_pool.py new file mode 100644 index 000000000..308ffd4b8 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_ip_pool.py @@ -0,0 +1,452 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_ip_pool +short_description: Configures IP address pools on Cisco UCS Manager +description: +- Configures IP address pools and blocks of IP addresses on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify IP pool is present and will create if needed. + - If C(absent), will verify IP pool is absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the IP address pool. + - This name can be between 1 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the IP address pool is created. + required: yes + description: + description: + - The user-defined description of the IP address pool. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + order: + description: + - The Assignment Order field. + - "This can be one of the following:" + - "default - Cisco UCS Manager selects a random identity from the pool." + - "sequential - Cisco UCS Manager selects the lowest available identity from the pool." + choices: [default, sequential] + default: default + ip_blocks: + description: + - List of IPv4 blocks used by the IP Pool. + suboptions: + first_addr: + description: + - The first IPv4 address in the IPv4 addresses block. + - This is the From field in the UCS Manager Add IPv4 Blocks menu. + last_addr: + description: + - The last IPv4 address in the IPv4 addresses block. + - This is the To field in the UCS Manager Add IPv4 Blocks menu. + subnet_mask: + description: + - The subnet mask associated with the IPv4 addresses in the block. + default: 255.255.255.0 + default_gw: + description: + - The default gateway associated with the IPv4 addresses in the block. + default: 0.0.0.0 + primary_dns: + description: + - The primary DNS server that this block of IPv4 addresses should access. + default: 0.0.0.0 + secondary_dns: + description: + - The secondary DNS server that this block of IPv4 addresses should access. + default: 0.0.0.0 + ipv6_blocks: + description: + - List of IPv6 blocks used by the IP Pool. + suboptions: + ipv6_first_addr: + description: + - The first IPv6 address in the IPv6 addresses block. + - This is the From field in the UCS Manager Add IPv6 Blocks menu. + ipv6_last_addr: + description: + - The last IPv6 address in the IPv6 addresses block. + - This is the To field in the UCS Manager Add IPv6 Blocks menu. + ipv6_prefix: + description: + - The network address prefix associated with the IPv6 addresses in the block. + default: '64' + ipv6_default_gw: + description: + - The default gateway associated with the IPv6 addresses in the block. + default: '::' + ipv6_primary_dns: + description: + - The primary DNS server that this block of IPv6 addresses should access. + default: '::' + ipv6_secondary_dns: + description: + - The secondary DNS server that this block of IPv6 addresses should access. + default: '::' + org_dn: + description: + - Org dn (distinguished name) + default: org-root +requirements: +- ucsmsdk +author: + - Brett Johnson (@sdbrett) + - David Soper (@dsoper2) + - John McDonough (@movinalot) + - CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure IPv4 and IPv6 address pool + cisco.ucs.ucs_ip_pool: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + name: ip-pool-01 + org_dn: org-root/org-level1 + ipv4_blocks: + - first_addr: 192.168.10.1 + last_addr: 192.168.10.20 + subnet_mask: 255.255.255.128 + default_gw: 192.168.10.2 + - first_addr: 192.168.11.1 + last_addr: 192.168.11.20 + subnet_mask: 255.255.255.128 + default_gw: 192.168.11.2 + ipv6_blocks: + - ipv6_first_addr: fe80::1cae:7992:d7a1:ed07 + ipv6_last_addr: fe80::1cae:7992:d7a1:edfe + ipv6_default_gw: fe80::1cae:7992:d7a1:ecff + - ipv6_first_addr: fe80::1cae:7992:d7a1:ec07 + ipv6_last_addr: fe80::1cae:7992:d7a1:ecfe + ipv6_default_gw: fe80::1cae:7992:d7a1:ecff + +- name: Delete IPv4 and IPv6 address pool blocks + cisco.ucs.ucs_ip_pool: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + name: ip-pool-01 + org_dn: org-root/org-level1 + ipv4_blocks: + - first_addr: 192.168.10.1 + last_addr: 192.168.10.20 + state: absent + ipv6_blocks: + - ipv6_first_addr: fe80::1cae:7992:d7a1:ec07 + ipv6_last_addr: fe80::1cae:7992:d7a1:ecfe + state: absent + +- name: Remove IPv4 and IPv6 address pool + cisco.ucs.ucs_ip_pool: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + name: ip-pool-01 + state: absent +''' + +RETURN = r''' +# +''' + + +def update_ip_pool(ucs, module): + from ucsmsdk.mometa.ippool.IppoolPool import IppoolPool + + mo = IppoolPool( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['name'], + descr=module.params['descr'], + assignment_order=module.params['order'], + ) + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + + return mo + + +def match_existing_ipv4_block(ucs, dn, ipv4_block): + # ipv4 block specified, check properties + mo_1 = get_ip_block(ucs, dn, ipv4_block['first_addr'], ipv4_block['last_addr'], 'v4') + if not mo_1: + if ipv4_block['state'] == 'absent': + return True + return False + else: + if ipv4_block['state'] == 'absent': + return False + kwargs = dict(subnet=ipv4_block['subnet_mask']) + kwargs['def_gw'] = ipv4_block['default_gw'] + kwargs['prim_dns'] = ipv4_block['primary_dns'] + kwargs['sec_dns'] = ipv4_block['secondary_dns'] + return mo_1.check_prop_match(**kwargs) + + +def match_existing_ipv6_block(ucs, dn, ipv6_block): + # ipv6 block specified, check properties + mo_1 = get_ip_block(ucs, dn, ipv6_block['ipv6_first_addr'], ipv6_block['ipv6_last_addr'], 'v6') + if not mo_1: + if ipv6_block['state'] == 'absent': + return True + return False + else: + if ipv6_block['state'] == 'absent': + return False + kwargs = dict(prefix=ipv6_block['ipv6_prefix']) + kwargs['def_gw'] = ipv6_block['ipv6_default_gw'] + kwargs['prim_dns'] = ipv6_block['ipv6_primary_dns'] + kwargs['sec_dns'] = ipv6_block['ipv6_secondary_dns'] + return mo_1.check_prop_match(**kwargs) + + +def remove_ip_block(ucs, dn, ip_block, ip_version): + if ip_version == 'v6': + first_addr = ip_block['ipv6_first_addr'] + last_addr = ip_block['ipv6_last_addr'] + else: + first_addr = ip_block['first_addr'] + last_addr = ip_block['last_addr'] + + mo_1 = get_ip_block(ucs, dn, first_addr, last_addr, ip_version) + if mo_1: + ucs.login_handle.remove_mo(mo_1) + ucs.login_handle.commit() + + +def update_ip_block(ucs, mo, ip_block, ip_version): + + remove_ip_block(ucs, mo.dn, ip_block, ip_version) + if not ip_block['state'] == 'absent': + if ip_version == 'v6': + from ucsmsdk.mometa.ippool.IppoolIpV6Block import IppoolIpV6Block + IppoolIpV6Block( + parent_mo_or_dn=mo, + to=ip_block['ipv6_last_addr'], + r_from=ip_block['ipv6_first_addr'], + prefix=ip_block['ipv6_prefix'], + def_gw=ip_block['ipv6_default_gw'], + prim_dns=ip_block['ipv6_primary_dns'], + sec_dns=ip_block['ipv6_secondary_dns'] + ) + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + else: + from ucsmsdk.mometa.ippool.IppoolBlock import IppoolBlock + IppoolBlock( + parent_mo_or_dn=mo, + to=ip_block['last_addr'], + r_from=ip_block['first_addr'], + subnet=ip_block['subnet_mask'], + def_gw=ip_block['default_gw'], + prim_dns=ip_block['primary_dns'], + sec_dns=ip_block['secondary_dns'] + ) + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + + +def get_ip_block(ucs, pool_dn, first_addr, last_addr, ip_version): + if ip_version == 'v6': + dn_type = '/v6block-' + else: + dn_type = '/block-' + + block_dn = pool_dn + dn_type + first_addr + '-' + last_addr + return ucs.login_handle.query_dn(block_dn) + + +def main(): + from ansible.module_utils.basic import AnsibleModule + from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + ipv4_configuration_spec = dict( + first_addr=dict(type='str'), + last_addr=dict(type='str'), + subnet_mask=dict(type='str', default='255.255.255.0'), + default_gw=dict(type='str', default='0.0.0.0'), + primary_dns=dict(type='str', default='0.0.0.0'), + secondary_dns=dict(type='str', default='0.0.0.0'), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + ipv6_configuration_spec = dict( + ipv6_first_addr=dict(type='str'), + ipv6_last_addr=dict(type='str'), + ipv6_prefix=dict(type='str', default='64'), + ipv6_default_gw=dict(type='str', default='::'), + ipv6_primary_dns=dict(type='str', default='::'), + ipv6_secondary_dns=dict(type='str', default='::'), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str', required=True), + descr=dict(type='str', default='', aliases=['description']), + order=dict(type='str', default='default', choices=['default', 'sequential']), + first_addr=dict(type='str'), + last_addr=dict(type='str'), + subnet_mask=dict(type='str', default='255.255.255.0'), + default_gw=dict(type='str', default='0.0.0.0'), + primary_dns=dict(type='str', default='0.0.0.0'), + secondary_dns=dict(type='str', default='0.0.0.0'), + ipv6_first_addr=dict(type='str'), + ipv6_last_addr=dict(type='str'), + ipv6_prefix=dict(type='str', default='64'), + ipv6_default_gw=dict(type='str', default='::'), + ipv6_primary_dns=dict(type='str', default='::'), + ipv6_secondary_dns=dict(type='str', default='::'), + state=dict(type='str', default='present', choices=['present', 'absent']), + ipv4_blocks=dict(type='list', default=None, elements='dict', options=ipv4_configuration_spec), + ipv6_blocks=dict(type='list', default=None, elements='dict', options=ipv6_configuration_spec), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + # UCSModule verifies ucsmsdk is present and exits on failure. Imports are below ucs object creation. + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.ippool.IppoolBlock import IppoolBlock + from ucsmsdk.mometa.ippool.IppoolIpV6Block import IppoolIpV6Block + + changed = False + try: + mo_exists = False + ipv4_props_match = True + ipv6_props_match = True + # dn is <org_dn>/ip-pool-<name> + dn = module.params['org_dn'] + '/ip-pool-' + module.params['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + if module.params['state'] == 'absent': + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if not mo_exists: + if not module.check_mode: + mo = update_ip_pool(ucs, module) + changed = True + if mo_exists: + # check top-level mo props + kwargs = dict(assignment_order=module.params['order']) + kwargs['descr'] = module.params['descr'] + if not mo.check_prop_match(**kwargs): + if not module.check_mode: + mo = update_ip_pool(ucs, module) + changed = True + # top-level props match, check next level mo/props + if module.params['ipv4_blocks']: + for ipv4_block in module.params['ipv4_blocks']: + if not match_existing_ipv4_block(ucs, dn, ipv4_block): + if not module.check_mode: + update_ip_block(ucs, mo, ipv4_block, 'v4') + changed = True + elif module.params['last_addr'] and module.params['first_addr']: + # ipv4 block specified, check properties + mo_1 = get_ip_block(ucs, dn, module.params['first_addr'], module.params['last_addr'], 'v4') + if mo_1: + kwargs = dict(subnet=module.params['subnet_mask']) + kwargs['def_gw'] = module.params['default_gw'] + kwargs['prim_dns'] = module.params['primary_dns'] + kwargs['sec_dns'] = module.params['secondary_dns'] + if not mo_1.check_prop_match(**kwargs): + # ipv4 block exists and properties match + ipv4_props_match = False + else: + ipv4_props_match = False + + # only check ipv6 props if the top-level and ipv4 props matched + if module.params['ipv6_blocks']: + for ipv6_block in module.params['ipv6_blocks']: + if not match_existing_ipv6_block(ucs, dn, ipv6_block): + if not module.check_mode: + update_ip_block(ucs, mo, ipv6_block, 'v6') + changed = True + elif module.params['ipv6_last_addr'] and module.params['ipv6_first_addr']: + # ipv6 block specified, check properties + block_dn = dn + '/v6block-' + module.params['ipv6_first_addr'].lower() + '-' + module.params[ + 'ipv6_last_addr'].lower() + mo_1 = ucs.login_handle.query_dn(block_dn) + if mo_1: + kwargs = dict(prefix=module.params['ipv6_prefix']) + kwargs['def_gw'] = module.params['ipv6_default_gw'] + kwargs['prim_dns'] = module.params['ipv6_primary_dns'] + kwargs['sec_dns'] = module.params['ipv6_secondary_dns'] + if not mo_1.check_prop_match(**kwargs): + # ipv6 block exists and properties match + ipv6_props_match = False + else: + ipv6_props_match = False + + if not ipv4_props_match or not ipv6_props_match: + if not module.check_mode: + if module.params['last_addr'] and module.params['first_addr']: + IppoolBlock( + parent_mo_or_dn=mo, + to=module.params['last_addr'], + r_from=module.params['first_addr'], + subnet=module.params['subnet_mask'], + def_gw=module.params['default_gw'], + prim_dns=module.params['primary_dns'], + sec_dns=module.params['secondary_dns'], + ) + + if module.params['ipv6_last_addr'] and module.params['ipv6_first_addr']: + IppoolIpV6Block( + parent_mo_or_dn=mo, + to=module.params['ipv6_last_addr'], + r_from=module.params['ipv6_first_addr'], + prefix=module.params['ipv6_prefix'], + def_gw=module.params['ipv6_default_gw'], + prim_dns=module.params['ipv6_primary_dns'], + sec_dns=module.params['ipv6_secondary_dns'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_lan_connectivity.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_lan_connectivity.py new file mode 100644 index 000000000..7638091f5 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_lan_connectivity.py @@ -0,0 +1,355 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_lan_connectivity +short_description: Configures LAN Connectivity Policies on Cisco UCS Manager +description: +- Configures LAN Connectivity Policies on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify LAN Connectivity Policies are present and will create if needed. + - If C(absent), will verify LAN Connectivity Policies are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the LAN Connectivity Policy. + - This name can be between 1 and 16 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the policy is created. + required: yes + description: + description: + - A description of the LAN Connectivity Policy. + - Cisco recommends including information about where and when to use the policy. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + vnic_list: + description: + - List of vNICs used by the LAN Connectivity Policy. + - vNICs used by the LAN Connectivity Policy must be created from a vNIC template. + suboptions: + name: + description: + - The name of the vNIC. + required: yes + vnic_template: + description: + - The name of the vNIC template. + required: yes + adapter_policy: + description: + - The name of the Ethernet adapter policy. + - A user defined policy can be used, or one of the system defined policies. + order: + description: + - String specifying the vNIC assignment order (e.g., '1', '2'). + default: 'unspecified' + state: + description: + - If C(present), will verify vnic is configured within policy. + If C(absent), will verify vnic is absent from policy. + choices: [ present, absent ] + default: present + version_added: '2.8' + iscsi_vnic_list: + description: + - List of iSCSI vNICs used by the LAN Connectivity Policy. + suboptions: + name: + description: + - The name of the iSCSI vNIC. + required: yes + overlay_vnic: + description: + - The LAN vNIC associated with this iSCSI vNIC. + iscsi_adapter_policy: + description: + - The iSCSI adapter policy associated with this iSCSI vNIC. + mac_address: + description: + - The MAC address associated with this iSCSI vNIC. + - If the MAC address is not set, Cisco UCS Manager uses a derived MAC address. + default: derived + vlan_name: + description: + - The VLAN used for the iSCSI vNIC. + default: default + state: + description: + - If C(present), will verify iscsi vnic is configured within policy. + If C(absent), will verify iscsi vnic is absent from policy. + choices: [ present, absent ] + default: present + version_added: '2.8' + org_dn: + description: + - Org dn (distinguished name) + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure LAN Connectivity Policy + cisco.ucs.ucs_lan_connectivity: + hostname: 172.16.143.150 + username: admin + password: password + name: Cntr-FC-Boot + vnic_list: + - name: eno1 + vnic_template: Cntr-Template + adapter_policy: Linux + - name: eno2 + vnic_template: Container-NFS-A + adapter_policy: Linux + - name: eno3 + vnic_template: Container-NFS-B + adapter_policy: Linux + iscsi_vnic_list: + - name: iSCSIa + overlay_vnic: eno1 + iscsi_adapter_policy: default + vlan_name: Container-MGMT-VLAN + - name: iSCSIb + overlay_vnic: eno3 + iscsi_adapter_policy: default + vlan_name: Container-TNT-A-NFS + +- name: Remove LAN Connectivity Policy + cisco.ucs.ucs_lan_connectivity: + hostname: 172.16.143.150 + username: admin + password: password + name: Cntr-FC-Boot + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def configure_lan_connectivity(ucs, module, dn): + from ucsmsdk.mometa.vnic.VnicLanConnPolicy import VnicLanConnPolicy + from ucsmsdk.mometa.vnic.VnicEther import VnicEther + from ucsmsdk.mometa.vnic.VnicIScsiLCP import VnicIScsiLCP + from ucsmsdk.mometa.vnic.VnicVlan import VnicVlan + + if not module.check_mode: + try: + # create if mo does not already exist + mo = VnicLanConnPolicy( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['name'], + descr=module.params['description'], + ) + + if module.params.get('vnic_list'): + for vnic in module.params['vnic_list']: + if vnic['state'] == 'absent': + child_dn = dn + '/ether-' + vnic['name'] + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + ucs.login_handle.remove_mo(mo_1) + else: # state == 'present' + mo_1 = VnicEther( + addr='derived', + parent_mo_or_dn=mo, + name=vnic['name'], + adaptor_profile_name=vnic['adapter_policy'], + nw_templ_name=vnic['vnic_template'], + order=vnic['order'], + ) + + if module.params.get('iscsi_vnic_list'): + for iscsi_vnic in module.params['iscsi_vnic_list']: + if iscsi_vnic['state'] == 'absent': + child_dn = dn + '/iscsi-' + iscsi_vnic['name'] + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + ucs.login_handle.remove_mo(mo_1) + else: # state == 'present' + mo_1 = VnicIScsiLCP( + parent_mo_or_dn=mo, + name=iscsi_vnic['name'], + adaptor_profile_name=iscsi_vnic['iscsi_adapter_policy'], + vnic_name=iscsi_vnic['overlay_vnic'], + addr=iscsi_vnic['mac_address'], + ) + VnicVlan( + parent_mo_or_dn=mo_1, + vlan_name=iscsi_vnic['vlan_name'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + except Exception as e: # generic Exception handling because SDK can throw a variety of exceptions + ucs.result['msg'] = "setup error: %s " % str(e) + module.fail_json(**ucs.result) + + ucs.result['changed'] = True + + +def check_vnic_props(ucs, module, dn): + props_match = True + + if module.params.get('vnic_list'): + # check vnicEther props + for vnic in module.params['vnic_list']: + child_dn = dn + '/ether-' + vnic['name'] + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + if vnic['state'] == 'absent': + props_match = False + break + else: # state == 'present' + kwargs = dict(adaptor_profile_name=vnic['adapter_policy']) + kwargs['order'] = vnic['order'] + kwargs['nw_templ_name'] = vnic['vnic_template'] + if not (mo_1.check_prop_match(**kwargs)): + props_match = False + break + else: # mo_1 did not exist + if vnic['state'] == 'present': + props_match = False + break + + return props_match + + +def check_iscsi_vnic_props(ucs, module, dn): + props_match = True + + if module.params.get('iscsi_vnic_list'): + # check vnicIScsiLCP props + for iscsi_vnic in module.params['iscsi_vnic_list']: + child_dn = dn + '/iscsi-' + iscsi_vnic['name'] + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + if iscsi_vnic['state'] == 'absent': + props_match = False + break + else: # state == 'present' + kwargs = dict(vnic_name=iscsi_vnic['overlay_vnic']) + kwargs['adaptor_profile_name'] = iscsi_vnic['iscsi_adapter_policy'] + kwargs['addr'] = iscsi_vnic['mac_address'] + if (mo_1.check_prop_match(**kwargs)): + # check vlan + child_dn = child_dn + '/vlan' + mo_2 = ucs.login_handle.query_dn(child_dn) + if mo_2: + kwargs = dict(vlan_name=iscsi_vnic['vlan_name']) + if not (mo_2.check_prop_match(**kwargs)): + props_match = False + break + else: # mo_1 props did not match + props_match = False + break + else: # mo_1 did not exist + if iscsi_vnic['state'] == 'present': + props_match = False + break + + return props_match + + +def check_lan_connecivity_props(ucs, module, mo, dn): + props_match = False + + # check top-level mo props + kwargs = dict(descr=module.params['description']) + if (mo.check_prop_match(**kwargs)): + # top-level props match, check next level mo/props + # check vnic 1st + props_match = check_vnic_props(ucs, module, dn) + + if props_match: + props_match = check_iscsi_vnic_props(ucs, module, dn) + + return props_match + + +def main(): + vnic = dict( + name=dict(type='str', required=True), + vnic_template=dict(type='str', required=True), + adapter_policy=dict(type='str', default=''), + order=dict(type='str', default='unspecified'), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + iscsi_vnic = dict( + name=dict(type='str', required=True), + overlay_vnic=dict(type='str', default=''), + iscsi_adapter_policy=dict(type='str', default=''), + mac_address=dict(type='str', default='derived'), + vlan_name=dict(type='str', default='default'), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str', required=True), + description=dict(type='str', aliases=['descr'], default=''), + vnic_list=dict(type='list', elements='dict', options=vnic), + iscsi_vnic_list=dict(type='list', elements='dict', options=iscsi_vnic), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + ucs = UCSModule(module) + # UCSModule creation above verifies ucsmsdk is present and exits on failure. + # Additional imports are done below or in called functions. + + ucs.result['changed'] = False + props_match = False + # dn is <org_dn>/lan-conn-pol-<name> + dn = module.params['org_dn'] + '/lan-conn-pol-' + module.params['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + ucs.result['changed'] = True + else: # state == 'present' + props_match = check_lan_connecivity_props(ucs, module, mo, dn) + + if module.params['state'] == 'present' and not props_match: + configure_lan_connectivity(ucs, module, dn) + + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_mac_pool.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_mac_pool.py new file mode 100644 index 000000000..35bdbfa59 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_mac_pool.py @@ -0,0 +1,185 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_mac_pool +short_description: Configures MAC address pools on Cisco UCS Manager +description: +- Configures MAC address pools and MAC address blocks on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify MAC pool is present and will create if needed. + - If C(absent), will verify MAC pool is absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the MAC pool. + - This name can be between 1 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the MAC pool is created. + required: yes + description: + description: + - A description of the MAC pool. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + order: + description: + - The Assignment Order field. + - "This can be one of the following:" + - "default - Cisco UCS Manager selects a random identity from the pool." + - "sequential - Cisco UCS Manager selects the lowest available identity from the pool." + choices: [default, sequential] + default: default + first_addr: + description: + - The first MAC address in the block of addresses. + - This is the From field in the UCS Manager MAC Blocks menu. + last_addr: + description: + - The last MAC address in the block of addresses. + - This is the To field in the UCS Manager Add MAC Blocks menu. + org_dn: + description: + - The distinguished name (dn) of the organization where the resource is assigned. + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure MAC address pool + cisco.ucs.ucs_mac_pool: + hostname: 172.16.143.150 + username: admin + password: password + name: mac-A + first_addr: 00:25:B5:00:66:00 + last_addr: 00:25:B5:00:67:F3 + order: sequential + +- name: Remove MAC address pool + cisco.ucs.ucs_mac_pool: + hostname: 172.16.143.150 + username: admin + password: password + name: mac-A + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str', required=True), + descr=dict(type='str', default='', aliases=['description']), + order=dict(type='str', default='default', choices=['default', 'sequential']), + first_addr=dict(type='str'), + last_addr=dict(type='str'), + state=dict(default='present', choices=['present', 'absent'], type='str'), + ) + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + # UCSModule verifies ucsmsdk is present and exits on failure. Imports are below ucs object creation. + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.macpool.MacpoolPool import MacpoolPool + from ucsmsdk.mometa.macpool.MacpoolBlock import MacpoolBlock + + changed = False + try: + mo_exists = False + props_match = False + # dn is <org_dn>/mac-pool-<name> + dn = module.params['org_dn'] + '/mac-pool-' + module.params['name'] + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(assignment_order=module.params['order']) + kwargs['descr'] = module.params['descr'] + if mo.check_prop_match(**kwargs): + # top-level props match, check next level mo/props + if module.params['last_addr'] and module.params['first_addr']: + # mac address block specified, check properties + block_dn = dn + '/block-' + module.params['first_addr'].upper() + '-' + module.params['last_addr'].upper() + mo_1 = ucs.login_handle.query_dn(block_dn) + if mo_1: + props_match = True + else: + # no MAC address block specified, but top-level props matched + props_match = True + + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = MacpoolPool( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['name'], + descr=module.params['descr'], + assignment_order=module.params['order'], + ) + + if module.params['last_addr'] and module.params['first_addr']: + mo_1 = MacpoolBlock( + parent_mo_or_dn=mo, + to=module.params['last_addr'], + r_from=module.params['first_addr'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_managed_objects.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_managed_objects.py new file mode 100644 index 000000000..7e679e124 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_managed_objects.py @@ -0,0 +1,256 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_managed_objects +short_description: Configures Managed Objects on Cisco UCS Manager +description: +- Configures Managed Objects on Cisco UCS Manager. +- The Python SDK module, Python class within the module (UCSM Class), and all properties must be directly specified. +- More information on the UCSM Python SDK and how to directly configure Managed Objects is available at L(UCSM Python SDK,http://ucsmsdk.readthedocs.io/). +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify that the Managed Objects are present and will create if needed. + - If C(absent), will verify that the Managed Objects are absent and will delete if needed. + choices: [ absent, present ] + default: present + objects: + description: + - List of managed objects to configure. Each managed object has suboptions the specify the Python SDK module, class, and properties to configure. + suboptions: + module: + description: + - Name of the Python SDK module implementing the required class. + required: yes + class_name: + description: + - Name of the Python class that will be used to configure the Managed Object. + required: yes + properties: + description: + - List of properties to configure on the Managed Object. See the UCSM Python SDK for information on properties for each class. + required: yes + children: + description: + - Optional list of child objects. Each child has its own module, class, and properties suboptions. + - The parent_mo_or_dn property for child objects is automatically set as the list of children is configured. + required: yes +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- CiscoUcs (@CiscoUcs) +version_added: '2.8' +''' + +EXAMPLES = r''' +- name: Configure Network Control Policy + cisco.ucs.ucs_managed_objects: + hostname: 172.16.143.150 + username: admin + password: password + objects: + - module: ucsmsdk.mometa.nwctrl.NwctrlDefinition + class: NwctrlDefinition + properties: + parent_mo_or_dn: org-root + cdp: enabled + descr: '' + lldp_receive: enabled + lldp_transmit: enabled + name: Enable-CDP-LLDP + +- name: Remove Network Control Policy + cisco.ucs.ucs_managed_objects: + hostname: 172.16.143.150 + username: admin + password: password + objects: + - module: ucsmsdk.mometa.nwctrl.NwctrlDefinition + class: NwctrlDefinition + properties: + parent_mo_or_dn: org-root + name: Enable-CDP-LLDP + state: absent + +- name: Configure Boot Policy Using JSON objects list with children + cisco.ucs.ucs_managed_objects: + hostname: 172.16.143.150 + username: admin + password: password + objects: + - { + "module": "ucsmsdk.mometa.lsboot.LsbootPolicy", + "class": "LsbootPolicy", + "properties": { + "parent_mo_or_dn": "org-root", + "name": "Python_SDS", + "enforce_vnic_name": "yes", + "boot_mode": "legacy", + "reboot_on_update": "no" + }, + "children": [ + { + "module": "ucsmsdk.mometa.lsboot.LsbootVirtualMedia", + "class": "LsbootVirtualMedia", + "properties": { + "access": "read-only-local", + "lun_id": "0", + "order": "2" + } + }, + { + "module": "ucsmsdk.mometa.lsboot.LsbootStorage", + "class": "LsbootStorage", + "properties": { + "order": "1" + }, + "children": [ + { + "module": "ucsmsdk.mometa.lsboot.LsbootLocalStorage", + "class": "LsbootLocalStorage", + "properties": {}, + "children": [ + { + "module": "ucsmsdk.mometa.lsboot.LsbootDefaultLocalImage", + "class": "LsbootDefaultLocalImage", + "properties": { + "order": "1" + } + } + ] + } + ] + } + ] + } + +- name: Remove Boot Policy Using JSON objects list + cisco.ucs.ucs_managed_objects: + hostname: 172.16.143.150 + username: admin + password: password + objects: + - { + "module": "ucsmsdk.mometa.lsboot.LsbootPolicy", + "class": "LsbootPolicy", + "properties": { + "parent_mo_or_dn": "org-root", + "name": "Python_SDS" + } + } + state: absent + + +''' + +RETURN = r''' +# +''' + +try: + from importlib import import_module + HAS_IMPORT_MODULE = True +except ImportError: + HAS_IMPORT_MODULE = False + +from copy import deepcopy +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def traverse_objects(module, ucs, managed_object, mo=''): + props_match = False + + mo_module = import_module(managed_object['module']) + mo_class = getattr(mo_module, managed_object['class']) + + if not managed_object['properties'].get('parent_mo_or_dn'): + managed_object['properties']['parent_mo_or_dn'] = mo + + mo = mo_class(**managed_object['properties']) + + existing_mo = ucs.login_handle.query_dn(mo.dn) + + if module.params['state'] == 'absent': + # mo must exist, but all properties do not have to match + if existing_mo: + if not module.check_mode: + ucs.login_handle.remove_mo(existing_mo) + ucs.result['changed'] = True + else: + if existing_mo: + # check mo props + kwargs = dict(managed_object['properties']) + # remove parent info and passwords because those aren't presented in the actual props + kwargs.pop('parent_mo_or_dn', None) + kwargs.pop('pwd', None) + kwargs.pop('password', None) + if existing_mo.check_prop_match(**kwargs): + props_match = True + + if not props_match: + if not module.check_mode: + ucs.login_handle.add_mo(mo, modify_present=True) + ucs.result['changed'] = True + + if managed_object.get('children'): + for child in managed_object['children']: + # explicit deep copy of child object since traverse_objects may modify parent mo information + copy_of_child = deepcopy(child) + traverse_objects(module, ucs, copy_of_child, mo) + + +def main(): + object_dict = dict( + module=dict(type='str', required=True), + class_name=dict(type='str', aliases=['class'], required=True), + properties=dict(type='dict', required=True), + children=dict(type='list'), + ) + argument_spec = ucs_argument_spec + argument_spec.update( + objects=dict(type='list', elements='dict', options=object_dict, required=True), + state=dict(type='str', choices=['present', 'absent'], default='present'), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + + if not HAS_IMPORT_MODULE: + module.fail_json(msg='import_module is required for this module') + ucs = UCSModule(module) + + # note that all objects specified in the object list report a single result (including a single changed). + ucs.result['changed'] = False + + for managed_object in module.params['objects']: + traverse_objects(module, ucs, managed_object) + # single commit for object and any children + if not module.check_mode and ucs.result['changed']: + try: + ucs.login_handle.commit() + except Exception as e: + # generic Exception because UCSM can throw a variety of exceptions + ucs.result['msg'] = "setup error: %s " % str(e) + module.fail_json(**ucs.result) + + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_ntp_server.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_ntp_server.py new file mode 100644 index 000000000..a917ed60a --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_ntp_server.py @@ -0,0 +1,169 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_ntp_server +short_description: Configures NTP server on Cisco UCS Manager +extends_documentation_fragment: cisco.ucs.ucs +description: +- Configures NTP server on Cisco UCS Manager. +options: + state: + description: + - If C(absent), will remove an NTP server. + - If C(present), will add or update an NTP server. + choices: [absent, present] + default: present + + ntp_server: + description: + - NTP server IP address or hostname. + - Enter up to 63 characters that form a valid hostname. + - Enter a valid IPV4 Address. + aliases: [ name ] + default: "" + + description: + description: + - A user-defined description of the NTP server. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + default: "" + +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: "2.7" +''' + +EXAMPLES = r''' +- name: Configure NTP server + cisco.ucs.ucs_ntp_server: + hostname: 172.16.143.150 + username: admin + password: password + ntp_server: 10.10.10.10 + description: Internal NTP Server by IP address + state: present + +- name: Configure NTP server + cisco.ucs.ucs_ntp_server: + hostname: 172.16.143.150 + username: admin + password: password + ntp_server: pool.ntp.org + description: External NTP Server by hostname + state: present + +- name: Remove NTP server + cisco.ucs.ucs_ntp_server: + hostname: 172.16.143.150 + username: admin + password: password + ntp_server: 10.10.10.10 + state: absent + +- name: Remove NTP server + cisco.ucs.ucs_ntp_server: + hostname: 172.16.143.150 + username: admin + password: password + ntp_server: pool.ntp.org + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def run_module(): + argument_spec = ucs_argument_spec + argument_spec.update( + ntp_server=dict(type='str', aliases=['name']), + description=dict(type='str', aliases=['descr'], default=''), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['ntp_server']], + ], + ) + # UCSModule verifies ucsmsdk is present and exits on failure. Imports are below ucs object creation. + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.comm.CommNtpProvider import CommNtpProvider + + changed = False + try: + mo_exists = False + props_match = False + + dn = 'sys/svc-ext/datetime-svc/ntp-' + module.params['ntp_server'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(descr=module.params['description']) + if mo.check_prop_match(**kwargs): + props_match = True + + if not props_match: + if not module.check_mode: + # update/add mo + mo = CommNtpProvider(parent_mo_or_dn='sys/svc-ext/datetime-svc', + name=module.params['ntp_server'], + descr=module.params['description']) + ucs.login_handle.add_mo(mo, modify_present=True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_org.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_org.py new file mode 100644 index 000000000..51237ca65 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_org.py @@ -0,0 +1,227 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_org + +short_description: Manages UCS Organizations for UCS Manager + +description: + - Manages UCS Organizations for UCS Manager. + +extends_documentation_fragment: cisco.ucs.ucs + +options: + state: + description: + - If C(absent), will remove organization. + - If C(present), will create or update organization. + choices: [absent, present] + default: present + type: str + + org_name: + description: + - The name of the organization. + - Enter up to 16 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ name ] + type: str + + parent_org_path: + description: + - A forward slash / separated hierarchical path from the root organization to the parent of the organization to be added or updated. + - UCS Manager supports a hierarchical structure of organizations up to five levels deep not including the root organization. + - For example the parent_org_path for an organization named level5 could be root/level1/level2/level3/level4 + default: root + type: str + + description: + description: + - A user-defined description of the organization. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + type: str + + delegate_to: + description: + - Where the module will be run + default: localhost + type: str + +requirements: +- ucsmsdk + +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: "2.8" +''' + +EXAMPLES = r''' +- name: Add UCS Organization + cisco.ucs.ucs_org: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + org_name: test + description: testing org + state: present + delegate_to: localhost + +- name: Update UCS Organization + cisco.ucs.ucs_org: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + org_name: test + description: Testing org + state: present + delegate_to: localhost + +- name: Add UCS Organization + cisco.ucs.ucs_org: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + org_name: level1 + parent_org_path: root + description: level1 org + state: present + delegate_to: localhost + +- name: Add UCS Organization + cisco.ucs.ucs_org: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + org_name: level2 + parent_org_path: root/level1 + description: level2 org + state: present + +- name: Add UCS Organization + cisco.ucs.ucs_org: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + org_name: level3 + parent_org_path: root/level1/level2 + description: level3 org + state: present + +- name: Remove UCS Organization + cisco.ucs.ucs_org: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + org_name: level2 + parent_org_path: root/level1 + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_name=dict(type='str', aliases=['name']), + parent_org_path=dict(type='str', default='root'), + description=dict(type='str', aliases=['descr']), + state=dict(type='str', default='present', choices=['present', 'absent']), + delegate_to=dict(type='str', default='localhost'), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['org_name']], + ], + ) + + # UCSModule verifies ucsmsdk is present and exits on failure. + # Imports are below for UCS object creation. + ucs = UCSModule(module) + from ucsmsdk.mometa.org.OrgOrg import OrgOrg + + err = False + changed = False + requested_state = module.params['state'] + + kwargs = dict() + + if module.params['description'] is not None: + kwargs['descr'] = module.params['description'] + + try: + parent_org_dn = 'org-' + module.params['parent_org_path'].replace('/', '/org-') + dn = parent_org_dn + '/org-' + module.params['org_name'] + + mo = ucs.login_handle.query_dn(dn) + + # Determine state change + if mo: + # Object exists, if it should exist has anything changed? + if requested_state == 'present': + # Do some or all Object properties not match, that is a change + if not mo.check_prop_match(**kwargs): + changed = True + + # Object does not exist but should, that is a change + else: + if requested_state == 'present': + changed = True + + # Object exists but should not, that is a change + if mo and requested_state == 'absent': + changed = True + + # Apply state if not check_mode + if changed and not module.check_mode: + if requested_state == 'absent': + ucs.login_handle.remove_mo(mo) + else: + kwargs['parent_mo_or_dn'] = parent_org_dn + kwargs['name'] = module.params['org_name'] + if module.params['description'] is not None: + kwargs['descr'] = module.params['description'] + + mo = OrgOrg(**kwargs) + ucs.login_handle.add_mo(mo, modify_present=True) + ucs.login_handle.commit() + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_query.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_query.py new file mode 100644 index 000000000..f9a7a1a01 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_query.py @@ -0,0 +1,172 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_query + +short_description: Queries UCS Manager objects by class or distinguished name + +description: + -Queries UCS Manager objects by class or distinguished name. + +extends_documentation_fragment: cisco.ucs.ucs + +options: + class_ids: + description: + - One or more UCS Manager Class IDs to query. + - As a comma separated list + type: str + + distinguished_names: + description: + - One or more UCS Manager Distinguished Names to query. + - As a comma separated list + type: str + + delegate_to: + description: + - Where the module will be run + default: localhost + type: str + +requirements: + - ucsmsdk + +author: + - John McDonough (@movinalot) + - CiscoUcs (@CiscoUcs) +version_added: "2.8" +''' + +EXAMPLES = r''' +- name: Query UCS Class ID + cisco.ucs.ucs_query: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + class_ids: computeBlade + delegate_to: localhost + +- name: Query UCS Class IDs + cisco.ucs.ucs_query: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + class_ids: computeBlade, fabricVlan + delegate_to: localhost + +- name: Query UCS Distinguished Name + cisco.ucs.ucs_query: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + distinguished_names: org-root + delegate_to: localhost + +- name: Query UCS Distinguished Names + cisco.ucs.ucs_query: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + distinguished_names: org-root, sys/rack-unit-1, sys/chassis-1/blade-2 + delegate_to: localhost +''' + +RETURN = ''' +objects: + description: results JSON encodded + type: dict +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def retrieve_class_id(class_id, ucs): + return ucs.login_handle.query_classid(class_id) + + +def retrieve_distinguished_name(distinguished_name, ucs): + return ucs.login_handle.query_dn(distinguished_name) + + +def make_mo_dict(ucs_mo): + obj_dict = {} + for mo_property in ucs_mo.prop_map.values(): + obj_dict[mo_property] = getattr(ucs_mo, mo_property) + return obj_dict + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + class_ids=dict(type='str'), + distinguished_names=dict(type='str'), + delegate_to=dict(type='str', default='localhost'), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=False, + mutually_exclusive=[ + ['class_ids', 'distinguished_names'], + ], + ) + + # UCSModule verifies ucsmsdk is present and exits on failure. + # Imports are below for UCS object creation. + ucs = UCSModule(module) + err = False + query_result = {} + + try: + if module.params['class_ids']: + class_ids = [ + x.strip() for x in module.params['class_ids'].split(',') + ] + for class_id in class_ids: + query_result[class_id] = [] + ucs_mos = retrieve_class_id(class_id, ucs) + if ucs_mos: + for ucs_mo in ucs_mos: + query_result[class_id].append(make_mo_dict(ucs_mo)) + + ucs.result['objects'] = query_result + + elif module.params['distinguished_names']: + distinguished_names = [ + x.strip() + for x in module.params['distinguished_names'].split(',') + ] + for distinguished_name in distinguished_names: + query_result[distinguished_name] = {} + ucs_mo = retrieve_distinguished_name(distinguished_name, ucs) + + if ucs_mo: + query_result[distinguished_name] = make_mo_dict(ucs_mo) + + ucs.result['objects'] = query_result + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + if err: + module.fail_json(**ucs.result) + + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_san_connectivity.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_san_connectivity.py new file mode 100644 index 000000000..7e9c1054b --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_san_connectivity.py @@ -0,0 +1,248 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_san_connectivity +short_description: Configures SAN Connectivity Policies on Cisco UCS Manager +description: +- Configures SAN Connectivity Policies on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify SAN Connectivity Policies are present and will create if needed. + - If C(absent), will verify SAN Connectivity Policies are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the SAN Connectivity Policy. + - This name can be between 1 and 16 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the policy is created. + required: yes + description: + description: + - A description of the policy. + - Cisco recommends including information about where and when to use the policy. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + wwnn_pool: + description: + - Name of the WWNN pool to use for WWNN assignment. + default: default + vhba_list: + description: + - List of vHBAs used by the SAN Connectivity Policy. + - vHBAs used by the SAN Connectivity Policy must be created from a vHBA template. + - "Each list element has the following suboptions:" + - "= name" + - " The name of the virtual HBA (required)." + - "= vhba_template" + - " The name of the virtual HBA template (required)." + - "- adapter_policy" + - " The name of the Fibre Channel adapter policy." + - " A user defined policy can be used, or one of the system defined policies (default, Linux, Solaris, VMware, Windows, WindowsBoot)" + - " [Default: default]" + - "- order" + - " String specifying the vHBA assignment order (e.g., '1', '2')." + - " [Default: unspecified]" + org_dn: + description: + - Org dn (distinguished name) + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure SAN Connectivity Policy + cisco.ucs.ucs_san_connectivity: + hostname: 172.16.143.150 + username: admin + password: password + name: Cntr-FC-Boot + wwnn_pool: WWNN-Pool + vhba_list: + - name: Fabric-A + vhba_template: vHBA-Template-A + adapter_policy: Linux + - name: Fabric-B + vhba_template: vHBA-Template-B + adapter_policy: Linux + +- name: Remove SAN Connectivity Policy + cisco.ucs.ucs_san_connectivity: + hostname: 172.16.143.150 + username: admin + password: password + name: Cntr-FC-Boot + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str'), + descr=dict(type='str'), + wwnn_pool=dict(type='str', default='default'), + vhba_list=dict(type='list'), + state=dict(type='str', default='present', choices=['present', 'absent']), + san_connectivity_list=dict(type='list'), + ) + + # Note that use of san_connectivity_list is an experimental feature which allows multiple resource updates with a single UCSM connection. + # Support for san_connectivity_list may change or be removed once persistent UCS connections are supported. + # Either san_connectivity_list or name is required (user can specify either a list or single resource). + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_one_of=[ + ['san_connectivity_list', 'name'], + ], + mutually_exclusive=[ + ['san_connectivity_list', 'name'], + ], + ) + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.vnic.VnicSanConnPolicy import VnicSanConnPolicy + from ucsmsdk.mometa.vnic.VnicFcNode import VnicFcNode + from ucsmsdk.mometa.vnic.VnicFc import VnicFc + from ucsmsdk.mometa.vnic.VnicFcIf import VnicFcIf + + changed = False + try: + # Only documented use is a single resource, but to also support experimental + # feature allowing multiple updates all params are converted to a san_connectivity_list below. + + if module.params['san_connectivity_list']: + # directly use the list (single resource and list are mutually exclusive + san_connectivity_list = module.params['san_connectivity_list'] + else: + # single resource specified, create list from the current params + san_connectivity_list = [module.params] + for san_connectivity in san_connectivity_list: + mo_exists = False + props_match = False + # set default params. Done here to set values for lists which can't be done in the argument_spec + if not san_connectivity.get('descr'): + san_connectivity['descr'] = '' + if not san_connectivity.get('wwnn_pool'): + san_connectivity['wwnn_pool'] = 'default' + if san_connectivity.get('vhba_list'): + for vhba in san_connectivity['vhba_list']: + if not vhba.get('adapter_policy'): + vhba['adapter_policy'] = '' + if not vhba.get('order'): + vhba['order'] = 'unspecified' + # dn is <org_dn>/san-conn-pol-<name> + dn = module.params['org_dn'] + '/san-conn-pol-' + san_connectivity['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + # check top-level mo props + kwargs = dict(descr=san_connectivity['descr']) + if (mo.check_prop_match(**kwargs)): + # top-level props match, check next level mo/props + # vnicFcNode object + child_dn = dn + '/fc-node' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(ident_pool_name=san_connectivity['wwnn_pool']) + if (mo_1.check_prop_match(**kwargs)): + if not san_connectivity.get('vhba_list'): + props_match = True + else: + # check vnicFc props + for vhba in san_connectivity['vhba_list']: + child_dn = dn + '/fc-' + vhba['name'] + mo_2 = ucs.login_handle.query_dn(child_dn) + kwargs = {} + kwargs['adaptor_profile_name'] = vhba['adapter_policy'] + kwargs['order'] = vhba['order'] + kwargs['nw_templ_name'] = vhba['vhba_template'] + if (mo_2.check_prop_match(**kwargs)): + props_match = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = VnicSanConnPolicy( + parent_mo_or_dn=module.params['org_dn'], + name=san_connectivity['name'], + descr=san_connectivity['descr'], + ) + mo_1 = VnicFcNode( + parent_mo_or_dn=mo, + ident_pool_name=san_connectivity['wwnn_pool'], + addr='pool-derived', + ) + if san_connectivity.get('vhba_list'): + for vhba in san_connectivity['vhba_list']: + mo_2 = VnicFc( + parent_mo_or_dn=mo, + name=vhba['name'], + adaptor_profile_name=vhba['adapter_policy'], + nw_templ_name=vhba['vhba_template'], + order=vhba['order'], + ) + mo_2_1 = VnicFcIf( + parent_mo_or_dn=mo_2, + name='default', + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_scrub_policy.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_scrub_policy.py new file mode 100644 index 000000000..f6c262e69 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_scrub_policy.py @@ -0,0 +1,320 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_scrub_policy + +short_description: Manages UCS Scrub Policies on UCS Manager + +description: + - Manages UCS Scrub Policies on UCS Manager. + +extends_documentation_fragment: cisco.ucs.ucs + +options: + state: + description: + - If C(absent), will remove organization. + - If C(present), will create or update organization. + choices: [absent, present] + default: present + type: str + + name: + description: + - The name of the organization. + - Enter up to 16 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote)" + - "= (equal sign), > (greater than), < (less than), ' (single quote)." + required: true + type: str + + description: + description: + - A user-defined description of the organization. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote)" + - "= (equal sign), > (greater than), < (less than), ' (single quote)." + aliases: [ descr ] + type: str + + bios_settings_scrub: + description: + - Scrub the BIOS settings. + - If the field is set to Yes, when a service profile containing this + - scrub policy is disassociated from a server, the BIOS settings for + - that server are erased and reset to the defaults for that server + - type and vendor. If this field is set to No, the BIOS settings are + - preserved. + - yes scrub the BIOS settings. + - no do not scrub the BIOS settings. + choices: [yes, no] + type: str + + disk_scrub: + description: + - Scrub the BIOS settings. + - If this field is set to Yes, when a service profile containing this + - scrub policy is disassociated from a server, all data on the server + - local drives is completely erased. If this field is set to No, the + - data on the local drives is preserved, including all local storage + - configuration. + - yes scrub the server disks. + - no do not scrub the server disks. + choices: [yes, no] + type: str + + flex_flash_scrub: + description: + - Scrub the BIOS settings. + - If the field is set to Yes, the HV partition on the SD card is + - formatted using the PNUOS formatting utility when the server is + - reacknowledged. If this field is set to No, the SD card is preserved. + - yes scrub the flex flash. + - no do not scrub the flex flash. + choices: [yes, no] + type: str + + persistent_memory_scrub: + description: + - Scrub the BIOS settings. + - If the field is set to Yes, when a service profile containing this + - scrub policy is disassociated from a server, all persistent memory + - modules for that server are erased and reset to the defaults for that + - server type and vendor. If this field is set to No, the persistent + - memory modules are preserved. + - yes scrub the persistent memory. + - no do not scrub the persistent memory. + choices: [yes, no] + type: str + + org_dn: + description: + - Org dn (distinguished name) + default: org-root + type: str + +requirements: +- ucsmsdk + +author: +- John McDonough (@movinalot) +version_added: "2.9" +''' + +EXAMPLES = r''' +- name: Add UCS Scrub Policy + cisco.ucs.ucs_scrub_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + description: Scrub All Policy + name: all_scrub + bios_settings_scrub: yes + disk_scrub: yes + flex_flash_scrub: yes + persistent_memory_scrub: yes + delegate_to: localhost + +- name: Add UCS Scrub Policy in an Organization + cisco.ucs.ucs_scrub_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + org_dn: org-root/org-prod + name: all_scrub + description: Scrub All Policy Org Prod servers + bios_settings_scrub: yes + disk_scrub: yes + flex_flash_scrub: yes + persistent_memory_scrub: yes + delegate_to: localhost + +- name: Update UCS Scrub Policy + cisco.ucs.ucs_scrub_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + org_dn: org-root/org-prod + name: BD_scrub + description: Scrub BIOS and Disk Policy Org Prod servers + bios_settings_scrub: yes + disk_scrub: yes + flex_flash_scrub: no + persistent_memory_scrub: no + delegate_to: localhost + +- name: Update UCS Scrub Policy + cisco.ucs.ucs_scrub_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + org_dn: org-root/org-prod + name: BD_scrub + description: Scrub BIOS and Disk Policy Org Prod servers + bios_settings_scrub: yes + disk_scrub: yes + flex_flash_scrub: yes + delegate_to: localhost + +- name: Delete UCS Scrub Policy + cisco.ucs.ucs_scrub_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: absent + org_dn: org-root/org-prod + name: BD_scrub + delegate_to: localhost + +- name: Delete UCS Scrub Policy + cisco.ucs.ucs_scrub_policy: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: absent + name: BD_scrub + delegate_to: localhost +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import ( + UCSModule, + ucs_argument_spec +) + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(required=True, type='str'), + descr=dict(type='str'), + description=dict(type='str', aliases=['descr']), + bios_settings_scrub=dict(type='str', choices=['yes', 'no']), + disk_scrub=dict(type='str', choices=['yes', 'no']), + flex_flash_scrub=dict(type='str', choices=['yes', 'no']), + persistent_memory_scrub=dict(type='str', choices=['yes', 'no']), + state=dict( + type='str', default='present', + choices=['present', 'absent'] + ), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['name']], + ], + ) + + # UCSModule verifies ucsmsdk is present and exits on failure. + # Imports are below for UCS object creation. + ucs = UCSModule(module) + from importlib import import_module + from ucsmsdk.ucscoreutils import get_meta_info + + # The Class(es) this module is managing + module_file = 'ucsmsdk.mometa.compute.ComputeScrubPolicy' + module_class = 'ComputeScrubPolicy' + mo_module = import_module(module_file) + mo_class = getattr(mo_module, module_class) + + META = get_meta_info(class_id=module_class) + + err = False + changed = False + requested_state = module.params['state'] + + kwargs = dict() + + # Manage Aliased Attributes + for attribute in ['descr:description']: + attribute_alias = attribute.split(':') + if module.params[attribute_alias[1]] is not None: + kwargs[attribute_alias[0]] = module.params[attribute_alias[1]] + + # Manage Attributes + for attribute in [ + 'bios_settings_scrub', + 'descr', + 'disk_scrub', + 'flex_flash_scrub', + 'persistent_memory_scrub' + ]: + if module.params[attribute] is not None: + kwargs[attribute] = module.params[attribute] + + try: + dn = ( + module.params['org_dn'] + '/' + + META.rn[0:META.rn.index('-') + 1] + + module.params['name'] + ) + mo = ucs.login_handle.query_dn(dn) + + # Determine state change + if mo: + # Object exists, if it should exist has anything changed? + if requested_state == 'present': + # Do some or all Object properties not match, that is a change + + if not mo.check_prop_match(**kwargs): + changed = True + + # Object does not exist but should, that is a change + else: + if requested_state == 'present': + changed = True + + # Object exists but should not, that is a change + if mo and requested_state == 'absent': + changed = True + + # Apply state if not check_mode + if changed and not module.check_mode: + if requested_state == 'absent': + ucs.login_handle.remove_mo(mo) + else: + kwargs['parent_mo_or_dn'] = module.params['org_dn'] + kwargs['name'] = module.params['name'] + + mo = mo_class(**kwargs) + ucs.login_handle.add_mo(mo, modify_present=True) + ucs.login_handle.commit() + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_serial_over_lan_policy.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_serial_over_lan_policy.py new file mode 100644 index 000000000..0dd9ba57a --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_serial_over_lan_policy.py @@ -0,0 +1,271 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_serial_over_lan_policy + +short_description: Manages UCS Serial Over Lan Policies on UCS Manager + +description: + - Manages UCS Serial Over Lan Policies on UCS Manager. + +extends_documentation_fragment: cisco.ucs.ucs + +options: + state: + description: + - If C(absent), will remove Serial Over Lan Policy. + - If C(present), will create or update Serial Over Lan Policy. + choices: [absent, present] + default: present + type: str + + name: + description: + - The name of the serial over lan policy. + - Enter up to 16 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote)" + - "= (equal sign), > (greater than), < (less than), ' (single quote)." + required: true + type: str + + description: + description: + - A user-defined description of the serial over lan policy. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote)" + - "= (equal sign), > (greater than), < (less than), ' (single quote)." + aliases: [ descr ] + type: str + + admin_state: + description: + - The administrative state of the serial over lan policy. + - disable Serial over LAN access is blocked. + - enable Serial over LAN access is permitted. + choices: [disable, enable] + type: str + + speed: + description: + - The transmission speed of the serial over lan policy. + choices: [9600, 19200, 38400, 57600, 115200] + type: str + + org_dn: + description: + - Org dn (distinguished name) of the serial over lan policy. + default: org-root + type: str + +requirements: +- ucsmsdk + +author: +- John McDonough (@movinalot) +version_added: "2.9" +''' + +EXAMPLES = r''' +- name: Add UCS Serial Over Lan Policy + cisco.ucs.ucs_serial_over_lan: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + name: sol_org_root + description: Serial Over Lan for Org root servers + admin_state: enable + speed: 115200 + delegate_to: localhost + +- name: Add UCS Serial Over Lan Policy in Organization + cisco.ucs.ucs_serial_over_lan: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + org_dn: org-root/org-prod + name: sol_org_prod + description: Serial Over Lan for Org Prod servers + admin_state: enable + speed: 115200 + delegate_to: localhost + +- name: Update UCS Serial Over Lan Policy in Organization + cisco.ucs.ucs_serial_over_lan: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + org_dn: org-root/org-prod + name: sol_org_prod + description: Serial Over Lan for Org Prod servers + admin_state: enable + speed: 38400 + delegate_to: localhost + +- name: Update UCS Serial Over Lan Policy in Organization + cisco.ucs.ucs_serial_over_lan: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: present + org_dn: org-root/org-prod + name: sol_org_prod + descr: Serial Over Lan for Org Prod servers + admin_state: enable + speed: 57600 + delegate_to: localhost + +- name: Delete UCS Serial Over Lan Policy in Organization + cisco.ucs.ucs_serial_over_lan: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: absent + org_dn: org-root/org-prod + name: sol_org_prod + delegate_to: localhost + +- name: Delete UCS Serial Over Lan Policy + cisco.ucs.ucs_serial_over_lan: + hostname: "{{ ucs_hostname }}" + username: "{{ ucs_username }}" + password: "{{ ucs_password }}" + state: absent + name: sol_org_root + delegate_to: localhost +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import ( + UCSModule, + ucs_argument_spec +) + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(required=True, type='str'), + descr=dict(type='str'), + description=dict(type='str', aliases=['descr']), + admin_state=dict(type='str', choices=['enable', 'disable']), + speed=dict(type='str', choices=[ + '9600', '19200', '38400', '57600', '115200' + ]), + state=dict( + type='str', default='present', + choices=['present', 'absent'] + ), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['name']], + ], + ) + + # UCSModule verifies ucsmsdk is present and exits on failure. + # Imports are below for UCS object creation. + ucs = UCSModule(module) + from importlib import import_module + from ucsmsdk.ucscoreutils import get_meta_info + + # The Class(es) this module is managing + module_file = 'ucsmsdk.mometa.sol.SolPolicy' + module_class = 'SolPolicy' + mo_module = import_module(module_file) + mo_class = getattr(mo_module, module_class) + + META = get_meta_info(class_id=module_class) + + err = False + changed = False + requested_state = module.params['state'] + + kwargs = dict() + + # Manage Aliased Attributes + for attribute in ['descr:description']: + attribute_alias = attribute.split(':') + if module.params[attribute_alias[1]] is not None: + kwargs[attribute_alias[0]] = module.params[attribute_alias[1]] + + # Manage Attributes + for attribute in ['admin_state', 'descr', 'speed']: + if module.params[attribute] is not None: + kwargs[attribute] = module.params[attribute] + + try: + dn = ( + module.params['org_dn'] + '/' + + META.rn[0:META.rn.index('-') + 1] + + module.params['name'] + ) + mo = ucs.login_handle.query_dn(dn) + + # Determine state change + if mo: + # Object exists, if it should exist has anything changed? + if requested_state == 'present': + # Do some or all Object properties not match, that is a change + + if not mo.check_prop_match(**kwargs): + changed = True + + # Object does not exist but should, that is a change + else: + if requested_state == 'present': + changed = True + + # Object exists but should not, that is a change + if mo and requested_state == 'absent': + changed = True + + # Apply state if not check_mode + if changed and not module.check_mode: + if requested_state == 'absent': + ucs.login_handle.remove_mo(mo) + else: + kwargs['parent_mo_or_dn'] = module.params['org_dn'] + kwargs['name'] = module.params['name'] + + mo = mo_class(**kwargs) + ucs.login_handle.add_mo(mo, modify_present=True) + ucs.login_handle.commit() + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_server_maintenance.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_server_maintenance.py new file mode 100644 index 000000000..a8aca9432 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_server_maintenance.py @@ -0,0 +1,158 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_server_maintenance +short_description: Creates Server Maintenance Policy on Cisco UCS Manager +version_added: 2.10 +description: +- Configures Server Maintenance Policy on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify Server Maintenance Policy is present and will create if needed. + - If C(absent), will verify Server Maintenance Policy is absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name assigned to the Server Maintenance Policy. + - The Server Maintenance Policy name is case sensitive. + - This name can be between 1 and 16 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the Server Maintenance Policy is created. + required: yes + description: + description: + - A description of the Server Maintenance Package Policy. + - Cisco recommends including information about where and when to use the policy. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + trigger_config: + description: + - This option is used in combination with either User Ack (user-ack) or Timer Automatic (timer-automatic). + - When the On Next Boot option is enabled, the host OS reboot, shutdown, or server reset also triggers the associated FSM to apply the changes. + - Note that de-selecting the On Next Boot option disables the Maintenance Policy on the BMC. + choices: [on-next-boot] + uptime_disr: + description: + - When a Server profile is associated with a Server, or when changes are made to a Server profile that is already associated with a Server, you must reboot the Server to complete the process. + - The Reboot Policy field determines when the reboot occurs for Server associated with any Server profiles that include this maintenance policy. + choices: [immediate, timer-automatic, user-ack] + required: true +requirements: +- ucsmsdk +author: +- Brett Johnson (@brettjohnson008) +''' + +EXAMPLES = r''' +- name: Add Server Maintenance Policy + cisco.ucs.ucs_server_maintenance: + hostname: 172.16.143.150 + username: admin + password: password + name: user-ack + uptime_disr: user-ack + trigger_config: on-next-boot +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + name=dict(type='str', required=True), + description=dict(type='str', default=''), + trigger_config=dict(type='str', default='', choices=['on-next-boot']), + uptime_disr=dict(type='str', required=True, choices=['immediate', 'timer-automatic', 'user-ack']), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + + ucs = UCSModule(module) + + err = False + + # UCSModule creation above verifies ucsmsdk is present and exits on failure, so additional imports are done below. + from ucsmsdk.mometa.lsmaint.LsmaintMaintPolicy import LsmaintMaintPolicy + + changed = False + try: + mo_exists = False + props_match = False + dn_base = 'org-root' + dn = dn_base + '/maint-' + module.params['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(name=module.params['name']) + kwargs['descr'] = module.params['description'] + kwargs['trigger_config'] = module.params['trigger_config'] + kwargs['uptime_disr'] = module.params['uptime_disr'] + if mo.check_prop_match(**kwargs): + props_match = True + + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = LsmaintMaintPolicy( + parent_mo_or_dn=dn_base, + name=module.params['name'], + descr=module.params['description'], + trigger_config=module.params['trigger_config'], + uptime_disr=module.params['uptime_disr'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_service_profile_association.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_service_profile_association.py new file mode 100644 index 000000000..81d9aa4bb --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_service_profile_association.py @@ -0,0 +1,252 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_service_profile_association +short_description: Configures Service Profile Association on Cisco UCS Manager +description: +- Configures Service Profile Association (change association or disassociate) on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify service profile association and associate with specified server or server pool if needed. + - If C(absent), will verify service profile is not associated and will disassociate if needed. This is the same as specifying Assign Later in the webUI. + choices: [present, absent] + default: present + service_profile_name: + description: + - The name of the Service Profile being associated or disassociated. + required: yes + server_assignment: + description: + - "Specifies how to associate servers with this service profile using the following choices:" + - "server - Use to pre-provision a slot or select an existing server. Slot or server is specified by the server_dn option." + - "pool - Use to select from a server pool. The server_pool option specifies the name of the server pool to use." + - Option is not valid if the service profile is bound to a template. + - Optional if the state is absent. + choices: [server, pool] + required: yes + server_dn: + description: + - The Distinguished Name (dn) of the server object used for pre-provisioning or selecting an existing server. + - Required if the server_assignment option is server. + - Optional if the state is absent. + server_pool_name: + description: + - Name of the server pool used for server pool based assignment. + - Required if the server_assignment option is pool. + - Optional if the state is absent. + restrict_migration: + description: + - Restricts the migration of the service profile after it has been associated with a server. + - If set to no, Cisco UCS Manager does not perform any compatibility checks on the new server before migrating the existing service profile. + - If set to no and the hardware of both servers used in migration are not similar, the association might fail. + choices: ['yes', 'no'] + default: 'no' + org_dn: + description: + - The distinguished name (dn) of the organization where the resource is assigned. + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- CiscoUcs (@CiscoUcs) +version_added: 2.10 +''' + +EXAMPLES = r''' +- name: Change Service Profile Association to server pool Container-Pool and restrict migration + cisco.ucs.ucs_service_profile_association: + hostname: 172.16.143.150 + username: admin + password: password + service_profile_name: test-sp + server_assignment: pool + server_pool_name: Container-Pool + restrict_migration: 'yes' + +- name: Attempt to change association once a minute for up to 10 minutes + cisco.ucs.ucs_service_profile_association: + hostname: 172.16.143.150 + username: admin + password: password + service_profile_name: test-sp + server_assignment: server + server_dn: sys/chassis-2/blade-1 + register: result + until: result.assign_state == 'assigned' and result.assoc_state == 'associated' + retries: 10 + delay: 60 + +- name: Disassociate Service Profile + cisco.ucs.ucs_service_profile_association: + hostname: 172.16.143.150 + username: admin + password: password + service_profile_name: test-sp + state: absent +''' + +RETURN = r''' +assign_state: + description: The logical server Assigned State (assigned, unassigned, or failed). + returned: success + type: string + sample: assigned +assoc_state: + description: The logical server Association State (associated or unassociated). + returned: success + type: string + sample: associated +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + service_profile_name=dict(type='str', required=True), + server_assignment=dict(type='str', choices=['server', 'pool']), + server_dn=dict(type='str'), + server_pool_name=dict(type='str'), + restrict_migration=dict(type='str', default='no', choices=['yes', 'no']), + state=dict(default='present', choices=['present', 'absent'], type='str'), + ) + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['server_assignment']], + ['server_assignment', 'server', ['server_dn']], + ['server_assignment', 'pool', ['server_pool_name']], + ], + mutually_exclusive=[ + ['server_dn', 'server_pool_name'], + ], + ) + # UCSModule verifies ucsmsdk is present and exits on failure. Imports are below ucs object creation. + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.ls.LsRequirement import LsRequirement + from ucsmsdk.mometa.ls.LsBinding import LsBinding + from ucsmsdk.mometa.ls.LsServer import LsServer + + changed = False + ucs.result['assign_state'] = 'unassigned' + ucs.result['assoc_state'] = 'unassociated' + try: + ls_mo_exists = False + pn_mo_exists = False + pn_req_mo_exists = False + props_match = False + + # logical server distinguished name is <org>/ls-<name> and physical node dn appends 'pn' or 'pn-req' + ls_dn = module.params['org_dn'] + '/ls-' + module.params['service_profile_name'] + ls_mo = ucs.login_handle.query_dn(ls_dn) + if ls_mo: + ls_mo_exists = True + pn_dn = ls_dn + '/pn' + pn_mo = ucs.login_handle.query_dn(pn_dn) + if pn_mo: + pn_mo_exists = True + + pn_req_dn = ls_dn + '/pn-req' + pn_req_mo = ucs.login_handle.query_dn(pn_req_dn) + if pn_req_mo: + pn_req_mo_exists = True + + if module.params['state'] == 'absent': + if ls_mo_exists and ls_mo.assign_state != 'unassigned': + if pn_mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(pn_mo) + ucs.login_handle.commit() + changed = True + elif pn_req_mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(pn_req_mo) + ucs.login_handle.commit() + changed = True + elif ls_mo_exists: + # check if logical server is assigned and associated + ucs.result['assign_state'] = ls_mo.assign_state + ucs.result['assoc_state'] = ls_mo.assoc_state + if module.params['server_assignment'] == 'pool' and pn_req_mo_exists: + # check the current pool + kwargs = dict(name=module.params['server_pool_name']) + kwargs['restrict_migration'] = module.params['restrict_migration'] + if pn_req_mo.check_prop_match(**kwargs): + props_match = True + elif pn_mo_exists: + kwargs = dict(pn_dn=module.params['server_dn']) + kwargs['restrict_migration'] = module.params['restrict_migration'] + if pn_mo.check_prop_match(**kwargs): + props_match = True + + if not props_match: + if not module.check_mode: + # create if mo does not already exist in desired state + mo = LsServer( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['service_profile_name'], + ) + if module.params['server_assignment'] == 'pool': + if pn_mo_exists: + ucs.login_handle.remove_mo(pn_mo) + + mo_1 = LsRequirement( + parent_mo_or_dn=mo, + name=module.params['server_pool_name'], + restrict_migration=module.params['restrict_migration'], + ) + else: + mo_1 = LsBinding( + parent_mo_or_dn=mo, + pn_dn=module.params['server_dn'], + restrict_migration=module.params['restrict_migration'], + ) + ucs.login_handle.add_mo(mo_1, True) + ucs.login_handle.commit() + + pn_req_mo = ucs.login_handle.query_dn(pn_req_dn) + if pn_req_mo: + # profiles from templates will add a server pool, so remove and add the server again + ucs.login_handle.remove_mo(pn_req_mo) + + ucs.login_handle.add_mo(mo_1, True) + ucs.login_handle.commit() + ls_mo = ucs.login_handle.query_dn(ls_dn) + if ls_mo: + ucs.result['assign_state'] = ls_mo.assign_state + ucs.result['assoc_state'] = ls_mo.assoc_state + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_service_profile_from_template.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_service_profile_from_template.py new file mode 100644 index 000000000..8773f03f0 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_service_profile_from_template.py @@ -0,0 +1,184 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_service_profile_from_template +short_description: Configures Service Profiles from templates on Cisco UCS Manager +description: +- Configures Service Profile created from templates on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify Service Profiles are present and will create if needed. + - If C(absent), will verify Service Profiles are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the service profile. + - This name can be between 2 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - This name must be unique across all service profiles and service profile templates within the same organization. + required: yes + source_template: + description: + - The name of the service profile template used to create this serivce profile. + required: yes + power_state: + description: + - The power state to be applied when this service profile is associated with a server. + - If no value is provided, the power_state for the service profile will not be modified. + choices: [up, down] + user_label: + description: + - The User Label you want to assign to this service profile. + org_dn: + description: + - Org dn (distinguished name) + default: org-root + description: + description: + - Optional + - The Description of the service profile +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure Service Profile from Template + cisco.ucs.ucs_service_profile_from_template: + hostname: 172.16.143.150 + username: admin + password: password + name: test-sp-instance1 + source_template: test-sp + discription: Created from Ansible + +- name: Remove Service Profile + cisco.ucs.ucs_service_profile_from_template: + hostname: 172.16.143.150 + username: admin + password: password + name: test-sp-instance1 + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str', required=True), + source_template=dict(type='str', required=True), + user_label=dict(type='str', default=''), + power_state=dict(type='str', choices=['up', 'down']), + state=dict(type='str', default='present', choices=['present', 'absent']), + description=dict(type='str', default=''), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + ucs = UCSModule(module) + + err = False + + # UCSModule creation above verifies ucsmsdk is present and exits on failure. Additional imports are done below. + from ucsmsdk.mometa.ls.LsServer import LsServer + from ucsmsdk.mometa.ls.LsPower import LsPower + + changed = False + try: + mo_exists = False + props_match = False + dn = module.params['org_dn'] + '/ls-' + module.params['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(src_templ_name=module.params['source_template']) + kwargs['usr_lbl'] = module.params['user_label'] + # service profiles are of type 'instance' + kwargs['type'] = 'instance' + + if mo.check_prop_match(**kwargs): + # top-level props match + if module.params.get('power_state'): + child_dn = dn + '/power' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(state=module.params['power_state']) + if mo_1.check_prop_match(**kwargs): + props_match = True + else: + # no power state provided, use existing state as match + props_match = True + + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = LsServer( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['name'], + src_templ_name=module.params['source_template'], + type='instance', + usr_lbl=module.params['user_label'], + descr=module.params['description'], + ) + if module.params.get('power_state'): + admin_state = 'admin-' + module.params['power_state'] + mo_1 = LsPower( + parent_mo_or_dn=mo, + state=admin_state, + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_service_profile_template.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_service_profile_template.py new file mode 100644 index 000000000..0c6f2e71b --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_service_profile_template.py @@ -0,0 +1,523 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_service_profile_template +short_description: Configures Service Profile Templates on Cisco UCS Manager +description: +- Configures Service Profile Templates on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify Service Profile Templates are present and will create if needed. + - If C(absent), will verify Service Profile Templates are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the service profile template. + - This name can be between 2 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - This name must be unique across all service profiles and service profile templates within the same organization. + required: yes + template_type: + description: + - "The template type field which can be one of the following:" + - "initial-template — Any service profiles created from this template are not updated if the template changes." + - "updating-template — Any service profiles created from this template are updated if the template changes." + choices: [initial-template, updating-template] + default: initial-template + uuid_pool: + description: + - Specifies how the UUID will be set on a server associated with a service profile created from this template. + - "The uuid_pool option can be the name of the UUID pool to use or '' (the empty string)." + - An empty string will use the UUID assigned to the server by the manufacturer, and the + - UUID remains unassigned until a service profile created from this template is associated with a server. At that point, + - the UUID is set to the UUID value assigned to the server by the manufacturer. If the service profile is later moved to + - a different server, the UUID is changed to match the new server." + default: default + description: + description: + - A user-defined description of the service profile template. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + storage_profile: + description: + - The name of the storage profile you want to associate with service profiles created from this template + local_disk_policy: + description: + - The name of the local disk policy you want to associate with service profiles created from this template. + lan_connectivity_policy: + description: + - The name of the LAN connectivity policy you want to associate with service profiles created from this template. + iqn_pool: + description: + - The name of the IQN pool (initiator) you want to apply to all iSCSI vNICs for service profiles created from this template. + san_connectivity_policy: + description: + - The name of the SAN connectivity policy you want to associate with service profiles created from this template. + vmedia_policy: + description: + - The name of the vMedia policy you want to associate with service profiles created from this template. + boot_policy: + description: + - The name of the boot order policy you want to associate with service profiles created from this template. + default: default + maintenance_policy: + description: + - The name of the maintenance policy you want to associate with service profiles created from this template. + server_pool: + description: + - The name of the server pool you want to associate with this service profile template. + server_pool_qualification: + description: + - The name of the server pool policy qualificaiton you want to use for this service profile template. + power_state: + description: + - The power state to be applied when a service profile created from this template is associated with a server. + choices: [up, down] + default: up + host_firmware_package: + description: + - The name of the host firmware package you want to associate with service profiles created from this template. + bios_policy: + description: + - The name of the BIOS policy you want to associate with service profiles created from this template. + ipmi_access_profile: + description: + - The name of the IPMI access profile you want to associate with service profiles created from this template. + sol_policy: + description: + - The name of the Serial over LAN (SoL) policy you want to associate with service profiles created from this template. + mgmt_ip_state: + description: + - The state for the Outband Management IP pool you want to use with service profiles created from this template. + choices: [none, pooled] + default: pooled + mgmt_ip_pool: + description: + - The name of the Outband Management IP pool you want to use with service profiles created from this template. + default: ext-mgmt + power_control_policy: + description: + - The name of the power control policy you want to associate with service profiles created from this template. + default: default + power_sync_policy: + description: + - The name of the power sync policy you want to associate with service profiles created from this template. + scrub_policy: + description: + - The name of the scrub policy you want to associate with service profiles created from this template. + kvm_mgmt_policy: + description: + - The name of the KVM management policy you want to associate with service profiles created from this template. + graphics_card_policy: + description: + - The name of the graphics card policy you want to associate with service profiles created from this template. + threshold_policy: + description: + - The name of the threshold policy you want to associate with service profiles created from this template. + default: default + user_label: + description: + - The User Label you want to assign to service profiles created from this template. + mgmt_interface_mode: + description: + - The Management Interface you want to assign to service profiles created from this template. + choices: ['', in-band] + mgmt_vnet_name: + description: + - A VLAN selected from the associated VLAN group. + mgmt_inband_pool_name: + description: + - How the inband management IPv4 address is derived for the server associated with this service profile. + org_dn: + description: + - Org dn (distinguished name) + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- CiscoUcs (@CiscoUcs) +version_added: '2.8' +''' + +EXAMPLES = r''' +- name: Configure Service Profile Template with LAN/SAN Connectivity and all other options defaulted + cisco.ucs.ucs_service_profile_template: + hostname: 172.16.143.150 + username: admin + password: password + name: DEE-Ctrl + template_type: updating-template + uuid_pool: UUID-Pool + storage_profile: DEE-StgProf + lan_connectivity_policy: Cntr-FC-Boot + iqn_pool: iSCSI-Boot-A + san_connectivity_policy: Cntr-FC-Boot + boot_policy: DEE-vMedia + maintenance_policy: default + server_pool: Container-Pool + host_firmware_package: 3.1.2b + bios_policy: Docker + +- name: Remove Service Profile Template + cisco.ucs.ucs_service_profile_template: + hostname: 172.16.143.150 + username: admin + password: password + name: DEE-Ctrl + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def configure_service_profile_template(ucs, module): + from ucsmsdk.mometa.ls.LsServer import LsServer + from ucsmsdk.mometa.vnic.VnicConnDef import VnicConnDef + from ucsmsdk.mometa.vnic.VnicIScsiNode import VnicIScsiNode + from ucsmsdk.mometa.ls.LsRequirement import LsRequirement + from ucsmsdk.mometa.ls.LsPower import LsPower + from ucsmsdk.mometa.lstorage.LstorageProfileBinding import LstorageProfileBinding + from ucsmsdk.mometa.mgmt.MgmtInterface import MgmtInterface + from ucsmsdk.mometa.mgmt.MgmtVnet import MgmtVnet + from ucsmsdk.mometa.vnic.VnicIpV4MgmtPooledAddr import VnicIpV4MgmtPooledAddr + + if not module.check_mode: + try: + # create if mo does not already exist + mo = LsServer( + parent_mo_or_dn=module.params['org_dn'], + bios_profile_name=module.params['bios_policy'], + boot_policy_name=module.params['boot_policy'], + descr=module.params['description'], + ext_ip_state=module.params['mgmt_ip_state'], + ext_ip_pool_name=module.params['mgmt_ip_pool'], + # graphics_card_policy_name=module.params['graphics_card_policy'], + host_fw_policy_name=module.params['host_firmware_package'], + ident_pool_name=module.params['uuid_pool'], + kvm_mgmt_policy_name=module.params['kvm_mgmt_policy'], + local_disk_policy_name=module.params['local_disk_policy'], + maint_policy_name=module.params['maintenance_policy'], + mgmt_access_policy_name=module.params['ipmi_access_profile'], + name=module.params['name'], + power_policy_name=module.params['power_control_policy'], + power_sync_policy_name=module.params['power_sync_policy'], + scrub_policy_name=module.params['scrub_policy'], + sol_policy_name=module.params['sol_policy'], + stats_policy_name=module.params['threshold_policy'], + type=module.params['template_type'], + usr_lbl=module.params['user_label'], + vmedia_policy_name=module.params['vmedia_policy'], + ) + + if module.params['storage_profile']: + # Storage profile + mo_1 = LstorageProfileBinding( + parent_mo_or_dn=mo, + storage_profile_name=module.params['storage_profile'], + ) + + if module.params['mgmt_interface_mode']: + # Management Interface + mo_1 = MgmtInterface( + parent_mo_or_dn=mo, + mode=module.params['mgmt_interface_mode'], + ip_v4_state='pooled', + ) + mo_2 = MgmtVnet( + parent_mo_or_dn=mo_1, + id='1', + name=module.params['mgmt_vnet_name'], + ) + VnicIpV4MgmtPooledAddr( + parent_mo_or_dn=mo_2, + name=module.params['mgmt_inband_pool_name'], + ) + + # LAN/SAN connectivity policy + mo_1 = VnicConnDef( + parent_mo_or_dn=mo, + lan_conn_policy_name=module.params['lan_connectivity_policy'], + san_conn_policy_name=module.params['san_connectivity_policy'], + ) + + if module.params['iqn_pool']: + # IQN pool + mo_1 = VnicIScsiNode( + parent_mo_or_dn=mo, + iqn_ident_pool_name=module.params['iqn_pool'] + ) + + # power state + admin_state = 'admin-' + module.params['power_state'] + mo_1 = LsPower( + parent_mo_or_dn=mo, + state=admin_state, + ) + + if module.params['server_pool']: + # server pool + mo_1 = LsRequirement( + parent_mo_or_dn=mo, + name=module.params['server_pool'], + qualifier=module.params['server_pool_qualification'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + except Exception as e: # generic Exception handling because SDK can throw a variety of exceptions + ucs.result['msg'] = "setup error: %s " % str(e) + module.fail_json(**ucs.result) + + ucs.result['changed'] = True + + +def check_storage_profile_props(ucs, module, dn): + props_match = False + + child_dn = dn + '/profile-binding' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(storage_profile_name=module.params['storage_profile']) + if mo_1.check_prop_match(**kwargs): + props_match = True + elif not module.params['storage_profile']: + # no stroage profile mo or desired state + props_match = True + + return props_match + + +def check_connectivity_policy_props(ucs, module, dn): + props_match = False + + child_dn = dn + '/conn-def' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(lan_conn_policy_name=module.params['lan_connectivity_policy']) + kwargs['san_conn_policy_name'] = module.params['san_connectivity_policy'] + if mo_1.check_prop_match(**kwargs): + props_match = True + elif not module.params['lan_connectivity_policy'] and not module.params['san_connectivity_policy']: + # no mo and no desired state + props_match = True + + return props_match + + +def check_iqn_pool_props(ucs, module, dn): + props_match = False + + child_dn = dn + '/iscsi-node' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(iqn_ident_pool_name=module.params['iqn_pool']) + if mo_1.check_prop_match(**kwargs): + props_match = True + elif not module.params['iqn_pool']: + # no mo and no desired state + props_match = True + + return props_match + + +def check_inband_management_props(ucs, module, dn): + props_match = False + + child_dn = dn + '/iface-in-band' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(mode=module.params['mgmt_interface_mode']) + if mo_1.check_prop_match(**kwargs): + child_dn = child_dn + '/network' + mo_2 = ucs.login_handle.query_dn(child_dn) + if mo_2: + kwargs = dict(name=module.params['mgmt_vnet_name']) + if mo_2.check_prop_match(**kwargs): + child_dn = child_dn + '/ipv4-pooled-addr' + mo_3 = ucs.login_handle.query_dn(child_dn) + if mo_3: + kwargs = dict(name=module.params['mgmt_inband_pool_name']) + if mo_3.check_prop_match(**kwargs): + props_match = True + elif not module.params['mgmt_interface_mode']: + # no mo and no desired state + props_match = True + + return props_match + + +def check_power_props(ucs, module, dn): + props_match = False + + child_dn = dn + '/power' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(state=module.params['power_state']) + if mo_1.check_prop_match(**kwargs): + props_match = True + elif not module.params['power_state']: + # no mo and no desired state + props_match = True + + return props_match + + +def check_server_pool(ucs, module, dn): + props_match = False + + child_dn = dn + '/pn-req' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(name=module.params['server_pool']) + kwargs['qualifier'] = module.params['server_pool_qualification'] + if mo_1.check_prop_match(**kwargs): + props_match = True + elif not module.params['server_pool']: + # no pn-req object and no server pool name provided + props_match = True + + return props_match + + +def check_serivce_profile_templates_props(ucs, module, mo, dn): + props_match = False + + # check top-level mo props + kwargs = dict(bios_profile_name=module.params['bios_policy']) + kwargs['boot_policy_name'] = module.params['boot_policy'] + kwargs['descr'] = module.params['description'] + kwargs['ext_ip_state'] = module.params['mgmt_ip_state'] + kwargs['ext_ip_pool_name'] = module.params['mgmt_ip_pool'] + # kwargs['graphics_card_policy_name'] = module.params['graphics_card_policy'] + kwargs['host_fw_policy_name'] = module.params['host_firmware_package'] + kwargs['ident_pool_name'] = module.params['uuid_pool'] + kwargs['kvm_mgmt_policy_name'] = module.params['kvm_mgmt_policy'] + kwargs['local_disk_policy_name'] = module.params['local_disk_policy'] + kwargs['maint_policy_name'] = module.params['maintenance_policy'] + kwargs['mgmt_access_policy_name'] = module.params['ipmi_access_profile'] + kwargs['power_policy_name'] = module.params['power_control_policy'] + kwargs['power_sync_policy_name'] = module.params['power_sync_policy'] + kwargs['scrub_policy_name'] = module.params['scrub_policy'] + kwargs['sol_policy_name'] = module.params['sol_policy'] + kwargs['stats_policy_name'] = module.params['threshold_policy'] + kwargs['type'] = module.params['template_type'] + kwargs['usr_lbl'] = module.params['user_label'] + kwargs['vmedia_policy_name'] = module.params['vmedia_policy'] + + if mo.check_prop_match(**kwargs): + # top-level props match, check next level mo/props + # code below should discontinue checks once any mismatch is found + + # check storage profile 1st + props_match = check_storage_profile_props(ucs, module, dn) + + if props_match: + props_match = check_connectivity_policy_props(ucs, module, dn) + + if props_match: + props_match = check_iqn_pool_props(ucs, module, dn) + + if props_match: + props_match = check_inband_management_props(ucs, module, dn) + + if props_match: + props_match = check_power_props(ucs, module, dn) + + if props_match: + props_match = check_server_pool(ucs, module, dn) + + return props_match + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str', required=True), + bios_policy=dict(type='str', default=''), + boot_policy=dict(type='str', default='default'), + description=dict(type='str', aliases=['descr'], default=''), + mgmt_ip_state=dict(type='str', default='pooled'), + mgmt_ip_pool=dict(type='str', default='ext-mgmt'), + graphics_card_policy=dict(type='str', default=''), + host_firmware_package=dict(type='str', default=''), + uuid_pool=dict(type='str', default='default'), + kvm_mgmt_policy=dict(type='str', default=''), + local_disk_policy=dict(type='str', default=''), + maintenance_policy=dict(type='str', default=''), + ipmi_access_profile=dict(type='str', default=''), + power_control_policy=dict(type='str', default='default'), + power_sync_policy=dict(type='str', default=''), + scrub_policy=dict(type='str', default=''), + sol_policy=dict(type='str', default=''), + threshold_policy=dict(type='str', default='default'), + template_type=dict(type='str', default='initial-template', choices=['initial-template', 'updating-template']), + user_label=dict(type='str', default=''), + vmedia_policy=dict(type='str', default=''), + storage_profile=dict(type='str', default=''), + lan_connectivity_policy=dict(type='str', default=''), + iqn_pool=dict(type='str', default=''), + san_connectivity_policy=dict(type='str', default=''), + server_pool=dict(type='str', default=''), + server_pool_qualification=dict(type='str', default=''), + power_state=dict(type='str', default='up', choices=['up', 'down']), + mgmt_interface_mode=dict(type='str', default='', choices=['', 'in-band']), + mgmt_vnet_name=dict(type='str', default=''), + mgmt_inband_pool_name=dict(type='str', default=''), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + ucs = UCSModule(module) + # UCSModule creation above verifies ucsmsdk is present and exits on failure. + # Additional imports are done below or in called functions. + + ucs.result['changed'] = False + props_match = False + # dn is <org_dn>/ls-<name> + dn = module.params['org_dn'] + '/ls-' + module.params['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + ucs.result['changed'] = True + else: # state == 'present' + props_match = check_serivce_profile_templates_props(ucs, module, mo, dn) + + if module.params['state'] == 'present' and not props_match: + configure_service_profile_template(ucs, module) + + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_sp_vnic_order.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_sp_vnic_order.py new file mode 100644 index 000000000..6498323c3 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_sp_vnic_order.py @@ -0,0 +1,230 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_sp_vnic_order + +short_description: Configures vNIC order for service profiles and templates on Cisco UCS Manager + +version_added: 2.10 + +description: + - Configures Configures vNIC order for service profiles and templates on Cisco UCS Manager + +options: + sp_name: + description: DN of the service profile + vnics: + description: List of vNIC order properties + suboptions: + name: + description: Name of the vNIC + required: true + admin_vcon: + description: Name of the virtual connection + choices: ["1","2","3","4","any"] + order: + description: vNIC connection order + choices: ["unspecified", "0-256"] + transport: + description: transport medium + choices: ["ethernet", "fc"] + required: true + state: + description: Desired state of the vNIC. + choices: [present, absent] + default: present + org_dn: + description: root org dn +extends_documentation_fragment: + - cisco.ucs.ucs +requirements: + - ucsmsdk +author: + - Brett Johnson (@sdbrett) + +''' + +EXAMPLES = r''' +- name: Configure vnic order + cisco.ucs.ucs_sp_vnic_order: + sp_name: my_sp + vnics: + - name: 'my_vnic' + admin_vcon: '1' + order: '1' + transport: 'ethernet' + hostname: 192.168.99.100 + username: admin + password: password +- name: Configure vhba order + cisco.ucs.ucs_sp_vnic_order: + sp_name: my_sp + vnics: + - name: 'my_vhba' + admin_vcon: '2' + order: '1' + transport: 'fc' + hostname: 192.168.99.100 + username: admin + password: password +- name: Configure vnic and vhba order + cisco.ucs.ucs_sp_vnic_order: + sp_name: my_sp + vnics: + - name: my_vhba + admin_vcon: '2' + order: '1' + transport: fc + - name: my_vnic + admin_vcon: '1' + order: '1' + transport: ethernet + hostname: 192.168.99.100 + username: admin + password: password +- name: Remove vnic order configuration from my_vnic + cisco.ucs.ucs_sp_vnic_order: + sp_name: my_sp + vnics: + - name: 'my_vnic' + transport: ethernet + state: absent + hostname: 192.168.99.100 + username: admin + password: password + +''' + +RETURN = r''' +# +''' + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def get_service_profile(handle, org_dn, sp_name): + dn = org_dn + "/ls-" + sp_name + sp = handle.query_dn(dn) + return sp + + +def update_vnic_assignment_order(ucs, vnic, sp): + from ucsmsdk.mometa.ls.LsVConAssign import LsVConAssign + + mo = LsVConAssign(parent_mo_or_dn=sp, admin_vcon=vnic['admin_vcon'], + order=vnic['order'], transport=vnic['transport'], + vnic_name=vnic['name']) + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + + +def remove_vnic_assignment_order(ucs, vnic, sp): + from ucsmsdk.mometa.ls.LsVConAssign import LsVConAssign + + mo = LsVConAssign(parent_mo_or_dn=sp, admin_vcon='any', + order='unspecified', transport=vnic['transport'], + vnic_name=vnic['name']) + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + + +def get_vnic(ucs, dn): + return ucs.login_handle.query_dn(dn) + + +def get_vnic_dn(sp_dn, transport, name): + if transport == 'ethernet': + return sp_dn + '/ether-' + name + return sp_dn + '/fc-' + name + + +def matches_existing_vnic_order(vnic, vnic_mo): + if vnic['state'] == 'absent': + kwargs = dict(admin_vcon='any') + kwargs['order'] = 'unspecified' + else: + kwargs = dict(admin_vcon=vnic['admin_vcon']) + kwargs['order'] = vnic['order'] + + if vnic['transport'] == 'ethernet': + kwargs['type'] = 'ether' + else: + kwargs['type'] = vnic['transport'] + return vnic_mo.check_prop_match(**kwargs) + + +def main(): + vnic_spec = dict( + name=dict(type='str', required=True), + admin_vcon=dict(type='str', choices=['1', '2', '3', '4', 'any']), + order=dict(type='str'), + transport=dict(type='str', required=True, choices=['ethernet', 'fc']), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + argument_spec = ucs_argument_spec + argument_spec.update( + sp_name=dict(required=True, type='str'), + vnics=dict(required=True, type='list', elements='dict', options=vnic_spec), + org_dn=dict(required=False, type='str', default='org-root'), + ) + module = AnsibleModule(argument_spec, + supports_check_mode=True) + ucs = UCSModule(module) + + err = False + changed = False + + try: + sp_dn = dn = module.params['org_dn'] + "/ls-" + module.params['sp_name'] + sp = ucs.login_handle.query_dn(dn) + if not sp: + raise ValueError("SP '%s' does not exist" % sp_dn) + + for vnic in module.params['vnics']: + vnic_mo = get_vnic(ucs, (get_vnic_dn(sp_dn, vnic['transport'], vnic['name']))) + + if vnic['state'] != 'absent' and not vnic_mo: + raise ValueError("vNIC '%s' is not assigned to service profile '%s'" % (vnic['name'], sp_dn)) + + if vnic_mo: + if not matches_existing_vnic_order(vnic, vnic_mo): + changed = True + break + + if changed and not module.check_mode: + for vnic in module.params['vnics']: + vnic_mo = get_vnic(ucs, (get_vnic_dn(sp_dn, vnic['transport'], vnic['name']))) + if vnic['state'] == 'absent' and vnic_mo: + remove_vnic_assignment_order(ucs, vnic, sp) + elif not vnic_mo: + + update_vnic_assignment_order(ucs, vnic, sp) + elif not matches_existing_vnic_order(vnic, vnic_mo): + update_vnic_assignment_order(ucs, vnic, sp) + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_storage_profile.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_storage_profile.py new file mode 100644 index 000000000..9ca825a8b --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_storage_profile.py @@ -0,0 +1,254 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_storage_profile +short_description: Configures storage profiles on Cisco UCS Manager +description: +- Configures storage profiles on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify that the storage profile is present and will create if needed. + - If C(absent), will verify that the storage profile is absent and will delete if needed. + choices: [ absent, present ] + default: present + name: + description: + - The name of the storage profile. + - This name can be between 1 and 16 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after profile is created. + required: yes + description: + description: + - The user-defined description of the storage profile. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + local_luns: + description: + - List of Local LUNs used by the storage profile. + suboptions: + name: + description: + - The name of the local LUN. + required: yes + size: + description: + - Size of this LUN in GB. + - The size can range from 1 to 10240 GB. + default: '1' + auto_deploy: + description: + - Whether the local LUN should be automatically deployed or not. + choices: [ auto-deploy, no-auto-deploy ] + default: auto-deploy + expand_to_avail: + description: + - Specifies that this LUN can be expanded to use the entire available disk group. + - For each service profile, only one LUN can use this option. + - Expand To Available option is not supported for already deployed LUN. + type: bool + default: 'no' + fractional_size: + description: + - Fractional size of this LUN in MB. + default: '0' + disk_policy_name: + description: + - The disk group configuration policy to be applied to this local LUN. + state: + description: + - If C(present), will verify local LUN is present on profile. + If C(absent), will verify local LUN is absent on profile. + choices: [ absent, present ] + default: present + org_dn: + description: + - The distinguished name (dn) of the organization where the resource is assigned. + default: org-root +requirements: +- ucsmsdk +author: +- Sindhu Sudhir (@sisudhir) +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: '2.7' +''' + +EXAMPLES = r''' +- name: Configure Storage Profile + cisco.ucs.ucs_storage_profile: + hostname: 172.16.143.150 + username: admin + password: password + name: DEE-StgProf + local_luns: + - name: Boot-LUN + size: '60' + disk_policy_name: DEE-DG + - name: Data-LUN + size: '200' + disk_policy_name: DEE-DG + +- name: Remove Storage Profile + cisco.ucs.ucs_storage_profile: + hostname: 172.16.143.150 + username: admin + password: password + name: DEE-StgProf + state: absent + +- name: Remove Local LUN from Storage Profile + cisco.ucs.ucs_storage_profile: + hostname: 172.16.143.150 + username: admin + password: password + name: DEE-StgProf + local_luns: + - name: Data-LUN + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + local_lun = dict( + name=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['present', 'absent']), + size=dict(type='str', default='1'), + auto_deploy=dict(type='str', default='auto-deploy', choices=['auto-deploy', 'no-auto-deploy']), + expand_to_avail=dict(type='str', default='no', choices=['no', 'yes']), + fractional_size=dict(type='str', default='0'), + disk_policy_name=dict(type='str', default=''), + ) + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str', required=True), + description=dict(type='str', aliases=['descr'], default=''), + local_luns=dict(type='list', elements='dict', options=local_lun), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + ucs = UCSModule(module) + + err = False + + # UCSModule creation above verifies ucsmsdk is present and exits on failure. Additional imports are done below. + from ucsmsdk.mometa.lstorage.LstorageProfile import LstorageProfile + from ucsmsdk.mometa.lstorage.LstorageDasScsiLun import LstorageDasScsiLun + + ucs.result['changed'] = False + try: + mo_exists = False + props_match = False + # dn is <org_dn>/profile-<name> + dn = module.params['org_dn'] + '/profile-' + module.params['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + ucs.result['changed'] = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(descr=module.params['description']) + if mo.check_prop_match(**kwargs): + # top-level props match, check next level mo/props + if not module.params.get('local_luns'): + props_match = True + else: + # check local lun props + for lun in module.params['local_luns']: + child_dn = dn + '/das-scsi-lun-' + lun['name'] + mo_1 = ucs.login_handle.query_dn(child_dn) + if lun['state'] == 'absent': + if mo_1: + props_match = False + break + else: + if mo_1: + kwargs = dict(size=str(lun['size'])) + kwargs['auto_deploy'] = lun['auto_deploy'] + kwargs['expand_to_avail'] = lun['expand_to_avail'] + kwargs['fractional_size'] = str(lun['fractional_size']) + kwargs['local_disk_policy_name'] = lun['disk_policy_name'] + if mo_1.check_prop_match(**kwargs): + props_match = True + else: + props_match = False + break + + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = LstorageProfile( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['name'], + descr=module.params['description'], + ) + + if module.params.get('local_luns'): + for lun in module.params['local_luns']: + if lun['state'] == 'absent': + child_dn = dn + '/das-scsi-lun-' + lun['name'] + mo_1 = ucs.login_handle.query_dn(child_dn) + ucs.login_handle.remove_mo(mo_1) + else: + mo_1 = LstorageDasScsiLun( + parent_mo_or_dn=mo, + name=lun['name'], + size=str(lun['size']), + auto_deploy=lun['auto_deploy'], + expand_to_avail=lun['expand_to_avail'], + fractional_size=str(lun['fractional_size']), + local_disk_policy_name=lun['disk_policy_name'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + ucs.result['changed'] = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_system_qos.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_system_qos.py new file mode 100644 index 000000000..6dcf7aac5 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_system_qos.py @@ -0,0 +1,152 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_system_qos +short_description: Configures system QoS settings +version_added: 2.10 +description: + - Configures system QoS settings +extends_documentation_fragment: cisco.ucs.ucs +options: + priority: + description: Priority to configure + choices: ["best-effort", "bronze", "fc", "gold","platinum", "silver"] + required: true + admin_state: + description: Admin state of QoS Policy + choices: ['disabled', 'enabled'] + default: enabled + cos: + description: CoS setting + choices: ['any', '0-6'] + required: true + weight: + description: CoS profile weight + choices: ['best-effort', 'none', '0-10'] + required: true + mtu: + description: MTU size + choices: ['fc', 'normal', '0-4294967295'] + default: normal + multicast_optimize: + description: Set multicast optimization options + choices: ['false', 'no', 'true', 'yes'] + drop: + description: Set multicast optimization options + default: 'drop' + choices: ['drop', 'no-drop'] +requirements: ['ucsmsdk'] +author: "Brett Johnson (@sdbrett)" +''' + +EXAMPLES = ''' +- name: + cisco.ucs.ucs_system_qos: + priority: platinum + admin_state: enabled + multicast_optimize: no + cos: '5' + weight: '10' + mtu: '9216' + hostname: 192.168.99.100 + username: admin + password: password +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +# TODO Add ranges for cos, weight and mtu +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + priority=dict(required=True, type='str', choices=["best-effort", "bronze", "fc", "gold", "platinum", "silver"]), + cos=dict(required=True, type='str'), + weight=dict(required=True, type='str'), + admin_state=dict(required=False, type='str', default='enabled', choices=['disabled', 'enabled']), + drop=dict(required=False, type='str', default='drop', choices=['drop', 'no-drop']), + mtu=dict(required=False, type='str', default='normal'), + multicast_optimize=dict(required=False, type='str', default='no', choices=['false', 'no', 'true', 'yes']), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + ucs = UCSModule(module) + + err = False + + changed = False + try: + dn = "fabric/lan/classes/class-" + module.params['priority'] + mo = ucs.login_handle.query_dn(dn) + # check top-level mo props + if module.params['priority'] == 'best-effort': + kwargs = dict(weight=module.params['weight']) + kwargs['mtu'] = module.params['mtu'] + kwargs['multicast_optimize'] = module.params['multicast_optimize'] + if not mo.check_prop_match(**kwargs): + if not module.check_mode: + mo.weight = module.params['weight'] + mo.mtu = module.params['mtu'] + mo.multicast_optimize = module.params['multicast_optimize'] + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + elif module.params['priority'] == 'fc': + kwargs = dict(weight=module.params['weight']) + kwargs['cos'] = module.params['cos'] + if not mo.check_prop_match(**kwargs): + if not module.check_mode: + mo.weight = module.params['weight'] + mo.cos = module.params['cos'] + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + else: + kwargs = dict(weight=module.params['weight']) + kwargs['priority'] = module.params['priority'] + kwargs['mtu'] = module.params['mtu'] + kwargs['cos'] = module.params['cos'] + kwargs['drop'] = module.params['drop'] + kwargs['admin_state'] = module.params['admin_state'] + kwargs['multicast_optimize'] = module.params['multicast_optimize'] + if not mo.check_prop_match(**kwargs): + if not module.check_mode: + mo.weight = module.params['weight'] + mo.mtu = module.params['mtu'] + mo.cos = module.params['cos'] + mo.drop = module.params['drop'] + mo.admin_state = module.params['admin_state'] + mo.multicast_optimize = module.params['multicast_optimize'] + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_timezone.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_timezone.py new file mode 100644 index 000000000..d7a22e78f --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_timezone.py @@ -0,0 +1,165 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_timezone +short_description: Configures timezone on Cisco UCS Manager +description: +- Configures timezone on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(absent), will unset timezone. + - If C(present), will set or update timezone. + choices: [absent, present] + default: present + + admin_state: + description: + - The admin_state setting + - The enabled admin_state indicates the timezone configuration is utilized by UCS Manager. + - The disabled admin_state indicates the timezone configuration is ignored by UCS Manager. + choices: [disabled, enabled] + default: enabled + + description: + description: + - A user-defined description of the timezone. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + default: "" + + timezone: + description: + - The timezone name. + - Time zone names are from the L(tz database,https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) + - The timezone name is case sensitive. + - The timezone name can be between 0 and 510 alphanumeric characters. + - You cannot use spaces or any special characters other than + - "\"-\" (hyphen), \"_\" (underscore), \"/\" (backslash)." + +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: '2.7' +''' + +EXAMPLES = r''' +- name: Configure Time Zone + cisco.ucs.ucs_timezone: + hostname: 172.16.143.150 + username: admin + password: password + state: present + admin_state: enabled + timezone: America/Los_Angeles + description: 'Time Zone for Los Angeles' + +- name: Unconfigure Time Zone + cisco.ucs.ucs_timezone: + hostname: 172.16.143.150 + username: admin + password: password + state: absent + admin_state: disabled +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def run_module(): + argument_spec = ucs_argument_spec + argument_spec.update( + timezone=dict(type='str'), + description=dict(type='str', aliases=['descr'], default=''), + admin_state=dict(type='str', default='enabled', choices=['disabled', 'enabled']), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['timezone']], + ], + ) + ucs = UCSModule(module) + + err = False + + changed = False + try: + mo_exists = False + props_match = False + + dn = 'sys/svc-ext/datetime-svc' + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + mo.timezone = "" + mo.descr = "" + ucs.login_handle.add_mo(mo, modify_present=True) + ucs.login_handle.commit() + changed = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(descr=module.params['description']) + kwargs['timezone'] = module.params['timezone'] + kwargs['admin_state'] = module.params['admin_state'] + if mo.check_prop_match(**kwargs): + props_match = True + + if not props_match: + if not module.check_mode: + # update mo, timezone mo always exists + mo.timezone = module.params['timezone'] + mo.descr = module.params['description'] + mo.admin_state = module.params['admin_state'] + ucs.login_handle.add_mo(mo, modify_present=True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_uuid_pool.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_uuid_pool.py new file mode 100644 index 000000000..d314a9ba0 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_uuid_pool.py @@ -0,0 +1,196 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_uuid_pool +short_description: Configures server UUID pools on Cisco UCS Manager +description: +- Configures server UUID pools and UUID blocks on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify UUID pool is present and will create if needed. + - If C(absent), will verify UUID pool is absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the UUID pool. + - This name can be between 1 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the UUID pool is created. + required: yes + description: + description: + - "The user-defined description of the UUID pool." + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + prefix: + description: + - UUID prefix used for the range of server UUIDs. + - "If no value is provided, the system derived prefix will be used (equivalent to selecting 'derived' option in UI)." + - "If the user provides a value, the user provided prefix will be used (equivalent to selecting 'other' option in UI)." + - A user provided value should be in the format XXXXXXXX-XXXX-XXXX. + order: + description: + - The Assignment Order field. + - "This can be one of the following:" + - "default - Cisco UCS Manager selects a random identity from the pool." + - "sequential - Cisco UCS Manager selects the lowest available identity from the pool." + choices: [default, sequential] + default: default + first_uuid: + description: + - The first UUID in the block of UUIDs. + - This is the From field in the UCS Manager UUID Blocks menu. + last_uuid: + description: + - The last UUID in the block of UUIDs. + - This is the To field in the UCS Manager Add UUID Blocks menu. + org_dn: + description: + - The distinguished name (dn) of the organization where the resource is assigned. + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- CiscoUcs (@CiscoUcs) +version_added: '2.7' +''' + +EXAMPLES = r''' +- name: Configure UUID address pool + cisco.ucs.ucs_uuid_pool: + hostname: 172.16.143.150 + username: admin + password: password + name: UUID-Pool + order: sequential + first_uuid: 0000-000000000001 + last_uuid: 0000-000000000078 + +- name: Remove UUID address pool + cisco.ucs.ucs_uuid_pool: + hostname: 172.16.143.150 + username: admin + password: password + name: UUID-Pool + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str', required=True), + description=dict(type='str', aliases=['descr'], default=''), + order=dict(type='str', default='default', choices=['default', 'sequential']), + prefix=dict(type='str', default=''), + first_uuid=dict(type='str'), + last_uuid=dict(type='str'), + state=dict(default='present', choices=['present', 'absent'], type='str'), + ) + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + ) + # UCSModule verifies ucsmsdk is present and exits on failure. Imports are below ucs object creation. + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.uuidpool.UuidpoolPool import UuidpoolPool + from ucsmsdk.mometa.uuidpool.UuidpoolBlock import UuidpoolBlock + + ucs.result['changed'] = False + try: + mo_exists = False + props_match = False + # dn is <org_dn>/uuid-pool-<name> + dn = module.params['org_dn'] + '/uuid-pool-' + module.params['name'] + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + ucs.result['changed'] = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(assignment_order=module.params['order']) + kwargs['descr'] = module.params['description'] + if module.params['prefix']: + kwargs['prefix'] = module.params['prefix'] + if mo.check_prop_match(**kwargs): + # top-level props match, check next level mo/props + if module.params['last_uuid'] and module.params['first_uuid']: + # uuid address block specified, check properties + block_dn = dn + '/block-from-' + module.params['first_uuid'].upper() + '-to-' + module.params['last_uuid'].upper() + mo_1 = ucs.login_handle.query_dn(block_dn) + if mo_1: + props_match = True + else: + # no UUID address block specified, but top-level props matched + props_match = True + + if not props_match: + if not module.check_mode: + # create if mo does not already exist + if not module.params['prefix']: + module.params['prefix'] = 'derived' + mo = UuidpoolPool( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['name'], + descr=module.params['description'], + assignment_order=module.params['order'], + prefix=module.params['prefix'] + ) + + if module.params['last_uuid'] and module.params['first_uuid']: + mo_1 = UuidpoolBlock( + parent_mo_or_dn=mo, + to=module.params['last_uuid'], + r_from=module.params['first_uuid'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + ucs.result['changed'] = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_vhba_template.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_vhba_template.py new file mode 100644 index 000000000..597e8c914 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_vhba_template.py @@ -0,0 +1,274 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_vhba_template +short_description: Configures vHBA templates on Cisco UCS Manager +description: +- Configures vHBA templates on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify vHBA templates are present and will create if needed. + - If C(absent), will verify vHBA templates are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the virtual HBA template. + - This name can be between 1 and 16 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the template is created. + required: yes + description: + description: + - A user-defined description of the template. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + fabric: + description: + - The Fabric ID field. + - The name of the fabric interconnect that vHBAs created with this template are associated with. + choices: [A, B] + default: A + redundancy_type: + description: + - The Redundancy Type used for template pairing from the Primary or Secondary redundancy template. + - "primary — Creates configurations that can be shared with the Secondary template." + - Any other shared changes on the Primary template are automatically synchronized to the Secondary template. + - "secondary — All shared configurations are inherited from the Primary template." + - "none - Legacy vHBA template behavior. Select this option if you do not want to use redundancy." + choices: [none, primary, secondary] + default: none + vsan: + description: + - The VSAN to associate with vHBAs created from this template. + default: default + template_type: + description: + - The Template Type field. + - "This can be one of the following:" + - "initial-template — vHBAs created from this template are not updated if the template changes." + - "updating-template - vHBAs created from this template are updated if the template changes." + choices: [initial-template, updating-template] + default: initial-template + max_data: + description: + - The Max Data Field Size field. + - The maximum size of the Fibre Channel frame payload bytes that the vHBA supports. + - Enter an string between '256' and '2112'. + default: '2048' + wwpn_pool: + description: + - The WWPN pool that a vHBA created from this template uses to derive its WWPN address. + default: default + qos_policy: + description: + - The QoS policy that is associated with vHBAs created from this template. + pin_group: + description: + - The SAN pin group that is associated with vHBAs created from this template. + stats_policy: + description: + - The statistics collection policy that is associated with vHBAs created from this template. + default: default + org_dn: + description: + - Org dn (distinguished name) + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure vHBA template + cisco.ucs.ucs_vhba_template: + hostname: 172.16.143.150 + username: admin + password: password + name: vHBA-A + fabric: A + vsan: VSAN-A + wwpn_pool: WWPN-Pool-A + +- name: Remote vHBA template + cisco.ucs.ucs_vhba_template: + hostname: 172.16.143.150 + username: admin + password: password + name: vHBA-A + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str'), + descr=dict(type='str'), + fabric=dict(type='str', default='A', choices=['A', 'B']), + redundancy_type=dict(type='str', default='none', choices=['none', 'primary', 'secondary']), + vsan=dict(type='str', default='default'), + template_type=dict(type='str', default='initial-template', choices=['initial-template', 'updating-template']), + max_data=dict(type='str', default='2048'), + wwpn_pool=dict(type='str', default='default'), + qos_policy=dict(type='str'), + pin_group=dict(type='str'), + stats_policy=dict(type='str', default='default'), + state=dict(type='str', default='present', choices=['present', 'absent']), + vhba_template_list=dict(type='list'), + ) + + # Note that use of vhba_template_list is an experimental feature which allows multiple resource updates with a single UCSM connection. + # Support for vhba_template_list may change or be removed once persistent UCS connections are supported. + # Either vhba_template_list or name is required (user can specify either a list of single resource). + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_one_of=[ + ['vhba_template_list', 'name'] + ], + mutually_exclusive=[ + ['vhba_template_list', 'name'] + ], + ) + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.vnic.VnicSanConnTempl import VnicSanConnTempl + from ucsmsdk.mometa.vnic.VnicFcIf import VnicFcIf + + changed = False + try: + # Only documented use is a single resource, but to also support experimental + # feature allowing multiple updates all params are converted to a vhba_template_list below. + + if module.params['vhba_template_list']: + # directly use the list (single resource and list are mutually exclusive + vhba_template_list = module.params['vhba_template_list'] + else: + # single resource specified, create list from the current params + vhba_template_list = [module.params] + for vhba_template in vhba_template_list: + mo_exists = False + props_match = False + # set default params. Done here to set values for lists which can't be done in the argument_spec + if not vhba_template.get('descr'): + vhba_template['descr'] = '' + if not vhba_template.get('fabric'): + vhba_template['fabric'] = 'A' + if not vhba_template.get('redundancy_type'): + vhba_template['redundancy_type'] = 'none' + if not vhba_template.get('vsan'): + vhba_template['vsan'] = 'default' + if not vhba_template.get('template_type'): + vhba_template['template_type'] = 'initial-template' + if not vhba_template.get('max_data'): + vhba_template['max_data'] = '2048' + if not vhba_template.get('wwpn_pool'): + vhba_template['wwpn_pool'] = 'default' + if not vhba_template.get('qos_policy'): + vhba_template['qos_policy'] = '' + if not vhba_template.get('pin_group'): + vhba_template['pin_group'] = '' + if not vhba_template.get('stats_policy'): + vhba_template['stats_policy'] = 'default' + # dn is <org_dn>/san-conn-templ-<name> + dn = module.params['org_dn'] + '/san-conn-templ-' + vhba_template['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + # check top-level mo props + kwargs = dict(descr=vhba_template['descr']) + kwargs['switch_id'] = vhba_template['fabric'] + kwargs['redundancy_pair_type'] = vhba_template['redundancy_type'] + kwargs['templ_type'] = vhba_template['template_type'] + kwargs['max_data_field_size'] = vhba_template['max_data'] + kwargs['ident_pool_name'] = vhba_template['wwpn_pool'] + kwargs['qos_policy_name'] = vhba_template['qos_policy'] + kwargs['pin_to_group_name'] = vhba_template['pin_group'] + kwargs['stats_policy_name'] = vhba_template['stats_policy'] + if (mo.check_prop_match(**kwargs)): + # top-level props match, check next level mo/props + child_dn = dn + '/if-default' + mo_1 = ucs.login_handle.query_dn(child_dn) + if mo_1: + kwargs = dict(name=vhba_template['vsan']) + if (mo_1.check_prop_match(**kwargs)): + props_match = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = VnicSanConnTempl( + parent_mo_or_dn=module.params['org_dn'], + name=vhba_template['name'], + descr=vhba_template['descr'], + switch_id=vhba_template['fabric'], + redundancy_pair_type=vhba_template['redundancy_type'], + templ_type=vhba_template['template_type'], + max_data_field_size=vhba_template['max_data'], + ident_pool_name=vhba_template['wwpn_pool'], + qos_policy_name=vhba_template['qos_policy'], + pin_to_group_name=vhba_template['pin_group'], + stats_policy_name=vhba_template['stats_policy'], + ) + + mo_1 = VnicFcIf( + parent_mo_or_dn=mo, + name=vhba_template['vsan'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_vlan_find.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_vlan_find.py new file mode 100644 index 000000000..595fb5960 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_vlan_find.py @@ -0,0 +1,123 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_vlan_find +short_description: Find VLANs on Cisco UCS Manager +description: +- Find VLANs on Cisco UCS Manager based on different criteria. +extends_documentation_fragment: cisco.ucs.ucs +options: + pattern: + description: + - Regex pattern to find within the name property of the fabricVlan class. + - This is required if C(vlanid) parameter is not supplied. + type: str + fabric: + description: + - "The fabric configuration of the VLAN. This can be one of the following:" + - "common - The VLAN applies to both fabrics and uses the same configuration parameters in both cases." + - "A — The VLAN only applies to fabric A." + - "B — The VLAN only applies to fabric B." + choices: [common, A, B] + default: common + type: str + vlanid: + description: + - The unique string identifier assigned to the VLAN. + - A VLAN ID can be between '1' and '3967', or between '4048' and '4093'. + - This is required if C(pattern) parameter is not supplied. + type: str +requirements: +- ucsmsdk +author: +- David Martinez (@dx0xm) +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: '2.9' +''' + +EXAMPLES = r''' +- name: Get all vlans in fabric A + cisco.ucs.ucs_vlan_find: + hostname: 172.16.143.150 + username: admin + password: password + fabric: 'A' + pattern: '.' +- name: Confirm if vlan 15 is present + cisco.ucs.ucs_vlan_find: + hostname: 172.16.143.150 + username: admin + password: password + vlanid: '15' +''' + +RETURN = r''' +vlan_list: + description: basic details of vlans found + returned: on success + type: list + sample: [ + { + "id": "0", + "name": "vlcloud1" + } + ] +''' + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + fabric=dict(type='str', default='common', choices=['common', 'A', 'B']), + pattern=dict(type='str'), + vlanid=dict(type='str') + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_one_of=[['pattern', 'vlanid']] + ) + + ucs = UCSModule(module) + + filtls = ['(cloud,"ethlan")'] + if module.params['fabric'] != 'common': + filtls.append('(switch_id,"' + module.params['fabric'] + '")') + if module.params['vlanid']: + filtls.append('(id,"' + module.params['vlanid'] + '")') + else: + filtls.append('(name,"' + module.params['pattern'] + '")') + + object_dict = ucs.login_handle.query_classid("fabricVlan", filter_str=' and '.join(filtls)) + + if object_dict is None: + module.fail_json(msg="Failed to query vlan objects") + + vlnlist = [] + for ob in object_dict: + vlnlist.append(dict(name=ob.name, id=ob.id)) + + module.exit_json(changed=False, + vlan_list=vlnlist) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_vlan_to_group.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_vlan_to_group.py new file mode 100644 index 000000000..c7b6e81c3 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_vlan_to_group.py @@ -0,0 +1,158 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: ucs_vlan_to_group +short_description: Add VLANs to a VLAN Group. Requires VLAN and VLAN Group to already be created on UCS prior to running module. +description: +- Add VLANs to VLAN Groups on Cisco UCS Manager. +extends_documentation_fragment: ucs +options: + state: + description: + - If C(present), will verify VLANs are present and will create if needed. + - If C(absent), will verify VLANs are absent and will delete if needed. + choices: [present, absent] + default: present + vlanname: + description: + - The name assigned to the VLAN. + - The VLAN name is case sensitive. + - This name can be between 1 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + required: yes + vlangroup: + description: + - The name assigned to the VLAN Group. + - The VLAN Group name is case sensitive. + - This name can be between 1 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + required: yes +requirements: +- ucsmsdk +author: +- Derrick Johnson @derricktj +version_added: '2.10' +''' + +EXAMPLES = r''' +- name: Configure VLAN + cisco.ucs.ucs_vlan_to_group: + hostname: 1.1.1.1 + username: admin + password: password + vlangroup: VLANGROUP + vlanname: VLANNAME + state: present +- name: Remove VLAN + cisco.ucs.ucs_vlan_to_group: + hostname: 1.1.1.1 + username: admin + password: password + vlangroup: VLANGROUP + vlanname: VLANNAME + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + vlangroup=dict(type='str', required=True), + vlanname=dict(type='str', required=True), + state=dict(default='present', choices=['present', 'absent'], type='str'), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['vlangroup', 'vlanname']], + ], + ) + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.fabric.FabricNetGroup import FabricNetGroup + from ucsmsdk.mometa.fabric.FabricPooledVlan import FabricPooledVlan + + changed = False + try: + dnpooled1_exists = False + + # dn = fabric/lan/net-group-VLANGROUP + # Check for VLAN Group + dngroup = 'fabric/lan/net-group-' + module.params['vlangroup'] + dngroup1 = ucs.login_handle.query_dn(dngroup) + + # dn = fabric/lan/net-VLANNAME + # Check for VLAN + dnvlan = 'fabric/lan/net-' + module.params['vlanname'] + dnvlan1 = ucs.login_handle.query_dn(dnvlan) + + # dn = fabric/lan/net-group-VLANGROUP/net-VLANNAME + # Check for VLAN within VLAN Group + dnpooled = 'fabric/lan/net-group-' + module.params['vlangroup'] + '/net-' + module.params['vlanname'] + dnpooled1 = ucs.login_handle.query_dn(dnpooled) + + # Configuration MOs. Couldn't really get this to work off the DNs, so I built additional objects. + mo = FabricNetGroup(parent_mo_or_dn="fabric/lan", name=module.params['vlangroup']) + mo_1 = FabricPooledVlan(parent_mo_or_dn=mo, name=module.params['vlanname']) + + if not dngroup1: + # Error out if the VLAN Group is missing + err = True + ucs.result['msg'] = module.params['vlangroup'] + " VLAN Group not configured in UCS" + + if not dnvlan1: + # Error out if VLAN is missing + err = True + ucs.result['msg'] = module.params['vlanname'] + " VLAN not configured in UCS. Use ucs_vlans module to create the VLAN first" + + if dnpooled1: + dnpooled1_exists = True + + if module.params['state'] == 'absent': + if dnpooled1_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo_1) + ucs.login_handle.commit() + changed = True + + if module.params['state'] == 'present': + if not dnpooled1_exists: + if not module.check_mode: + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_vlans.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_vlans.py new file mode 100644 index 000000000..a0376f9be --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_vlans.py @@ -0,0 +1,192 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_vlans +short_description: Configures VLANs on Cisco UCS Manager +description: +- Configures VLANs on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify VLANs are present and will create if needed. + - If C(absent), will verify VLANs are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name assigned to the VLAN. + - The VLAN name is case sensitive. + - This name can be between 1 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the VLAN is created. + required: yes + multicast_policy: + description: + - The multicast policy associated with this VLAN. + - This option is only valid if the Sharing Type field is set to None or Primary. + default: '' + fabric: + description: + - "The fabric configuration of the VLAN. This can be one of the following:" + - "common - The VLAN applies to both fabrics and uses the same configuration parameters in both cases." + - "A — The VLAN only applies to fabric A." + - "B — The VLAN only applies to fabric B." + - For upstream disjoint L2 networks, Cisco recommends that you choose common to create VLANs that apply to both fabrics. + choices: [common, A, B] + default: common + id: + description: + - The unique string identifier assigned to the VLAN. + - A VLAN ID can be between '1' and '3967', or between '4048' and '4093'. + - You cannot create VLANs with IDs from 4030 to 4047. This range of VLAN IDs is reserved. + - The VLAN IDs you specify must also be supported on the switch that you are using. + - VLANs in the LAN cloud and FCoE VLANs in the SAN cloud must have different IDs. + - Optional if state is absent. + required: yes + sharing: + description: + - The Sharing Type field. + - "Whether this VLAN is subdivided into private or secondary VLANs. This can be one of the following:" + - "none - This VLAN does not have any secondary or private VLANs. This is a regular VLAN." + - "primary - This VLAN can have one or more secondary VLANs, as shown in the Secondary VLANs area. This VLAN is a primary VLAN in the private VLAN domain." + - "isolated - This is a private VLAN associated with a primary VLAN. This VLAN is an Isolated VLAN." + - "community - This VLAN can communicate with other ports on the same community VLAN as well as the promiscuous port. This VLAN is a Community VLAN." + choices: [none, primary, isolated, community] + default: none + native: + description: + - Designates the VLAN as a native VLAN. + choices: ['yes', 'no'] + default: 'no' +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure VLAN + cisco.ucs.ucs_vlans: + hostname: 172.16.143.150 + username: admin + password: password + name: vlan2 + id: '2' + native: 'yes' + +- name: Remove VLAN + cisco.ucs.ucs_vlans: + hostname: 172.16.143.150 + username: admin + password: password + name: vlan2 + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + name=dict(type='str', required=True), + multicast_policy=dict(type='str', default=''), + fabric=dict(type='str', default='common', choices=['common', 'A', 'B']), + id=dict(type='str'), + sharing=dict(type='str', default='none', choices=['none', 'primary', 'isolated', 'community']), + native=dict(type='str', default='no', choices=['yes', 'no']), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'present', ['id']], + ], + ) + ucs = UCSModule(module) + + err = False + + # UCSModule creation above verifies ucsmsdk is present and exits on failure, so additional imports are done below. + from ucsmsdk.mometa.fabric.FabricVlan import FabricVlan + + changed = False + try: + mo_exists = False + props_match = False + # dn is fabric/lan/net-<name> for common vlans or fabric/lan/[A or B]/net-<name> for A or B + dn_base = 'fabric/lan' + if module.params['fabric'] != 'common': + dn_base += '/' + module.params['fabric'] + dn = dn_base + '/net-' + module.params['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(id=module.params['id']) + kwargs['default_net'] = module.params['native'] + kwargs['sharing'] = module.params['sharing'] + kwargs['mcast_policy_name'] = module.params['multicast_policy'] + if mo.check_prop_match(**kwargs): + props_match = True + + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = FabricVlan( + parent_mo_or_dn=dn_base, + name=module.params['name'], + id=module.params['id'], + default_net=module.params['native'], + sharing=module.params['sharing'], + mcast_policy_name=module.params['multicast_policy'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_vnic_template.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_vnic_template.py new file mode 100644 index 000000000..544af9a01 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_vnic_template.py @@ -0,0 +1,377 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_vnic_template +short_description: Configures vNIC templates on Cisco UCS Manager +description: +- Configures vNIC templates on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify vNIC templates are present and will create if needed. + - If C(absent), will verify vNIC templates are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the vNIC template. + - This name can be between 1 and 16 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the template is created. + required: yes + description: + description: + - A user-defined description of the vNIC template. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + fabric: + description: + - The Fabric ID field specifying the fabric interconnect associated with vNICs created from this template. + - If you want fabric failover enabled on vNICs created from this template, use of of the following:" + - "A-B to use Fabric A by default with failover enabled." + - "B-A to use Fabric B by default with failover enabled." + - "Do not enable vNIC fabric failover under the following circumstances:" + - "- If the Cisco UCS domain is running in Ethernet switch mode. vNIC fabric failover is not supported in that mode." + - "- If you plan to associate one or more vNICs created from this template to a server with an adapter that does not support fabric failover." + choices: [A, B, A-B, B-A] + default: A + redundancy_type: + description: + - The Redundancy Type used for vNIC redundancy pairs during fabric failover. + - "This can be one of the following:" + - "primary — Creates configurations that can be shared with the Secondary template." + - "secondary — All shared configurations are inherited from the Primary template." + - "none - Legacy vNIC template behavior. Select this option if you do not want to use redundancy." + choices: [none, primary, secondary] + default: none + peer_redundancy_template: + description: + - The Peer Redundancy Template. + - The name of the vNIC template sharing a configuration with this template. + - If the redundancy_type is primary, the name of the secondary template should be provided. + - If the redundancy_type is secondary, the name of the primary template should be provided. + - Secondary templates can only configure non-shared properties (name, description, and mac_pool). + aliases: [ peer_redundancy_templ ] + target: + description: + - The possible target for vNICs created from this template. + - The target determines whether or not Cisco UCS Manager automatically creates a VM-FEX port profile with the appropriate settings for the vNIC template. + - "This can be one of the following:" + - "adapter — The vNICs apply to all adapters. No VM-FEX port profile is created if you choose this option." + - "vm - The vNICs apply to all virtual machines. A VM-FEX port profile is created if you choose this option." + default: adapter + template_type: + description: + - The Template Type field. + - "This can be one of the following:" + - "initial-template — vNICs created from this template are not updated if the template changes." + - "updating-template - vNICs created from this template are updated if the template changes." + choices: [initial-template, updating-template] + default: initial-template + vlans_list: + description: + - List of VLANs used by the vNIC template. + - "Each list element has the following suboptions:" + - "= name" + - " The name of the VLAN (required)." + - "- native" + - " Designates the VLAN as a native VLAN. Only one VLAN in the list can be a native VLAN." + - " [choices: 'no', 'yes']" + - " [Default: 'no']" + - "- state" + - " If present, will verify VLAN is present on template." + - " If absent, will verify VLAN is absent on template." + - " choices: [present, absent]" + - " default: present" + cdn_source: + description: + - CDN Source field. + - "This can be one of the following options:" + - "vnic-name - Uses the vNIC template name of the vNIC instance as the CDN name. This is the default option." + - "user-defined - Uses a user-defined CDN name for the vNIC template. If this option is chosen, cdn_name must also be provided." + choices: [vnic-name, user-defined] + default: vnic-name + cdn_name: + description: + - CDN Name used when cdn_source is set to user-defined. + mtu: + description: + - The MTU field. + - The maximum transmission unit, or packet size, that vNICs created from this vNIC template should use. + - Enter a string between '1500' and '9000'. + - If the vNIC template has an associated QoS policy, the MTU specified here must be equal to or less than the MTU specified in the QoS system class. + default: '1500' + mac_pool: + description: + - The MAC address pool that vNICs created from this vNIC template should use. + qos_policy: + description: + - The quality of service (QoS) policy that vNICs created from this vNIC template should use. + network_control_policy: + description: + - The network control policy that vNICs created from this vNIC template should use. + pin_group: + description: + - The LAN pin group that vNICs created from this vNIC template should use. + stats_policy: + description: + - The statistics collection policy that vNICs created from this vNIC template should use. + default: default + org_dn: + description: + - Org dn (distinguished name) + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure vNIC template + cisco.ucs.ucs_vnic_template: + hostname: 172.16.143.150 + username: admin + password: password + name: vNIC-A + fabric: A + vlans_list: + - name: default + native: 'yes' + +- name: Configure vNIC template with failover + cisco.ucs.ucs_vnic_template: + hostname: 172.16.143.150 + username: admin + password: password + name: vNIC-A-B + fabric: A-B + vlans_list: + - name: default + native: 'yes' + state: present + +- name: Remove vNIC template + cisco.ucs.ucs_vnic_template: + hostname: 172.16.143.150 + username: admin + password: password + name: vNIC-A + state: absent + +- name: Remove another vNIC template + cisco.ucs.ucs_vnic_template: + hostname: 172.16.143.150 + username: admin + password: password + name: vNIC-A-B + state: absent + +- name: Remove VLAN from template + cisco.ucs.ucs_vnic_template: + hostname: 172.16.143.150 + username: admin + password: password + name: vNIC-A-B + fabric: A-B + vlans_list: + - name: default + native: 'yes' + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str', required=True), + description=dict(type='str', aliases=['descr'], default=''), + fabric=dict(type='str', default='A', choices=['A', 'B', 'A-B', 'B-A']), + redundancy_type=dict(type='str', default='none', choices=['none', 'primary', 'secondary']), + peer_redundancy_template=dict(type='str', aliases=['peer_redundancy_templ'], default=''), + target=dict(type='str', default='adapter', choices=['adapter', 'vm']), + template_type=dict(type='str', default='initial-template', choices=['initial-template', 'updating-template']), + vlans_list=dict(type='list'), + cdn_source=dict(type='str', default='vnic-name', choices=['vnic-name', 'user-defined']), + cdn_name=dict(type='str', default=''), + mtu=dict(type='str', default='1500'), + mac_pool=dict(type='str', default=''), + qos_policy=dict(type='str', default=''), + network_control_policy=dict(type='str', default=''), + pin_group=dict(type='str', default=''), + stats_policy=dict(type='str', default='default'), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_if=[ + ['cdn_source', 'user-defined', ['cdn_name']], + ], + ) + ucs = UCSModule(module) + + err = False + + # UCSModule creation above verifies ucsmsdk is present and exits on failure. Additional imports are done below. + from ucsmsdk.mometa.vnic.VnicLanConnTempl import VnicLanConnTempl + from ucsmsdk.mometa.vnic.VnicEtherIf import VnicEtherIf + + changed = False + try: + mo_exists = False + props_match = False + # dn is <org_dn>/lan-conn-templ-<name> + dn = module.params['org_dn'] + '/lan-conn-templ-' + module.params['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + # set default params for lists which can't be done in the argument_spec + if module.params.get('vlans_list'): + for vlan in module.params['vlans_list']: + if not vlan.get('native'): + vlan['native'] = 'no' + if not vlan.get('state'): + vlan['state'] = 'present' + # for target 'adapter', change to internal UCS Manager spelling 'adaptor' + if module.params['target'] == 'adapter': + module.params['target'] = 'adaptor' + if mo_exists: + # check top-level mo props + kwargs = dict(descr=module.params['description']) + kwargs['switch_id'] = module.params['fabric'] + kwargs['redundancy_pair_type'] = module.params['redundancy_type'] + kwargs['peer_redundancy_templ_name'] = module.params['peer_redundancy_template'] + kwargs['ident_pool_name'] = module.params['mac_pool'] + # do not check shared props if this is a secondary template + if module.params['redundancy_type'] != 'secondary': + kwargs['target'] = module.params['target'] + kwargs['templ_type'] = module.params['template_type'] + kwargs['cdn_source'] = module.params['cdn_source'] + kwargs['admin_cdn_name'] = module.params['cdn_name'] + kwargs['mtu'] = module.params['mtu'] + kwargs['qos_policy_name'] = module.params['qos_policy'] + kwargs['nw_ctrl_policy_name'] = module.params['network_control_policy'] + kwargs['pin_to_group_name'] = module.params['pin_group'] + kwargs['stats_policy_name'] = module.params['stats_policy'] + if mo.check_prop_match(**kwargs): + # top-level props match, check next level mo/props + if not module.params.get('vlans_list'): + props_match = True + else: + # check vlan props + for vlan in module.params['vlans_list']: + child_dn = dn + '/if-' + str(vlan['name']) + mo_1 = ucs.login_handle.query_dn(child_dn) + if vlan['state'] == 'absent': + if mo_1: + props_match = False + break + else: + if mo_1: + kwargs = dict(default_net=vlan['native']) + if mo_1.check_prop_match(**kwargs): + props_match = True + else: + props_match = False + break + + if not props_match: + if not module.check_mode: + # create if mo does not already exist + # secondary template only sets non shared props + if module.params['redundancy_type'] == 'secondary': + mo = VnicLanConnTempl( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['name'], + descr=module.params['description'], + switch_id=module.params['fabric'], + redundancy_pair_type=module.params['redundancy_type'], + peer_redundancy_templ_name=module.params['peer_redundancy_template'], + ident_pool_name=module.params['mac_pool'], + ) + else: + mo = VnicLanConnTempl( + parent_mo_or_dn=module.params['org_dn'], + name=module.params['name'], + descr=module.params['description'], + switch_id=module.params['fabric'], + redundancy_pair_type=module.params['redundancy_type'], + peer_redundancy_templ_name=module.params['peer_redundancy_template'], + target=module.params['target'], + templ_type=module.params['template_type'], + cdn_source=module.params['cdn_source'], + admin_cdn_name=module.params['cdn_name'], + mtu=module.params['mtu'], + ident_pool_name=module.params['mac_pool'], + qos_policy_name=module.params['qos_policy'], + nw_ctrl_policy_name=module.params['network_control_policy'], + pin_to_group_name=module.params['pin_group'], + stats_policy_name=module.params['stats_policy'], + ) + + if module.params.get('vlans_list'): + for vlan in module.params['vlans_list']: + if vlan['state'] == 'absent': + child_dn = dn + '/if-' + str(vlan['name']) + mo_1 = ucs.login_handle.query_dn(child_dn) + ucs.login_handle.remove_mo(mo_1) + else: + mo_1 = VnicEtherIf( + parent_mo_or_dn=mo, + name=str(vlan['name']), + default_net=vlan['native'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_vsans.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_vsans.py new file mode 100644 index 000000000..cf4c656e0 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_vsans.py @@ -0,0 +1,204 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_vsans +short_description: Configures VSANs on Cisco UCS Manager +description: +- Configures VSANs on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify VSANs are present and will create if needed. + - If C(absent), will verify VSANs are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name assigned to the VSAN. + - This name can be between 1 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the VSAN is created. + required: yes + vsan_id: + description: + - The unique identifier assigned to the VSAN. + - The ID can be a string between '1' and '4078', or between '4080' and '4093'. '4079' is a reserved VSAN ID. + - In addition, if you plan to use FC end-host mode, the range between '3840' to '4079' is also a reserved VSAN ID range. + - Optional if state is absent. + required: yes + vlan_id: + description: + - The unique string identifier assigned to the VLAN used for Fibre Channel connections. + - Note that Cisco UCS Manager uses VLAN '4048'. See the UCS Manager configuration guide if you want to assign '4048' to a VLAN. + - Optional if state is absent. + required: yes + fc_zoning: + description: + - Fibre Channel zoning configuration for the Cisco UCS domain. + - "Fibre Channel zoning can be set to one of the following values:" + - "disabled — The upstream switch handles Fibre Channel zoning, or Fibre Channel zoning is not implemented for the Cisco UCS domain." + - "enabled — Cisco UCS Manager configures and controls Fibre Channel zoning for the Cisco UCS domain." + - If you enable Fibre Channel zoning, do not configure the upstream switch with any VSANs that are being used for Fibre Channel zoning. + choices: [disabled, enabled] + default: disabled + fabric: + description: + - "The fabric configuration of the VSAN. This can be one of the following:" + - "common - The VSAN maps to the same VSAN ID in all available fabrics." + - "A - The VSAN maps to the a VSAN ID that exists only in fabric A." + - "B - The VSAN maps to the a VSAN ID that exists only in fabric B." + choices: [common, A, B] + default: common +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure VSAN + cisco.ucs.ucs_vsans: + hostname: 172.16.143.150 + username: admin + password: password + name: vsan110 + fabric: common + vsan_id: '110' + vlan_id: '110' + +- name: Remove VSAN + cisco.ucs.ucs_vsans: + hostname: 172.16.143.150 + username: admin + password: password + name: vsan110 + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + name=dict(type='str'), + vsan_id=dict(type='str'), + vlan_id=dict(type='str'), + fc_zoning=dict(type='str', default='disabled', choices=['disabled', 'enabled']), + fabric=dict(type='str', default='common', choices=['common', 'A', 'B']), + state=dict(type='str', default='present', choices=['present', 'absent']), + vsan_list=dict(type='list'), + ) + + # Note that use of vsan_list is an experimental feature which allows multiple resource updates with a single UCSM connection. + # Support for vsan_list may change or be removed once persistent UCS connections are supported. + # Either vsan_list or name/vsan_id/vlan_id is required (user can specify either a list or single resource). + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_one_of=[ + ['vsan_list', 'name'] + ], + mutually_exclusive=[ + ['vsan_list', 'name'] + ], + ) + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.fabric.FabricVsan import FabricVsan + + changed = False + try: + # Only documented use is a single resource, but to also support experimental + # feature allowing multiple updates all params are converted to a vsan_list below. + + if module.params['vsan_list']: + # directly use the list (single resource and list are mutually exclusive + vsan_list = module.params['vsan_list'] + else: + # single resource specified, create list from the current params + vsan_list = [module.params] + for vsan in vsan_list: + mo_exists = False + props_match = False + # set default params. Done here to set values for lists which can't be done in the argument_spec + if not vsan.get('fc_zoning'): + vsan['fc_zoning'] = 'disabled' + if not vsan.get('fabric'): + vsan['fabric'] = 'common' + # dn is fabric/san/net-<name> for common vsans or fabric/san/[A or B]/net-<name> for A or B + dn_base = 'fabric/san' + if vsan['fabric'] != 'common': + dn_base += '/' + vsan['fabric'] + dn = dn_base + '/net-' + vsan['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + # mo must exist but all properties do not have to match + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + if mo_exists: + # check top-level mo props + kwargs = dict(id=vsan['vsan_id']) + kwargs['fcoe_vlan'] = vsan['vlan_id'] + kwargs['zoning_state'] = vsan['fc_zoning'] + if (mo.check_prop_match(**kwargs)): + props_match = True + + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = FabricVsan( + parent_mo_or_dn=dn_base, + name=vsan['name'], + id=vsan['vsan_id'], + fcoe_vlan=vsan['vlan_id'], + zoning_state=vsan['fc_zoning'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/plugins/modules/ucs_wwn_pool.py b/ansible_collections/cisco/ucs/plugins/modules/ucs_wwn_pool.py new file mode 100644 index 000000000..9751ebcb4 --- /dev/null +++ b/ansible_collections/cisco/ucs/plugins/modules/ucs_wwn_pool.py @@ -0,0 +1,239 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'certified'} + +DOCUMENTATION = r''' +--- +module: ucs_wwn_pool +short_description: Configures WWNN or WWPN pools on Cisco UCS Manager +description: +- Configures WWNNs or WWPN pools on Cisco UCS Manager. +extends_documentation_fragment: cisco.ucs.ucs +options: + state: + description: + - If C(present), will verify WWNNs/WWPNs are present and will create if needed. + - If C(absent), will verify WWNNs/WWPNs are absent and will delete if needed. + choices: [present, absent] + default: present + name: + description: + - The name of the World Wide Node Name (WWNN) or World Wide Port Name (WWPN) pool. + - This name can be between 1 and 32 alphanumeric characters. + - "You cannot use spaces or any special characters other than - (hyphen), \"_\" (underscore), : (colon), and . (period)." + - You cannot change this name after the WWNN or WWPN pool is created. + required: yes + purpose: + description: + - Specify whether this is a node (WWNN) or port (WWPN) pool. + - Optional if state is absent. + choices: [node, port] + required: yes + description: + description: + - A description of the WWNN or WWPN pool. + - Enter up to 256 characters. + - "You can use any characters or spaces except the following:" + - "` (accent mark), \ (backslash), ^ (carat), \" (double quote), = (equal sign), > (greater than), < (less than), or ' (single quote)." + aliases: [ descr ] + order: + description: + - The Assignment Order field. + - "This can be one of the following:" + - "default - Cisco UCS Manager selects a random identity from the pool." + - "sequential - Cisco UCS Manager selects the lowest available identity from the pool." + choices: [default, sequential] + default: default + first_addr: + description: + - The first initiator in the World Wide Name (WWN) block. + - This is the From field in the UCS Manager Add WWN Blocks menu. + last_addr: + description: + - The last initiator in the World Wide Name (WWN) block. + - This is the To field in the UCS Manager Add WWN Blocks menu. + - For WWxN pools, the pool size must be a multiple of ports-per-node + 1. + - For example, if there are 7 ports per node, the pool size must be a multiple of 8. + - If there are 63 ports per node, the pool size must be a multiple of 64. + org_dn: + description: + - Org dn (distinguished name) + default: org-root +requirements: +- ucsmsdk +author: +- David Soper (@dsoper2) +- John McDonough (@movinalot) +- CiscoUcs (@CiscoUcs) +version_added: '2.5' +''' + +EXAMPLES = r''' +- name: Configure WWNN/WWPN pools + cisco.ucs.ucs_wwn_pool: + hostname: 172.16.143.150 + username: admin + password: password + name: WWNN-Pool + purpose: node + first_addr: 20:00:00:25:B5:48:00:00 + last_addr: 20:00:00:25:B5:48:00:0F +- cisco.ucs.ucs_wwn_pool: + hostname: 172.16.143.150 + username: admin + password: password + name: WWPN-Pool-A + purpose: port + order: sequential + first_addr: 20:00:00:25:B5:48:0A:00 + last_addr: 20:00:00:25:B5:48:0A:0F + +- name: Remove WWNN/WWPN pools + cisco.ucs.ucs_wwn_pool: + hostname: 172.16.143.150 + username: admin + password: password + name: WWNN-Pool + state: absent +- cisco.ucs.ucs_wwn_pool: + hostname: 172.16.143.150 + username: admin + password: password + name: WWPN-Pool-A + state: absent +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.ucs.plugins.module_utils.ucs import UCSModule, ucs_argument_spec + + +def main(): + argument_spec = ucs_argument_spec + argument_spec.update( + org_dn=dict(type='str', default='org-root'), + name=dict(type='str'), + purpose=dict(type='str', choices=['node', 'port']), + descr=dict(type='str'), + order=dict(type='str', default='default', choices=['default', 'sequential']), + first_addr=dict(type='str'), + last_addr=dict(type='str'), + state=dict(type='str', default='present', choices=['present', 'absent']), + wwn_list=dict(type='list'), + ) + + # Note that use of wwn_list is an experimental feature which allows multiple resource updates with a single UCSM connection. + # Support for wwn_list may change or be removed once persistent UCS connections are supported. + # Either wwn_list or name is required (user can specify either a list or single resource). + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + required_one_of=[ + ['wwn_list', 'name'] + ], + mutually_exclusive=[ + ['wwn_list', 'name'] + ], + ) + ucs = UCSModule(module) + + err = False + + from ucsmsdk.mometa.fcpool.FcpoolInitiators import FcpoolInitiators + from ucsmsdk.mometa.fcpool.FcpoolBlock import FcpoolBlock + + changed = False + try: + # Only documented use is a single resource, but to also support experimental + # feature allowing multiple updates all params are converted to a wwn_list below. + + if module.params['wwn_list']: + # directly use the list (single resource and list are mutually exclusive + wwn_list = module.params['wwn_list'] + else: + # single resource specified, create list from the current params + wwn_list = [module.params] + for wwn in wwn_list: + mo_exists = False + props_match = False + # set default params. Done here to set values for lists which can't be done in the argument_spec + if not wwn.get('descr'): + wwn['descr'] = '' + if not wwn.get('order'): + wwn['order'] = 'default' + # dn is <org_dn>/wwn-pool-<name> for WWNN or WWPN + dn = module.params['org_dn'] + '/wwn-pool-' + wwn['name'] + + mo = ucs.login_handle.query_dn(dn) + if mo: + mo_exists = True + + if module.params['state'] == 'absent': + if mo_exists: + if not module.check_mode: + ucs.login_handle.remove_mo(mo) + ucs.login_handle.commit() + changed = True + else: + # append purpose param with suffix used by UCSM + purpose_param = wwn['purpose'] + '-wwn-assignment' + if mo_exists: + # check top-level mo props + kwargs = dict(assignment_order=wwn['order']) + kwargs['descr'] = wwn['descr'] + kwargs['purpose'] = purpose_param + if (mo.check_prop_match(**kwargs)): + # top-level props match, check next level mo/props + if 'last_addr' in wwn and 'first_addr' in wwn: + block_dn = dn + '/block-' + wwn['first_addr'].upper() + '-' + wwn['last_addr'].upper() + mo_1 = ucs.login_handle.query_dn(block_dn) + if mo_1: + props_match = True + else: + props_match = True + + if not props_match: + if not module.check_mode: + # create if mo does not already exist + mo = FcpoolInitiators( + parent_mo_or_dn=module.params['org_dn'], + name=wwn['name'], + descr=wwn['descr'], + assignment_order=wwn['order'], + purpose=purpose_param, + ) + if 'last_addr' in wwn and 'first_addr' in wwn: + mo_1 = FcpoolBlock( + parent_mo_or_dn=mo, + to=wwn['last_addr'], + r_from=wwn['first_addr'], + ) + + ucs.login_handle.add_mo(mo, True) + ucs.login_handle.commit() + changed = True + + except Exception as e: + err = True + ucs.result['msg'] = "setup error: %s " % str(e) + + ucs.result['changed'] = changed + if err: + module.fail_json(**ucs.result) + module.exit_json(**ucs.result) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/cisco/ucs/requirements.txt b/ansible_collections/cisco/ucs/requirements.txt new file mode 100644 index 000000000..b27af455c --- /dev/null +++ b/ansible_collections/cisco/ucs/requirements.txt @@ -0,0 +1 @@ +ucsmsdk |