mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Timezone modeule: Better env choice for Linux (#28229)
* timezone module: fixed platform decision rule for Linux — For better handling of environments where timedatectl is unavailable * timezone module: allow absence of configuration files if specific commands are available * timezone module: remove duplicated line * timezone module: fixed docs to clarify returned diff * timezone module: fixed “undefined variable err” * Revert "timezone module: fixed docs to clarify returned diff" This reverts commit 4b783227f713eee9aa6717c0a8b9e697b939f471. * timezone module: revert platform decision rule; just warn instead of futher command checks * timezone module: [NosystemdTimezone] enhanced error message
This commit is contained in:
parent
229a86c952
commit
934ae28365
1 changed files with 42 additions and 13 deletions
|
@ -70,6 +70,7 @@ EXAMPLES = '''
|
|||
name: Asia/Tokyo
|
||||
'''
|
||||
|
||||
import errno
|
||||
import os
|
||||
import platform
|
||||
import random
|
||||
|
@ -99,8 +100,13 @@ class Timezone(object):
|
|||
"""
|
||||
if get_platform() == 'Linux':
|
||||
timedatectl = module.get_bin_path('timedatectl')
|
||||
if timedatectl is not None and module.run_command(timedatectl)[0] == 0:
|
||||
return super(Timezone, SystemdTimezone).__new__(SystemdTimezone)
|
||||
if timedatectl is not None:
|
||||
rc, stdout, stderr = module.run_command(timedatectl)
|
||||
if rc == 0:
|
||||
return super(Timezone, SystemdTimezone).__new__(SystemdTimezone)
|
||||
else:
|
||||
module.warn('timedatectl command was found but not usable: %s. using other method.' % stderr)
|
||||
return super(Timezone, NosystemdTimezone).__new__(NosystemdTimezone)
|
||||
else:
|
||||
return super(Timezone, NosystemdTimezone).__new__(NosystemdTimezone)
|
||||
elif re.match('^joyent_.*Z', platform.version()):
|
||||
|
@ -311,6 +317,8 @@ class NosystemdTimezone(Timezone):
|
|||
adjtime='/etc/adjtime'
|
||||
)
|
||||
|
||||
allow_no_file = dict()
|
||||
|
||||
regexps = dict(
|
||||
name =None, # To be set in __init__
|
||||
hwclock=re.compile(r'^UTC\s*=\s*([^\s]+)', re.MULTILINE),
|
||||
|
@ -325,28 +333,41 @@ class NosystemdTimezone(Timezone):
|
|||
self.update_timezone = self.module.get_bin_path('cp', required=True)
|
||||
self.update_timezone += ' %s /etc/localtime' % tzfile
|
||||
self.update_hwclock = self.module.get_bin_path('hwclock', required=True)
|
||||
self.allow_no_file['hwclock'] = True # Since this is only used for get values, file absense does not metter
|
||||
# Distribution-specific configurations
|
||||
if self.module.get_bin_path('dpkg-reconfigure') is not None:
|
||||
# Debian/Ubuntu
|
||||
self.update_timezone = self.module.get_bin_path('dpkg-reconfigure', required=True)
|
||||
self.update_timezone += ' --frontend noninteractive tzdata'
|
||||
self.conf_files['name'] = '/etc/timezone'
|
||||
self.allow_no_file['name'] = True
|
||||
self.conf_files['hwclock'] = '/etc/default/rcS'
|
||||
self.regexps['name'] = re.compile(r'^([^\s]+)', re.MULTILINE)
|
||||
self.tzline_format = '%s\n'
|
||||
else:
|
||||
# RHEL/CentOS
|
||||
if self.module.get_bin_path('tzdata-update') is not None:
|
||||
self.update_timezone = self.module.get_bin_path('tzdata-update', required=True)
|
||||
self.update_timezone = self.module.get_bin_path('tzdata-update', required=True)
|
||||
self.allow_no_file['name'] = True
|
||||
# else:
|
||||
# self.update_timezone = 'cp ...' <- configured above
|
||||
# self.update_timezone = 'cp ...' <- configured above
|
||||
# self.allow_no_file['name'] = False <- this is default behavior
|
||||
self.conf_files['name'] = '/etc/sysconfig/clock'
|
||||
self.conf_files['hwclock'] = '/etc/sysconfig/clock'
|
||||
self.regexps['name'] = re.compile(r'^ZONE\s*=\s*"?([^"\s]+)"?', re.MULTILINE)
|
||||
self.tzline_format = 'ZONE="%s"\n'
|
||||
self.update_hwclock = self.module.get_bin_path('hwclock', required=True)
|
||||
|
||||
def _edit_file(self, filename, regexp, value):
|
||||
def _allow_ioerror(self, err, key):
|
||||
# In some cases, even if the target file does not exist,
|
||||
# simply creating it may solve the problem.
|
||||
# In such cases, we should continue the configuration rather than aborting.
|
||||
if err.errno != errno.ENOENT:
|
||||
# If the error is not ENOENT ("No such file or directory"),
|
||||
# (e.g., permission error, etc), we should abort.
|
||||
return False
|
||||
return self.allow_no_file.get(key, False)
|
||||
|
||||
def _edit_file(self, filename, regexp, value, key):
|
||||
"""Replace the first matched line with given `value`.
|
||||
|
||||
If `regexp` matched more than once, other than the first line will be deleted.
|
||||
|
@ -355,12 +376,16 @@ class NosystemdTimezone(Timezone):
|
|||
filename: The name of the file to edit.
|
||||
regexp: The regular expression to search with.
|
||||
value: The line which will be inserted.
|
||||
key: For what key the file is being editted.
|
||||
"""
|
||||
# Read the file
|
||||
try:
|
||||
file = open(filename, 'r')
|
||||
except IOError:
|
||||
self.abort('cannot read "%s"' % filename)
|
||||
except IOError as err:
|
||||
if self._allow_ioerror(err, key):
|
||||
lines = []
|
||||
else:
|
||||
self.abort('tried to configure %s using a file "%s", but could not read it' % (key, filename))
|
||||
else:
|
||||
lines = file.readlines()
|
||||
file.close()
|
||||
|
@ -382,7 +407,7 @@ class NosystemdTimezone(Timezone):
|
|||
try:
|
||||
file = open(filename, 'w')
|
||||
except IOError:
|
||||
self.abort('cannot write to "%s"' % filename)
|
||||
self.abort('tried to configure %s using a file "%s", but could not write to it' % (key, filename))
|
||||
else:
|
||||
file.writelines(lines)
|
||||
file.close()
|
||||
|
@ -397,15 +422,18 @@ class NosystemdTimezone(Timezone):
|
|||
|
||||
try:
|
||||
file = open(filename, mode='r')
|
||||
except IOError:
|
||||
self.abort('cannot read configuration file "%s" for %s' % (filename, key))
|
||||
except IOError as err:
|
||||
if self._allow_ioerror(err, key):
|
||||
return None
|
||||
else:
|
||||
self.abort('tried to configure %s using a file "%s", but could not read it' % (key, filename))
|
||||
else:
|
||||
status = file.read()
|
||||
file.close()
|
||||
try:
|
||||
value = self.regexps[key].search(status).group(1)
|
||||
except AttributeError:
|
||||
self.abort('cannot find the valid value from configuration file "%s" for %s' % (filename, key))
|
||||
self.abort('tried to configure %s using a file "%s", but could not find a valid value in it' % (key, filename))
|
||||
else:
|
||||
if key == 'hwclock':
|
||||
# For key='hwclock'; convert yes/no -> UTC/local
|
||||
|
@ -422,7 +450,8 @@ class NosystemdTimezone(Timezone):
|
|||
def set_timezone(self, value):
|
||||
self._edit_file(filename=self.conf_files['name'],
|
||||
regexp=self.regexps['name'],
|
||||
value=self.tzline_format % value)
|
||||
value=self.tzline_format % value,
|
||||
key='name')
|
||||
self.execute(self.update_timezone)
|
||||
|
||||
def set_hwclock(self, value):
|
||||
|
|
Loading…
Reference in a new issue