import re import reversion from django.core.management.base import BaseCommand, CommandError import peeringdb_server.models as pdbm from peeringdb_server.mail import mail_users_entity_merge def soft_delete(fac, cmd): # overriding for k in fac._handleref.delete_cascade: q = getattr(fac, k).exclude(status="deleted") for c in q: if c not in cmd.moved: soft_delete(c, cmd) cmd.log("soft deleting %s" % fac) if cmd.commit: fac.status = "deleted" fac.save() class Command(BaseCommand): help = "merge facilities" pretend = False def add_arguments(self, parser): parser.add_argument( "--commit", action="store_true", help="will commit the fac merge" ) parser.add_argument("--target", help="target of the merge (facility id)") parser.add_argument("--ids", help="comma separated list of facility ids") parser.add_argument( "--match", help="all facs with matching names will be merged (regex)" ) def log(self, msg): if not self.commit: self.stdout.write("%s [pretend]" % msg) else: self.stdout.write(msg) @reversion.create_revision() def handle(self, *args, **options): self.commit = options.get("commit", False) self.moved = [] self.target = options.get("target", 0) if not self.target: msg = "Target ID required (--target)" self.log(msg) raise CommandError(msg) self.match = options.get("match", None) self.ids = options.get("ids", "") facs = [] moved = self.moved if self.match: if self.ids: msg = "ids and match are mutually exclusive" self.log(msg) raise CommandError(msg) self.log("Merging all facilities matching '%s'" % self.match) for fac in pdbm.Facility.objects.exclude(status="deleted"): if re.match(self.match, fac.name, re.IGNORECASE): facs.append(fac) elif self.ids: self.ids = self.ids.split(",") self.log( "Merging facilities {} -> {}".format(", ".join(self.ids), self.target) ) for fac in pdbm.Facility.objects.filter(id__in=self.ids): facs.append(fac) else: msg = "IDs or match is required" self.log(msg) raise CommandError(msg) self.target = pdbm.Facility.objects.get(id=self.target) if self.target.status == "deleted": self.target.status = "ok" if self.commit: self.target.save() for fac in facs: if fac.id == self.target.id: continue self.log("Merging %s (%d) .." % (fac, fac.id)) for netfac in pdbm.NetworkFacility.objects.filter(facility=fac).exclude( status="deleted" ): netfac_other = pdbm.NetworkFacility.objects.filter( facility=self.target, network_id=netfac.network_id ) # we check if the target fac already has a netfac to the same network (that is currently undeleted), if it does we skip it if netfac_other.exclude(status="deleted").exists(): self.log( " - netfac %s : connection already exists at target, skipping." % netfac ) continue # if it exists but is currently delete, we simply undelete it elif netfac_other.exists(): netfac_other = netfac_other.first() netfac_other.local_asn = netfac.local_asn netfac_other.avail_sonet = netfac.avail_sonet netfac_other.avail_ethernet = netfac.avail_ethernet netfac_other.avail_atm = netfac.avail_atm netfac_other.status = "ok" self.log(" - netfac %s (undeleting and updating)" % netfac_other) moved.append(netfac_other) if self.commit: netfac_other.save() # if it doesnt exist, we update the facility to the target facility and save else: self.log(" - netfac %s" % netfac) netfac.facility = self.target moved.append(netfac) if self.commit: netfac.save() for ixfac in pdbm.InternetExchangeFacility.objects.filter( facility=fac ).exclude(status="deleted"): ixfac_other = pdbm.InternetExchangeFacility.objects.filter( facility=self.target, ix=ixfac.ix ) # we check if the target fac already has an ixfac to the same exchange (that is currently undeleted), if it does, we skip it if ixfac_other.exclude(status="deleted").exists(): self.log( " - ixfac %s : connection already exists at target, skipping." % ixfac ) continue # if it exists but is currently deleted, we undelete and copy elif ixfac_other.exists(): ixfac_other = ixfac_other.first() ixfac_other.status = "ok" moved.append(ixfac_other) self.log(" - ixfac %s (undeleting and updating)" % ixfac_other) if self.commit: ixfac_other.save() # if it doesnt exist, we update the facility to the target facility and save else: self.log(" - ixfac %s" % ixfac) ixfac.facility = self.target moved.append(ixfac) if self.commit: ixfac.save() soft_delete(fac, self) if self.commit: mail_users_entity_merge( fac.org.admin_usergroup.user_set.all(), self.target.org.admin_usergroup.user_set.all(), fac, self.target, )