From 6c88b69d6fc32c1b1e8917fa71819cab588033cd Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Wed, 30 Dec 2020 11:25:32 +0530 Subject: [PATCH] launchd: Handle deprecated APIs in plistlib (#1554) Co-authored-by: Felix Fontein --- changelogs/fragments/1552_launchd.yml | 2 ++ plugins/modules/system/launchd.py | 48 +++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 changelogs/fragments/1552_launchd.yml diff --git a/changelogs/fragments/1552_launchd.yml b/changelogs/fragments/1552_launchd.yml new file mode 100644 index 0000000000..2e8df5471f --- /dev/null +++ b/changelogs/fragments/1552_launchd.yml @@ -0,0 +1,2 @@ +bugfixes: +- launchd - handle deprecated APIs like ``readPlist`` and ``writePlist`` in ``plistlib`` (https://github.com/ansible-collections/community.general/issues/1552). diff --git a/plugins/modules/system/launchd.py b/plugins/modules/system/launchd.py index d3d9fcb108..919d8d7bd2 100644 --- a/plugins/modules/system/launchd.py +++ b/plugins/modules/system/launchd.py @@ -114,6 +114,7 @@ from abc import ABCMeta, abstractmethod from time import sleep from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_native class ServiceState: @@ -142,6 +143,9 @@ class Plist: state, pid, dummy, dummy = LaunchCtlList(module, service).run() + # Check if readPlist is available or not + self.old_plistlib = hasattr(plistlib, 'readPlist') + self.__file = self.__find_service_plist(service) if self.__file is None: msg = 'Unable to infer the path of %s service plist file' % service @@ -150,7 +154,8 @@ class Plist: module.fail_json(msg=msg) self.__update(module) - def __find_service_plist(self, service_name): + @staticmethod + def __find_service_plist(service_name): """Finds the plist file associated with a service""" launchd_paths = [ @@ -176,9 +181,38 @@ class Plist: self.__handle_param_enabled(module) self.__handle_param_force_stop(module) + def __read_plist_file(self, module): + service_plist = {} + if self.old_plistlib: + return plistlib.readPlist(self.__file) + + # readPlist is deprecated in Python 3 and onwards + try: + with open(self.__file, 'rb') as plist_fp: + service_plist = plistlib.load(plist_fp) + except Exception as e: + module.fail_json(msg="Failed to read plist file " + "%s due to %s" % (self.__file, to_native(e))) + return service_plist + + def __write_plist_file(self, module, service_plist=None): + if not service_plist: + service_plist = {} + + if self.old_plistlib: + plistlib.writePlist(service_plist, self.__file) + return + # writePlist is deprecated in Python 3 and onwards + try: + with open(self.__file, 'wb') as plist_fp: + plistlib.dump(service_plist, plist_fp) + except Exception as e: + module.fail_json(msg="Failed to write to plist file " + " %s due to %s" % (self.__file, to_native(e))) + def __handle_param_enabled(self, module): if module.params['enabled'] is not None: - service_plist = plistlib.readPlist(self.__file) + service_plist = self.__read_plist_file(module) # Enable/disable service startup at boot if requested # Launchctl does not expose functionality to set the RunAtLoad @@ -191,12 +225,12 @@ class Plist: # Update the plist with one of the changes done. if not module.check_mode: - plistlib.writePlist(service_plist, self.__file) + self.__write_plist_file(module, service_plist) self.__changed = True def __handle_param_force_stop(self, module): if module.params['force_stop'] is not None: - service_plist = plistlib.readPlist(self.__file) + service_plist = self.__read_plist_file(module) # Set KeepAlive to false in case force_stop is defined to avoid # that the service gets restarted when stopping was requested. @@ -207,7 +241,7 @@ class Plist: # Update the plist with one of the changes done. if not module.check_mode: - plistlib.writePlist(service_plist, self.__file) + self.__write_plist_file(module, service_plist) self.__changed = True def is_changed(self): @@ -325,7 +359,7 @@ class LaunchCtlStart(LaunchCtlTask): def runCommand(self): state, dummy, dummy, dummy = self.get_state() - if state == ServiceState.STOPPED or state == ServiceState.LOADED: + if state in (ServiceState.STOPPED, ServiceState.LOADED): self.reload() self.start() elif state == ServiceState.STARTED: @@ -361,7 +395,7 @@ class LaunchCtlStop(LaunchCtlTask): if self._plist.is_changed(): self.reload() self.stop() - elif state == ServiceState.STARTED or state == ServiceState.LOADED: + elif state in (ServiceState.STARTED, ServiceState.LOADED): if self._plist.is_changed(): self.reload() self.stop()