def new_prefix(self, vrf, afi, prefix, interface=None, description=None): # Enable IPv4 AFI on VRF, if not set if afi == "4" and not vrf.afi_ipv4: self.info("Enabling IPv4 AFI on VRF %s (%s)" % (vrf.name, vrf.rd)) if self.to_save: vrf.afi_ipv4 = True vrf.save() # Enable IPv6 AFI on VRF, if not set if afi == "6" and not vrf.afi_ipv6: self.info("Enabling IPv6 AFI on VRF %s (%s)" % (vrf.name, vrf.rd)) if self.to_save: vrf.afi_ipv6 = True vrf.save() # Save prefix self.info("IPv%s prefix found: %s:%s" % (afi, vrf, prefix)) self.new_prefixes += [{ "vrf": vrf, "prefix": prefix, "object": self.object, "interface": interface, "description": description }] if self.to_save: Prefix(vrf=vrf, afi=afi, prefix=prefix, description=description).save()
def is_discovery_enabled(cls, vrf, afi, address): prefix = Prefix.get_parent(vrf, afi, address) k = "ip-discovery-enable-%s" % prefix.id ds = cache.get(k) if ds: return ds == "E" r = prefix.effective_ip_discovery cache.set(k, r, 600) return r == "E"
def has_address_permission(self, vrf, address): """ Check discovery has permission to manipulate address :param vrf: VRF instance :param address: DiscoveredAddress instance :return: """ parent = Prefix.get_parent(vrf, "6" if ":" in address.address else "4", address.address) if parent: return parent.effective_address_discovery == "E" return False
def has_prefix_permission(self, vrf, prefix): """ Check discovery has permission to manipulate prefix :param vrf: VRF instance :param prefix: DiscoveredPrefix instance :return: """ parent = Prefix.get_parent(vrf, "6" if ":" in prefix.prefix else "4", prefix.prefix) if parent: return parent.effective_prefix_discovery == "E" return False
def create_prefix(self, prefix): """ Create new prefix :param prefix: DiscoveredPrefix instance :return: """ if self.is_ignored_prefix(prefix): return vrf = VRF.get_by_vpn_id(prefix.vpn_id) self.ensure_afi(vrf, prefix) if not self.has_prefix_permission(vrf, prefix): self.logger.debug( "Do not creating vpn_id=%s asn=%s prefix=%s: Disabled by policy", prefix.vpn_id, prefix.asn.asn if prefix.asn else None, prefix.prefix, ) metrics["prefix_creation_denied"] += 1 return p = Prefix( vrf=vrf, prefix=prefix.prefix, name=self.get_prefix_name(prefix), profile=prefix.profile, asn=prefix.asn, description=prefix.description, source=prefix.source, ) self.logger.info( "Creating prefix %s (%s): name=%s profile=%s source=%s", p.prefix, p.vrf.name, p.name, p.profile.name, p.source, ) p.save() self.fire_seen(p) metrics["prefix_created"] += 1
def view_quickjump(self, request, vrf_id, afi): """ Quickjump to closest suitable block """ # Interpolate string to valid IPv4 address def interpolate_ipv4(s): p = s.split(".") if len(p) > 4: return None elif len(p) < 4: p += ["0"] * (4 - len(p)) s = ".".join(p) if not is_ipv4(s): return None return s # Interpolate string to valid IPv6 address # @todo: implement def interpolate_ipv6(s): if not is_ipv6(s): return None return s vrf = self.get_object_or_404(VRF, id=int(vrf_id)) if (afi == "4" and not vrf.afi_ipv4) or (afi == "6" and not vrf.afi_ipv6): return self.response_forbidden("Invalid AFI") if request.POST: form = self.QuickJumpForm(request.POST) if form.is_valid(): prefix = form.cleaned_data["jump"].strip() # Interpolate prefix if afi == "4": prefix = interpolate_ipv4(prefix) else: prefix = interpolate_ipv6(prefix) if not prefix: self.message_user(request, _("Invalid address")) return self.response_redirect_to_referrer(request) # Find prefix prefix = Prefix.get_parent(vrf, afi, prefix).prefix # Redirect self.message_user( request, _("Redirected to %(prefix)s") % {"prefix": prefix}) return self.response_redirect("ip:ipam:vrf_index", vrf.id, afi, prefix) return self.response_redirect_to_referrer(request)
def view_quickjump(self, request, vrf_id, afi): """ Quickjump to closest suitable block """ # Interpolate string to valid IPv4 address def interpolate_ipv4(s): p = s.split(".") if len(p) > 4: return None elif len(p) < 4: p += ["0"] * (4 - len(p)) s = ".".join(p) if not is_ipv4(s): return None return s # Interpolate string to valid IPv6 address # @todo: implement def interpolate_ipv6(s): if not is_ipv6(s): return None return s vrf = self.get_object_or_404(VRF, id=int(vrf_id)) if (afi == "4" and not vrf.afi_ipv4) or (afi == "6" and not vrf.afi_ipv6): return self.response_forbidden("Invalid AFI") if request.POST: d = orjson.loads(request.body) prefix = d["jump"].strip() # Interpolate prefix if afi == "4": prefix = interpolate_ipv4(prefix) else: prefix = interpolate_ipv6(prefix) if not prefix: return self.response_bad_request("Invalid address") # Find prefix prefix = Prefix.get_parent(vrf, afi, prefix) return {"id": prefix.id} return self.response_redirect_to_referrer(request)
def view_vrf_index(self, request, vrf_id, afi, prefix): """ Display VRF Index """ # Validate vrf = self.get_object_or_404(VRF, id=int(vrf_id)) if (afi == "4" and (not is_ipv4_prefix(prefix)) or not vrf.afi_ipv4) or ( afi == "6" and (not is_ipv6_prefix(prefix) or not vrf.afi_ipv6) ): return self.response_forbidden("Invalid prefix") prefix = self.get_object_or_404(Prefix, vrf=vrf, afi=afi, prefix=prefix) # Get prefix path path = [] p = prefix.parent while p: path = [p] + path p = p.parent # List of nested prefixes # @todo: prefetch_related prefixes = list(prefix.children_set.select_related().order_by("prefix")) # Bulk utilization Prefix.update_prefixes_usage(prefixes) # Get permissions user = request.user can_view = prefix.can_view(user) can_change = prefix.can_change(user) can_bind_vc = can_change and Permission.has_perm(user, "ip:ipam:bind_vc") can_change_maintainers = user.is_superuser can_add_prefix = can_change can_add_address = can_change and len(prefixes) == 0 can_edit_special = prefix.effective_prefix_special_address == "I" # Bookmarks has_bookmark = prefix.has_bookmark(user) bookmarks = PrefixBookmark.user_bookmarks(user, vrf=vrf, afi=afi) s_bookmarks = set(b.prefix for b in bookmarks) # Add free prefixes free_prefixes = list(IP.prefix(prefix.prefix).iter_free([pp.prefix for pp in prefixes])) l_prefixes = sorted( ( [(True, IP.prefix(pp.prefix), pp, pp.prefix in s_bookmarks) for pp in prefixes] + [(False, pp, None, None) for pp in free_prefixes] ), key=lambda x: x[1], ) # List of nested addresses # @todo: prefetch_related addresses = list(prefix.address_set.select_related().order_by("address")) # Prepare block info prefix_info = [("Network", prefix.prefix)] if afi == "4": prefix_info += [ ("Broadcast", prefix.broadcast), ("Netmask", prefix.netmask), ("Widlcard", prefix.wildcard), ("Size", prefix.size), ("Usage", prefix.usage_percent), ("Usage Address", prefix.address_usage_percent), ] if addresses: prefix_info += [("Used addresses", len(addresses))] if afi == "4": free = prefix.size - len(addresses) prefix_info += [("Free addresses", free - 2 if free >= 2 else free)] # Prefix discovery dmap = {"E": "Enabled", "D": "Disabled"} if prefix.prefix_discovery_policy == "P": t = "Profile (%s)" % dmap[prefix.profile.prefix_discovery_policy] else: t = dmap[prefix.prefix_discovery_policy] prefix_info += [("Prefix Discovery", t)] # Address discovery if prefix.address_discovery_policy == "P": t = "Profile (%s)" % dmap[prefix.profile.address_discovery_policy] else: t = dmap[prefix.address_discovery_policy] prefix_info += [("Address Discovery", t)] # Source prefix_info += [ ( "Source", {"M": "Manual", "i": "Interface", "w": "Whois Route", "n": "Neighbor"}.get( prefix.source, "-" ), ) ] # # Add custom fields for f in CustomField.table_fields("ip_prefix"): v = getattr(prefix, f.name) prefix_info += [(f.label, v if v is not None else "")] # Ranges ranges = [] rs = [] max_slots = 0 r_spots = [] if addresses: # Assign ranges colors ranges = list(prefix.address_ranges) for r, c in zip(ranges, get_colors(len(ranges))): r.color = c # Schedule ranges r_changes = {} # Address -> (set of entering ranges, set of leaving ranges) for r in ranges: if r.from_address not in r_changes: r_changes[r.from_address] = (set(), set()) if r.to_address not in r_changes: r_changes[r.to_address] = (set(), set()) r_changes[r.from_address][0].add(r) r_changes[r.to_address][1].add(r) # <!> n = (IP.prefix(r.to_address) + 1).address if n not in r_changes: r_changes[n] = (set(), set()) r_spots = list(six.iterkeys(r_changes)) # Allocate slots used_slots = set() free_slots = set() r_slots = {} # Range -> slot max_slots = 0 rs = sorted( ([IP.prefix(i), d, []] for i, d in six.iteritems(r_changes)), key=itemgetter(0) ) for address, d, x in rs: entering, leaving = d for r in entering: if not free_slots: free_slots.add(max_slots) max_slots += 1 s = free_slots.pop() used_slots.add(s) r_slots[r] = s for r in leaving: s = r_slots[r] used_slots.remove(s) free_slots.add(s) # Assign ranges to slots slots = [None] * max_slots for r in rs: address, [entering, leaving], _ = r for e in entering: slots[r_slots[e]] = e r[2] = slots[:] for l in leaving: slots[r_slots[l]] = None # Assign slots to addresses c = [None] * max_slots rrs = rs[:] cr = rrs.pop(0) if rrs else None for a in addresses: address = IP.prefix(a.address) while cr and address >= cr[0]: c = cr[2] if rrs: cr = rrs.pop(0) else: break a.slots = c # Address spot if can_add_address: special_addr = IP.prefix(prefix.prefix).special_addresses c = [None] * max_slots rrs = rs[:] if rrs: cr = rrs.pop(0) else: cr = None spot = [] for a in self.get_prefix_spot(prefix, extra=r_spots): if cr and a is not None and a == cr[0]: c = [None if cc is None else cc.id for cc in cr[2]] if rrs: cr = rrs.pop(0) spot += [(None if a is None else a.address, c, a in special_addr)] spot = ujson.dumps(spot) else: spot = None can_ping = spot is not None and len([a for a in addresses if a.managed_object]) > 0 # Build custom styles styles = {} if prefix.profile.style: styles[prefix.profile.style.css_class_name] = prefix.profile.style.css for p in prefixes: if p.profile.style and p.profile.style.css_class_name not in styles: styles[p.profile.style.css_class_name] = p.profile.style.css for a in addresses: if a.profile.style and a.profile.style.css_class_name not in styles: styles[a.profile.style.css_class_name] = a.profile.style.css styles = "\n".join(six.itervalues(styles)) # Render return self.render( request, "vrf_index.html.j2", user=request.user, vrf=vrf, prefix=prefix, path=path, prefixes=prefixes, addresses=addresses, prefix_info=prefix_info, display_empty_message=not addresses and not prefixes, can_view=can_view, can_change=can_change, can_bind_vc=can_bind_vc, can_change_maintainers=can_change_maintainers, can_add_prefix=can_add_prefix, can_add_address=can_add_address, can_edit_special=can_edit_special, has_bookmark=has_bookmark, bookmarks=bookmarks, spot=spot, can_ping=can_ping, styles=styles, ranges=ranges, max_slots=max_slots, l_prefixes=l_prefixes, )
def prefix_contents(self, request, prefix): vrf = prefix.vrf # List of nested prefixes # @todo: prefetch_related prefixes = list( prefix.children_set.select_related().order_by("prefix")) # Bulk utilization Prefix.update_prefixes_usage(prefixes) # Free prefixes free_prefixes = list( IP.prefix(prefix.prefix).iter_free([pp.prefix for pp in prefixes])) # Get permissions user = request.user can_view = prefix.can_view(user) can_change = prefix.can_change(user) can_bind_vc = can_change and Permission.has_perm( user, "ip:ipam:bind_vc") can_change_maintainers = user.is_superuser can_add_prefix = can_change can_add_address = can_change and len(prefixes) == 0 # Bookmarks has_bookmark = prefix.has_bookmark(user) bookmarks = set(b.prefix for b in PrefixBookmark.user_bookmarks( user, vrf=vrf, afi=prefix.afi)) l_prefixes = sorted( ([(IP.prefix(pp.prefix), pp, pp.prefix in bookmarks) for pp in prefixes] + [(pp, None, False) for pp in free_prefixes]), key=lambda x: x[0], ) # List of nested addresses addresses = list( prefix.address_set.select_related().order_by("address")) # Ranges ranges = [] rs = [] max_slots = 0 r_spots = [] allocated_addresses = set() if addresses: # Assign ranges colors ranges = list(prefix.address_ranges) for r, c in zip(ranges, get_colors(len(ranges))): r.color = c # Schedule ranges r_changes = { } # Address -> (set of entering ranges, set of leaving ranges) for r in ranges: if r.from_address not in r_changes: r_changes[r.from_address] = (set(), set()) if r.to_address not in r_changes: r_changes[r.to_address] = (set(), set()) r_changes[r.from_address][0].add(r) r_changes[r.to_address][1].add(r) # <!> n = (IP.prefix(r.to_address) + 1).address if n not in r_changes: r_changes[n] = (set(), set()) r_spots = list(r_changes) # Allocate slots used_slots = set() free_slots = set() r_slots = {} # Range -> slot max_slots = 0 rs = sorted([[IP.prefix(i), d, []] for i, d in r_changes.items()], key=itemgetter(0)) for address, d, x in rs: entering, leaving = d for r in entering: if not free_slots: free_slots.add(max_slots) max_slots += 1 spt = free_slots.pop() used_slots.add(spt) r_slots[r] = spt for r in leaving: spt = r_slots[r] used_slots.remove(spt) free_slots.add(spt) # Assign ranges to slots slots = [None] * max_slots for r in rs: address, [entering, leaving], _ = r for e in entering: slots[r_slots[e]] = e r[2] = slots[:] for ll in leaving: slots[r_slots[ll]] = None # Assign slots to addresses c = [None] * max_slots rrs = rs[:] cr = rrs.pop(0) if rrs else None for a in addresses: allocated_addresses.add(str(a.address)) address = IP.prefix(a.address) while cr and address >= cr[0]: c = cr[2] if rrs: cr = rrs.pop(0) else: break a.slots = c # Address spot if can_add_address: c = [None] * max_slots rrs = rs[:] if rrs: cr = rrs.pop(0) else: cr = None spot = [] for a in self.get_prefix_spot(prefix, extra=r_spots): if cr and a is not None and a == cr[0]: c = [None if cc is None else cc.id for cc in cr[2]] if rrs: cr = rrs.pop(0) spot += [(None if a is None else a.address, c)] # spot += [(None if a is None else a.address, c, a in special_addr)] # spot = ujson.dumps(spot) # spot = JSONEncoder(ensure_ascii=False).encode(spot) else: spot = None can_ping = spot is not None and len( [a for a in addresses if a.managed_object]) > 0 prefix_info = self.get_info_block(prefix.afi, prefix, addresses) path = [Prefix.objects.get(id=pp) for pp in prefix.get_path()] return { "id": prefix.id, "name": prefix.prefix, "vrf": prefix.vrf.id, "vrf__label": "%s (%s)" % (prefix.vrf.name, prefix.vrf.vpn_id), "description": prefix.description, "afi": prefix.afi, "profile": prefix.profile.name, "state": prefix.state.name, "maintainers": [m.username for m in prefix.maintainers], "has_bookmark": has_bookmark, "permissions": { "view": can_view, "change": can_change, "bind_vc": can_bind_vc, "change_maintainers": can_change_maintainers, "add_prefix": can_add_prefix, "delete_prefix": True, "add_address": can_add_address, "ping": can_ping, }, "path": [{ "id": p.id, "parent_id": p.parent_id, "name": p.prefix } for p in path], "prefixes": [{ "id": p.id, "name": p.prefix, "has_bookmark": is_bookmarks, "description": p.description, "afi": p.afi, "project": p.project.code if p.project else None, "as": "AS%d" % p.asn.asn if p.asn else None, "vc": p.vc.name if p.vc else None, "tt": p.tt, "usage": p.usage_percent, "address_usage": p.address_usage_percent, "labels": p.labels, "state": p.state.name, "state_desc": p.state.description, "isFree": False, } if p else { "name": ip.prefix, "isFree": True } for ip, p, is_bookmarks in l_prefixes], "addresses": sorted( [{ "id": a.id, "name": a.name, "address": a.address, "state": a.state.name, "fqdn": a.fqdn if a.fqdn else None, "mac": a.mac if a.mac else None, "mo_id": a.managed_object.id if a.managed_object else None, "mo_name": a.managed_object.name if a.managed_object else None, "is_router": a.managed_object.is_router if a.managed_object else None, "project": a.project.code if a.project else None, "subinterface": a.subinterface if a.subinterface else None, "short_desc": a.short_description, "desc": a.description, "source": { "M": "Manual", "i": "Interface", "m": "Mgmt", "d": "DHCP", "n": "Neighbor", }.get(a.source, "-"), "tt": a.tt, "labels": a.labels, "isFree": False, } for a in addresses] + ([{ "address": z[0], "isFree": True } for z in spot if str(z[0]) not in allocated_addresses] if spot else []), key=lambda x: IP.prefix(x["address"]), ), "info": dict(prefix_info), "ranges": [{ "name": r.name, "description": r.description, "from_address": r.from_address, "to_address": r.to_address, "color": r.color, } for r in ranges], "bookmarks": [{ "id": b.id, "text": b.prefix } for b in PrefixBookmark.user_bookmarks( user, vrf=vrf, afi=prefix.afi)], }