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

Enables pure IPv6 configurations for nd_setup module on ND version 3.0.1 and later (DCNE-241) #108

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions plugins/module_utils/nd_argument_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ def ntp_keys_spec():

def network_spec(vlan=False):
spec = dict(
ipv4_address=dict(type="str", aliases=["ip"], required=True),
ipv4_gateway=dict(type="str", aliases=["gateway"], required=True),
ipv4_address=dict(type="str", aliases=["ip"]),
ipv4_gateway=dict(type="str", aliases=["gateway"]),
ipv6_address=dict(type="str"),
ipv6_gateway=dict(type="str"),
)
Expand Down
68 changes: 57 additions & 11 deletions plugins/modules/nd_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@
description:
- The management IP address of the node.
type: str
required: true
username:
description:
- The username of the node.
Expand All @@ -185,13 +184,11 @@
description:
- The IPv4 address of the management network.
type: str
required: true
aliases: [ ip ]
ipv4_gateway:
description:
- The IPv4 gateway of the management network.
type: str
required: true
aliases: [ gateway ]
ipv6_address:
description:
Expand All @@ -211,13 +208,11 @@
description:
- The IPv4 address of the data network.
type: str
required: true
aliases: [ ip ]
ipv4_gateway:
description:
- The IPv4 gateway of the data network.
type: str
required: true
aliases: [ gateway ]
ipv6_address:
description:
Expand Down Expand Up @@ -270,7 +265,7 @@
- This option is only applicable for ND version 3.1.1 and later.
type: list
elements: str
choices: [ ndo, ndfc, ndi ]
choices: [ ndo, ndfc, ndi-virtual, ndi-physical ]
aliases: [ mode ]
external_services:
description:
Expand Down Expand Up @@ -357,11 +352,56 @@
from ansible_collections.cisco.nd.plugins.module_utils.constants import ND_SETUP_NODE_ROLE_MAPPING


def check_network_requirements(nd, version, nodes, internal_network_ipv4, internal_network_ipv6):
if version >= "3.0.1":
# checking minimum requirements for internal network
if not all(internal_network_ipv4) and not all(internal_network_ipv6):
nd.fail_json(msg="Application and service network addresses, IPv4 or IPv6, are required during ND setup.")
# Conditions for Dual Stack configuration for internal network
elif all(internal_network_ipv4) and any(internal_network_ipv6) and not all(internal_network_ipv6):
nd.fail_json(
msg="For a dual stack configuration, application and service network IPv6 addresses are required. Otherwise, the extra address must be removed."
)
elif all(internal_network_ipv6) and any(internal_network_ipv4) and not all(internal_network_ipv4):
nd.fail_json(
msg="For a dual stack configuration, application and service network IPv4 addresses are required. Otherwise, the extra address must be removed."
)
for node in nodes:
for network in ["data_network", "management_network"]:
network_ipv4_config = [node[network].get(ip) for ip in ["ipv4_address", "ipv4_gateway"]]
network_ipv6_config = [node[network].get(ip) for ip in ["ipv6_address", "ipv6_gateway"]]
# checking minimum requirements for external network
if not all(network_ipv4_config) and not all(network_ipv6_config):
nd.fail_json(msg="A complete IPv4 subnet/gateway or IPv6 subnet/gateway configuration is required in node's {0}.".format(network))
# Conditions for Dual Stack configuration for external network
elif all(network_ipv4_config) and any(network_ipv6_config) and not all(network_ipv6_config):
nd.fail_json(
msg="For a dual stack configuration,"
/ " a complete IPv6 subnet/gateway configuration in node's {0} must be provided.".format(network)
/ " Otherwise, the extra address must be removed."
)
elif all(network_ipv6_config) and any(network_ipv4_config) and not all(network_ipv4_config):
nd.fail_json(
msg="For a dual stack configuration,"
/ " a complete IPv4 subnet/gateway configuration in node's {0} must be provided.".format(network)
/ " Otherwise, the extra address must be removed."
)
else:
# checking minimum requirements for internal network
if not all(internal_network_ipv4):
nd.fail_json(msg="Application and service network IPv4 addresses are required during ND setup.")
# checking minimum requirements for external network
for node in nodes:
for network in ["data_network", "management_network"]:
if not all(node[network].get(ip) for ip in ["ipv4_address", "ipv4_gateway"]):
nd.fail_json(msg="A complete IPv4 subnet/gateway configuration is required in node's {0}.".format(network))


def main():
argument_spec = nd_argument_spec()
argument_spec.update(
cluster_name=dict(type="str"),
deployment_mode=dict(type="list", elements="str", choices=["ndo", "ndfc", "ndi"], aliases=["mode"]),
deployment_mode=dict(type="list", elements="str", choices=["ndo", "ndfc", "ndi-virtual", "ndi-physical"], aliases=["mode"]),
external_services=dict(
type="dict",
options=dict(
Expand Down Expand Up @@ -394,7 +434,7 @@ def main():
hostname=dict(type="str", required=True),
serial_number=dict(type="str", required=True),
role=dict(type="str", default="primary", choices=["primary", "secondary", "standby"], aliases=["type"]),
management_ip_address=dict(type="str", required=True),
management_ip_address=dict(type="str"),
username=dict(type="str", required=True),
password=dict(type="str", required=True, no_log=True),
management_network=dict(type="dict", required=True, options=network_spec()),
Expand All @@ -409,7 +449,7 @@ def main():
argument_spec=argument_spec,
supports_check_mode=True,
required_if=[
["state", "present", ["cluster_name", "dns_server", "app_network", "service_network", "nodes"]],
["state", "present", ["cluster_name", "dns_server", "nodes"]],
],
required_together=[
["proxy_username", "proxy_password"],
Expand Down Expand Up @@ -442,6 +482,12 @@ def main():
if state == "query":
nd.existing = nd.request("/clusterstatus/install", method="GET")
else:
nd_version = nd.query_obj("/version.json")
nd_version = ".".join(str(nd_version[key]) for key in ["major", "minor", "maintenance"])
# Checking internal and external network requirements
check_network_requirements(nd, nd_version, nodes, (app_network, service_network), (app_network_ipv6, service_network_ipv6))

# Checking cluster name validation
if len(cluster_name) > 63:
nd.fail_json("A length of 1 to 63 characters is allowed.")
elif len(re.findall(r"[^a-zA-Z0-9-]", cluster_name)) > 0:
Expand Down Expand Up @@ -520,9 +566,9 @@ def main():
}

# Deployment mode options introduced in ND version 3.1.1
if isinstance(deployment_mode, list):
if isinstance(deployment_mode, list) and nd_version >= "3.1.1":
payload["clusterConfig"]["deploymentMode"] = deployment_mode if len(deployment_mode) > 1 else deployment_mode[0]
if external_services is not None and any(service in {"ndi", "ndfc"} for service in deployment_mode):
if external_services is not None and any(service in {"ndi-virtual", "ndi-physical", "ndfc"} for service in deployment_mode):
payload["clusterConfig"]["externalServices"] = []
if external_services.get("management_service_ips") is not None:
payload["clusterConfig"]["externalServices"].append(
Expand Down
159 changes: 159 additions & 0 deletions tests/integration/targets/nd_setup/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,70 @@
ignore_errors: true
register: cluster_name_invalid_hyphen

- name: Install ND without complete node's management network
cisco.nd.nd_setup:
output_level: debug
cluster_name: cluster-one
dns_server: "{{ dns_server }}"
dns_search_domain: cisco.com
app_network: "{{ app_network }}"
service_network: "{{ service_network }}"
ntp_config:
servers:
- ntp_host: "{{ ntp_server }}"
ntp_key_id: 1
preferred: true
keys:
- ntp_key_id: 1
ntp_key: "ntp_secure_key"
authentication_type: "AES128CMAC"
trusted: true
nodes:
- hostname: Test
serial_number: "{{ serial_number }}"
management_ip_address: "{{ management_ip_address }}"
username: "{{ deployment_username | default('rescue-user') }}"
password: "{{ deployment_password }}"
management_network:
ipv4_address: "{{ management_ip }}"
data_network:
ipv4_address: "{{ data_ip }}"
ipv4_gateway: "{{ data_gateway }}"
ignore_errors: true
register: cluster_invalid_node_management_network

- name: Install ND without application network
cisco.nd.nd_setup:
output_level: debug
cluster_name: cluster-one
dns_server: "{{ dns_server }}"
dns_search_domain: cisco.com
service_network: "{{ service_network }}"
ntp_config:
servers:
- ntp_host: "{{ ntp_server }}"
ntp_key_id: 1
preferred: true
keys:
- ntp_key_id: 1
ntp_key: "ntp_secure_key"
authentication_type: "AES128CMAC"
trusted: true
nodes:
- hostname: Test
serial_number: "{{ serial_number }}"
management_ip_address: "{{ management_ip_address }}"
username: "{{ deployment_username | default('rescue-user') }}"
password: "{{ deployment_password }}"
management_network:
ipv4_address: "{{ management_ip }}"
ipv4_gateway: "{{ management_gateway }}"
data_network:
ipv4_address: "{{ data_ip }}"
ipv4_gateway: "{{ data_gateway }}"
ignore_errors: true
register: cluster_invalid_application_network

- name: Install ND in normal mode
cisco.nd.nd_setup:
output_level: debug
Expand Down Expand Up @@ -168,6 +232,8 @@
- cluster_name_length_error.msg == "A length of 1 to 63 characters is allowed."
- cluster_name_invalid_chars.msg == "Valid characters include letters, digits and hyphen."
- cluster_name_invalid_hyphen.msg == "The name cannot start or end with a hyphen."
- cluster_invalid_node_management_network.msg == "A complete IPv4 subnet/gateway configuration is required in node's management_network."
- cluster_invalid_application_network.msg == "Application and service network IPv4 addresses are required during ND setup."
- cluster_cm.current.clusterConfig.appNetwork == "{{ app_network }}"
- cluster_cm.current.clusterConfig.nameServers.0 == "{{ dns_server }}"
- cluster_cm.current.clusterConfig.ntpConfig.servers.0.host == "{{ ntp_server }}"
Expand Down Expand Up @@ -265,6 +331,93 @@
cluster_name: clusterone-
ignore_errors: true
register: cluster_name_invalid_hyphen

- name: Install ND without complete node's management network
cisco.nd.nd_setup:
<<: *nd_setup_test_config
nodes:
- hostname: Test
serial_number: "{{ serial_number }}"
role: primary
management_ip_address: "{{ management_ip_address }}"
username: "{{ deployment_username | default('rescue-user') }}"
password: "{{ deployment_password }}"
management_network:
ipv4_address: "{{ management_ip }}"
data_network:
ipv4_address: "{{ data_ip }}"
ipv4_gateway: "{{ data_gateway }}"
ignore_errors: true
register: cluster_invalid_node_management_network

- name: Install ND without complete dual stack ipv6 configurations in node management network
cisco.nd.nd_setup:
<<: *nd_setup_test_config
nodes:
- hostname: Test
serial_number: "{{ serial_number }}"
role: primary
management_ip_address: "{{ management_ip_address }}"
username: "{{ deployment_username | default('rescue-user') }}"
password: "{{ deployment_password }}"
management_network:
ipv4_address: "{{ management_ip }}"
ipv4_gateway: "{{ management_gateway }}"
ipv6_address: "{{ management_ipv6 }}"
data_network:
ipv4_address: "{{ data_ip }}"
ipv4_gateway: "{{ data_gateway }}"
ipv6_address: "{{ data_ipv6 }}"
ipv6_gateway: "{{ data_gateway_ipv6 }}"
ignore_errors: true
register: cluster_invalid_dual_stack_node_management_network_v6

- name: Install ND without complete dual stack ipv4 configurations in node management network
cisco.nd.nd_setup:
<<: *nd_setup_test_config
nodes:
- hostname: Test
serial_number: "{{ serial_number }}"
role: primary
management_ip_address: "{{ management_ip_address }}"
username: "{{ deployment_username | default('rescue-user') }}"
password: "{{ deployment_password }}"
management_network:
ipv4_address: "{{ management_ip }}"
ipv6_address: "{{ management_ipv6 }}"
ipv6_gateway: "{{ management_gateway_ipv6 }}"
data_network:
ipv4_address: "{{ data_ip }}"
ipv4_gateway: "{{ data_gateway }}"
ipv6_address: "{{ data_ipv6 }}"
ipv6_gateway: "{{ data_gateway_ipv6 }}"
ignore_errors: true
register: cluster_invalid_dual_stack_node_management_network_v4

- name: Install ND without any application network addresses
cisco.nd.nd_setup:
<<: *nd_setup_test_config
app_network: null
ignore_errors: true
register: cluster_invalid_application_network

- name: Install ND without complete dual stack ipv6 configurations in internal network
cisco.nd.nd_setup:
<<: *nd_setup_test_config
app_network: "{{ app_network }}"
service_network: "{{ service_network }}"
service_network_ipv6: "{{ service_network_ipv6 }}"
ignore_errors: true
register: cluster_invalid_dual_stack_application_network_v6

- name: Install ND without complete dual stack ipv4 configurations in internal network
cisco.nd.nd_setup:
<<: *nd_setup_test_config
service_network: "{{ service_network }}"
app_network_ipv6: "{{ app_network_ipv6 }}"
service_network_ipv6: "{{ service_network_ipv6 }}"
ignore_errors: true
register: cluster_invalid_dual_stack_application_network_v4

- name: Install ND in normal mode
cisco.nd.nd_setup:
Expand All @@ -290,6 +443,12 @@
- cluster_name_length_error.msg == "A length of 1 to 63 characters is allowed."
- cluster_name_invalid_chars.msg == "Valid characters include letters, digits and hyphen."
- cluster_name_invalid_hyphen.msg == "The name cannot start or end with a hyphen."
- cluster_invalid_node_management_network.msg == "A complete IPv4 subnet/gateway or IPv6 subnet/gateway configuration is required in node's management_network."
- cluster_invalid_dual_stack_node_management_network_v6 == "For a dual stack configuration, a complete IPv6 subnet/gateway configuration in node's management_network must be provided. Otherwise, the extra address must be removed."
- cluster_invalid_dual_stack_node_management_network_v4 == "For a dual stack configuration, a complete IPv4 subnet/gateway configuration in node's management_network must be provided. Otherwise, the extra address must be removed."
- cluster_invalid_application_network.msg == "Application and service network addresses, IPv4 or IPv6, are required during ND setup."
- cluster_invalid_dual_stack_application_network_v6.msg == "For a dual stack configuration, application and service network IPv6 addresses are required. Otherwise, the extra address must be removed."
- cluster_invalid_dual_stack_application_network_v4.msg == "For a dual stack configuration, application and service network IPv4 addresses are required. Otherwise, the extra address must be removed."
- cluster_cm.current.clusterConfig.appNetwork == "{{ app_network }}"
- cluster_cm.current.clusterConfig.nameServers.0 == "{{ dns_server }}"
- cluster_cm.current.clusterConfig.ntpConfig.servers.0.host == "{{ ntp_server }}"
Expand Down
Loading