class OUI(models.Model): # oui = MACAddressField() # mask = MACAddressField() start = MACAddressField() stop = MACAddressField() shortname = models.CharField(max_length=255, blank=True, null=True) name = models.CharField(max_length=255, blank=True, null=True) def __str__(self): return "%s: %s" % (self.pk, self.shortname) class Meta: db_table = "ouis"
class Disabled(models.Model): mac = MACAddressField(primary_key=True) # host = models.OneToOneField('Host', primary_key=True, db_column='mac', db_constraint=False, related_name='disabled_host', on_delete=models.PROTECT) reason = models.TextField(blank=True, null=True) changed = models.DateTimeField(auto_now=True, db_column="disabled") changed_by = models.ForeignKey(settings.AUTH_USER_MODEL, db_column="disabled_by") def __init__(self, *args, **kwargs): # Initialize setters self._host = None super(Disabled, self).__init__(*args, **kwargs) def __str__(self): return "%s" % self.pk @property def host(self): if self._host: return self._host else: host_obj = Host.objects.filter(mac=self.mac).first() return host_obj.hostname if host_obj else None @host.setter def host(self, host): self._host = host class Meta: db_table = "disabled" verbose_name = "Disabled Host" ordering = ("-changed",)
class IgnoredMac(models.Model): mac = MACAddressField() reason = models.TextField(max_length=250) created = models.DateTimeField(auto_now_add=True) def __str__(self): return str(self.mac)
class MacOui(models.Model): oui = MACAddressField(primary_key=True) vendor = models.TextField() objects = NetManager() def __str__(self): return self.oui class Meta: db_table = "mac_oui"
class UnknownScanConflict(ScanConflict): ip = InetAddressField() mac = MACAddressField() lab = models.ForeignKey(Lab, on_delete=models.SET_NULL, null=True, blank=True) lastseen = models.DateTimeField(default=timezone.now) def safe_OUI_org(self): try: return self.mac.info['OUI']['org'] except NotRegisteredError: return "unknown" def __str__(self): return f"[unknown conflict] {self.mac} with IP {self.ip} firstseen at {self.scan.timestamp}"
class CustomerIpLeaseLog(models.Model): customer = models.ForeignKey(Customer, on_delete=models.CASCADE) ip_address = models.GenericIPAddressField(_("Ip address")) lease_time = models.DateTimeField(_("Lease time"), auto_now_add=True) last_update = models.DateTimeField(_("Last update"), blank=True, null=True, default=None) mac_address = MACAddressField(verbose_name=_("Mac address"), null=True, default=None) is_dynamic = models.BooleanField(_("Is synamic"), default=False) event_time = models.DateTimeField(_("Event time"), auto_now_add=True) end_use_time = models.DateTimeField(_("Lease end use time"), null=True, blank=True, default=None) def __str__(self): return self.ip_address class Meta: db_table = "networks_ip_lease_log"
class Interface(BaseAccessLevel): """ Interface model """ device = models.ForeignKey('net.Device') type = models.IntegerField(_('type'), max_length=2, choices=INTERFACE_TYPE_CHOICES, blank=True) name = models.CharField(_('name'), max_length=10, blank=True, null=True) mac = MACAddressField(_('mac address'), max_length=17, unique=True, default=None) mtu = models.IntegerField(_('MTU (Maximum Trasmission Unit)'), blank=True, null=True, default=1500) tx_rate = models.IntegerField(_('TX Rate'), null=True, default=None, blank=True) rx_rate = models.IntegerField(_('RX Rate'), null=True, default=None, blank=True) class Meta: app_label= 'net' permissions = (('can_view_interfaces', 'Can view interfaces'),) def __unicode__(self): return '%s %s' % (self.get_type_display(), self.mac)
class AttributeToHost(models.Model): attribute = models.IntegerField(null=True, blank=True, db_column="aid") name = models.CharField(max_length=255, blank=True, null=True) structured = models.BooleanField(default=None) required = models.BooleanField(default=False) mac = MACAddressField(blank=True, null=True) avid = models.IntegerField(blank=True, null=True) value = models.TextField(blank=True, null=True) objects = NetManager() def __str__(self): return "%s %s" % (self.attribute.name, self.name) class Meta: managed = False db_table = "attributes_to_hosts"
class VirtualHostsScan(models.Model): scan = models.ForeignKey(ScanSession, on_delete=models.CASCADE, related_name='virtualhosts') status = models.CharField( max_length=8, choices=[ ('active', 'Active'), ('old', 'Old'), ('ignore', 'Ignored'), ('super', 'Superseded') ], null=False, default='active' ) ip = InetAddressField() mac = MACAddressField() lab = models.ForeignKey(Lab, on_delete=models.SET_NULL, null=True, blank=True) lastseen = models.DateTimeField(default=timezone.now) def __str__(self): return f"Virtual MAC: {self.mac}/{self.ip} in {self.lab}"
class Nic(models.Model): model = models.CharField(max_length=30, blank=True) mac = MACAddressField(unique=True) integrated = models.BooleanField(default=False) management = models.BooleanField(default=False) host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='nics', null=True, blank=True) ip = InetAddressField(store_prefix_length=False, blank=True, null=True) primary = models.BooleanField(help_text="Is this the primary NIC in the assigned host?") lastseen = models.DateTimeField(null=True, blank=True, help_text="Updated automatically from primary LabScan") objects = NetManager() def __str__(self): return str(self.mac) + ' / ' + self.model def clean(self): if self.host: primaries = Nic.objects.filter(Q(primary=True) & Q(host=self.host) & ~Q(pk=self.pk)) if primaries.count() > 1: raise ValidationError(f"The assigned host cannot have more than one primary NIC, id(s) {primaries.all()}") def save(self, *args, **kwargs): self.full_clean() # update the IP fields on the card's parent host if it has one. if self.host!=None and self.ip: if self.primary: self.host.ip = self.ip elif self.host.ip == None: primarynic = self.host.nics.filter(primary=True) if primarynic: if primarynic[0].ip is None: self.host.ip = self.ip if self.host.lastseen: if self.lastseen > self.host.lastseen: self.host.lastseen = self.lastseen else: self.host.lastseen = self.lastseen self.host.save() return super().save(*args, **kwargs)
class IXLan(models.Model): name = models.CharField(max_length=255, blank=True) descr = models.TextField(blank=True) mtu = models.PositiveIntegerField(null=True, blank=True) vlan = models.PositiveIntegerField(null=True, blank=True) dot1q_support = models.BooleanField(default=False) rs_asn = ASNField( verbose_name="Route Server ASN", allow_zero=True, null=True, blank=True, default=0, ) arp_sponge = MACAddressField(verbose_name="ARP sponging MAC", null=True, unique=True, blank=True) ixf_ixp_member_list_url = models.URLField( verbose_name="IX-F Member Export URL", null=True, blank=True) ixf_ixp_member_list_url_visible = models.CharField( verbose_name="IX-F Member Export URL Visibility", max_length=64, choices=Visibility.choices, default=Visibility.PRIVATE, ) ix = models.ForeignKey( InternetExchange, default=0, related_name="ixlan_set", verbose_name="Internet Exchange", on_delete=models.CASCADE, ) class Meta: verbose_name = "Internet Exchange LAN" verbose_name_plural = "Internet Exchange LANs"
class MACTestModel(Model): field = MACAddressField(null=True) objects = NetManager() class Meta: db_table = 'mac'
class MACAddress(AbstractDatedModel): address = MACAddressField()
class Interface(BaseAccessLevel): """ Interface model """ device = models.ForeignKey('net.Device') type = models.IntegerField(_('type'), max_length=2, choices=INTERFACE_TYPE_CHOICES, blank=True) name = models.CharField(_('name'), max_length=10, blank=True, null=True) mac = MACAddressField(_('mac address'), max_length=17, unique=True, default=None, null=True, blank=True) mtu = models.IntegerField(_('MTU'), blank=True, null=True, default=1500, help_text=_('Maximum Trasmission Unit')) tx_rate = models.IntegerField(_('TX Rate'), null=True, default=None, blank=True) rx_rate = models.IntegerField(_('RX Rate'), null=True, default=None, blank=True) # extra data data = DictionaryField( _('extra data'), null=True, blank=True, help_text=_('store extra attributes in JSON string')) shortcuts = ReferencesField(null=True, blank=True) objects = InterfaceManager() class Meta: app_label = 'net' def __unicode__(self): return '%s %s' % (self.get_type_display(), self.mac) def save(self, *args, **kwargs): """ Custom save method does the following: * save shortcuts if HSTORE is enabled """ if 'node' not in self.shortcuts: self.shortcuts['node'] = self.device.node if 'user' not in self.shortcuts and self.device.node.user: self.shortcuts['user'] = self.device.node.user if 'layer' not in self.shortcuts and 'nodeshot.core.layers' in settings.INSTALLED_APPS: self.shortcuts['layer'] = self.device.node.layer super(Interface, self).save(*args, **kwargs) @property def owner(self): if 'user' not in self.shortcuts: if self.device or self.device_id: self.save() else: raise Exception('Instance does not have a device set yet') return self.shortcuts['user'] @property def node(self): if 'node' not in self.shortcuts: if self.device or self.device_id: self.save() else: raise Exception('Instance does not have a device set yet') return self.shortcuts['node'] @property def layer(self): if 'nodeshot.core.layers' not in settings.INSTALLED_APPS: return False if 'layer' not in self.shortcuts: if self.device or self.device_id: self.save() else: raise Exception('Instance does not have a device set yet') return self.shortcuts['layer'] @property def ip_addresses(self): try: addresses = self.data.get('ip_addresses', '') # self.data might be none, hence self.data['ip_addresses'] will raise an exception except AttributeError: addresses = '' return addresses.replace(' ', '').split(',') if addresses else [] @ip_addresses.setter def ip_addresses(self, value): """ :param value: a list of ip addresses """ if not isinstance(value, list): raise ValueError('ip_addresses value must be a list') # in soem cases self.data might be none, so let's instantiate an empty dict if self.data is None: self.data = {} # update field self.data['ip_addresses'] = ', '.join(value) if 'grappelli' in settings.INSTALLED_APPS: @staticmethod def autocomplete_search_fields(): return ('mac__icontains', 'data__icontains')
class Host(DirtyFieldsMixin, models.Model): mac = MACAddressField("Mac Address", primary_key=True) hostname = models.CharField( max_length=255, unique=True, validators=[validate_hostname], db_index=True ) description = models.TextField(blank=True, null=True) address_type_id = models.ForeignKey( "network.AddressType", blank=True, null=True, db_column="address_type_id", on_delete=models.SET_NULL, ) pools = models.ManyToManyField( "network.Pool", through="network.HostToPool", related_name="pool_hosts" ) # freeform_attributes = models.ManyToManyField('Attribute', through='FreeformAttributeToHost', # related_name='freeform_hosts', blank=True, null=True) # structured_attributes = models.ManyToManyField('Attribute', through='StructuredAttributeToHost', # related_name='structured_hosts', blank=True, null=True) dhcp_group = models.ForeignKey( "network.DhcpGroup", db_column="dhcp_group", verbose_name="DHCP Group", blank=True, null=True, on_delete=models.SET_NULL, ) expires = models.DateTimeField() changed = models.DateTimeField(auto_now=True) changed_by = models.ForeignKey(settings.AUTH_USER_MODEL, db_column="changed_by") last_notified = models.DateTimeField(blank=True, null=True) objects = HostManager.from_queryset(HostQuerySet)() search_index = VectorField() searcher = SearchManager( fields=("hostname", "description"), config="pg_catalog.english", # this is default search_field="search_index", # this is default auto_update_search_field=True, ) def __init__(self, *args, **kwargs): # Initialize setters self._expire_days = None self._user_owners = None self._group_owners = None self._user = None self._master_dns_deleted = False self.ip_address = None self.pool = None self.network = None super(Host, self).__init__(*args, **kwargs) def __str__(self): return self.hostname # Overload getattr for get original values def __getattr__(self, name): if name.startswith("original_") and name.split("_", 1)[1] in list( self._original_state.keys() ): def _original(fieldname): fieldvalue = self._original_state.get(fieldname, None) if fieldvalue is not None: return fieldvalue return _original(name.split("_", 1)[1]) else: return self.__getattribute__(name) def reset_state(self): self._expire_days = None self._user_owners = None self._group_owners = None self._user = None self._master_dns_deleted = False self.ip_address = None self.pool = None self.network = None try: del self.ip_addresses del self.master_ip_address del self.owners del self._pools_cache del self._addresses_cache except AttributeError: pass @cached_property def _addresses_cache(self): return list(self.addresses.all()) @cached_property def _pools_cache(self): return list(self.pools.all()) @property def expire_days(self): if self._expire_days: return self._expire_days else: return self.get_expire_days() @expire_days.setter def expire_days(self, days): self._expire_days = days @cached_property def owners(self): return self.get_owners() def get_owners( self, ids_only=False, name_only=False, owner_detail=False, users_only=False, user_perms_prefetch=None, group_perms_prefetch=None, ): # users_dict = get_users_with_perms(self, attach_perms=True, with_group_users=False) # groups_dict = get_groups_with_perms(self, attach_perms=True) content_type = ContentType.objects.get_for_model(self) users = [] if user_perms_prefetch: user_perms = list( filter( lambda x: x.object_pk == str(self.mac) and x.permission.codename == "is_owner_host", user_perms_prefetch, ) ) else: user_perms = UserObjectPermission.objects.filter( content_type=content_type, object_pk=str(self.mac), permission__codename="is_owner_host", ) for perm in user_perms: users.append(perm.user) groups = [] if group_perms_prefetch: group_perms = list( filter( lambda x: x.object_pk == str(self.mac) and x.permission.codename == "is_owner_host", group_perms_prefetch, ) ) else: group_perms = GroupObjectPermission.objects.filter( content_type=content_type, object_pk=str(self.mac), permission__codename="is_owner_host", ) for perm in group_perms: groups.append(perm.group) # for user, permissions in users_dict.iteritems(): # if 'is_owner_host' in permissions: # users.append(user) # groups = [] # for group, permissions in groups_dict.iteritems(): # if 'is_owner_host' in permissions: # groups.append(group) if users_only: User = get_user_model() users_from_groups = [ user for user in User.objects.filter(groups__in=groups) ] users = list(set(users + users_from_groups)) return users if owner_detail: users = [ (user.pk, user.username, user.get_full_name(), user.email) for user in users ] groups = [(group.pk, group.name) for group in groups] elif ids_only: users = [user.pk for user in users] groups = [group.pk for group in groups] elif name_only: users = [user.username for user in users] groups = [group.name for group in groups] return users, groups @property def user_owners(self): if self._user_owners: return self._user_owners else: return [owner.username for owner in self.owners[0]] @user_owners.setter def user_owners(self, owners): self._user_owners = owners @property def user(self): if self._user: return self._user else: return self.changed_by @user.setter def user(self, value): self._user = value @property def master_dns_deleted(self): return self._master_dns_deleted @master_dns_deleted.setter def master_dns_deleted(self, value): self._master_dns_deleted = value @property def group_owners(self): if self._group_owners: return self._group_owners else: return [owner.name for owner in self.owners[1]] @group_owners.setter def group_owners(self, owners): self._group_owners = owners @property def is_dynamic(self): if self.is_dirty() is False: return True if self._pools_cache else False else: return True if self.pools.all() else False @property def is_static(self): return True if self.is_dynamic is False else False # This is set on the queryset manager now. # @property # def is_disabled(self): # try: # return True if self.disabled_host else False # except ObjectDoesNotExist: # return False @property def disabled_host(self): if self.is_disabled: return Disabled.objects.filter(pk=self.mac).first() else: return None @property def is_expired(self): return True if self.expires < timezone.now() else False @property def address_type(self): # TODO: Address type is old and eventually will be deprecated. # Try to set address type if doesn't exist if host already exists in DB. if self.pk and not self.address_type_id: from openipam.network.models import AddressType, NetworkRange addresses = self._addresses_cache pools = self._pools_cache try: # if (len(addresses) + len(pools)) > 1: # self.address_type = None # elif addresses: if addresses: try: ranges = NetworkRange.objects.filter( range__net_contains_or_equals=addresses[0].address ) if ranges: self.address_type_id = AddressType.objects.get( ranges__in=ranges ) else: raise AddressType.DoesNotExist except AddressType.DoesNotExist: self.address_type_id = AddressType.objects.get(is_default=True) elif pools: self.address_type_id = AddressType.objects.get(pool=pools[0]) except AddressType.DoesNotExist: self.address_type_id = None return self.address_type_id @address_type.setter def address_type(self, value): self.address_type_id = value @property def mac_stripped(self): mac = str(self.mac) mac = [c for c in mac if c.isdigit() or c.isalpha()] return "".join(mac) @property def mac_last_seen(self): gul_mac = GulRecentArpBymac.objects.filter(mac=self.mac).order_by("-stopstamp") if gul_mac: return gul_mac[0].stopstamp else: return None @property def oui(self): return OUI.objects.extra( where=["'%s' >= ouis.start and '%s' <= ouis.stop" % (self.mac, self.mac)] ).first() @cached_property def master_ip_address(self): if self.is_static: if not self.ip_addresses: return None elif len(self.ip_addresses) == 1: return self.ip_addresses[0] else: address = self.addresses.filter(arecords__name=self.hostname).first() return str(address) if address else self.ip_addresses[0] return None @cached_property def ip_addresses(self): return [str(address) for address in self.addresses.all()] def delete_ip_address(self, user, address): if isinstance(address, string_types): address = self.addresses.filter(address=address) # Delete DNS PTR and A Records self.delete_dns_records(user=user, addresses=address) # Release address address.release(user=user) def add_ip_address(self, user=None, ip_address=None, network=None, hostname=None): from openipam.network.models import Network, Address from openipam.dns.models import DnsRecord, DnsType user = user or self._user if not user: raise Exception("A User must be given to add ip addresses.") if not hostname: raise ValidationError("A hostname is required.") address = None # Check to see if hostname already taken for any hosts other then the current one if being updated. used_hostname = ( DnsRecord.objects.filter( dns_type__in=[DnsType.objects.A, DnsType.objects.AAAA], name=hostname ) .exclude(ip_content__address=self.master_ip_address) .first() ) if used_hostname: raise ValidationError( "Hostname %s is already assigned to DNS A Record: %s." % (hostname, used_hostname.ip_content) ) user_pools = get_objects_for_user( user, ["network.add_records_to_pool", "network.change_pool"], any_perm=True ) user_nets = get_objects_for_user( user, [ "network.add_records_to_network", "network.is_owner_network", "network.change_network", ], any_perm=True, ) if network: if isinstance(network, string_types): network = Network.objects.get(network=network) if not user_nets.filter(network=network.network): raise ValidationError( "You do not have access to assign host '%s' to the " "network specified: %s." % (hostname, network) ) try: network_address = ( Address.objects.filter( Q(pool__in=user_pools) | Q(pool__isnull=True), Q(leases__isnull=True) | Q(leases__abandoned=True) | Q(leases__ends__lte=timezone.now()) | Q(leases__host=self), network=network, host__isnull=True, reserved=False, ) .order_by("address") .first() ) if not network_address: raise Address.DoesNotExist else: address = network_address except ValidationError: raise ValidationError("The network '%s' is invalid." % network) except Address.DoesNotExist: raise ValidationError( "There are no avaiable addresses for the network entered: %s" % network ) elif ip_address: # Validate IP Address try: validate_ipv46_address(ip_address) except ValidationError: raise ValidationError( "IP Address %s is invalid. Enter a valid IPv4 or IPv6 address." % ip_address ) if ip_address in self.ip_addresses: raise ValidationError( "IP address %s is already assigned to this host." % ip_address ) try: address = Address.objects.get( Q(pool__in=user_pools) | Q(pool__isnull=True) | Q(network__in=user_nets), Q(leases__isnull=True) | Q(leases__abandoned=True) | Q(leases__ends__lte=timezone.now()) | Q(leases__host=self), Q(host__isnull=True) | Q(host=self), address=ip_address, reserved=False, ) except ValidationError: raise ValidationError( "There IP Address %s is not available." % ip_address ) except Address.DoesNotExist: raise ValidationError( "There are no avaiable addresses for the IP entered: %s" % ip_address ) else: raise ValidationError( "A Network or IP Address must be given to assign this host an address." ) # Make sure pool is clear on addresses we are assigning. address.pool_id = None address.host = self address.changed_by = user address.save() # Update A and PTR dns records self.add_dns_records(user=user, hostname=hostname, address=address) return address def delete_dns_records( self, user=None, delete_only_master_dns=False, delete_dchpdns=True, addresses=[] ): from openipam.dns.models import DnsType user = user or self._user if not user: raise Exception("A User must be given to delete dns records for host.") if self.master_dns_deleted is False: # If addresses list is empty, we use the master address # So By default we are deleting DNS for just the primary address if not addresses: addresses = self.addresses.filter(address=self.master_ip_address) if delete_only_master_dns: # Here we only delete the master DNS (A and PTR) record # If modifying a host, this will get recreated later in the call. self.dns_records.filter( Q(name=self.original_hostname) | Q(text_content=self.original_hostname), dns_type__in=[ DnsType.objects.PTR, DnsType.objects.A, DnsType.objects.AAAA, ], ).update(changed=timezone.now(), changed_by=user) # Delete Assocatiated PTR and A or AAAA records. self.dns_records.filter( Q(name=self.original_hostname) | Q(text_content=self.original_hostname), dns_type__in=[ DnsType.objects.PTR, DnsType.objects.A, DnsType.objects.AAAA, ], ).delete() else: # TODO: There is a foreign key for host on this table but we cant use it # cause are aren't sure this will get everything due to not all records # using the FK. # Update Changed by Assocatiated PTR and A or AAAA records. self.dns_records.filter( Q( name__in=[ address.address.reverse_pointer for address in addresses ] ) | Q(ip_content__in=[address for address in addresses]), dns_type__in=[ DnsType.objects.PTR, DnsType.objects.A, DnsType.objects.AAAA, ], ).update(changed=timezone.now(), changed_by=user) # Delete Assocatiated PTR and A or AAAA records. self.dns_records.filter( Q( name__in=[ address.address.reverse_pointer for address in addresses ] ) | Q(ip_content__in=[address for address in addresses]), dns_type__in=[ DnsType.objects.PTR, DnsType.objects.A, DnsType.objects.AAAA, ], ).delete() if delete_dchpdns: # Delete DHCP DNS records for dynamics if they exist. DhcpDnsRecord.objects.filter( host__hostname=self.original_hostname ).delete() if not addresses or self.master_ip_address in [ str(address.address) for address in addresses ]: self.master_dns_deleted = True def add_dns_records(self, user=None, hostname=None, address=None): from openipam.dns.models import DnsRecord, DnsType from openipam.network.models import Address user = user or self._user if not user: raise Exception("A User must be given to add dns records for host.") # Only do this on static hosts. if self.is_static: if not hostname: hostname = self.hostname if isinstance(address, string_types): address = Address.objects.filter(address=address).first() elif not address: address = Address.objects.filter(address=self.master_ip_address).first() # Add Associated PTR DnsRecord.objects.add_or_update_record( user=user, name=address.address.reverse_pointer, content=hostname, dns_type=DnsType.objects.PTR, host=self, ) # Add Associated A or AAAA record arecord = DnsRecord.objects.filter( dns_type__in=[DnsType.objects.A, DnsType.objects.AAAA], host=self, name=hostname, ).first() DnsRecord.objects.add_or_update_record( user=user, name=hostname, content=address.address, dns_type=DnsType.objects.A if address.address.version == 4 else DnsType.objects.AAAA, host=self, record=arecord if arecord else None, ) # Reset dns deleted flag if this is the master hostname if hostname == self.hostname: self.master_dns_deleted = False def get_dns_records(self): from openipam.dns.models import DnsRecord addresses = self.addresses.all() a_record_names = ( DnsRecord.objects.select_related("ip_content", "host", "dns_type") .filter(ip_content__in=addresses) .values_list("name") ) dns_records = ( DnsRecord.objects.select_related("ip_content", "host", "dns_type") .filter( Q(text_content__in=a_record_names) | Q(name__in=a_record_names) | Q(ip_content__in=addresses) | Q(host=self) | Q(text_content=self.hostname) # For dynamic hosts ) .order_by("dns_type__name") ) return dns_records def get_expire_days(self): if self.expires: delta = self.expires - timezone.now() return delta.days if delta.days > 0 else None else: return None def set_expiration(self, expire_days): if isinstance(expire_days, int) or isinstance(expire_days, string_types): expire_days = timedelta(int(expire_days)) now = timezone.now() self.expires = ( datetime(now.year, now.month, now.day) + timedelta(1) + expire_days ) self.expires = self.expires.replace(tzinfo=utc) def set_mac_address(self, new_mac_address): if self.mac and str(self.mac).lower() != str(new_mac_address).lower(): cursor = connection.cursor() cursor.execute( """ UPDATE hosts SET mac = %s WHERE mac = %s """, [str(new_mac_address), str(self.mac)], ) self.mac = str(new_mac_address).lower() elif not self.pk: self.mac = str(new_mac_address).lower() def set_hostname(self, hostname, user=None): user = user or self._user if not user: raise Exception("A User must be given to save hosts.") self.hostname = hostname if ( self.original_hostname and self.hostname and self.hostname != self.original_hostname ): self.delete_dns_records(user=user, delete_only_master_dns=True) # TODO: Clean this up, I dont like where this is at. def set_network_ip_or_pool(self, user=None, delete=False): user = user or self._user if not user: raise Exception("A User must be given to save hosts.") # Set the pool if attached to model otherwise find it by address type pool = self.pool current_pool = self._pools_cache[0] if self._pools_cache else None # TODO: Currently un-used function if delete: # Remove all pools self.pools.clear() # Delete DNS self.delete_dns_records(user=user, addresses=self.addresses.all()) # Remove all addresses self.addresses.release(user=user) # If we have a pool, this dynamic and we assign if pool and pool != current_pool: from openipam.network.models import Pool # Delete DNS self.delete_dns_records(user=user, addresses=self.addresses.all()) # Remove all addresses self.addresses.release(user=user) # TODO: Kill this later. host_pool_check = self.host_pools.all() if len(host_pool_check) > 1: self.pools.clear() host_pool = self.host_pools.filter(pool__name=pool).first() if host_pool: host_pool.changed_by = user host_pool.save() else: # Delete what is there and create a new one. self.pools.clear() # Assign new pool if it doesn't already exist self.host_pools.create( host=self, pool=Pool.objects.get(name=pool), changed_by=user ) # If we have a Network or IP address, then assign that address to host elif self.network or ( self.ip_address and self.ip_address not in self.ip_addresses ): # Remove all pools self.pools.clear() # TODO: Look at delete_dns for a way to only delete dhcp dns records. try: self.dhcpdnsrecord.delete() except ObjectDoesNotExist: pass # Current IP current_ip_address = self.master_ip_address if current_ip_address: # Delete DNS self.delete_dns_records(user=user) # Release the current IP to add another self.addresses.filter(address=current_ip_address).release(user=user) # Add new IP self.add_ip_address( user=user, ip_address=self.ip_address, network=self.network, hostname=self.hostname, ) def remove_owners(self): users, groups = self.get_owners() self.remove_user_owners(users) self.remove_group_owners(groups) def remove_user_owners(self, users=None): if not users: users = self.get_owners(users_only=True) for user in users: remove_perm("is_owner_host", user, self) def remove_group_owners(self, groups=None): if not groups: users, groups = self.get_owners() for group in groups: remove_perm("is_owner_host", group, self) def remove_owner(self, user_or_group): return remove_perm("is_owner_host", user_or_group, self) def assign_owner(self, user_or_group): return assign_perm("is_owner_host", user_or_group, self) def save(self, user=None, add_dns=True, *args, **kwargs): user = user or self._user if not user: raise Exception("A User must be given to save hosts.") # Make sure hostname is lowercase self.hostname = self.hostname.lower() # Make sure mac is lowercase self.mac = str(self.mac).lower() # Updating changed and changed_by self.changed_by = user self.changed = timezone.now() # If master DNS delete, re-create it if add_dns and self.master_dns_deleted is True: self.add_dns_records(user=user) super(Host, self).save(*args, **kwargs) def delete(self, user=None, *args, **kwargs): user = user or self._user if not user: raise Exception("A User must be given to save hosts.") # Delete primary DNS (PTR, A, and AAAA, updating changed and changed by) self.delete_dns_records(user=user, addresses=self.addresses.all()) # Release all addresses associated with host. self.addresses.release(user=user) # Re-save so that it captures user for postgres log table try: self.save(user=user, add_dns=False, force_update=True) except DatabaseError: pass with transaction.atomic(): super(Host, self).delete(*args, **kwargs) def clean(self): from openipam.dns.models import DnsRecord, DnsType from openipam.network.models import Address # Perform check to on hostname to not let users create a host if self.hostname and self.hostname != self.original_hostname: existing_hostname = Host.objects.filter(hostname=self.hostname).first() if existing_hostname: raise ValidationError( "The hostname '%s' already exists." % (self.hostname) ) existing_dns_hostname = ( DnsRecord.objects.filter( dns_type__in=[DnsType.objects.A, DnsType.objects.AAAA], name=self.hostname, ) .exclude(host=self) .first() ) if existing_dns_hostname: raise ValidationError( "DNS Records already exist for this hostname: %s. " " Please contact an IPAM Administrator." % (self.hostname) ) # Perform permission checks if user is attached to this instance # Domain permission checks if hostname has changed if self.hostname and self.hostname != self.original_hostname: domain_from_host = self.hostname.split(".")[1:] domain_from_host = ".".join(domain_from_host) valid_domain = get_objects_for_user( self.user, [ "dns.add_records_to_domain", "dns.is_owner_domain", "dns.change_domain", ], any_perm=True, ).filter(name=domain_from_host) if not valid_domain: raise ValidationError( "Insufficient permissions to add hosts " "for domain: %s. Please contact an IPAM Administrator." % domain_from_host ) # Pool and Network permission checks # Check for pool assignment and perms if self.address_type and self.address_type.pool: valid_pools = get_objects_for_user( self.user, ["network.add_records_to_pool", "network.change_pool"], any_perm=True, ) if self.address_type.pool not in valid_pools: raise ValidationError( "Insufficient permissions to add hosts to " "the assigned pool: %s. Please contact an IPAM Administrator." % self.address_type.pool ) # If network defined check for address assignment and perms if self.network: valid_network = get_objects_for_user( self.user, [ "network.add_records_to_network", "network.is_owner_network", "network.change_network", ], any_perm=True, ) if self.network.network not in [ network.network for network in valid_network ]: raise ValidationError( "Insufficient permissions to add hosts to " "the assigned network: %s. Please contact an IPAM Administrator." % self.network.network ) # If IP Address defined, check validity and perms if self.ip_address: ip_address = self.ip_address user_pools = get_objects_for_user( self.user, ["network.add_records_to_pool", "network.change_pool"], any_perm=True, ) user_nets = get_objects_for_user( self.user, [ "network.add_records_to_network", "network.is_owner_network", "network.change_network", ], any_perm=True, ) # Make sure this is valid. validate_ipv46_address(ip_address) address = Address.objects.filter( Q(pool__in=user_pools) | Q(pool__isnull=True) | Q(network__in=user_nets), Q(leases__isnull=True) | Q(leases__abandoned=True) | Q(leases__ends__lte=timezone.now()) | Q(leases__host=self), Q(host__isnull=True) | Q(host=self), address=ip_address, reserved=False, ) if not address: raise ValidationError( "The IP Address is reserved, in use, or not allowed. " "Please contact an IPAM Administrator." ) class Meta: db_table = "hosts" permissions = (("is_owner_host", "Is owner"),) default_permissions = ("add", "change", "delete", "view") ordering = ("hostname",)
class MACArrayTestModel(Model): field = ArrayField(MACAddressField(), blank=True, null=True) class Meta: db_table = 'macarray'
class InternetExchangeMember(PdbRefModel): """ Describes a member at an internet exchange Can have a reference to a peeringdb netixlan object """ ix = models.ForeignKey( InternetExchange, help_text=_("Members at this Exchange"), related_name="member_set", on_delete=models.CASCADE, ) ipaddr4 = InetAddressField(blank=True, null=True, store_prefix_length=False) ipaddr6 = InetAddressField(blank=True, null=True, store_prefix_length=False) macaddr = MACAddressField(null=True, blank=True) as_macro_override = models.CharField(max_length=255, blank=True, null=True, validators=[validate_as_set]) is_rs_peer = models.BooleanField(default=False) speed = models.PositiveIntegerField() asn = models.PositiveIntegerField() name = models.CharField(max_length=255, blank=True, null=True) ixf_state = models.CharField(max_length=255, default="active", choices=django_ixctl.enum.MEMBER_STATE) ixf_member_type = models.CharField( max_length=255, choices=django_ixctl.enum.IXF_MEMBER_TYPE, default="peering") class PdbRef(PdbRefModel.PdbRef): pdbctl = pdbctl.NetworkIXLan class HandleRef: tag = "member" class Meta: db_table = "ixctl_member" verbose_name_plural = _("Internet Exchange Members") verbose_name = _("Internet Exchange Member") unique_together = (("ipaddr4", "ix"), ("ipaddr6", "ix"), ("macaddr", "ix")) @classmethod def create_from_pdb(cls, pdb_object, ix, save=True, **fields): """ Create `InternetExchangeMember` from peeringdb netixlan Argument(s): - pdb_object (`fullctl.service_bridge.pdbctl.NetworkIXLan`): netixlan instance - ix (`InternetExchange`): member of this ix Keyword Argument(s): And keyword arguments passwed will be used to inform properties of the InternetExchangeMember to be created """ member = super().create_from_pdb(pdb_object, ix=ix, save=False, **fields) member.ipaddr4 = pdb_object.ipaddr4 member.ipaddr6 = pdb_object.ipaddr6 member.is_rs_peer = pdb_object.is_rs_peer member.speed = pdb_object.speed member.asn = pdb_object.net.asn member.name = pdb_object.net.name if save: member.save() return member @classmethod def preload_as_macro(cls, queryset): asns = set([member.asn for member in queryset]) if not asns: return queryset asn_map = {} for net in sot.ASSet().objects(asns=list(asns)): asn_map[net.asn] = net for member in queryset: member._net = asn_map.get(member.asn) yield member @property def display_name(self): return self.name or f"AS{self.asn}" @property def org(self): return self.ix.instance.org @property def ix_name(self): return self.ix.name @property def as_sets(self): if not self.as_macro: return [] return [as_set.strip() for as_set in self.as_macro.split(",")] @property def as_macro(self): if self.as_macro_override: return self.as_macro_override if self.net: if self.net.source == "peerctl": return self.net.as_set elif self.net.source == "pdbctl": return self.net.irr_as_set return "" @property def net(self): if hasattr(self, "_net"): return self._net self._net = sot.ASSet().first(asn=self.asn) return self._net def __str__(self): return f"AS{self.asn} - {self.ipaddr4} - {self.ipaddr6} ({self.id})"
class Device(BaseAbstractModel): _cached_manager = None ip_address = models.GenericIPAddressField(verbose_name=_("Ip address"), null=True, blank=True, default=None) mac_addr = MACAddressField(verbose_name=_("Mac address"), unique=True) comment = models.CharField(_("Comment"), max_length=256) dev_type = models.PositiveSmallIntegerField( _("Device type"), default=DEVICE_TYPE_UNKNOWN, choices=MyChoicesAdapter(DEVICE_TYPES)) man_passw = models.CharField(_("SNMP password"), max_length=16, null=True, blank=True) group = models.ForeignKey(Group, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("Device group")) parent_dev = models.ForeignKey("self", verbose_name=_("Parent device"), blank=True, null=True, on_delete=models.SET_NULL) snmp_extra = models.CharField(_("SNMP extra info"), max_length=256, null=True, blank=True) extra_data = models.JSONField( verbose_name=_("Extra data"), help_text=_( "Extra data in JSON format. You may use it for your custom data"), blank=True, null=True, ) vlans = models.ManyToManyField(VlanIf, verbose_name=_("Available vlans"), blank=True) NETWORK_STATE_UNDEFINED = 0 NETWORK_STATE_UP = 1 NETWORK_STATE_UNREACHABLE = 2 NETWORK_STATE_DOWN = 3 NETWORK_STATES = ( (NETWORK_STATE_UNDEFINED, _("Undefined")), (NETWORK_STATE_UP, _("Up")), (NETWORK_STATE_UNREACHABLE, _("Unreachable")), (NETWORK_STATE_DOWN, _("Down")), ) status = models.PositiveSmallIntegerField(_("Status"), choices=NETWORK_STATES, default=NETWORK_STATE_UNDEFINED) is_noticeable = models.BooleanField( _("Send notify when monitoring state changed"), default=False) code = models.CharField(_("Code"), max_length=64, blank=True, null=True, default=None, choices=_make_device_code_config_choices()) sites = models.ManyToManyField(Site, blank=True) class Meta: db_table = "device" verbose_name = _("Device") verbose_name_plural = _("Devices") ordering = ("id", ) permissions = [ ("can_remove_from_olt", _("Can remove from OLT")), ("can_fix_onu", _("Can fix onu")), ("can_apply_onu_config", _("Can apply onu config")), ] def get_manager_klass(self): try: return next(klass for code, klass in DEVICE_TYPES if code == safe_int(self.dev_type)) except StopIteration: raise TypeError( "one of types is not subclass of BaseDeviceInterface. " "Or implementation of that device type is not found") def get_manager_object_switch(self) -> BaseSwitchInterface: man_klass = self.get_manager_klass() if self._cached_manager is None: self._cached_manager = man_klass(dev_instance=self, host=str(self.ip_address), snmp_community=str( self.man_passw)) return self._cached_manager def get_manager_object_olt(self) -> BasePONInterface: man_klass = self.get_manager_klass() if self._cached_manager is None: self._cached_manager = man_klass(dev_instance=self) return self._cached_manager def get_manager_object_onu(self) -> BasePON_ONU_Interface: man_klass = self.get_manager_klass() if self._cached_manager is None: self._cached_manager = man_klass(dev_instance=self) return self._cached_manager # Can attach device to customer in customer page def has_attachable_to_customer(self) -> bool: mngr = self.get_manager_klass() return mngr.has_attachable_to_customer def __str__(self): return "{} {}".format(self.ip_address or "", self.comment) def generate_config_template(self): mng = self.get_manager_object_switch() return mng.monitoring_template() def remove_from_olt(self): pdev = self.parent_dev if not pdev: raise DeviceConfigurationError( _("You should config parent OLT device for ONU")) if not pdev.extra_data: raise DeviceConfigurationError( _("You have not info in extra_data " "field, please fill it in JSON")) mng = self.get_manager_object_olt() r = mng.remove_from_olt(dict(pdev.extra_data)) if r: self.snmp_extra = None self.save(update_fields=["snmp_extra"]) return r def onu_find_sn_by_mac(self) -> Tuple[Optional[int], Optional[str]]: parent = self.parent_dev if parent is not None: manager = parent.get_manager_object_olt() mac = self.mac_addr ports = manager.get_list_keyval(".1.3.6.1.4.1.3320.101.10.1.1.3") for srcmac, snmpnum in ports: # convert bytes mac address to str presentation mac address real_mac = macbin2str(srcmac) if mac == real_mac: return safe_int(snmpnum), None return None, _('Onu with mac "%(onu_mac)s" not found on OLT') % { "onu_mac": mac } return None, _("Parent device not found") def fix_onu(self): onu_sn, err_text = self.onu_find_sn_by_mac() if onu_sn is not None: self.snmp_extra = str(onu_sn) self.save(update_fields=("snmp_extra", )) return True, _("Fixed") return False, err_text def get_if_name(self): mng = self.get_manager_object_olt() if hasattr(mng, "get_fiber_str"): return mng.get_fiber_str() return r"¯ \ _ (ツ) _ / ¯" def get_config_types(self) -> ListDeviceConfigType: mng_klass = self.get_manager_klass() return mng_klass.get_config_types() def apply_onu_config(self, config: dict) -> OptionalScriptCallResult: self.code = config.get("configTypeCode") self.save(update_fields=["code"]) all_device_types = self.get_config_types() self_device_type_code = str(self.code) dtypes = (dtype for dtype in all_device_types if dtype.short_code == self_device_type_code) dtype_for_run = next(dtypes, None) if dtype_for_run is not None: device_manager = dtype_for_run(title=dtype_for_run.title, code=dtype_for_run.short_code) return device_manager.entry_point(config=config, device=self) ############################# # Remote access(i.e. snmp) ############################# def dev_get_all_vlan_list(self) -> Vlans: mng = self.get_manager_object_switch() return mng.read_all_vlan_info() def read_onu_vlan_info(self) -> Vlans: mng = self.get_manager_object_onu() return mng.read_onu_vlan_info() def default_vlan_info(self) -> Vlans: mng = self.get_manager_object_onu() return mng.default_vlan_info() def is_onu_registered(self) -> bool: return self.snmp_extra is not None # @_telnet_methods_wrapper # def dev_create_vlans(self, tln: BaseDeviceInterface, vids: Vlans) -> None: # if not tln.create_vlans(vids): # raise DeviceConsoleError(_('Failed while create vlans')) # @_telnet_methods_wrapper # def dev_delete_vlan(self, tln: BaseDeviceInterface, vids: Vlans) -> None: # if not tln.delete_vlans(vlan_list=vids): # raise DeviceConsoleError(_('Failed while removing vlan')) def dev_read_mac_address_vlan(self, vid: int) -> Macs: mng = self.get_manager_object_switch() return mng.read_mac_address_vlan(vid=vid) ############################## # Switch telnet methods ############################## # @_telnet_methods_wrapper # def telnet_switch_attach_vlan_to_port(self, tln: BaseSwitchInterface, vid: int, # port: int, tag: bool = True) -> bool: # return tln.attach_vlan_to_port(vid=vid, port=port, tag=tag) # @_telnet_methods_wrapper # def telnet_switch_detach_vlan_from_port(self, tln: BaseSwitchInterface, vid: int, port: int) -> bool: # return tln.detach_vlan_from_port(vid=vid, port=port) def dev_switch_get_mac_address_port(self, device_port_num: int) -> Macs: mng = self.get_manager_object_switch() return mng.read_mac_address_port(port_num=device_port_num)
class Migration(migrations.Migration): initial = True dependencies = [ ("groupapp", "0001_initial"), ] operations = [ migrations.CreateModel( name="Device", fields=[ ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ( "ip_address", models.GenericIPAddressField(blank=True, null=True, verbose_name="Ip address", default=None), ), ("mac_addr", MACAddressField(unique=True, verbose_name="Mac address")), ("comment", models.CharField(max_length=256, verbose_name="Comment")), ( "dev_type", models.PositiveSmallIntegerField( choices=[ (1, "DLink switch"), (2, "PON OLT"), (3, "PON ONU BDCOM"), (4, "Eltex switch"), (5, "OLT ZTE C320"), (6, "Zte ONU F660"), (7, "Zte ONU F601"), (8, "Huawei switch"), ], default=1, verbose_name="Device type", ), ), ("man_passw", models.CharField(blank=True, max_length=16, null=True, verbose_name="SNMP password")), ( "snmp_extra", models.CharField(blank=True, max_length=256, null=True, verbose_name="SNMP extra info"), ), ( "extra_data", django.contrib.postgres.fields.jsonb.JSONField( blank=True, help_text="Extra data in JSON format. You may use it for your custom data", null=True, verbose_name="Extra data", ), ), ( "status", models.PositiveSmallIntegerField( choices=[(0, "Undefined"), (1, "Up"), (2, "Unreachable"), (3, "Down")], default=0, verbose_name="Status", ), ), ( "is_noticeable", models.BooleanField(default=False, verbose_name="Send notify when monitoring state changed"), ), ( "group", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="groupapp.Group", verbose_name="Device group", ), ), ( "parent_dev", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="devices.Device", verbose_name="Parent device", ), ), ], options={ "verbose_name": "Device", "verbose_name_plural": "Devices", "db_table": "device", "ordering": ("id",), }, ), migrations.CreateModel( name="Port", fields=[ ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("num", models.PositiveSmallIntegerField(default=0, verbose_name="Number")), ("descr", models.CharField(blank=True, max_length=60, null=True, verbose_name="Description")), ( "device", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, to="devices.Device", verbose_name="Device" ), ), ], options={ "verbose_name": "Port", "verbose_name_plural": "Ports", "db_table": "device_port", "ordering": ("num",), "permissions": (("can_toggle_ports", "Can toggle ports"),), "unique_together": {("device", "num")}, }, ), ]
class CustomerIpLeaseModel(models.Model): ip_address = models.GenericIPAddressField(_("Ip address"), unique=True) pool = models.ForeignKey(NetworkIpPool, on_delete=models.CASCADE) lease_time = models.DateTimeField(_("Lease time"), auto_now_add=True) customer = models.ForeignKey(Customer, on_delete=models.CASCADE, null=True) mac_address = MACAddressField(verbose_name=_("Mac address"), null=True, default=None) is_dynamic = models.BooleanField(_("Is synamic"), default=False) last_update = models.DateTimeField(_("Last update"), blank=True, null=True, default=None) objects = CustomerIpLeaseModelQuerySet.as_manager() def __str__(self): return f"{self.ip_address} [{self.mac_address}]" @staticmethod def find_customer_by_device_credentials(device_mac: str, device_port: int = 0) -> Optional[Customer]: with connection.cursor() as cur: cur.execute( "SELECT * FROM find_customer_by_device_credentials(%s::macaddr, %s::smallint)", (device_mac, device_port), ) res = cur.fetchone() if res is None or res[0] is None: return None ( baseaccount_id, balance, ip_addr, descr, house, is_dyn_ip, auto_renw_srv, markers, curr_srv_id, dev_port_id, dev_id, gw_id, grp_id, last_srv_id, street_id, *others, ) = res return Customer( pk=baseaccount_id, balance=balance, description=descr, house=house, is_dynamic_ip=is_dyn_ip, auto_renewal_service=auto_renw_srv, markers=markers, current_service_id=curr_srv_id, device_id=dev_id, dev_port_id=dev_port_id, gateway_id=gw_id, group_id=grp_id, last_connected_service_id=last_srv_id, street_id=street_id, ) @staticmethod def get_service_permit_by_ip(ip_addr: str) -> bool: with connection.cursor() as cur: cur.execute("select * from find_service_permit(%s::inet)", [ip_addr]) res = cur.fetchone() return res[0] if len(res) > 0 else False @process_lock() def ping_icmp(self, num_count=10, arp=False) -> bool: host_ip = str(self.ip_address) return icmp_ping(ip_addr=host_ip, count=num_count, arp=arp) @staticmethod def lease_commit_add_update(client_ip: str, mac_addr: str, dev_mac: str, dev_port: int): """ When external system assign ip address for customer then it ip address may be store to billing via this method. :param client_ip: client ip address :param mac_addr: client mac address :param dev_mac: device mac address :param dev_port: device port number :return: str about result """ dev_port = safe_int(dev_port) dev_port = dev_port if dev_port > 0 else None try: with connection.cursor() as cur: cur.execute( "SELECT * FROM lease_commit_add_update" "(%s::inet, %s::macaddr, %s::macaddr, %s::smallint)", (client_ip, mac_addr, dev_mac, dev_port), ) res = cur.fetchone() # lease_id, ip_addr, pool_id, lease_time, mac_addr, customer_id, is_dynamic, last_update = res return res except InternalError as err: raise LogicError(str(err)) class Meta: db_table = "networks_ip_leases" verbose_name = _("IP lease") verbose_name_plural = _("IP leases") unique_together = ("ip_address", "mac_address", "pool", "customer") ordering = ("id",)