From cb1db84a17cb469ef42db6d763ae991bfc4b15ac Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Tue, 3 Dec 2024 08:25:48 -0500 Subject: [PATCH 1/4] [minor_change] Enable the possibility to support pure IPv6 configurations for nd_setup module on nd version 3.0.1 and later. --- plugins/module_utils/nd_argument_specs.py | 4 +- plugins/modules/nd_setup.py | 60 ++++++++++++------- .../targets/nd_setup/tasks/main.yml | 19 ++++++ 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/plugins/module_utils/nd_argument_specs.py b/plugins/module_utils/nd_argument_specs.py index 93f8150..7ef10d0 100644 --- a/plugins/module_utils/nd_argument_specs.py +++ b/plugins/module_utils/nd_argument_specs.py @@ -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"), ) diff --git a/plugins/modules/nd_setup.py b/plugins/modules/nd_setup.py index b1dd716..b010b93 100644 --- a/plugins/modules/nd_setup.py +++ b/plugins/modules/nd_setup.py @@ -185,13 +185,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: @@ -211,13 +209,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: @@ -270,7 +266,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: @@ -357,11 +353,21 @@ from ansible_collections.cisco.nd.plugins.module_utils.constants import ND_SETUP_NODE_ROLE_MAPPING +def check_network_requirements(nd, version, type, network): + if version >= "3.0.1": + if not all(network.get(ip) for ip in ["ipv6Subnet", "gatewayv6"]) and not all(network.get(ip) for ip in ["ipSubnet", "gateway"]): + nd.fail_json(msg="Missing IPv4 subnet/gateway or IPv6 subnet/gateway in node {0} configuration.".format(type)) + else: + if not all(network.get(ip) for ip in ["ipSubnet", "gateway"]): + nd.fail_json(msg="Missing IPv4 subnet/gateway in node {0} configuration.".format(type)) + return 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( @@ -442,6 +448,8 @@ 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"]) 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: @@ -492,19 +500,29 @@ def main(): "hostName": node.get("hostname"), "serialNumber": node.get("serial_number"), "role": ND_SETUP_NODE_ROLE_MAPPING.get(node.get("role")), - "dataNetwork": { - "ipSubnet": node["data_network"].get("ipv4_address"), - "gateway": node["data_network"].get("ipv4_gateway"), - "ipv6Subnet": node["data_network"].get("ipv6_address"), - "gatewayv6": node["data_network"].get("ipv6_gateway"), - "vlan": node["data_network"].get("vlan"), - }, - "managementNetwork": { - "ipSubnet": node["management_network"].get("ipv4_address"), - "gateway": node["management_network"].get("ipv4_gateway"), - "ipv6Subnet": node["management_network"].get("ipv6_address"), - "gatewayv6": node["management_network"].get("ipv6_gateway"), - }, + "dataNetwork": check_network_requirements( + nd, + nd_version, + "data network", + { + "ipSubnet": node["data_network"].get("ipv4_address"), + "gateway": node["data_network"].get("ipv4_gateway"), + "ipv6Subnet": node["data_network"].get("ipv6_address"), + "gatewayv6": node["data_network"].get("ipv6_gateway"), + "vlan": node["data_network"].get("vlan"), + }, + ), + "managementNetwork": check_network_requirements( + nd, + nd_version, + "management network", + { + "ipSubnet": node["management_network"].get("ipv4_address"), + "gateway": node["management_network"].get("ipv4_gateway"), + "ipv6Subnet": node["management_network"].get("ipv6_address"), + "gatewayv6": node["management_network"].get("ipv6_gateway"), + }, + ), "bgpConfig": { "as": node.get("bgp").get("asn") if node.get("bgp") is not None else None, "peers": node.get("bgp").get("peers") if node.get("bgp") is not None else None, @@ -520,9 +538,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( diff --git a/tests/integration/targets/nd_setup/tasks/main.yml b/tests/integration/targets/nd_setup/tasks/main.yml index 85bde61..341d640 100644 --- a/tests/integration/targets/nd_setup/tasks/main.yml +++ b/tests/integration/targets/nd_setup/tasks/main.yml @@ -265,6 +265,24 @@ cluster_name: clusterone- ignore_errors: true register: cluster_name_invalid_hyphen + + - name: Install ND without management network ipv4 address + 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_gateway: "{{ management_gateway }}" + data_network: + ipv4_address: "{{ data_ip }}" + ipv4_gateway: "{{ data_gateway }}" + ignore_errors: true + register: cluster_invalid_node_management_network - name: Install ND in normal mode cisco.nd.nd_setup: @@ -290,6 +308,7 @@ - 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 == "Missing IPv4 subnet/gateway or IPv6 subnet/gateway in node management network configuration." - 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 }}" From 20fee0111f71fe4ba73ed6bd618720c4710a865f Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Tue, 3 Dec 2024 11:21:52 -0500 Subject: [PATCH 2/4] [ignore] Enables flexible requirements during ND setup for app and service network for both IPv4 and IPv6 addresses. --- plugins/modules/nd_setup.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/modules/nd_setup.py b/plugins/modules/nd_setup.py index b010b93..c17ac9d 100644 --- a/plugins/modules/nd_setup.py +++ b/plugins/modules/nd_setup.py @@ -164,7 +164,6 @@ description: - The management IP address of the node. type: str - required: true username: description: - The username of the node. @@ -400,7 +399,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()), @@ -415,7 +414,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"], @@ -450,6 +449,12 @@ def main(): else: nd_version = nd.query_obj("/version.json") nd_version = ".".join(str(nd_version[key]) for key in ["major", "minor", "maintenance"]) + if nd_version >= "3.0.1": + if not all((app_network, service_network)) and not all((app_network_ipv6, service_network_ipv6)): + nd.fail_json(msg="Application and service network addresses, IPv4 or IPv6, are required during ND setup.") + else: + if not all((app_network, service_network)): + nd.fail_json(msg="Application and service network IPv4 addresses are required during ND setup.") 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: From 3fcbb58f554826b1deca3a2dba7aefd49e9cb7e3 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Tue, 10 Dec 2024 06:09:06 -0500 Subject: [PATCH 3/4] [ignore] Set checking network requirements process before payload initialization. Add conditions for Dual Stack configuration for version 3.0.1 or later. --- plugins/modules/nd_setup.py | 93 +++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/plugins/modules/nd_setup.py b/plugins/modules/nd_setup.py index c17ac9d..5f5c6c8 100644 --- a/plugins/modules/nd_setup.py +++ b/plugins/modules/nd_setup.py @@ -352,14 +352,49 @@ from ansible_collections.cisco.nd.plugins.module_utils.constants import ND_SETUP_NODE_ROLE_MAPPING -def check_network_requirements(nd, version, type, network): +def check_network_requirements(nd, version, nodes, internal_network_ipv4, internal_network_ipv6): if version >= "3.0.1": - if not all(network.get(ip) for ip in ["ipv6Subnet", "gatewayv6"]) and not all(network.get(ip) for ip in ["ipSubnet", "gateway"]): - nd.fail_json(msg="Missing IPv4 subnet/gateway or IPv6 subnet/gateway in node {0} configuration.".format(type)) + # 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 fo 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 one 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 one 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 fo 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 one 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 one must be removed" + ) else: - if not all(network.get(ip) for ip in ["ipSubnet", "gateway"]): - nd.fail_json(msg="Missing IPv4 subnet/gateway in node {0} configuration.".format(type)) - return network + # 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(): @@ -449,12 +484,10 @@ def main(): else: nd_version = nd.query_obj("/version.json") nd_version = ".".join(str(nd_version[key]) for key in ["major", "minor", "maintenance"]) - if nd_version >= "3.0.1": - if not all((app_network, service_network)) and not all((app_network_ipv6, service_network_ipv6)): - nd.fail_json(msg="Application and service network addresses, IPv4 or IPv6, are required during ND setup.") - else: - if not all((app_network, service_network)): - nd.fail_json(msg="Application and service network IPv4 addresses are required during ND setup.") + # 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: @@ -505,29 +538,19 @@ def main(): "hostName": node.get("hostname"), "serialNumber": node.get("serial_number"), "role": ND_SETUP_NODE_ROLE_MAPPING.get(node.get("role")), - "dataNetwork": check_network_requirements( - nd, - nd_version, - "data network", - { - "ipSubnet": node["data_network"].get("ipv4_address"), - "gateway": node["data_network"].get("ipv4_gateway"), - "ipv6Subnet": node["data_network"].get("ipv6_address"), - "gatewayv6": node["data_network"].get("ipv6_gateway"), - "vlan": node["data_network"].get("vlan"), - }, - ), - "managementNetwork": check_network_requirements( - nd, - nd_version, - "management network", - { - "ipSubnet": node["management_network"].get("ipv4_address"), - "gateway": node["management_network"].get("ipv4_gateway"), - "ipv6Subnet": node["management_network"].get("ipv6_address"), - "gatewayv6": node["management_network"].get("ipv6_gateway"), - }, - ), + "dataNetwork": { + "ipSubnet": node["data_network"].get("ipv4_address"), + "gateway": node["data_network"].get("ipv4_gateway"), + "ipv6Subnet": node["data_network"].get("ipv6_address"), + "gatewayv6": node["data_network"].get("ipv6_gateway"), + "vlan": node["data_network"].get("vlan"), + }, + "managementNetwork": { + "ipSubnet": node["management_network"].get("ipv4_address"), + "gateway": node["management_network"].get("ipv4_gateway"), + "ipv6Subnet": node["management_network"].get("ipv6_address"), + "gatewayv6": node["management_network"].get("ipv6_gateway"), + }, "bgpConfig": { "as": node.get("bgp").get("asn") if node.get("bgp") is not None else None, "peers": node.get("bgp").get("peers") if node.get("bgp") is not None else None, From fc0e13e8c3d007e3e5650afd028314c65f128713 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Mon, 16 Dec 2024 10:06:08 -0500 Subject: [PATCH 4/4] [ignore] Add negative tests for IPv4/IPv6 configuration requirements. --- plugins/modules/nd_setup.py | 12 +- .../targets/nd_setup/tasks/main.yml | 146 +++++++++++++++++- 2 files changed, 149 insertions(+), 9 deletions(-) diff --git a/plugins/modules/nd_setup.py b/plugins/modules/nd_setup.py index 5f5c6c8..c195a18 100644 --- a/plugins/modules/nd_setup.py +++ b/plugins/modules/nd_setup.py @@ -357,14 +357,14 @@ def check_network_requirements(nd, version, nodes, internal_network_ipv4, intern # 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 fo Dual Stack configuration for internal network + # 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 one must be removed." + 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 one must be removed." + 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"]: @@ -373,18 +373,18 @@ def check_network_requirements(nd, version, nodes, internal_network_ipv4, intern # 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 fo Dual Stack configuration for external 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 one must be removed" + / " 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 one must be removed" + / " Otherwise, the extra address must be removed." ) else: # checking minimum requirements for internal network diff --git a/tests/integration/targets/nd_setup/tasks/main.yml b/tests/integration/targets/nd_setup/tasks/main.yml index 341d640..92814a5 100644 --- a/tests/integration/targets/nd_setup/tasks/main.yml +++ b/tests/integration/targets/nd_setup/tasks/main.yml @@ -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 @@ -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 }}" @@ -266,7 +332,7 @@ ignore_errors: true register: cluster_name_invalid_hyphen - - name: Install ND without management network ipv4 address + - name: Install ND without complete node's management network cisco.nd.nd_setup: <<: *nd_setup_test_config nodes: @@ -277,12 +343,81 @@ username: "{{ deployment_username | default('rescue-user') }}" password: "{{ deployment_password }}" management_network: - ipv4_gateway: "{{ management_gateway }}" + 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: @@ -308,7 +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 == "Missing IPv4 subnet/gateway or IPv6 subnet/gateway in node management network configuration." + - 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 }}"