Server IP : 85.214.239.14 / Your IP : 3.137.175.83 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/self/root/lib/python3/dist-packages/ansible/parsing/utils/ |
Upload File : |
# Copyright 2015 Abhijit Menon-Sen <ams@2ndQuadrant.com> # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. # Make coding more python3-ish from __future__ import (absolute_import, division, print_function) __metaclass__ = type import re from ansible.errors import AnsibleParserError, AnsibleError # Components that match a numeric or alphanumeric begin:end or begin:end:step # range expression inside square brackets. numeric_range = r''' \[ (?:[0-9]+:[0-9]+) # numeric begin:end (?::[0-9]+)? # numeric :step (optional) \] ''' hexadecimal_range = r''' \[ (?:[0-9a-f]+:[0-9a-f]+) # hexadecimal begin:end (?::[0-9]+)? # numeric :step (optional) \] ''' alphanumeric_range = r''' \[ (?: [a-z]:[a-z]| # one-char alphabetic range [0-9]+:[0-9]+ # ...or a numeric one ) (?::[0-9]+)? # numeric :step (optional) \] ''' # Components that match a 16-bit portion of an IPv6 address in hexadecimal # notation (0..ffff) or an 8-bit portion of an IPv4 address in decimal notation # (0..255) or an [x:y(:z)] numeric range. ipv6_component = r''' (?: [0-9a-f]{{1,4}}| # 0..ffff {range} # or a numeric range ) '''.format(range=hexadecimal_range) ipv4_component = r''' (?: [01]?[0-9]{{1,2}}| # 0..199 2[0-4][0-9]| # 200..249 25[0-5]| # 250..255 {range} # or a numeric range ) '''.format(range=numeric_range) # A hostname label, e.g. 'foo' in 'foo.example.com'. Consists of alphanumeric # characters plus dashes (and underscores) or valid ranges. The label may not # start or end with a hyphen or an underscore. This is interpolated into the # hostname pattern below. We don't try to enforce the 63-char length limit. label = r''' (?:[\w]|{range}) # Starts with an alphanumeric or a range (?:[\w_-]|{range})* # Then zero or more of the same or [_-] (?<![_-]) # ...as long as it didn't end with [_-] '''.format(range=alphanumeric_range) patterns = { # This matches a square-bracketed expression with a port specification. What # is inside the square brackets is validated later. 'bracketed_hostport': re.compile( r'''^ \[(.+)\] # [host identifier] :([0-9]+) # :port number $ ''', re.X ), # This matches a bare IPv4 address or hostname (or host pattern including # [x:y(:z)] ranges) with a port specification. 'hostport': re.compile( r'''^ ((?: # We want to match: [^:\[\]] # (a non-range character | # ...or... \[[^\]]*\] # a complete bracketed expression) )*) # repeated as many times as possible :([0-9]+) # followed by a port number $ ''', re.X ), # This matches an IPv4 address, but also permits range expressions. 'ipv4': re.compile( r'''^ (?:{i4}\.){{3}}{i4} # Three parts followed by dots plus one $ '''.format(i4=ipv4_component), re.X | re.I ), # This matches an IPv6 address, but also permits range expressions. # # This expression looks complex, but it really only spells out the various # combinations in which the basic unit of an IPv6 address (0..ffff) can be # written, from :: to 1:2:3:4:5:6:7:8, plus the IPv4-in-IPv6 variants such # as ::ffff:192.0.2.3. # # Note that we can't just use ipaddress.ip_address() because we also have to # accept ranges in place of each component. 'ipv6': re.compile( r'''^ (?:{0}:){{7}}{0}| # uncompressed: 1:2:3:4:5:6:7:8 (?:{0}:){{1,6}}:| # compressed variants, which are all (?:{0}:)(?::{0}){{1,6}}| # a::b for various lengths of a,b (?:{0}:){{2}}(?::{0}){{1,5}}| (?:{0}:){{3}}(?::{0}){{1,4}}| (?:{0}:){{4}}(?::{0}){{1,3}}| (?:{0}:){{5}}(?::{0}){{1,2}}| (?:{0}:){{6}}(?::{0})| # ...all with 2 <= a+b <= 7 :(?::{0}){{1,6}}| # ::ffff(:ffff...) {0}?::| # ffff::, :: # ipv4-in-ipv6 variants (?:0:){{6}}(?:{0}\.){{3}}{0}| ::(?:ffff:)?(?:{0}\.){{3}}{0}| (?:0:){{5}}ffff:(?:{0}\.){{3}}{0} $ '''.format(ipv6_component), re.X | re.I ), # This matches a hostname or host pattern including [x:y(:z)] ranges. # # We roughly follow DNS rules here, but also allow ranges (and underscores). # In the past, no systematic rules were enforced about inventory hostnames, # but the parsing context (e.g. shlex.split(), fnmatch.fnmatch()) excluded # various metacharacters anyway. # # We don't enforce DNS length restrictions here (63 characters per label, # 253 characters total) or make any attempt to process IDNs. 'hostname': re.compile( r'''^ {label} # We must have at least one label (?:\.{label})* # Followed by zero or more .labels $ '''.format(label=label), re.X | re.I | re.UNICODE ), } def parse_address(address, allow_ranges=False): """ Takes a string and returns a (host, port) tuple. If the host is None, then the string could not be parsed as a host identifier with an optional port specification. If the port is None, then no port was specified. The host identifier may be a hostname (qualified or not), an IPv4 address, or an IPv6 address. If allow_ranges is True, then any of those may contain [x:y] range specifications, e.g. foo[1:3] or foo[0:5]-bar[x-z]. The port number is an optional :NN suffix on an IPv4 address or host name, or a mandatory :NN suffix on any square-bracketed expression: IPv6 address, IPv4 address, or host name. (This means the only way to specify a port for an IPv6 address is to enclose it in square brackets.) """ # First, we extract the port number if one is specified. port = None for matching in ['bracketed_hostport', 'hostport']: m = patterns[matching].match(address) if m: (address, port) = m.groups() port = int(port) continue # What we're left with now must be an IPv4 or IPv6 address, possibly with # numeric ranges, or a hostname with alphanumeric ranges. host = None for matching in ['ipv4', 'ipv6', 'hostname']: m = patterns[matching].match(address) if m: host = address continue # If it isn't any of the above, we don't understand it. if not host: raise AnsibleError("Not a valid network hostname: %s" % address) # If we get to this point, we know that any included ranges are valid. # If the caller is prepared to handle them, all is well. # Otherwise we treat it as a parse failure. if not allow_ranges and '[' in host: raise AnsibleParserError("Detected range in host but was asked to ignore ranges") return (host, port)