mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-27 15:11:23 -07: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
|
name: Asia/Tokyo
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import errno
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import random
|
import random
|
||||||
|
@ -99,8 +100,13 @@ class Timezone(object):
|
||||||
"""
|
"""
|
||||||
if get_platform() == 'Linux':
|
if get_platform() == 'Linux':
|
||||||
timedatectl = module.get_bin_path('timedatectl')
|
timedatectl = module.get_bin_path('timedatectl')
|
||||||
if timedatectl is not None and module.run_command(timedatectl)[0] == 0:
|
if timedatectl is not None:
|
||||||
|
rc, stdout, stderr = module.run_command(timedatectl)
|
||||||
|
if rc == 0:
|
||||||
return super(Timezone, SystemdTimezone).__new__(SystemdTimezone)
|
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:
|
else:
|
||||||
return super(Timezone, NosystemdTimezone).__new__(NosystemdTimezone)
|
return super(Timezone, NosystemdTimezone).__new__(NosystemdTimezone)
|
||||||
elif re.match('^joyent_.*Z', platform.version()):
|
elif re.match('^joyent_.*Z', platform.version()):
|
||||||
|
@ -311,6 +317,8 @@ class NosystemdTimezone(Timezone):
|
||||||
adjtime='/etc/adjtime'
|
adjtime='/etc/adjtime'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
allow_no_file = dict()
|
||||||
|
|
||||||
regexps = dict(
|
regexps = dict(
|
||||||
name =None, # To be set in __init__
|
name =None, # To be set in __init__
|
||||||
hwclock=re.compile(r'^UTC\s*=\s*([^\s]+)', re.MULTILINE),
|
hwclock=re.compile(r'^UTC\s*=\s*([^\s]+)', re.MULTILINE),
|
||||||
|
@ -325,12 +333,14 @@ class NosystemdTimezone(Timezone):
|
||||||
self.update_timezone = self.module.get_bin_path('cp', required=True)
|
self.update_timezone = self.module.get_bin_path('cp', required=True)
|
||||||
self.update_timezone += ' %s /etc/localtime' % tzfile
|
self.update_timezone += ' %s /etc/localtime' % tzfile
|
||||||
self.update_hwclock = self.module.get_bin_path('hwclock', required=True)
|
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
|
# Distribution-specific configurations
|
||||||
if self.module.get_bin_path('dpkg-reconfigure') is not None:
|
if self.module.get_bin_path('dpkg-reconfigure') is not None:
|
||||||
# Debian/Ubuntu
|
# Debian/Ubuntu
|
||||||
self.update_timezone = self.module.get_bin_path('dpkg-reconfigure', required=True)
|
self.update_timezone = self.module.get_bin_path('dpkg-reconfigure', required=True)
|
||||||
self.update_timezone += ' --frontend noninteractive tzdata'
|
self.update_timezone += ' --frontend noninteractive tzdata'
|
||||||
self.conf_files['name'] = '/etc/timezone'
|
self.conf_files['name'] = '/etc/timezone'
|
||||||
|
self.allow_no_file['name'] = True
|
||||||
self.conf_files['hwclock'] = '/etc/default/rcS'
|
self.conf_files['hwclock'] = '/etc/default/rcS'
|
||||||
self.regexps['name'] = re.compile(r'^([^\s]+)', re.MULTILINE)
|
self.regexps['name'] = re.compile(r'^([^\s]+)', re.MULTILINE)
|
||||||
self.tzline_format = '%s\n'
|
self.tzline_format = '%s\n'
|
||||||
|
@ -338,15 +348,26 @@ class NosystemdTimezone(Timezone):
|
||||||
# RHEL/CentOS
|
# RHEL/CentOS
|
||||||
if self.module.get_bin_path('tzdata-update') is not None:
|
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:
|
# 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['name'] = '/etc/sysconfig/clock'
|
||||||
self.conf_files['hwclock'] = '/etc/sysconfig/clock'
|
self.conf_files['hwclock'] = '/etc/sysconfig/clock'
|
||||||
self.regexps['name'] = re.compile(r'^ZONE\s*=\s*"?([^"\s]+)"?', re.MULTILINE)
|
self.regexps['name'] = re.compile(r'^ZONE\s*=\s*"?([^"\s]+)"?', re.MULTILINE)
|
||||||
self.tzline_format = 'ZONE="%s"\n'
|
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`.
|
"""Replace the first matched line with given `value`.
|
||||||
|
|
||||||
If `regexp` matched more than once, other than the first line will be deleted.
|
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.
|
filename: The name of the file to edit.
|
||||||
regexp: The regular expression to search with.
|
regexp: The regular expression to search with.
|
||||||
value: The line which will be inserted.
|
value: The line which will be inserted.
|
||||||
|
key: For what key the file is being editted.
|
||||||
"""
|
"""
|
||||||
# Read the file
|
# Read the file
|
||||||
try:
|
try:
|
||||||
file = open(filename, 'r')
|
file = open(filename, 'r')
|
||||||
except IOError:
|
except IOError as err:
|
||||||
self.abort('cannot read "%s"' % filename)
|
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:
|
else:
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
file.close()
|
file.close()
|
||||||
|
@ -382,7 +407,7 @@ class NosystemdTimezone(Timezone):
|
||||||
try:
|
try:
|
||||||
file = open(filename, 'w')
|
file = open(filename, 'w')
|
||||||
except IOError:
|
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:
|
else:
|
||||||
file.writelines(lines)
|
file.writelines(lines)
|
||||||
file.close()
|
file.close()
|
||||||
|
@ -397,15 +422,18 @@ class NosystemdTimezone(Timezone):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
file = open(filename, mode='r')
|
file = open(filename, mode='r')
|
||||||
except IOError:
|
except IOError as err:
|
||||||
self.abort('cannot read configuration file "%s" for %s' % (filename, key))
|
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:
|
else:
|
||||||
status = file.read()
|
status = file.read()
|
||||||
file.close()
|
file.close()
|
||||||
try:
|
try:
|
||||||
value = self.regexps[key].search(status).group(1)
|
value = self.regexps[key].search(status).group(1)
|
||||||
except AttributeError:
|
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:
|
else:
|
||||||
if key == 'hwclock':
|
if key == 'hwclock':
|
||||||
# For key='hwclock'; convert yes/no -> UTC/local
|
# For key='hwclock'; convert yes/no -> UTC/local
|
||||||
|
@ -422,7 +450,8 @@ class NosystemdTimezone(Timezone):
|
||||||
def set_timezone(self, value):
|
def set_timezone(self, value):
|
||||||
self._edit_file(filename=self.conf_files['name'],
|
self._edit_file(filename=self.conf_files['name'],
|
||||||
regexp=self.regexps['name'],
|
regexp=self.regexps['name'],
|
||||||
value=self.tzline_format % value)
|
value=self.tzline_format % value,
|
||||||
|
key='name')
|
||||||
self.execute(self.update_timezone)
|
self.execute(self.update_timezone)
|
||||||
|
|
||||||
def set_hwclock(self, value):
|
def set_hwclock(self, value):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue