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 4b783227f7.

* timezone module: revert platform decision rule; just warn instead of futher command checks

* timezone module: [NosystemdTimezone] enhanced error message
pull/30535/head
Shinichi TAMURA 7 years ago committed by Brian Coca
parent 229a86c952
commit 934ae28365

@ -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…
Cancel
Save