Skip to content

Commit

Permalink
Merge pull request #84 from nutanix/feat/vpcs
Browse files Browse the repository at this point in the history
Add Vpcs Module
  • Loading branch information
Gevorg-Khachatryan-97 authored Feb 10, 2022
2 parents a4bd406 + c167ce1 commit 0480653
Show file tree
Hide file tree
Showing 9 changed files with 497 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/unit_testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
run: |
cd /home/${USER}/.ansible/collections/ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }}
ansible-test units --docker default --python ${{ matrix.python-version }} --coverage -v
ansible-test coverage report --include */plugins/* --omit */utils.py* > coverage.txt
ansible-test coverage report --include */plugins/* --omit */utils.py,_fetch_url* > coverage.txt
- name: Code Coverage Check
run: |
cd /home/${USER}/.ansible/collections/ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }}
Expand Down
17 changes: 17 additions & 0 deletions plugins/module_utils/prism/subnets.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,20 @@ def _get_default_dhcp_spec(self):
"tftp_server_name": "",
}
)


# Helper functions


def get_subnet_uuid(config, module):
if "name" in config or "subnet_name" in config:
subnet = Subnet(module)
name = config.get("name") or config.get("subnet_name")
uuid = subnet.get_uuid(name)
if not uuid:
error = "Subnet {0} not found.".format(name)
return None, error
elif "uuid" in config or "subnet_uuid" in config:
uuid = config.get("uuid") or config.get("subnet_uuid")

return uuid, None
60 changes: 55 additions & 5 deletions plugins/module_utils/prism/vpcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,69 @@ def __init__(self, module):
resource_type = "/vpcs"
super(Vpc, self).__init__(module, resource_type=resource_type)
self.build_spec_methods = {
# TODO. This is a Map of
# ansible attirbute and corresponding API spec generation method
# Example: method name should start with _build_spec_<method_name>
# name: _build_spec_name
"name": self._build_spec_name,
"external_subnets": self._build_spec_external_subnet,
"routable_ips": self._build_spec_routable_ips,
"dns_servers": self._build_dns_servers,
}

def _get_default_spec(self):
return deepcopy(
{
# TODO: Default API spec
"api_version": "3.1.0",
"metadata": {"kind": "vpc", "categories": {}},
"spec": {
"name": None,
"resources": {
"common_domain_name_server_ip_list": [],
"external_subnet_list": [],
"externally_routable_prefix_list": [],
},
},
}
)

def _build_spec_name(self, payload, name):
payload["spec"]["name"] = name
return payload, None

def _build_spec_external_subnet(self, payload, subnets):
from .subnets import get_subnet_uuid

external_subnets = []
for subnet in subnets:
uuid, error = get_subnet_uuid(subnet, self.module)
if error:
return None, error
subnet_ref_spec = self._get_external_subnet_ref_spec(uuid)
external_subnets.append(subnet_ref_spec)

payload["spec"]["resources"]["external_subnet_list"] = external_subnets
return payload, None

def _build_spec_routable_ips(self, payload, ips):
routable_ips = []
for ip in ips:
routable_ip_ref_spec = self._get_routable_ip_spec(
ip["network_ip"], ip["network_prefix"]
)
routable_ips.append(routable_ip_ref_spec)

payload["spec"]["resources"]["externally_routable_prefix_list"] = routable_ips
return payload, None

def _build_dns_servers(self, payload, dns_servers):
payload["spec"]["resources"]["common_domain_name_server_ip_list"] = [
{"ip": i} for i in dns_servers
]
return payload, None

def _get_external_subnet_ref_spec(self, uuid):
return deepcopy({"external_subnet_reference": {"kind": "subnet", "uuid": uuid}})

def _get_routable_ip_spec(self, ip, prefix):
return deepcopy({"ip": ip, "prefix_length": prefix})


# Helper functions

Expand Down
230 changes: 230 additions & 0 deletions plugins/modules/ntnx_vpcs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
#!/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_vpcs
short_description: vpcs module which suports vpc CRUD operations
version_added: 1.0.0
description: 'Create, Update, Delete vpcs'
options:
nutanix_host:
description:
- PC hostname or IP address
type: str
required: true
nutanix_port:
description:
- PC port
type: str
default: 9440
required: false
nutanix_username:
description:
- PC username
type: str
required: true
nutanix_password:
description:
- PC password;
required: true
type: str
validate_certs:
description:
- Set value to C(False) to skip validation for self signed certificates
- This is not recommended for production setup
type: bool
default: true
state:
description:
- Specify state of vpc
- If C(state) is set to C(present) then vpc is created.
- >-
If C(state) is set to C(absent) and if the vpc exists, then
vpc is removed.
choices:
- present
- absent
type: str
default: present
wait:
description: Wait for vpc CRUD operation to complete.
type: bool
required: false
default: True
name:
description: vpc Name
type: str
vpc_uuid:
description: vpc uuid
type: str
dns_servers:
description: List of DNS servers IPs
type: list
elements: str
routable_ips:
description: Address space within the VPC which can talk externally without NAT. These are in effect when No-NAT External subnet is used.
type: list
elements: dict
suboptions:
network_ip:
description: ip address
type: str
network_prefix:
description: Subnet ip address prefix length
type: int
external_subnets:
description: A subnet with external connectivity
type: list
elements: dict
suboptions:
subnet_uuid:
description: Subnet UUID
type: str
subnet_name:
description: Subnet Name
type: str
author:
- Prem Karat (@premkarat)
- Gevorg Khachatryan (@Gevorg-Khachatryan-97)
- Alaa Bishtawi (@alaa-bish)
- Dina AbuHijleh (@dina-abuhijleh)
"""

EXAMPLES = r"""
# TODO
"""

RETURN = r"""
# TODO
"""

from ..module_utils.base_module import BaseModule # noqa: E402
from ..module_utils.prism.tasks import Task # noqa: E402
from ..module_utils.prism.vpcs import Vpc # noqa: E402
from ..module_utils.utils import remove_param_with_none_value # noqa: E402


def get_module_spec():
external_subnets_spec = dict(
subnet_name=dict(type="str"), subnet_uuid=dict(type="str")
)
routable_ips_spec = dict(
network_ip=dict(type="str"), network_prefix=dict(type="int")
)
module_args = dict(
name=dict(type="str"),
vpc_uuid=dict(type="str"),
external_subnets=dict(
type="list",
elements="dict",
options=external_subnets_spec,
mutually_exclusive=[("subnet_name", "subnet_uuid")],
),
routable_ips=dict(type="list", elements="dict", options=routable_ips_spec),
dns_servers=dict(type="list", elements="str"),
)

return module_args


def create_vpc(module, result):
vpc = Vpc(module)
spec, error = vpc.get_spec()
if error:
result["error"] = error
module.fail_json(msg="Failed generating vpc spec", **result)

if module.check_mode:
result["response"] = spec
return

resp, status = vpc.create(spec)
if status["error"]:
result["error"] = status["error"]
result["response"] = resp
module.fail_json(msg="Failed creating vpc", **result)

vpc_uuid = resp["metadata"]["uuid"]
result["changed"] = True
result["response"] = resp
result["vpc_uuid"] = vpc_uuid
result["task_uuid"] = resp["status"]["execution_context"]["task_uuid"]

if module.params.get("wait"):
wait_for_task_completion(module, result)
resp, tmp = vpc.read(vpc_uuid)
result["response"] = resp


def delete_vpc(module, result):
vpc_uuid = module.params["vpc_uuid"]
if not vpc_uuid:
result["error"] = "Missing parameter vpc_uuid in playbook"
module.fail_json(msg="Failed deleting vpc", **result)

vpc = Vpc(module)
resp, status = vpc.delete(vpc_uuid)
if status["error"]:
result["error"] = status["error"]
result["response"] = resp
module.fail_json(msg="Failed deleting vpc", **result)

result["changed"] = True
result["response"] = resp
result["vpc_uuid"] = vpc_uuid
result["task_uuid"] = resp["status"]["execution_context"]["task_uuid"]

if module.params.get("wait"):
wait_for_task_completion(module, result)


def wait_for_task_completion(module, result):
task = Task(module)
task_uuid = result["task_uuid"]
resp, status = task.wait_for_completion(task_uuid)
result["response"] = resp
if status["error"]:
result["error"] = status["error"]
result["response"] = resp
module.fail_json(msg="Failed creating vpc", **result)


def run_module():
module = BaseModule(
argument_spec=get_module_spec(),
supports_check_mode=True,
required_if=[
("state", "present", ("name",)),
("state", "absent", ("vpc_uuid",)),
],
)
remove_param_with_none_value(module.params)
result = {
"changed": False,
"error": None,
"response": None,
"vpc_uuid": None,
"task_uuid": None,
}
state = module.params["state"]
if state == "present":
create_vpc(module, result)
elif state == "absent":
delete_vpc(module, result)

module.exit_json(**result)


def main():
run_module()


if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions scripts/create_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,14 @@ def _get_default_spec(self):


def create_module(name):
with open("plugins/modules/ntnx_{0}s.py".format(name), "w") as f:
with open("plugins/modules/ntnx_{0}s.py".format(name), "wb") as f:
f.write(
module_content.replace("object", name.lower()).replace(
"Object", name.capitalize()
)
)

with open("plugins/module_utils/prism/{0}s.py".format(name), "w") as f:
with open("plugins/module_utils/prism/{0}s.py".format(name), "wb") as f:
f.write(
object_content.replace("object", name.lower()).replace(
"Object", name.capitalize()
Expand Down
Loading

0 comments on commit 0480653

Please sign in to comment.