Merge branch '2019.2' into issue_51869

This commit is contained in:
Gareth J. Greenaway 2019-04-05 13:35:27 -07:00 committed by GitHub
commit cd78485aab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 244 additions and 93 deletions

View file

@ -1193,6 +1193,57 @@ def _clean_value(key, val):
return val
def _windows_os_release_grain(caption, product_type):
'''
helper function for getting the osrelease grain
:return:
'''
# This creates the osrelease grain based on the Windows Operating
# System Product Name. As long as Microsoft maintains a similar format
# this should be future proof
version = 'Unknown'
release = ''
if 'Server' in caption:
for item in caption.split(' '):
# If it's all digits, then it's version
if re.match(r'\d+', item):
version = item
# If it starts with R and then numbers, it's the release
# ie: R2
if re.match(r'^R\d+$', item):
release = item
os_release = '{0}Server{1}'.format(version, release)
else:
for item in caption.split(' '):
# If it's a number, decimal number, Thin or Vista, then it's the
# version
if re.match(r'^(\d+(\.\d+)?)|Thin|Vista|XP$', item):
version = item
os_release = version
# If the version is still Unknown, revert back to the old way of getting
# the os_release
# https://github.com/saltstack/salt/issues/52339
if os_release in ['Unknown']:
os_release = platform.release()
server = {'Vista': '2008Server',
'7': '2008ServerR2',
'8': '2012Server',
'8.1': '2012ServerR2',
'10': '2016Server'}
# Starting with Python 2.7.12 and 3.5.2 the `platform.uname()`
# function started reporting the Desktop version instead of the
# Server version on # Server versions of Windows, so we need to look
# those up. So, if you find a Server Platform that's a key in the
# server dictionary, then lookup the actual Server Release.
# (Product Type 1 is Desktop, Everything else is Server)
if product_type > 1 and os_release in server:
os_release = server[os_release]
return os_release
def _windows_platform_data():
'''
Use the platform module for as much as we can.
@ -1240,7 +1291,6 @@ def _windows_platform_data():
except IndexError:
log.debug('Motherboard info not available on this system')
os_release = platform.release()
kernel_version = platform.version()
info = salt.utils.win_osinfo.get_os_version_info()
net_info = salt.utils.win_osinfo.get_join_info()
@ -1249,28 +1299,8 @@ def _windows_platform_data():
if info['ServicePackMajor'] > 0:
service_pack = ''.join(['SP', six.text_type(info['ServicePackMajor'])])
# This creates the osrelease grain based on the Windows Operating
# System Product Name. As long as Microsoft maintains a similar format
# this should be future proof
version = 'Unknown'
release = ''
if 'Server' in osinfo.Caption:
for item in osinfo.Caption.split(' '):
# If it's all digits, then it's version
if re.match(r'\d+', item):
version = item
# If it starts with R and then numbers, it's the release
# ie: R2
if re.match(r'^R\d+$', item):
release = item
os_release = '{0}Server{1}'.format(version, release)
else:
for item in osinfo.Caption.split(' '):
# If it's a number, decimal number, Thin or Vista, then it's the
# version
if re.match(r'^(\d+(\.\d+)?)|Thin|Vista$', item):
version = item
os_release = version
os_release = _windows_os_release_grain(caption=osinfo.Caption,
product_type=osinfo.ProductType)
grains = {
'kernelrelease': _clean_value('kernelrelease', osinfo.Version),

View file

@ -53,11 +53,10 @@ def _render_tab(lst):
for pre in lst['pre']:
ret.append('{0}\n'.format(pre))
for cron in lst['crons']:
ret.append('{0} {1} {2} {3}\n'.format(cron['path'],
cron['mask'],
cron['cmd'],
TAG
)
ret.append('{0} {1} {2}\n'.format(cron['path'],
cron['mask'],
cron['cmd'],
)
)
return ret
@ -191,23 +190,18 @@ def list_tab(user):
'pre': []
}
flag = False
comment = None
tag = '# Line managed by Salt, do not edit'
for line in data.splitlines():
if line.endswith(tag):
if len(line.split()) > 3:
# Appears to be a standard incron line
comps = line.split()
path = comps[0]
mask = comps[1]
(cmd, comment) = ' '.join(comps[2:]).split(' # ')
if len(line.split()) > 3:
# Appears to be a standard incron line
comps = line.split()
path = comps[0]
mask = comps[1]
cmd = ' '.join(comps[2:])
dat = {'path': path,
'mask': mask,
'cmd': cmd,
'comment': comment}
ret['crons'].append(dat)
comment = None
dat = {'path': path,
'mask': mask,
'cmd': cmd}
ret['crons'].append(dat)
else:
ret['pre'].append(line)
return ret

View file

@ -823,6 +823,7 @@ def active_tcp():
return {}
@salt.utils.decorators.path.which('traceroute')
def traceroute(host):
'''
Performs a traceroute to a 3rd party host
@ -840,29 +841,23 @@ def traceroute(host):
salt '*' network.traceroute archlinux.org
'''
ret = []
if not salt.utils.path.which('traceroute'):
log.info('This minion does not have traceroute installed')
return ret
cmd = 'traceroute {0}'.format(salt.utils.network.sanitize_host(host))
out = __salt__['cmd.run'](cmd)
# Parse version of traceroute
if salt.utils.platform.is_sunos() or salt.utils.platform.is_aix():
traceroute_version = [0, 0, 0]
else:
cmd2 = 'traceroute --version'
out2 = __salt__['cmd.run'](cmd2)
version_out = __salt__['cmd.run']('traceroute --version')
try:
# Linux traceroute version looks like:
# Modern traceroute for Linux, version 2.0.19, Dec 10 2012
# Darwin and FreeBSD traceroute version looks like: Version 1.4a12+[FreeBSD|Darwin]
traceroute_version_raw = re.findall(r'.*[Vv]ersion (\d+)\.([\w\+]+)\.*(\w*)', out2)[0]
log.debug('traceroute_version_raw: %s', traceroute_version_raw)
version_raw = re.findall(r'.*[Vv]ersion (\d+)\.([\w\+]+)\.*(\w*)', version_out)[0]
log.debug('traceroute_version_raw: %s', version_raw)
traceroute_version = []
for t in traceroute_version_raw:
for t in version_raw:
try:
traceroute_version.append(int(t))
except ValueError:
@ -877,26 +872,28 @@ def traceroute(host):
traceroute_version = [0, 0, 0]
for line in out.splitlines():
# Pre requirements for line parsing
skip_line = False
if ' ' not in line:
continue
skip_line = True
if line.startswith('traceroute'):
continue
skip_line = True
if salt.utils.platform.is_aix():
if line.startswith('trying to get source for'):
continue
skip_line = True
if line.startswith('source should be'):
continue
skip_line = True
if line.startswith('outgoing MTU'):
continue
skip_line = True
if line.startswith('fragmentation required'):
continue
skip_line = True
if skip_line:
log.debug('Skipping traceroute output line: %s', line)
continue
# Parse output from unix variants
if 'Darwin' in six.text_type(traceroute_version[1]) or \
'FreeBSD' in six.text_type(traceroute_version[1]) or \
'FreeBSD' in six.text_type(traceroute_version[1]) or \
__grains__['kernel'] in ('SunOS', 'AIX'):
try:
traceline = re.findall(r'\s*(\d*)\s+(.*)\s+\((.*)\)\s+(.*)$', line)[0]
@ -923,14 +920,15 @@ def traceroute(host):
except IndexError:
result = {}
# Parse output from specific version ranges
elif (traceroute_version[0] >= 2 and traceroute_version[2] >= 14
or traceroute_version[0] >= 2 and traceroute_version[1] > 0):
comps = line.split(' ')
if comps[1] == '* * *':
if len(comps) >= 2 and comps[1] == '* * *':
result = {
'count': int(comps[0]),
'hostname': '*'}
else:
elif len(comps) >= 5:
result = {
'count': int(comps[0]),
'hostname': comps[1].split()[0],
@ -938,21 +936,29 @@ def traceroute(host):
'ms1': float(comps[2].split()[0]),
'ms2': float(comps[3].split()[0]),
'ms3': float(comps[4].split()[0])}
else:
result = {}
# Parse anything else
else:
comps = line.split()
result = {
'count': comps[0],
'hostname': comps[1],
'ip': comps[2],
'ms1': comps[4],
'ms2': comps[6],
'ms3': comps[8],
'ping1': comps[3],
'ping2': comps[5],
'ping3': comps[7]}
if len(comps) >= 8:
result = {
'count': comps[0],
'hostname': comps[1],
'ip': comps[2],
'ms1': comps[4],
'ms2': comps[6],
'ms3': comps[8],
'ping1': comps[3],
'ping2': comps[5],
'ping3': comps[7]}
else:
result = {}
ret.append(result)
if not result:
log.warn('Cannot parse traceroute output line: %s', line)
return ret

View file

@ -67,7 +67,7 @@ def present(name,
if beacon_data == current_beacons[name]:
ret['comment'].append('Job {0} in correct state'.format(name))
else:
if 'test' in __opts__ and __opts__['test']:
if __opts__.get('test'):
kwargs['test'] = True
result = __salt__['beacons.modify'](name, beacon_data, **kwargs)
ret['comment'].append(result['comment'])
@ -86,7 +86,7 @@ def present(name,
ret['comment'].append(result['comment'])
else:
if 'test' in __opts__ and __opts__['test']:
if __opts__.get('test'):
kwargs['test'] = True
result = __salt__['beacons.add'](name, beacon_data, **kwargs)
ret['comment'].append(result['comment'])
@ -100,8 +100,11 @@ def present(name,
ret['comment'].append('Adding {0} to beacons'.format(name))
if save:
result = __salt__['beacons.save']()
ret['comment'].append('Beacon {0} saved'.format(name))
if __opts__.get('test'):
ret['comment'].append('Beacon {0} would be saved'.format(name))
else:
result = __salt__['beacons.save']()
ret['comment'].append('Beacon {0} saved'.format(name))
ret['comment'] = '\n'.join(ret['comment'])
return ret
@ -130,7 +133,7 @@ def absent(name,
current_beacons = __salt__['beacons.list'](return_yaml=False, **kwargs)
if name in current_beacons:
if 'test' in __opts__ and __opts__['test']:
if __opts__.get('test'):
kwargs['test'] = True
result = __salt__['beacons.delete'](name, **kwargs)
ret['comment'].append(result['comment'])
@ -146,8 +149,11 @@ def absent(name,
ret['comment'].append('{0} not configured in beacons'.format(name))
if save:
result = __salt__['beacons.save']()
ret['comment'].append('Beacon {0} saved'.format(name))
if __opts__.get('test'):
ret['comment'].append('Beacon {0} would be saved'.format(name))
else:
result = __salt__['beacons.save']()
ret['comment'].append('Beacon {0} saved'.format(name))
ret['comment'] = '\n'.join(ret['comment'])
return ret
@ -172,7 +178,7 @@ def enabled(name, **kwargs):
current_beacons = __salt__['beacons.list'](return_yaml=False, **kwargs)
if name in current_beacons:
if 'test' in __opts__ and __opts__['test']:
if __opts__.get('test'):
kwargs['test'] = True
result = __salt__['beacons.enable_beacon'](name, **kwargs)
ret['comment'].append(result['comment'])
@ -210,7 +216,7 @@ def disabled(name, **kwargs):
current_beacons = __salt__['beacons.list'](return_yaml=False, **kwargs)
if name in current_beacons:
if 'test' in __opts__ and __opts__['test']:
if __opts__.get('test'):
kwargs['test'] = True
result = __salt__['beacons.disable_beacon'](name, **kwargs)
ret['comment'].append(result['comment'])

View file

@ -44,6 +44,9 @@ then a new cron job will be added to the user's crontab.
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import logging
log = logging.getLogger(__name__)
def _check_cron(user,
path,
@ -56,6 +59,8 @@ def _check_cron(user,
arg_mask.sort()
lst = __salt__['incron.list_tab'](user)
if cmd.endswith('\n'):
cmd = cmd[:-1]
for cron in lst['crons']:
if path == cron['path'] and cron['cmd'] == cmd:
cron_mask = cron['mask'].split(',')

View file

@ -604,6 +604,115 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin):
'2016Server', '2019Server']
self.assertIn(returned_grains['osrelease'], valid_releases)
def test__windows_os_release_grain(self):
versions = {
'Windows 10 Home': '10',
'Windows 10 Pro': '10',
'Windows 10 Pro for Workstations': '10',
'Windows 10 Pro Education': '10',
'Windows 10 Enterprise': '10',
'Windows 10 Enterprise LTSB': '10',
'Windows 10 Education': '10',
'Windows 10 IoT Core': '10',
'Windows 10 IoT Enterprise': '10',
'Windows 10 S': '10',
'Windows 8.1': '8.1',
'Windows 8.1 Pro': '8.1',
'Windows 8.1 Enterprise': '8.1',
'Windows 8.1 OEM': '8.1',
'Windows 8.1 with Bing': '8.1',
'Windows 8': '8',
'Windows 8 Pro': '8',
'Windows 8 Enterprise': '8',
'Windows 8 OEM': '8',
'Windows 7 Starter': '7',
'Windows 7 Home Basic': '7',
'Windows 7 Home Premium': '7',
'Windows 7 Professional': '7',
'Windows 7 Enterprise': '7',
'Windows 7 Ultimate': '7',
'Windows Thin PC': 'Thin',
'Windows Vista Starter': 'Vista',
'Windows Vista Home Basic': 'Vista',
'Windows Vista Home Premium': 'Vista',
'Windows Vista Business': 'Vista',
'Windows Vista Enterprise': 'Vista',
'Windows Vista Ultimate': 'Vista',
'Windows Server 2019 Essentials': '2019Server',
'Windows Server 2019 Standard': '2019Server',
'Windows Server 2019 Datacenter': '2019Server',
'Windows Server 2016 Essentials': '2016Server',
'Windows Server 2016 Standard': '2016Server',
'Windows Server 2016 Datacenter': '2016Server',
'Windows Server 2012 R2 Foundation': '2012ServerR2',
'Windows Server 2012 R2 Essentials': '2012ServerR2',
'Windows Server 2012 R2 Standard': '2012ServerR2',
'Windows Server 2012 R2 Datacenter': '2012ServerR2',
'Windows Server 2012 Foundation': '2012Server',
'Windows Server 2012 Essentials': '2012Server',
'Windows Server 2012 Standard': '2012Server',
'Windows Server 2012 Datacenter': '2012Server',
'Windows MultiPoint Server 2012': '2012Server',
'Windows Small Business Server 2011': '2011Server',
'Windows MultiPoint Server 2011': '2011Server',
'Windows Home Server 2011': '2011Server',
'Windows MultiPoint Server 2010': '2010Server',
'Windows Server 2008 R2 Foundation': '2008ServerR2',
'Windows Server 2008 R2 Standard': '2008ServerR2',
'Windows Server 2008 R2 Enterprise': '2008ServerR2',
'Windows Server 2008 R2 Datacenter': '2008ServerR2',
'Windows Server 2008 R2 for Itanium-based Systems': '2008ServerR2',
'Windows Web Server 2008 R2': '2008ServerR2',
'Windows Storage Server 2008 R2': '2008ServerR2',
'Windows HPC Server 2008 R2': '2008ServerR2',
'Windows Server 2008 Standard': '2008Server',
'Windows Server 2008 Enterprise': '2008Server',
'Windows Server 2008 Datacenter': '2008Server',
'Windows Server 2008 for Itanium-based Systems': '2008Server',
'Windows Server Foundation 2008': '2008Server',
'Windows Essential Business Server 2008': '2008Server',
'Windows HPC Server 2008': '2008Server',
'Windows Small Business Server 2008': '2008Server',
'Windows Storage Server 2008': '2008Server',
'Windows Web Server 2008': '2008Server'
}
for caption in versions:
version = core._windows_os_release_grain(caption, 1)
self.assertEqual(
version,
versions[caption],
'version: {0}\n'
'found: {1}\n'
'caption: {2}'.format(version, versions[caption], caption)
)
embedded_versions = {
'Windows Embedded 8.1 Industry Pro': '8.1',
'Windows Embedded 8 Industry Pro': '8',
'Windows POSReady 7': '7',
'Windows Embedded Standard 7': '7',
'Windows Embedded POSReady 2009': '2009',
'Windows Embedded Standard 2009': '2009',
'Windows XP Embedded': 'XP',
}
for caption in embedded_versions:
version = core._windows_os_release_grain(caption, 1)
self.assertEqual(
version,
embedded_versions[caption],
'{0} != {1}\n'
'version: {0}\n'
'found: {1}\n'
'caption: {2}'.format(version, embedded_versions[caption], caption)
)
# Special Cases
# Windows Embedded Standard is Windows 7
caption = 'Windows Embedded Standard'
with patch('platform.release', MagicMock(return_value='7')):
version = core._windows_os_release_grain(caption, 1)
self.assertEqual(version, '7')
@skipIf(not salt.utils.platform.is_linux(), 'System is not Linux')
def test_linux_memdata(self):
'''

View file

@ -102,7 +102,7 @@ class IncronTestCase(TestCase, LoaderModuleMockMixin):
val = {'pre': [], 'crons': [{'path': '/home/cybage',
'mask': 'IN_MODIFY',
'cmd': 'echo "SALT"', 'comment': ''}]}
'cmd': 'echo "SALT"'}]}
with patch.object(incron, 'list_tab',
MagicMock(return_value=val)):
self.assertEqual(incron.set_job('cybage', '/home/cybage',
@ -135,7 +135,7 @@ class IncronTestCase(TestCase, LoaderModuleMockMixin):
val = {'pre': [], 'crons': [{'path': '/home/cybage',
'mask': 'IN_MODIFY,IN_DELETE',
'cmd': 'echo "SALT"', 'comment': ''}]}
'cmd': 'echo "SALT"'}]}
with patch.object(incron, 'list_tab',
MagicMock(return_value=val)):
mock = MagicMock(return_value='incrontab')

View file

@ -113,14 +113,15 @@ class NetworkTestCase(TestCase, LoaderModuleMockMixin):
'''
Test for Performs a traceroute to a 3rd party host
'''
with patch.object(salt.utils.path, 'which', side_effect=[False, True]):
self.assertListEqual(network.traceroute('host'), [])
with patch('salt.utils.path.which', MagicMock(return_value='traceroute')):
with patch.dict(network.__salt__, {'cmd.run': MagicMock(return_value='')}):
self.assertListEqual(network.traceroute('gentoo.org'), [])
with patch.object(salt.utils.network, 'sanitize_host',
return_value='A'):
return_value='gentoo.org'):
with patch.dict(network.__salt__, {'cmd.run':
MagicMock(return_value="")}):
self.assertListEqual(network.traceroute('host'), [])
MagicMock(return_value='')}):
self.assertListEqual(network.traceroute('gentoo.org'), [])
def test_dig(self):
'''