Merge pull request #2168 from archtaku/develop

Add Subnet/IP address matcher
This commit is contained in:
Thomas S Hatch 2012-10-04 13:16:04 -07:00
commit 6bbef3df8e
5 changed files with 64 additions and 12 deletions

View file

@ -94,7 +94,11 @@ Options
.. option:: -N, --nodegroup
Use a predefined compound target defined in the Salt master configuration
file
file.
.. option:: -S, --ipcidr
Match based on Subnet (CIDR notation) or IPv4 address.
.. option:: -R, --range

View file

@ -21,6 +21,7 @@ E PCRE Minion id match ``E@web\d+\.(dev|qa|prod)\.loc``
P Grains PCRE match ``P@os:(RedHat|Fedora|CentOS)``
L List of minions ``L@minion1.example.com,minion3.domain.com and bl*.domain.com``
I Pillar glob match ``I@pdata:foobar``
S Subnet/IP addr match ``S@192.168.1.0/24`` or ``S@192.168.1.100``
====== ==================== ===============================================================
Matchers can be joined using boolean ``and``, ``or``, and ``not`` operators.

View file

@ -857,6 +857,25 @@ class Matcher(object):
comps[1].lower(),
))
def ipcidr_match(self, tgt):
'''
Matches based on ip address or CIDR notation
'''
num_parts = len(tgt.split('/'))
if num_parts > 2:
return False
elif num_parts == 2:
return self.functions['network.in_subnet'](tgt)
else:
import socket
try:
socket.inet_aton(tgt)
except socket.error:
# Not a valid IPv4 address
return False
else:
return tgt in self.functions['network.ip_addrs']()
def compound_match(self, tgt):
'''
Runs the compound target check
@ -869,12 +888,13 @@ class Matcher(object):
'X': 'exsel',
'I': 'pillar',
'L': 'list',
'S': 'ipcidr',
'E': 'pcre'}
results = []
opers = ['and', 'or', 'not']
for match in tgt.split():
# Try to match tokens from the compound target, first by using
# the 'G, X, I, L, E' matcher types, then by hostname glob.
# the 'G, X, I, L, S, E' matcher types, then by hostname glob.
if '@' in match and match[1] == '@':
comps = match.split('@')
matcher = ref.get(comps[0])

View file

@ -293,8 +293,6 @@ def in_subnet(cidr):
log.error('Invalid CIDR \'{0}\''.format(cidr))
return False
ifaces = interfaces()
netstart_bin = _ipv4_to_bits(netstart)
if netsize < 32 and len(netstart_bin.rstrip('0')) > netsize:
@ -303,18 +301,41 @@ def in_subnet(cidr):
return False
netstart_leftbits = netstart_bin[0:netsize]
for ipv4_info in ifaces.values():
for ipv4 in ipv4_info.get('inet', []):
if ipv4['address'] == '127.0.0.1': continue
if netsize == 32:
if netstart == ipv4['address']: return True
else:
ip_leftbits = _ipv4_to_bits(ipv4['address'])[0:netsize]
if netstart_leftbits == ip_leftbits: return True
for ip_addr in ip_addrs():
if netsize == 32:
if netstart == ip_addr: return True
else:
ip_leftbits = _ipv4_to_bits(ip_addr)[0:netsize]
if netstart_leftbits == ip_leftbits: return True
return False
def ip_addrs():
'''
Returns a list of IPv4 addresses assigned to the host. (127.0.0.1 is
ignored)
'''
ret = []
ifaces = interfaces()
for ipv4_info in ifaces.values():
for ipv4 in ipv4_info.get('inet',[]):
if ipv4['address'] != '127.0.0.1': ret.append(ipv4['address'])
return ret
def ip_addrs6():
'''
Returns a list of IPv6 addresses assigned to the host. (::1 is ignored)
'''
ret = []
ifaces = interfaces()
for ipv6_info in ifaces.values():
for ipv6 in ipv6_info.get('inet6',[]):
if ipv6['address'] != '::1': ret.append(ipv6['address'])
return ret
def ping(host):
'''
Performs a ping to a host

View file

@ -487,6 +487,12 @@ class ExtendedTargetOptionsMixIn(TargetOptionsMixIn):
'for the target is the pillar key followed by a glob'
'expression:\n"role:production*"')
)
group.add_option(
'-S', '--ipcidr',
default=False,
action='store_true',
help=('Match based on Subnet (CIDR notation) or IPv4 address.')
)
self._create_process_functions()