mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
timezone: Add support for macOS (#23447)
* timezone: Add support for macOS On macOS, preferred way of managing timezone is via `systemsetup(8)`. Thus, we use this command instead of relying on directly modifying `/etc/localtime` as in other *BSDs. * timezone: Use % instead of .format() in strings This ensures better compatibility across different versions of Python.
This commit is contained in:
parent
cb5f2c7ac3
commit
90e2def72a
1 changed files with 63 additions and 12 deletions
|
@ -21,9 +21,10 @@ description:
|
|||
- This module configures the timezone setting, both of the system clock and of the hardware clock. If you want to set up the NTP, use M(service) module.
|
||||
- It is recommended to restart C(crond) after changing the timezone, otherwise the jobs may run at the wrong time.
|
||||
- Several different tools are used depending on the OS/Distribution involved.
|
||||
For Linux it can use C(timedatectl) or edit C(/etc/sysconfig/clock) or C(/etc/timezone) andC(hwclock).
|
||||
On SmartOS , C(sm-set-timezone), for BSD, C(/etc/localtime) is modified.
|
||||
For Linux it can use C(timedatectl) or edit C(/etc/sysconfig/clock) or C(/etc/timezone) andC(hwclock).
|
||||
On SmartOS, C(sm-set-timezone), for macOS, C(systemsetup), for BSD, C(/etc/localtime) is modified.
|
||||
- As of version 2.3 support was added for SmartOS and BSDs.
|
||||
- As of version 2.4 support was added for macOS.
|
||||
- Windows, AIX and HPUX are not supported, please let us know if you find any other OS/distro in which this fails.
|
||||
version_added: "2.2"
|
||||
options:
|
||||
|
@ -48,6 +49,7 @@ notes:
|
|||
author:
|
||||
- "Shinichi TAMURA (@tmshn)"
|
||||
- "Jasper Lievisse Adriaanse (@jasperla)"
|
||||
- "Indrajit Raychaudhuri (@indrajitr)"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
|
@ -120,6 +122,8 @@ class Timezone(object):
|
|||
module.fail_json(msg='Adjusting timezone is not supported in Global Zone')
|
||||
|
||||
return super(Timezone, SmartOSTimezone).__new__(SmartOSTimezone)
|
||||
elif re.match('^Darwin', platform.platform()):
|
||||
return super(Timezone, DarwinTimezone).__new__(DarwinTimezone)
|
||||
elif re.match('^(Free|Net|Open)BSD', platform.platform()):
|
||||
return super(Timezone, BSDTimezone).__new__(BSDTimezone)
|
||||
else:
|
||||
|
@ -500,14 +504,14 @@ class SmartOSTimezone(Timezone):
|
|||
except:
|
||||
self.module.fail_json(msg='Failed to read /etc/default/init')
|
||||
else:
|
||||
self.module.fail_json(msg='{0} is not a supported option on target platform'.format(key))
|
||||
self.module.fail_json(msg='%s is not a supported option on target platform' % key)
|
||||
|
||||
def set(self, key, value):
|
||||
"""Set the requested timezone through sm-set-timezone, an invalid timezone name
|
||||
will be rejected and we have no further input validation to perform.
|
||||
"""
|
||||
if key == 'name':
|
||||
cmd = 'sm-set-timezone {0}'.format(value)
|
||||
cmd = 'sm-set-timezone %s' % value
|
||||
|
||||
(rc, stdout, stderr) = self.module.run_command(cmd)
|
||||
|
||||
|
@ -516,12 +520,60 @@ class SmartOSTimezone(Timezone):
|
|||
|
||||
# sm-set-timezone knows no state and will always set the timezone.
|
||||
# XXX: https://github.com/joyent/smtools/pull/2
|
||||
m = re.match('^\* Changed (to)? timezone (to)? ({0}).*'.format(value), stdout.splitlines()[1])
|
||||
m = re.match('^\* Changed (to)? timezone (to)? (%s).*' % value, stdout.splitlines()[1])
|
||||
if not (m and m.groups()[-1] == value):
|
||||
self.module.fail_json(msg='Failed to set timezone')
|
||||
else:
|
||||
self.module.fail_json(msg='{0} is not a supported option on target platform'.
|
||||
format(key))
|
||||
self.module.fail_json(msg='%s is not a supported option on target platform' % key)
|
||||
|
||||
|
||||
class DarwinTimezone(Timezone):
|
||||
"""This is the timezone implementation for Darwin which, unlike other *BSD
|
||||
implementations, uses the `systemsetup` command on Darwin to check/set
|
||||
the timezone.
|
||||
"""
|
||||
|
||||
regexps = dict(
|
||||
name = re.compile(r'^\s*Time ?Zone\s*:\s*([^\s]+)', re.MULTILINE)
|
||||
)
|
||||
|
||||
def __init__(self, module):
|
||||
super(DarwinTimezone, self).__init__(module)
|
||||
self.systemsetup = module.get_bin_path('systemsetup', required=True)
|
||||
self.status = dict()
|
||||
# Validate given timezone
|
||||
if 'name' in self.value:
|
||||
self._verify_timezone()
|
||||
|
||||
def _get_current_timezone(self, phase):
|
||||
"""Lookup the current timezone via `systemsetup -gettimezone`."""
|
||||
if phase not in self.status:
|
||||
self.status[phase] = self.execute(self.systemsetup, '-gettimezone')
|
||||
return self.status[phase]
|
||||
|
||||
def _verify_timezone(self):
|
||||
tz = self.value['name']['planned']
|
||||
# Lookup the list of supported timezones via `systemsetup -listtimezones`.
|
||||
# Note: Skip the first line that contains the label 'Time Zones:'
|
||||
out = self.execute(self.systemsetup, '-listtimezones').splitlines()[1:]
|
||||
tz_list = list(map(lambda x: x.strip(), out))
|
||||
if not tz in tz_list:
|
||||
self.abort('given timezone "%s" is not available' % tz)
|
||||
return tz
|
||||
|
||||
def get(self, key, phase):
|
||||
if key == 'name':
|
||||
status = self._get_current_timezone(phase)
|
||||
value = self.regexps[key].search(status).group(1)
|
||||
return value
|
||||
else:
|
||||
self.module.fail_json(msg='%s is not a supported option on target platform' % key)
|
||||
|
||||
def set(self, key, value):
|
||||
if key == 'name':
|
||||
self.execute(self.systemsetup, '-settimezone', value, log=True)
|
||||
else:
|
||||
self.module.fail_json(msg='%s is not a supported option on target platform' % key)
|
||||
|
||||
|
||||
class BSDTimezone(Timezone):
|
||||
|
@ -543,8 +595,7 @@ class BSDTimezone(Timezone):
|
|||
self.module.warn('Could not read /etc/localtime. Assuming UTC')
|
||||
return 'UTC'
|
||||
else:
|
||||
self.module.fail_json(msg='{0} is not a supported option on target platform'.
|
||||
format(key))
|
||||
self.module.fail_json(msg='%s is not a supported option on target platform' % key)
|
||||
|
||||
def set(self, key, value):
|
||||
if key == 'name':
|
||||
|
@ -553,9 +604,9 @@ class BSDTimezone(Timezone):
|
|||
zonefile = '/usr/share/zoneinfo/' + value
|
||||
try:
|
||||
if not os.path.isfile(zonefile):
|
||||
self.module.fail_json(msg='{0} is not a recognized timezone'.format(value))
|
||||
self.module.fail_json(msg='%s is not a recognized timezone' % value)
|
||||
except:
|
||||
self.module.fail_json(msg='Failed to stat {0}'.format(zonefile))
|
||||
self.module.fail_json(msg='Failed to stat %s' % zonefile)
|
||||
|
||||
# Now (somewhat) atomically update the symlink by creating a new
|
||||
# symlink and move it into place. Otherwise we have to remove the
|
||||
|
@ -572,7 +623,7 @@ class BSDTimezone(Timezone):
|
|||
os.remove(new_localtime)
|
||||
self.module.fail_json(msg='Could not update /etc/localtime')
|
||||
else:
|
||||
self.module.fail_json(msg='{0} is not a supported option on target platform'.format(key))
|
||||
self.module.fail_json(msg='%s is not a supported option on target platform' % key)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
Loading…
Reference in a new issue