コード例 #1
0
ファイル: IPv6Number.py プロジェクト: unioslo/cerebrum
 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))})
コード例 #2
0
ファイル: IPv6Number.py プロジェクト: Narvik-kommune/cerebrum
 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))
         })
コード例 #3
0
    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()
コード例 #4
0
    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()
コード例 #5
0
ファイル: Utils.py プロジェクト: chrnux/cerebrum
    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
コード例 #6
0
ファイル: IPv6Number.py プロジェクト: unioslo/cerebrum
    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)
コード例 #7
0
ファイル: IPv6Number.py プロジェクト: Narvik-kommune/cerebrum
 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)
コード例 #8
0
ファイル: Utils.py プロジェクト: unioslo/cerebrum
    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
コード例 #9
0
ファイル: IPv6Number.py プロジェクト: chrnux/cerebrum
    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
コード例 #10
0
    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
コード例 #11
0
ファイル: IPv6Number.py プロジェクト: unioslo/cerebrum
    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
コード例 #12
0
    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
コード例 #13
0
    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
コード例 #14
0
    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
コード例 #15
0
    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 = []
コード例 #16
0
    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 = []
コード例 #17
0
ファイル: IPv6Number.py プロジェクト: Narvik-kommune/cerebrum
    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
コード例 #18
0
ファイル: test_IPv6Utils.py プロジェクト: chrnux/cerebrum
def test_not_is_valid_ipv6(addr):
    assert not IPv6Utils.is_valid_ipv6(addr)