Skip to content

Commit

Permalink
[networking_mapper] Complete the Move to best effort approach and all…
Browse files Browse the repository at this point in the history
…ow multiple base nets
  • Loading branch information
pablintino committed May 27, 2024
1 parent 8e675e6 commit 13451ac
Show file tree
Hide file tree
Showing 32 changed files with 788 additions and 547 deletions.
51 changes: 5 additions & 46 deletions plugins/action/ci_net_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,9 @@
- Dict that contains the list of MAC addresses of each instance to be mapped.
- Each dict entry is a list of dicts with at least a `mac` field.
- Each item in the list is an interface in a given infrastructure network.
- If not passed a partial map is done.
- It's required for full maps.
type: iterable
required: false
network_name:
description:
- Required if interfaces_info is given.
- Represents the name of the physical network that accommodates the OSP networks.
- Filters out the interfaces_info interfaces by selecting only the proper one.
type: str
required: false
search_domain_base:
description:
- Domain name used for generating networks search domains.
Expand Down Expand Up @@ -247,8 +240,8 @@
type: bool
"""

from ansible.plugins.action import ActionBase
from ansible.errors import AnsibleActionFail
from ansible.plugins.action import ActionBase

from ansible_collections.cifmw.general.plugins.module_utils.encoding import (
ansible_encoding,
Expand All @@ -260,26 +253,6 @@


class ActionModule(ActionBase):
@staticmethod
def __build_interfaces_info_dict(
ifaces_info_raw: typing.Dict[str, typing.Any], net_name: str
) -> typing.Dict[str, typing.Any]:
ifaces_info_dict = {}
for instance_name, ifaces_list in ifaces_info_raw.items():
iface_data = next(
(
iface_content
for iface_content in ifaces_list
if "network" in iface_content
and iface_content["network"] == net_name
),
None,
)
if instance_name not in ifaces_info_dict and iface_data:
ifaces_info_dict[instance_name] = iface_data

return ifaces_info_dict

def run(self, tmp=None, task_vars=None):
if task_vars is None:
task_vars = dict()
Expand All @@ -292,33 +265,19 @@ def run(self, tmp=None, task_vars=None):
if not networking_definition:
raise AnsibleActionFail("networking_definition is a mandatory argument")

interfaces_info_dict = None
is_complete_map = "interfaces_info" in task_args
network_name = task_args.get("network_name", None)
if is_complete_map and not network_name:
raise AnsibleActionFail(
"network_name is a mandatory " "argument if interfaces_info is given"
)
elif is_complete_map:
interfaces_info_dict = self.__build_interfaces_info_dict(
task_args["interfaces_info"], network_name
)

mapper = networking_mapper.NetworkingDefinitionMapper(
dict(task_vars["hostvars"]),
task_vars["groups"],
options=networking_mapper.NetworkingMapperOptions.from_dict(task_args),
)

try:
mapping_result = (
mapper.map_complete(networking_definition, interfaces_info_dict)
if is_complete_map
else mapper.map_partial(networking_definition)
mapping_result = mapper.map(
networking_definition,
interfaces_info=task_args.get("interfaces_info", None),
)
result["failed"] = False
result["networking_env_definition"] = mapping_result
result["complete_map"] = is_complete_map
except exceptions.NetworkMappingError as run_exception:
result = {**result, **(run_exception.to_raw()), "failed": True}

Expand Down
149 changes: 85 additions & 64 deletions plugins/module_utils/net_map/networking_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -1608,10 +1608,10 @@ class GroupTemplateNetworkDefinition:
skip_nm_configuration: Indicates if the instances of the
group should skip configuring Network Manager for the
given network.
is_trunk_parent: indicates wheater the instance nic for
this network is parent for trunked vlans.
trunk_parent: idicates which instance network is vlan
parent for this network.
is_trunk_parent: indicates whether the instance nic for
this network is a parent of the trunked VLANs.
trunk_parent: if the network is a trunk in the target instances,
it points to the GroupTemplateNetworkDefinition of that network.
"""

network: NetworkDefinition
Expand All @@ -1620,7 +1620,7 @@ class GroupTemplateNetworkDefinition:
ipv4_range: HostNetworkRange = None
skip_nm_configuration: bool = None
is_trunk_parent: bool = None
trunk_parent: str = None
trunk_parent: GroupTemplateNetworkDefinition = None

def __hash__(self) -> int:
return hash(
Expand Down Expand Up @@ -1653,7 +1653,7 @@ class GroupTemplateDefinition:
# 'network-template' is an optional field that can hold
# the base configuration for each network. If given, each
# declared network content will use the variables defined
# there as a base that can be overriden by each network
# there as a base that can be overridden by each network
# content
network-template: # Optional template
Expand Down Expand Up @@ -1685,8 +1685,8 @@ class GroupTemplateDefinition:
__FIELD_NETWORK_RANGE = "range"
__FIELD_NETWORK_RANGE_IPV4 = "range-v4"
__FIELD_NETWORK_RANGE_IPV6 = "range-v6"
__FIELD_IS_TRUNK_PARENT = "is_trunk_parent"
__FIELD_TRUNK_PARENT = "trunk_parent"
__FIELD_IS_TRUNK_PARENT = "is-trunk-parent"
__FIELD_TRUNK_PARENT = "trunk-parent"

def __init__(
self,
Expand All @@ -1710,7 +1710,6 @@ def __init__(
if not group_name:
raise ValueError("group_name is a mandatory argument")
self.__group_name = group_name
self.__trunk_parents = set()

self.__skip_nm_configuration: typing.Optional[bool] = None
self.__groups_networks_definitions = {}
Expand Down Expand Up @@ -1764,32 +1763,39 @@ def __parse_raw(
or {}
)

# Process trunk parents to populate self.__trunk_parents
for network_name, network_data in networks.items():
if network_data.get(self.__FIELD_IS_TRUNK_PARENT):
self.__parse_raw_net(
network_name,
network_data,
network_template_raw,
network_definitions,
)
# Firstly, process networks that won't be trunk parents
trunk_parents = {
net_name: net_data
for net_name, net_data in networks.items()
if net_data is not None and self.__FIELD_TRUNK_PARENT not in net_data
}
for network_name, network_data in trunk_parents.items():
self.__parse_raw_net(
network_name,
network_data,
network_template_raw,
network_definitions,
)

# Process non trunk parents
for network_name, network_data in networks.items():
if not network_data.get(self.__FIELD_IS_TRUNK_PARENT):
self.__parse_raw_net(
network_name,
network_data,
network_template_raw,
network_definitions,
)
# Process networks that are part of a trunk
child_nets = dict(networks)
map(child_nets.pop, trunk_parents)
for network_name, network_data in child_nets.items():
self.__parse_raw_net(
network_name,
network_data,
network_template_raw,
network_definitions,
trunk_parents=set(trunk_parents.keys()),
)

def __parse_raw_net(
self,
network_name: str,
network_data: typing.Dict[str, typing.Any],
network_template_raw: typing.Dict[str, typing.Any],
network_definitions: typing.Dict[str, NetworkDefinition],
trunk_parents: typing.Set[str] = None,
):
network_definition = network_definitions.get(network_name, None)
if not network_definition:
Expand All @@ -1799,7 +1805,7 @@ def __parse_raw_net(
invalid_value=network_name,
)

templated_net_data = {**network_template_raw, **network_data}
templated_net_data = {**network_template_raw, **(network_data or {})}
ipv4_network_range, ipv6_network_range = self.__parse_raw_net_ranges(
templated_net_data, network_definition
)
Expand All @@ -1809,24 +1815,27 @@ def __parse_raw_net(
if self.__FIELD_NETWORK_SKIP_NM in templated_net_data
else None
)

is_trunk_parent = _validate_parse_field_type(
self.__FIELD_IS_TRUNK_PARENT,
templated_net_data,
bool,
parent_name=self.group_name,
parent_name=self.__group_name,
parent_type=self.__OBJECT_TYPE_NAME,
mandatory=False,
)
if is_trunk_parent:
self.__trunk_parents.add(network_name)

trunk_parent = _validate_parse_trunk_parent_field(
trunk_parent_str = _validate_parse_trunk_parent_field(
self.__FIELD_TRUNK_PARENT,
templated_net_data,
self.group_name,
self.__OBJECT_TYPE_NAME,
self.__trunk_parents,
trunk_parents,
)

trunk_parent = (
self.__groups_networks_definitions[trunk_parent_str]
if trunk_parent_str
else None
)

self.__groups_networks_definitions[network_name] = (
Expand Down Expand Up @@ -1915,7 +1924,7 @@ class InstanceNetworkDefinition:
ipv6: ipaddress.IPv6Address = None
skip_nm_configuration: bool = None
is_trunk_parent: bool = None
trunk_parent: str = None
trunk_parent: InstanceNetworkDefinition = None

def __hash__(self) -> int:
return hash(
Expand Down Expand Up @@ -1965,8 +1974,8 @@ class InstanceDefinition:
__FIELD_NETWORKS = "networks"
__FIELD_NETWORKS_IP = "ip"
__FIELD_NETWORK_SKIP_NM = "skip-nm-configuration"
__FIELD_IS_TRUNK_PARENT = "is_trunk_parent"
__FIELD_TRUNK_PARENT = "trunk_parent"
__FIELD_IS_TRUNK_PARENT = "is-trunk-parent"
__FIELD_TRUNK_PARENT = "trunk-parent"

def __init__(
self,
Expand All @@ -1993,7 +2002,6 @@ def __init__(
self.__name = name
self.__skip_nm_configuration: typing.Optional[bool] = None
self.__instances_network_definitions = {}
self.__trunk_parents: typing.Set = set()
self.__parse_raw(raw_definition, network_definitions)

@property
Expand Down Expand Up @@ -2029,29 +2037,36 @@ def __parse_raw(
field=self.__FIELD_NETWORKS,
)

# Process trunk parents to populate self.__trunk_parents
for network_name, network_data in networks.items():
if network_data.get(self.__FIELD_IS_TRUNK_PARENT):
self.__parse_raw_net(
network_name,
network_data,
network_definitions,
)
# Firstly, process networks that won't be trunk parents
trunk_parents = {
net_name: net_data
for net_name, net_data in networks.items()
if net_data is not None and self.__FIELD_TRUNK_PARENT not in net_data
}
for network_name, network_data in trunk_parents.items():
self.__parse_raw_net(
network_name,
network_data,
network_definitions,
)

# Process non trunk parents
for network_name, network_data in networks.items():
if not network_data.get(self.__FIELD_IS_TRUNK_PARENT):
self.__parse_raw_net(
network_name,
network_data,
network_definitions,
)
# Process networks that are part of a trunk
child_nets = dict(networks)
map(child_nets.pop, trunk_parents)
for network_name, network_data in child_nets.items():
self.__parse_raw_net(
network_name,
network_data,
network_definitions,
trunk_parents=set(trunk_parents.keys()),
)

def __parse_raw_net(
self,
network_name: str,
network_data: typing.Dict[str, typing.Any],
network_definitions: typing.Dict[str, NetworkDefinition],
trunk_parents: typing.Set[str] = None,
):
network_definition = network_definitions.get(network_name, None)
if not network_definition:
Expand Down Expand Up @@ -2080,20 +2095,26 @@ def __parse_raw_net(
self.__FIELD_IS_TRUNK_PARENT,
network_data,
bool,
parent_name=self.name,
parent_type=self.__OBJECT_TYPE_NAME,
parent_name=self.__name,
mandatory=False,
)
if is_trunk_parent:
self.__trunk_parents.add(network_name)

trunk_parent = _validate_parse_trunk_parent_field(
self.__FIELD_TRUNK_PARENT,
network_data,
self.name,
self.__OBJECT_TYPE_NAME,
self.__trunk_parents,
)
trunk_parent = None
if trunk_parents:
trunk_parent_str = _validate_parse_trunk_parent_field(
self.__FIELD_TRUNK_PARENT,
network_data,
self.name,
self.__OBJECT_TYPE_NAME,
trunk_parents,
)

trunk_parent = (
self.__instances_network_definitions[trunk_parent_str]
if trunk_parent_str
else None
)

self.__instances_network_definitions[network_name] = InstanceNetworkDefinition(
network_definition,
Expand Down
Loading

0 comments on commit 13451ac

Please sign in to comment.