Merge pull request #26951 from terminalmage/fix-timezone

Fix timezone module for CentOS
This commit is contained in:
Justin Findlay 2015-09-10 14:46:07 -06:00
commit fbc95f4685
2 changed files with 211 additions and 128 deletions

View File

@ -6,8 +6,10 @@ from __future__ import absolute_import
# Import python libs
import os
import errno
import logging
import re
import string
# Import salt libs
import salt.utils
@ -25,6 +27,67 @@ def __virtual__():
return True
def _get_zone_solaris():
tzfile = '/etc/TIMEZONE'
with salt.utils.fopen(tzfile, 'r') as fp_:
for line in fp_:
if 'TZ=' in line:
zonepart = line.rstrip('\n').split('=')[-1]
return zonepart.strip('\'"') or 'UTC'
raise CommandExecutionError('Unable to get timezone from ' + tzfile)
def _get_zone_sysconfig():
tzfile = '/etc/sysconfig/clock'
with salt.utils.fopen(tzfile, 'r') as fp_:
for line in fp_:
if re.match(r'^\s*#', line):
continue
if 'ZONE' in line and '=' in line:
zonepart = line.rstrip('\n').split('=')[-1]
return zonepart.strip('\'"') or 'UTC'
raise CommandExecutionError('Unable to get timezone from ' + tzfile)
def _get_zone_etc_localtime():
tzfile = '/etc/localtime'
tzdir = '/usr/share/zoneinfo/'
tzdir_len = len(tzdir)
try:
olson_name = os.path.normpath(
os.path.join('/etc', os.readlink(tzfile))
)
if olson_name.startswith(tzdir):
return olson_name[tzdir_len:]
except OSError as exc:
if exc.errno == errno.ENOENT:
raise CommandExecutionError(tzfile + ' does not exist')
elif exc.errno == errno.EINVAL:
log.warning(
tzfile + ' is not a symbolic link, attempting to match ' +
tzfile + ' to zoneinfo files'
)
# Regular file. Try to match the hash.
hash_type = __opts__.get('hash_type', 'md5')
tzfile_hash = salt.utils.get_hash(tzfile, hash_type)
# Not a link, just a copy of the tzdata file
for root, dirs, files in os.walk(tzdir):
for filename in files:
full_path = os.path.join(root, filename)
olson_name = full_path[tzdir_len:]
if olson_name[0] in string.ascii_lowercase:
continue
if tzfile_hash == \
salt.utils.get_hash(full_path, hash_type):
return olson_name
raise CommandExecutionError('Unable to determine timezone')
def _get_zone_etc_timezone():
with salt.utils.fopen('/etc/timezone', 'r') as fp_:
return fp_.read().strip()
def get_zone():
'''
Get current timezone (i.e. America/Denver)
@ -37,7 +100,7 @@ def get_zone():
'''
cmd = ''
if salt.utils.which('timedatectl'):
out = __salt__['cmd.run']('timedatectl')
out = __salt__['cmd.run'](['timedatectl'], python_shell=False)
for line in (x.strip() for x in out.splitlines()):
try:
return re.match(r'Time ?zone:\s+(\S+)', line).group(1)
@ -46,23 +109,21 @@ def get_zone():
raise CommandExecutionError(
'Failed to parse timedatectl output, this is likely a bug'
)
elif 'RedHat' in __grains__['os_family']:
cmd = 'grep ZONE /etc/sysconfig/clock | grep -vE "^#"'
elif 'Suse' in __grains__['os_family']:
cmd = 'grep ZONE /etc/sysconfig/clock | grep -vE "^#"'
elif 'Debian' in __grains__['os_family']:
with salt.utils.fopen('/etc/timezone', 'r') as ofh:
return ofh.read().strip()
elif 'Gentoo' in __grains__['os_family']:
with salt.utils.fopen('/etc/timezone', 'r') as ofh:
return ofh.read().strip()
elif __grains__['os_family'] in ('FreeBSD', 'OpenBSD', 'NetBSD'):
return os.readlink('/etc/localtime').lstrip('/usr/share/zoneinfo/')
elif 'Solaris' in __grains__['os_family']:
cmd = 'grep "TZ=" /etc/TIMEZONE'
out = __salt__['cmd.run'](cmd, python_shell=True).split('=')
ret = out[1].replace('"', '')
return ret
else:
if __grains__['os'].lower() == 'centos':
return _get_zone_etc_localtime()
os_family = __grains__['os_family']
for family in ('RedHat', 'Suse'):
if family in os_family:
return _get_zone_sysconfig()
for family in ('Debian', 'Gentoo'):
if family in os_family:
return _get_zone_etc_timezone()
if os_family in ('FreeBSD', 'OpenBSD', 'NetBSD'):
return _get_zone_etc_localtime()
elif 'Solaris' in os_family:
return _get_zone_solaris()
raise CommandExecutionError('Unable to get timezone')
def get_zonecode():
@ -75,9 +136,7 @@ def get_zonecode():
salt '*' timezone.get_zonecode
'''
cmd = 'date +%Z'
out = __salt__['cmd.run'](cmd)
return out
return __salt__['cmd.run'](['date', '+%Z'], python_shell=False)
def get_offset():
@ -90,9 +149,7 @@ def get_offset():
salt '*' timezone.get_offset
'''
cmd = 'date +%z'
out = __salt__['cmd.run'](cmd)
return out
return __salt__['cmd.run'](['date', '+%z'], python_shell=False)
def set_zone(timezone):
@ -101,7 +158,8 @@ def set_zone(timezone):
The timezone is crucial to several system processes, each of which SHOULD
be restarted (for instance, whatever you system uses as its cron and
syslog daemons). This will not be magically done for you!
syslog daemons). This will not be automagically done and must be done
manually!
CLI Example:
@ -195,7 +253,7 @@ def get_hwclock():
'''
cmd = ''
if salt.utils.which('timedatectl'):
out = __salt__['cmd.run']('timedatectl')
out = __salt__['cmd.run'](['timedatectl'], python_shell=False)
for line in (x.strip() for x in out.splitlines()):
if 'rtc in local tz' in line.lower():
try:
@ -208,39 +266,64 @@ def get_hwclock():
raise CommandExecutionError(
'Failed to parse timedatectl output, this is likely a bug'
)
elif 'RedHat' in __grains__['os_family']:
cmd = 'tail -n 1 /etc/adjtime'
return __salt__['cmd.run'](cmd)
elif 'Suse' in __grains__['os_family']:
cmd = 'tail -n 1 /etc/adjtime'
return __salt__['cmd.run'](cmd)
elif 'Debian' in __grains__['os_family']:
#Original way to look up hwclock on Debian-based systems
cmd = 'grep "UTC=" /etc/default/rcS | grep -vE "^#"'
out = __salt__['cmd.run'](
cmd, ignore_retcode=True, python_shell=True).split('=')
if len(out) > 1:
if out[1] == 'yes':
return 'UTC'
else:
return 'localtime'
else:
#Since Wheezy
cmd = 'tail -n 1 /etc/adjtime'
return __salt__['cmd.run'](cmd)
elif 'Gentoo' in __grains__['os_family']:
cmd = 'grep "^clock=" /etc/conf.d/hwclock | grep -vE "^#"'
out = __salt__['cmd.run'](cmd, python_shell=True).split('=')
return out[1].replace('"', '')
elif 'Solaris' in __grains__['os_family']:
if os.path.isfile('/etc/rtc_config'):
with salt.utils.fopen('/etc/rtc_config', 'r') as fp_:
for line in fp_:
if line.startswith('zone_info=GMT'):
return 'UTC'
return 'localtime'
else:
return 'UTC'
else:
os_family = __grains__['os_family']
for family in ('RedHat', 'Suse'):
if family in os_family:
cmd = ['tail', '-n', '1', '/etc/adjtime']
return __salt__['cmd.run'](cmd, python_shell=False)
if 'Debian' in __grains__['os_family']:
# Original way to look up hwclock on Debian-based systems
try:
with salt.utils.fopen('/etc/default/rcS', 'r') as fp_:
for line in fp_:
if re.match(r'^\s*#', line):
continue
if 'UTC=' in line:
is_utc = line.rstrip('\n').split('=')[-1].lower()
if is_utc == 'yes':
return 'UTC'
else:
return 'localtime'
except IOError as exc:
pass
# Since Wheezy
cmd = ['tail', '-n', '1', '/etc/adjtime']
return __salt__['cmd.run'](cmd, python_shell=False)
elif 'Gentoo' in __grains__['os_family']:
offset_file = '/etc/conf.d/hwclock'
try:
with salt.utils.fopen(offset_file, 'r') as fp_:
for line in fp_:
if line.startswith('clock='):
line = line.rstrip('\n')
return line.split('=')[-1].strip('\'"')
raise CommandExecutionError(
'Offset information not found in {0}'.format(
offset_file
)
)
except IOError as exc:
raise CommandExecutionError(
'Problem reading offset file {0}: {1}'
.format(offset_file, exc.strerror)
)
elif 'Solaris' in __grains__['os_family']:
offset_file = '/etc/rtc_config'
try:
with salt.utils.fopen(offset_file, 'r') as fp_:
for line in fp_:
if line.startswith('zone_info=GMT'):
return 'UTC'
return 'localtime'
except IOError as exc:
if exc.errno == errno.ENOENT:
# offset file does not exist
return 'UTC'
raise CommandExecutionError(
'Problem reading offset file {0}: {1}'
.format(offset_file, exc.strerror)
)
def set_hwclock(clock):
@ -256,33 +339,31 @@ def set_hwclock(clock):
timezone = get_zone()
if 'Solaris' in __grains__['os_family']:
if clock.lower() not in ('localtime', 'utc'):
raise SaltInvocationError(
'localtime and UTC are the only permitted values'
)
if 'sparc' in __grains__['cpuarch']:
return 'UTC is the only choice for SPARC architecture'
if clock == 'localtime':
cmd = 'rtc -z {0}'.format(timezone)
__salt__['cmd.run'](cmd)
return True
elif clock == 'UTC':
cmd = 'rtc -z GMT'
__salt__['cmd.run'](cmd)
return True
else:
zonepath = '/usr/share/zoneinfo/{0}'.format(timezone)
raise SaltInvocationError(
'UTC is the only choice for SPARC architecture'
)
cmd = ['rtc', '-z', 'GMT' if clock.lower() == 'utc' else timezone]
return __salt__['cmd.retcode'](cmd, python_shell=False) == 0
zonepath = '/usr/share/zoneinfo/{0}'.format(timezone)
if not os.path.exists(zonepath):
return 'Zone does not exist: {0}'.format(zonepath)
raise CommandExecutionError(
'Zone \'{0}\' does not exist'.format(zonepath)
)
if 'Solaris' not in __grains__['os_family']:
os.unlink('/etc/localtime')
os.symlink(zonepath, '/etc/localtime')
os.unlink('/etc/localtime')
os.symlink(zonepath, '/etc/localtime')
if 'Arch' in __grains__['os_family']:
if clock == 'localtime':
cmd = 'timezonectl set-local-rtc true'
__salt__['cmd.run'](cmd)
else:
cmd = 'timezonectl set-local-rtc false'
__salt__['cmd.run'](cmd)
cmd = ['timezonectl', 'set-local-rtc',
'true' if clock == 'localtime' else 'false']
return __salt__['cmd.retcode'](cmd, python_shell=False) == 0
elif 'RedHat' in __grains__['os_family']:
__salt__['file.sed'](
'/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="{0}"'.format(timezone))

View File

@ -22,8 +22,7 @@ ensure_in_syspath('../../')
import salt.utils
from salt.modules import timezone
import os
from salt.exceptions import CommandExecutionError
from salt.exceptions import SaltInvocationError
from salt.exceptions import CommandExecutionError, SaltInvocationError
# Globals
@ -53,22 +52,28 @@ class TimezoneTestCase(TestCase):
with patch('salt.utils.fopen', mock_open(read_data=file_data),
create=True) as mfile:
mfile.return_value.__iter__.return_value = file_data.splitlines()
with patch.dict(timezone.__grains__, {'os_family': 'Debian'}):
with patch.dict(timezone.__grains__, {'os_family': 'Debian',
'os': 'Debian'}):
self.assertEqual(timezone.get_zone(), '#\nA')
with patch.dict(timezone.__grains__, {'os_family': 'Gentoo'}):
with patch.dict(timezone.__grains__, {'os_family': 'Gentoo',
'os': 'Gentoo'}):
self.assertEqual(timezone.get_zone(), '')
with patch.dict(timezone.__grains__, {'os_family': 'FreeBSD'}):
with patch.object(os, 'readlink',
return_value='/usr/share/zoneinfo/'):
self.assertEqual(timezone.get_zone(), '')
with patch.dict(timezone.__grains__, {'os_family': 'FreeBSD',
'os': 'FreeBSD'}):
zone = 'America/Denver'
linkpath = '/usr/share/zoneinfo/' + zone
with patch.object(os, 'readlink', return_value=linkpath):
self.assertEqual(timezone.get_zone(), zone)
with patch.dict(timezone.__grains__, {'os_family': 'Solaris'}):
with patch.dict(timezone.__salt__,
{'cmd.run':
MagicMock(return_value='A=B')}):
self.assertEqual(timezone.get_zone(), 'B')
with patch.dict(timezone.__grains__, {'os_family': 'Solaris',
'os': 'Solaris'}):
fl_data = 'TZ=Foo\n'
with patch('salt.utils.fopen',
mock_open(read_data=fl_data)) as mfile:
mfile.return_value.__iter__.return_value = [fl_data]
self.assertEqual(timezone.get_zone(), 'Foo')
def test_get_zonecode(self):
'''
@ -171,26 +176,24 @@ class TimezoneTestCase(TestCase):
self.assertEqual(timezone.get_hwclock(), 'A')
with patch.dict(timezone.__grains__, {'os_family': 'Debian'}):
with patch.dict(timezone.__salt__,
{'cmd.run':
MagicMock(return_value='A=yes')}):
fl_data = 'UTC=yes\n'
with patch('salt.utils.fopen',
mock_open(read_data=fl_data)) as mfile:
mfile.return_value.__iter__.return_value = [fl_data]
self.assertEqual(timezone.get_hwclock(), 'UTC')
with patch.dict(timezone.__salt__,
{'cmd.run':
MagicMock(return_value='A=no')}):
fl_data = 'UTC=no\n'
with patch('salt.utils.fopen',
mock_open(read_data=fl_data)) as mfile:
mfile.return_value.__iter__.return_value = [fl_data]
self.assertEqual(timezone.get_hwclock(), 'localtime')
with patch.dict(timezone.__salt__,
{'cmd.run':
MagicMock(return_value='A')}):
self.assertEqual(timezone.get_hwclock(), 'A')
with patch.dict(timezone.__grains__, {'os_family': 'Gentoo'}):
with patch.dict(timezone.__salt__,
{'cmd.run':
MagicMock(return_value='A=B')}):
self.assertEqual(timezone.get_hwclock(), 'B')
fl_data = 'clock=UTC\n'
with patch('salt.utils.fopen',
mock_open(read_data=fl_data)) as mfile:
mfile.return_value.__iter__.return_value = [fl_data]
self.assertEqual(timezone.get_hwclock(), 'UTC')
mock = MagicMock(return_value=True)
with patch.object(os.path, 'isfile', mock):
@ -226,31 +229,30 @@ class TimezoneTestCase(TestCase):
'''
Test to sets the hardware clock to be either UTC or localtime
'''
ret = ('UTC is the only choice for SPARC architecture')
ret1 = ('Zone does not exist: /usr/share/zoneinfo/America/Denver')
with patch.object(timezone, 'get_zone',
return_value='America/Denver'):
zone = 'America/Denver'
with patch.object(timezone, 'get_zone', return_value=zone):
with patch.dict(timezone.__grains__, {'os_family': 'Solaris',
'cpuarch': 'sparc'}):
self.assertEqual(timezone.set_hwclock('clock'), ret)
self.assertRaises(
SaltInvocationError,
timezone.set_hwclock,
'clock'
)
self.assertRaises(
SaltInvocationError,
timezone.set_hwclock,
'localtime'
)
with patch.dict(timezone.__salt__,
{'cmd.run':
MagicMock(return_value=None)}):
self.assertTrue(timezone.set_hwclock('localtime'))
self.assertTrue(timezone.set_hwclock('UTC'))
with patch.dict(timezone.__grains__, {'os_family': 'Sola'}):
with patch.dict(timezone.__grains__,
{'os_family': 'DoesNotMatter'}):
with patch.object(os.path, 'exists', return_value=False):
self.assertEqual(timezone.set_hwclock('clock'), ret1)
with patch.object(os.path, 'exists', return_value=True):
with patch.object(os, 'unlink', return_value=None):
with patch.object(os, 'symlink', return_value=None):
self.assertTrue(timezone.set_hwclock('clock'))
self.assertRaises(
CommandExecutionError,
timezone.set_hwclock,
'UTC'
)
if __name__ == '__main__':
from integration import run_tests