From ee71a7df748e95a932cddf055f45364a0ba9d9de Mon Sep 17 00:00:00 2001 From: d-little Date: Mon, 15 Jul 2019 14:52:51 -0500 Subject: [PATCH] timezone - Add AIX support using `chtz` (#58838) * init AIX support * fix pylint whitespace issues * Switch chtz from required=False to True * Refactor AIX to include _get_timezone() * More Pylint... --- lib/ansible/modules/system/timezone.py | 100 ++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/lib/ansible/modules/system/timezone.py b/lib/ansible/modules/system/timezone.py index b42d82f36a5..a9a189b3709 100644 --- a/lib/ansible/modules/system/timezone.py +++ b/lib/ansible/modules/system/timezone.py @@ -21,9 +21,11 @@ description: - 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) and C(hwclock). On SmartOS, C(sm-set-timezone), for macOS, C(systemsetup), for BSD, C(/etc/localtime) is modified. + On AIX, C(chtz) is used. - As of Ansible 2.3 support was added for SmartOS and BSDs. - As of Ansible 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. + - As of Ansible 2.9 support was added for AIX 6.1+ + - Windows 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: name: @@ -45,6 +47,8 @@ options: choices: [ local, UTC ] notes: - On SmartOS the C(sm-set-timezone) utility (part of the smtools package) is required to set the zone timezone + - On AIX only Olson/tz database timezones are useable (POSIX is not supported). + - An OS reboot is also required on AIX for the new timezone setting to take effect. author: - Shinichi TAMURA (@tmshn) - Jasper Lievisse Adriaanse (@jasperla) @@ -126,6 +130,12 @@ class Timezone(object): return super(Timezone, DarwinTimezone).__new__(DarwinTimezone) elif re.match('^(Free|Net|Open)BSD', platform.platform()): return super(Timezone, BSDTimezone).__new__(BSDTimezone) + elif platform.system() == 'AIX': + AIXoslevel = int(platform.version() + platform.release()) + if AIXoslevel >= 61: + return super(Timezone, AIXTimezone).__new__(AIXTimezone) + else: + module.fail_json(msg='AIX os level must be >= 61 for timezone module (Target: %s).' % AIXoslevel) else: # Not supported yet return super(Timezone, Timezone).__new__(Timezone) @@ -768,6 +778,94 @@ class BSDTimezone(Timezone): self.module.fail_json(msg='%s is not a supported option on target platform' % key) +class AIXTimezone(Timezone): + """This is a Timezone manipulation class for AIX instances. + + It uses the C(chtz) utility to set the timezone, and + inspects C(/etc/environment) to determine the current timezone. + + While AIX time zones can be set using two formats (POSIX and + Olson) the prefered method is Olson. + See the following article for more information: + https://developer.ibm.com/articles/au-aix-posix/ + + NB: AIX needs to be rebooted in order for the change to be + activated. + """ + + def __init__(self, module): + super(AIXTimezone, self).__init__(module) + self.settimezone = self.module.get_bin_path('chtz', required=True) + + def __get_timezone(self): + """ Return the current value of TZ= in /etc/environment """ + try: + f = open('/etc/environment', 'r') + etcenvironment = f.read() + f.close() + except Exception: + self.module.fail_json(msg='Issue reading contents of /etc/environment') + + match = re.search(r'^TZ=(.*)$', etcenvironment, re.MULTILINE) + if match: + return match.group(1) + else: + return None + + def get(self, key, phase): + """Lookup the current timezone name in `/etc/environment`. If anything else + is requested, or if the TZ field is not set we fail. + """ + if key == 'name': + return self.__get_timezone() + else: + 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 chtz, an invalid timezone name + will be rejected and we have no further input validation to perform. + """ + if key == 'name': + # chtz seems to always return 0 on AIX 7.2, even for invalid timezone values. + # It will only return non-zero if the chtz command itself fails, it does not check for + # valid timezones. We need to perform a basic check to confirm that the timezone + # definition exists in /usr/share/lib/zoneinfo + # This does mean that we can only support Olson for now. The below commented out regex + # detects Olson date formats, so in the future we could detect Posix or Olson and + # act accordingly. + + # regex_olson = re.compile('^([a-z0-9_\-\+]+\/?)+$', re.IGNORECASE) + # if not regex_olson.match(value): + # msg = 'Supplied timezone (%s) does not appear to a be valid Olson string' % value + # self.module.fail_json(msg=msg) + + # First determine if the requested timezone is valid by looking in the zoneinfo + # directory. + zonefile = '/usr/share/lib/zoneinfo/' + value + try: + if not os.path.isfile(zonefile): + self.module.fail_json(msg='%s is not a recognized timezone.' % value) + except Exception: + self.module.fail_json(msg='Failed to check %s.' % zonefile) + + # Now set the TZ using chtz + cmd = 'chtz %s' % value + (rc, stdout, stderr) = self.module.run_command(cmd) + + if rc != 0: + self.module.fail_json(msg=stderr) + + # The best condition check we can do is to check the value of TZ after making the + # change. + TZ = self.__get_timezone() + if TZ != value: + msg = 'TZ value does not match post-change (Actual: %s, Expected: %s).' % (TZ, value) + self.module.fail_json(msg=msg) + + else: + self.module.fail_json(msg='%s is not a supported option on target platform' % key) + + def main(): # Construct 'module' and 'tz' module = AnsibleModule(