Skip to content

Commit 8d4af84

Browse files
miguecormiguecormiguecor
authored
Add dhcp servers list (#516)
* Fix issue 506 Increase DHCP Server List * Fix issue with DHCP server list and key-value pairs * Add new function has_partial_dhcp_config to plugins/module_utils/network/dcnm/dcnm.py * Refactor dhcp_servers logic to make use of the has_partial_dhcp_config helper_function * Change dhcp_servers description to reflect actual behavior * Update Copyright information * Refactor logic to remove unnecessary checks * Fix bad-indentation on line 2871 --------- Co-authored-by: miguecor <[email protected]> Co-authored-by: miguecor <[email protected]>
1 parent 706e9c6 commit 8d4af84

File tree

2 files changed

+139
-9
lines changed

2 files changed

+139
-9
lines changed

plugins/module_utils/network/dcnm/dcnm.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,3 +955,51 @@ def search_nested_json(obj, search_string):
955955
if search_string in obj.lower():
956956
return True
957957
return False
958+
959+
960+
def has_partial_dhcp_config(server):
961+
"""
962+
# Summary
963+
964+
Check if a DHCP server has incomplete configuration (IP address set but no VRF or vice versa).
965+
966+
## Raises
967+
968+
None
969+
970+
## Parameters
971+
972+
- server (dict): A dictionary representing the DHCP server configuration.
973+
974+
## Returns
975+
976+
- bool: True if the server has partial configuration, False otherwise.
977+
978+
## Usage
979+
980+
```python
981+
server1 = {
982+
"srvr_ip": "ip_address",
983+
"srvr_vrf": "vrf_name"
984+
}
985+
result1 = has_partial_dhcp_config(server1)
986+
print(result1)
987+
# -> False (complete configuration)
988+
989+
server2 = {
990+
"srvr_ip": "ip_address"
991+
}
992+
result2 = has_partial_dhcp_config(server2)
993+
print(result2)
994+
# -> True (partial configuration)
995+
996+
server3 = {
997+
"srvr_vrf": "vrf_name"
998+
}
999+
result3 = has_partial_dhcp_config(server3)
1000+
print(result3)
1001+
# -> True (partial configuration)
1002+
"""
1003+
ip = server.get("srvr_ip")
1004+
vrf = server.get("srvr_vrf")
1005+
return bool(ip) != bool(vrf)

plugins/modules/dcnm_network.py

Lines changed: 91 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@
156156
- VRF ID of third DHCP server
157157
type: str
158158
required: false
159+
dhcp_servers:
160+
description:
161+
- List of DHCP servers
162+
- This is an alternative to dhcp_srvr1_ip, dhcp_srvr1_vrf, dhcp_srvr2_ip, dhcp_srvr2_vrf,
163+
dhcp_srvr3_ip, dhcp_srvr3_vrf
164+
- If both dhcp_servers and any of dhcp_srvr1_ip, dhcp_srvr1_vrf, dhcp_srvr2_ip,
165+
dhcp_srvr2_vrf, dhcp_srvr3_ip, dhcp_srvr3_vrf are specified, unexpected results may occur
166+
type: list
167+
elements: dict
168+
required: false
159169
dhcp_loopback_id:
160170
description:
161171
- Loopback ID for DHCP Relay interface
@@ -480,6 +490,7 @@
480490
get_fabric_inventory_details,
481491
get_ip_sn_dict,
482492
get_ip_sn_fabric_dict,
493+
has_partial_dhcp_config,
483494
validate_list_of_dicts,
484495
)
485496

@@ -893,6 +904,7 @@ def diff_for_create(self, want, have):
893904
dhcp1_vrf_changed = False
894905
dhcp2_vrf_changed = False
895906
dhcp3_vrf_changed = False
907+
dhcp_servers_changed = False
896908
dhcp_loopback_changed = False
897909
multicast_group_address_changed = False
898910
gwv6_changed = False
@@ -934,7 +946,6 @@ def diff_for_create(self, want, have):
934946
arpsup_want = str(json_to_dict_want.get("suppressArp", "")).lower()
935947
arpsup_have = json_to_dict_have.get("suppressArp", "")
936948
dhcp1_ip_want = json_to_dict_want.get("dhcpServerAddr1", "")
937-
dhcp1_ip_want = json_to_dict_want.get("dhcpServerAddr1", "")
938949
dhcp1_ip_have = json_to_dict_have.get("dhcpServerAddr1", "")
939950
dhcp2_ip_want = json_to_dict_want.get("dhcpServerAddr2", "")
940951
dhcp2_ip_have = json_to_dict_have.get("dhcpServerAddr2", "")
@@ -946,6 +957,8 @@ def diff_for_create(self, want, have):
946957
dhcp2_vrf_have = json_to_dict_have.get("vrfDhcp2", "")
947958
dhcp3_vrf_want = json_to_dict_want.get("vrfDhcp3", "")
948959
dhcp3_vrf_have = json_to_dict_have.get("vrfDhcp3", "")
960+
dhcp_servers_want = json_to_dict_want.get("dhcpServers", "")
961+
dhcp_servers_have = json_to_dict_have.get("dhcpServers", "")
949962
dhcp_loopback_want = json_to_dict_want.get("loopbackId", "")
950963
dhcp_loopback_have = json_to_dict_have.get("loopbackId", "")
951964
multicast_group_address_want = json_to_dict_want.get("mcastGroup", "")
@@ -1001,6 +1014,7 @@ def diff_for_create(self, want, have):
10011014
or dhcp1_vrf_have != dhcp1_vrf_want
10021015
or dhcp2_vrf_have != dhcp2_vrf_want
10031016
or dhcp3_vrf_have != dhcp3_vrf_want
1017+
or dhcp_servers_have != dhcp_servers_want
10041018
or dhcp_loopback_have != dhcp_loopback_want
10051019
or multicast_group_address_have != multicast_group_address_want
10061020
or gw_ipv6_have != gw_ipv6_want
@@ -1047,6 +1061,8 @@ def diff_for_create(self, want, have):
10471061
dhcp2_vrf_changed = True
10481062
if dhcp3_vrf_have != dhcp3_vrf_want:
10491063
dhcp3_vrf_changed = True
1064+
if dhcp_servers_have != dhcp_servers_want:
1065+
dhcp_servers_changed = True
10501066
if dhcp_loopback_have != dhcp_loopback_want:
10511067
dhcp_loopback_changed = True
10521068
if multicast_group_address_have != multicast_group_address_want:
@@ -1096,6 +1112,7 @@ def diff_for_create(self, want, have):
10961112
or dhcp1_vrf_have != dhcp1_vrf_want
10971113
or dhcp2_vrf_have != dhcp2_vrf_want
10981114
or dhcp3_vrf_have != dhcp3_vrf_want
1115+
or dhcp_servers_have != dhcp_servers_want
10991116
or dhcp_loopback_have != dhcp_loopback_want
11001117
or multicast_group_address_have != multicast_group_address_want
11011118
or gw_ipv6_have != gw_ipv6_want
@@ -1139,6 +1156,8 @@ def diff_for_create(self, want, have):
11391156
dhcp2_vrf_changed = True
11401157
if dhcp3_vrf_have != dhcp3_vrf_want:
11411158
dhcp3_vrf_changed = True
1159+
if dhcp_servers_have != dhcp_servers_want:
1160+
dhcp_servers_changed = True
11421161
if dhcp_loopback_have != dhcp_loopback_want:
11431162
dhcp_loopback_changed = True
11441163
if multicast_group_address_have != multicast_group_address_want:
@@ -1186,6 +1205,7 @@ def diff_for_create(self, want, have):
11861205
dhcp1_vrf_changed,
11871206
dhcp2_vrf_changed,
11881207
dhcp3_vrf_changed,
1208+
dhcp_servers_changed,
11891209
dhcp_loopback_changed,
11901210
multicast_group_address_changed,
11911211
gwv6_changed,
@@ -1244,6 +1264,9 @@ def update_create_params(self, net):
12441264
"vrfDhcp": net.get("dhcp_srvr1_vrf", ""),
12451265
"vrfDhcp2": net.get("dhcp_srvr2_vrf", ""),
12461266
"vrfDhcp3": net.get("dhcp_srvr3_vrf", ""),
1267+
"dhcpServers": [
1268+
{"srvrAddr": srvr["srvr_ip"], "srvrVrf": srvr["srvr_vrf"]} for srvr in net.get("dhcp_servers", [])
1269+
],
12471270
"loopbackId": net.get("dhcp_loopback_id", ""),
12481271
"mcastGroup": net.get("multicast_group_address", ""),
12491272
"gatewayIpV6Address": net.get("gw_ipv6_subnet", ""),
@@ -1277,6 +1300,30 @@ def update_create_params(self, net):
12771300
template_conf["vrfDhcp2"] = ""
12781301
if template_conf["vrfDhcp3"] is None:
12791302
template_conf["vrfDhcp3"] = ""
1303+
if template_conf["dhcpServers"] == []:
1304+
dhcp_srvr_list = []
1305+
if template_conf["dhcpServerAddr1"] != "" and template_conf["vrfDhcp"] != "":
1306+
dhcp_srvr_list.append({"srvrAddr": template_conf["dhcpServerAddr1"], "srvrVrf": template_conf["vrfDhcp"]})
1307+
if template_conf["dhcpServerAddr2"] != "" and template_conf["vrfDhcp2"] != "":
1308+
dhcp_srvr_list.append({"srvrAddr": template_conf["dhcpServerAddr2"], "srvrVrf": template_conf["vrfDhcp2"]})
1309+
if template_conf["dhcpServerAddr3"] != "" and template_conf["vrfDhcp3"] != "":
1310+
dhcp_srvr_list.append({"srvrAddr": template_conf["dhcpServerAddr3"], "srvrVrf": template_conf["vrfDhcp3"]})
1311+
if dhcp_srvr_list != []:
1312+
template_conf["dhcpServers"] = json.dumps(dict(dhcpServers=dhcp_srvr_list), separators=(",", ":"))
1313+
else:
1314+
template_conf["dhcpServers"] = ""
1315+
elif template_conf["dhcpServers"] != []:
1316+
dhcp_srvr_list = template_conf["dhcpServers"]
1317+
if dhcp_srvr_list[0:1]:
1318+
template_conf["dhcpServerAddr1"] = dhcp_srvr_list[0]["srvrAddr"]
1319+
template_conf["vrfDhcp"] = dhcp_srvr_list[0]["srvrVrf"]
1320+
if dhcp_srvr_list[1:2]:
1321+
template_conf["dhcpServerAddr2"] = dhcp_srvr_list[1]["srvrAddr"]
1322+
template_conf["vrfDhcp2"] = dhcp_srvr_list[1]["srvrVrf"]
1323+
if dhcp_srvr_list[2:3]:
1324+
template_conf["dhcpServerAddr3"] = dhcp_srvr_list[2]["srvrAddr"]
1325+
template_conf["vrfDhcp3"] = dhcp_srvr_list[2]["srvrVrf"]
1326+
template_conf["dhcpServers"] = json.dumps(dict(dhcpServers=dhcp_srvr_list), separators=(",", ":"))
12801327
if template_conf["loopbackId"] is None:
12811328
template_conf["loopbackId"] = ""
12821329
if self.is_ms_fabric is True:
@@ -1294,6 +1341,7 @@ def update_create_params(self, net):
12941341
template_conf["secondaryGW3"] = ""
12951342
if template_conf["secondaryGW4"] is None:
12961343
template_conf["secondaryGW4"] = ""
1344+
12971345
if self.dcnm_version > 11:
12981346
if template_conf["SVI_NETFLOW_MONITOR"] is None:
12991347
template_conf["SVI_NETFLOW_MONITOR"] = ""
@@ -1375,6 +1423,7 @@ def get_have(self):
13751423
"vrfDhcp": json_to_dict.get("vrfDhcp", ""),
13761424
"vrfDhcp2": json_to_dict.get("vrfDhcp2", ""),
13771425
"vrfDhcp3": json_to_dict.get("vrfDhcp3", ""),
1426+
"dhcpServers": json_to_dict.get("dhcpServers", ""),
13781427
"loopbackId": json_to_dict.get("loopbackId", ""),
13791428
"mcastGroup": json_to_dict.get("mcastGroup", ""),
13801429
"gatewayIpV6Address": json_to_dict.get("gatewayIpV6Address", ""),
@@ -1428,6 +1477,7 @@ def get_have(self):
14281477
"vrfDhcp": json_to_dict.get("vrfDhcp", ""),
14291478
"vrfDhcp2": json_to_dict.get("vrfDhcp2", ""),
14301479
"vrfDhcp3": json_to_dict.get("vrfDhcp3", ""),
1480+
"dhcpServers": json_to_dict.get("dhcpServers", ""),
14311481
"loopbackId": json_to_dict.get("loopbackId", ""),
14321482
"mcastGroup": json_to_dict.get("mcastGroup", ""),
14331483
"gatewayIpV6Address": json_to_dict.get("gatewayIpV6Address", ""),
@@ -1868,6 +1918,7 @@ def get_diff_merge(self, replace=False):
18681918
dhcp1_vrf_changed = {}
18691919
dhcp2_vrf_changed = {}
18701920
dhcp3_vrf_changed = {}
1921+
dhcp_servers_changed = {}
18711922
dhcp_loopback_changed = {}
18721923
multicast_group_address_changed = {}
18731924
gwv6_changed = {}
@@ -1904,6 +1955,7 @@ def get_diff_merge(self, replace=False):
19041955
dhcp1_vrf_chg,
19051956
dhcp2_vrf_chg,
19061957
dhcp3_vrf_chg,
1958+
dhcp_servers_chg,
19071959
dhcp_loopbk_chg,
19081960
mcast_grp_chg,
19091961
gwv6_chg,
@@ -1931,6 +1983,7 @@ def get_diff_merge(self, replace=False):
19311983
dhcp1_vrf_changed.update({want_c["networkName"]: dhcp1_vrf_chg})
19321984
dhcp2_vrf_changed.update({want_c["networkName"]: dhcp2_vrf_chg})
19331985
dhcp3_vrf_changed.update({want_c["networkName"]: dhcp3_vrf_chg})
1986+
dhcp_servers_changed.update({want_c["networkName"]: dhcp_servers_chg})
19341987
dhcp_loopback_changed.update({want_c["networkName"]: dhcp_loopbk_chg})
19351988
if self.is_ms_fabric is False:
19361989
multicast_group_address_changed.update({want_c["networkName"]: mcast_grp_chg})
@@ -2043,6 +2096,7 @@ def get_diff_merge(self, replace=False):
20432096
or dhcp1_vrf_changed.get(want_a["networkName"], False)
20442097
or dhcp2_vrf_changed.get(want_a["networkName"], False)
20452098
or dhcp3_vrf_changed.get(want_a["networkName"], False)
2099+
or dhcp_servers_changed.get(want_a["networkName"], False)
20462100
or dhcp_loopback_changed.get(want_a["networkName"], False)
20472101
or multicast_group_address_changed.get(want_a["networkName"], False)
20482102
or gwv6_changed.get(want_a["networkName"], False)
@@ -2151,6 +2205,7 @@ def format_diff(self):
21512205
found_c.update({"dhcp_srvr1_vrf": json_to_dict.get("vrfDhcp", "")})
21522206
found_c.update({"dhcp_srvr2_vrf": json_to_dict.get("vrfDhcp2", "")})
21532207
found_c.update({"dhcp_srvr3_vrf": json_to_dict.get("vrfDhcp3", "")})
2208+
found_c.update({"dhcp_servers": json_to_dict.get("dhcpServers", "")})
21542209
found_c.update({"dhcp_loopback_id": json_to_dict.get("loopbackId", "")})
21552210
found_c.update({"multicast_group_address": json_to_dict.get("mcastGroup", "")})
21562211
found_c.update({"gw_ipv6_subnet": json_to_dict.get("gatewayIpV6Address", "")})
@@ -2531,6 +2586,7 @@ def push_to_remote(self, is_rollback=False):
25312586
"vrfDhcp": json_to_dict.get("vrfDhcp", ""),
25322587
"vrfDhcp2": json_to_dict.get("vrfDhcp2", ""),
25332588
"vrfDhcp3": json_to_dict.get("vrfDhcp3", ""),
2589+
"dhcpServers": json_to_dict.get("dhcpServers", ""),
25342590
"loopbackId": json_to_dict.get("loopbackId", ""),
25352591
"mcastGroup": json_to_dict.get("mcastGroup", ""),
25362592
"gatewayIpV6Address": json_to_dict.get("gatewayIpV6Address", ""),
@@ -2671,6 +2727,7 @@ def validate_input(self):
26712727
dhcp_srvr1_vrf=dict(type="str", length_max=32),
26722728
dhcp_srvr2_vrf=dict(type="str", length_max=32),
26732729
dhcp_srvr3_vrf=dict(type="str", length_max=32),
2730+
dhcp_servers=dict(type="list", elements="dict", default=[]),
26742731
dhcp_loopback_id=dict(type="int", range_min=0, range_max=1023),
26752732
multicast_group_address=dict(type="ipv4", default=mcast_group_addr),
26762733
gw_ipv6_subnet=dict(type="ipv6_subnet", default=""),
@@ -2737,6 +2794,7 @@ def validate_input(self):
27372794
dhcp_srvr1_vrf=dict(type="str", length_max=32),
27382795
dhcp_srvr2_vrf=dict(type="str", length_max=32),
27392796
dhcp_srvr3_vrf=dict(type="str", length_max=32),
2797+
dhcp_servers=dict(type="list", elements="dict", default=[]),
27402798
dhcp_loopback_id=dict(type="int", range_min=0, range_max=1023),
27412799
multicast_group_address=dict(type="ipv4", default=mcast_group_addr),
27422800
gw_ipv6_subnet=dict(type="ipv6_subnet", default=""),
@@ -2798,16 +2856,20 @@ def validate_input(self):
27982856
if net.get("vrf_name", "") is None:
27992857
invalid_params.append("vrf_name is required for L3 Networks")
28002858

2801-
if (
2802-
(net.get("dhcp_srvr1_ip") and not net.get("dhcp_srvr1_vrf"))
2803-
or (net.get("dhcp_srvr1_vrf") and not net.get("dhcp_srvr1_ip"))
2804-
or (net.get("dhcp_srvr2_ip") and not net.get("dhcp_srvr2_vrf"))
2805-
or (net.get("dhcp_srvr2_vrf") and not net.get("dhcp_srvr2_ip"))
2806-
or (net.get("dhcp_srvr3_ip") and not net.get("dhcp_srvr3_vrf"))
2807-
or (net.get("dhcp_srvr3_vrf") and not net.get("dhcp_srvr3_ip"))
2808-
):
2859+
if any(has_partial_dhcp_config(srvr) for srvr in [
2860+
dict(srvr_ip=net.get("dhcp_srvr1_ip"), srvr_vrf=net.get("dhcp_srvr1_vrf")),
2861+
dict(srvr_ip=net.get("dhcp_srvr2_ip"), srvr_vrf=net.get("dhcp_srvr2_vrf")),
2862+
dict(srvr_ip=net.get("dhcp_srvr3_ip"), srvr_vrf=net.get("dhcp_srvr3_vrf")),
2863+
]):
28092864
invalid_params.append("DHCP server IP should be specified along with DHCP server VRF")
28102865

2866+
if net.get("dhcp_servers"):
2867+
dhcp_servers = net.get("dhcp_servers")
2868+
if len(dhcp_servers) > 16:
2869+
invalid_params.append("A maximum of 16 DHCP servers can be specified")
2870+
if any(has_partial_dhcp_config(srvr) for srvr in dhcp_servers):
2871+
invalid_params.append("DHCP server IP should be specified along with DHCP server VRF")
2872+
28112873
if self.dcnm_version == 11:
28122874
if net.get("netflow_enable") or net.get("intfvlan_nf_monitor") or net.get("vlan_nf_monitor"):
28132875
invalid_params.append("Netflow configurations are supported only on NDFC")
@@ -2982,6 +3044,26 @@ def dcnm_update_network_information(self, want, have, cfg):
29823044
if cfg.get("dhcp_srvr3_vrf", None) is None:
29833045
json_to_dict_want["vrfDhcp3"] = json_to_dict_have["vrfDhcp3"]
29843046

3047+
if cfg.get("dhcp_servers", None) is None:
3048+
want_have_dhcp_servers = [None] * 3
3049+
if cfg.get("dhcp_srvr1_ip", None) is not None:
3050+
want_have_dhcp_servers[0] = dict(srvrAddr=cfg.get("dhcp_srvr1_ip"), srvrVrf=cfg.get("dhcp_srvr1_vrf"))
3051+
elif json_to_dict_have["dhcpServerAddr1"] != "":
3052+
want_have_dhcp_servers[0] = dict(srvrAddr=json_to_dict_have["dhcpServerAddr1"], srvrVrf=json_to_dict_have["vrfDhcp"])
3053+
if cfg.get("dhcp_srvr2_ip", None) is not None:
3054+
want_have_dhcp_servers[1] = dict(srvrAddr=cfg.get("dhcp_srvr2_ip"), srvrVrf=cfg.get("dhcp_srvr2_vrf"))
3055+
elif json_to_dict_have["dhcpServerAddr2"] != "":
3056+
want_have_dhcp_servers[1] = dict(srvrAddr=json_to_dict_have["dhcpServerAddr2"], srvrVrf=json_to_dict_have["vrfDhcp2"])
3057+
if cfg.get("dhcp_srvr3_ip", None) is not None:
3058+
want_have_dhcp_servers[2] = dict(srvrAddr=cfg.get("dhcp_srvr3_ip"), srvrVrf=cfg.get("dhcp_srvr3_vrf"))
3059+
elif json_to_dict_have["dhcpServerAddr3"] != "":
3060+
want_have_dhcp_servers[2] = dict(srvrAddr=json_to_dict_have["dhcpServerAddr3"], srvrVrf=json_to_dict_have["vrfDhcp3"])
3061+
want_have_dhcp_servers = [srvr for srvr in want_have_dhcp_servers[:] if srvr is not None]
3062+
if want_have_dhcp_servers != []:
3063+
json_to_dict_want["dhcpServers"] = json.dumps(dict(dhcpServers=want_have_dhcp_servers, separators=(",", ":")))
3064+
else:
3065+
json_to_dict_want["dhcpServers"] = json_to_dict_have["dhcpServers"]
3066+
29853067
if cfg.get("dhcp_loopback_id", None) is None:
29863068
json_to_dict_want["loopbackId"] = json_to_dict_have["loopbackId"]
29873069

0 commit comments

Comments
 (0)