def count_in_range(self, start, stop): return self.query_1(""" SELECT count(ipv6_number_id) FROM [:table schema=cerebrum name=dns_ipv6_number] WHERE aaaa_ip >= :start AND aaaa_ip <= :stop""", { 'start': IPv6Utils.explode(IPv6Calc.long_to_ip(start)), 'stop': IPv6Utils.explode(IPv6Calc.long_to_ip(stop))})
def count_in_range(self, start, stop): return self.query_1( """ SELECT count(ipv6_number_id) FROM [:table schema=cerebrum name=dns_ipv6_number] WHERE aaaa_ip >= :start AND aaaa_ip <= :stop""", { 'start': IPv6Utils.explode(IPv6Calc.long_to_ip(start)), 'stop': IPv6Utils.explode(IPv6Calc.long_to_ip(stop)) })
def populate(self, subnet, description, vlan=None): """Populate subnet instance's attributes.""" Entity.populate(self, self.const.entity_dns_ipv6_subnet) # If __in_db is present, it must be True; calling populate on # an object where __in_db is present and False is very likely # a programming error. # # If __in_db in not present, we'll set it to False. try: if not self.__in_db: raise RuntimeError("populate() called multiple times.") except AttributeError: self.__in_db = False self.subnet_ip, subnet_mask = subnet.split('/') self.subnet_mask = int(subnet_mask) self.subnet_ip = IPv6Utils.explode(self.subnet_ip) self.ip_min, self.ip_max = IPv6Calc.ip_range_by_netmask( self.subnet_ip, self.subnet_mask) self.description = description self.vlan = vlan self.name_prefix = '' # TODO: Fix this res_net_size = cereconf.DEFAULT_RESERVED_BY_IPv6_NET_SIZE max_res = max(res_net_size.values()) self.no_of_reserved_adr = res_net_size.get(self.subnet_mask, max_res) self.calculate_reserved_addresses()
def populate(self, subnet, description, vlan=None): """Populate subnet instance's attributes.""" Entity.populate(self, self.const.entity_dns_ipv6_subnet) # If __in_db is present, it must be True; calling populate on # an object where __in_db is present and False is very likely # a programming error. # # If __in_db in not present, we'll set it to False. try: if not self.__in_db: raise RuntimeError("populate() called multiple times.") except AttributeError: self.__in_db = False self.subnet_ip, subnet_mask = subnet.split('/') self.subnet_mask = int(subnet_mask) self.subnet_ip = IPv6Utils.explode(self.subnet_ip) self.ip_min, self.ip_max = IPv6Calc.ip_range_by_netmask( self.subnet_ip, self.subnet_mask) self.description = description self.vlan = vlan # TODO: Fix this max_res = max(cereconf.DEFAULT_RESERVED_BY_IPv6_NET_SIZE.values()) self.no_of_reserved_adr = cereconf.DEFAULT_RESERVED_BY_IPv6_NET_SIZE.get( self.subnet_mask, max_res) self.calculate_reserved_addresses()
def parse_subnet_or_ip(self, ip_id): """Parse ip_id either as a subnet, or as an IP-number. Return: (subnet, ip) - subnet is None if unknown - ip is only set if the user requested a specific IP A request for a subnet is identified by a trailing /, or for IPv4 an IP with < 4 octets. IPv6-subnets must always be specified with a trailing /, followed by a mask number. Examples:: 129.240.200 -> adress on 129.240.200.0/23 129.240.200.0/ -> adress on 129.240.200.0/23 129.240.200.0 -> explicit IP 2001:700:100:2::/64 -> address on 2001:700:100:2::/64 2001:700:100:2::3 -> explicit IP """ tmp = ip_id.split("/") ip = tmp[0] subnet_slash = len(tmp) > 1 full_ip = False if IPUtils.is_valid_ipv4(ip): # ipv4 IPUtils.parse_ipv4(ip) if ip.count('.') == 3 and not subnet_slash: full_ip = True elif ip.count('.') == 3 and subnet_slash or \ ip.count('.') == 2 and not subnet_slash: pass else: raise CerebrumError(("'%s' does not look like a valid subnet " "or ip-address.") % ip_id) elif IPv6Utils.is_valid_ipv6(ip_id): # full ipv6 full_ip = True ip = ip_id elif IPv6Subnet.IPv6Subnet.is_valid_subnet(ip_id): ip = ip_id else: try: # Assume hostname self._arecord.clear() self._arecord.find_by_name(self.qualify_hostname(ip)) self._ip_number.clear() self._ip_number.find(self._arecord.ip_number_id) except Errors.NotFoundError: raise CerebrumError("Could not find host %s" % ip) ip = self._ip_number.a_ip try: ipc = Find(self._db, None) subnet_ip = ipc._find_subnet(ip) except: subnet_ip = None return subnet_ip, full_ip and ip or None
def find_by_ip(self, aaaa_ip): aaaa_ip = IPv6Utils.explode(aaaa_ip) ipv6_number_id = self.query_1(""" SELECT ipv6_number_id FROM [:table schema=cerebrum name=dns_ipv6_number] WHERE aaaa_ip=:aaaa_ip""", {'aaaa_ip': aaaa_ip}) self.find(ipv6_number_id)
def find_by_ip(self, aaaa_ip): aaaa_ip = IPv6Utils.explode(aaaa_ip) ipv6_number_id = self.query_1( """ SELECT ipv6_number_id FROM [:table schema=cerebrum name=dns_ipv6_number] WHERE aaaa_ip=:aaaa_ip""", {'aaaa_ip': aaaa_ip}) self.find(ipv6_number_id)
def write_db(self): self.__super.write_db() if not self.__updated: return is_new = not self.__in_db if 'aaaa_ip' in self.__updated: # normalize the address self.aaaa_ip = IPv6Utils.explode(self.aaaa_ip) if 'mac_adr' in self.__updated and self.mac_adr is not None: self.mac_adr = self._verify_mac_format(self.mac_adr) cols = [('ipv6_number_id', ':ipv6_number_id'), ('aaaa_ip', ':aaaa_ip'), ('mac_adr', ':mac_adr')] binds = { 'ipv6_number_id': self.entity_id, 'aaaa_ip': self.aaaa_ip, 'mac_adr': self.mac_adr } if is_new: self.execute( """ INSERT INTO [:table schema=cerebrum name=dns_ipv6_number] (%(tcols)s) VALUES (%(binds)s)""" % { 'tcols': ", ".join([x[0] for x in cols]), 'binds': ", ".join([x[1] for x in cols]) }, binds) self._db.log_change(self.entity_id, self.const.ipv6_number_add, None, change_params={'aaaa_ip': self.aaaa_ip}) else: self.execute( """ UPDATE [:table schema=cerebrum name=dns_ipv6_number] SET %(defs)s WHERE ipv6_number_id=:ipv6_number_id""" % {'defs': ", ".join(["%s=%s" % x for x in cols])}, binds) self._db.log_change(self.entity_id, self.const.ipv6_number_add, None, change_params={'aaaa_ip': self.aaaa_ip}) del self.__in_db if 'mac_adr' in self.__updated: self._db.log_change(self.entity_id, self.const.mac_adr_set, None, change_params={'mac_adr': self.mac_adr}) self.__in_db = True self.__updated = [] return is_new
def get_relevant_ips(self, subnet_or_ip, force=False, no_of_addrs=None): """ Returns a list of available IPs. If a subnet is given as input, the list consists of avaiable IPs on the subnet. If a specific IP is given as input, the list will only contain that IP. :param subnet_or_ip: An IPv4/IPv6 subnet or IP-address :type subnet_or_ip: str :param force: Indicates if the method should attempt to force the operation, even if there is no record that the IP given as input belongs to any subnet records. :type force: boolean :param no_of_addrs: The max number of ips to be returned. :type no_of_addrs: int :returns: A list of available IPs found, or a list containing only the specified IP given to the method in subnet_or_ip, if it is evaluated to a full IP. :rtype: list """ subnet, ip = self._parser.parse_subnet_or_ip(subnet_or_ip) if subnet is None and not force: raise CerebrumError("Unknown subnet. Must force") elif subnet is None and ip is None: raise CerebrumError("Please specify a valid subnet or IP-address.") elif subnet is not None and ip is None: first = subnet_or_ip.split('/')[0] if IPUtils.is_valid_ipv4(first): first = IPCalc.ip_to_long(first) elif IPv6Utils.is_valid_ipv6(first): first = None free_ip_numbers = self._find.find_free_ip(subnet, first=first, no_of_addrs=no_of_addrs) else: free_ip_numbers = [ip] return free_ip_numbers
def write_db(self): self.__super.write_db() if not self.__updated: return is_new = not self.__in_db if 'aaaa_ip' in self.__updated: # normalize the address self.aaaa_ip = IPv6Utils.explode(self.aaaa_ip) if 'mac_adr' in self.__updated and self.mac_adr is not None: self.mac_adr = self._verify_mac_format(self.mac_adr) cols = [('ipv6_number_id', ':ipv6_number_id'), ('aaaa_ip', ':aaaa_ip'), ('mac_adr', ':mac_adr')] binds = {'ipv6_number_id': self.entity_id, 'aaaa_ip': self.aaaa_ip, 'mac_adr': self.mac_adr} if is_new: self.execute(""" INSERT INTO [:table schema=cerebrum name=dns_ipv6_number] (%(tcols)s) VALUES (%(binds)s)""" % {'tcols': ", ".join([x[0] for x in cols]), 'binds': ", ".join([x[1] for x in cols])}, binds) self._db.log_change(self.entity_id, self.clconst.ipv6_number_add, None, change_params={'aaaa_ip': self.aaaa_ip}) else: self.execute(""" UPDATE [:table schema=cerebrum name=dns_ipv6_number] SET %(defs)s WHERE ipv6_number_id=:ipv6_number_id""" % {'defs': ", ".join( ["%s=%s" % x for x in cols])}, binds) self._db.log_change(self.entity_id, self.clconst.ipv6_number_add, None, change_params={'aaaa_ip': self.aaaa_ip}) del self.__in_db if 'mac_adr' in self.__updated: self._db.log_change(self.entity_id, self.clconst.mac_adr_set, None, change_params={'mac_adr': self.mac_adr}) self.__in_db = True self.__updated = [] return is_new
def validate_subnet(subnet): """Validates that a subnet specification is correctly formatted and with legal values. Raises SubnetError if invalid. """ try: ip, mask = subnet.split('/') mask = int(mask) except ValueError: raise SubnetError("Not a valid subnet '%s'" % subnet) if not IPv6Utils.is_valid_ipv6(ip): raise SubnetError("Invalid adress: %s" % ip) # TODO 25-08-2015: # Since Subnet-masks for IPv6 are not properly implemented yet, this # will validate an unspecified subnet mask (''), enabling bofh-users # to specify a subnet like this: '2007:700:111:1/'. This should be # fixed when/if proper subnet-handling is implemented. if mask < 0 or mask > 128: raise SubnetError("Invalid subnet mask '%s'; " "outside range 0-128" % mask) return True
def find(self, identifier): """Find and instantiate the subnet entity with data from the db. @type identifier: mixed @param identifier: The identifier of the Subnet. Note that the DNS module behaves a bit differently than other Cerebrum modules, in that this find method accepts other input than entity_id. Possibilities are: - A string containing the entity_id, prefixed with 'entity_id:' or 'id:'. - A string with a subnet address, e.g. '2002:123:111::/64'. - A string with an IPv6 address, e.g. '2001:700:111::0:12'. """ binds = {} if identifier is None: raise SubnetError("Unable to find IPv6 subnet identified" " by '%s'" % identifier) if isinstance(identifier, (int, long)): # The proper way of running find() where_param = "entity_id = :e_id" binds['e_id'] = identifier elif identifier.startswith('id:') or identifier.startswith( 'entity_id:'): # E.g. 'id:X' or 'entity_id:X'; where_param = "entity_id = :e_id" try: binds['e_id'] = int(identifier.split(':')[1]) except ValueError: raise SubnetError("Entity ID must be an integer") elif identifier.find('/') > 0: # A '/' indicates a subnet spec: just need the ip where_param = "subnet_ip = :subnet_ip" subnet_ip = identifier.split('/')[0] binds['subnet_ip'] = IPv6Utils.explode(subnet_ip) else: # Last valid type is simply an IP; need to find correct range where_param = "ip_min <= :ip AND ip_max >= :ip" binds['ip'] = IPv6Calc.ip_to_long(identifier) try: (eid, self.subnet_ip, self.ip_min, self.ip_max, self.description, self.dns_delegated, self.name_prefix, self.vlan_number, self.no_of_reserved_adr) = self.query_1( """ SELECT entity_id, subnet_ip, ip_min, ip_max, description, dns_delegated, name_prefix, vlan_number, no_of_reserved_adr FROM [:table schema=cerebrum name=dns_ipv6_subnet] WHERE %s""" % where_param, binds) self.__super.find(eid) self.subnet_mask = IPv6Subnet.calculate_subnet_mask( self.ip_min, self.ip_max) self.calculate_reserved_addresses() except NotFoundError: raise SubnetError("Unable to find IPv6 subnet identified" " by '%s'" % identifier) self.__in_db = True self.__updated = []
def find(self, identifier): """Find and instantiate the subnet entity with data from the db. @type identifier: mixed @param identifier: The identifier of the Subnet. Note that the DNS module behaves a bit differently than other Cerebrum modules, in that this find method accepts other input than entity_id. Possibilities are: - A string containing the entity_id, prefixed with 'entity_id:' or 'id:'. - A string with a subnet address, e.g. '2002:123:111::/64'. - A string with an IPv6 address, e.g. '2001:700:111::0:12'. """ binds = {} if identifier is None: raise SubnetError("Unable to find IPv6 subnet identified by '%s'" % identifier) if isinstance(identifier, (int, long)): # The proper way of running find() where_param = "entity_id = :e_id" binds['e_id'] = identifier elif identifier.startswith('id:') or identifier.startswith('entity_id:'): # E.g. 'id:X' or 'entity_id:X'; where_param = "entity_id = :e_id" try: binds['e_id'] = int(identifier.split(':')[1]) except ValueError: raise SubnetError("Entity ID must be an integer") elif identifier.find('/') > 0: # A '/' indicates a subnet spec: just need the ip where_param = "subnet_ip = :subnet_ip" subnet_ip = identifier.split('/')[0] binds['subnet_ip'] = IPv6Utils.explode(subnet_ip) else: # Last valid type is simply an IP; need to find correct range where_param = "ip_min <= :ip AND ip_max >= :ip" binds['ip'] = IPv6Calc.ip_to_long(identifier) try: (eid, self.subnet_ip, self.ip_min, self.ip_max, self.description, self.dns_delegated, self.name_prefix, self.vlan_number, self.no_of_reserved_adr) = self.query_1( """ SELECT entity_id, subnet_ip, ip_min, ip_max, description, dns_delegated, name_prefix, vlan_number, no_of_reserved_adr FROM [:table schema=cerebrum name=dns_ipv6_subnet] WHERE %s""" % where_param, binds) self.__super.find(eid) self.subnet_mask = IPv6Subnet.calculate_subnet_mask(self.ip_min, self.ip_max) self.calculate_reserved_addresses() except NotFoundError: raise SubnetError("Unable to find IPv6 subnet identified by '%s'" % identifier) self.__in_db = True self.__updated = []
def write_db(self): self.__super.write_db() if not self.__updated: return is_new = not self.__in_db if 'aaaa_ip' in self.__updated: # normalize the address self.aaaa_ip = IPv6Utils.explode(self.aaaa_ip) if 'mac_adr' in self.__updated and self.mac_adr is not None: self.mac_adr = self._verify_mac_format(self.mac_adr) binds = { 'ipv6_number_id': self.entity_id, 'aaaa_ip': self.aaaa_ip, 'mac_adr': self.mac_adr } defs = { 'tc': ', '.join(x for x in sorted(binds)), 'tb': ', '.join(':{0}'.format(x) for x in sorted(binds)), 'ts': ', '.join('{0}=:{0}'.format(x) for x in binds if x != 'ipv6_number_id') } if is_new: insert_stmt = """ INSERT INTO [:table schema=cerebrum name=dns_ipv6_number] (%(tc)s) VALUES (%(tb)s)""" % defs self.execute(insert_stmt, binds) self._db.log_change(self.entity_id, self.clconst.ipv6_number_add, None, change_params={'aaaa_ip': self.aaaa_ip}) else: exists_stmt = """ SELECT EXISTS ( SELECT 1 FROM [:table schema=cerebrum name=dns_ipv6_number] WHERE (mac_adr is NULL AND :mac_adr is NULL OR mac_adr=:mac_adr) AND (aaaa_ip is NULL AND :aaaa_ip is NULL OR aaaa_ip=:aaaa_ip) AND ipv6_number_id=:ipv6_number_id ) """ if not self.query_1(exists_stmt, binds): # True positive update_stmt = """ UPDATE [:table schema=cerebrum name=dns_ipv6_number] SET %(ts)s WHERE ipv6_number_id=:ipv6_number_id""" % defs self.execute(update_stmt, binds) self._db.log_change(self.entity_id, self.clconst.ipv6_number_add, None, change_params={'aaaa_ip': self.aaaa_ip}) del self.__in_db if 'mac_adr' in self.__updated: self._db.log_change(self.entity_id, self.clconst.mac_adr_set, None, change_params={'mac_adr': self.mac_adr}) self.__in_db = True self.__updated = [] return is_new
def test_not_is_valid_ipv6(addr): assert not IPv6Utils.is_valid_ipv6(addr)