diff --git a/CHANGELOG.md b/CHANGELOG.md index 452667a53..d7f7b3bcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## v1.8.0-beta.1 (20 Oct 2022) + +**Features** + +**Nutanix Database Service (Era)** +- Ansible module for Single Instance Databases +- Ansible info module for Database Instances +- Ansible info module for NDB Clusters +- Ansible info module for DB server VMs +- Ansible info module for Profiles +- Ansible info module for SLAs +- Ansible info module for Time Machines +- Ansible info module for Database Clones + +**Full Changelog:** [here](https://github.com/nutanix/nutanix.ansible/compare/v1.7.0...v1.8.0-beta.1) + ## v1.7.0 (30 Sep 2022) **Feature:** diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f768cbb56..52490f58a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,21 @@ Nutanix.Ncp Release Notes .. contents:: Topics +v1.8.0-beta.1 +============= + +New Modules +----------- + +- ntnx_ndb_clones_info - info module for database clones +- ntnx_ndb_clusters_info - info module for ndb clusters info +- ntnx_ndb_databases - Module for create, update and delete of single instance database. Currently, postgres type database is officially supported. +- ntnx_ndb_databases_info - info module for ndb database instances +- ntnx_ndb_db_servers_info - info module for ndb db server vms info +- ntnx_ndb_profiles_info - info module for ndb profiles +- ntnx_ndb_slas_info - info module for ndb slas +- ntnx_ndb_time_machines_info - info module for ndb time machines + v1.7.0 ====== diff --git a/README.md b/README.md index 157946e72..37eaaa388 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ This collection requires Python 2.7 or greater > For the 1.7.0 release of the ansible plugin it will have N-2 compatibility with the Prism Central APIs. This release was tested against Prism Central versions pc.2022.6, pc.2022.4 and pc2022.1.0.2. +> For the 1.8.0-beta.1 release of the ansible plugin it will have N compatibility with the Prism Central APIs. This release was tested against Prism Central version pc.2022.6 . ### Notes: 1. Static routes module (ntnx_static_routes) is supported for PC versions >= pc.2022.1 @@ -40,15 +41,6 @@ This collection requires Python 2.7 or greater 3. For Users and User Groups modules (ntnx_users and ntnx_user_groups), adding Identity Provider (IdP) & Organizational Unit (OU) based users/groups are supported for PC versions >= pc.2022.1 -#### v1.7.0 additions: -1. Added functionality to add cluster reference while using vlan subnet name in vms to pull subnet info from particular cluster. Checkout vm.yml example for same. - -2. Added functionality to configure role mappings, collaboration, new user/user groups, vpcs and accounts in ntnx_projects. Checkout projects_with_role_mapping.yml example for same. - -3. Added constructed inventory feature to inventory module to construct custom vars and groups based on existing inventory. - -4. Now inventory module can fetch 500+ entities. - Prism Central based examples: https://github.com/nutanix/nutanix.ansible/tree/main/examples/ ## Foundation @@ -66,6 +58,16 @@ Foundation Central based examples : https://github.com/nutanix/nutanix.ansible/t Karbon based examples : https://github.com/nutanix/nutanix.ansible/tree/main/examples/karbon +## Nutanix Database Service (ERA) +> For the 1.8.0-beta.1 release of the ansible plugin, it will have N-1 compatibility with the Nutanix Database Service (ERA). This release was tested against era versions v2.4.1 and v2.4.0 + +NDB based examples : https://github.com/nutanix/nutanix.ansible/tree/main/examples/ndb + +Nutanix Ansible support for Nutanix Database Service is currently at beta stage. + +### Notes: +1. Currently for ntnx_ndb_databases, creation of only postgres type database instance is tested and offically supported. + # Installing the collection **Prerequisite** @@ -150,6 +152,14 @@ ansible-playbook examples/iaas/iaas.yml | ntnx_karbon_clusters_info | Get clusters info. | | ntnx_karbon_registries | Create, Delete a karbon private registry entry | | ntnx_karbon_registries_info | Get karbon private registry registry info. | +| ntnx_ndb_databases | Create, Update and Delete single instance database. | +| ntnx_ndb_databases_info | Get database info. | +| ntnx_ndb_db_servers_info | Get db servers vm info. | +| ntnx_ndb_clusters_info | Get clusters info. | +| ntnx_ndb_slas_info | Get slas info | +| ntnx_ndb_profiles_info | Get profiles info. | +| ntnx_ndb_time_machines_info | Get time machines info. | +| ntnx_ndb_clones_info | Get database clones info. | | ntnx_pbrs | Create or delete a PBR. | | ntnx_pbrs_info | List existing PBRs. | | ntnx_permissions_info | List permissions info | @@ -171,6 +181,10 @@ ansible-playbook examples/iaas/iaas.yml | ntnx_static_routes_info | List existing static routes of a vpc. | | ntnx_subnets | Create or delete a Subnet. | | ntnx_subnets_info | List existing Subnets. | +| ntnx_user_groups | Create, Delete user_groups | +| ntnx_user_groups_info | Get user groups info. | +| ntnx_users | Create, Delete users | +| ntnx_users_info | Get users info. | | ntnx_vms | Create or delete a VM. | | ntnx_vms_clone | Clone VM. | | ntnx_vms_ova | Create OVA image from VM. | @@ -189,10 +203,6 @@ ansible-playbook examples/iaas/iaas.yml | ntnx_foundation_central_api_keys_info | List all the api keys created in Foundation Central. | | ntnx_foundation_central_imaged_clusters_info | List all the clusters created using Foundation Central. | | ntnx_foundation_central_imaged_nodes_info | List all the nodes registered with Foundation Central. | -| ntnx_user_groups | Create, Delete user_groups | -| ntnx_user_groups_info | Get user groups info. | -| ntnx_users | Create, Delete users | -| ntnx_users_info | Get users info. | ## Inventory Plugins diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 9f848535e..dad2effb2 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -282,3 +282,31 @@ releases: - nutanix.ncp.ntnx_prism_vm_inventory - [Imprv] add functionality constructed to module inventory [\#235](https://github.com/nutanix/nutanix.ansible/issues/235) release_date: '2022-09-30' + 1.8.0-beta.1: + modules: + - description: info module for database clones + name: ntnx_ndb_clones_info + namespace: '' + - description: info module for ndb clusters info + name: ntnx_ndb_clusters_info + namespace: '' + - description: Module for create, update and delete of single instance database. + Currently, postgres type database is officially supported. + name: ntnx_ndb_databases + namespace: '' + - description: info module for ndb database instances + name: ntnx_ndb_databases_info + namespace: '' + - description: info module for ndb db server vms info + name: ntnx_ndb_db_servers_info + namespace: '' + - description: info module for ndb profiles + name: ntnx_ndb_profiles_info + namespace: '' + - description: info module for ndb slas + name: ntnx_ndb_slas_info + namespace: '' + - description: info module for ndb time machines + name: ntnx_ndb_time_machines_info + namespace: '' + release_date: '2022-10-20' diff --git a/examples/ndb/provision_database_on_registered_db_server.yml b/examples/ndb/provision_database_on_registered_db_server.yml new file mode 100644 index 000000000..36737d145 --- /dev/null +++ b/examples/ndb/provision_database_on_registered_db_server.yml @@ -0,0 +1,50 @@ +--- +- name: Single instance postgres database creation on registered db server + hosts: localhost + gather_facts: false + collections: + - nutanix.ncp + module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: + nutanix_username: + nutanix_password: + validate_certs: false + + tasks: + + - name: Create single instance postgres database on registered db server vm + ntnx_ndb_databases: + + name: POSTGRES_DATABASE_ANSIBLE + + db_params_profile: + name: DEFAULT_POSTGRES_PARAMS + + db_vm: + use_registered_server: + name: otiakmxh + + postgres: + listener_port: "5432" + db_name: prad + db_password: db_password + db_size: 200 + + time_machine: + name: POSTGRES_DATABASE_ANSIBLE_TM + sla: + name: DEFAULT_OOB_GOLD_SLA + schedule: + daily: "11:00:00" + weekly: WEDNESDAY + monthly: 4 + quaterly: JANUARY + yearly: FEBRUARY + log_catchup: 30 + snapshots_per_day: 2 + + register: output + + - debug: + msg: "{{output}}" diff --git a/examples/ndb/single_instance_postgress_database.yml b/examples/ndb/single_instance_postgress_database.yml new file mode 100644 index 000000000..c1fbb29a6 --- /dev/null +++ b/examples/ndb/single_instance_postgress_database.yml @@ -0,0 +1,60 @@ +--- +- name: Single instance postgres database creation with new db server VM + hosts: localhost + gather_facts: false + collections: + - nutanix.ncp + module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: + nutanix_username: + nutanix_password: + validate_certs: false + + tasks: + + - name: Create single instance postgres database + ntnx_ndb_databases: + + name: POSTGRES_DATABASE_ANSIBLE + + db_params_profile: + name: DEFAULT_POSTGRES_PARAMS + + db_vm: + create_new_server: + name: postgres_server_ansible + password: temp_password + cluster: + name: EraCluster + software_profile: + name: POSTGRES_10.4_OOB + network_profile: + name: DEFAULT_OOB_POSTGRESQL_NETWORK + compute_profile: + name: DEFAULT_OOB_SMALL_COMPUTE + pub_ssh_key: "" + + postgres: + listener_port: "5432" + db_name: prad + db_password: db_password + db_size: 200 + + time_machine: + name: POSTGRES_DATABASE_ANSIBLE_TM + sla: + name: DEFAULT_OOB_GOLD_SLA + schedule: + daily: "11:00:00" + weekly: WEDNESDAY + monthly: 4 + quaterly: JANUARY + yearly: FEBRUARY + log_catchup: 30 + snapshots_per_day: 2 + + register: output + + - debug: + msg: "{{output}}" diff --git a/examples/ndb/soft_delete_database_instance.yml b/examples/ndb/soft_delete_database_instance.yml new file mode 100644 index 000000000..b569989ea --- /dev/null +++ b/examples/ndb/soft_delete_database_instance.yml @@ -0,0 +1,25 @@ +--- +- name: Soft delete single instance database and time machine associated + hosts: localhost + gather_facts: false + collections: + - nutanix.ncp + module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: + nutanix_username: + nutanix_password: + validate_certs: false + + tasks: + + - name: Soft delete single instance database and time machine associated + ntnx_ndb_databases: + state: "absent" + db_uuid: c0a4433a-49f2-40f3-ae52-d88788d2824b + soft_delete: true + delete_time_machine: true + register: output + + - debug: + msg: "{{output}}" diff --git a/galaxy.yml b/galaxy.yml index c1a002d30..3fec09102 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,6 +1,6 @@ namespace: "nutanix" name: "ncp" -version: "1.7.0" +version: "1.8.0-beta.1" readme: "README.md" authors: - "Abhishek Chaudhary (@abhimutant)" diff --git a/meta/runtime.yml b/meta/runtime.yml index 12e2e3f84..b83f94afa 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -63,3 +63,11 @@ action_groups: - ntnx_karbon_clusters_info - ntnx_karbon_registries - ntnx_karbon_registries_info + - ntnx_ndb_databases_info + - ntnx_ndb_clones_info + - ntnx_ndb_time_machines_info + - ntnx_ndb_profiles_info + - ntnx_ndb_db_servers_info + - ntnx_ndb_slas_info + - ntnx_ndb_databases + - ntnx_ndb_clusters_info diff --git a/plugins/doc_fragments/ntnx_ndb_base_module.py b/plugins/doc_fragments/ntnx_ndb_base_module.py new file mode 100644 index 000000000..a52e798f7 --- /dev/null +++ b/plugins/doc_fragments/ntnx_ndb_base_module.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Ansible Project +# 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 + + +class ModuleDocFragment(object): + + # Plugin options for ntnx ndb + DOCUMENTATION = r""" +options: + nutanix_host: + description: + - ndb era server IP address + - C(nutanix_host). If not set then the value of the C(NUTANIX_HOST), environment variable is used. + type: str + required: true + nutanix_password: + description: + - ndb era server password + - C(nutanix_password). If not set then the value of the C(NUTANIX_PASSWORD), environment variable is used. + type: str + required: true + nutanix_username: + description: + - ndb era server username + - C(nutanix_username). If not set then the value of the C(NUTANIX_USERNAME), environment variable is used. + type: str + required: true + validate_certs: + description: + - Set value to C(False) to skip validation for self signed certificates + - This is not recommended for production setup + - C(validate_certs). If not set then the value of the C(VALIDATE_CERTS), environment variable is used. + type: bool + default: true +""" diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index 0c08b2a5d..a487b64fc 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -277,3 +277,15 @@ class EntityFilterExpressionList: "right_hand_side": {"collection": "SELF_OWNED"}, }, } + + +class NDB: + + OPERATIONS_POLLING_DELAY = 30 + + class DatabaseTypes: + POSTGRES = "postgres_database" + + class StatusCodes: + SUCCESS = "5" + FAILURE = "4" diff --git a/plugins/module_utils/entity.py b/plugins/module_utils/entity.py index ba1d0642c..aad30c40d 100644 --- a/plugins/module_utils/entity.py +++ b/plugins/module_utils/entity.py @@ -90,6 +90,7 @@ def update( raise_error=True, no_response=False, timeout=30, + method="PUT", ): url = self.base_url + "/{0}".format(uuid) if uuid else self.base_url if endpoint: @@ -98,7 +99,7 @@ def update( url = self._build_url_with_query(url, query) return self._fetch_url( url, - method="PUT", + method=method, data=data, raise_error=raise_error, no_response=no_response, @@ -136,6 +137,7 @@ def delete( raise_error=True, no_response=False, timeout=30, + data=None, ): url = self.base_url + "/{0}".format(uuid) if uuid else self.base_url if endpoint: @@ -145,6 +147,7 @@ def delete( return self._fetch_url( url, method="DELETE", + data=data, raise_error=raise_error, no_response=no_response, timeout=timeout, @@ -373,7 +376,12 @@ def _fetch_url( return resp_json if status_code >= 300: - err = info.get("msg", "Status code != 2xx") + if resp_json and resp_json.get("message"): # for ndb apis + err = resp_json["message"] + elif info.get("msg"): + err = info["msg"] + else: + err = "Status code != 2xx" self.module.fail_json( msg="Failed fetching URL: {0}".format(url), status_code=status_code, @@ -384,7 +392,7 @@ def _fetch_url( if no_response: return {"status_code": status_code} - if not resp_json: + if resp_json is None: self.module.fail_json( msg="Failed to convert API response to json", status_code=status_code, diff --git a/plugins/module_utils/ndb/__init__.py b/plugins/module_utils/ndb/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/module_utils/ndb/base_info_module.py b/plugins/module_utils/ndb/base_info_module.py new file mode 100644 index 000000000..3d7ccc287 --- /dev/null +++ b/plugins/module_utils/ndb/base_info_module.py @@ -0,0 +1,18 @@ +# Copyright: 2021, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause ) +from __future__ import absolute_import, division, print_function + +from copy import deepcopy + +from .base_module import NdbBaseModule + +__metaclass__ = type + + +class NdbBaseInfoModule(NdbBaseModule): + def __init__(self, **kwargs): + self.argument_spec = deepcopy(NdbBaseModule.argument_spec) + self.argument_spec.pop("state") + self.argument_spec.pop("wait") + self.argument_spec.pop("timeout") + super(NdbBaseInfoModule, self).__init__(**kwargs) diff --git a/plugins/module_utils/ndb/base_module.py b/plugins/module_utils/ndb/base_module.py new file mode 100644 index 000000000..00ccfd742 --- /dev/null +++ b/plugins/module_utils/ndb/base_module.py @@ -0,0 +1,41 @@ +# Copyright: 2021, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause ) +from __future__ import absolute_import, division, print_function + +from ansible.module_utils.basic import AnsibleModule, env_fallback + +__metaclass__ = type + + +class NdbBaseModule(AnsibleModule): + argument_spec = dict( + nutanix_host=dict( + type="str", required=True, fallback=(env_fallback, ["NUTANIX_HOST"]) + ), + nutanix_username=dict( + type="str", fallback=(env_fallback, ["NUTANIX_USERNAME"]), required=True + ), + nutanix_password=dict( + type="str", + no_log=True, + fallback=(env_fallback, ["NUTANIX_PASSWORD"]), + required=True, + ), + validate_certs=dict( + type="bool", default=True, fallback=(env_fallback, ["VALIDATE_CERTS"]) + ), + state=dict(type="str", choices=["present", "absent"], default="present"), + timeout=dict(type="int", required=False, default=35 * 60), + wait=dict(type="bool", default=True), + ) + + def __init__(self, **kwargs): + if kwargs.get("argument_spec"): + kwargs["argument_spec"].update(self.argument_spec) + else: + kwargs["argument_spec"] = self.argument_spec + + if not kwargs.get("supports_check_mode"): + kwargs["supports_check_mode"] = True + + super(NdbBaseModule, self).__init__(**kwargs) diff --git a/plugins/module_utils/ndb/clones.py b/plugins/module_utils/ndb/clones.py new file mode 100644 index 000000000..dead5a246 --- /dev/null +++ b/plugins/module_utils/ndb/clones.py @@ -0,0 +1,28 @@ +# This file is part of Ansible +# 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 + + +from .nutanix_database import NutanixDatabase + + +class Clone(NutanixDatabase): + def __init__(self, module): + resource_type = "/clones" + super(Clone, self).__init__(module, resource_type=resource_type) + + def get_clone(self, uuid=None, name=None): + if uuid: + resp = self.read(uuid=uuid) + elif name: + query = {"value-type": "name", "value": name} + resp = self.read(query=query) + if not resp: + return None, "Clone with name {0} not found".format(name) + resp = resp[0] + else: + return None, "Please provide either uuid or name for fetching clone details" + + return resp, None diff --git a/plugins/module_utils/ndb/clusters.py b/plugins/module_utils/ndb/clusters.py new file mode 100644 index 000000000..53d9948de --- /dev/null +++ b/plugins/module_utils/ndb/clusters.py @@ -0,0 +1,62 @@ +# This file is part of Ansible +# 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 + + +from .nutanix_database import NutanixDatabase + + +class Cluster(NutanixDatabase): + def __init__(self, module): + resource_type = "/clusters" + super(Cluster, self).__init__(module, resource_type=resource_type) + + def get_uuid( + self, + value, + key="name", + data=None, + entity_type=None, + raise_error=True, + no_response=False, + ): + endpoint = "{0}/{1}".format(key, value) + resp = self.read(uuid=None, endpoint=endpoint) + return resp.get("id") + + def get_cluster(self, uuid=None, name=None): + if uuid: + resp = self.read(uuid=uuid) + elif name: + endpoint = "{0}/{1}".format("name", name) + resp = self.read(endpoint=endpoint) + + # we fetch cluster using ID again to get complete info. + if resp and resp.get("id"): + resp = self.read(uuid=resp["id"]) + + else: + return ( + None, + "Please provide either uuid or name for fetching cluster details", + ) + + return resp, None + + +# helper functions + + +def get_cluster_uuid(module, config): + uuid = "" + if config.get("name"): + clusters = Cluster(module) + uuid = clusters.get_uuid(config["name"]) + elif config.get("uuid"): + uuid = config["uuid"] + else: + error = "cluster config {0} doesn't have name or uuid key".format(config) + return error, None + return uuid, None diff --git a/plugins/module_utils/ndb/databases.py b/plugins/module_utils/ndb/databases.py new file mode 100644 index 000000000..9235a4f82 --- /dev/null +++ b/plugins/module_utils/ndb/databases.py @@ -0,0 +1,351 @@ +# This file is part of Ansible +# 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 + +from copy import deepcopy + +from ..constants import NDB as NDB_CONSTANTS +from .clusters import get_cluster_uuid +from .db_servers import get_db_server_uuid +from .nutanix_database import NutanixDatabase +from .profiles import Profile, get_profile_uuid +from .slas import get_sla_uuid +from .tags import Tag + +__metaclass__ = type + + +class Database(NutanixDatabase): + def __init__(self, module): + resource_type = "/databases" + super(Database, self).__init__(module, resource_type=resource_type) + self.build_spec_methods = { + "name": self._build_spec_name, + "desc": self._build_spec_desc, + "auto_tune_staging_drive": self._build_spec_auto_tune_staging_drive, + "db_params_profile": self._build_spec_db_params_profile, + "db_vm": self._build_spec_db_vm, + "time_machine": self._build_spec_time_machine, + "postgres": self._build_spec_postgres, + "tags": self._build_spec_tags, + } + + def get_uuid( + self, + value, + key="name", + data=None, + entity_type=None, + raise_error=True, + no_response=False, + ): + query = {"value-type": key, "value": value} + resp = self.read(query=query) + + if not resp: + return None, "Database instance with name {0} not found.".format(value) + + uuid = resp[0].get("id") + return uuid, None + + def create( + self, + data=None, + endpoint=None, + query=None, + method="POST", + raise_error=True, + no_response=False, + timeout=30, + ): + endpoint = "provision" + return super().create( + data, endpoint, query, method, raise_error, no_response, timeout + ) + + def update( + self, + data=None, + uuid=None, + endpoint=None, + query=None, + raise_error=True, + no_response=False, + timeout=30, + method="PATCH", + ): + return super().update( + data, uuid, endpoint, query, raise_error, no_response, timeout, method + ) + + def get_database(self, name=None, uuid=None): + default_query = {"detailed": True} + if uuid: + resp = self.read(uuid=uuid, query=default_query) + elif name: + query = {"value-type": "name", "value": name} + query.update(deepcopy(default_query)) + resp = self.read(query=query) + if not resp: + return None, "Database with name {0} not found".format(name) + if isinstance(resp, list): + resp = resp[0] + return resp, None + else: + return ( + None, + "Please provide either uuid or name for fetching database details", + ) + + return resp, None + + def _get_default_spec(self): + return deepcopy( + { + "databaseType": None, + "name": None, + "dbParameterProfileId": None, + "timeMachineInfo": { + "name": None, + "slaId": None, + "schedule": {}, + "autoTuneLogDrive": True, + }, + "actionArguments": [], + "nodes": [], + "nodeCount": 1, + "clustered": False, + "autoTuneStagingDrive": True, + "tags": [], + } + ) + + def get_default_update_spec(self, override_spec=None): + spec = deepcopy( + { + "name": None, + "description": None, + "tags": [], + "resetTags": True, + "resetName": True, + "resetDescription": True, + } + ) + if override_spec: + for key in spec.keys(): + if override_spec.get(key): + spec[key] = deepcopy(override_spec[key]) + + return spec + + def get_default_delete_spec(self): + return deepcopy( + { + "delete": False, + "remove": False, + "deleteTimeMachine": False, + "deleteLogicalCluster": True, + } + ) + + def _build_spec_name(self, payload, name): + payload["name"] = name + return payload, None + + def _build_spec_desc(self, payload, desc): + payload["databaseDescription"] = desc + return payload, None + + def _build_spec_auto_tune_staging_drive(self, payload, value): + payload["autoTuneStagingDrive"] = value + return payload, None + + def _build_spec_db_params_profile(self, payload, db_params_profile): + uuid, err = get_profile_uuid( + self.module, "Database_Parameter", db_params_profile + ) + if err: + return None, err + + payload["dbParameterProfileId"] = uuid + return payload, None + + def _build_spec_db_vm(self, payload, db_vm): + if db_vm.get("use_registered_server"): + + uuid, err = get_db_server_uuid(self.module, db_vm["use_registered_server"]) + if err: + return None, err + + payload["createDbserver"] = False + payload["dbserverId"] = uuid + payload["nodes"] = [{"properties": [], "dbserverId": uuid}] + + else: + vm_config = db_vm["create_new_server"] + + # set compute profile + uuid, err = get_profile_uuid( + self.module, "Compute", vm_config["compute_profile"] + ) + if err: + return None, err + payload["computeProfileId"] = uuid + + # set software profile + uuid, err = get_profile_uuid( + self.module, "Software", vm_config["software_profile"] + ) + if err: + return None, err + + payload["softwareProfileId"] = uuid + if vm_config["software_profile"].get("version_id"): + payload["softwareProfileVersionId"] = vm_config["software_profile"][ + "version_id" + ] + else: + profiles = Profile(self.module) + software_profile = profiles.read(uuid) + payload["softwareProfileId"] = uuid + payload["softwareProfileVersionId"] = software_profile[ + "latestVersionId" + ] + + # set network prfile + uuid, err = get_profile_uuid( + self.module, "Network", vm_config["network_profile"] + ) + if err: + return None, err + + payload["nodes"] = [ + { + "properties": [], + "vmName": vm_config["name"], + "networkProfileId": uuid, + } + ] + payload["networkProfileId"] = uuid + + # set cluster config + uuid, err = get_cluster_uuid(self.module, vm_config["cluster"]) + if err: + return None, err + payload["nxClusterId"] = uuid + + # set other params + payload["sshPublicKey"] = vm_config["pub_ssh_key"] + payload["vmPassword"] = vm_config["password"] + payload["createDbserver"] = True + + return payload, None + + def _build_spec_time_machine(self, payload, time_machine): + + # set sla uuid + uuid, err = get_sla_uuid(self.module, time_machine["sla"]) + if err: + return None, err + + time_machine_spec = {} + time_machine_spec["slaId"] = uuid + + schedule = time_machine.get("schedule") + schedule_spec = {} + if schedule.get("daily"): + + time = schedule["daily"].split(":") + if len(time) != 3: + return None, "Daily snapshot schedule not in HH:MM:SS format." + + schedule_spec["snapshotTimeOfDay"] = { + "hours": int(time[0]), + "minutes": int(time[1]), + "seconds": int(time[2]), + } + + if schedule.get("weekly"): + schedule_spec["weeklySchedule"] = { + "enabled": True, + "dayOfWeek": schedule["weekly"], + } + + if schedule.get("monthly"): + schedule_spec["monthlySchedule"] = { + "enabled": True, + "dayOfMonth": schedule["monthly"], + } + + # set quaterly and yearly as they are dependent on monthly + if schedule.get("quaterly"): + schedule_spec["quartelySchedule"] = { + "enabled": True, + "startMonth": schedule["quaterly"], + "dayOfMonth": schedule.get("monthly"), + } + + if schedule.get("yearly"): + schedule_spec["yearlySchedule"] = { + "enabled": True, + "month": schedule["yearly"], + "dayOfMonth": schedule.get("monthly"), + } + + if schedule.get("log_catchup") or schedule.get("snapshots_per_day"): + schedule_spec["continuousSchedule"] = { + "enabled": True, + "logBackupInterval": schedule.get("log_catchup"), + "snapshotsPerDay": schedule.get("snapshots_per_day"), + } + + time_machine_spec["schedule"] = schedule_spec + time_machine_spec["name"] = time_machine["name"] + time_machine_spec["description"] = time_machine.get("desc", "") + time_machine_spec["autoTuneLogDrive"] = time_machine.get("auto_tune_log_drive") + payload["timeMachineInfo"] = time_machine_spec + return payload, None + + def _build_spec_postgres(self, payload, postgres): + action_arguments = [] + + # fields to their defaults maps + args = { + "listener_port": "", + "auto_tune_staging_drive": False, + "allocate_pg_hugepage": False, + "cluster_database": False, + "auth_method": "", + "db_password": "", + "pre_create_script": "", + "post_create_script": "", + } + + # create action arguments + for key, value in args.items(): + spec = {"name": key, "value": postgres.get(key, value)} + action_arguments.append(spec) + + # handle scenariors where display names are diff + action_arguments.append( + {"name": "database_names", "value": postgres.get("db_name")} + ) + action_arguments.append( + {"name": "database_size", "value": str(postgres.get("db_size"))} + ) + + payload["actionArguments"] = action_arguments + payload["databaseType"] = NDB_CONSTANTS.DatabaseTypes.POSTGRES + return payload, None + + def _build_spec_tags(self, payload, tags): + _tags = Tag(self.module) + name_uuid_map = _tags.get_all_name_uuid_map() + specs = [] + for name, val in tags.items(): + if name not in name_uuid_map: + return None, "Tag with name {0} not found".format(name) + spec = {"tagId": name_uuid_map[name], "tagName": name, "value": val} + specs.append(spec) + payload["tags"] = specs + return payload, None diff --git a/plugins/module_utils/ndb/db_servers.py b/plugins/module_utils/ndb/db_servers.py new file mode 100644 index 000000000..e5e7d0435 --- /dev/null +++ b/plugins/module_utils/ndb/db_servers.py @@ -0,0 +1,71 @@ +# This file is part of Ansible +# 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 + + +from .nutanix_database import NutanixDatabase + + +class DBServers(NutanixDatabase): + def __init__(self, module): + resource_type = "/dbservers" + super(DBServers, self).__init__(module, resource_type=resource_type) + + def get_uuid( + self, + value, + key="name", + data=None, + entity_type=None, + raise_error=True, + no_response=False, + ): + query = {"value-type": key, "value": value} + resp = self.read(query=query) + + if not resp: + return None, "DB server vm with name {0} not found.".format(value) + + uuid = resp[0].get("id") + return uuid, None + + def get_db_server(self, name=None, uuid=None, ip=None): + resp = None + if uuid: + resp = self.read(uuid=uuid) + elif name or ip: + key = "name" if name else "ip" + val = name if name else ip + query = {"value-type": key, "value": val} + resp = self.read(query=query) + if not resp: + return None, "Database server with {0} {1} not found".format(key, val) + resp = resp[0] + else: + return ( + None, + "Please provide uuid, name or server IP for fetching database server details", + ) + + return resp, None + + +# Helper functions + + +def get_db_server_uuid(module, config): + if "name" in config: + db_servers = DBServers(module) + name = config["name"] + uuid, err = db_servers.get_uuid(name) + if err: + return None, err + elif "uuid" in config: + uuid = config["uuid"] + else: + error = "Config {0} doesn't have name or uuid key".format(config) + return None, error + + return uuid, None diff --git a/plugins/module_utils/ndb/nutanix_database.py b/plugins/module_utils/ndb/nutanix_database.py new file mode 100644 index 000000000..278f31bde --- /dev/null +++ b/plugins/module_utils/ndb/nutanix_database.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import, division, print_function + +from ..entity import Entity + +__metaclass__ = type + + +class NutanixDatabase(Entity): + __BASEURL__ = "/era" + api_version = "/v0.9" + + def __init__(self, module, resource_type, additional_headers=None): + resource_type = self.__BASEURL__ + self.api_version + resource_type + super(NutanixDatabase, self).__init__( + module, + resource_type, + additional_headers=additional_headers, + ) diff --git a/plugins/module_utils/ndb/operations.py b/plugins/module_utils/ndb/operations.py new file mode 100644 index 000000000..0423dd2e7 --- /dev/null +++ b/plugins/module_utils/ndb/operations.py @@ -0,0 +1,42 @@ +# This file is part of Ansible +# 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 + +import time + +__metaclass__ = type + + +from ..constants import NDB +from .nutanix_database import NutanixDatabase + + +class Operation(NutanixDatabase): + def __init__(self, module): + resource_type = "/operations" + super(Operation, self).__init__(module, resource_type=resource_type) + + def wait_for_completion(self, uuid, raise_error=True): + delay = NDB.OPERATIONS_POLLING_DELAY + timeout = time.time() + self.module.params["timeout"] + resp = None + while True: + resp = self.read(uuid) + status = resp.get("status") + if status == NDB.StatusCodes.SUCCESS: + return resp + elif status == NDB.StatusCodes.FAILURE: + if not raise_error: + break + self.module.fail_json( + msg=resp["message"], + response=resp, + ) + else: + if time.time() > timeout: + self.module.fail_json( + msg="Failed to poll on provision database instance operations. Reason: Timeout", + response=resp, + ) + time.sleep(delay) + return resp diff --git a/plugins/module_utils/ndb/profiles.py b/plugins/module_utils/ndb/profiles.py new file mode 100644 index 000000000..0e2f29d8f --- /dev/null +++ b/plugins/module_utils/ndb/profiles.py @@ -0,0 +1,94 @@ +# This file is part of Ansible +# 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 + + +from .nutanix_database import NutanixDatabase + + +class Profile(NutanixDatabase): + types = ["Database_Parameter", "Compute", "Network", "Software"] + + def __init__(self, module): + resource_type = "/profiles" + super(Profile, self).__init__(module, resource_type=resource_type) + + def get_profile_uuid(self, type, name): + if type not in self.types: + return None, "{0} is not a valid type. Allowed types are {1}".format( + type, self.types + ) + query = {"type": type, "name": name} + resp = self.read(query=query) + uuid = resp.get("id") + return uuid + + def read( + self, + uuid=None, + endpoint=None, + query=None, + raise_error=True, + no_response=False, + timeout=30, + ): + if uuid: + if not query: + query = {} + query["id"] = uuid + return super().read( + uuid=None, + endpoint=endpoint, + query=query, + raise_error=raise_error, + no_response=no_response, + timeout=timeout, + ) + + def get_profile_by_version(self, uuid, version_id="latest"): + endpoint = "{0}/versions/{1}".format(uuid, version_id) + resp = self.read(endpoint=endpoint) + return resp + + def get_profiles(self, uuid=None, name=None, type=None): + if name or uuid: + query = {} + if name: + query["name"] = name + else: + query["id"] = uuid + + if type: + query["type"] = type + + resp = self.read(query=query) + elif type: + query = {"type": type} + resp = self.read(query=query) + if not resp: + return None, "Profiles with type {0} not found".format(type) + else: + return ( + None, + "Please provide uuid, name or profile type for fetching profile details", + ) + + return resp, None + + +# helper functions + + +def get_profile_uuid(module, type, config): + uuid = "" + if config.get("name"): + profiles = Profile(module) + uuid = profiles.get_profile_uuid(type, config["name"]) + elif config.get("uuid"): + uuid = config["uuid"] + else: + error = "Profile config {0} doesn't have name or uuid key".format(config) + return error, None + return uuid, None diff --git a/plugins/module_utils/ndb/slas.py b/plugins/module_utils/ndb/slas.py new file mode 100644 index 000000000..c02ceb164 --- /dev/null +++ b/plugins/module_utils/ndb/slas.py @@ -0,0 +1,54 @@ +# This file is part of Ansible +# 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 + + +from .nutanix_database import NutanixDatabase + + +class SLA(NutanixDatabase): + def __init__(self, module): + resource_type = "/slas" + super(SLA, self).__init__(module, resource_type=resource_type) + + def get_uuid( + self, + value, + key="name", + data=None, + entity_type=None, + raise_error=True, + no_response=False, + ): + endpoint = "{0}/{1}".format(key, value) + resp = self.read(uuid=None, endpoint=endpoint) + return resp.get("id") + + def get_sla(self, uuid=None, name=None): + if uuid: + resp = self.read(uuid=uuid) + elif name: + endpoint = "{0}/{1}".format("name", name) + resp = self.read(endpoint=endpoint) + + else: + return None, "Please provide either uuid or name for fetching sla details" + return resp, None + + +# helper functions + + +def get_sla_uuid(module, config): + uuid = "" + if config.get("name"): + slas = SLA(module) + uuid = slas.get_uuid(config["name"]) + elif config.get("uuid"): + uuid = config["uuid"] + else: + error = "sla config {0} doesn't have name or uuid key".format(config) + return error, None + return uuid, None diff --git a/plugins/module_utils/ndb/tags.py b/plugins/module_utils/ndb/tags.py new file mode 100644 index 000000000..295e41b3d --- /dev/null +++ b/plugins/module_utils/ndb/tags.py @@ -0,0 +1,23 @@ +# This file is part of Ansible +# 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 + + +from .nutanix_database import NutanixDatabase + + +class Tag(NutanixDatabase): + types = ["Database_Parameter"] + + def __init__(self, module): + resource_type = "/tags" + super(Tag, self).__init__(module, resource_type=resource_type) + + def get_all_name_uuid_map(self): + resp = self.read() + name_uuid_map = {} + for tag in resp: + name_uuid_map[tag["name"]] = tag["id"] + return name_uuid_map diff --git a/plugins/module_utils/ndb/time_machines.py b/plugins/module_utils/ndb/time_machines.py new file mode 100644 index 000000000..1ad0bc8ca --- /dev/null +++ b/plugins/module_utils/ndb/time_machines.py @@ -0,0 +1,43 @@ +# This file is part of Ansible +# 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 + + +from .nutanix_database import NutanixDatabase + + +class TimeMachine(NutanixDatabase): + def __init__(self, module): + resource_type = "/tms" + super(TimeMachine, self).__init__(module, resource_type=resource_type) + + def get_time_machine(self, uuid=None, name=None): + if uuid: + resp = self.read(uuid=uuid) + elif name: + endpoint = "{0}/{1}".format("name", name) + resp = self.read(endpoint=endpoint) + if isinstance(resp, list): + if not resp: + return None, "Time machine with name {0} not found".format(name) + else: + tm = None + for entity in resp: + if entity["name"] == name: + tm = entity + break + if not tm: + return None, "Time machine with name {0} not found".format(name) + resp = tm + + # fetch all details using uuid + if resp.get("id"): + resp = self.read(uuid=resp["id"]) + else: + return ( + None, + "Please provide either uuid or name for fetching time machine details", + ) + return resp, None diff --git a/plugins/modules/ntnx_ndb_clones_info.py b/plugins/modules/ntnx_ndb_clones_info.py new file mode 100644 index 000000000..d5aa4c83e --- /dev/null +++ b/plugins/modules/ntnx_ndb_clones_info.py @@ -0,0 +1,361 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Prem Karat +# 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 + +DOCUMENTATION = r""" +--- +module: ntnx_ndb_clones_info +short_description: info module for database clones +version_added: 1.8.0-beta.1 +description: 'Get clone info' +options: + name: + description: + - clone name + type: str + uuid: + description: + - clone id + type: str +extends_documentation_fragment: + - nutanix.ncp.ntnx_ndb_base_module +author: + - Prem Karat (@premkarat) + - Gevorg Khachatryan (@Gevorg-Khachatryan-97) + - Alaa Bishtawi (@alaa-bish) +""" +EXAMPLES = r""" +- name: List all era db clones + ntnx_ndb_clones_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + register: clones + +- name: get era clones using it's name + ntnx_ndb_clones_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + name: "test_clone" + register: result + +- name: List clones use id + ntnx_ndb_clones_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + uuid: "" + register: result + +""" +RETURN = r""" +response: + description: response for listing all clones + returned: always + type: list + sample: + [ + { + "accessLevel": null, + "category": "DB_GROUP_IMPLICIT", + "clone": true, + "clustered": false, + "databaseClusterType": null, + "databaseGroupStateInfo": null, + "databaseName": "root", + "databaseNodes": [ + { + "accessLevel": null, + "databaseId": "3bf23402-67f0-yd87-967d-3a80b1d336f9", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 17:10:46", + "dateModified": "2022-10-17 17:10:46", + "dbserver": null, + "dbserverId": "eafakzef-5e63-4e93-bfa5-bb79967e3f3c", + "description": "", + "id": "e45a4eb7-c32a-4895-a32d-8450fe0b6be3", + "info": { + "info": null, + "secureInfo": null + }, + "metadata": null, + "name": "PRAD_POSTGRESS_2022_Oct_17_22_35_49", + "ownerId": "eac70dbf-22fb-462b-9498-965796ca1f73", + "primary": false, + "properties": [], + "protectionDomain": null, + "protectionDomainId": "3e62f008-7739-47a5-b26c-fd6971a110d8", + "softwareInstallationId": "2ac645c8-988b-4cbf-9dc6-b37d19b4464f", + "status": "READY", + "tags": [] + } + ], + "databaseStatus": "UNKNOWN", + "databases": null, + "dateCreated": "2022-10-17 17:06:12", + "dateModified": "2022-10-19 11:07:11", + "dbserverLogicalClusterId": null, + "dbserverlogicalCluster": null, + "description": "", + "eraCreated": true, + "groupInfo": null, + "id": "3bfe2902-67f0-4d87-967d-3a80b1d361f9", + "info": null, + "internal": false, + "lcmConfig": null, + "linkedDatabases": [ + { + "databaseName": "postgres", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 17:10:00", + "dateModified": "2022-10-17 17:10:00", + "description": null, + "id": "255e2610-d78a-42f5-8878-80ca01048651", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "postgres", + "ownerId": "eac70dbf-22fb-462b-9218-949796ca1f73", + "parentDatabaseId": "3bfe2902-74f0-4d87-967d-3a80b1d336f9", + "parentLinkedDatabaseId": "7453a712-c770-456a-b217-0a1c74bf0a50", + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "root", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 17:10:00", + "dateModified": "2022-10-17 17:10:00", + "description": null, + "id": "b93c2c70-fc6b-4148-8871-9c2912cea041", + "info": { + "info": { + "created_by": "user" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "root", + "ownerId": "eac70dbf-22fb-462b-9148-949796ca1f73", + "parentDatabaseId": "3bfe2907-47f0-4d87-967d-3a80b1d336f9", + "parentLinkedDatabaseId": "4195a159-0186-4741-8076-bd815269753f", + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "template1", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 17:10:00", + "dateModified": "2022-10-17 17:10:00", + "description": null, + "id": "50b6fd43-c5ee-4e85-b1f3-241ce3b16377", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "template1", + "ownerId": "eac70dbf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "3bfe2902-78f0-4d87-967d-3a80b1d336f9", + "parentLinkedDatabaseId": "4115126e-2913-48a2-b7a6-0a94fbefe16b", + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "template0", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 17:10:00", + "dateModified": "2022-10-17 17:10:00", + "description": null, + "id": "eb3b9d14-91bb-4735-9479-2366a45b592b", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "template0", + "ownerId": "eac70dbf-22fb-462b-9298-949796ca1f73", + "parentDatabaseId": "3bfe2902-67f0-4d87-9682d-3a80b1d336f9", + "parentLinkedDatabaseId": "56e97fff-4ae2-12d9-9c89-3fe50b3903d3", + "snapshotId": null, + "status": "READY", + "timeZone": null + } + ], + "metadata": { + "baseSizeComputed": true, + "capabilityResetTime": null, + "createdDbservers": null, + "deregisterInfo": null, + "deregisteredWithDeleteTimeMachine": false, + "info": null, + "lastLogCatchUpForRestoreOperationId": null, + "lastRefreshTimestamp": "2022-10-17 13:17:48", + "lastRequestedRefreshTimestamp": null, + "logCatchUpForRestoreDispatched": false, + "originalDatabaseName": null, + "pitrBased": false, + "provisionOperationId": null, + "refreshBlockerInfo": null, + "registeredDbservers": null, + "sanitised": false, + "secureInfo": null, + "sourceSnapshotId": "7657037a-c39d-dsada-99cd-d90217b40a9d", + "stateBeforeRefresh": null, + "stateBeforeRestore": null, + "stateBeforeScaling": null, + "tmActivateOperationId": null + }, + "metric": null, + "name": "PRAD_POSTGRESS_2022_Oct_17_22_35_49", + "ownerId": "eac70dbf-22fb-462b-9498-9497dasca1f73", + "parentDatabaseId": null, + "parentSourceDatabaseId": null, + "parentTimeMachine": null, + "parentTimeMachineId": "b051e542-1b96-40ba-dsef-52e9feb77003", + "placeholder": false, + "properties": [ + { + "description": null, + "name": "version", + "ref_id": "3bfe2902-67f0-4d87-967d-3asdb1d336f9", + "secure": false, + "value": "10.4" + }, + { + "description": null, + "name": "vm_ip", + "ref_id": "3bfe2902-67f0-4d87-967d-3asdb1d336f9", + "secure": false, + "value": "000.000.000.000" + }, + { + "description": null, + "name": "postgres_software_home", + "ref_id": "3bfe2902-67f0-4d87-967d-3asdb1d336f9", + "secure": false, + "value": "%2Fusr%2Fpgsql-10.4" + }, + { + "description": null, + "name": "listener_port", + "ref_id": "3sde2902-67f0-4d87-967d-3a80b1d336f9", + "secure": false, + "value": "5432" + }, + { + "description": null, + "name": "db_parameter_profile_id", + "ref_id": "3sae2902-67f0-4d87-967d-3a80b1d336f9", + "secure": false, + "value": "a8auc1fb-8787-4442-8f38-eeb2417a8cbb" + }, + { + "description": null, + "name": "BASE_SIZE", + "ref_id": "3gfe2902-67f0-4d87-967d-3a80b1d336f9", + "secure": false, + "value": "{\"clusterStorage\": + {\"d7881b99-5a9d-4da7-8e7d-c938499214de\": + {\"41f39cb1-a5fd-4bc8-bb78-d3fe81a76539\": + {\"size\": 26416896, \"allocatedSize\": 0, \"usedSize\": 0, \"unit\": \"B\"}, + \"dcd08c14-20dd-4b27-893c-c65a4c2e1d3b\": + {\"size\": 23134208, \"allocatedSize\": 0, + \"usedSize\": 0, \"unit\": \"B\"}, \"7c695994-a1ff-459c-9174-c93f75b470a4\": + {\"size\": 9189376, \"allocatedSize\": 0, \"usedSize\": 0, \"unit\": \"B\"}, + \"c35e56bd-f1b8-4349-b9aa-a86273c473c3\": {\"size\": 5496832, \"allocatedSize\": 0, \"usedSize\": 0, \"unit\": \"B\"}}}}" + } + ], + "status": "READY", + "tags": [], + "timeMachine": null, + "timeMachineId": "f5gf5d90-835c-4d37-a922-3a936aaa21da", + "timeZone": "UTC", + "type": "postgres_database" + } + ] + +""" + +from ..module_utils.ndb.base_info_module import NdbBaseInfoModule # noqa: E402 +from ..module_utils.ndb.clones import Clone # noqa: E402 + + +def get_module_spec(): + + module_args = dict( + name=dict(type="str"), + uuid=dict(type="str"), + ) + + return module_args + + +def get_clone(module, result): + clone = Clone(module) + if module.params.get("name"): + name = module.params["name"] + resp, err = clone.get_clone(name=name) + else: + uuid = module.params["uuid"] + resp, err = clone.get_clone(uuid=uuid) + + if err: + result["error"] = err + module.fail_json(msg="Failed fetching clone info", **result) + + result["response"] = resp + + +def get_clones(module, result): + clone = Clone(module) + + resp = clone.read() + + result["response"] = resp + + +def run_module(): + module = NdbBaseInfoModule( + argument_spec=get_module_spec(), + supports_check_mode=False, + mutually_exclusive=[("name", "uuid")], + ) + result = {"changed": False, "error": None, "response": None} + if module.params.get("name") or module.params.get("uuid"): + get_clone(module, result) + else: + get_clones(module, result) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ntnx_ndb_clusters_info.py b/plugins/modules/ntnx_ndb_clusters_info.py new file mode 100644 index 000000000..cbde76e4b --- /dev/null +++ b/plugins/modules/ntnx_ndb_clusters_info.py @@ -0,0 +1,222 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Prem Karat +# 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 + +DOCUMENTATION = r""" +--- +module: ntnx_ndb_clusters_info +short_description: info module for ndb clusters info +version_added: 1.8.0-beta.1 +description: 'Get clusters info' +options: + name: + description: + - cluster name + type: str + uuid: + description: + - cluster id + type: str +extends_documentation_fragment: + - nutanix.ncp.ntnx_ndb_base_module +author: + - Prem Karat (@premkarat) + - Pradeepsingh Bhati (@bhati-pradeep) + - Alaa Bishtawi (@alaa-bish) +""" + +EXAMPLES = r""" +- name: List all era clusters + ntnx_ndb_clusters_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + register: clusters + +- name: get era clusters using it's name + ntnx_ndb_clusters_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + name: "test_cluster" + register: result + +- name: List clusters use id + ntnx_ndb_clusters_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + uuid: "" + register: result + +""" +RETURN = r""" +response: + description: listing all clusters + returned: always + type: list + sample: [ + { + "cloudInfo": null, + "cloudType": "NTNX", + "dateCreated": "2022-09-30 11:09:58.232685", + "dateModified": "2022-10-08 05:20:02.513367", + "description": "", + "entityCounts": null, + "fqdns": null, + "healthy": true, + "hypervisorType": "AHV", + "hypervisorVersion": "6.1", + "id": "d7844b99-5a9d-4da7-8e7d-c938499214de", + "ipAddresses": [ + "000.000.000.000" + ], + "managementServerInfo": null, + "name": "EraCluster", + "nxClusterUUID": "4705e777-bfc7-11e4-3507-ac1f6b60292f", + "ownerId": "eac70dbf-42fb-468b-9498-949796ca1f73", + "password": null, + "properties": [ + { + "description": null, + "name": "CLUSTER_ID", + "ref_id": null, + "secure": false, + "value": "0005e777-bf47-11e4-3507-ac1f6b60292f::3821212059792582959" + }, + { + "description": null, + "name": "CLUSTER_INCARNATION_ID", + "ref_id": null, + "secure": false, + "value": "1661876478172260" + }, + { + "description": null, + "name": "ERA_STORAGE_CONTAINER", + "ref_id": null, + "secure": false, + "value": "default-container-75707403530678" + }, + { + "description": null, + "name": "MODEL_NAME", + "ref_id": null, + "secure": false, + "value": "NX-1065-G5" + }, + { + "description": null, + "name": "ONDEMAND_REPLICATION_SUPPORTED", + "ref_id": null, + "secure": false, + "value": "true" + }, + { + "description": null, + "name": "PRISM_VM_LIST_PAGINATION_LIMIT", + "ref_id": null, + "secure": false, + "value": "500" + }, + { + "description": null, + "name": "PRISM_VM_LIST_PAGINATION_SIZE", + "ref_id": null, + "secure": false, + "value": "50" + }, + { + "description": null, + "name": "RESOURCE_CONFIG", + "ref_id": null, + "secure": false, + "value": "{\"storageThresholdPercentage\":95.0,\"memoryThresholdPercentage\":95.0}" + }, + { + "description": null, + "name": "TIMEZONE", + "ref_id": null, + "secure": false, + "value": "UTC" + } + ], + "referenceCount": 0, + "resourceConfig": { + "memoryThresholdPercentage": 95.0, + "storageThresholdPercentage": 95.0 + }, + "status": "UP", + "uniqueName": "ERACLUSTER", + "username": null, + "version": "v2" + } + ] + +""" + +from ..module_utils.ndb.base_info_module import NdbBaseInfoModule # noqa: E402 +from ..module_utils.ndb.clusters import Cluster # noqa: E402 + + +def get_module_spec(): + + module_args = dict( + name=dict(type="str"), + uuid=dict(type="str"), + ) + + return module_args + + +def get_cluster(module, result): + cluster = Cluster(module) + if module.params.get("name"): + name = module.params["name"] + resp, err = cluster.get_cluster(name=name) + else: + uuid = module.params["uuid"] + resp, err = cluster.get_cluster(uuid=uuid) + + if err: + result["error"] = err + module.fail_json(msg="Failed fetching cluster info", **result) + result["response"] = resp + + +def get_clusters(module, result): + cluster = Cluster(module) + + resp = cluster.read() + + result["response"] = resp + + +def run_module(): + module = NdbBaseInfoModule( + argument_spec=get_module_spec(), + supports_check_mode=False, + mutually_exclusive=[("name", "uuid")], + ) + result = {"changed": False, "error": None, "response": None} + if module.params.get("name") or module.params.get("uuid"): + get_cluster(module, result) + else: + get_clusters(module, result) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ntnx_ndb_databases.py b/plugins/modules/ntnx_ndb_databases.py new file mode 100644 index 000000000..e9aeee9b0 --- /dev/null +++ b/plugins/modules/ntnx_ndb_databases.py @@ -0,0 +1,920 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Prem Karat +# 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 + +DOCUMENTATION = r""" +--- +module: ntnx_ndb_databases +short_description: Module for create, update and delete of single instance database. Currently, postgres type database is officially supported. +version_added: 1.8.0-beta.1 +description: Module for create, update and delete of single instance database in Nutanix Database Service +options: + db_uuid: + description: + - uuid for update or delete of database instance + type: str + name: + description: + - name of database instance + - update allowed + type: str + desc: + description: + - description of database + - update allowed + type: str + db_params_profile: + description: + - DB parameters profile details + type: dict + suboptions: + name: + type: str + description: + - name of profile + - mutually_exclusive with C(uuid) + uuid: + type: str + description: + - uuid of profile + - mutually_exclusive with C(name) + db_vm: + description: + - DB server VM details + type: dict + suboptions: + create_new_server: + description: + - details for creating new db server vms + - mutually_exclusive with C(use_registered_server) + type: dict + suboptions: + name: + type: str + description: name of vm + required: true + pub_ssh_key: + type: str + description: public ssh key for access to vm + required: true + password: + type: str + description: set vm era driver user password + required: true + cluster: + description: + - era cluster details + type: dict + required: true + suboptions: + name: + type: str + description: + - name of cluster + - mutually_exclusive with C(uuid) + uuid: + type: str + description: + - uuid of cluster + - mutually_exclusive with C(name) + software_profile: + description: + - software profile details + type: dict + required: true + suboptions: + name: + type: str + description: + - name of profile + - mutually_exclusive with C(uuid) + uuid: + type: str + description: + - uuid of profile + - mutually_exclusive with C(name) + version_id: + type: str + description: + - version id of software profile + - by default latest version will be used + network_profile: + description: + - network profile details + type: dict + required: true + suboptions: + name: + type: str + description: + - name of profile + - mutually_exclusive with C(uuid) + uuid: + type: str + description: + - uuid of profile + - mutually_exclusive with C(name) + compute_profile: + description: + - compute profile details + type: dict + required: true + suboptions: + name: + type: str + description: + - name of profile + - mutually_exclusive with C(uuid) + uuid: + type: str + description: + - uuid of profile + - mutually_exclusive with C(name) + use_registered_server: + description: + - registered server details + - mutually_exclusive with C(create_new_server) + type: dict + suboptions: + name: + type: str + description: + - name of registered vm + - mutually_exclusive with C(uuid) + uuid: + type: str + description: + - uuid of registered vm + - mutually_exclusive with C(name) + time_machine: + description: + - time machine details + type: dict + suboptions: + name: + type: str + description: name of time machine + required: True + desc: + type: str + description: description of time machine + sla: + type: dict + description: sla details + required: True + suboptions: + name: + type: str + description: + - name of sla + - mutually_exclusive with C(uuid) + uuid: + type: str + description: + - uuid of sla + - mutually_exclusive with C(name) + schedule: + type: dict + description: schedule for taking snapshot + required: True + suboptions: + daily: + type: str + description: daily snapshot time in HH:MM:SS format + weekly: + type: str + description: weekly snapshot day. For Example, "WEDNESDAY" + monthly: + type: int + description: monthly snapshot day in a month + quaterly: + type: str + description: + - quaterly snapshot month + - day of month is set based on C(monthly) + - C(monthly) is required for setting C(quaterly) else it is ignored + - For Example, "JANUARY" + yearly: + type: str + description: + - yearly snapshot month + - day of month is set based on C(monthly) + - C(monthly) is required for setting C(yearly) else it is ignored + - For Example, "JANUARY" + log_catchup: + type: int + description: log catchup intervals in minutes + choices: + - 15 + - 30 + - 60 + - 90 + - 120 + snapshots_per_day: + type: int + description: num of snapshots per day + default: 1 + auto_tune_log_drive: + type: bool + default: true + description: enable/disable auto tuning of log drive + postgres: + type: dict + description: action arguments for postgres type database + suboptions: + listener_port: + type: str + description: listener port for db + required: true + db_name: + type: str + description: initial database name + required: true + db_password: + type: str + description: postgres database password + required: true + auto_tune_staging_drive: + type: bool + default: true + description: enable/disable autotuning of staging drive + allocate_pg_hugepage: + type: bool + default: false + description: enable/disable allocating HugePage in postgres + auth_method: + type: str + default: md5 + description: auth method + cluster_database: + type: bool + default: false + description: if clustered database + db_size: + type: int + description: database instance size + required: true + pre_create_script: + type: str + description: commands to run before database instance creation + required: false + post_create_script: + type: str + description: commands to run after database instance creation + required: false + tags: + type: dict + description: + - dict of tag name as key and tag value as value + - update allowed + auto_tune_staging_drive: + type: bool + description: + - enable/disable auto tuning of stage drive + - enabled by default + soft_delete: + type: bool + description: + - only unregister from era in delete process + - if not provided, database instance from db server VM will be deleted + delete_time_machine: + type: bool + description: delete time machine as well in delete process + timeout: + description: + - timeout for polling database operations in seconds + - default is 2100 secs i.e. 35 minutes + type: int + required: false + default: 2100 +extends_documentation_fragment: + - nutanix.ncp.ntnx_ndb_base_module + - nutanix.ncp.ntnx_operations +author: + - Prem Karat (@premkarat) + - Pradeepsingh Bhati (@bhati-pradeep) +""" + +EXAMPLES = r""" +- name: Create postgres database instance using with new vm + ntnx_ndb_databases: + name: "test" + + db_params_profile: + name: "TEST_PROFILE" + + db_vm: + create_new_server: + name: "test-vm" + password: "test-vm-password" + cluster: + name: "EraCluster" + software_profile: + name: "TEST_SOFTWARE_PROFILE" + network_profile: + name: "TEST_NETWORK_PROFILE" + compute_profile: + name: "TEST_COMPUTE_PROFILE" + pub_ssh_key: "" + + postgres: + listener_port: "5432" + db_name: ansible_test + db_password: "postgres-test-password" + db_size: 200 + + time_machine: + name: POSTGRES_SERVER_PRAD_TM_1 + sla: + name: "TEST_SLA" + schedule: + daily: "11:10:02" + weekly: WEDNESDAY + monthly: 4 + quaterly: JANUARY + yearly: FEBRUARY + log_catchup: 30 + snapshots_per_day: 2 + register: db +""" + +RETURN = r""" +response: + description: database creation response after provisioning + returned: always + type: dict + sample: { + "accessLevel": null, + "category": "DB_GROUP_IMPLICIT", + "clone": false, + "clustered": false, + "databaseClusterType": null, + "databaseGroupStateInfo": null, + "databaseName": "POSTGRES_DATABASE_ANSIBLE", + "databaseNodes": [ + { + "accessLevel": null, + "databaseId": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 18:49:25", + "dateModified": "2022-10-19 18:51:33", + "dbserver": null, + "dbserverId": "0bee18d7-1f7c-4a7b-8d52-cd7f22f3121a", + "description": "postgres_database POSTGRES_DATABASE_ANSIBLE on host 10.51.144.213", + "id": "7228a75f-86d9-4a5b-aa1a-cc52c1fcfce3", + "info": { + "info": {}, + "secureInfo": null + }, + "metadata": null, + "name": "POSTGRES_DATABASE_ANSIBLE", + "ownerId": "eac70dbf-22fb-462b-9498-949796ca1f73", + "primary": false, + "properties": [], + "protectionDomain": null, + "protectionDomainId": "d67b312c-6f3a-4322-a9f2-15ec0bdc9dc5", + "softwareInstallationId": "b48c4b34-a6a1-4040-b4df-0bd4ab9c9e2c", + "status": "READY", + "tags": [] + } + ], + "databaseStatus": "UNKNOWN", + "databases": null, + "dateCreated": "2022-10-19 18:26:55", + "dateModified": "2022-10-19 18:51:26", + "dbserverLogicalClusterId": null, + "dbserverlogicalCluster": null, + "description": null, + "eraCreated": true, + "groupInfo": null, + "id": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "info": { + "info": { + "bpg_configs": { + "bpg_db_param": { + "effective_cache_size": "3GB", + "maintenance_work_mem": "512MB", + "max_parallel_workers_per_gather": "2", + "max_worker_processes": "8", + "shared_buffers": "1024MB", + "work_mem": "32MB" + }, + "storage": { + "archive_storage": { + "size": 600.0 + }, + "data_disks": { + "count": 4.0 + }, + "log_disks": { + "count": 4.0, + "size": 100.0 + } + }, + "vm_properties": { + "dirty_background_ratio": 5.0, + "dirty_expire_centisecs": 500.0, + "dirty_ratio": 15.0, + "dirty_writeback_centisecs": 100.0, + "nr_hugepages": 118.0, + "overcommit_memory": 1.0, + "swappiness": 0.0 + } + } + }, + "secureInfo": {} + }, + "internal": false, + "lcmConfig": null, + "linkedDatabases": [ + { + "databaseName": "prad", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 18:48:37", + "dateModified": "2022-10-19 18:48:37", + "description": null, + "id": "6d4da687-a425-43f1-a9df-fa28a6b0af80", + "info": { + "info": { + "created_by": "user" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "prad", + "ownerId": "eac70dbf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "postgres", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 18:48:37", + "dateModified": "2022-10-19 18:48:37", + "description": null, + "id": "67314b51-326f-4fc8-a345-668933a18cbd", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "postgres", + "ownerId": "eac70dbf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "template0", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 18:48:37", + "dateModified": "2022-10-19 18:48:37", + "description": null, + "id": "ba4bf273-b5ab-4743-a222-dffa178220f2", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "template0", + "ownerId": "eac70dbf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "template1", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 18:48:37", + "dateModified": "2022-10-19 18:48:37", + "description": null, + "id": "704d8464-d8aa-47ff-8f79-347cfae90abd", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "template1", + "ownerId": "eac70dbf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + } + ], + "metadata": { + "baseSizeComputed": false, + "capabilityResetTime": null, + "createdDbservers": null, + "deregisterInfo": null, + "deregisteredWithDeleteTimeMachine": false, + "info": null, + "lastLogCatchUpForRestoreOperationId": null, + "lastRefreshTimestamp": null, + "lastRequestedRefreshTimestamp": null, + "logCatchUpForRestoreDispatched": false, + "originalDatabaseName": null, + "pitrBased": false, + "provisionOperationId": "d9b1924f-a768-4cd8-886b-7a69e61f5b89", + "refreshBlockerInfo": null, + "registeredDbservers": null, + "sanitised": false, + "secureInfo": null, + "sourceSnapshotId": null, + "stateBeforeRefresh": null, + "stateBeforeRestore": null, + "stateBeforeScaling": null, + "tmActivateOperationId": "40d6b3a3-4f57-4c17-9ba2-9279d2f247c2" + }, + "metric": null, + "name": "POSTGRES_DATABASE_ANSIBLE", + "ownerId": "eac70dbf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": null, + "parentSourceDatabaseId": null, + "parentTimeMachineId": null, + "placeholder": false, + "properties": [ + { + "description": null, + "name": "db_parameter_profile_id", + "ref_id": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "secure": false, + "value": "a80ac1fb-8787-4442-8f38-eeb2417a8cbb" + }, + { + "description": null, + "name": "auth", + "ref_id": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "secure": false, + "value": "md5" + }, + { + "description": null, + "name": "AUTO_EXTEND_DB_STAGE", + "ref_id": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "secure": false, + "value": "true" + }, + { + "description": null, + "name": "provisioning_spec", + "ref_id": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "secure": false, + "value": "" + }, + { + "description": null, + "name": "version", + "ref_id": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "secure": false, + "value": "10.4" + }, + { + "description": null, + "name": "vm_ip", + "ref_id": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "secure": false, + "value": "xx.xx.xx.xx" + }, + { + "description": null, + "name": "postgres_software_home", + "ref_id": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "secure": false, + "value": "%2Fusr%2Fpgsql-10.4" + }, + { + "description": null, + "name": "listener_port", + "ref_id": "e9374379-de51-4cc8-8d12-b1b6eb64d129", + "secure": false, + "value": "5432" + } + ], + "status": "READY", + "tags": [], + "timeMachine": null, + "timeMachineId": "be524e70-60ad-4a8c-a0ee-8d72f954d7e6", + "timeZone": "UTC", + "type": "postgres_database" + } +db_uuid: + description: created database UUID + returned: always + type: str + sample: "be524e70-60ad-4a8c-a0ee-8d72f954d7e6" +""" +import time # noqa: E402 +from copy import deepcopy # noqa: E402 + +from ..module_utils.ndb.base_module import NdbBaseModule # noqa: E402 +from ..module_utils.ndb.databases import Database # noqa: E402 +from ..module_utils.ndb.operations import Operation # noqa: E402 +from ..module_utils.utils import remove_param_with_none_value # noqa: E402 + + +def get_module_spec(): + default_db_arguments = dict( + db_size=dict(type="int", required=True), + pre_create_script=dict(type="str", required=False), + post_create_script=dict(type="str", required=False), + ) + mutually_exclusive = [("name", "uuid")] + entity_by_spec = dict(name=dict(type="str"), uuid=dict(type="str")) + software_profile = dict(version_id=dict(type="str")) + software_profile.update(deepcopy(entity_by_spec)) + + new_server = dict( + name=dict(type="str", required=True), + pub_ssh_key=dict(type="str", required=True, no_log=True), + password=dict(type="str", required=True, no_log=True), + cluster=dict( + type="dict", + options=entity_by_spec, + mutually_exclusive=mutually_exclusive, + required=True, + ), + software_profile=dict( + type="dict", + options=software_profile, + mutually_exclusive=mutually_exclusive, + required=True, + ), + network_profile=dict( + type="dict", + options=entity_by_spec, + mutually_exclusive=mutually_exclusive, + required=True, + ), + compute_profile=dict( + type="dict", + options=entity_by_spec, + mutually_exclusive=mutually_exclusive, + required=True, + ), + ) + + db_vm = dict( + create_new_server=dict(type="dict", options=new_server, required=False), + use_registered_server=dict( + type="dict", + options=entity_by_spec, + mutually_exclusive=mutually_exclusive, + required=False, + ), + ) + + sla = dict( + uuid=dict(type="str", required=False), + name=dict(type="str", required=False), + ) + + schedule = dict( + daily=dict(type="str", required=False), + weekly=dict(type="str", required=False), + monthly=dict(type="int", required=False), + quaterly=dict(type="str", required=False), + yearly=dict(type="str", required=False), + log_catchup=dict(type="int", choices=[15, 30, 60, 90, 120], required=False), + snapshots_per_day=dict(type="int", required=False, default=1), + ) + + time_machine = dict( + name=dict(type="str", required=True), + desc=dict(type="str", required=False), + sla=dict( + type="dict", + options=sla, + mutually_exclusive=mutually_exclusive, + required=True, + ), + schedule=dict(type="dict", options=schedule, required=True), + auto_tune_log_drive=dict(type="bool", required=False, default=True), + ) + + postgres = dict( + listener_port=dict(type="str", required=True), + db_name=dict(type="str", required=True), + db_password=dict(type="str", required=True, no_log=True), + auto_tune_staging_drive=dict(type="bool", default=True, required=False), + allocate_pg_hugepage=dict(type="bool", default=False, required=False), + auth_method=dict(type="str", default="md5", required=False), + cluster_database=dict(type="bool", default=False, required=False), + ) + postgres.update(deepcopy(default_db_arguments)) + + module_args = dict( + db_uuid=dict(type="str", required=False), + name=dict(type="str", required=False), + desc=dict(type="str", required=False), + db_params_profile=dict( + type="dict", + options=entity_by_spec, + mutually_exclusive=mutually_exclusive, + required=False, + ), + db_vm=dict( + type="dict", + options=db_vm, + mutually_exclusive=[("create_new_server", "use_registered_server")], + required=False, + ), + time_machine=dict(type="dict", options=time_machine, required=False), + postgres=dict(type="dict", options=postgres, required=False), + tags=dict(type="dict", required=False), + auto_tune_staging_drive=dict(type="bool", required=False), + soft_delete=dict(type="bool", required=False), + delete_time_machine=dict(type="bool", required=False), + ) + return module_args + + +def create_instance(module, result): + _databases = Database(module) + + name = module.params["name"] + uuid, err = _databases.get_uuid(name) + if uuid: + module.fail_json( + msg="Database instance with given name already exists", **result + ) + + spec, err = _databases.get_spec() + if err: + result["error"] = err + module.fail_json( + msg="Failed generating create database instance spec", **result + ) + + if module.check_mode: + result["response"] = spec + return + + resp = _databases.create(data=spec) + result["response"] = resp + result["db_uuid"] = resp["entityId"] + db_uuid = resp["entityId"] + + if module.params.get("wait"): + ops_uuid = resp["operationId"] + operations = Operation(module) + time.sleep(5) # to get operation ID functional + operations.wait_for_completion(ops_uuid) + resp = _databases.read(db_uuid) + result["response"] = resp + + result["changed"] = True + + +def check_for_idempotency(old_spec, update_spec): + if ( + old_spec["name"] != update_spec["name"] + or old_spec["description"] != update_spec["description"] + ): + return False + + if len(old_spec["tags"]) != len(update_spec["tags"]): + return False + + old_tag_values = {} + new_tag_values = {} + for i in range(len(old_spec["tags"])): + old_tag_values[old_spec["tags"][i]["tagName"]] = old_spec["tags"][i]["value"] + new_tag_values[update_spec["tags"][i]["tagName"]] = update_spec["tags"][i][ + "value" + ] + + if old_tag_values != new_tag_values: + return False + + return True + + +def update_instance(module, result): + _databases = Database(module) + + uuid = module.params.get("db_uuid") + if not uuid: + module.fail_json(msg="uuid is required field for update", **result) + + resp = _databases.read(uuid) + old_spec = _databases.get_default_update_spec(override_spec=resp) + + update_spec, err = _databases.get_spec(old_spec=old_spec) + + # due to field name changes + if update_spec.get("databaseDescription"): + update_spec["description"] = update_spec.pop("databaseDescription") + + if err: + result["error"] = err + module.fail_json( + msg="Failed generating update database instance spec", **result + ) + + if module.check_mode: + result["response"] = update_spec + return + + if check_for_idempotency(old_spec, update_spec): + result["skipped"] = True + module.exit_json(msg="Nothing to change.") + + resp = _databases.update(data=update_spec, uuid=uuid) + result["response"] = resp + result["db_uuid"] = uuid + result["changed"] = True + + +def delete_instance(module, result): + _databases = Database(module) + + uuid = module.params.get("db_uuid") + if not uuid: + module.fail_json(msg="uuid is required field for delete", **result) + + spec = _databases.get_default_delete_spec() + if module.params.get("soft_delete"): + spec["remove"] = True + spec["delete"] = False + else: + spec["delete"] = True + spec["remove"] = False + + if module.params.get("delete_time_machine"): + spec["deleteTimeMachine"] = True + + if module.check_mode: + result["response"] = spec + return + + resp = _databases.delete(uuid, data=spec) + + if module.params.get("wait"): + ops_uuid = resp["operationId"] + time.sleep(5) # to get operation ID functional + operations = Operation(module) + resp = operations.wait_for_completion(ops_uuid) + + result["response"] = resp + result["changed"] = True + + +def run_module(): + mutually_exclusive_list = [ + ("db_uuid", "db_params_profile"), + ("db_uuid", "db_vm"), + ("db_uuid", "postgres"), + ("db_uuid", "time_machine"), + ("db_uuid", "auto_tune_staging_drive"), + ] + module = NdbBaseModule( + argument_spec=get_module_spec(), + mutually_exclusive=mutually_exclusive_list, + required_if=[ + ("state", "present", ("name", "db_uuid"), True), + ("state", "absent", ("db_uuid",)), + ], + supports_check_mode=True, + ) + remove_param_with_none_value(module.params) + result = {"changed": False, "error": None, "response": None, "db_uuid": None} + if module.params["state"] == "present": + if module.params.get("db_uuid"): + update_instance(module, result) + else: + create_instance(module, result) + else: + delete_instance(module, result) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ntnx_ndb_databases_info.py b/plugins/modules/ntnx_ndb_databases_info.py new file mode 100644 index 000000000..22ce95253 --- /dev/null +++ b/plugins/modules/ntnx_ndb_databases_info.py @@ -0,0 +1,718 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Prem Karat +# 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 + +DOCUMENTATION = r""" +--- +module: ntnx_ndb_databases_info +short_description: info module for ndb database instances +version_added: 1.8.0-beta.1 +description: 'Get database instance info' +options: + name: + description: + - database name + type: str + uuid: + description: + - database id + type: str +extends_documentation_fragment: + - nutanix.ncp.ntnx_ndb_base_module +author: + - Prem Karat (@premkarat) + - Gevorg Khachatryan (@Gevorg-Khachatryan-97) + - Alaa Bishtawi (@alaa-bish) +""" +EXAMPLES = r""" + + +- name: List era databases + ntnx_ndb_databases_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + register: databases + +- name: Get era databases using its name + ntnx_ndb_databases_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + name: "test_name" + register: result + +- name: Get era databases using its uuid + ntnx_ndb_databases_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + uuid: "" + register: result + +""" +RETURN = r""" +response: + description: response for listing all databases + returned: always + type: list + sample: [ + { + "accessLevel": null, + "category": "DB_GROUP_IMPLICIT", + "clone": false, + "clustered": false, + "databaseClusterType": null, + "databaseGroupStateInfo": null, + "databaseName": "PRAD_POSTGRESS", + "databaseNodes": [ + { + "accessLevel": null, + "databaseId": "e4a3cdaf-d643-43c5-8e11-83cbdabf16fa", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 13:13:36", + "dateModified": "2022-10-17 13:14:57", + "dbserver": null, + "dbserverId": "70d53dsaf-8022-44ba-9996-d0b6b351e8d5", + "description": "postgres_database PRAD_POSTGRESS on host 10.51.144.207", + "id": "fc6f3159-8a45-sadc-98a3-c503d62e2d71", + "info": { + "info": {}, + "secureInfo": null + }, + "metadata": null, + "name": "PRAD_POSTGRESS", + "ownerId": "eac70asa-22fb-462b-9498-949796ca1f73", + "primary": false, + "properties": [], + "protectionDomain": null, + "protectionDomainId": "dfdbbcac-ce90-46e6-fgdg-19ba74f1f569", + "softwareInstallationId": "0819dd55-4grf0-4a95-9ece-306edsfc404b4a", + "status": "READY", + "tags": [] + } + ], + "databaseStatus": "UNKNOWN", + "databases": null, + "dateCreated": "2022-10-17 12:50:50", + "dateModified": "2022-10-19 11:50:04", + "dbserverLogicalClusterId": null, + "dbserverlogicalCluster": null, + "description": "new description", + "eraCreated": true, + "groupInfo": null, + "id": "e4a3c27f-d643-43c5-8e11-83sdfabf16fa", + "info": { + "info": { + "bpg_configs": { + "bpg_db_param": { + "effective_cache_size": "3GB", + "maintenance_work_mem": "512MB", + "max_parallel_workers_per_gather": "2", + "max_worker_processes": "8", + "shared_buffers": "1024MB", + "work_mem": "32MB" + }, + "storage": { + "archive_storage": { + "size": 600.0 + }, + "data_disks": { + "count": 4.0 + }, + "log_disks": { + "count": 4.0, + "size": 100.0 + } + }, + "vm_properties": { + "dirty_background_ratio": 5.0, + "dirty_expire_centisecs": 500.0, + "dirty_ratio": 15.0, + "dirty_writeback_centisecs": 100.0, + "nr_hugepages": 118.0, + "overcommit_memory": 1.0, + "swappiness": 0.0 + } + } + }, + "secureInfo": {} + }, + "internal": false, + "lcmConfig": null, + "linkedDatabases": [ + { + "databaseName": "template0", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 13:12:52", + "dateModified": "2022-10-17 13:12:52", + "description": null, + "id": "56e97fff-4ae2-45d9-sfds9-3fe50b3903d3", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "template0", + "ownerId": "eac70dbf-22fb-46sfd-9498-94sfd96ca1f73", + "parentDatabaseId": "e4a3c27f-d643-4fd5-8e11-83cbdsdf16fa", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "postgres", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 13:12:52", + "dateModified": "2022-10-17 13:12:52", + "description": null, + "id": "3153a712-c770-456a-b217-0a1c7fsd0a50", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "postgres", + "ownerId": "eac70dbf-22fb-462b-9fsd8-94fsd6ca1f73", + "parentDatabaseId": "e4a3c27f-d643-43c5-8e11-8sfdabf16fa", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "template1", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 13:12:52", + "dateModified": "2022-10-17 13:12:52", + "description": null, + "id": "41sdfs26e-2913-48a2-b7a6-0a94fbesf16b", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "template1", + "ownerId": "easdfdbf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "e4asds7f-d643-43c5-8e11-83cbdabf16fa", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "root", + "databaseStatus": "READY", + "dateCreated": "2022-10-17 13:12:52", + "dateModified": "2022-10-17 13:12:52", + "description": null, + "id": "3d9sfd59-0186-4741-8076-bdsdf269753f", + "info": { + "info": { + "created_by": "user" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "root", + "ownerId": "esdfdbf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "e4asdff-d643-43c5-8e11-83cbdabf16fa", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + } + ], + "metadata": { + "baseSizeComputed": false, + "capabilityResetTime": null, + "createdDbservers": null, + "deregisterInfo": null, + "deregisteredWithDeleteTimeMachine": false, + "info": null, + "lastLogCatchUpForRestoreOperationId": null, + "lastRefreshTimestamp": null, + "lastRequestedRefreshTimestamp": null, + "logCatchUpForRestoreDispatched": false, + "originalDatabaseName": null, + "pitrBased": false, + "provisionOperationId": "ff5fsdf-6f95-4b58-abf3-e4fbcd6b27fc", + "refreshBlockerInfo": null, + "registeredDbservers": null, + "sanitised": false, + "secureInfo": null, + "sourceSnapshotId": null, + "stateBeforeRefresh": null, + "stateBeforeRestore": null, + "stateBeforeScaling": null, + "tmActivateOperationId": null + }, + "metric": null, + "name": "PRAD_POSTGRESS", + "ownerId": "eaasddf-22fb-462b-9498-94979dsa1f73", + "parentDatabaseId": null, + "parentSourceDatabaseId": null, + "parentTimeMachineId": null, + "placeholder": false, + "properties": [ + { + "description": null, + "name": "vm_ip", + "ref_id": "e4a3asdf-d643-43c5-8e11-83adsabf16fa", + "secure": false, + "value": "000.000.000.000" + }, + { + "description": null, + "name": "USAGE_SIZE", + "ref_id": "e4aasddf-d643-43c5-8e11-83cadsbf16fa", + "secure": false, + "value": "0" + }, + { + "description": null, + "name": "db_parameter_profile_id", + "ref_id": "e4a3ads-d643-43c5-8e11-83cbdaadsafa", + "secure": false, + "value": "a80aasda-8787-4442-8f38-eeb2adsd8cbb" + }, + { + "description": null, + "name": "auth", + "ref_id": "easda27f-d643-43c5-8e11-83adsabf16fa", + "secure": false, + "value": "md5" + }, + { + "description": null, + "name": "version", + "ref_id": "e4aasdaf-d643-43c5-8e11-83asdbf16fa", + "secure": false, + "value": "10.4" + }, + { + "description": null, + "name": "provisioning_spec", + "ref_id": "e4das7f-d643-43c5-8e11-83cbasd6fa", + "secure": false, + "value": "" + }, + { + "description": null, + "name": "postgres_software_home", + "ref_id": "easda27f-d643-43c5-8e11-83cbdabf16fa", + "secure": false, + "value": "%2Fasdr%2Fpgsql-10.4" + }, + { + "description": null, + "name": "listener_port", + "ref_id": "edasdc27f-d643-43c5-8e11-83cbdabf16fa", + "secure": false, + "value": "5432" + }, + { + "description": null, + "name": "SIZE", + "ref_id": "edas27f-d643-43c5-8e11-83cbdabf16fa", + "secure": false, + "value": "295" + }, + { + "description": null, + "name": "SIZE_UNIT", + "ref_id": "easda27f-d643-43c5-8e11-83cbdabf16fa", + "secure": false, + "value": "GB" + }, + { + "description": null, + "name": "AUTO_EXTEND_DB_STAGE", + "ref_id": "edasdc27f-d643-43c5-8e11-83cbdabf16fa", + "secure": false, + "value": "true" + }, + { + "description": null, + "name": "last_backed_up_txn_log", + "ref_id": "edasd3c27f-d643-43c5-8e11-83cbdabf16fa", + "secure": false, + "value": "000000010000000000000001" + } + ], + "status": "READY", + "tags": [ + { + "entityId": "easda7f-d643-43c5-8e11-83cbdabf16fa", + "entityType": "DATABASE", + "tagId": "93asdbe-5b8c-4ba2-a98b-0086272e187d", + "tagName": "test", + "value": "check1" + } + ], + "timeMachine": null, + "timeMachineId": "basd42-1b96-40ba-89ef-52e9feb77003", + "timeZone": "UTC", + "type": "postgres_database" + }, + { + "accessLevel": null, + "category": "DB_GROUP_IMPLICIT", + "clone": false, + "clustered": false, + "databaseClusterType": null, + "databaseGroupStateInfo": null, + "databaseName": "ujbcfnhq", + "databaseNodes": [ + { + "accessLevel": null, + "databaseId": "2c8asd5-16c7-4cb7-9341-670bd36b684c", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 12:02:24", + "dateModified": "2022-10-19 12:04:30", + "dbserver": null, + "dbserverId": "3bdasdcb-f392-4873-9460-960308e043a9", + "description": "postgres_database ujbcfnhq on host 10.51.144.214", + "id": "cbdasd8-a4db-4f14-b757-32459df965aa", + "info": { + "info": {}, + "secureInfo": null + }, + "metadata": null, + "name": "ujbcfnhq", + "ownerId": "eadasdbf-22fb-462b-9498-949796ca1f73", + "primary": false, + "properties": [], + "protectionDomain": null, + "protectionDomainId": "c0asd9-0672-43d4-83df-8167176d4d2a", + "softwareInstallationId": "20asd662-8fdb-44b3-9bf4-9f58ff2ca51b", + "status": "READY", + "tags": [] + } + ], + "databaseStatus": "UNKNOWN", + "databases": null, + "dateCreated": "2022-10-19 11:39:20", + "dateModified": "2022-10-19 12:04:23", + "dbserverLogicalClusterId": null, + "dbserverlogicalCluster": null, + "description": "ansible-created", + "eraCreated": true, + "groupInfo": null, + "id": "2c8asd15-16c7-4cb7-9341-670bd36b684c", + "info": { + "info": { + "bpg_configs": { + "bpg_db_param": { + "effective_cache_size": "3GB", + "maintenance_work_mem": "512MB", + "max_parallel_workers_per_gather": "2", + "max_worker_processes": "8", + "shared_buffers": "1024MB", + "work_mem": "32MB" + }, + "storage": { + "archive_storage": { + "size": 600.0 + }, + "data_disks": { + "count": 4.0 + }, + "log_disks": { + "count": 4.0, + "size": 100.0 + } + }, + "vm_properties": { + "dirty_background_ratio": 5.0, + "dirty_expire_centisecs": 500.0, + "dirty_ratio": 15.0, + "dirty_writeback_centisecs": 100.0, + "nr_hugepages": 118.0, + "overcommit_memory": 1.0, + "swappiness": 0.0 + } + } + }, + "secureInfo": {} + }, + "internal": false, + "lcmConfig": null, + "linkedDatabases": [ + { + "databaseName": "postgres", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 12:01:36", + "dateModified": "2022-10-19 12:01:36", + "description": null, + "id": "94asdad0-172b-4723-bc81-49a15b9bbfc8", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "postgres", + "ownerId": "eaasdabf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "dasd6a15-16c7-4cb7-9341-670dasd684c", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "ansible_test", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 12:01:36", + "dateModified": "2022-10-19 12:01:36", + "description": null, + "id": "e1das7bb-8be3-49e0-a42b-8f8ce59c8d7c", + "info": { + "info": { + "created_by": "user" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "ansible_test", + "ownerId": "eadasddbf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "2casdaa15-16c7-4cb7-9341-670bd36b684c", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "template0", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 12:01:36", + "dateModified": "2022-10-19 12:01:36", + "description": null, + "id": "7f12dase-22b5-4b47-a6b2-d3729aa8d97a", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "template0", + "ownerId": "eac7dasf-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "2cdasda15-16c7-4cb7-9341-670bd36b684c", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + }, + { + "databaseName": "template1", + "databaseStatus": "READY", + "dateCreated": "2022-10-19 12:01:36", + "dateModified": "2022-10-19 12:01:36", + "description": null, + "id": "61e5das2-8018-4e85-9794-5fe61fc8e4de", + "info": { + "info": { + "created_by": "system" + }, + "secureInfo": null + }, + "metadata": null, + "metric": null, + "name": "template1", + "ownerId": "eac7asd-22fb-462b-9498-949796ca1f73", + "parentDatabaseId": "2c836a15-16c7-4cb7-9341-670dasd6b684c", + "parentLinkedDatabaseId": null, + "snapshotId": null, + "status": "READY", + "timeZone": null + } + ], + "metadata": { + "baseSizeComputed": false, + "capabilityResetTime": null, + "createdDbservers": null, + "deregisterInfo": null, + "deregisteredWithDeleteTimeMachine": false, + "info": null, + "lastLogCatchUpForRestoreOperationId": null, + "lastRefreshTimestamp": null, + "lastRequestedRefreshTimestamp": null, + "logCatchUpForRestoreDispatched": false, + "originalDatabaseName": null, + "pitrBased": false, + "provisionOperationId": "63751472-ca25-4d94-8f65-54dasde80b59", + "refreshBlockerInfo": null, + "registeredDbservers": null, + "sanitised": false, + "secureInfo": null, + "sourceSnapshotId": null, + "stateBeforeRefresh": null, + "stateBeforeRestore": null, + "stateBeforeScaling": null, + "tmActivateOperationId": "66339401-2eda-4e28-abff-basdcb9f9031" + }, + "metric": null, + "name": "ujbcfnhq", + "ownerId": "eac70dbf-22fb-462b-9498-94asd6ca1f73", + "parentDatabaseId": null, + "parentSourceDatabaseId": null, + "parentTimeMachineId": null, + "placeholder": false, + "properties": [ + { + "description": null, + "name": "version", + "ref_id": "2c836a15-16c7-4cb7-9341-6dasd36b684c", + "secure": false, + "value": "10.4" + }, + { + "description": null, + "name": "vm_ip", + "ref_id": "2c836a15-16c7-4cb7-9341-6dasd36b684c", + "secure": false, + "value": "xx.xx.xx.xx" + }, + { + "description": null, + "name": "postgres_software_home", + "ref_id": "2c836a15-16c7-4cb7-9341-6asd36b684c", + "secure": false, + "value": "%2Fusr%2Fpgsql-10.4" + }, + { + "description": null, + "name": "db_parameter_profile_id", + "ref_id": "2c836a15-16c7-4cb7-9341-670dasb684c", + "secure": false, + "value": "a80ac1fb-8787-4442-8f38-eebdas7a8cbb" + }, + { + "description": null, + "name": "auth", + "ref_id": "2c836a15-16c7-4cb7-9341-67dasb684c", + "secure": false, + "value": "md5" + }, + { + "description": null, + "name": "AUTO_EXTEND_DB_STAGE", + "ref_id": "2c83das15-16c7-4cb7-9341-670bd36b684c", + "secure": false, + "value": "true" + }, + { + "description": null, + "name": "provisioning_spec", + "ref_id": "2sada15-16c7-4cb7-9341-6dasd36b684c", + "secure": false, + "value": "" + }, + { + "description": null, + "name": "listener_port", + "ref_id": "2c836a15-16c7-4cb7-9341-670dasdf6b684c", + "secure": false, + "value": "5432" + } + ], + "status": "READY", + "tags": [ + { + "entityId": "2c83wea15-16c7-4cb7-9341-6asd36b684c", + "entityType": "DATABASE", + "tagId": "a5312c47-b4aa-4133-88cd-079387423g279", + "tagName": "test1", + "value": "check1" + } + ], + "timeMachine": null, + "timeMachineId": "a71as3e0-5dc5-4336-8484-8214ea43a440", + "timeZone": "UTC", + "type": "postgres_database" + } + ] + +""" + +from ..module_utils.ndb.base_info_module import NdbBaseInfoModule # noqa: E402 +from ..module_utils.ndb.databases import Database # noqa: E402 + + +def get_module_spec(): + + module_args = dict( + name=dict(type="str"), + uuid=dict(type="str"), + ) + + return module_args + + +def get_database(module, result): + database = Database(module) + if module.params.get("name"): + name = module.params["name"] + resp, err = database.get_database(name=name) + else: + uuid = module.params["uuid"] + resp, err = database.get_database(uuid=uuid) + + if err: + result["error"] = err + module.fail_json(msg="Failed fetching database info", **result) + result["response"] = resp + + +def get_databases(module, result): + database = Database(module) + + resp = database.read() + + result["response"] = resp + + +def run_module(): + module = NdbBaseInfoModule( + argument_spec=get_module_spec(), + supports_check_mode=False, + mutually_exclusive=[("name", "uuid")], + ) + result = {"changed": False, "error": None, "response": None} + if module.params.get("name") or module.params.get("uuid"): + get_database(module, result) + else: + get_databases(module, result) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ntnx_ndb_db_servers_info.py b/plugins/modules/ntnx_ndb_db_servers_info.py new file mode 100644 index 000000000..753d8bd54 --- /dev/null +++ b/plugins/modules/ntnx_ndb_db_servers_info.py @@ -0,0 +1,403 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Prem Karat +# 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 + +DOCUMENTATION = r""" +--- +module: ntnx_ndb_db_servers_info +short_description: info module for ndb db server vms info +version_added: 1.8.0-beta.1 +description: 'Get database server info' +options: + name: + description: + - server name + type: str + uuid: + description: + - server id + type: str + server_ip: + description: + - db server vm ip + type: str +extends_documentation_fragment: + - nutanix.ncp.ntnx_ndb_base_module +author: + - Prem Karat (@premkarat) + - Gevorg Khachatryan (@Gevorg-Khachatryan-97) + - Alaa Bishtawi (@alaa-bish) +""" +EXAMPLES = r""" +- name: List era db_servers + ntnx_ndb_db_servers_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + register: db_servers + +- name: get era db_servers using it's name + ntnx_ndb_db_servers_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + name: "test_name" + register: result + +- name: get era db_servers using it's id + ntnx_ndb_db_servers_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + uuid: "" + register: result + +- name: get era db_servers using ip + ntnx_ndb_db_servers_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + server_ip: "" + register: result +""" +RETURN = r""" +response: + description: listing all db servers + returned: always + type: list + sample: [ + { + "accessKey": null, + "accessKeyId": "883dasd5bb-8122-473e-91d5-434asd785426", + "accessLevel": null, + "associatedTimeMachineIds": null, + "associated_time_machine_id": null, + "clientId": "d7adas12e-8042-48e1-95c1-2362dasdd653", + "clones": null, + "clustered": false, + "databaseType": "postgres_database", + "databases": null, + "dateCreated": "2022-10-14 12:22:36", + "dateModified": "2022-10-19 12:44:10", + "dbserverClusterId": null, + "dbserverInValidEaState": true, + "description": "DBServer for pnkwlheh", + "eraCreated": true, + "eraDrive": null, + "eraDriveId": "c225dsafcb-8f16-4d86-8eac-56ad85c1789f", + "eraVersion": "2.4.1", + "fqdns": null, + "id": "eaf72bef-das63-4e93-bfa5-bb799das3f3c", + "info": null, + "internal": false, + "ipAddresses": [ + "000.000.000.000" + ], + "is_server_driven": false, + "lcmConfig": null, + "macAddresses": [ + "xx:xx:xx:xx:xx" + ], + "metadata": { + "associatedTimeMachines": [ + "b05das42-1b96-40ba-89ef-52e9fdas003" + ], + "clustered": false, + "databaseType": "postgres_database", + "deregisterInfo": null, + "eraDriveInitialised": true, + "info": null, + "lastClockSyncAlertTime": null, + "leaderMetadata": null, + "markedForDeletion": false, + "physicalEraDrive": true, + "pinnedHostId": null, + "protectionDomainMigrationStatus": null, + "provisionOperationId": "8d9das7b-eeb1-48df-b277-be27927e19e3", + "secureInfo": null, + "singleInstance": true, + "softwareSnaphotInterval": 0 + }, + "metric": null, + "name": "wuuoyqzj", + "nxClusterId": "d78das99-5a9d-4da7-8e7d-c9dsa499214de", + "ownerId": "eac70dbf-22fb-462b-9498-94979dsaa1f73", + "placeholder": false, + "properties": [ + { + "description": null, + "name": "software_profile_version_id", + "ref_id": "eafsadef-5e63-4e93-bfa5-bb79dsa3f3c", + "secure": false, + "value": "1c34dase-0f44-43d6-b886-af10fdsa9783" + }, + { + "description": null, + "name": "vm_core_count", + "ref_id": "eafdsaf-5e63-4e93-bfa5-bdsa67e3f3c", + "secure": false, + "value": "1" + }, + { + "description": null, + "name": "compute_profile_id", + "ref_id": "eadasbef-5e63-4e93-bfa5-bbdas7e3f3c", + "secure": false, + "value": "75a0dsab-d67b-4474-86e6-af6dasaeb71" + }, + { + "description": null, + "name": "network_profile_id", + "ref_id": "eadasef-5e63-4e93-bfa5-bb79dase3f3c", + "secure": false, + "value": "6dsac92-450f-4b7b-aa62-eebdase2d91d" + }, + { + "description": null, + "name": "software_profile_id", + "ref_id": "eaf7ewf-5e63-4e93-bfa5-bda7e3f3c", + "secure": false, + "value": "b00bewa-41f1-4f01-8201-e0cddasd7dbc6" + }, + { + "description": null, + "name": "access_key_id", + "ref_id": "eadsad2bef-5e63-4e93-bfa5-bbczxc7e3f3c", + "secure": false, + "value": "883dasb-8122-473e-91d5-43vsdv6785426" + }, + { + "description": null, + "name": "era_user", + "ref_id": "eafdasef-5e63-4e93-bfa5-bbsa67e3f3c", + "secure": false, + "value": "era" + }, + { + "description": null, + "name": "era_base", + "ref_id": "eadasbef-5e63-4e93-bfa5-bb799sad3c", + "secure": false, + "value": "/opt/era_base" + }, + { + "description": null, + "name": "current_op_id", + "ref_id": "eafdaef-5e63-4e93-bfa5-bb7da7e3f3c", + "secure": false, + "value": "8d9dsab-eeb1-48df-b277-bedasd19e3" + }, + { + "description": null, + "name": "isEraCreated", + "ref_id": "edsaef-5e63-4e93-bfa5-bb7dasdf3c", + "secure": false, + "value": "true" + }, + { + "description": null, + "name": "software_home", + "ref_id": "das2bef-5e63-4e93-bfa5-bb7dsade3f3c", + "secure": false, + "value": "/usr/pgsql-10.4" + }, + { + "description": null, + "name": "vm_ip_address_list", + "ref_id": "edasdef-5e63-4e93-bfa5-bbdasd7e3f3c", + "secure": false, + "value": "000.000.000.000" + }, + { + "description": null, + "name": "working_dir", + "ref_id": "eaf7dasdf-5e63-4e93-bfa5-bb79asda3f3c", + "secure": false, + "value": "/tmp" + }, + { + "description": null, + "name": "os_type", + "ref_id": "eafdasdf-5e63-4e93-bfa5-bb79dasd3f3c", + "secure": false, + "value": "linux" + }, + { + "description": null, + "name": "application_type", + "ref_id": "eadsadef-5e63-4e93-bfa5-bbdasd7e3f3c", + "secure": false, + "value": "postgres_database" + }, + { + "description": null, + "name": "application_version", + "ref_id": "edadbef-5e63-4e93-bfa5-bdasd967e3f3c", + "secure": false, + "value": "10.4" + }, + { + "description": null, + "name": "os_info", + "ref_id": "eafdasf-5e63-4e93-bfa5-dasd9967e3f3c", + "secure": false, + "value": "Linux wuuoyqzj 5.10.0-1.el7.elrepo.x86_64 #1 SMP Sun Dec 13 18:34:48 EST 2020 x86_64 x86_64 x86_64 GNU/Linux\n" + }, + { + "description": null, + "name": "node_type", + "ref_id": "eadasef-5e63-4e93-bfa5-bdas67e3f3c", + "secure": false, + "value": "database" + }, + { + "description": null, + "name": "vm_cpu_count", + "ref_id": "eadasdbef-5e63-4e93-bfa5-bb799dsad3f3c", + "secure": false, + "value": "2" + }, + { + "description": null, + "name": "listener_port", + "ref_id": "eadasdef-5e63-4e93-bfa5-bb79dasde3f3c", + "secure": false, + "value": "5432" + } + ], + "protectionDomain": null, + "protectionDomainId": "7a16dasd-8596-4e19-9aed-b776adsa736a", + "queryCount": 0, + "requestedVersion": null, + "softwareInstallations": null, + "status": "UP", + "tags": [], + "time_machine_info": null, + "type": "DBSERVER", + "validDiagnosticBundleState": true, + "vmClusterName": "wuuosyqzj", + "vmClusterUuid": "417das8-c8e0-4718-b91f-cb40dasbd95", + "vmInfo": { + "deregisterInfo": null, + "distribution": null, + "info": null, + "networkInfo": [ + { + "accessInfo": [ + { + "accessType": "PRISM", + "destinationSubnet": "" + }, + { + "accessType": "DSIP", + "destinationSubnet": "000.000.000.000/23" + }, + { + "accessType": "ERA_SERVER", + "destinationSubnet": "000.000.000.000/24" + }, + { + "accessType": "PUBLIC", + "destinationSubnet": null + } + ], + "defaultGatewayDevice": true, + "deviceName": "eth0", + "eraConfigured": true, + "flags": "4163 ", + "gateway": "000.000.000.000", + "hostname": null, + "ipAddresses": [ + "000.000.000.000" + ], + "macAddress": "00000-0000-00000-0000000", + "mtu": "1500", + "subnetMask": "000.000.000.000", + "vlanName": "vlan.x", + "vlanType": "Static", + "vlanUuid": "00000-0000-00000-0000000" + } + ], + "osType": null, + "osVersion": null, + "secureInfo": null + }, + "vmTimeZone": "UTC", + "windowsDBServer": false, + "workingDirectory": "/tmp" + } + ] +""" + +from ..module_utils.ndb.base_info_module import NdbBaseInfoModule # noqa: E402 +from ..module_utils.ndb.db_servers import DBServers # noqa: E402 + + +def get_module_spec(): + + module_args = dict( + name=dict(type="str"), + uuid=dict(type="str"), + server_ip=dict(type="str"), + ) + + return module_args + + +def get_db_server(module, result): + db_server = DBServers(module) + if module.params.get("uuid"): + resp, err = db_server.get_db_server(uuid=module.params["uuid"]) + elif module.params.get("name"): + resp, err = db_server.get_db_server(name=module.params["name"]) + else: + resp, err = db_server.get_db_server(ip=module.params["server_ip"]) + + if err: + result["error"] = err + module.fail_json(msg="Failed fetching database server info", **result) + + result["response"] = resp + + +def get_db_servers(module, result): + db_server = DBServers(module) + + resp = db_server.read() + + result["response"] = resp + + +def run_module(): + module = NdbBaseInfoModule( + argument_spec=get_module_spec(), + supports_check_mode=False, + mutually_exclusive=[("name", "uuid", "server_ip")], + ) + result = {"changed": False, "error": None, "response": None} + if ( + module.params.get("name") + or module.params.get("uuid") + or module.params.get("server_ip") + ): + get_db_server(module, result) + else: + get_db_servers(module, result) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ntnx_ndb_profiles_info.py b/plugins/modules/ntnx_ndb_profiles_info.py new file mode 100644 index 000000000..0756d97fc --- /dev/null +++ b/plugins/modules/ntnx_ndb_profiles_info.py @@ -0,0 +1,253 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Prem Karat +# 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 + +DOCUMENTATION = r""" +--- +module: ntnx_ndb_profiles_info +short_description: info module for ndb profiles +version_added: 1.8.0-beta.1 +description: 'Get profile info' +options: + name: + description: + - profile name + type: str + uuid: + description: + - profile id + type: str + profile_type: + description: + - profile type + type: str + choices: ["Software", "Compute", "Network", "Database_Parameter"] + version_id: + description: + - vrsion id + type: str + latest_version: + description: + - whether the lastet version of profile or no + type: bool + default: false +extends_documentation_fragment: + - nutanix.ncp.ntnx_ndb_base_module +author: + - Prem Karat (@premkarat) + - Gevorg Khachatryan (@Gevorg-Khachatryan-97) + - Alaa Bishtawi (@alaa-bish) +""" +EXAMPLES = r""" +- name: List profiles + ntnx_ndb_profiles_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + register: profiles + +- name: List Database_Parameter profiles + ntnx_ndb_profiles_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + profile_type: Database_Parameter + register: result + +- name: List Network profiles + ntnx_ndb_profiles_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + profile_type: Network + register: result + +- name: List Compute profiles + ntnx_ndb_profiles_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + profile_type: Compute + register: result + +- name: List Software profiles + ntnx_ndb_profiles_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + profile_type: Software + register: result + +- name: get era profile using era profile name + ntnx_ndb_profiles_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + name: "test_name" + register: result + + +- name: List profiles + ntnx_ndb_profiles_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + uuid: "" + latest_version: true + register: result + +""" +RETURN = r""" +response: + description: list of db_profiles + returned: always + type: list + sample: [ + { + "dbVersion": "ALL", + "description": "Default Database Storage Profile", + "engineType": "Generic", + "id": "a1c3033d-f999-47b2-8565-feced1a33503", + "latestVersion": "1.0", + "latestVersionId": "a1cdasdd-f999-47b2-8565-feced1a33503", + "name": "DB_DEFAULT_STORAGE_PROFILE", + "owner": "eacdasbf-22fb-462b-9498-949796ca1f73", + "status": "READY", + "systemProfile": true, + "topology": "ALL", + "type": "Storage", + "versions": [ + { + "dbVersion": "ALL", + "deprecated": false, + "description": "Default Database Storage Profile", + "engineType": "Generic", + "id": "a1c3033d-f999-47b2-8565-feced1a33503", + "name": "DB_DEFAULT_STORAGE_PROFILE", + "owner": "eacdsaf-22fb-462b-9498-94979dsaf73", + "profileId": "a1c30dsa-f999-47b2-8565-fecedsa3503", + "properties": [ + { + "name": "DEFAULT_CONTAINER", + "secure": false, + "value": "" + }, + { + "name": "MAX_VDISK_SIZE", + "secure": false, + "value": "200" + } + ], + "propertiesMap": { + "DEFAULT_CONTAINER": "", + "MAX_VDISK_SIZE": "200" + }, + "published": true, + "status": "READY", + "systemProfile": false, + "topology": "ALL", + "type": "Storage", + "version": "1.0", + "versionClusterAssociation": [] + } + ] + } + ] +""" + +from ..module_utils.ndb.base_info_module import NdbBaseInfoModule # noqa: E402 +from ..module_utils.ndb.profiles import Profile # noqa: E402 + + +def get_module_spec(): + + module_args = dict( + name=dict(type="str"), + uuid=dict(type="str"), + profile_type=dict( + type="str", choices=["Software", "Compute", "Network", "Database_Parameter"] + ), + version_id=dict(type="str"), + latest_version=dict(type="bool", default=False), + ) + + return module_args + + +def get_profile(module, result): + profile = Profile(module) + name = module.params.get("name") + uuid = module.params.get("uuid") + type = module.params.get("profile_type") + resp, err = profile.get_profiles(uuid, name, type) + if err: + result["error"] = err + module.fail_json(msg="Failed fetching profile info", **result) + + result["response"] = resp + + +def get_profiles(module, result): + profile = Profile(module) + + resp = profile.read() + + result["response"] = resp + + +def get_profiles_version(module, result): + profile = Profile(module) + + version_id = "" + if module.params.get("latest_version"): + version_id = "latest" + else: + version_id = module.params.get("version_id") + resp = profile.get_profile_by_version(module.params["uuid"], version_id) + + result["response"] = resp + + +def run_module(): + module = NdbBaseInfoModule( + argument_spec=get_module_spec(), + supports_check_mode=False, + mutually_exclusive=[ + ("name", "uuid"), + ("version_id", "latest_version"), + ], + required_by={"version_id": "uuid"}, + required_if=[("latest_version", True, ("uuid",))], + ) + result = {"changed": False, "error": None, "response": None} + if module.params.get("version_id") or module.params.get("latest_version"): + get_profiles_version(module, result) + elif ( + module.params.get("name") + or module.params.get("uuid") + or module.params.get("profile_type") + ): + get_profile(module, result) + else: + get_profiles(module, result) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ntnx_ndb_slas_info.py b/plugins/modules/ntnx_ndb_slas_info.py new file mode 100644 index 000000000..35bbbb102 --- /dev/null +++ b/plugins/modules/ntnx_ndb_slas_info.py @@ -0,0 +1,160 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Prem Karat +# 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 + +DOCUMENTATION = r""" +--- +module: ntnx_ndb_slas_info +short_description: info module for ndb slas +version_added: 1.8.0-beta.1 +description: 'Get sla info' +options: + name: + description: + - sla name + type: str + uuid: + description: + - sla id + type: str +extends_documentation_fragment: + - nutanix.ncp.ntnx_ndb_base_module +author: + - Prem Karat (@premkarat) + - Gevorg Khachatryan (@Gevorg-Khachatryan-97) + - Alaa Bishtawi (@alaa-bish) +""" +EXAMPLES = r""" +- name: List all era slas + ntnx_ndb_slas_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + register: slas + +- name: get era slas using it's name + ntnx_ndb_slas_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + name: "test_name" + register: result + +- name: List slas use id + ntnx_ndb_slas_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + uuid: "" + register: result + +""" +RETURN = r""" +response: + description: listing all slas + returned: always + type: list + sample: [ + { + "continuousRetention": 30, + "currentActiveFrequency": "CONTINUOUS", + "dailyRetention": 90, + "dateCreated": "2022-04-08 16:21:51.591815", + "dateModified": "2022-04-08 16:21:51.591815", + "description": "Out of the box Gold SLA for Era Time Machines.", + "id": "50dsad9-db5e-47af-8102-ff9dsad9bd81", + "monthlyRetention": 12, + "name": "DEFAULT_OOB_GOLD_SLA", + "ownerId": "era-internal-user-id", + "pitrEnabled": true, + "quarterlyRetention": 35, + "referenceCount": 1, + "systemSla": true, + "uniqueName": "DEFAULT_OOB_GOLD_SLA", + "weeklyRetention": 16, + "yearlyRetention": 0 + }, + { + "continuousRetention": 14, + "currentActiveFrequency": "CONTINUOUS", + "dailyRetention": 60, + "dateCreated": "2022-04-08 16:21:51.591815", + "dateModified": "2022-04-08 16:21:51.591815", + "description": "Out of the box Silver SLA for Era Time Machines.", + "id": "27dasdd9-db5e-47af-8102-ff9354dsada0", + "monthlyRetention": 12, + "name": "DEFAULT_OOB_SILVER_SLA", + "ownerId": "era-internal-user-id", + "pitrEnabled": true, + "quarterlyRetention": 0, + "referenceCount": 0, + "systemSla": true, + "uniqueName": "DEFAULT_OOB_SILVER_SLA", + "weeklyRetention": 12, + "yearlyRetention": 0 + }, + ] +""" + +from ..module_utils.ndb.base_info_module import NdbBaseInfoModule # noqa: E402 +from ..module_utils.ndb.slas import SLA # noqa: E402 + + +def get_module_spec(): + + module_args = dict( + name=dict(type="str"), + uuid=dict(type="str"), + ) + + return module_args + + +def get_sla(module, result): + sla = SLA(module) + resp, err = sla.get_sla( + uuid=module.params.get("uuid"), name=module.params.get("name") + ) + + if err: + result["error"] = err + module.fail_json(msg="Failed fetching sla info", **result) + result["response"] = resp + + +def get_slas(module, result): + sla = SLA(module) + + resp = sla.read() + + result["response"] = resp + + +def run_module(): + module = NdbBaseInfoModule( + argument_spec=get_module_spec(), + supports_check_mode=False, + mutually_exclusive=[("name", "uuid")], + ) + result = {"changed": False, "error": None, "response": None} + if module.params.get("name") or module.params.get("uuid"): + get_sla(module, result) + else: + get_slas(module, result) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ntnx_ndb_time_machines_info.py b/plugins/modules/ntnx_ndb_time_machines_info.py new file mode 100644 index 000000000..3fcc9a15c --- /dev/null +++ b/plugins/modules/ntnx_ndb_time_machines_info.py @@ -0,0 +1,278 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Prem Karat +# 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 + +DOCUMENTATION = r""" +--- +module: ntnx_ndb_time_machines_info +short_description: info module for ndb time machines +version_added: 1.8.0-beta.1 +description: 'Get tm info' +options: + name: + description: + - time machine name + type: str + uuid: + description: + - time machine id + type: str +extends_documentation_fragment: + - nutanix.ncp.ntnx_ndb_base_module +author: + - Prem Karat (@premkarat) + - Gevorg Khachatryan (@Gevorg-Khachatryan-97) + - Alaa Bishtawi (@alaa-bish) +""" +EXAMPLES = r""" +- name: List all era time machines + ntnx_ndb_time_machines_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + register: tms + +- name: get era time machines using it's name + ntnx_ndb_time_machines_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + name: "test_name" + register: result + +- name: List time machines use id + ntnx_ndb_time_machines_info: + nutanix_host: "" + nutanix_username: "" + nutanix_password: "" + validate_certs: false + uuid: "" + register: result +""" +RETURN = r""" +response: + description: listing all time machines + returned: always + type: list + sample: + [ + { + "accessLevel": null, + "associatedClusters": null, + "category": "DB_GROUP_IMPLICIT", + "clone": false, + "clones": null, + "clustered": false, + "database": null, + "databaseId": "e4dsad7f-d643-43c5-8e11-83dasdabf16fa", + "dateCreated": "2022-10-17 12:50:50", + "dateModified": "2022-10-19 13:07:07", + "description": "Time Machine for instance 'PRAD_POSTGRESS'", + "eaStatus": "NOT_APPLICABLE", + "id": "b05dasd42-1b96-40ba-89ef-52e9das003", + "info": null, + "internal": false, + "metadata": { + "absoluteThresholdExhausted": false, + "authorizedDbservers": [ + "eafdsaef-5e63-4e93-bfa5-bb79dsad3f3c" + ], + "autoHeal": true, + "autoHealLogCatchupCount": 0, + "autoHealRetryCount": 0, + "autoHealSnapshotCount": 0, + "autoSnapshotRetryInfo": null, + "capabilityResetTime": null, + "databasesFirstSnapshotInfo": null, + "deregisterInfo": null, + "dispatchOnboardingSnapshot": false, + "firstSnapshotCaptured": true, + "firstSnapshotDispatched": true, + "firstSnapshotRetryCount": 0, + "implicitResumeCount": 0, + "info": null, + "lastAutoSnapshotOperationId": "16dasd7-bf83-46b6-9386-55adsad103", + "lastAutoSnapshotTime": "2022-10-18 14:02:27", + "lastEaBreakdownTime": null, + "lastHealSnapshotOperation": null, + "lastHealSystemTriggered": false, + "lastHealTime": null, + "lastHealthAlertedTime": null, + "lastImplicitResumeTime": null, + "lastLogCatchupOperationId": null, + "lastLogCatchupSkipped": false, + "lastLogCatchupTime": null, + "lastNonExtraAutoSnapshotTime": "2022-10-18 14:02:27", + "lastPauseByForce": false, + "lastPauseReason": null, + "lastPauseTime": null, + "lastResumeTime": null, + "lastSnapshotOperationId": "16edsad7-bf83-46b6-9386-55dasd8103", + "lastSnapshotTime": "2022-10-18 14:02:23", + "lastSuccessfulLogCatchupOperationId": null, + "lastSuccessfulLogCatchupPostHealWithResetCapability": null, + "lastSuccessfulSnapshotOperationId": "16edsad7-bf83-46b6-9386-55ab1dsad103", + "logCatchupSuccessiveFailureCount": 0, + "onboardingSnapshotProperties": null, + "requiredSpace": 0.0, + "secureInfo": null, + "snapshotCapturedForTheDay": false, + "snapshotSuccessiveFailureCount": 0, + "stateBeforeRestore": null, + "storageLimitExhausted": false + }, + "metric": null, + "name": "PRAD_POSTGRESS_TM_1", + "ownerId": "eac7dsaf-22fb-462b-9498-949796dsad73", + "properties": [ + { + "description": null, + "name": "CLONE_COUNT", + "ref_id": "b05dsa42-1b96-40ba-89ef-52e9fdsa7003", + "secure": false, + "value": "1" + } + ], + "schedule": { + "continuousSchedule": { + "enabled": true, + "logBackupInterval": 30, + "snapshotsPerDay": 1 + }, + "dailySchedule": null, + "dateCreated": "2022-10-17 12:50:50.016262", + "dateModified": "2022-10-17 12:50:50.016262", + "description": "Schedule for Time Machine PRAD_POSTGRESS_TM_1", + "globalPolicy": false, + "id": "87ddsad76-66ac-44a7-b645-57cedasd441e", + "monthlySchedule": { + "dayOfMonth": 17, + "enabled": true + }, + "name": "Schedule_PRAD_POSTGRESS_TM_1_2022-10-17 12:50:50", + "ownerId": "edasddbf-22fb-462b-9498-9497dsad1f73", + "quartelySchedule": { + "dayOfMonth": 17, + "enabled": true, + "startMonth": "JANUARY", + "startMonthValue": "JANUARY" + }, + "referenceCount": 1, + "snapshotTimeOfDay": { + "extra": false, + "hours": 14, + "minutes": 0, + "seconds": 0 + }, + "startTime": null, + "systemPolicy": false, + "timeZone": null, + "uniqueName": "SCHEDULE_PRAD_POSTGRESS_TM_1_2022-10-17 12:50:50", + "weeklySchedule": { + "dayOfWeek": "MONDAY", + "dayOfWeekValue": "MONDAY", + "enabled": true + }, + "yearlySchedule": { + "dayOfMonth": 31, + "enabled": false, + "month": "DECEMBER", + "monthValue": null + } + }, + "scheduleId": "87dfdsad6-66ac-44a7-b645-57cedsad41e", + "scope": "LOCAL", + "sla": { + "continuousRetention": 0, + "currentActiveFrequency": "DAILY", + "dailyRetention": 7, + "dateCreated": "2022-04-08 16:21:51.591815", + "dateModified": "2022-04-08 16:21:51.591815", + "description": "Out of the box Brass SLA for Era Time Machines. All retentions except daily retention are disabled.", + "id": "4d9ddsad6d-b6f8-47f0-8015-9e69dsadd3cf4", + "monthlyRetention": 0, + "name": "DEFAULT_OOB_BRASS_SLA", + "ownerId": "era-internal-user-id", + "pitrEnabled": false, + "quarterlyRetention": 0, + "referenceCount": 1, + "systemSla": true, + "uniqueName": "DEFAULT_OOB_BRASS_SLA", + "weeklyRetention": 0, + "yearlyRetention": 0 + }, + "slaId": "4d9dsadd-b6f8-47f0-8015-9e69dasdd3cf4", + "slaUpdateInProgress": false, + "slaUpdateMetadata": null, + "sourceNxClusters": [ + "d7dasdb99-5a9d-4da7-8e7d-c93dsad14de" + ], + "status": "READY", + "tags": [], + "type": "postgres_database" + } + ] + +""" + +from ..module_utils.ndb.base_info_module import NdbBaseInfoModule # noqa: E402 +from ..module_utils.ndb.time_machines import TimeMachine # noqa: E402 + + +def get_module_spec(): + + module_args = dict( + name=dict(type="str"), + uuid=dict(type="str"), + ) + + return module_args + + +def get_tm(module, result): + tm = TimeMachine(module) + + uuid = module.params.get("uuid") + name = module.params.get("name") + resp, err = tm.get_time_machine(uuid=uuid, name=name) + if err: + result["error"] = err + module.fail_json(msg="Failed fetching time machine info", **result) + result["response"] = resp + + +def get_tms(module, result): + tm = TimeMachine(module) + + resp = tm.read() + + result["response"] = resp + + +def run_module(): + module = NdbBaseInfoModule( + argument_spec=get_module_spec(), + supports_check_mode=False, + mutually_exclusive=[("name", "uuid")], + ) + result = {"changed": False, "error": None, "response": None} + if module.params.get("name") or module.params.get("uuid"): + get_tm(module, result) + else: + get_tms(module, result) + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/ntnx_ndb_clones_info/meta/main.yml b/tests/integration/targets/ntnx_ndb_clones_info/meta/main.yml new file mode 100644 index 000000000..23b0fb268 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_clones_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_env \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_clones_info/tasks/info.yml b/tests/integration/targets/ntnx_ndb_clones_info/tasks/info.yml new file mode 100644 index 000000000..ad92320a3 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_clones_info/tasks/info.yml @@ -0,0 +1,65 @@ +--- +- debug: + msg: Start testing ntnx_ndb_clones_info + +- name: List all era clones + ntnx_ndb_clones_info: + register: clones + +- name: check listing status + assert: + that: + - clones.response is defined + - clones.failed == false + - clones.changed == false + - clones.response | length > 0 + fail_msg: "Unable to list all era clones" + success_msg: "era clones listed successfully" +################################################################ +- name: get era clones using it's name + ntnx_ndb_clones_info: + name: "{{clones.response[0].name}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{clones.response[0].name}}" + fail_msg: "Unable to get era clones using it's name " + success_msg: "get era clones using it's name successfully" +################################################################ +- name: List clones use id + ntnx_ndb_clones_info: + uuid: "{{clones.response[0].id}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{clones.response[0].name}}" + fail_msg: "Unable to get era clones using it's id " + success_msg: "get era clones using it's id successfully" +################################################################ + + +- name: get era clones with incorrect name + ntnx_ndb_clones_info: + name: "abcd" + register: result + no_log: true + ignore_errors: True + +- name: check listing status + assert: + that: + - result.error is defined + - result.failed == true + - result.changed == false + fail_msg: "module didn't errored out correctly when incorrect name is given" + success_msg: "module errored out correctly when incorrect name is given" \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_clones_info/tasks/main.yml b/tests/integration/targets/ntnx_ndb_clones_info/tasks/main.yml new file mode 100644 index 000000000..da502fcc5 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_clones_info/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: "{{ndb_ip}}" + nutanix_username: "{{ndb_username}}" + nutanix_password: "{{ndb_password}}" + validate_certs: false + block: + - import_tasks: "info.yml" diff --git a/tests/integration/targets/ntnx_ndb_clusters_info/meta/main.yml b/tests/integration/targets/ntnx_ndb_clusters_info/meta/main.yml new file mode 100644 index 000000000..23b0fb268 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_clusters_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_env \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_clusters_info/tasks/info.yml b/tests/integration/targets/ntnx_ndb_clusters_info/tasks/info.yml new file mode 100644 index 000000000..e19713ba0 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_clusters_info/tasks/info.yml @@ -0,0 +1,65 @@ +--- +- debug: + msg: Start testing ntnx_ndb_clusters_info + +- name: List all era clusters + ntnx_ndb_clusters_info: + register: clusters + +- name: check listing status + assert: + that: + - clusters.response is defined + - clusters.failed == false + - clusters.changed == false + - clusters.response | length > 0 + fail_msg: "Unable to list all era clusters" + success_msg: "era clusters listed successfully" +################################################################ +- name: get era clusters using it's name + ntnx_ndb_clusters_info: + name: "{{clusters.response[0].name}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{clusters.response[0].name}}" + fail_msg: "Unable to get era clusters using it's name " + success_msg: "get era clusters using it's name successfully" +################################################################ +- name: List clusters use id + ntnx_ndb_clusters_info: + uuid: "{{clusters.response[0].id}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{clusters.response[0].name}}" + fail_msg: "Unable to get era clusters using it's id " + success_msg: "get era clusters using it's id successfully" +################################################################ + + +- name: get era clusters with incorrect name + ntnx_ndb_clusters_info: + name: "abcd" + register: result + ignore_errors: True + no_log: true + +- name: check listing status + assert: + that: + - result.error is defined + - result.failed == true + - result.changed == false + fail_msg: "module didn't errored out correctly when incorrect name is given" + success_msg: "module errored out correctly when incorrect name is given" \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_clusters_info/tasks/main.yml b/tests/integration/targets/ntnx_ndb_clusters_info/tasks/main.yml new file mode 100644 index 000000000..da502fcc5 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_clusters_info/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: "{{ndb_ip}}" + nutanix_username: "{{ndb_username}}" + nutanix_password: "{{ndb_password}}" + validate_certs: false + block: + - import_tasks: "info.yml" diff --git a/tests/integration/targets/ntnx_ndb_databases_and_info/aliases b/tests/integration/targets/ntnx_ndb_databases_and_info/aliases new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration/targets/ntnx_ndb_databases_and_info/meta/main.yml b/tests/integration/targets/ntnx_ndb_databases_and_info/meta/main.yml new file mode 100644 index 000000000..e4f447d3a --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_databases_and_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_env diff --git a/tests/integration/targets/ntnx_ndb_databases_and_info/tasks/crud.yml b/tests/integration/targets/ntnx_ndb_databases_and_info/tasks/crud.yml new file mode 100644 index 000000000..6f00a0dbd --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_databases_and_info/tasks/crud.yml @@ -0,0 +1,347 @@ +- set_fact: + db_name: "{{ query('community.general.random_string', upper=false, numbers=false, special=false)[0] }}" + db_name_updated: "{{ query('community.general.random_string', upper=false, numbers=false, special=false)[0] }}" + desc: "ansible-created" + desc_updated: "ansible-created-updated" + vm_name: "{{ query('community.general.random_string', upper=false, numbers=false, special=false)[0] }}" + +- debug: + msg: "Starting ntnx_ndb_database crud tests" + +# ############################################################################################ + +- name: Create postgres database instance with new vm spec using check mode + check_mode: yes + ntnx_ndb_databases: + name: "{{db_name}}" + desc: "{{desc}}" + + db_params_profile: + uuid: "TEST_DB_PROFILE_UUID" + + db_vm: + create_new_server: + name: "{{vm_name}}" + password: "{{ndb.password}}" + cluster: + uuid: "TEST_CLUSTER_UUID" + software_profile: + uuid: "{{ndb.software_profile.uuid}}" + network_profile: + uuid: "TEST_NETWORK_UUID" + compute_profile: + uuid: "TEST_COMPUTE_UUID" + pub_ssh_key: "{{ndb.key}}" + + postgres: + listener_port: "5432" + db_name: ansible_test + db_password: "{{ndb.password}}" + db_size: 200 + pre_create_script: "ls" + post_create_script: "ls" + + time_machine: + name: POSTGRES_SERVER_PRAD_TM_1 + desc: POSTGRES_SERVER_PRAD_TM_1_DESC + sla: + uuid: "TEST_SLA_UUID" + schedule: + daily: "11:10:02" + weekly: WEDNESDAY + monthly: 4 + quaterly: JANUARY + yearly: FEBRUARY + log_catchup: 30 + snapshots_per_day: 2 + + tags: + test1: check1 + wait: true + no_log: true + register: db + +- set_fact: + expected_schedule: { + "continuousSchedule": { + "enabled": true, + "logBackupInterval": 30, + "snapshotsPerDay": 2 + }, + "monthlySchedule": { + "dayOfMonth": 4, + "enabled": true + }, + "quartelySchedule": { + "dayOfMonth": 4, + "enabled": true, + "startMonth": "JANUARY" + }, + "snapshotTimeOfDay": { + "hours": 11, + "minutes": 10, + "seconds": 2 + }, + "weeklySchedule": { + "dayOfWeek": "WEDNESDAY", + "enabled": true + }, + "yearlySchedule": { + "dayOfMonth": 4, + "enabled": true, + "month": "FEBRUARY" + } + } + +- name: Verify check mode generated spec + assert: + that: + - db.response is defined + - db.failed == false + - db.changed == false + - db.response.name == db_name + - db.response.databaseDescription == "{{desc}}" + - db.response.actionArguments is defined + - db.response.autoTuneStagingDrive == true + - db.response.clustered == false + - db.response.createDbserver == true + - db.response.nodeCount == 1 + - db.response.nodes | length == 1 + - db.response.nodes[0].networkProfileId == "TEST_NETWORK_UUID" + - db.response.nodes[0].vmName == "{{vm_name}}" + - db.response.dbParameterProfileId == "TEST_DB_PROFILE_UUID" + - db.response.softwareProfileId == "{{ndb.software_profile.uuid}}" + - db.response.softwareProfileVersionId is defined + - db.response.networkProfileId == "TEST_NETWORK_UUID" + - db.response.computeProfileId == "TEST_COMPUTE_UUID" + - db.response.timeMachineInfo is defined + - db.response.timeMachineInfo.name == "POSTGRES_SERVER_PRAD_TM_1" + - db.response.timeMachineInfo.schedule == expected_schedule + - db.response.timeMachineInfo.slaId == "TEST_SLA_UUID" + - db.response.databaseType == "postgres_database" + - db.response.tags[0]["value"] == "check1" + - db.response.tags[0]["tagName"] == "test1" + fail_msg: "Unable to create spec for database instance in check mode" + success_msg: "Created check mode spec for database instance successfully" + +# ############################################################################################ + +- name: Create postgres database instance using with new vm + ntnx_ndb_databases: + name: "{{db_name}}" + desc: "{{desc}}" + + db_params_profile: + name: "{{ndb.db_params}}" + + db_vm: + create_new_server: + name: "{{vm_name}}" + password: "{{ndb.password}}" + cluster: + name: "{{ndb.cluster}}" + software_profile: + name: "{{ndb.software_profile.name}}" + network_profile: + name: "{{ndb.network_profile}}" + compute_profile: + name: "{{ndb.compute_profile}}" + pub_ssh_key: "{{ndb.key}}" + + postgres: + listener_port: "5432" + db_name: ansible_test + db_password: "{{ndb.password}}" + db_size: 200 + pre_create_script: "ls" + post_create_script: "ls" + + time_machine: + name: POSTGRES_SERVER_PRAD_TM_1 + desc: POSTGRES_SERVER_PRAD_TM_1_DESC + sla: + name: "{{ndb.sla_name}}" + schedule: + daily: "11:10:02" + weekly: WEDNESDAY + monthly: 4 + quaterly: JANUARY + yearly: FEBRUARY + log_catchup: 30 + snapshots_per_day: 2 + + tags: + test1: check1 + wait: true + no_log: true + register: db + +- name: Verify attributes + assert: + that: + - db.response is defined + - db.failed == false + - db.changed == true + - db.db_uuid is defined + - db.response.status == "READY" + - db.response.tags | length == 1 + - db.response.type == "postgres_database" + - db.response.name == "{{db_name}}" + - db.response.description == "{{desc}}" + - db.response.tags[0]["value"] == "check1" + - db.response.tags[0]["tagName"] == "test1" + fail_msg: "Unable to create database instance" + success_msg: "Created database instance successfully" + +############################################################################################ + +- name: update db instance + ntnx_ndb_databases: + db_uuid: "{{db.db_uuid}}" + name: "{{db_name_updated}}" + desc: "{{desc_updated}}" + tags: + test1: check1_updated + test2: check2_updated + no_log: true + register: db + +- set_fact: + expected_tags: { + "test1": "check1_updated", + "test2": "check2_updated" + } + +- name: Verify attributes + assert: + that: + - db.response is defined + - db.failed == false + - db.changed == true + - db.db_uuid == "{{db.db_uuid}}" + - db.response.status == "READY" + - db.response.tags | length == 2 + - db.response.type == "postgres_database" + - db.response.name == "{{db_name_updated}}" + - db.response.description == "{{desc_updated}}" + - expected_tags[db.response.tags[0].tagName] == db.response.tags[0].value + - expected_tags[db.response.tags[1].tagName] == db.response.tags[1].value + + fail_msg: "Unable to update database instance" + success_msg: "Database instance updated successfully" + +############################################################################################ + +- name: idempotency check, update db instance with same spec + ntnx_ndb_databases: + db_uuid: "{{db.db_uuid}}" + name: "{{db_name_updated}}" + desc: "{{desc_updated}}" + tags: + test1: check1_updated + test2: check2_updated + register: result + no_log: true + ignore_errors: True + +- name: idempotency check status + assert: + that: + - result.changed == False + - result.failed == False + - "'Nothing to change' in result.msg" + fail_msg: "Idempotency check failed" + success_msg: "Database instance updated call skipped successfully" + +#####################################INFO Module tests####################################################### + +- debug: + msg: Start testing ntnx_ndb_databases_info based on created database + +- name: List era databases + ntnx_ndb_databases_info: + register: databases + no_log: true + +- name: check listing status + assert: + that: + - databases.response is defined + - databases.failed == false + - databases.changed == false + - databases.response | length > 0 + fail_msg: "Unable to list all era databases" + success_msg: "era databases listed successfully" +################################################################ +- name: Get era databases using its name + ntnx_ndb_databases_info: + name: "{{databases.response[0].name}}" + register: result + no_log: true + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.id == "{{databases.response[0].id}}" + fail_msg: "Unable to Get era databases using its name" + success_msg: "Get era databases using its name finished successfully" +################################################################ +- name: Get era databases using its name + ntnx_ndb_databases_info: + uuid: "{{databases.response[0].id}}" + register: result + no_log: true + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{databases.response[0].name}}" + fail_msg: "Unable to Get era databases using its id" + success_msg: "Get era databases using its id finished successfully" + +################################################################ + +- name: get era database with incorrect name + ntnx_ndb_databases_info: + name: "xxxxxxx" + register: result + ignore_errors: True + no_log: true + +- name: check listing status + assert: + that: + - result.error is defined + - result.failed == true + - result.changed == false + fail_msg: "module didn't errored out correctly when incorrect name is given" + success_msg: "module errored out correctly when incorrect name is given" + +############################################################################################ + +- name: delete database + ntnx_ndb_databases: + state: "absent" + db_uuid: "{{db.db_uuid}}" + delete_time_machine: True + wait: true + no_log: true + register: db + +- name: Verify attributes + assert: + that: + - db.response is defined + - db.failed == false + - db.changed == true + - db.response.status == "5" + fail_msg: "Unable to delete database instance" + success_msg: "Database instance deleted successfully" + +############################################################################################ diff --git a/tests/integration/targets/ntnx_ndb_databases_and_info/tasks/main.yml b/tests/integration/targets/ntnx_ndb_databases_and_info/tasks/main.yml new file mode 100644 index 000000000..87263bc16 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_databases_and_info/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: "{{ndb_ip}}" + nutanix_username: "{{ndb_username}}" + nutanix_password: "{{ndb_password}}" + validate_certs: false + block: + - import_tasks: "crud.yml" diff --git a/tests/integration/targets/ntnx_ndb_db_servers_info/meta/main.yml b/tests/integration/targets/ntnx_ndb_db_servers_info/meta/main.yml new file mode 100644 index 000000000..23b0fb268 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_db_servers_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_env \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_db_servers_info/tasks/info.yml b/tests/integration/targets/ntnx_ndb_db_servers_info/tasks/info.yml new file mode 100644 index 000000000..8aa65c2e2 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_db_servers_info/tasks/info.yml @@ -0,0 +1,86 @@ +--- + + +- debug: + msg: Start testing ntnx_ndb_db_servers_info + +- name: List era db_servers + ntnx_ndb_db_servers_info: + register: db_servers + + +- name: check listing status + assert: + that: + - db_servers.response is defined + - db_servers.failed == false + - db_servers.changed == false + - db_servers.response | length > 0 + fail_msg: "Unable to list all era db_servers" + success_msg: "era db_servers listed successfully" +################################################################ +- name: get era db_servers using it's name + ntnx_ndb_db_servers_info: + name: "{{db_servers.response[0].name}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.id == "{{db_servers.response[0].id}}" + - result.response.ipAddresses[0] == "{{db_servers.response[0].ipAddresses[0]}}" + fail_msg: "Unable to get era db_servers using it's name " + success_msg: "get era db_server using it's name finished successfully" +################################################################ +- name: get era db_servers using it's id + ntnx_ndb_db_servers_info: + uuid: "{{db_servers.response[0].id}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{db_servers.response[0].name}}" + - result.response.ipAddresses[0] == "{{db_servers.response[0].ipAddresses[0]}}" + fail_msg: "Unable to get era db_servers using it's id " + success_msg: "get era db_server using it's id finished successfully" +################################################################ +- name: get era db_servers using ip + ntnx_ndb_db_servers_info: + server_ip: "{{db_servers.response[0].ipAddresses[0]}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{db_servers.response[0].name}}" + - result.response.id == "{{db_servers.response[0].id}}" + fail_msg: "Unable to get era db_servers using it's ip " + success_msg: "get era db_server using it's ip finished successfully" + +################################################################ + +- name: get era db_servers with incorrect name + ntnx_ndb_db_servers_info: + name: "abcd" + register: result + no_log: true + ignore_errors: True + +- name: check listing status + assert: + that: + - result.error is defined + - result.failed == true + - result.changed == false + fail_msg: "module didn't errored out correctly when incorrect name is given" + success_msg: "module errored out correctly when incorrect name is given" \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_db_servers_info/tasks/main.yml b/tests/integration/targets/ntnx_ndb_db_servers_info/tasks/main.yml new file mode 100644 index 000000000..da502fcc5 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_db_servers_info/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: "{{ndb_ip}}" + nutanix_username: "{{ndb_username}}" + nutanix_password: "{{ndb_password}}" + validate_certs: false + block: + - import_tasks: "info.yml" diff --git a/tests/integration/targets/ntnx_ndb_profiles_info/meta/main.yml b/tests/integration/targets/ntnx_ndb_profiles_info/meta/main.yml new file mode 100644 index 000000000..23b0fb268 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_profiles_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_env \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_profiles_info/tasks/info.yml b/tests/integration/targets/ntnx_ndb_profiles_info/tasks/info.yml new file mode 100644 index 000000000..b47afc749 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_profiles_info/tasks/info.yml @@ -0,0 +1,127 @@ +--- +- debug: + msg: Start testing ntnx_ndb_profiles_info + +- name: List profiles + ntnx_ndb_profiles_info: + register: profiles + + +- name: check listing status + assert: + that: + - profiles.response is defined + - profiles.failed == false + - profiles.changed == false + - profiles.response | length > 0 + fail_msg: "Unable to list all era profile" + success_msg: "era profiles listed successfully" +################################################################ +- name: List Database_Parameter profiles + ntnx_ndb_profiles_info: + profile_type: Database_Parameter + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response[0].type == "Database_Parameter" + fail_msg: "Unable to list all Database_Parameter era profile" + success_msg: "Database_Parameter era profiles listed successfully" +################################################################ +- name: List Network profiles + ntnx_ndb_profiles_info: + profile_type: Network + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response[0].type == "Network" + fail_msg: "Unable to list all Network era profile" + success_msg: "Network era profiles listed successfully" +################################################################ +- name: List Compute profiles + ntnx_ndb_profiles_info: + profile_type: Compute + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response[0].type == "Compute" + fail_msg: "Unable to list all Compute era profile" + success_msg: "Compute era profiles listed successfully" +################################################################ +- name: List Software profiles + ntnx_ndb_profiles_info: + profile_type: Software + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response[0].type == "Software" + fail_msg: "Unable to list all Software era profile" + success_msg: "Software era profiles listed successfully" +################################################################ +- name: get era profile using era profile name + ntnx_ndb_profiles_info: + name: "{{profiles.response[0].name}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.id == "{{profiles.response[0].id}}" + fail_msg: "Unable to get era profile using era profile name" + success_msg: "get era profile using era profile name finished successfully" +################################################################ +- name: List profiles + ntnx_ndb_profiles_info: + uuid: "{{profiles.response[0].id}}" + latest_version: true + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{profiles.response[0].name}}" + fail_msg: "Unable to get era profile using era profile id" + success_msg: "get era profile using era profile id finished successfully" +################################################################ + + +- name: get era profiles with incorrect name + ntnx_ndb_profiles_info: + name: "abcd" + register: result + no_log: true + ignore_errors: True + +- name: check listing status + assert: + that: + - result.error is defined + - result.failed == true + - result.changed == false + fail_msg: "module didn't errored out correctly when incorrect name is given" + success_msg: "module errored out correctly when incorrect name is given" \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_profiles_info/tasks/main.yml b/tests/integration/targets/ntnx_ndb_profiles_info/tasks/main.yml new file mode 100644 index 000000000..da502fcc5 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_profiles_info/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: "{{ndb_ip}}" + nutanix_username: "{{ndb_username}}" + nutanix_password: "{{ndb_password}}" + validate_certs: false + block: + - import_tasks: "info.yml" diff --git a/tests/integration/targets/ntnx_ndb_slas_info/meta/main.yml b/tests/integration/targets/ntnx_ndb_slas_info/meta/main.yml new file mode 100644 index 000000000..23b0fb268 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_slas_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_env \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_slas_info/tasks/info.yml b/tests/integration/targets/ntnx_ndb_slas_info/tasks/info.yml new file mode 100644 index 000000000..2acec16a5 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_slas_info/tasks/info.yml @@ -0,0 +1,65 @@ +--- +- debug: + msg: Start testing ntnx_ndb_slas_info + +- name: List all era slas + ntnx_ndb_slas_info: + register: slas + +- name: check listing status + assert: + that: + - slas.response is defined + - slas.failed == false + - slas.changed == false + - slas.response | length > 0 + fail_msg: "Unable to list all era slas" + success_msg: "era slas listed successfully" +################################################################ +- name: get era slas using it's name + ntnx_ndb_slas_info: + name: "{{slas.response[0].name}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{slas.response[0].name}}" + fail_msg: "Unable to get era slas using it's name " + success_msg: "get era slas using it's name successfully" +################################################################ +- name: List slas use id + ntnx_ndb_slas_info: + uuid: "{{slas.response[0].id}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{slas.response[0].name}}" + fail_msg: "Unable to get era slas using it's id " + success_msg: "get era slas using it's id successfully" +################################################################ + + +- name: get era slas with incorrect name + ntnx_ndb_slas_info: + name: "abcd" + register: result + no_log: true + ignore_errors: True + +- name: check listing status + assert: + that: + - result.error is defined + - result.failed == true + - result.changed == false + fail_msg: "module didn't errored out correctly when incorrect name is given" + success_msg: "module errored out correctly when incorrect name is given" \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_slas_info/tasks/main.yml b/tests/integration/targets/ntnx_ndb_slas_info/tasks/main.yml new file mode 100644 index 000000000..da502fcc5 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_slas_info/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: "{{ndb_ip}}" + nutanix_username: "{{ndb_username}}" + nutanix_password: "{{ndb_password}}" + validate_certs: false + block: + - import_tasks: "info.yml" diff --git a/tests/integration/targets/ntnx_ndb_time_machines_info/meta/main.yml b/tests/integration/targets/ntnx_ndb_time_machines_info/meta/main.yml new file mode 100644 index 000000000..23b0fb268 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_time_machines_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_env \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_time_machines_info/tasks/info.yml b/tests/integration/targets/ntnx_ndb_time_machines_info/tasks/info.yml new file mode 100644 index 000000000..4fa4b2fb6 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_time_machines_info/tasks/info.yml @@ -0,0 +1,65 @@ +--- +- debug: + msg: Start testing ntnx_ndb_time_machines_info + +- name: List all era tms + ntnx_ndb_time_machines_info: + register: tms + +- name: check listing status + assert: + that: + - tms.response is defined + - tms.failed == false + - tms.changed == false + - tms.response | length > 0 + fail_msg: "Unable to list all era tms" + success_msg: "era tms listed successfully" +################################################################ +- name: get era tms using it's name + ntnx_ndb_time_machines_info: + name: "{{tms.response[0].name}}" + register: result + + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.id == "{{tms.response[0].id}}" + fail_msg: "Unable to get era tms using it's name " + success_msg: "get era tms using it's name successfully" +################################################################ +- name: List tms use id + ntnx_ndb_time_machines_info: + uuid: "{{tms.response[0].id}}" + register: result + +- name: check listing status + assert: + that: + - result.response is defined + - result.failed == false + - result.changed == false + - result.response.name == "{{tms.response[0].name}}" + fail_msg: "Unable to get era tms using it's id " + success_msg: "get era tms using it's id successfully" +################################################################ + +- name: get era timemachine with incorrect name + ntnx_ndb_time_machines_info: + name: "abcd" + register: result + no_log: true + ignore_errors: True + +- name: check listing status + assert: + that: + - result.error is defined + - result.failed == true + - result.changed == false + fail_msg: "module didn't errored out correctly when incorrect name is given" + success_msg: "module errored out correctly when incorrect name is given" \ No newline at end of file diff --git a/tests/integration/targets/ntnx_ndb_time_machines_info/tasks/main.yml b/tests/integration/targets/ntnx_ndb_time_machines_info/tasks/main.yml new file mode 100644 index 000000000..da502fcc5 --- /dev/null +++ b/tests/integration/targets/ntnx_ndb_time_machines_info/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- module_defaults: + group/nutanix.ncp.ntnx: + nutanix_host: "{{ndb_ip}}" + nutanix_username: "{{ndb_username}}" + nutanix_password: "{{ndb_password}}" + validate_certs: false + block: + - import_tasks: "info.yml" diff --git a/tests/integration/targets/ntnx_projects/tasks/create_project.yml b/tests/integration/targets/ntnx_projects/tasks/create_project.yml index 7c97da225..57bf3cb9c 100644 --- a/tests/integration/targets/ntnx_projects/tasks/create_project.yml +++ b/tests/integration/targets/ntnx_projects/tasks/create_project.yml @@ -2,9 +2,21 @@ debug: msg: "Start ntnx_project create tests" +- name: Generate random project_name + set_fact: + random_name: "{{query('community.general.random_string',numbers=false, special=false,length=12)[0]}}" + +- set_fact: + suffix_name: "ansible-role-mapping" + +- set_fact: + project1_name: "{{random_name}}{{suffix_name}}1" + project2_name: "{{random_name}}{{suffix_name}}2" + + - name: Create Project with minimal spec ntnx_projects: - name: "test-ansible-project" + name: "{{project1_name}}" register: result ignore_errors: true @@ -14,7 +26,7 @@ - result.response is defined - result.changed == true - result.response.status.state == 'COMPLETE' - - result.response.status.name == 'test-ansible-project' + - result.response.status.name == "{{project1_name}}" fail_msg: "Unable to create project with minimal spec" success_msg: "Project with minimal spec created successfully" @@ -26,7 +38,7 @@ - name: Create Project with check mode check_mode: yes ntnx_projects: - name: "test-ansible-project-1" + name: "{{project2_name}}" desc: desc-123 subnets: - name: "{{ network.dhcp.name }}" @@ -49,7 +61,7 @@ that: - result.response is defined - result.changed == false - - result.response.spec.name == 'test-ansible-project-1' + - result.response.spec.name == "{{project2_name}}" - result.response.spec.description == 'desc-123' - result.response.spec.resources.resource_domain.resources[0].limit == 2046 - result.response.spec.resources.resource_domain.resources[0].resource_type == 'STORAGE' @@ -67,7 +79,7 @@ - name: Create Project with all specs ntnx_projects: - name: "test-ansible-project-1" + name: "{{project2_name}}" desc: desc-123 clusters: - "{{ cluster.uuid }}" @@ -101,7 +113,7 @@ that: - result.response is defined - result.response.status.state == 'COMPLETE' - - result.response.status.name == 'test-ansible-project-1' + - result.response.status.name == "{{project2_name}}" - result.response.status.description == 'desc-123' - result.response.status.resources.resource_domain.resources[0].limit == 2046 - result.response.status.resources.resource_domain.resources[0].resource_type == 'STORAGE' diff --git a/tests/integration/targets/ntnx_projects/tasks/update_project.yml b/tests/integration/targets/ntnx_projects/tasks/update_project.yml index 0034c31d0..47b909247 100644 --- a/tests/integration/targets/ntnx_projects/tasks/update_project.yml +++ b/tests/integration/targets/ntnx_projects/tasks/update_project.yml @@ -2,9 +2,20 @@ debug: msg: "Start ntnx_project update tests" +- name: Generate random project_name + set_fact: + random_name: "{{query('community.general.random_string',numbers=false, special=false,length=12)[0]}}" + +- set_fact: + suffix_name: "ansible-role-mapping" + +- set_fact: + project1_name: "{{random_name}}{{suffix_name}}1" + + - name: Create Project ntnx_projects: - name: "test-ansible-project-4" + name: "{{project1_name}}" desc: desc-123 subnets: - name: "{{ network.dhcp.name }}" diff --git a/tests/integration/targets/ntnx_roles_info/tasks/roles_info.yml b/tests/integration/targets/ntnx_roles_info/tasks/roles_info.yml index 874870b07..2a3b80e7d 100644 --- a/tests/integration/targets/ntnx_roles_info/tasks/roles_info.yml +++ b/tests/integration/targets/ntnx_roles_info/tasks/roles_info.yml @@ -2,8 +2,9 @@ msg: start testing ntnx_roles_info ################################################## -- name: List all roles +- name: List roles ntnx_roles_info: + length: 2 register: result ignore_errors: True diff --git a/tests/integration/targets/ntnx_user_groups_info/tasks/user_groups_info.yml b/tests/integration/targets/ntnx_user_groups_info/tasks/user_groups_info.yml index 65ff38673..a85155f9a 100644 --- a/tests/integration/targets/ntnx_user_groups_info/tasks/user_groups_info.yml +++ b/tests/integration/targets/ntnx_user_groups_info/tasks/user_groups_info.yml @@ -44,7 +44,7 @@ - name: List user_groups using filter criteria ntnx_user_groups_info: filter: - group_name: "{{ test_user_group_name }}" + name: "{{ test_user_group_name }}" register: result ignore_errors: True diff --git a/tests/integration/targets/nutanix_vms/tasks/create.yml b/tests/integration/targets/nutanix_vms/tasks/create.yml index bc6bf0609..f8f43736a 100644 --- a/tests/integration/targets/nutanix_vms/tasks/create.yml +++ b/tests/integration/targets/nutanix_vms/tasks/create.yml @@ -151,6 +151,15 @@ - set_fact: todelete: '{{ todelete + [ result["response"]["metadata"]["uuid"] ] }}' when: result.response.status.state == 'COMPLETE' + + - name: Delete all Created VMs + ntnx_vms: + state: absent + vm_uuid: '{{ item }}' + register: result + loop: '{{ todelete }}' + - set_fact: + todelete: [] ################################################################################# - name: VM with Cluster, Network, Universal time zone, one Disk ntnx_vms: @@ -239,6 +248,15 @@ - set_fact: todelete: '{{ todelete + [ result["response"]["metadata"]["uuid"] ] }}' + + - name: Delete all Created VMs + ntnx_vms: + state: absent + vm_uuid: '{{ item }}' + register: result + loop: '{{ todelete }}' + - set_fact: + todelete: [] #################################################################################### - name: VM with all specification ntnx_vms: @@ -334,6 +352,15 @@ - set_fact: todelete: '{{ todelete + [ result["response"]["metadata"]["uuid"] ] }}' when: result.response.status.state == 'COMPLETE' + + - name: Delete all Created VMs + ntnx_vms: + state: absent + vm_uuid: '{{ item }}' + register: result + loop: '{{ todelete }}' + - set_fact: + todelete: [] ################################################################################################## - name: VM with unmanaged vlan ntnx_vms: