Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding protection policies and protected resources module dev&tests #552

Open
wants to merge 22 commits into
base: release/2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
757dfb2
Adding protected resources module dev
george-ghawali Dec 30, 2024
4169794
black & isort fixes
george-ghawali Dec 30, 2024
c437ae7
flake8 fix
george-ghawali Dec 30, 2024
1c6b356
Adding protection policies module dev
george-ghawali Dec 30, 2024
5fb9ca4
update author name
george-ghawali Dec 30, 2024
918e715
Added chech mode test for creating protection policy
abhinavbansal29 Jan 7, 2025
a5a6d5f
Adding protection policies tests & code fixes
george-ghawali Jan 9, 2025
3f96326
Adding protection policies examples file
george-ghawali Jan 9, 2025
9beedd2
Adding Create Synchronous replication protection policy test
george-ghawali Jan 12, 2025
b96764a
Merge branch 'task/protected-resources-V4' into task/protection-polic…
george-ghawali Jan 13, 2025
aa7774d
Adding promote and restore test cases
george-ghawali Jan 13, 2025
3993d83
flake8 fix
george-ghawali Jan 13, 2025
ab43173
Added module for volume group categories
abhinavbansal29 Jan 14, 2025
4813c4a
Added ntnx_volume_groups_categories_v2 in runtime.yml
abhinavbansal29 Jan 14, 2025
f783812
Fixed ext_id typo in module_spec
abhinavbansal29 Jan 14, 2025
b759a5e
Adding promote&restore volume groups
george-ghawali Jan 14, 2025
90a5e72
Adding examples for promote and restore in examples file
george-ghawali Jan 15, 2025
0bf39dd
minor fix in examples
george-ghawali Jan 15, 2025
e441e2c
Adding creating and deleting categories tests
george-ghawali Jan 22, 2025
f043de7
minor fix in filter protected VMs only
george-ghawali Jan 22, 2025
1da0b20
Ansible lint fixes
george-ghawali Feb 2, 2025
360ea89
Fixing promote VG issue by creating new cluster
george-ghawali Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
456 changes: 456 additions & 0 deletions examples/protection_policies_v2/protection_policies.yml

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,9 @@ action_groups:
- ntnx_storage_containers_stats_v2
- ntnx_storage_containers_info_v2
- ntnx_storage_containers_v2
- ntnx_protection_policies_info_v2
- ntnx_protection_policies_v2
- ntnx_promote_protected_resources_v2
- ntnx_restore_protected_resources_v2
- ntnx_protected_resources_info_v2
- ntnx_volume_groups_categories_v2
1 change: 1 addition & 0 deletions plugins/module_utils/v4/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ class CompletetionDetailsName:
RECOVERY_POINT = "recoveryPointExtId"
VM_EXT_IDS = "vmExtIds"
VG_EXT_IDS = "volumeGroupExtIds"
PROTECTION_POLICY = "protectionPolicyExtId"
72 changes: 72 additions & 0 deletions plugins/module_utils/v4/data_policies/api_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2024, Nutanix
# 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

import traceback
from base64 import b64encode

from ansible.module_utils.basic import missing_required_lib

SDK_IMP_ERROR = None
try:
import ntnx_datapolicies_py_client
except ImportError:
SDK_IMP_ERROR = traceback.format_exc()


def get_api_client(module):
"""
This method will return client to be used in api connection using
given connection details.
"""
if SDK_IMP_ERROR:
module.fail_json(
msg=missing_required_lib("ntnx_datapolicies_py_client"),
exception=SDK_IMP_ERROR,
)

config = ntnx_datapolicies_py_client.Configuration()
config.host = module.params.get("nutanix_host")
config.port = module.params.get("nutanix_port")
config.username = module.params.get("nutanix_username")
config.password = module.params.get("nutanix_password")
config.verify_ssl = module.params.get("validate_certs")
client = ntnx_datapolicies_py_client.ApiClient(configuration=config)

cred = "{0}:{1}".format(config.username, config.password)
try:
encoded_cred = b64encode(bytes(cred, encoding="ascii")).decode("ascii")
except BaseException:
encoded_cred = b64encode(bytes(cred).encode("ascii")).decode("ascii")
auth_header = "Basic " + encoded_cred
client.add_default_header(header_name="Authorization", header_value=auth_header)
return client


def get_etag(data):
"""
This method will fetch etag from a v4 api response.
Args:
data (dict): v4 api response
Returns:
str: etag value
"""
return ntnx_datapolicies_py_client.ApiClient.get_etag(data)


def get_protection_policies_api_instance(module):
"""
This method will return data policies api instance.
Args:
module (object): Ansible module object
Returns:
api_instance (object): data policies api instance
"""
client = get_api_client(module)
return ntnx_datapolicies_py_client.ProtectionPoliciesApi(client)
31 changes: 31 additions & 0 deletions plugins/module_utils/v4/data_policies/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2024, Nutanix
# 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 ..utils import raise_api_exception # noqa: E402


def get_protection_policy(module, api_instance, ext_id):
"""
This method will return protection policy info using external ID.
Args:
module: Ansible module
api_instance: ProtectionPoliciesApi instance from ntnx_datapolicies_py_clien sdk
ext_id (str): top level recovery point external ID
Returns:
protection_policy_info (object): protection policy info
"""
try:
return api_instance.get_protection_policy_by_id(extId=ext_id).data
except Exception as e:
raise_api_exception(
module=module,
exception=e,
msg="Api Exception raised while fetching protection policy info using ext_id",
)
12 changes: 12 additions & 0 deletions plugins/module_utils/v4/data_protection/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,15 @@ def get_recovery_point_api_instance(module):
"""
client = get_api_client(module)
return ntnx_dataprotection_py_client.RecoveryPointsApi(client)


def get_protected_resource_api_instance(module):
"""
This method will return data protection api instance.
Args:
module (object): Ansible module object
Returns:
api_instance (object): data protection api instance
"""
client = get_api_client(module)
return ntnx_dataprotection_py_client.ProtectedResourcesApi(client)
18 changes: 18 additions & 0 deletions plugins/module_utils/v4/data_protection/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,21 @@ def get_recovery_point(module, api_instance, ext_id):
exception=e,
msg="Api Exception raised while fetching recovery point info using ext_id",
)


def get_protected_resource(module, api_instance, ext_id):
"""
This method will return protected resource info using external ID.
Args:
module: Ansible module
Returns:
protected_resource_info (object): protected resource info
"""
try:
return api_instance.get_protected_resource_by_id(extId=ext_id).data
except Exception as e:
raise_api_exception(
module=module,
exception=e,
msg="Api Exception raised while fetching protected resource info using ext_id",
)
190 changes: 190 additions & 0 deletions plugins/modules/ntnx_promote_protected_resources_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2024, Nutanix
# 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_promote_protected_resources_v2
short_description: Module to promote a protected resource in Nutanix Prism Central.
description:
- This module can be used to promote a protected resource in Nutanix Prism Central.
options:
ext_id:
description:
- The external identifier of a protected VM or volume group used to retrieve the protected resource.
type: str
required: true
wait:
description:
- Wait for the task to complete.
type: bool
required: false
default: True
extends_documentation_fragment:
- nutanix.ncp.ntnx_credentials
- nutanix.ncp.ntnx_operations_v2
author:
- George Ghawali (@george-ghawali)
"""

EXAMPLES = r"""
- name: Promote VM
nutanix.ncp.ntnx_promote_protected_resources_v2:
nutanix_host: "{{ availability_zone_pc_ip }}"
nutanix_username: "{{ username }}"
nutanix_password: "{{ password }}"
validate_certs: false
ext_id: "1ca2963d-77b6-453a-ae23-2c19e7a954a3"
register: result
ignore_errors: true
"""

RETURN = r"""
response:
description: Task response for promoting a protected resource.
returned: always
type: dict
sample:
{
"cluster_ext_ids": [
"00062aa9-5634-d950-185b-ac1f6b6f97e2"
],
"completed_time": "2025-01-14T08:03:14.995979+00:00",
"completion_details": [
{
"name": "promotedVmExtId",
"value": "655ea7b5-22f6-4989-9820-4992f8165bc1"
}
],
"created_time": "2025-01-14T08:03:03.027768+00:00",
"entities_affected": null,
"error_messages": null,
"ext_id": "ZXJnb24=:3db0a14c-53d4-4b3e-8c86-7417ee9cae43",
"is_background_task": false,
"is_cancelable": true,
"last_updated_time": "2025-01-14T08:03:14.995978+00:00",
"legacy_error_message": null,
"number_of_entities_affected": 0,
"number_of_subtasks": 0,
"operation": "VmPromote",
"operation_description": "Promote Virtual Machine",
"owned_by": {
"ext_id": "00000000-0000-0000-0000-000000000000",
"name": "admin"
},
"parent_task": null,
"progress_percentage": 100,
"root_task": null,
"started_time": "2025-01-14T08:03:03.089069+00:00",
"status": "SUCCEEDED",
"sub_steps": null,
"sub_tasks": null,
"warnings": null
}

changed:
description: This indicates whether the task resulted in any changes
returned: always
type: bool
sample: true

error:
description: This field typically holds information about if the task have errors that occurred during the task execution
returned: always
type: bool
sample: false

failed:
description: This indicates whether the task failed
returned: always
type: bool
sample: false

ext_id:
description: The external identifier of the protected resource.
returned: always
type: str
sample: "1ca2963d-77b6-453a-ae23-2c19e7a954a3"

task_ext_id:
description: The external identifier of the task.
returned: always
type: str
sample: "ZXJnb24=:af298405-1d59-4c28-9b78-f8f94a5adf2d"
"""

from ..module_utils.base_module import BaseModule # noqa: E402
from ..module_utils.utils import remove_param_with_none_value # noqa: E402
from ..module_utils.v4.data_protection.api_client import ( # noqa: E402
get_protected_resource_api_instance,
)
from ..module_utils.v4.prism.tasks import wait_for_completion # noqa: E402
from ..module_utils.v4.utils import ( # noqa: E402
raise_api_exception,
strip_internal_attributes,
)


def get_module_spec():

module_args = dict(
ext_id=dict(type="str", required=True),
)
return module_args


def promote_protected_resource(module, result):
protected_resource = get_protected_resource_api_instance(module)
ext_id = module.params.get("ext_id")
result["ext_id"] = ext_id

resp = None
try:
resp = protected_resource.promote_protected_resource(extId=ext_id)

except Exception as e:
raise_api_exception(
module=module,
exception=e,
msg="Api Exception raised while promoting protected resource",
)

task_ext_id = resp.data.ext_id
result["task_ext_id"] = task_ext_id
result["response"] = strip_internal_attributes(resp.data.to_dict())
if task_ext_id and module.params.get("wait"):
resp = wait_for_completion(module, task_ext_id)
result["response"] = strip_internal_attributes(resp.to_dict())
result["changed"] = True


def run_module():
module = BaseModule(
argument_spec=get_module_spec(),
supports_check_mode=True,
)

remove_param_with_none_value(module.params)
result = {
"changed": False,
"error": None,
"response": None,
"ext_id": None,
}
promote_protected_resource(module, result)

module.exit_json(**result)


def main():
run_module()


if __name__ == "__main__":
main()
Loading
Loading