1
0
mirror of https://github.com/peeringdb/peeringdb.git synced 2024-05-11 05:55:09 +00:00
Files
peeringdb-peeringdb/peeringdb_server/management/commands/pdb_undelete.py

176 lines
6.4 KiB
Python
Raw Normal View History

2018-11-08 19:45:21 +00:00
from django.core.management.base import BaseCommand
from peeringdb_server.models import REFTAG_MAP
import reversion
import json
2019-02-15 17:46:20 +00:00
import re
2018-11-08 19:45:21 +00:00
class Command(BaseCommand):
help = "Undo object deletion"
def add_arguments(self, parser):
2019-12-05 16:57:52 +00:00
parser.add_argument(
"reftag", nargs="?", help="object reftag (net, ix, fac etc..)"
)
2018-11-08 19:45:21 +00:00
parser.add_argument("id", nargs="?", help="object id")
2019-12-05 16:57:52 +00:00
parser.add_argument(
"version_id", nargs="?", help="object version id where it was deleted"
)
parser.add_argument(
"--commit", action="store_true", help="will commit the changes"
)
2018-11-08 19:45:21 +00:00
def log(self, msg):
if self.commit:
self.stdout.write(msg)
else:
self.stdout.write(f"[pretend] {msg}")
2018-11-08 19:45:21 +00:00
2019-02-15 17:46:20 +00:00
def log_err(self, msg):
self.log(f"[error] {msg}")
2019-02-15 17:46:20 +00:00
def log_warn(self, msg):
self.log(f"[warning] {msg}")
2019-02-15 17:46:20 +00:00
2018-11-08 19:45:21 +00:00
def handle(self, *args, **options):
self.commit = options.get("commit", False)
self.version_id = options.get("version_id")
2019-02-15 17:46:20 +00:00
self.suppress_warning = None
self.version = version = reversion.models.Version.objects.get(
2019-12-05 16:57:52 +00:00
id=self.version_id
)
2018-11-08 19:45:21 +00:00
self.date = version.revision.date_created
self.log(f"UNDELETING FROM DATE: {self.date}")
2018-11-08 19:45:21 +00:00
self.undelete(options.get("reftag"), options.get("id"))
2019-02-15 17:46:20 +00:00
def handle_netixlan(self, netixlan):
model = REFTAG_MAP["netixlan"]
conflict_ip4, conflict_ip6 = netixlan.ipaddress_conflict()
if conflict_ip4:
# ipv4 exists in another netixlan now
2019-12-05 16:57:52 +00:00
others = model.objects.filter(ipaddr4=netixlan.ipaddr4, status="ok")
for other in [o for o in others if o.ixlan.ix_id == netixlan.ixlan.ix_id]:
2019-02-15 17:46:20 +00:00
# netixlan is at same ix as the one being undeleted, delete the other
# one so we can proceed with undeletion
2019-12-05 16:57:52 +00:00
self.log(
"Found duplicate netixlan at same ix: {} - deleting".format(
other.ipaddr4
)
)
2019-02-15 17:46:20 +00:00
if self.commit:
other.delete()
else:
# when in pretend mode we need suppress the next warning as we
# are not deleting the conflict
self.suppress_warning = True
2019-12-05 16:57:52 +00:00
for other in [o for o in others if o.ixlan.ix_id != netixlan.ixlan.ix_id]:
2019-02-15 17:46:20 +00:00
# unless ipv4 also exists in a netixlan that is NOT at the same ix
# then we need the warning again
self.suppress_warning = False
if conflict_ip6:
# ipv6 exists in another netixlan now
2019-12-05 16:57:52 +00:00
others = model.objects.filter(ipaddr6=netixlan.ipaddr6, status="ok")
for other in [o for o in others if o.ixlan.ix_id == netixlan.ixlan.ix_id]:
2019-02-15 17:46:20 +00:00
# netixlan is at same ix as the one being undeleted, delete the other
# one so we can proceed with undeletion
2019-12-05 16:57:52 +00:00
self.log(
"Found duplicate netixlan at same ix: {} - deleting".format(
other.ipaddr6
)
)
2019-02-15 17:46:20 +00:00
if self.commit:
other.delete()
else:
# when in pretend mode we need suppress the next warning as we
# are not deleting the conflict
self.suppress_warning = True
2019-12-05 16:57:52 +00:00
for other in [o for o in others if o.ixlan.ix_id != netixlan.ixlan.ix_id]:
2019-02-15 17:46:20 +00:00
# unless ipv6 also exists in a netixlan that is NOT at the same ix
# then we need the warning again
self.suppress_warning = False
2018-11-08 19:45:21 +00:00
def undelete(self, reftag, _id, parent=None, date=None):
cls = REFTAG_MAP.get(reftag)
obj = cls.objects.get(id=_id)
2019-02-15 17:46:20 +00:00
self.suppress_warning = False
def _label(obj):
if hasattr(obj, "descriptive_name"):
return obj.descriptive_name
return obj
2018-11-08 19:45:21 +00:00
if date:
2019-12-05 16:57:52 +00:00
version = (
reversion.models.Version.objects.get_for_object(obj)
.filter(revision__date_created__lt=date)
.order_by("revision__date_created")
.last()
)
2018-11-08 19:45:21 +00:00
try:
2019-12-05 16:57:52 +00:00
status = json.loads(version.serialized_data)[0].get("fields")["status"]
2018-11-08 19:45:21 +00:00
except:
status = None
if status == "deleted":
2019-02-15 17:46:20 +00:00
self.log_warn(
2018-11-08 19:45:21 +00:00
"{} was already deleted at snapshot, skipping ..".format(
2019-12-05 16:57:52 +00:00
_label(obj)
)
)
2018-11-08 19:45:21 +00:00
return
can_undelete_obj = True
for field in cls._meta.get_fields():
if field.is_relation:
if field.many_to_one:
# relation parent
try:
relation = getattr(obj, field.name)
except:
continue
if relation.status == "deleted" and relation != parent:
can_undelete_obj = False
2019-02-15 17:46:20 +00:00
self.log_warn(
2019-12-05 16:57:52 +00:00
"Cannot undelete {}, dependent relation marked as deleted: {}".format(
_label(obj), relation
)
)
2018-11-08 19:45:21 +00:00
if not can_undelete_obj:
return
if obj.status == "deleted":
obj.status = "ok"
2019-02-15 17:46:20 +00:00
self.log("Undeleting {}".format(_label(obj)))
handler = getattr(self, f"handle_{reftag}", None)
2019-02-15 17:46:20 +00:00
if handler:
handler(obj)
try:
obj.clean()
if self.commit:
obj.save()
except Exception as exc:
if not self.suppress_warning:
2019-12-05 16:57:52 +00:00
self.log_warn("Cannot undelete {}: {}".format(_label(obj), exc))
2018-11-08 19:45:21 +00:00
for field in cls._meta.get_fields():
if field.is_relation:
if not field.many_to_one:
# relation child
try:
relation = getattr(obj, field.name)
except:
continue
if not hasattr(field.related_model, "ref_tag"):
continue
for child in relation.filter(updated__gte=self.date):
2019-12-05 16:57:52 +00:00
self.undelete(child.ref_tag, child.id, obj, date=self.date)