From fc8c161df644768f9ef51a9d23fc17ef44f46917 Mon Sep 17 00:00:00 2001 From: Lucas Ratusznei Fonseca Date: Thu, 11 Apr 2024 19:29:08 -0300 Subject: [PATCH] Add dual stack support to the platform firewall This change updates the firewall configuration generation to take into account that a network can have more than one address pool associated to it. More tests were added to address dual stack setups. Test plan ========= Online setup tests ------------------ System: - AIO-DX - STANDARD (2 Controllers, 2 Storages, 1 Compute) Acceptance criteria: For all the platform interfaces, incoming ICMP, TCP and UDP traffic is allowed only for networks/ports that are configured in the associated address pools. [PASS] TC1 - Install IPv4, add IPv6 pools to the platform networks [PASS] TC2 - Install IPv6, add IPv4 pools to the platform networks Installation tests ------------------ Systems: AIO-SX, AIO-DX, STANDARD [PASS] TC3 - Regular installation on VirtualBox, IPv4 [PASS] TC4 - Regular installation on VirtualBox, IPv6 Related changes: - https://review.opendev.org/c/starlingx/stx-puppet/+/915509 - https://review.opendev.org/c/starlingx/ansible-playbooks/+/915510 Story: 2011027 Task: 49816 Depends-On: https://review.opendev.org/c/starlingx/config/+/914141 Change-Id: Id05a583e7fd806a6ea448ac5a521902b2c7e96e4 Signed-off-by: Lucas Ratusznei Fonseca --- .../sysinv/sysinv/sysinv/db/sqlalchemy/api.py | 11 + .../sysinv/sysinv/puppet/platform_firewall.py | 490 ++++---- .../tests/puppet/test_platform_firewall.py | 1112 +++++++++++++---- 3 files changed, 1149 insertions(+), 464 deletions(-) diff --git a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py index 9705edb77c..17778c354e 100644 --- a/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py +++ b/sysinv/sysinv/sysinv/sysinv/db/sqlalchemy/api.py @@ -5905,6 +5905,17 @@ class Connection(api.Connection): ) return result + @db_objects.objectify(objects.address_pool) + def address_pools_get_by_network(self, network_id, + limit=None, marker=None, + sort_key=None, sort_dir=None): + query = model_query(models.AddressPools) + query = (query.join(models.NetworkAddressPools, + models.NetworkAddressPools.address_pool_id == models.AddressPools.id)) + query = query.filter(models.NetworkAddressPools.network_id == network_id) + return _paginate_query(models.AddressPools, limit, marker, + sort_key, sort_dir, query) + def address_pool_destroy(self, address_pool_uuid): query = model_query(models.AddressPools) query = add_identity_filter(query, address_pool_uuid) diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/platform_firewall.py b/sysinv/sysinv/sysinv/sysinv/puppet/platform_firewall.py index 7ed3e73bb3..1bdd6996fe 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/platform_firewall.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/platform_firewall.py @@ -6,7 +6,6 @@ import copy -from netaddr import IPAddress from oslo_log import log from sysinv.common import constants from sysinv.common import exception @@ -248,14 +247,9 @@ class PlatformFirewallPuppet(base.BasePuppet): for network in firewall_networks: gnp_name = host.personality + "-" + network.type + "-if-gnp" - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version nodetype_selector = f"has(nodetype) && nodetype == '{host.personality}'" iftype_selector = f"has(iftype) && iftype contains '{network.type}'" selector = f"{nodetype_selector} && {iftype_selector}" - ICMP = "ICMP" - if (ip_version == 6): - ICMP = "ICMPv6" firewall_gnp = dict() firewall_gnp["apiVersion"] = "crd.projectcalico.org/v1" @@ -270,13 +264,20 @@ class PlatformFirewallPuppet(base.BasePuppet): firewall_gnp["spec"].update({"egress": list()}) firewall_gnp["spec"].update({"ingress": list()}) - for proto in ["TCP", "UDP", ICMP]: - self._add_gnp_proto_rules(host, network, ip_version, firewall_gnp, proto) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + ICMP = "ICMP" + if ip_version == 6: + ICMP = "ICMPv6" - if network.type in IPSEC_NETWORKS: - self._add_gnp_proto_rules(host, network, ip_version, firewall_gnp, "ESP", 50) + for proto in ["TCP", "UDP", ICMP]: + self._add_gnp_proto_rules(host, network, ip_version, firewall_gnp, proto) - config[PLATFORM_FIREWALL_CLASSES[network.type]] = copy.copy(firewall_gnp) + if network.type in IPSEC_NETWORKS: + self._add_gnp_proto_rules(host, network, ip_version, firewall_gnp, "ESP", 50) + + config[PLATFORM_FIREWALL_CLASSES[network.type]] = firewall_gnp def _set_rules_oam(self, gnp_config, network, host, dc_role): """ Fill the OAM network specific filtering data @@ -317,15 +318,21 @@ class PlatformFirewallPuppet(base.BasePuppet): elif rule["protocol"] == "UDP": rule.update({"destination": {"ports": udp_ports}}) - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - self._add_destination_net_filter(gnp_config["spec"]["ingress"], - f"{addr_pool.network}/{addr_pool.prefix}") - if (ip_version == 6): - for rule in gnp_config["spec"]["ingress"]: - if rule["protocol"] == "ICMPv6": - rule["destination"]["nets"].append(LINK_LOCAL) - rule["destination"]["nets"].append(LINK_LOCAL_MC) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + self._add_destination_net_filter(gnp_config["spec"]["ingress"], + f"{addr_pool.network}/{addr_pool.prefix}") + if (ip_version == constants.IPV6_FAMILY): + for rule in gnp_config["spec"]["ingress"]: + if rule["protocol"] == "ICMPv6": + rule["destination"]["nets"].append(LINK_LOCAL) + rule["destination"]["nets"].append(LINK_LOCAL_MC) + + def _copy_tcp_rule(self, config, direction, ip_version): + for rule in config[direction]: + if rule["protocol"] == "TCP" and rule["ipVersion"] == ip_version: + return copy.deepcopy(rule) def _set_rules_mgmt(self, gnp_config, network, host): """ Fill the management network specific filtering data @@ -333,33 +340,36 @@ class PlatformFirewallPuppet(base.BasePuppet): :param gnp_config: the dict containing the hiera data to be filled :param network: the sysinv.object.network object for this network """ - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - self._add_source_net_filter(gnp_config["spec"]["ingress"], - f"{addr_pool.network}/{addr_pool.prefix}") - if (ip_version == 6): - self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL) - if (ip_version == 4): - # add rule to allow DHCP requests (dhcp-offer have src addr == 0.0.0.0) - # worker/storage nodes request IP dynamically - rule = self._get_dhcp_rule(host.personality, "UDP", ip_version) - gnp_config["spec"]["ingress"].append(rule) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + self._add_source_net_filter(gnp_config["spec"]["ingress"], + f"{addr_pool.network}/{addr_pool.prefix}", ip_version) - # copy the TCP rule and do the same for IGMP - igmp_proto = 2 - igmp_egr_rule = copy.deepcopy(gnp_config["spec"]["egress"][0]) - igmp_egr_rule["protocol"] = igmp_proto - igmp_egr_rule["metadata"]["annotations"]["name"] = \ - f"stx-egr-{host.personality}-{network.type}-igmp{ip_version}" - gnp_config["spec"]["egress"].append(igmp_egr_rule) - igmp_ingr_rule = copy.deepcopy(gnp_config["spec"]["ingress"][0]) - igmp_ingr_rule["protocol"] = igmp_proto - igmp_ingr_rule["metadata"]["annotations"]["name"] = \ - f"stx-ingr-{host.personality}-{network.type}-igmp{ip_version}" - # Allow 0.0.0.0/32 for the case the switch sends IGMP queries from - # a VLAN without the IP address configured. - igmp_ingr_rule["source"]["nets"].append("0.0.0.0/32") - gnp_config["spec"]["ingress"].append(igmp_ingr_rule) + if (ip_version == 6): + self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL, ip_version) + + if (ip_version == 4): + # add rule to allow DHCP requests (dhcp-offer have src addr == 0.0.0.0) + # worker/storage nodes request IP dynamically + rule = self._get_dhcp_rule(host.personality, "UDP", constants.IPV4_FAMILY) + gnp_config["spec"]["ingress"].append(rule) + + # copy the TCP rule and do the same for IGMP + igmp_proto = 2 + igmp_egr_rule = self._copy_tcp_rule(gnp_config["spec"], "egress", ip_version) + igmp_egr_rule["protocol"] = igmp_proto + igmp_egr_rule["metadata"]["annotations"]["name"] = \ + f"stx-egr-{host.personality}-{network.type}-igmp{constants.IPV4_FAMILY}" + gnp_config["spec"]["egress"].append(igmp_egr_rule) + igmp_ingr_rule = self._copy_tcp_rule(gnp_config["spec"], "ingress", ip_version) + igmp_ingr_rule["protocol"] = igmp_proto + igmp_ingr_rule["metadata"]["annotations"]["name"] = \ + f"stx-ingr-{host.personality}-{network.type}-igmp{constants.IPV4_FAMILY}" + # Allow 0.0.0.0/32 for the case the switch sends IGMP queries from + # a VLAN without the IP address configured. + igmp_ingr_rule["source"]["nets"].append("0.0.0.0/32") + gnp_config["spec"]["ingress"].append(igmp_ingr_rule) def _set_rules_admin(self, gnp_config, network, host): """ Fill the admin network specific filtering data @@ -367,28 +377,29 @@ class PlatformFirewallPuppet(base.BasePuppet): :param gnp_config: the dict containing the hiera data to be filled :param network: the sysinv.object.network object for this network """ - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - self._add_source_net_filter(gnp_config["spec"]["ingress"], - f"{addr_pool.network}/{addr_pool.prefix}") - if (ip_version == 6): - self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL) - if (ip_version == 4): - # copy the TCP rule and do the same for IGMP - igmp_proto = 2 - igmp_egr_rule = copy.deepcopy(gnp_config["spec"]["egress"][0]) - igmp_egr_rule["protocol"] = igmp_proto - igmp_egr_rule["metadata"]["annotations"]["name"] = \ - f"stx-egr-{host.personality}-{network.type}-igmp{ip_version}" - gnp_config["spec"]["egress"].append(igmp_egr_rule) - igmp_ingr_rule = copy.deepcopy(gnp_config["spec"]["ingress"][0]) - igmp_ingr_rule["protocol"] = igmp_proto - igmp_ingr_rule["metadata"]["annotations"]["name"] = \ - f"stx-ingr-{host.personality}-{network.type}-igmp{ip_version}" - # Allow 0.0.0.0/32 for the case the switch sends IGMP queries from - # a VLAN without the IP address configured. - igmp_ingr_rule["source"]["nets"].append("0.0.0.0/32") - gnp_config["spec"]["ingress"].append(igmp_ingr_rule) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + self._add_source_net_filter(gnp_config["spec"]["ingress"], + f"{addr_pool.network}/{addr_pool.prefix}", ip_version) + if (ip_version == 6): + self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL, ip_version) + if (ip_version == 4): + # copy the TCP rule and do the same for IGMP + igmp_proto = 2 + igmp_egr_rule = self._copy_tcp_rule(gnp_config["spec"], "egress", ip_version) + igmp_egr_rule["protocol"] = igmp_proto + igmp_egr_rule["metadata"]["annotations"]["name"] = \ + f"stx-egr-{host.personality}-{network.type}-igmp{ip_version}" + gnp_config["spec"]["egress"].append(igmp_egr_rule) + igmp_ingr_rule = self._copy_tcp_rule(gnp_config["spec"], "ingress", ip_version) + igmp_ingr_rule["protocol"] = igmp_proto + igmp_ingr_rule["metadata"]["annotations"]["name"] = \ + f"stx-ingr-{host.personality}-{network.type}-igmp{ip_version}" + # Allow 0.0.0.0/32 for the case the switch sends IGMP queries from + # a VLAN without the IP address configured. + igmp_ingr_rule["source"]["nets"].append("0.0.0.0/32") + gnp_config["spec"]["ingress"].append(igmp_ingr_rule) def _set_rules_cluster_host(self, gnp_config, network, host): """ Fill the cluster-host network specific filtering data @@ -397,59 +408,64 @@ class PlatformFirewallPuppet(base.BasePuppet): :param network: the sysinv.object.network object for this network """ - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - self._add_source_net_filter(gnp_config["spec"]["ingress"], - f"{addr_pool.network}/{addr_pool.prefix}") - - # add cluster-pod to cover the cases where there is no tunneling, the pod traffic goes - # directly in the cluster-host interface + cpod_pool_index = {} cpod_net = self.dbapi.network_get_by_type(constants.NETWORK_TYPE_CLUSTER_POD) if cpod_net: - cpod_pool = self.dbapi.address_pool_get(cpod_net.pool_uuid) - cpod_ip_version = IPAddress(f"{cpod_pool.network}").version - if (cpod_ip_version == ip_version): - self._add_source_net_filter(gnp_config["spec"]["ingress"], - f"{cpod_pool.network}/{cpod_pool.prefix}") + cpod_pools = self.dbapi.address_pools_get_by_network(cpod_net.id) + for cpod_pool in cpod_pools: + cpod_pool_index[cpod_pool.family] = cpod_pool else: LOG.info("Cannot find cluster-pod network to add to cluster-host firewall") - # copy the TCP rule and do the same for SCTP - sctp_egr_rule = copy.deepcopy(gnp_config["spec"]["egress"][0]) - sctp_egr_rule["protocol"] = "SCTP" - sctp_egr_rule["metadata"]["annotations"]["name"] = \ - f"stx-egr-{host.personality}-{network.type}-sctp{ip_version}" - gnp_config["spec"]["egress"].append(sctp_egr_rule) - sctp_ingr_rule = copy.deepcopy(gnp_config["spec"]["ingress"][0]) - sctp_ingr_rule["protocol"] = "SCTP" - sctp_ingr_rule["metadata"]["annotations"]["name"] = \ - f"stx-ingr-{host.personality}-{network.type}-sctp{ip_version}" - gnp_config["spec"]["ingress"].append(sctp_ingr_rule) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + self._add_source_net_filter(gnp_config["spec"]["ingress"], + f"{addr_pool.network}/{addr_pool.prefix}", ip_version) - if (ip_version == 6): - # add link-local network too - self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL) + # add cluster-pod to cover the cases where there is no tunneling, the pod traffic goes + # directly in the cluster-host interface + cpod_pool = cpod_pool_index.get(ip_version, None) + if cpod_pool: + self._add_source_net_filter(gnp_config["spec"]["ingress"], + f"{cpod_pool.network}/{cpod_pool.prefix}", ip_version) - if (ip_version == 4): - # add rule to allow DHCP requests (dhcp-offer have src addr == 0.0.0.0) - rule = self._get_dhcp_rule(host.personality, "UDP", ip_version) - gnp_config["spec"]["ingress"].append(rule) + # copy the TCP rule and do the same for SCTP + sctp_egr_rule = self._copy_tcp_rule(gnp_config["spec"], "egress", ip_version) + sctp_egr_rule["protocol"] = "SCTP" + sctp_egr_rule["metadata"]["annotations"]["name"] = \ + f"stx-egr-{host.personality}-{network.type}-sctp{ip_version}" + gnp_config["spec"]["egress"].append(sctp_egr_rule) + sctp_ingr_rule = self._copy_tcp_rule(gnp_config["spec"], "ingress", ip_version) + sctp_ingr_rule["protocol"] = "SCTP" + sctp_ingr_rule["metadata"]["annotations"]["name"] = \ + f"stx-ingr-{host.personality}-{network.type}-sctp{ip_version}" + gnp_config["spec"]["ingress"].append(sctp_ingr_rule) - # copy the TCP rule and do the same for IGMP - igmp_proto = 2 - igmp_egr_rule = copy.deepcopy(gnp_config["spec"]["egress"][0]) - igmp_egr_rule["protocol"] = igmp_proto - igmp_egr_rule["metadata"]["annotations"]["name"] = \ - f"stx-egr-{host.personality}-{network.type}-igmp{ip_version}" - gnp_config["spec"]["egress"].append(igmp_egr_rule) - igmp_ingr_rule = copy.deepcopy(gnp_config["spec"]["ingress"][0]) - igmp_ingr_rule["protocol"] = igmp_proto - igmp_ingr_rule["metadata"]["annotations"]["name"] = \ - f"stx-ingr-{host.personality}-{network.type}-igmp{ip_version}" - # Allow 0.0.0.0/32 for the case the switch sends IGMP queries from - # a VLAN without the IP address configured. - igmp_ingr_rule["source"]["nets"].append("0.0.0.0/32") - gnp_config["spec"]["ingress"].append(igmp_ingr_rule) + if (ip_version == 6): + # add link-local network too + self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL, ip_version) + + if (ip_version == 4): + # add rule to allow DHCP requests (dhcp-offer have src addr == 0.0.0.0) + rule = self._get_dhcp_rule(host.personality, "UDP", ip_version) + gnp_config["spec"]["ingress"].append(rule) + + # copy the TCP rule and do the same for IGMP + igmp_proto = 2 + igmp_egr_rule = self._copy_tcp_rule(gnp_config["spec"], "egress", ip_version) + igmp_egr_rule["protocol"] = igmp_proto + igmp_egr_rule["metadata"]["annotations"]["name"] = \ + f"stx-egr-{host.personality}-{network.type}-igmp{ip_version}" + gnp_config["spec"]["egress"].append(igmp_egr_rule) + igmp_ingr_rule = self._copy_tcp_rule(gnp_config["spec"], "ingress", ip_version) + igmp_ingr_rule["protocol"] = igmp_proto + igmp_ingr_rule["metadata"]["annotations"]["name"] = \ + f"stx-ingr-{host.personality}-{network.type}-igmp{ip_version}" + # Allow 0.0.0.0/32 for the case the switch sends IGMP queries from + # a VLAN without the IP address configured. + igmp_ingr_rule["source"]["nets"].append("0.0.0.0/32") + gnp_config["spec"]["ingress"].append(igmp_ingr_rule) def _set_rules_pxeboot(self, gnp_config, network, host): """ Fill the pxeboot network specific filtering data @@ -458,16 +474,17 @@ class PlatformFirewallPuppet(base.BasePuppet): :param network: the sysinv.object.network object for this network """ - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - self._add_source_net_filter(gnp_config["spec"]["ingress"], - f"{addr_pool.network}/{addr_pool.prefix}") - if (ip_version == 6): - self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL) - if (ip_version == 4): - # add rule to allow DHCP requests (dhcp-offer have src addr == 0.0.0.0) - rule = self._get_dhcp_rule(host.personality, "UDP", ip_version) - gnp_config["spec"]["ingress"].append(rule) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + self._add_source_net_filter(gnp_config["spec"]["ingress"], + f"{addr_pool.network}/{addr_pool.prefix}", ip_version) + if (ip_version == 6): + self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL, ip_version) + if (ip_version == 4): + # add rule to allow DHCP requests (dhcp-offer have src addr == 0.0.0.0) + rule = self._get_dhcp_rule(host.personality, "UDP", ip_version) + gnp_config["spec"]["ingress"].append(rule) def _set_rules_storage(self, gnp_config, network, host): """ Fill the storage network specific filtering data @@ -476,18 +493,19 @@ class PlatformFirewallPuppet(base.BasePuppet): :param network: the sysinv.object.network object for this network """ - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - self._add_source_net_filter(gnp_config["spec"]["ingress"], - f"{addr_pool.network}/{addr_pool.prefix}") - if (ip_version == 6): - self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL) - if (ip_version == 4): - # add rule to allow DHCP requests (dhcp-offer have src addr == 0.0.0.0) - rule = self._get_dhcp_rule(host.personality, "UDP", ip_version) - gnp_config["spec"]["ingress"].append(rule) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + self._add_source_net_filter(gnp_config["spec"]["ingress"], + f"{addr_pool.network}/{addr_pool.prefix}", ip_version) + if (ip_version == 6): + self._add_source_net_filter(gnp_config["spec"]["ingress"], LINK_LOCAL, ip_version) + if (ip_version == 4): + # add rule to allow DHCP requests (dhcp-offer have src addr == 0.0.0.0) + rule = self._get_dhcp_rule(host.personality, "UDP", ip_version) + gnp_config["spec"]["ingress"].append(rule) - def _add_source_net_filter(self, rule_list, source_net): + def _add_source_net_filter(self, rule_list, source_net, ip_version): """ Add source network in the rule list :param rule_list: the list containing the firewall rules that need to receive the source @@ -495,6 +513,8 @@ class PlatformFirewallPuppet(base.BasePuppet): :param source_net: the string containing the value """ for rule in rule_list: + if rule["ipVersion"] != ip_version: + continue if ("source" in rule.keys()): if ("nets" in rule["source"].keys()): rule["source"]["nets"].append(source_net) @@ -527,33 +547,35 @@ class PlatformFirewallPuppet(base.BasePuppet): :param host_personality: the node personality (controller, storage, or worker) """ - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - ICMP = "ICMP" - if ip_version == 6: - ICMP = "ICMPv6" + routes_networks = self._get_routes_networks(network.type) - rules = list() - for proto in ["TCP", "UDP", ICMP]: - rule = {"metadata": dict()} - rule["metadata"] = {"annotations": dict()} - rule["metadata"]["annotations"] = {"name": - f"stx-ingr-{host_personality}-subcloud-{proto.lower()}{ip_version}"} - rule.update({"protocol": proto}) - rule.update({"ipVersion": ip_version}) - rule.update({"action": "Allow"}) - if (proto == "TCP"): - rule.update({"destination": {"ports": self._get_subcloud_tcp_ports()}}) - elif (proto == "UDP"): - rule.update({"destination": {"ports": self._get_subcloud_udp_ports()}}) - rules.append(rule) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + ICMP = "ICMP" + if ip_version == 6: + ICMP = "ICMPv6" - networks = self._get_routes_networks(network.type) - for network in networks: - self._add_source_net_filter(rules, network) + rules = list() + for proto in ["TCP", "UDP", ICMP]: + rule = {"metadata": dict()} + rule["metadata"] = {"annotations": dict()} + rule["metadata"]["annotations"] = {"name": + f"stx-ingr-{host_personality}-subcloud-{proto.lower()}{ip_version}"} + rule.update({"protocol": proto}) + rule.update({"ipVersion": ip_version}) + rule.update({"action": "Allow"}) + if (proto == "TCP"): + rule.update({"destination": {"ports": self._get_subcloud_tcp_ports()}}) + elif (proto == "UDP"): + rule.update({"destination": {"ports": self._get_subcloud_udp_ports()}}) + rules.append(rule) - for rule in rules: - gnp_config["spec"]["ingress"].append(rule) + for route_network in routes_networks[ip_version]: + self._add_source_net_filter(rules, route_network, ip_version) + + for rule in rules: + gnp_config["spec"]["ingress"].append(rule) def _set_rules_subcloud_mgmt(self, gnp_config, network, host_personality): """ Add filtering rules for mgmt network in a subcloud installation @@ -566,41 +588,46 @@ class PlatformFirewallPuppet(base.BasePuppet): :param host_personality: the node personality (controller, storage, or worker) """ - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - ICMP = "ICMP" - if ip_version == 6: - ICMP = "ICMPv6" + routes_networks = self._get_routes_networks(network.type) - rules = list() - for proto in ["TCP", "UDP", ICMP]: - rule = {"metadata": dict()} - rule["metadata"] = {"annotations": dict()} - rule["metadata"]["annotations"] = {"name": - f"stx-ingr-{host_personality}-subcloud-{proto.lower()}{ip_version}"} - rule.update({"protocol": proto}) - rule.update({"ipVersion": ip_version}) - rule.update({"action": "Allow"}) - if (proto == "TCP"): - rule.update({"destination": {"ports": self._get_subcloud_tcp_ports()}}) - elif (proto == "UDP"): - rule.update({"destination": {"ports": self._get_subcloud_udp_ports()}}) - gnp_config["spec"]["ingress"].append(rule) - rules.append(rule) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + ICMP = "ICMP" + if ip_version == 6: + ICMP = "ICMPv6" - networks = self._get_routes_networks(network.type) - for network in networks: - self._add_source_net_filter(rules, network) + rules = list() + for proto in ["TCP", "UDP", ICMP]: + rule = {"metadata": dict()} + rule["metadata"] = {"annotations": dict()} + rule["metadata"]["annotations"] = {"name": + f"stx-ingr-{host_personality}-subcloud-{proto.lower()}{ip_version}"} + rule.update({"protocol": proto}) + rule.update({"ipVersion": ip_version}) + rule.update({"action": "Allow"}) + if (proto == "TCP"): + rule.update({"destination": {"ports": self._get_subcloud_tcp_ports()}}) + elif (proto == "UDP"): + rule.update({"destination": {"ports": self._get_subcloud_udp_ports()}}) + gnp_config["spec"]["ingress"].append(rule) + rules.append(rule) + + for route_network in routes_networks[ip_version]: + self._add_source_net_filter(rules, route_network, ip_version) def _get_routes_networks(self, network_type): routes = self.dbapi.routes_get_by_network_type_and_host_personality( network_type, constants.CONTROLLER) - networks = set() + network_sets = {constants.IPV4_FAMILY: set(), constants.IPV6_FAMILY: set()} + networks = {} for route in routes: network = route.network + '/' + str(route.prefix) - networks.add(network) - networks = list(networks) - networks.sort() + network_sets[int(route.family)].add(network) + for family, net_set in network_sets.items(): + net_list = list(net_set) + net_list.sort() + networks[family] = net_list return networks def _set_rules_systemcontroller(self, gnp_config, network, host_personality): @@ -611,68 +638,38 @@ class PlatformFirewallPuppet(base.BasePuppet): :param host_personality: the node personality (controller, storage, or worker) """ + routes_networks = self._get_routes_networks(network.type) + rules = [] - addr_pool = self.dbapi.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - ICMP = "ICMP" - if ip_version == 6: - ICMP = "ICMPv6" + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family + ICMP = "ICMP" + if ip_version == 6: + ICMP = "ICMPv6" - for proto in ["TCP", "UDP", ICMP]: - rule = {"metadata": dict()} - rule["metadata"] = {"annotations": dict()} - rule["metadata"]["annotations"] = {"name": - f"stx-ingr-{host_personality}-systemcontroller-{proto.lower()}{ip_version}"} - rule.update({"protocol": proto}) - rule.update({"ipVersion": ip_version}) - rule.update({"action": "Allow"}) - if (proto == "TCP"): - tcp_list = self._get_systemcontroller_tcp_ports() - rule.update({"destination": {"ports": tcp_list}}) - elif (proto == "UDP"): - udp_list = self._get_systemcontroller_udp_ports() - rule.update({"destination": {"ports": udp_list}}) - gnp_config["spec"]["ingress"].append(rule) - rules.append(rule) + for proto in ["TCP", "UDP", ICMP]: + rule = {"metadata": dict()} + rule["metadata"] = {"annotations": dict()} + rule["metadata"]["annotations"] = {"name": + f"stx-ingr-{host_personality}-systemcontroller-{proto.lower()}{ip_version}"} + rule.update({"protocol": proto}) + rule.update({"ipVersion": ip_version}) + rule.update({"action": "Allow"}) + if (proto == "TCP"): + tcp_list = self._get_systemcontroller_tcp_ports() + rule.update({"destination": {"ports": tcp_list}}) + elif (proto == "UDP"): + udp_list = self._get_systemcontroller_udp_ports() + rule.update({"destination": {"ports": udp_list}}) + gnp_config["spec"]["ingress"].append(rule) + rules.append(rule) - networks = self._get_routes_networks(network.type) - for network in networks: - self._add_source_net_filter(rules, network) + for route_network in routes_networks[ip_version]: + self._add_source_net_filter(rules, route_network, ip_version) def _set_extra_rules(self, config): - self._ingress_ipv6_for_ipv4_install_case(config) - return - - def _ingress_ipv6_for_ipv4_install_case(self, full_config): - - intf_ip_version = dict() - for hep in full_config[FIREWALL_HE_INTERFACE_CFG].keys(): - hep_data = full_config[FIREWALL_HE_INTERFACE_CFG][hep] - iftype_list = (hep_data['metadata']['labels']['iftype']).split('.') - interface = hep_data['spec']['interfaceName'] - intf_ip_version.update({interface: set()}) - for iftype in iftype_list: - for gnp_config in full_config.keys(): - if (gnp_config == FIREWALL_HE_INTERFACE_CFG - or gnp_config == FIREWALL_EXTRA_FILTER_CFG): - continue - if not full_config[gnp_config]: - continue - gnp_data = full_config[gnp_config] - if (iftype in gnp_data['spec']['selector']): - for ingress in gnp_data['spec']['ingress']: - intf_ip_version[interface].add(ingress['ipVersion']) - - is_ipv4_install = True - for intf in intf_ip_version.keys(): - if (6 in intf_ip_version[intf]): - is_ipv4_install = False - break - - if is_ipv4_install: - full_config[FIREWALL_EXTRA_FILTER_CFG].update( - {"ingress-ipv6-for-ipv4-install": list(intf_ip_version.keys())}) - return + pass def _get_subcloud_tcp_ports(self): """ Get the TCP L4 ports for subclouds @@ -768,12 +765,13 @@ class PlatformFirewallPuppet(base.BasePuppet): elif host.hostname == constants.CONTROLLER_1_HOSTNAME: address_name = cutils.format_address_name(constants.CONTROLLER_1_HOSTNAME, net_type) - address = cutils.get_primary_address_by_name(self.dbapi, address_name, net_type) - if (address): + addresses = self.dbapi.address_get_by_name(address_name) + address_texts = [str(address.address) for address in addresses] + if (address_texts): if ("expectedIPs" in host_endpoints["spec"].keys()): - host_endpoints["spec"]["expectedIPs"].append(str(address.address)) + host_endpoints["spec"]["expectedIPs"].extend(address_texts) else: - host_endpoints["spec"].update({"expectedIPs": [str(address.address)]}) + host_endpoints["spec"].update({"expectedIPs": address_texts}) else: LOG.info(f"cannot find address:{address_name} for net_type:{net_type} expectedIPs") diff --git a/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_platform_firewall.py b/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_platform_firewall.py index e16cb7ed2e..5de7ff8931 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_platform_firewall.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/puppet/test_platform_firewall.py @@ -8,7 +8,6 @@ import mock import os import yaml -from netaddr import IPAddress from sysinv.tests.puppet import base from sysinv.puppet import puppet from sysinv.objects import base as objbase @@ -251,28 +250,28 @@ class PlatformFirewallTestCaseMixin(base.PuppetTestCaseMixin): class_name = self.__class__.__name__ return os.path.join(hiera_directory, class_name) + ".yaml" - def _check_egress_rules(self, gnp, ip_version, net_type, ICMP): + def _check_egress_rules(self, filtered_rules, ip_version, net_type, ICMP): - self.assertEqual(gnp['spec']['egress'][0]['protocol'], "TCP") - self.assertEqual(gnp['spec']['egress'][0]['metadata']['annotations']['name'], + self.assertEqual(filtered_rules['egress'][0]['protocol'], "TCP") + self.assertEqual(filtered_rules['egress'][0]['metadata']['annotations']['name'], f"stx-egr-{self.host.personality}-{net_type}-tcp{ip_version}") - self.assertEqual(gnp['spec']['egress'][0]['ipVersion'], ip_version) - self.assertFalse('destination' in gnp['spec']['egress'][0].keys()) - self.assertFalse('source' in gnp['spec']['egress'][0].keys()) + self.assertEqual(filtered_rules['egress'][0]['ipVersion'], ip_version) + self.assertFalse('destination' in filtered_rules['egress'][0].keys()) + self.assertFalse('source' in filtered_rules['egress'][0].keys()) - self.assertEqual(gnp['spec']['egress'][1]['protocol'], "UDP") - self.assertEqual(gnp['spec']['egress'][1]['metadata']['annotations']['name'], + self.assertEqual(filtered_rules['egress'][1]['protocol'], "UDP") + self.assertEqual(filtered_rules['egress'][1]['metadata']['annotations']['name'], f"stx-egr-{self.host.personality}-{net_type}-udp{ip_version}") - self.assertEqual(gnp['spec']['egress'][1]['ipVersion'], ip_version) - self.assertFalse('destination' in gnp['spec']['egress'][1].keys()) - self.assertFalse('source' in gnp['spec']['egress'][1].keys()) + self.assertEqual(filtered_rules['egress'][1]['ipVersion'], ip_version) + self.assertFalse('destination' in filtered_rules['egress'][1].keys()) + self.assertFalse('source' in filtered_rules['egress'][1].keys()) - self.assertEqual(gnp['spec']['egress'][2]['protocol'], ICMP) - self.assertEqual(gnp['spec']['egress'][2]['metadata']['annotations']['name'], + self.assertEqual(filtered_rules['egress'][2]['protocol'], ICMP) + self.assertEqual(filtered_rules['egress'][2]['metadata']['annotations']['name'], f"stx-egr-{self.host.personality}-{net_type}-{ICMP.lower()}{ip_version}") - self.assertEqual(gnp['spec']['egress'][2]['ipVersion'], ip_version) - self.assertFalse('destination' in gnp['spec']['egress'][2].keys()) - self.assertFalse('source' in gnp['spec']['egress'][2].keys()) + self.assertEqual(filtered_rules['egress'][2]['ipVersion'], ip_version) + self.assertFalse('destination' in filtered_rules['egress'][2].keys()) + self.assertFalse('source' in filtered_rules['egress'][2].keys()) def _check_tcp_port(self, gnp, port, present=True): if (present): @@ -283,12 +282,19 @@ class PlatformFirewallTestCaseMixin(base.PuppetTestCaseMixin): def _check_gnp_values(self, gnp, net_type, db_api, egress_size=3, ingress_size=3): network = self.context['networks'][net_type] - addr_pool = db_api.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - ICMP = "ICMP" - if (ip_version == 6): - ICMP = "ICMPv6" + cpod_pool_index = {} + if net_type == constants.NETWORK_TYPE_CLUSTER_HOST: + cpod_net = self.dbapi.network_get_by_type(constants.NETWORK_TYPE_CLUSTER_POD) + if cpod_net: + cpod_pools = self.dbapi.address_pools_get_by_network(cpod_net.id) + for cpod_pool in cpod_pools: + cpod_pool_index[cpod_pool.family] = cpod_pool + + rule_index = {family: {'egress': [], 'ingress': []} for family in [4, 6]} + for direction in ['egress', 'ingress']: + for rule in gnp['spec'][direction]: + rule_index[rule["ipVersion"]][direction].append(rule) self.assertEqual(gnp["apiVersion"], "crd.projectcalico.org/v1") self.assertEqual(gnp["kind"], "GlobalNetworkPolicy") @@ -311,124 +317,135 @@ class PlatformFirewallTestCaseMixin(base.PuppetTestCaseMixin): self.assertEqual(gnp['spec']['selector'], selector) - # egress rules - self._check_egress_rules(gnp, ip_version, net_type, ICMP) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family - # ingress rules - self.assertEqual(gnp['spec']['ingress'][0]['protocol'], "TCP") - self.assertEqual(gnp['spec']['ingress'][0]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-{net_type}-tcp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][0]['ipVersion'], ip_version) + filtered_rules = rule_index[ip_version] - self.assertEqual(gnp['spec']['ingress'][1]['protocol'], "UDP") - self.assertEqual(gnp['spec']['ingress'][1]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-{net_type}-udp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][1]['ipVersion'], ip_version) + ICMP = "ICMP" + if (ip_version == 6): + ICMP = "ICMPv6" - self.assertEqual(gnp['spec']['ingress'][2]['protocol'], ICMP) - self.assertEqual(gnp['spec']['ingress'][2]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-{net_type}-{ICMP.lower()}{ip_version}") - self.assertEqual(gnp['spec']['ingress'][2]['ipVersion'], ip_version) + # egress rules + self._check_egress_rules(filtered_rules, ip_version, net_type, ICMP) - if (net_type == constants.NETWORK_TYPE_OAM): - tcp_ports = set(gnp['spec']['ingress'][0]['destination']['ports']) - udp_ports = set(gnp['spec']['ingress'][1]['destination']['ports']) - for port in firewall.OAM_COMMON["tcp"]: - self.assertIn(port, tcp_ports) - for port in firewall.OAM_COMMON["udp"]: - self.assertIn(port, udp_ports) + # ingress rules + self.assertEqual(filtered_rules['ingress'][0]['protocol'], "TCP") + self.assertEqual(filtered_rules['ingress'][0]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-{net_type}-tcp{ip_version}") + self.assertEqual(filtered_rules['ingress'][0]['ipVersion'], ip_version) - else: - self.assertEqual(gnp['spec']['ingress'][0]['source']['nets'][0], - f"{addr_pool.network}/{addr_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][1]['source']['nets'][0], - f"{addr_pool.network}/{addr_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][2]['source']['nets'][0], - f"{addr_pool.network}/{addr_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][1]['protocol'], "UDP") + self.assertEqual(filtered_rules['ingress'][1]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-{net_type}-udp{ip_version}") + self.assertEqual(filtered_rules['ingress'][1]['ipVersion'], ip_version) - cpod_net = db_api.network_get_by_type(constants.NETWORK_TYPE_CLUSTER_POD) - cpod_pool = db_api.address_pool_get(cpod_net.pool_uuid) + self.assertEqual(filtered_rules['ingress'][2]['protocol'], ICMP) + self.assertEqual(filtered_rules['ingress'][2]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-{net_type}-{ICMP.lower()}{ip_version}") + self.assertEqual(filtered_rules['ingress'][2]['ipVersion'], ip_version) - if net_type == constants.NETWORK_TYPE_MGMT: - self.assertEqual(gnp['spec']['ingress'][3]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-mgmt-esp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][3]['protocol'], 50) - self.assertEqual(gnp['spec']['ingress'][3]['ipVersion'], ip_version) + if (net_type == constants.NETWORK_TYPE_OAM): + tcp_ports = set(filtered_rules['ingress'][0]['destination']['ports']) + udp_ports = set(filtered_rules['ingress'][1]['destination']['ports']) + for port in firewall.OAM_COMMON["tcp"]: + self.assertIn(port, tcp_ports) + for port in firewall.OAM_COMMON["udp"]: + self.assertIn(port, udp_ports) - if (ip_version == 4 and (net_type == constants.NETWORK_TYPE_PXEBOOT - or net_type == constants.NETWORK_TYPE_STORAGE)): - self.assertEqual(gnp['spec']['ingress'][3]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-dhcp-udp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][3]['protocol'], "UDP") - self.assertEqual(gnp['spec']['ingress'][3]['ipVersion'], ip_version) - self.assertEqual(gnp['spec']['ingress'][3]['destination']['ports'], [67]) + else: + self.assertEqual(filtered_rules['ingress'][0]['source']['nets'][0], + f"{addr_pool.network}/{addr_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][1]['source']['nets'][0], + f"{addr_pool.network}/{addr_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][2]['source']['nets'][0], + f"{addr_pool.network}/{addr_pool.prefix}") - if (ip_version == 4 and (net_type == constants.NETWORK_TYPE_CLUSTER_HOST)): - self.assertEqual(gnp['spec']['ingress'][0]['source']['nets'][1], - f"{cpod_pool.network}/{cpod_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][1]['source']['nets'][1], - f"{cpod_pool.network}/{cpod_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][2]['source']['nets'][1], - f"{cpod_pool.network}/{cpod_pool.prefix}") + if net_type == constants.NETWORK_TYPE_MGMT: + self.assertEqual(filtered_rules['ingress'][3]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-mgmt-esp{ip_version}") + self.assertEqual(filtered_rules['ingress'][3]['protocol'], 50) + self.assertEqual(filtered_rules['ingress'][3]['ipVersion'], ip_version) - # check that SCTP rule was added for egress cluster-host in IPv6 - self.assertEqual(gnp['spec']['egress'][3]['protocol'], "SCTP") - self.assertEqual(gnp['spec']['egress'][3]['metadata']['annotations']['name'], - f"stx-egr-{self.host.personality}-{net_type}-sctp{ip_version}") - self.assertEqual(gnp['spec']['egress'][3]['ipVersion'], ip_version) - self.assertFalse('destination' in gnp['spec']['egress'][3].keys()) - self.assertFalse('source' in gnp['spec']['egress'][3].keys()) - # check that SCTP rule was added for ingress cluster-host in IPv4 - self.assertEqual(gnp['spec']['ingress'][3]['protocol'], "SCTP") - self.assertEqual(gnp['spec']['ingress'][3]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-{net_type}-sctp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][3]['ipVersion'], ip_version) - self.assertEqual(gnp['spec']['ingress'][3]['source']['nets'][0], - f"{addr_pool.network}/{addr_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][3]['source']['nets'][1], - f"{cpod_pool.network}/{cpod_pool.prefix}") + if (ip_version == 4 and (net_type == constants.NETWORK_TYPE_PXEBOOT + or net_type == constants.NETWORK_TYPE_STORAGE)): + self.assertEqual(filtered_rules['ingress'][3]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-dhcp-udp{ip_version}") + self.assertEqual(filtered_rules['ingress'][3]['protocol'], "UDP") + self.assertEqual(filtered_rules['ingress'][3]['ipVersion'], ip_version) + self.assertEqual(filtered_rules['ingress'][3]['destination']['ports'], [67]) - self.assertEqual(gnp['spec']['ingress'][4]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-dhcp-udp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][4]['protocol'], "UDP") - self.assertEqual(gnp['spec']['ingress'][4]['ipVersion'], ip_version) - self.assertEqual(gnp['spec']['ingress'][4]['destination']['ports'], [67]) + if (ip_version == 4 and (net_type == constants.NETWORK_TYPE_CLUSTER_HOST)): + cpod_pool = cpod_pool_index[ip_version] - if (ip_version == 6 and (net_type == constants.NETWORK_TYPE_CLUSTER_HOST)): - self.assertEqual(gnp['spec']['ingress'][0]['source']['nets'][1], - f"{cpod_pool.network}/{cpod_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][0]['source']['nets'][2], "fe80::/64") - self.assertEqual(gnp['spec']['ingress'][1]['source']['nets'][1], - f"{cpod_pool.network}/{cpod_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][1]['source']['nets'][2], "fe80::/64") - self.assertEqual(gnp['spec']['ingress'][2]['source']['nets'][1], - f"{cpod_pool.network}/{cpod_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][2]['source']['nets'][2], "fe80::/64") + self.assertEqual(filtered_rules['ingress'][0]['source']['nets'][1], + f"{cpod_pool.network}/{cpod_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][1]['source']['nets'][1], + f"{cpod_pool.network}/{cpod_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][2]['source']['nets'][1], + f"{cpod_pool.network}/{cpod_pool.prefix}") - # check that SCTP rule was added for egress cluster-host in IPv6 - self.assertEqual(gnp['spec']['egress'][3]['protocol'], "SCTP") - self.assertEqual(gnp['spec']['egress'][3]['metadata']['annotations']['name'], - f"stx-egr-{self.host.personality}-{net_type}-sctp{ip_version}") - self.assertEqual(gnp['spec']['egress'][3]['ipVersion'], ip_version) - self.assertFalse('destination' in gnp['spec']['egress'][3].keys()) - self.assertFalse('source' in gnp['spec']['egress'][3].keys()) + # check that SCTP rule was added for egress cluster-host in IPv6 + self.assertEqual(filtered_rules['egress'][3]['protocol'], "SCTP") + self.assertEqual(filtered_rules['egress'][3]['metadata']['annotations']['name'], + f"stx-egr-{self.host.personality}-{net_type}-sctp{ip_version}") + self.assertEqual(filtered_rules['egress'][3]['ipVersion'], ip_version) + self.assertFalse('destination' in filtered_rules['egress'][3].keys()) + self.assertFalse('source' in filtered_rules['egress'][3].keys()) + # check that SCTP rule was added for ingress cluster-host in IPv4 + self.assertEqual(filtered_rules['ingress'][3]['protocol'], "SCTP") + self.assertEqual(filtered_rules['ingress'][3]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-{net_type}-sctp{ip_version}") + self.assertEqual(filtered_rules['ingress'][3]['ipVersion'], ip_version) + self.assertEqual(filtered_rules['ingress'][3]['source']['nets'][0], + f"{addr_pool.network}/{addr_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][3]['source']['nets'][1], + f"{cpod_pool.network}/{cpod_pool.prefix}") - # check that SCTP rule was added for ingress cluster-host in IPv6 - self.assertEqual(gnp['spec']['ingress'][3]['protocol'], "SCTP") - self.assertEqual(gnp['spec']['ingress'][3]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-{net_type}-sctp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][3]['ipVersion'], ip_version) - self.assertEqual(gnp['spec']['ingress'][3]['source']['nets'][0], - f"{addr_pool.network}/{addr_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][3]['source']['nets'][1], - f"{cpod_pool.network}/{cpod_pool.prefix}") - self.assertEqual(gnp['spec']['ingress'][3]['source']['nets'][2], "fe80::/64") + self.assertEqual(filtered_rules['ingress'][4]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-dhcp-udp{ip_version}") + self.assertEqual(filtered_rules['ingress'][4]['protocol'], "UDP") + self.assertEqual(filtered_rules['ingress'][4]['ipVersion'], ip_version) + self.assertEqual(filtered_rules['ingress'][4]['destination']['ports'], [67]) - if (ip_version == 6 and (net_type != constants.NETWORK_TYPE_CLUSTER_HOST) - and (net_type != constants.NETWORK_TYPE_OAM)): - self.assertEqual(gnp['spec']['ingress'][0]['source']['nets'][1], "fe80::/64") - self.assertEqual(gnp['spec']['ingress'][1]['source']['nets'][1], "fe80::/64") - self.assertEqual(gnp['spec']['ingress'][2]['source']['nets'][1], "fe80::/64") + if (ip_version == 6 and (net_type == constants.NETWORK_TYPE_CLUSTER_HOST)): + cpod_pool = cpod_pool_index[ip_version] + + self.assertEqual(filtered_rules['ingress'][0]['source']['nets'][1], + f"{cpod_pool.network}/{cpod_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][0]['source']['nets'][2], "fe80::/64") + self.assertEqual(filtered_rules['ingress'][1]['source']['nets'][1], + f"{cpod_pool.network}/{cpod_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][1]['source']['nets'][2], "fe80::/64") + self.assertEqual(filtered_rules['ingress'][2]['source']['nets'][1], + f"{cpod_pool.network}/{cpod_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][2]['source']['nets'][2], "fe80::/64") + + # check that SCTP rule was added for egress cluster-host in IPv6 + self.assertEqual(filtered_rules['egress'][3]['protocol'], "SCTP") + self.assertEqual(filtered_rules['egress'][3]['metadata']['annotations']['name'], + f"stx-egr-{self.host.personality}-{net_type}-sctp{ip_version}") + self.assertEqual(filtered_rules['egress'][3]['ipVersion'], ip_version) + self.assertFalse('destination' in filtered_rules['egress'][3].keys()) + self.assertFalse('source' in filtered_rules['egress'][3].keys()) + + # check that SCTP rule was added for ingress cluster-host in IPv6 + self.assertEqual(filtered_rules['ingress'][3]['protocol'], "SCTP") + self.assertEqual(filtered_rules['ingress'][3]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-{net_type}-sctp{ip_version}") + self.assertEqual(filtered_rules['ingress'][3]['ipVersion'], ip_version) + self.assertEqual(filtered_rules['ingress'][3]['source']['nets'][0], + f"{addr_pool.network}/{addr_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][3]['source']['nets'][1], + f"{cpod_pool.network}/{cpod_pool.prefix}") + self.assertEqual(filtered_rules['ingress'][3]['source']['nets'][2], "fe80::/64") + + if (ip_version == 6 and (net_type != constants.NETWORK_TYPE_CLUSTER_HOST) + and (net_type != constants.NETWORK_TYPE_OAM)): + self.assertEqual(filtered_rules['ingress'][0]['source']['nets'][1], "fe80::/64") + self.assertEqual(filtered_rules['ingress'][1]['source']['nets'][1], "fe80::/64") + self.assertEqual(filtered_rules['ingress'][2]['source']['nets'][1], "fe80::/64") def _check_he_values(self, hep, intf, network_list): @@ -459,18 +476,12 @@ class PlatformFirewallTestCaseMixin(base.PuppetTestCaseMixin): if cutils.is_aio_simplex_system(db_api): addr_name = cutils.format_address_name(constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_OAM) - address = dbutils.get_primary_address_by_name(addr_name, - constants.NETWORK_TYPE_OAM) else: addr_name = cutils.format_address_name(nodename, constants.NETWORK_TYPE_OAM) - if nodename == constants.CONTROLLER_0_HOSTNAME: - address = dbutils.get_primary_address_by_name(addr_name, - constants.NETWORK_TYPE_OAM) - if nodename == constants.CONTROLLER_1_HOSTNAME: - address = dbutils.get_primary_address_by_name(addr_name, - constants.NETWORK_TYPE_OAM) - self.assertTrue(address) - self.assertEqual(hep[hep_name]["spec"]["expectedIPs"], [str(address.address)]) + addresses = self.dbapi.address_get_by_name(addr_name) + address_texts = [str(address.address) for address in addresses] + self.assertTrue(address_texts) + self.assertEqual(hep[hep_name]["spec"]["expectedIPs"], address_texts) def _create_service_parameter_test_set(self): service_parameter_data = [ @@ -522,12 +533,11 @@ class PlatformFirewallTestCaseMixin(base.PuppetTestCaseMixin): def _check_gnp_admin_values(self, gnp, net_type, db_api, egress_size=3, ingress_size=3): network = self.context['networks'][net_type] - addr_pool = db_api.address_pool_get(network.pool_uuid) - ip_version = IPAddress(f"{addr_pool.network}").version - ICMP = "ICMP" - if (ip_version == 6): - ICMP = "ICMPv6" + rule_index = {family: {'egress': [], 'ingress': []} for family in [4, 6]} + for direction in ['egress', 'ingress']: + for rule in gnp['spec'][direction]: + rule_index[rule["ipVersion"]][direction].append(rule) nodetype_selector = f"has(nodetype) && nodetype == '{self.host.personality}'" iftype_selector = f"has(iftype) && iftype contains '{network.type}'" @@ -535,8 +545,7 @@ class PlatformFirewallTestCaseMixin(base.PuppetTestCaseMixin): self.assertEqual(gnp["apiVersion"], "crd.projectcalico.org/v1") self.assertEqual(gnp["kind"], "GlobalNetworkPolicy") - self.assertEqual(gnp['metadata']['name'], - f"{self.host.personality}-{net_type}-if-gnp") + self.assertEqual(gnp['metadata']['name'], f"{self.host.personality}-{net_type}-if-gnp") self.assertEqual(gnp['spec']['applyOnForward'], False) self.assertEqual(gnp['spec']['order'], 100) @@ -545,91 +554,101 @@ class PlatformFirewallTestCaseMixin(base.PuppetTestCaseMixin): self.assertEqual(len(gnp['spec']['egress']), egress_size) self.assertEqual(len(gnp['spec']['ingress']), ingress_size) - # egress rules - idx = 0 - self.assertEqual(gnp['spec']['egress'][idx]['protocol'], "TCP") - self.assertEqual(gnp['spec']['egress'][idx]['metadata']['annotations']['name'], - f"stx-egr-{self.host.personality}-{net_type}-tcp{ip_version}") - self.assertEqual(gnp['spec']['egress'][idx]['ipVersion'], ip_version) - self.assertFalse('destination' in gnp['spec']['egress'][idx].keys()) - self.assertFalse('source' in gnp['spec']['egress'][idx].keys()) + addr_pools = self.dbapi.address_pools_get_by_network(network.id) + for addr_pool in addr_pools: + ip_version = addr_pool.family - idx += 1 - self.assertEqual(gnp['spec']['egress'][idx]['protocol'], "UDP") - self.assertEqual(gnp['spec']['egress'][idx]['metadata']['annotations']['name'], - f"stx-egr-{self.host.personality}-{net_type}-udp{ip_version}") - self.assertEqual(gnp['spec']['egress'][idx]['ipVersion'], ip_version) - self.assertFalse('destination' in gnp['spec']['egress'][idx].keys()) - self.assertFalse('source' in gnp['spec']['egress'][idx].keys()) + filtered_rules = rule_index[ip_version] - idx += 1 - self.assertEqual(gnp['spec']['egress'][idx]['protocol'], ICMP) - self.assertEqual(gnp['spec']['egress'][idx]['metadata']['annotations']['name'], - f"stx-egr-{self.host.personality}-{net_type}-{ICMP.lower()}{ip_version}") - self.assertEqual(gnp['spec']['egress'][idx]['ipVersion'], ip_version) - self.assertFalse('destination' in gnp['spec']['egress'][idx].keys()) - self.assertFalse('source' in gnp['spec']['egress'][idx].keys()) + ICMP = "ICMP" + if (ip_version == 6): + ICMP = "ICMPv6" + + # egress rules + idx = 0 + self.assertEqual(filtered_rules['egress'][idx]['protocol'], "TCP") + self.assertEqual(filtered_rules['egress'][idx]['metadata']['annotations']['name'], + f"stx-egr-{self.host.personality}-{net_type}-tcp{ip_version}") + self.assertEqual(filtered_rules['egress'][idx]['ipVersion'], ip_version) + self.assertFalse('destination' in filtered_rules['egress'][idx].keys()) + self.assertFalse('source' in filtered_rules['egress'][idx].keys()) - if (ip_version == 4): idx += 1 - self.assertEqual(gnp['spec']['egress'][idx]['protocol'], 2) - self.assertEqual(gnp['spec']['egress'][idx]['metadata']['annotations']['name'], - f"stx-egr-{self.host.personality}-{net_type}-igmp{ip_version}") - self.assertEqual(gnp['spec']['egress'][idx]['ipVersion'], ip_version) - self.assertFalse('destination' in gnp['spec']['egress'][idx].keys()) - self.assertFalse('source' in gnp['spec']['egress'][idx].keys()) + self.assertEqual(filtered_rules['egress'][idx]['protocol'], "UDP") + self.assertEqual(filtered_rules['egress'][idx]['metadata']['annotations']['name'], + f"stx-egr-{self.host.personality}-{net_type}-udp{ip_version}") + self.assertEqual(filtered_rules['egress'][idx]['ipVersion'], ip_version) + self.assertFalse('destination' in filtered_rules['egress'][idx].keys()) + self.assertFalse('source' in filtered_rules['egress'][idx].keys()) - # ingress rules - tcp_ports = list(firewall.SUBCLOUD["tcp"].keys()) - tcp_ports.append(constants.SERVICE_PARAM_HTTP_PORT_HTTP_DEFAULT) - tcp_ports.sort() - udp_ports = list(firewall.SUBCLOUD["udp"].keys()) - udp_ports.sort() - - idx = 0 - self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "TCP") - self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-admin-tcp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], ip_version) - - idx += 1 - self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "UDP") - self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-admin-udp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], ip_version) - - idx += 1 - self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], ICMP) - self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-admin-{ICMP.lower()}{ip_version}") - self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], ip_version) - - if (ip_version == 4): idx += 1 - self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], 2) - self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-admin-igmp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], ip_version) + self.assertEqual(filtered_rules['egress'][idx]['protocol'], ICMP) + self.assertEqual(filtered_rules['egress'][idx]['metadata']['annotations']['name'], + f"stx-egr-{self.host.personality}-{net_type}-{ICMP.lower()}{ip_version}") + self.assertEqual(filtered_rules['egress'][idx]['ipVersion'], ip_version) + self.assertFalse('destination' in filtered_rules['egress'][idx].keys()) + self.assertFalse('source' in filtered_rules['egress'][idx].keys()) - idx += 1 - self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "TCP") - self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-subcloud-tcp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], ip_version) - self.assertEqual(gnp['spec']['ingress'][idx]['destination']['ports'], tcp_ports) + if (ip_version == 4): + idx += 1 + self.assertEqual(filtered_rules['egress'][idx]['protocol'], 2) + self.assertEqual(filtered_rules['egress'][idx]['metadata']['annotations']['name'], + f"stx-egr-{self.host.personality}-{net_type}-igmp{ip_version}") + self.assertEqual(filtered_rules['egress'][idx]['ipVersion'], ip_version) + self.assertFalse('destination' in filtered_rules['egress'][idx].keys()) + self.assertFalse('source' in filtered_rules['egress'][idx].keys()) - idx += 1 - self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "UDP") - self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-subcloud-udp{ip_version}") - self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], ip_version) - self.assertEqual(gnp['spec']['ingress'][idx]['destination']['ports'], udp_ports) + # ingress rules + tcp_ports = list(firewall.SUBCLOUD["tcp"].keys()) + tcp_ports.append(constants.SERVICE_PARAM_HTTP_PORT_HTTP_DEFAULT) + tcp_ports.sort() + udp_ports = list(firewall.SUBCLOUD["udp"].keys()) + udp_ports.sort() - idx += 1 - self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], ICMP) - self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], - f"stx-ingr-{self.host.personality}-subcloud-{ICMP.lower()}{ip_version}") - self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], ip_version) + idx = 0 + self.assertEqual(filtered_rules['ingress'][idx]['protocol'], "TCP") + self.assertEqual(filtered_rules['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-admin-tcp{ip_version}") + self.assertEqual(filtered_rules['ingress'][idx]['ipVersion'], ip_version) + + idx += 1 + self.assertEqual(filtered_rules['ingress'][idx]['protocol'], "UDP") + self.assertEqual(filtered_rules['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-admin-udp{ip_version}") + self.assertEqual(filtered_rules['ingress'][idx]['ipVersion'], ip_version) + + idx += 1 + self.assertEqual(filtered_rules['ingress'][idx]['protocol'], ICMP) + self.assertEqual(filtered_rules['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-admin-{ICMP.lower()}{ip_version}") + self.assertEqual(filtered_rules['ingress'][idx]['ipVersion'], ip_version) + + if (ip_version == 4): + idx += 1 + self.assertEqual(filtered_rules['ingress'][idx]['protocol'], 2) + self.assertEqual(filtered_rules['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-admin-igmp{ip_version}") + self.assertEqual(filtered_rules['ingress'][idx]['ipVersion'], ip_version) + + idx += 1 + self.assertEqual(filtered_rules['ingress'][idx]['protocol'], "TCP") + self.assertEqual(filtered_rules['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-subcloud-tcp{ip_version}") + self.assertEqual(filtered_rules['ingress'][idx]['ipVersion'], ip_version) + self.assertEqual(filtered_rules['ingress'][idx]['destination']['ports'], tcp_ports) + + idx += 1 + self.assertEqual(filtered_rules['ingress'][idx]['protocol'], "UDP") + self.assertEqual(filtered_rules['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-subcloud-udp{ip_version}") + self.assertEqual(filtered_rules['ingress'][idx]['ipVersion'], ip_version) + self.assertEqual(filtered_rules['ingress'][idx]['destination']['ports'], udp_ports) + + idx += 1 + self.assertEqual(filtered_rules['ingress'][idx]['protocol'], ICMP) + self.assertEqual(filtered_rules['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-subcloud-{ICMP.lower()}{ip_version}") + self.assertEqual(filtered_rules['ingress'][idx]['ipVersion'], ip_version) # Controller, non-DC @@ -1321,6 +1340,143 @@ class PlatformFirewallTestCaseControllerNonDc_Setup06(PlatformFirewallTestCaseMi [constants.NETWORK_TYPE_OAM]) +# Controller, non-DC, Dual Stack primary IPv4 +# eth0: oam +# eth1: pxeboot +# vlan100@eth1: mgmt +# vlan101@eth1: cluster-host +# bond0@[eth2,eth3]: storage +class PlatformFirewallTestCaseControllerNonDc_Setup07(PlatformFirewallTestCaseMixin, + dbbase.BaseDualStackPrimaryIPv4Mixin, + dbbase.BaseHostTestCase): + + def __init__(self, *args, **kwargs): + super(PlatformFirewallTestCaseControllerNonDc_Setup07, self).__init__(*args, **kwargs) + self.test_interfaces = dict() + + def setUp(self): + super(PlatformFirewallTestCaseControllerNonDc_Setup07, self).setUp() + self.dbapi = db_api.get_instance() + self._setup_context() + p = mock.patch('sysinv.puppet.platform_firewall._get_dc_role') + self.mock_platform_firewall_get_dc_role = p.start() + self.mock_platform_firewall_get_dc_role.return_value = None + self.addCleanup(p.stop) + + def _update_context(self): + # ensure DB entries are updated prior to updating the context which + # will re-read the entries from the DB. + + self.host.save(self.admin_context) + super(PlatformFirewallTestCaseControllerNonDc_Setup07, self)._update_context() + + def _setup_configuration(self): + # Create a single port/interface for basic function testing + self.host = self._create_test_host(constants.CONTROLLER) + + port, iface = self._create_ethernet_test("oam0", + constants.INTERFACE_CLASS_PLATFORM, + constants.NETWORK_TYPE_OAM) + self.test_interfaces.update({constants.NETWORK_TYPE_OAM: iface}) + + port, iface = self._create_ethernet_test("pxe0", + constants.INTERFACE_CLASS_PLATFORM, + constants.NETWORK_TYPE_PXEBOOT) + self.test_interfaces.update({constants.NETWORK_TYPE_PXEBOOT: iface}) + + iface = self._create_vlan_test("mgmt0", + constants.INTERFACE_CLASS_PLATFORM, + [constants.NETWORK_TYPE_MGMT], 100, + self.test_interfaces[constants.NETWORK_TYPE_PXEBOOT]) + self.test_interfaces.update({constants.NETWORK_TYPE_MGMT: iface}) + + iface = self._create_vlan_test("cluster0", + constants.INTERFACE_CLASS_PLATFORM, + [constants.NETWORK_TYPE_CLUSTER_HOST], 101, + self.test_interfaces[constants.NETWORK_TYPE_PXEBOOT]) + self.test_interfaces.update({constants.NETWORK_TYPE_CLUSTER_HOST: iface}) + + iface = self._create_bond_test("stor0", + constants.INTERFACE_CLASS_PLATFORM, + [constants.NETWORK_TYPE_STORAGE]) + self.test_interfaces.update({constants.NETWORK_TYPE_STORAGE: iface}) + + self._create_service_parameter_test_set() + + def test_generate_firewall_config(self): + hieradata_directory = self._create_hieradata_directory() + config_filename = self._get_config_filename(hieradata_directory) + with open(config_filename, 'w') as config_file: + config = self.operator.platform_firewall.get_host_config(self.host) # pylint: disable=no-member + yaml.dump(config, config_file, default_flow_style=False) + + hiera_data = dict() + with open(config_filename, 'r') as config_file: + hiera_data = yaml.safe_load(config_file) + + self.assertTrue('platform::firewall::calico::oam::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::admin::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::cluster_host::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::mgmt::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::pxeboot::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::storage::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::hostendpoint::config' in hiera_data.keys()) + + # do not install firewall if the network is assigned to the loopback + self.assertFalse(hiera_data['platform::firewall::calico::admin::config']) + + # these GNPs are filled + self.assertTrue(hiera_data['platform::firewall::calico::mgmt::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::mgmt::config'], + constants.NETWORK_TYPE_MGMT, self.dbapi, + egress_size=9, ingress_size=10) + + self.assertTrue(hiera_data['platform::firewall::calico::cluster_host::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::cluster_host::config'], + constants.NETWORK_TYPE_CLUSTER_HOST, self.dbapi, + egress_size=9, ingress_size=10) + + self.assertTrue(hiera_data['platform::firewall::calico::pxeboot::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::pxeboot::config'], + constants.NETWORK_TYPE_PXEBOOT, self.dbapi, + egress_size=3, ingress_size=4) + + self.assertTrue(hiera_data['platform::firewall::calico::storage::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::storage::config'], + constants.NETWORK_TYPE_STORAGE, self.dbapi, + egress_size=6, ingress_size=7) + + self.assertTrue(hiera_data['platform::firewall::calico::oam::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::oam::config'], + constants.NETWORK_TYPE_OAM, self.dbapi, + egress_size=6, ingress_size=6) + self._check_tcp_port(hiera_data['platform::firewall::calico::oam::config'], + constants.SERVICE_PARAM_HTTP_PORT_HTTP_DEFAULT) + + # the HE is filled + self.assertTrue(hiera_data['platform::firewall::calico::hostendpoint::config']) + self.assertEqual(len(hiera_data['platform::firewall::calico::hostendpoint::config']), 5) + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_PXEBOOT], + [constants.NETWORK_TYPE_PXEBOOT]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_MGMT], + [constants.NETWORK_TYPE_MGMT]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_CLUSTER_HOST], + [constants.NETWORK_TYPE_CLUSTER_HOST]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_STORAGE], + [constants.NETWORK_TYPE_STORAGE]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_OAM], + [constants.NETWORK_TYPE_OAM]) + + # Controller, DC, Subcloud # eth0: oam # eth1: pxeboot @@ -1969,6 +2125,308 @@ class PlatformFirewallTestCaseControllerDcSysCtrl_Setup02(PlatformFirewallTestCa [constants.NETWORK_TYPE_OAM]) +# Controller, DC, SystemController +# eth0: oam +# eth1: pxeboot +# vlan100@eth1: mgmt +# bond0@[eth2,eth3]: cluster-host.storage +class PlatformFirewallTestCaseControllerDcSysCtrl_Setup03(PlatformFirewallTestCaseMixin, + dbbase.BaseDualStackPrimaryIPv4Mixin, + dbbase.BaseHostTestCase): + + def __init__(self, *args, **kwargs): + super(PlatformFirewallTestCaseControllerDcSysCtrl_Setup03, self).__init__(*args, **kwargs) + self.test_interfaces = [] + self.hosts = [] + + def setUp(self): + super(PlatformFirewallTestCaseControllerDcSysCtrl_Setup03, self).setUp() + self.dbapi = db_api.get_instance() + self._setup_context() + + def _update_context(self): + # ensure DB entries are updated prior to updating the context which + # will re-read the entries from the DB. + for host in self.hosts: + host.save(self.admin_context) + super(PlatformFirewallTestCaseControllerDcSysCtrl_Setup03, self)._update_context() + + def _setup_controller(self, unit): + host = self._create_test_host(constants.CONTROLLER, unit=unit) + self.hosts.append(host) + + interfaces = dict() + + port, iface = self._create_ethernet_test("oam0", + constants.INTERFACE_CLASS_PLATFORM, + constants.NETWORK_TYPE_OAM, + host.id) + interfaces.update({constants.NETWORK_TYPE_OAM: iface}) + + port, iface = self._create_ethernet_test("pxe0", + constants.INTERFACE_CLASS_PLATFORM, + constants.NETWORK_TYPE_PXEBOOT, + host.id) + interfaces.update({constants.NETWORK_TYPE_PXEBOOT: iface}) + + iface = self._create_vlan_test("mgmt0", + constants.INTERFACE_CLASS_PLATFORM, + [constants.NETWORK_TYPE_MGMT], 100, + interfaces[constants.NETWORK_TYPE_PXEBOOT]) + interfaces.update({constants.NETWORK_TYPE_MGMT: iface}) + + iface = self._create_bond_test("cluster0", + constants.INTERFACE_CLASS_PLATFORM, + [constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_STORAGE], + host.id) + interfaces.update({constants.NETWORK_TYPE_CLUSTER_HOST: iface}) + + self.test_interfaces.append(interfaces) + + def _setup_worker(self): + host = self._create_test_host(constants.WORKER) + self.hosts.append(host) + + interfaces = dict() + + port, iface = self._create_ethernet_test("mgmt0", + constants.INTERFACE_CLASS_PLATFORM, + constants.NETWORK_TYPE_MGMT, + host.id) + interfaces.update({constants.NETWORK_TYPE_MGMT: iface}) + + port, iface = self._create_ethernet_test("cluster0", + constants.INTERFACE_CLASS_PLATFORM, + constants.NETWORK_TYPE_CLUSTER_HOST, + host.id) + interfaces.update({constants.NETWORK_TYPE_CLUSTER_HOST: iface}) + + port, iface = self._create_ethernet_test("pxe0", + constants.INTERFACE_CLASS_PLATFORM, + constants.NETWORK_TYPE_PXEBOOT, + host.id) + interfaces.update({constants.NETWORK_TYPE_PXEBOOT: iface}) + + self.test_interfaces.append(interfaces) + + def _setup_routes(self): + # Controller-0 + + # Common management routes in controller-0 and controller-1 + self._create_test_route(self.test_interfaces[0][constants.NETWORK_TYPE_MGMT], + '192.168.1.0', 26) + self._create_test_route(self.test_interfaces[0][constants.NETWORK_TYPE_MGMT], + 'dead:beef::0', 64, 'dead:beef::1', 6) + + # Management routes exclusive to controller-0 + self._create_test_route(self.test_interfaces[0][constants.NETWORK_TYPE_MGMT], + '192.168.1.64', 26) + self._create_test_route(self.test_interfaces[0][constants.NETWORK_TYPE_MGMT], + 'c0ca:c01a::0', 64, 'c0ca:c01a::1', 6) + + # Non-management routes + self._create_test_route(self.test_interfaces[0][constants.NETWORK_TYPE_OAM], + '192.168.5.0', 24) + self._create_test_route(self.test_interfaces[0][constants.NETWORK_TYPE_OAM], + '3001::0', 64, '3001::1', 6) + self._create_test_route(self.test_interfaces[0][constants.NETWORK_TYPE_PXEBOOT], + '192.168.20.0', 24) + self._create_test_route(self.test_interfaces[0][constants.NETWORK_TYPE_PXEBOOT], + '4001::0', 64, '4001::1', 6) + + # Controller-1 + + # Common management routes in controller-0 and controller-1 + self._create_test_route(self.test_interfaces[1][constants.NETWORK_TYPE_MGMT], + '192.168.1.0', 26) + self._create_test_route(self.test_interfaces[1][constants.NETWORK_TYPE_MGMT], + 'dead:beef::0', 64, 'dead:beef::1', 6) + + # Management route exclusive to controller-1 + self._create_test_route(self.test_interfaces[1][constants.NETWORK_TYPE_MGMT], + '192.168.1.128', 26) + self._create_test_route(self.test_interfaces[1][constants.NETWORK_TYPE_MGMT], + 'c0ca:c02a::0', 64, 'c0ca:c02a::1', 6) + + # Non-management routes + self._create_test_route(self.test_interfaces[1][constants.NETWORK_TYPE_OAM], + '192.168.5.0', 24) + self._create_test_route(self.test_interfaces[1][constants.NETWORK_TYPE_OAM], + '3002::0', 64, '3002::1', 6) + self._create_test_route(self.test_interfaces[1][constants.NETWORK_TYPE_PXEBOOT], + '192.168.20.0', 24) + self._create_test_route(self.test_interfaces[1][constants.NETWORK_TYPE_PXEBOOT], + '4002::0', 64, '4002::1', 6) + + # Worker + self._create_test_route(self.test_interfaces[2][constants.NETWORK_TYPE_MGMT], + '192.168.1.192', 26) + self._create_test_route(self.test_interfaces[2][constants.NETWORK_TYPE_MGMT], + 'c0ca:c03a::0', 64, 'c0ca:c03a::1', 6) + self._create_test_route(self.test_interfaces[2][constants.NETWORK_TYPE_CLUSTER_HOST], + '192.168.6.0', 24) + self._create_test_route(self.test_interfaces[2][constants.NETWORK_TYPE_CLUSTER_HOST], + 'c0ca:c04a::0', 64, 'c0ca:c04a::1', 6) + self._create_test_route(self.test_interfaces[2][constants.NETWORK_TYPE_PXEBOOT], + '192.168.30.0', 24) + self._create_test_route(self.test_interfaces[2][constants.NETWORK_TYPE_PXEBOOT], + '4003::0', 64, '4003::1', 6) + + def _setup_configuration(self): + + self._setup_controller(0) + self._setup_controller(1) + + self._setup_worker() + + self.host = self.hosts[0] + + self._setup_routes() + + self._create_service_parameter_test_set() + self._set_dc_role(constants.DISTRIBUTED_CLOUD_ROLE_SYSTEMCONTROLLER) + + def _check_gnp_values_mgmt_sysctrl(self, gnp): + + subcloud_networks_ipv4 = ['192.168.1.0/26', '192.168.1.128/26', '192.168.1.64/26'] + subcloud_networks_ipv6 = ['c0ca:c01a::/64', 'c0ca:c02a::/64', 'dead:beef::/64'] + + tcp_ports = list(firewall.SYSTEMCONTROLLER["tcp"].keys()) + tcp_ports.append(constants.SERVICE_PARAM_HTTP_PORT_HTTP_DEFAULT) + tcp_ports.sort() + + udp_ports = list(firewall.SYSTEMCONTROLLER["udp"].keys()) + udp_ports.sort() + + # ingress rules + idx = 10 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "TCP") + self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-systemcontroller-tcp4") + self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], 4) + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'], subcloud_networks_ipv4) + self.assertEqual(gnp['spec']['ingress'][idx]['destination']['ports'], tcp_ports) + + idx += 1 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "UDP") + self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-systemcontroller-udp4") + self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], 4) + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'], subcloud_networks_ipv4) + self.assertEqual(gnp['spec']['ingress'][idx]['destination']['ports'], udp_ports) + + idx += 1 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "ICMP") + self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-systemcontroller-icmp4") + self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], 4) + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'], subcloud_networks_ipv4) + + idx += 1 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "TCP") + self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-systemcontroller-tcp6") + self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], 6) + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'], subcloud_networks_ipv6) + self.assertEqual(gnp['spec']['ingress'][idx]['destination']['ports'], tcp_ports) + + idx += 1 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "UDP") + self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-systemcontroller-udp6") + self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], 6) + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'], subcloud_networks_ipv6) + self.assertEqual(gnp['spec']['ingress'][idx]['destination']['ports'], udp_ports) + + idx += 1 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "ICMPv6") + self.assertEqual(gnp['spec']['ingress'][idx]['metadata']['annotations']['name'], + f"stx-ingr-{self.host.personality}-systemcontroller-icmpv66") + self.assertEqual(gnp['spec']['ingress'][idx]['ipVersion'], 6) + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'], subcloud_networks_ipv6) + + def test_generate_firewall_config(self): + hieradata_directory = self._create_hieradata_directory() + config_filename = self._get_config_filename(hieradata_directory) + with open(config_filename, 'w') as config_file: + config = self.operator.platform_firewall.get_host_config(self.host) # pylint: disable=no-member + yaml.dump(config, config_file, default_flow_style=False) + + hiera_data = dict() + with open(config_filename, 'r') as config_file: + hiera_data = yaml.safe_load(config_file) + + self.assertTrue('platform::firewall::calico::oam::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::admin::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::cluster_host::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::mgmt::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::pxeboot::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::storage::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::hostendpoint::config' in hiera_data.keys()) + + # these GNPs are empty (not used in the current test database) + self.assertFalse(hiera_data['platform::firewall::calico::admin::config']) + + # these GNPs are filled + self.assertTrue(hiera_data['platform::firewall::calico::cluster_host::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::cluster_host::config'], + constants.NETWORK_TYPE_CLUSTER_HOST, self.dbapi, + egress_size=9, ingress_size=10) + + self.assertTrue(hiera_data['platform::firewall::calico::mgmt::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::mgmt::config'], + constants.NETWORK_TYPE_MGMT, self.dbapi, + egress_size=9, ingress_size=16) + self._check_gnp_values_mgmt_sysctrl(hiera_data['platform::firewall::calico::mgmt::config']) + + self.assertTrue(hiera_data['platform::firewall::calico::pxeboot::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::pxeboot::config'], + constants.NETWORK_TYPE_PXEBOOT, self.dbapi, + egress_size=3, ingress_size=4) + + self.assertTrue(hiera_data['platform::firewall::calico::storage::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::storage::config'], + constants.NETWORK_TYPE_STORAGE, self.dbapi, + egress_size=6, ingress_size=7) + + self.assertTrue(hiera_data['platform::firewall::calico::oam::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::oam::config'], + constants.NETWORK_TYPE_OAM, self.dbapi, + egress_size=6, ingress_size=6) + self._check_tcp_port(hiera_data['platform::firewall::calico::oam::config'], + constants.SERVICE_PARAM_HTTP_PORT_HTTP_DEFAULT) + self._check_tcp_port(hiera_data['platform::firewall::calico::oam::config'], + constants.PLATFORM_DCMANAGER_PARAMS_API_PORT) + self._check_tcp_port(hiera_data['platform::firewall::calico::oam::config'], + constants.PLATFORM_DCORCH_PARAMS_SYSINV_API_PROXY_PORT) + self._check_tcp_port(hiera_data['platform::firewall::calico::oam::config'], + constants.PLATFORM_DCORCH_PARAMS_PATCH_API_PROXY_PORT) + self._check_tcp_port(hiera_data['platform::firewall::calico::oam::config'], + constants.PLATFORM_DCORCH_PARAMS_USM_API_PROXY_PORT) + self._check_tcp_port(hiera_data['platform::firewall::calico::oam::config'], + constants.PLATFORM_DCORCH_PARAMS_IDENTITY_API_PROXY_PORT) + + # the HE is filled + self.assertTrue(hiera_data['platform::firewall::calico::hostendpoint::config']) + self.assertEqual(len(hiera_data['platform::firewall::calico::hostendpoint::config']), 4) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[0][constants.NETWORK_TYPE_MGMT], + [constants.NETWORK_TYPE_MGMT]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[0][constants.NETWORK_TYPE_CLUSTER_HOST], + [constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_STORAGE]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[0][constants.NETWORK_TYPE_PXEBOOT], + [constants.NETWORK_TYPE_PXEBOOT]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[0][constants.NETWORK_TYPE_OAM], + [constants.NETWORK_TYPE_OAM]) + + # AIO-DX, Controller, DC, Subcloud # eth0: oam # eth1: pxeboot @@ -2457,6 +2915,224 @@ class PlatformFirewallTestCaseControllerDcSubcloud_Setup04(PlatformFirewallTestC [constants.NETWORK_TYPE_OAM]) +# Controller, DC, Subcloud, Dual Stack primary IPv6 +# eth0: oam +# eth1: pxeboot +# vlan100@eth1: mgmt +# vlan101@eth1: admin +# bond0@[eth2,eth3]: cluster-host.storage +class PlatformFirewallTestCaseControllerDcSubcloud_Setup05(PlatformFirewallTestCaseMixin, + dbbase.BaseDualStackPrimaryIPv6Mixin, + dbbase.BaseHostTestCase): + + def __init__(self, *args, **kwargs): + super(PlatformFirewallTestCaseControllerDcSubcloud_Setup05, self).__init__(*args, **kwargs) + self.test_interfaces = dict() + + def setUp(self): + super(PlatformFirewallTestCaseControllerDcSubcloud_Setup05, self).setUp() + self.dbapi = db_api.get_instance() + self._setup_context() + + def _update_context(self): + # ensure DB entries are updated prior to updating the context which + # will re-read the entries from the DB. + self.host.save(self.admin_context) + super(PlatformFirewallTestCaseControllerDcSubcloud_Setup05, self)._update_context() + + def _setup_configuration(self): + # Create a single port/interface for basic function testing + self.host = self._create_test_host(constants.CONTROLLER) + + port, iface = self._create_ethernet_test("oam0", + constants.INTERFACE_CLASS_PLATFORM, + constants.NETWORK_TYPE_OAM) + self.test_interfaces.update({constants.NETWORK_TYPE_OAM: iface}) + + port, iface = self._create_ethernet_test("pxe0", + constants.INTERFACE_CLASS_PLATFORM, + constants.NETWORK_TYPE_PXEBOOT) + self.test_interfaces.update({constants.NETWORK_TYPE_PXEBOOT: iface}) + + iface = self._create_vlan_test("mgmt0", + constants.INTERFACE_CLASS_PLATFORM, [constants.NETWORK_TYPE_MGMT], 100, + self.test_interfaces[constants.NETWORK_TYPE_PXEBOOT]) + self.test_interfaces.update({constants.NETWORK_TYPE_MGMT: iface}) + + iface = self._create_vlan_test("admin0", + constants.INTERFACE_CLASS_PLATFORM, [constants.NETWORK_TYPE_ADMIN], 101, + self.test_interfaces[constants.NETWORK_TYPE_PXEBOOT]) + self.test_interfaces.update({constants.NETWORK_TYPE_ADMIN: iface}) + + iface = self._create_bond_test("cluster0", + constants.INTERFACE_CLASS_PLATFORM, + [constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_STORAGE]) + self.test_interfaces.update({constants.NETWORK_TYPE_CLUSTER_HOST: iface}) + + self._create_service_parameter_test_set() + self._set_dc_role(constants.DISTRIBUTED_CLOUD_ROLE_SUBCLOUD) + + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_MGMT], + 'dead:beef::0', 64, 'dead:beef::1', 6) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_MGMT], + 'c0ca:c01a::0', 64, 'c0ca:c01a::1', 6) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_ADMIN], + 'baba:baba::0', 64, 'baba:baba::1', 6) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_ADMIN], + '2001::0', 64, '2001::1', 6) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_OAM], + '3001::0', 64, '3001::1', 6) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_PXEBOOT], + '4001::0', 64, '4001::1', 6) + + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_MGMT], + '192.168.1.0', 26) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_MGMT], + '192.168.1.64', 26) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_ADMIN], + '192.168.3.0', 24) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_ADMIN], + '192.168.4.0', 24) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_OAM], + '192.168.5.0', 24) + self._create_test_route(self.test_interfaces[constants.NETWORK_TYPE_PXEBOOT], + '192.168.20.0', 24) + + def _check_gnp_admin_source_nets(self, gnp): + + idx = 0 # admin and link-local networks, TCP IPv6 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "TCP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "fd09::/64") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "fe80::/64") + + idx += 1 # admin and link-local networks, UDP IPv6 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "UDP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "fd09::/64") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "fe80::/64") + + idx += 1 # admin and link-local networks, ICMPv6 IPv6 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "ICMPv6") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "fd09::/64") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "fe80::/64") + + idx += 1 # admin network, TCP IPv4 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "TCP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "10.10.30.0/24") + + idx += 1 # admin network, UDP IPv4 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "UDP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "10.10.30.0/24") + + idx += 1 # admin network, ICMP IPv4 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "ICMP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "10.10.30.0/24") + + idx += 1 # admin network, IGMP IPv4 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], 2) + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "10.10.30.0/24") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "0.0.0.0/32") + + idx += 1 # admin routes, TCP IPv6 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "TCP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "2001::/64") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "baba:baba::/64") + + idx += 1 # admin routes, UDP IPv6 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "UDP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "2001::/64") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "baba:baba::/64") + + idx += 1 # admin routes, ICMPv6 IPv6 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "ICMPv6") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "2001::/64") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "baba:baba::/64") + + idx += 1 # admin routes, TCP IPv4 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "TCP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "192.168.3.0/24") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "192.168.4.0/24") + + idx += 1 # admin routes, UDP IPv4 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "UDP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "192.168.3.0/24") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "192.168.4.0/24") + + idx += 1 # admin routes, ICMP IPv4 + self.assertEqual(gnp['spec']['ingress'][idx]['protocol'], "ICMP") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][0], "192.168.3.0/24") + self.assertEqual(gnp['spec']['ingress'][idx]['source']['nets'][1], "192.168.4.0/24") + + def test_generate_firewall_config(self): + hieradata_directory = self._create_hieradata_directory() + config_filename = self._get_config_filename(hieradata_directory) + with open(config_filename, 'w') as config_file: + config = self.operator.platform_firewall.get_host_config(self.host) # pylint: disable=no-member + yaml.dump(config, config_file, default_flow_style=False) + + hiera_data = dict() + with open(config_filename, 'r') as config_file: + hiera_data = yaml.safe_load(config_file) + + self.assertTrue('platform::firewall::calico::oam::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::admin::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::cluster_host::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::mgmt::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::pxeboot::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::storage::config' in hiera_data.keys()) + self.assertTrue('platform::firewall::calico::hostendpoint::config' in hiera_data.keys()) + + # these GNPs are filled + self.assertTrue(hiera_data['platform::firewall::calico::cluster_host::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::cluster_host::config'], + constants.NETWORK_TYPE_CLUSTER_HOST, self.dbapi, + egress_size=9, ingress_size=10) + + self.assertTrue(hiera_data['platform::firewall::calico::mgmt::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::mgmt::config'], + constants.NETWORK_TYPE_MGMT, self.dbapi, + egress_size=9, ingress_size=10) + + self.assertTrue(hiera_data['platform::firewall::calico::pxeboot::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::pxeboot::config'], + constants.NETWORK_TYPE_PXEBOOT, self.dbapi, + egress_size=3, ingress_size=4) + + self.assertTrue(hiera_data['platform::firewall::calico::storage::config']) + self._check_gnp_values(hiera_data['platform::firewall::calico::storage::config'], + constants.NETWORK_TYPE_STORAGE, self.dbapi, + egress_size=6, ingress_size=7) + + self.assertTrue(hiera_data['platform::firewall::calico::admin::config']) + self._check_gnp_admin_values(hiera_data['platform::firewall::calico::admin::config'], + constants.NETWORK_TYPE_ADMIN, self.dbapi, egress_size=7, + ingress_size=13) + self._check_gnp_admin_source_nets(hiera_data['platform::firewall::calico::admin::config']) + + # the HE is filled + self.assertTrue(hiera_data['platform::firewall::calico::hostendpoint::config']) + self.assertEqual(len(hiera_data['platform::firewall::calico::hostendpoint::config']), 5) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_MGMT], + [constants.NETWORK_TYPE_MGMT]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_ADMIN], + [constants.NETWORK_TYPE_ADMIN]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_CLUSTER_HOST], + [constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_STORAGE]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_PXEBOOT], + [constants.NETWORK_TYPE_PXEBOOT]) + + self._check_he_values(hiera_data['platform::firewall::calico::hostendpoint::config'], + self.test_interfaces[constants.NETWORK_TYPE_OAM], + [constants.NETWORK_TYPE_OAM]) + + # Worker, non-DC # eth0:oam [oam] # eth1:mgmt0 [mgmt]