Repository: Azure/WALinuxAgent
Pull request #2174 (2021-02-10 17:53:25)

Create module for systemd utilities

codecov[bot]: # [Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174?src=pr&el=h1) Report
> Merging [#2174](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174?src=pr&el=desc) (5227d8e) into [develop](https://codecov.io/gh/Azure/WALinuxAgent/commit/d33acb4537880ae3d9d016f3f865f8783db48f49?el=desc) (d33acb4) will **increase** coverage by `0.01%`.
> The diff coverage is `84.44%`.

[![Impacted file tree graph](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174/graphs/tree.svg?width=650&height=150&src=pr&token=ae90E2KYCw)](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174?src=pr&el=tree)

```diff
@@             Coverage Diff             @@
##           develop    #2174      +/-   ##
===========================================
+ Coverage    70.53%   70.54%   +0.01%     
===========================================
  Files           96       97       +1     
  Lines        13373    13380       +7     
  Branches      1883     1884       +1     
===========================================
+ Hits          9432     9439       +7     
  Misses        3533     3533              
  Partials       408      408              
```


| [Impacted Files](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [azurelinuxagent/common/cgroupapi.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9jZ3JvdXBhcGkucHk=) | `80.95% <ø> (-0.37%)` | :arrow_down: |
| [azurelinuxagent/common/persist\_firewall\_rules.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9wZXJzaXN0X2ZpcmV3YWxsX3J1bGVzLnB5) | `84.16% <66.66%> (-0.14%)` | :arrow_down: |
| [azurelinuxagent/common/cgroupconfigurator.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9jZ3JvdXBjb25maWd1cmF0b3IucHk=) | `67.09% <77.77%> (+0.21%)` | :arrow_up: |
| [azurelinuxagent/common/osutil/systemd.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9vc3V0aWwvc3lzdGVtZC5weQ==) | `90.47% <90.47%> (ø)` | |
| [azurelinuxagent/ga/collect\_logs.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL2NvbGxlY3RfbG9ncy5weQ==) | `87.60% <100.00%> (ø)` | |
| [azurelinuxagent/ga/update.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL3VwZGF0ZS5weQ==) | `88.45% <100.00%> (-0.02%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174?src=pr&el=footer). Last update [d33acb4...5227d8e](https://codecov.io/gh/Azure/WALinuxAgent/pull/2174?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/azurelinuxagent/common/cgroupapi.py b/azurelinuxagent/common/cgroupapi.py
index 0768daec..5cb334d8 100644
--- a/azurelinuxagent/common/cgroupapi.py
+++ b/azurelinuxagent/common/cgroupapi.py
@@ -37,7 +37,6 @@ from azurelinuxagent.common.version import get_distro
 
 CGROUPS_FILE_SYSTEM_ROOT = '/sys/fs/cgroup'
 CGROUP_CONTROLLERS = ["cpu", "memory"]
-SYSTEMD_RUN_PATH = "/run/systemd/system/"
 
 
 class SystemdRunError(CGroupsException):
@@ -73,14 +72,6 @@ class CGroupsApi(object):
         # Since '-' is used as a separator in systemd unit names, we replace it with '_' to prevent side-effects.
         return extension_name.replace('-', '_')
 
-    @staticmethod
-    def is_systemd():
-        """
-        Determine if systemd is managing system services; the implementation follows the same strategy as, for example,
-        sd_booted() in libsystemd, or /usr/sbin/service
-        """
-        return os.path.exists(SYSTEMD_RUN_PATH)
-
     @staticmethod
     def get_processes_in_cgroup(cgroup_path):
         with open(os.path.join(cgroup_path, "cgroup.procs"), "r") as cgroup_procs:
@@ -138,15 +129,6 @@ class SystemdCgroupsApi(CGroupsApi):
         self._systemd_run_commands = []
         self._systemd_run_commands_lock = threading.RLock()
 
-    @staticmethod
-    def get_systemd_version():
-        # the output is similar to
-        #    $ systemctl --version
-        #    systemd 245 (245.4-4ubuntu3)
-        #    +PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP etc
-        #
-        return shellutil.run_command(['systemctl', '--version'])
-
     def get_systemd_run_commands(self):
         """
         Returns a list of the systemd-run commands currently running (given as PIDs)
@@ -246,14 +228,6 @@ class SystemdCgroupsApi(CGroupsApi):
                 return mount_point, controllers
         return None, None
 
-    @staticmethod
-    def get_unit_property(unit_name, property_name):
-        output = shellutil.run_command(["systemctl", "show", unit_name, "--property", property_name])
-        match = re.match("[^=]+=(?P<value>.+)", output)
-        if match is None:
-            raise ValueError("Can't find property {0} of {1}", property_name, unit_name)  # pylint: disable=W0715
-        return match.group('value')
-
     def get_agent_unit_name(self):
         if self._agent_unit_name is None:
             self._agent_unit_name = get_osutil().get_service_name() + ".service"
diff --git a/azurelinuxagent/common/cgroupconfigurator.py b/azurelinuxagent/common/cgroupconfigurator.py
index a0202cf0..b22ae628 100644
--- a/azurelinuxagent/common/cgroupconfigurator.py
+++ b/azurelinuxagent/common/cgroupconfigurator.py
@@ -25,7 +25,7 @@ from azurelinuxagent.common.cgroupapi import CGroupsApi, SystemdCgroupsApi, Syst
 from azurelinuxagent.common.cgroupstelemetry import CGroupsTelemetry
 from azurelinuxagent.common.exception import ExtensionErrorCodes, CGroupsException
 from azurelinuxagent.common.future import ustr
-from azurelinuxagent.common.osutil import get_osutil
+from azurelinuxagent.common.osutil import get_osutil, systemd
 from azurelinuxagent.common.version import get_distro
 from azurelinuxagent.common.utils import shellutil, fileutil
 from azurelinuxagent.common.utils.extensionprocessutil import handle_process_completion
@@ -100,11 +100,11 @@ class CGroupConfigurator(object):
 
                 # check that systemd is detected correctly
                 self._cgroups_api = SystemdCgroupsApi()
-                if not self._cgroups_api.is_systemd():
+                if not systemd.is_systemd():
                     self.__log_cgroup_warning("systemd was not detected on {0}", get_distro())
                     return
 
-                self.__log_cgroup_info("systemd version: {0}", self._cgroups_api.get_systemd_version())
+                self.__log_cgroup_info("systemd version: {0}", systemd.get_version())
 
                 # This is temporarily disabled while we analyze telemetry. Likely it will be removed.
                 # self.__collect_azure_unit_telemetry()
@@ -114,7 +114,7 @@ class CGroupConfigurator(object):
                     return
 
                 agent_unit_name = self._cgroups_api.get_agent_unit_name()
-                agent_slice = self._cgroups_api.get_unit_property(agent_unit_name, "Slice")
+                agent_slice = systemd.get_unit_property(agent_unit_name, "Slice")
                 if agent_slice not in (_AZURE_SLICE, "system.slice"):
                     self.__log_cgroup_warning("The agent is within an unexpected slice: {0}", agent_slice)
                     return
@@ -163,7 +163,7 @@ class CGroupConfigurator(object):
             for unit_name, unit_description in azure_units:
                 unit_slice = "Unknown"
                 try:
-                    unit_slice = SystemdCgroupsApi.get_unit_property(unit_name, "Slice")
+                    unit_slice = systemd.get_unit_property(unit_name, "Slice")
                 except Exception as exception:
                     self.__log_cgroup_warning("Failed to query Slice for {0}: {1}", unit_name, ustr(exception))
 
@@ -184,14 +184,14 @@ class CGroupConfigurator(object):
             agent_unit_files = []
             agent_service_name = get_osutil().get_service_name()
             try:
-                fragment_path = SystemdCgroupsApi.get_unit_property(agent_service_name, "FragmentPath")
+                fragment_path = systemd.get_unit_property(agent_service_name, "FragmentPath")
                 if fragment_path != "/lib/systemd/system/{0}.service".format(agent_service_name):
                     agent_unit_files.append(fragment_path)
             except Exception as exception:
                 self.__log_cgroup_warning("Failed to query the agent's FragmentPath: {0}", ustr(exception))
 
             try:
-                drop_in_paths = SystemdCgroupsApi.get_unit_property(agent_service_name, "DropInPaths")
+                drop_in_paths = systemd.get_unit_property(agent_service_name, "DropInPaths")
                 for path in drop_in_paths.split():
                     agent_unit_files.append(path)
             except Exception as exception:
@@ -271,12 +271,14 @@ class CGroupConfigurator(object):
             # Older agents used to create this slice, but it was never used. Cleanup the file.
             self._cleanup_unit_file("/etc/systemd/system/system-walinuxagent.extensions.slice")
 
-            azure_slice = os.path.join("/lib/systemd/system", _AZURE_SLICE)
-            vmextensions_slice = os.path.join("/lib/systemd/system", _VMEXTENSIONS_SLICE)
-            agent_unit_file = "/lib/systemd/system/{0}".format(self._cgroups_api.get_agent_unit_name())
-            agent_drop_in_file_slice = "/lib/systemd/system/{0}.d/{1}".format(self._cgroups_api.get_agent_unit_name(), _AGENT_DROP_IN_FILE_SLICE)
-            agent_drop_in_file_cpu_accounting = "/lib/systemd/system/{0}.d/{1}".format(self._cgroups_api.get_agent_unit_name(), _AGENT_DROP_IN_FILE_CPU_ACCOUNTING)
-            agent_drop_in_file_cpu_quota = "/lib/systemd/system/{0}.d/{1}".format(self._cgroups_api.get_agent_unit_name(), _AGENT_DROP_IN_FILE_CPU_QUOTA)
+            systemd_unit_file_install_path = systemd.get_unit_file_install_path()
+            agent_unit_file_dropin_path = os.path.join(systemd_unit_file_install_path, "{0}.d".format(self._cgroups_api.get_agent_unit_name()))
+            azure_slice = os.path.join(systemd_unit_file_install_path, _AZURE_SLICE)
+            vmextensions_slice = os.path.join(systemd_unit_file_install_path, _VMEXTENSIONS_SLICE)
+            agent_unit_file = os.path.join(systemd_unit_file_install_path, self._cgroups_api.get_agent_unit_name())
+            agent_drop_in_file_slice = os.path.join(agent_unit_file_dropin_path, _AGENT_DROP_IN_FILE_SLICE)
+            agent_drop_in_file_cpu_accounting = os.path.join(agent_unit_file_dropin_path, _AGENT_DROP_IN_FILE_CPU_ACCOUNTING)
+            agent_drop_in_file_cpu_quota = os.path.join(agent_unit_file_dropin_path, _AGENT_DROP_IN_FILE_CPU_QUOTA)
             agent_unit_name = self._cgroups_api.get_agent_unit_name()
 
             files_to_create = []
@@ -322,7 +324,7 @@ class CGroupConfigurator(object):
                     for unit_file in files_to_create:
                         self._cleanup_unit_file(unit_file)
 
-            logger.info("Agent's CPUQuota: {0}",   self._cgroups_api.get_unit_property(agent_unit_name, "CPUQuotaPerSecUSec"))
+            logger.info("Agent's CPUQuota: {0}",  systemd.get_unit_property(agent_unit_name, "CPUQuotaPerSecUSec"))
 
         def _create_unit_file(self, path, contents):
             try:
@@ -353,7 +355,7 @@ class CGroupConfigurator(object):
                 self.__log_cgroup_warning("The agent's process is not within a CPU cgroup")
             else:
                 if cpu_cgroup_relative_path == expected_relative_path:
-                    cpu_accounting = self._cgroups_api.get_unit_property(agent_service_name, "CPUAccounting")
+                    cpu_accounting = systemd.get_unit_property(agent_service_name, "CPUAccounting")
                     self.__log_cgroup_info('CPUAccounting: {0}', cpu_accounting)
                 else:
                     cpu_cgroup_relative_path = None  # Set the path to None to prevent monitoring
@@ -366,7 +368,7 @@ class CGroupConfigurator(object):
                 self.__log_cgroup_warning("The agent's process is not within a memory cgroup")
             else:
                 if memory_cgroup_relative_path == expected_relative_path:
-                    memory_accounting = self._cgroups_api.get_unit_property(agent_service_name, "MemoryAccounting")
+                    memory_accounting = systemd.get_unit_property(agent_service_name, "MemoryAccounting")
                     self.__log_cgroup_info('MemoryAccounting: {0}', memory_accounting)
                 else:
                     memory_cgroup_relative_path = None  # Set the path to None to prevent monitoring
diff --git a/azurelinuxagent/common/osutil/systemd.py b/azurelinuxagent/common/osutil/systemd.py
new file mode 100644
index 00000000..fdc69aee
--- /dev/null
+++ b/azurelinuxagent/common/osutil/systemd.py
@@ -0,0 +1,61 @@
+#
+# Copyright 2018 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.6+ and Openssl 1.0+
+#
+import os
+import re
+
+from azurelinuxagent.common.osutil import get_osutil
+from azurelinuxagent.common.utils import shellutil
+
+
+def _get_osutil():
+    if _get_osutil.value is None:
+        _get_osutil.value = get_osutil()
+    return _get_osutil.value
+
+
+_get_osutil.value = None
+
+
+def is_systemd():
+    """
+    Determine if systemd is managing system services; the implementation follows the same strategy as, for example,
+    sd_booted() in libsystemd, or /usr/sbin/service
+    """
+    return os.path.exists("/run/systemd/system/")
+
+
+def get_version():
+    # the output is similar to
+    #    $ systemctl --version
+    #    systemd 245 (245.4-4ubuntu3)
+    #    +PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP etc
+    #
+    return shellutil.run_command(['systemctl', '--version'])
+
+
+def get_unit_file_install_path():
+    return _get_osutil().get_systemd_unit_file_install_path()
+
+
+def get_unit_property(unit_name, property_name):
+    output = shellutil.run_command(["systemctl", "show", unit_name, "--property", property_name])
+    match = re.match("[^=]+=(?P<value>.+)", output)
+    if match is None:
+        raise ValueError("Can't find property {0} of {1}", property_name, unit_name)  # pylint: disable=W0715
+    return match.group('value')
+
diff --git a/azurelinuxagent/common/persist_firewall_rules.py b/azurelinuxagent/common/persist_firewall_rules.py
index e75b627e..f008de23 100644
--- a/azurelinuxagent/common/persist_firewall_rules.py
+++ b/azurelinuxagent/common/persist_firewall_rules.py
@@ -19,10 +19,9 @@ import os
 import sys
 
 from azurelinuxagent.common import logger
-from azurelinuxagent.common.cgroupapi import CGroupsApi
 from azurelinuxagent.common.event import add_event, WALAEventOperation
 from azurelinuxagent.common.future import ustr
-from azurelinuxagent.common.osutil import get_osutil
+from azurelinuxagent.common.osutil import get_osutil, systemd
 from azurelinuxagent.common.utils import shellutil, fileutil
 from azurelinuxagent.common.utils.networkutil import AddFirewallRules
 from azurelinuxagent.common.utils.shellutil import CommandError
@@ -72,7 +71,7 @@ Environment="EGG={egg_path}" "DST_IP={wire_ip}" "UID={user_id}" "WAIT={wait}"
         """
         osutil = get_osutil()
         self._network_setup_service_name = self._AGENT_NETWORK_SETUP_NAME_FORMAT.format(osutil.get_service_name())
-        self._is_systemd = CGroupsApi.is_systemd()
+        self._is_systemd = systemd.is_systemd()
         self._systemd_file_path = osutil.get_systemd_unit_file_install_path()
         self._dst_ip = dst_ip
         self._uid = uid
@@ -104,7 +103,7 @@ Environment="EGG={egg_path}" "DST_IP={wire_ip}" "UID={user_id}" "WAIT={wait}"
         logger.info(
             "Firewalld service not running/unavailable, trying to set up {0}".format(self._network_setup_service_name))
 
-        if not CGroupsApi.is_systemd():
+        if not systemd.is_systemd():
             raise Exception("Did not detect Systemd, unable to set {0}".format(self._network_setup_service_name))
 
         self._setup_network_setup_service()
diff --git a/azurelinuxagent/ga/collect_logs.py b/azurelinuxagent/ga/collect_logs.py
index f52aae17..b2fb5911 100644
--- a/azurelinuxagent/ga/collect_logs.py
+++ b/azurelinuxagent/ga/collect_logs.py
@@ -25,11 +25,11 @@ import time
 
 import azurelinuxagent.common.conf as conf
 from azurelinuxagent.common import logger
-from azurelinuxagent.common.cgroupapi import CGroupsApi
 from azurelinuxagent.common.event import elapsed_milliseconds, add_event, WALAEventOperation
 from azurelinuxagent.common.future import ustr
 from azurelinuxagent.common.interfaces import ThreadHandlerInterface
 from azurelinuxagent.common.logcollector import COMPRESSED_ARCHIVE_PATH
+from azurelinuxagent.common.osutil import systemd
 from azurelinuxagent.common.protocol.util import get_protocol_util
 from azurelinuxagent.common.utils import shellutil
 from azurelinuxagent.common.utils.shellutil import CommandError
@@ -46,7 +46,7 @@ def is_log_collection_allowed():
     # 2) The system must be using systemd to manage services. Needed for resource limiting of the log collection.
     # 3) The python version must be greater than 2.6 in order to support the ZipFile library used when collecting.
     conf_enabled = conf.get_collect_logs()
-    systemd_present = CGroupsApi.is_systemd()
+    systemd_present = systemd.is_systemd()
     supported_python = PY_VERSION_MINOR >= 7 if PY_VERSION_MAJOR == 2 else PY_VERSION_MAJOR == 3
     is_allowed = conf_enabled and systemd_present and supported_python
 
diff --git a/azurelinuxagent/ga/update.py b/azurelinuxagent/ga/update.py
index 2c813fb3..c05f6d7c 100644
--- a/azurelinuxagent/ga/update.py
+++ b/azurelinuxagent/ga/update.py
@@ -39,14 +39,13 @@ import azurelinuxagent.common.utils.fileutil as fileutil
 import azurelinuxagent.common.utils.restutil as restutil
 import azurelinuxagent.common.utils.textutil as textutil
 from azurelinuxagent.common.persist_firewall_rules import PersistFirewallRulesHandler
-from azurelinuxagent.common.cgroupapi import CGroupsApi
 from azurelinuxagent.common.cgroupconfigurator import CGroupConfigurator
 
 from azurelinuxagent.common.event import add_event, initialize_event_logger_vminfo_common_parameters, \
     elapsed_milliseconds, WALAEventOperation, EVENTS_DIRECTORY
 from azurelinuxagent.common.exception import ResourceGoneError, UpdateError
 from azurelinuxagent.common.future import ustr
-from azurelinuxagent.common.osutil import get_osutil
+from azurelinuxagent.common.osutil import get_osutil, systemd
 from azurelinuxagent.common.protocol.util import get_protocol_util
 from azurelinuxagent.common.protocol.hostplugin import HostPluginProtocol
 from azurelinuxagent.common.utils.flexible_version import FlexibleVersion
@@ -274,7 +273,7 @@ class UpdateHandler(object):
                     util_name=type(self.osutil).__name__,
                     service_name=self.osutil.service_name,
                     py_major=PY_VERSION_MAJOR, py_minor=PY_VERSION_MINOR,
-                    py_micro=PY_VERSION_MICRO, systemd=CGroupsApi.is_systemd(),
+                    py_micro=PY_VERSION_MICRO, systemd=systemd.is_systemd(),
                     lis_ver=get_lis_version(), has_logrotate=has_logrotate()
             )
 


Test patch:
diff --git a/tests/common/mock_cgroup_environment.py b/tests/common/mock_cgroup_environment.py
index ab4d36eb..e1215510 100644
--- a/tests/common/mock_cgroup_environment.py
+++ b/tests/common/mock_cgroup_environment.py
@@ -100,6 +100,6 @@ def mock_cgroup_environment(tmp_dir):
     ]
 
     with patch('azurelinuxagent.common.cgroupapi.CGroupsApi.cgroups_supported', return_value=True):
-        with patch('azurelinuxagent.common.cgroupapi.CGroupsApi.is_systemd', return_value=True):
+        with patch('azurelinuxagent.common.osutil.systemd.is_systemd', return_value=True):
             with MockEnvironment(tmp_dir, commands=_MOCKED_COMMANDS, paths=_MOCKED_PATHS, files=_MOCKED_FILES, data_files=data_files) as mock:
                 yield mock
diff --git a/tests/common/test_cgroupapi.py b/tests/common/test_cgroupapi.py
index 43d9009f..854f15d9 100644
--- a/tests/common/test_cgroupapi.py
+++ b/tests/common/test_cgroupapi.py
@@ -22,11 +22,12 @@ import re
 import subprocess
 import tempfile
 
-from azurelinuxagent.common.cgroupapi import CGroupsApi, SystemdCgroupsApi, SYSTEMD_RUN_PATH
+from azurelinuxagent.common.cgroupapi import CGroupsApi, SystemdCgroupsApi
 from azurelinuxagent.common.cgroupstelemetry import CGroupsTelemetry
+from azurelinuxagent.common.osutil import systemd
 from azurelinuxagent.common.utils import fileutil
 from tests.common.mock_cgroup_environment import mock_cgroup_environment
-from tests.tools import AgentTestCase, patch, skip_if_predicate_false, is_systemd_present, mock_sleep
+from tests.tools import AgentTestCase, patch, mock_sleep
 from tests.utils.cgroups_tools import CGroupsTools
 
 
@@ -68,48 +69,11 @@ class CGroupsApiTestCase(_MockedFileSystemTestCase):
             with patch("azurelinuxagent.common.cgroupapi.get_distro", return_value=distro):
                 self.assertEqual(CGroupsApi.cgroups_supported(), supported, "cgroups_supported() failed on {0}".format(distro))
 
-    def test_is_systemd_should_return_true_when_systemd_manages_current_process(self):
-        path_exists = os.path.exists
-
-        def mock_path_exists(path):
-            if path == SYSTEMD_RUN_PATH:
-                mock_path_exists.path_tested = True
-                return True
-            return path_exists(path)
-
-        mock_path_exists.path_tested = False
-
-        with patch("azurelinuxagent.common.cgroupapi.os.path.exists", mock_path_exists):
-            is_systemd = CGroupsApi.is_systemd()
-
-        self.assertTrue(is_systemd)
-
-        self.assertTrue(mock_path_exists.path_tested, 'The expected path was not tested; the implementation of CGroupsApi._is_systemd() may have changed.')
-
-    def test_is_systemd_should_return_false_when_systemd_does_not_manage_current_process(self):
-        path_exists = os.path.exists
-
-        def mock_path_exists(path):
-            if path == SYSTEMD_RUN_PATH:
-                mock_path_exists.path_tested = True
-                return False
-            return path_exists(path)
-
-        mock_path_exists.path_tested = False
-
-        with patch("azurelinuxagent.common.cgroupapi.os.path.exists", mock_path_exists):
-            is_systemd = CGroupsApi.is_systemd()
-
-        self.assertFalse(is_systemd)
-
-        self.assertTrue(mock_path_exists.path_tested, 'The expected path was not tested; the implementation of CGroupsApi._is_systemd() may have changed.')
-
-
-@skip_if_predicate_false(is_systemd_present, "Systemd cgroups API doesn't manage cgroups on systems not using systemd.")
+                
 class SystemdCgroupsApiTestCase(AgentTestCase):
     def test_get_systemd_version_should_return_a_version_number(self):
         with mock_cgroup_environment(self.tmp_dir):
-            version_info = SystemdCgroupsApi.get_systemd_version()
+            version_info = systemd.get_version()
             found = re.search(r"systemd \d+", version_info) is not None
             self.assertTrue(found, "Could not determine the systemd version: {0}".format(version_info))
 
@@ -135,7 +99,7 @@ class SystemdCgroupsApiTestCase(AgentTestCase):
 
     def test_get_unit_property_should_return_the_value_of_the_given_property(self):
         with mock_cgroup_environment(self.tmp_dir):
-            cpu_accounting = SystemdCgroupsApi.get_unit_property("walinuxagent.service", "CPUAccounting")
+            cpu_accounting = systemd.get_unit_property("walinuxagent.service", "CPUAccounting")
 
             self.assertEqual(cpu_accounting, "no", "Property {0} of {1} is incorrect".format("CPUAccounting", "walinuxagent.service"))
 
diff --git a/tests/common/test_persist_firewall_rules.py b/tests/common/test_persist_firewall_rules.py
index 93c8cb0a..e02e8d22 100644
--- a/tests/common/test_persist_firewall_rules.py
+++ b/tests/common/test_persist_firewall_rules.py
@@ -87,7 +87,7 @@ class TestPersistFirewallRulesHandler(AgentTestCase):
         with patch("azurelinuxagent.common.persist_firewall_rules.fileutil.mkdir",
                    side_effect=lambda path, **mode: orig_mkdir(path)):
             with patch("azurelinuxagent.common.persist_firewall_rules.get_osutil", return_value=osutil):
-                with patch('azurelinuxagent.common.cgroupapi.CGroupsApi.is_systemd', return_value=systemd):
+                with patch('azurelinuxagent.common.osutil.systemd.is_systemd', return_value=systemd):
                     with patch("azurelinuxagent.common.utils.shellutil.subprocess.Popen", side_effect=self.__mock_popen):
                         yield PersistFirewallRulesHandler(self.__test_dst_ip, self.__test_uid)
 
diff --git a/tests/ga/test_collect_logs.py b/tests/ga/test_collect_logs.py
index 70fec7c8..45e13b48 100644
--- a/tests/ga/test_collect_logs.py
+++ b/tests/ga/test_collect_logs.py
@@ -18,7 +18,6 @@ import contextlib
 import os
 
 from azurelinuxagent.common import logger
-from azurelinuxagent.common.cgroupapi import SYSTEMD_RUN_PATH
 from azurelinuxagent.common.logger import Logger
 from azurelinuxagent.common.protocol.util import ProtocolUtil
 from azurelinuxagent.ga.collect_logs import get_collect_logs_handler, is_log_collection_allowed
@@ -41,21 +40,13 @@ def _create_collect_logs_handler(iterations=1, systemd_present=True):
         * run_and_wait() - invokes run() and wait() on the CollectLogsHandler
 
     """
-
-    original_file_exists = os.path.exists
-
-    def mock_file_exists(filepath):
-        if filepath == SYSTEMD_RUN_PATH:
-            return systemd_present
-        return original_file_exists(filepath)
-
     with mock_wire_protocol(DATA_FILE) as protocol:
         protocol_util = MagicMock()
         protocol_util.get_protocol = Mock(return_value=protocol)
         with patch("azurelinuxagent.ga.collect_logs.get_protocol_util", return_value=protocol_util):
             with patch("azurelinuxagent.ga.collect_logs.CollectLogsHandler.stopped", side_effect=[False] * iterations + [True]):
                 with patch("time.sleep"):
-                    with patch("azurelinuxagent.ga.collect_logs.os.path.exists", side_effect=mock_file_exists):
+                    with patch("azurelinuxagent.common.osutil.systemd.is_systemd", return_value=systemd_present):
                         with patch("azurelinuxagent.ga.collect_logs.conf.get_collect_logs", return_value=True):
                             def run_and_wait():
                                 collect_logs_handler.run()
diff --git a/tests/tools.py b/tests/tools.py
index 6b4c303c..c9386324 100644
--- a/tests/tools.py
+++ b/tests/tools.py
@@ -35,7 +35,6 @@ import azurelinuxagent.common.conf as conf
 import azurelinuxagent.common.event as event
 import azurelinuxagent.common.logger as logger
 from azurelinuxagent.common.future import range  # pylint: disable=redefined-builtin
-from azurelinuxagent.common.cgroupapi import SYSTEMD_RUN_PATH
 from azurelinuxagent.common.utils import fileutil
 from azurelinuxagent.common.version import PY_VERSION_MAJOR
 
@@ -118,10 +117,6 @@ def running_under_travis():
     return 'TRAVIS' in os.environ and os.environ['TRAVIS'] == 'true'
 
 
-def is_systemd_present():
-    return os.path.exists(SYSTEMD_RUN_PATH)
-
-
 def i_am_root():
     return os.geteuid() == 0