Skip to content

Commit

Permalink
Add an "override" option and refactors "force" for host remove.
Browse files Browse the repository at this point in the history
`-force` is no longer a catch-all for `host remove`. If the host has cnames, ptrs, mx, srvs or ipadresses across multiple vlans, the user needs to declare a desire to override each of these explicitly. Note that `-force` alone works on multiple ipadresses from the same VLAN.

Example usage: `-override cnames,ipaddresses,mxs`.

This would allow deletion / removal of a host with cnames, ipadresses across different vlans, and mx set. However, any ptr or srv RRs will still cause the deletion to cancel.
  • Loading branch information
terjekv committed Feb 20, 2024
1 parent 21ec4da commit 018959a
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 27 deletions.
80 changes: 54 additions & 26 deletions mreg_cli/commands/host_submodules/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
host_info_by_name,
host_info_by_name_or_ip,
)
from mreg_cli.utilities.network import get_network_by_ip, ips_are_in_same_vlan
from mreg_cli.utilities.output import output_host_info, output_ip_info
from mreg_cli.utilities.shared import convert_wildcard_to_regex, format_mac
from mreg_cli.utilities.validators import is_valid_email, is_valid_ip, is_valid_mac
Expand Down Expand Up @@ -142,52 +143,74 @@ def add(args: argparse.Namespace) -> None:
Flag("-force", action="store_true", description="Enable force."),
Flag(
"-override",
short_desc="Override list",
description="Override list for forcing (cname, naptr, srv, ptr)",
short_desc="Comma separated override list",
description=(
"Comma separated overrides for forced removal. "
"Supports the following overrides: 'cname', 'naptr', 'mxs', 'srv', 'ptr'. "
"Example usage: '-override cnames,ipaddresses,mxs'"
),
metavar="OVERRIDE",
),
],
)
def remove(args: argparse.Namespace) -> None:
"""Remove host.
:param args: argparse.Namespace (name, force)
:param args: argparse.Namespace (name, force, override)
"""
# Get host info or raise exception
info = host_info_by_name_or_ip(args.name)
overrides: List[str] = args.override.split(",") if args.override else []

def forced(override_required: str = None) -> bool:
# If force wasn't set at all, return false.
if not args.force:
return False

# If we require an override, check if it's in the list of provided overrides.
if override_required:
return override_required in overrides

# We didn't require an override, so we only need to check for force.
if args.force:
return True

# And the fallback is "no".
return False

warn_msg = ""
warnings: List[str] = []
# Require force if host has any cnames.
cnames = info["cnames"]
if len(cnames):
if not forced("cnames"):
warn_msg += "{} cnames. ".format(len(cnames))

# Require force if host has multiple A/AAAA records
if len(info["ipaddresses"]) > 1 and not forced():
warn_msg += "{} ipaddresses. ".format(len(info["ipaddresses"]))
if info["cnames"] and not args.force:
warnings.append(f" {len(info['cnames'])} cnames, override with 'cnames'")
for cname in info["cnames"]:
warnings.append(f" - {cname['name']}")

# Require force if host has multiple A/AAAA records and they are not in the same VLAN.
if len(info["ipaddresses"]) > 1:
same_vlan = ips_are_in_same_vlan([ip["ipaddress"] for ip in info["ipaddresses"]])

if same_vlan and not forced():
warnings.append(" multiple ipaddresses on the same VLAN. Must use 'force'.")
elif not same_vlan and not forced("ipaddresses"):
warnings.append(
" {} ipaddresses on distinct VLANs, override with 'ipadresses'".format(
len(info["ipaddresses"])
)
)
for ip in info["ipaddresses"]:
vlan = get_network_by_ip(ip["ipaddress"]).get("vlan")
warnings.append(f" - {ip['ipaddress']} (vlan: {vlan})")

if len(info["mxs"]) > 0 and not args.force:
warn_msg += "MX record(s). "
if info["mxs"] and not forced("mxs"):
warnings.append(f" {len(info['mxs'])} MX records, override with 'mxs'")
for mx in info["mxs"]:
warnings.append(f" - {mx['mx']} (priority: {mx['priority']})")

# Require force if host has any NAPTR records. Delete the NAPTR records if
# force
path = "/api/v1/naptrs/"
naptrs = get_list(path, params={"host": info["id"]})
if len(naptrs) > 0:
if not args.force:
warn_msg += "{} NAPTR records. ".format(len(naptrs))
if not forced("naptrs"):
warnings.append(" {} NAPTR records. ".format(len(naptrs)))
for naptr in naptrs:
warnings.append(f" - {naptr['replacement']}")
else:
for naptr in naptrs:
cli_info(
Expand All @@ -201,8 +224,10 @@ def forced(override_required: str = None) -> bool:
path = "/api/v1/srvs/"
srvs = get_list(path, params={"host__name": info["name"]})
if len(srvs) > 0:
if not args.force:
warn_msg += "{} SRV records. ".format(len(srvs))
if not forced("srvs"):
warnings.append(f" {len(srvs)} SRV records, override with 'srvs'")
for srv in srvs:
warnings.append(f" - {srv['name']}")
else:
for srv in srvs:
cli_info(
Expand All @@ -214,8 +239,10 @@ def forced(override_required: str = None) -> bool:

# Require force if host has any PTR records. Delete the PTR records if force
if len(info["ptr_overrides"]) > 0:
if not args.force:
warn_msg += "{} PTR records. ".format(len(info["ptr_overrides"]))
if not forced("ptr"):
warnings.append(f" {len(info['ptr_overrides'])} PTR records, override with 'ptr'")
for ptr in info["ptr_overrides"]:
warnings.append(f" - {ptr['ipaddress']}")
else:
for ptr in info["ptr_overrides"]:
cli_info(
Expand All @@ -231,8 +258,9 @@ def forced(override_required: str = None) -> bool:
info["ipaddress"] = info["ipaddresses"][0]["ipaddress"]

# Warn user and raise exception if any force requirements was found
if warn_msg:
cli_warning("{} has: {}Must force".format(info["name"], warn_msg))
if warnings:
warn_msg = "\n".join(warnings)
cli_warning("{} will require override for deletion:\n{}".format(info["name"], warn_msg))

# Delete host
path = f"/api/v1/hosts/{info['name']}"
Expand Down
2 changes: 1 addition & 1 deletion mreg_cli/utilities/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def ips_are_in_same_vlan(ips: List[str]) -> bool:

last_vlan = network["vlan"]

return False
return True


def get_network_by_ip(ip: str) -> Dict[str, Any]:
Expand Down

0 comments on commit 018959a

Please sign in to comment.