Repository: Azure/WALinuxAgent
Pull request #2111 (2020-12-16 01:12:56)

Create root slice for Azure and root slice for extensions
<!-- DO NOT DELETE THIS TEMPLATE -->

## Description

This is the final cgroup hierarchy we want to achieve:

```
├─user.slice
...
├─system.slice
...
└─azure.slice
  └─walinuxagent.service
  └─azure-vmextensions.slice
    └─some_extension.slice
```

This PR is the first step in the process, ensuring the following:
1) On service startup, we create `azure.slice` in the root cgroup node (on par with `system.slice` and `user.slice`) which will host all agent and extensions-related processes,
2) On service startup, we create `azure-vmextensions.slice` under `azure.slice` which will host all extensions-related processes.

<!--
Please add an informative description that covers that changes made by the pull request. 
This checklist is used to make sure that common issues in a pull request are addressed.
This will expedite the process of getting your pull request merged and avoid extra work on your part to fix issues discovered during the review process.
-->

---

### PR information
- [ ] The title of the PR is clear and informative.
- [ ] There are a small number of commits, each of which has an informative message. This means that previously merged commits do not appear in the history of the PR. For information on cleaning up the commits in your pull request, [see this page](https://github.com/Azure/azure-powershell/blob/master/documentation/development-docs/cleaning-up-commits.md).
- [ ] Except for special cases involving multiple contributors, the PR is started from a fork of the main repository, not a branch.
- [ ] If applicable, the PR references the bug/issue that it fixes in the description.
- [ ] New Unit tests were added for the changes made and Travis.CI is passing.

### Quality of Code and Contribution Guidelines
- [ ] I have read the [contribution guidelines](https://github.com/Azure/WALinuxAgent/blob/master/.github/CONTRIBUTING.md).

codecov[bot]: # [Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2111?src=pr&el=h1) Report
> Merging [#2111](https://codecov.io/gh/Azure/WALinuxAgent/pull/2111?src=pr&el=desc) (e736c90) into [develop](https://codecov.io/gh/Azure/WALinuxAgent/commit/fd15961b13322a6d6b86d28e1a1c8be26ca6e078?el=desc) (fd15961) will **decrease** coverage by `0.03%`.
> The diff coverage is `88.57%`.

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

```diff
@@             Coverage Diff             @@
##           develop    #2111      +/-   ##
===========================================
- Coverage    70.69%   70.65%   -0.04%     
===========================================
  Files           95       95              
  Lines        13076    13100      +24     
  Branches      1854     1855       +1     
===========================================
+ Hits          9244     9256      +12     
- Misses        3423     3433      +10     
- Partials       409      411       +2     
```


| [Impacted Files](https://codecov.io/gh/Azure/WALinuxAgent/pull/2111?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [azurelinuxagent/common/cgroupapi.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2111/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9jZ3JvdXBhcGkucHk=) | `77.34% <84.21%> (-1.97%)` | :arrow_down: |
| [azurelinuxagent/common/cgroupconfigurator.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2111/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9jZ3JvdXBjb25maWd1cmF0b3IucHk=) | `80.70% <92.85%> (+4.75%)` | :arrow_up: |
| [azurelinuxagent/ga/update.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2111/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL3VwZGF0ZS5weQ==) | `88.24% <100.00%> (+0.03%)` | :arrow_up: |
| [azurelinuxagent/ga/collect\_telemetry\_events.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/2111/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL2NvbGxlY3RfdGVsZW1ldHJ5X2V2ZW50cy5weQ==) | `89.65% <0.00%> (-2.42%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/2111?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/2111?src=pr&el=footer). Last update [fd15961...e736c90](https://codecov.io/gh/Azure/WALinuxAgent/pull/2111?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 90c92d16..50f92c42 100644
--- a/azurelinuxagent/common/cgroupapi.py
+++ b/azurelinuxagent/common/cgroupapi.py
@@ -39,7 +39,8 @@ from azurelinuxagent.common.version import get_distro
 CGROUPS_FILE_SYSTEM_ROOT = '/sys/fs/cgroup'
 CGROUP_CONTROLLERS = ["cpu", "memory"]
 VM_AGENT_CGROUP_NAME = "walinuxagent.service"
-EXTENSIONS_ROOT_CGROUP_NAME = "walinuxagent.extensions"
+AZURE_CGROUP_NAME = "azure"
+EXTENSIONS_CGROUP_NAME = "vmextensions"
 UNIT_FILES_FILE_SYSTEM_PATH = "/etc/systemd/system"
 SYSTEMD_RUN_PATH = "/run/systemd/system/"
 
@@ -48,9 +49,6 @@ class CGroupsApi(object):
     """
     Interface for the cgroups API
     """
-    def create_extension_cgroups_root(self):
-        raise NotImplementedError()
-
     def create_extension_cgroups(self, extension_name):
         raise NotImplementedError()
 
@@ -203,7 +201,7 @@ class FileSystemCgroupsApi(CGroupsApi):
 
     @staticmethod
     def _get_extension_cgroups_root_path(controller):
-        return os.path.join(CGROUPS_FILE_SYSTEM_ROOT, controller, EXTENSIONS_ROOT_CGROUP_NAME)
+        return os.path.join(CGROUPS_FILE_SYSTEM_ROOT, controller, EXTENSIONS_CGROUP_NAME)
 
     def _get_extension_cgroup_path(self, controller, extension_name):
         extensions_root = self._get_extension_cgroups_root_path(controller)
@@ -317,7 +315,7 @@ class FileSystemCgroupsApi(CGroupsApi):
 
         return cgroups
 
-    def create_extension_cgroups_root(self):
+    def create_extensions_slice(self):
         """
         Creates the directory within the cgroups file system that will contain the cgroups for the extensions.
         """
@@ -455,6 +453,14 @@ class SystemdCgroupsApi(CGroupsApi):
         #
         return shellutil.run_command(['systemctl', '--version'])
 
+    @staticmethod
+    def get_azure_slice():
+        return os.path.join(UNIT_FILES_FILE_SYSTEM_PATH, AZURE_CGROUP_NAME, ".slice")
+
+    @staticmethod
+    def get_extensions_slice():
+        return os.path.join(UNIT_FILES_FILE_SYSTEM_PATH, EXTENSIONS_CGROUP_NAME, ".slice")
+
     def get_cgroup_mount_points(self):
         """
         Returns a tuple with the mount points for the cpu and memory controllers; the values can be None
@@ -556,50 +562,49 @@ class SystemdCgroupsApi(CGroupsApi):
         return match.group('value')
 
     @staticmethod
-    def create_and_start_unit(unit_filename, unit_contents):
+    def create_unit_file(unit_filename, unit_contents):
         try:
             unit_path = os.path.join(UNIT_FILES_FILE_SYSTEM_PATH, unit_filename)
             fileutil.write_file(unit_path, unit_contents)
-            shellutil.run_command(["systemctl", "daemon-reload"])
-            shellutil.run_command(["systemctl", "start", unit_filename])
-        except Exception as e:  # pylint: disable=C0103
-            raise CGroupsException("Failed to create and start {0}. Error: {1}".format(unit_filename, ustr(e)))
+        except Exception as error:
+            raise CGroupsException("Failed to create and start {0}. Error: {1}".format(unit_filename, ustr(error)))
 
     @staticmethod
-    def _get_extensions_slice_root_name():
-        return "system-{0}.slice".format(EXTENSIONS_ROOT_CGROUP_NAME)
+    def _get_azure_slice_name():
+        return "{0}.slice".format(AZURE_CGROUP_NAME)
+
+    @staticmethod
+    def _get_extensions_root_slice_name():
+        return "{0}-{1}.slice".format(AZURE_CGROUP_NAME, EXTENSIONS_CGROUP_NAME)
 
     def _get_extension_slice_name(self, extension_name):
-        return "system-{0}-{1}.slice".format(EXTENSIONS_ROOT_CGROUP_NAME, self._get_extension_cgroup_name(extension_name))
-
-    def create_extension_cgroups_root(self):
-        unit_contents = """
-[Unit]
-Description=Slice for walinuxagent extensions
-DefaultDependencies=no
-Before=slices.target
-Requires=system.slice
-After=system.slice"""
-        unit_filename = self._get_extensions_slice_root_name()
-        self.create_and_start_unit(unit_filename, unit_contents)
-        logger.info("Created slice for walinuxagent extensions {0}".format(unit_filename))
+        return "{0}-{1}-{2}.slice".format(AZURE_CGROUP_NAME, EXTENSIONS_CGROUP_NAME, self._get_extension_cgroup_name(extension_name))
+
+    def create_azure_slice(self):
+        unit_contents = """[Unit]
+Description=Slice for Azure VM Agent and Extensions"""
+        unit_filename = self._get_azure_slice_name()
+        self.create_unit_file(unit_filename, unit_contents)
+
+        message = "Created root slice for Azure VM Agent and Extensions {0}".format(unit_filename)
+        add_event(op=WALAEventOperation.CGroupsInitialize, is_success=True, log_event=False, message=message)
+        logger.info(message)
+
+    def create_extensions_slice(self):
+        unit_contents = """[Unit]
+Description=Slice for Azure VM Extensions"""
+        unit_filename = self._get_extensions_root_slice_name()
+
+        self.create_unit_file(unit_filename, unit_contents)
+
+        message = "Created root slice for Azure VM Extensions {0}".format(unit_filename)
+        add_event(op=WALAEventOperation.CGroupsInitialize, is_success=True, log_event=False, message=message)
+        logger.info(message)
 
     def create_extension_cgroups(self, extension_name):
         # TODO: The slice created by this function is not used currently. We need to create the extension scopes within
         #  this slice and use the slice to monitor the cgroups. Also see comment in get_extension_cgroups.
-        # the slice.
-        unit_contents = """
-[Unit]
-Description=Slice for extension {0}
-DefaultDependencies=no
-Before=slices.target
-Requires=system-{1}.slice
-After=system-{1}.slice""".format(extension_name, EXTENSIONS_ROOT_CGROUP_NAME)
-        unit_filename = self._get_extension_slice_name(extension_name)
-        self.create_and_start_unit(unit_filename, unit_contents)
-        logger.info("Created slice for {0}".format(unit_filename))
-
-        return self.get_extension_cgroups(extension_name)
+        pass
 
     def remove_extension_cgroups(self, extension_name):
         # For transient units, cgroups are released automatically when the unit stops, so it is sufficient
diff --git a/azurelinuxagent/common/cgroupconfigurator.py b/azurelinuxagent/common/cgroupconfigurator.py
index 45205174..ec5d3dea 100644
--- a/azurelinuxagent/common/cgroupconfigurator.py
+++ b/azurelinuxagent/common/cgroupconfigurator.py
@@ -1,3 +1,4 @@
+# -*- encoding: utf-8 -*-
 # Copyright 2018 Microsoft Corporation
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -193,6 +194,28 @@ class CGroupConfigurator(object):
             self._cgroups_enabled = False
             CGroupsTelemetry.reset()
 
+        def create_slices(self):
+            if not self.enabled():
+                return
+
+            # Create root slices for agent and agent and extensions for systemd-managed distros.
+            # The hierarchy is as follows:
+            # ├─user.slice
+            # ...
+            # ├─system.slice
+            # ...
+            # └─azure.slice
+            #   └─azure-vmextensions.slice
+
+            # Both methods will log to local log and emit telemetry.
+            # The slices will be created if they don't previously exist.
+
+            if not os.path.exists(SystemdCgroupsApi.get_azure_slice()):
+                self.create_azure_slice()
+
+            if not os.path.exists(SystemdCgroupsApi.get_extensions_slice()):
+                self.create_extensions_slice()
+
         def _invoke_cgroup_operation(self, operation, error_message, on_error=None):
             """
             Ensures the given operation is invoked only if cgroups are enabled and traps any errors on the operation.
@@ -210,14 +233,33 @@ class CGroupConfigurator(object):
                     except Exception as exception:
                         logger.warn("CGroupConfigurator._invoke_cgroup_operation: {0}".format(ustr(exception)))
 
-        def create_extension_cgroups_root(self):
+        def create_azure_slice(self):
+            """"
+            Creates the container (cgroup) that includes the cgroups for anything related to the Agent and VM extensions.
+            """
+            def __impl():
+                self._cgroups_api.create_azure_slice()  # pylint: disable=no-member
+
+            error_message = "Failed to create a root cgroup for agent and extensions; resource usage for agent and extensions will not be tracked."
+
+            def __on_error(exception):
+                add_event(op=WALAEventOperation.CGroupsInitialize, message="{0} Error: {1}".format(error_message, ustr(exception)))
+
+            self._invoke_cgroup_operation(__impl, error_message, on_error=__on_error)
+
+        def create_extensions_slice(self):
             """
             Creates the container (directory/cgroup) that includes the cgroups for all extensions (/sys/fs/cgroup/*/walinuxagent.extensions)
             """
             def __impl():
-                self._cgroups_api.create_extension_cgroups_root()
+                self._cgroups_api.create_extensions_slice()
+
+            error_message = "Failed to create a root cgroup for extensions; resource usage for extensions will not be tracked."
+
+            def __on_error(exception):
+                add_event(op=WALAEventOperation.CGroupsInitialize, message="{0} Error: {1}".format(error_message, ustr(exception)))
 
-            self._invoke_cgroup_operation(__impl, "Failed to create a root cgroup for extensions; resource usage for extensions will not be tracked.")
+            self._invoke_cgroup_operation(__impl, error_message, on_error=__on_error)
 
         def create_extension_cgroups(self, name):
             """
diff --git a/azurelinuxagent/ga/update.py b/azurelinuxagent/ga/update.py
index 72b46d13..370c3b96 100644
--- a/azurelinuxagent/ga/update.py
+++ b/azurelinuxagent/ga/update.py
@@ -525,6 +525,7 @@ class UpdateHandler(object):  # pylint: disable=R0902
     def _ensure_cgroups_initialized(self):
         configurator = CGroupConfigurator.get_instance()
         configurator.initialize()
+        configurator.create_slices()
 
     def _evaluate_agent_health(self, latest_agent):
         """
diff --git a/init/ubuntu/azure-vmextensions.slice b/init/ubuntu/azure-vmextensions.slice
new file mode 100644
index 00000000..f614e608
--- /dev/null
+++ b/init/ubuntu/azure-vmextensions.slice
@@ -0,0 +1,2 @@
+[Unit]
+Description=Slice for Azure VM Extensions
diff --git a/init/ubuntu/azure.slice b/init/ubuntu/azure.slice
new file mode 100644
index 00000000..674a9a51
--- /dev/null
+++ b/init/ubuntu/azure.slice
@@ -0,0 +1,2 @@
+[Unit]
+Description=Slice for Azure VM Agent and Extensions
diff --git a/setup.py b/setup.py
index 56561f63..0ad09521 100755
--- a/setup.py
+++ b/setup.py
@@ -68,6 +68,9 @@ def set_systemd_files(data_files, dest="/lib/systemd/system", src=None):
         src = ["init/waagent.service"]
     data_files.append((dest, src))
 
+    set_files(data_files, dest="/etc/systemd/system", src=["init/ubuntu/azure.slice",
+                                                           "init/ubuntu/azure-vmextensions.slice"])
+
 
 def set_freebsd_rc_files(data_files, dest="/etc/rc.d/", src=None):
     if src is None:
@@ -159,7 +162,7 @@ def get_data_files(name, version, fullname):  # pylint: disable=R0912
         if fullname == 'SUSE Linux Enterprise Server' and \
                 version.startswith('11') or \
                 fullname == 'openSUSE' and version.startswith(
-                    '13.1'): 
+                    '13.1'):
             set_sysv_files(data_files, dest='/etc/init.d',
                            src=["init/suse/waagent"])
         else:
@@ -193,7 +196,7 @@ def get_data_files(name, version, fullname):  # pylint: disable=R0912
         set_bin_files(data_files)
         set_conf_files(data_files)
         set_logrotate_files(data_files)
-        set_sysv_files(data_files, dest='/etc/init.d', src=["init/openwrt/waagent"])  
+        set_sysv_files(data_files, dest='/etc/init.d', src=["init/openwrt/waagent"])
     else:
         # Use default setting
         set_bin_files(data_files)


Test patch:
diff --git a/tests/common/mock_cgroup_commands.py b/tests/common/mock_cgroup_commands.py
index 09b9334b..a7622680 100644
--- a/tests/common/mock_cgroup_commands.py
+++ b/tests/common/mock_cgroup_commands.py
@@ -74,10 +74,12 @@ __DEFAULT_FILES = [
     (r"^/sys/fs/cgroup/unified/cgroup.controllers$", os.path.join(data_dir, 'cgroups', 'sys_fs_cgroup_unified_cgroup.controllers')),
 ]
 
+
 @contextlib.contextmanager
 def mock_cgroup_commands():
     original_popen = subprocess.Popen
     original_read_file = fileutil.read_file
+    original_write_file = fileutil.write_file
     original_path_exists = os.path.exists
 
     def add_file(pattern, file_path):
@@ -107,6 +109,13 @@ def mock_cgroup_commands():
                 filepath = item[1]
         return original_read_file(filepath, **kwargs)
 
+    def mock_write_file(filepath, content, **kwargs):
+        for item in patcher.files:
+            match = re.match(item[0], filepath)
+            if match is not None:
+                filepath = item[1]
+        return original_write_file(filepath, content, **kwargs)
+
     def mock_path_exists(path):
         for item in patcher.files:
             match = re.match(item[0], path)
@@ -117,11 +126,12 @@ def mock_cgroup_commands():
     with patch("azurelinuxagent.common.cgroupapi.subprocess.Popen", side_effect=mock_popen) as patcher:
         with patch("azurelinuxagent.common.cgroupapi.os.path.exists", side_effect=mock_path_exists):
             with patch("azurelinuxagent.common.cgroupapi.fileutil.read_file", side_effect=mock_read_file):
-                with patch('azurelinuxagent.common.cgroupapi.CGroupsApi.cgroups_supported', return_value=True):
-                    with patch('azurelinuxagent.common.cgroupapi.CGroupsApi.is_systemd', return_value=True):
-                        patcher.commands = __DEFAULT_COMMANDS[:]
-                        patcher.files = __DEFAULT_FILES[:]
-                        patcher.add_file = add_file
-                        patcher.add_command = add_command
-                        yield patcher
+                with patch("azurelinuxagent.common.cgroupapi.fileutil.write_file", side_effect=mock_write_file):
+                    with patch('azurelinuxagent.common.cgroupapi.CGroupsApi.cgroups_supported', return_value=True):
+                        with patch('azurelinuxagent.common.cgroupapi.CGroupsApi.is_systemd', return_value=True):
+                            patcher.commands = __DEFAULT_COMMANDS[:]
+                            patcher.files = __DEFAULT_FILES[:]
+                            patcher.add_file = add_file
+                            patcher.add_command = add_command
+                            yield patcher
 
diff --git a/tests/common/test_cgroupapi.py b/tests/common/test_cgroupapi.py
index 1b81f012..80cf07bf 100644
--- a/tests/common/test_cgroupapi.py
+++ b/tests/common/test_cgroupapi.py
@@ -373,21 +373,21 @@ class FileSystemCgroupsApiTestCase(_MockedFileSystemTestCase):
         assert_cgroup_created("memory")
 
     def test_create_extension_cgroups_root_should_create_root_directory_for_extensions(self):
-        FileSystemCgroupsApi().create_extension_cgroups_root()
+        FileSystemCgroupsApi().create_extensions_slice()
 
-        cpu_cgroup = os.path.join(self.cgroups_file_system_root, "cpu", "walinuxagent.extensions")
+        cpu_cgroup = os.path.join(self.cgroups_file_system_root, "cpu", "vmextensions")
         self.assertTrue(os.path.exists(cpu_cgroup))
 
-        memory_cgroup = os.path.join(self.cgroups_file_system_root, "memory", "walinuxagent.extensions")
+        memory_cgroup = os.path.join(self.cgroups_file_system_root, "memory", "vmextensions")
         self.assertTrue(os.path.exists(memory_cgroup))
 
     def test_create_extension_cgroups_should_create_cgroups_on_all_controllers(self):
         api = FileSystemCgroupsApi()
-        api.create_extension_cgroups_root()
+        api.create_extensions_slice()
         extension_cgroups = api.create_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")
 
         def assert_cgroup_created(controller):
-            cgroup_path = os.path.join(self.cgroups_file_system_root, controller, "walinuxagent.extensions",
+            cgroup_path = os.path.join(self.cgroups_file_system_root, controller, "vmextensions",
                                        "Microsoft.Compute.TestExtension_1.2.3")
 
             self.assertTrue(any(cgroups.path == cgroup_path for cgroups in extension_cgroups))
@@ -398,7 +398,7 @@ class FileSystemCgroupsApiTestCase(_MockedFileSystemTestCase):
 
     def test_remove_extension_cgroups_should_remove_all_cgroups(self):
         api = FileSystemCgroupsApi()
-        api.create_extension_cgroups_root()
+        api.create_extensions_slice()
         extension_cgroups = api.create_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")
 
         api.remove_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")
@@ -408,7 +408,7 @@ class FileSystemCgroupsApiTestCase(_MockedFileSystemTestCase):
 
     def test_remove_extension_cgroups_should_log_a_warning_when_the_cgroup_contains_active_tasks(self):
         api = FileSystemCgroupsApi()
-        api.create_extension_cgroups_root()
+        api.create_extensions_slice()
         api.create_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")
 
         with patch("azurelinuxagent.common.cgroupapi.logger.warn") as mock_logger_warn:
@@ -421,7 +421,7 @@ class FileSystemCgroupsApiTestCase(_MockedFileSystemTestCase):
 
     def test_get_extension_cgroups_should_return_all_cgroups(self):
         api = FileSystemCgroupsApi()
-        api.create_extension_cgroups_root()
+        api.create_extensions_slice()
         created = api.create_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")
 
         retrieved = api.get_extension_cgroups("Microsoft.Compute.TestExtension-1.2.3")
@@ -434,7 +434,7 @@ class FileSystemCgroupsApiTestCase(_MockedFileSystemTestCase):
     @patch('time.sleep', side_effect=lambda _: mock_sleep())
     def test_start_extension_command_should_add_the_child_process_to_the_extension_cgroup(self, _):
         api = FileSystemCgroupsApi()
-        api.create_extension_cgroups_root()
+        api.create_extensions_slice()
 
         with tempfile.TemporaryFile(dir=self.tmp_dir, mode="w+b") as stdout:
             with tempfile.TemporaryFile(dir=self.tmp_dir, mode="w+b") as stderr:
@@ -503,51 +503,35 @@ class SystemdCgroupsApiTestCase(AgentTestCase):
             self.assertEqual(cpu_accounting, "no", "Property {0} of {1} is incorrect".format("CPUAccounting", "walinuxagent.service"))
 
     def test_get_extensions_slice_root_name_should_return_the_root_slice_for_extensions(self):
-        root_slice_name = SystemdCgroupsApi()._get_extensions_slice_root_name()  # pylint: disable=protected-access
-        self.assertEqual(root_slice_name, "system-walinuxagent.extensions.slice")
+        root_slice_name = SystemdCgroupsApi()._get_extensions_root_slice_name()  # pylint: disable=protected-access
+        self.assertEqual(root_slice_name, "azure-vmextensions.slice")
 
     def test_get_extension_slice_name_should_return_the_slice_for_the_given_extension(self):
         extension_name = "Microsoft.Azure.DummyExtension-1.0"
         extension_slice_name = SystemdCgroupsApi()._get_extension_slice_name(extension_name)  # pylint: disable=protected-access
-        self.assertEqual(extension_slice_name, "system-walinuxagent.extensions-Microsoft.Azure.DummyExtension_1.0.slice")
+        self.assertEqual(extension_slice_name, "azure-vmextensions-Microsoft.Azure.DummyExtension_1.0.slice")
 
-    @attr('requires_sudo')
-    def test_create_extension_cgroups_root_should_create_extensions_root_slice(self):
-        self.assertTrue(i_am_root(), "Test does not run when non-root")
+    def test_create_azure_slice_should_create_unit_file(self):
+        azure_slice_path = os.path.join(self.tmp_dir, "azure.slice")
+        self.assertFalse(os.path.exists(azure_slice_path))
 
-        SystemdCgroupsApi().create_extension_cgroups_root()
+        with mock_cgroup_commands() as mocks:
+            # Mock out the actual calls to /etc/systemd
+            mocks.add_file(r"^/etc/systemd/system/azure.slice$", azure_slice_path)
+            SystemdCgroupsApi().create_azure_slice()
 
-        unit_name = SystemdCgroupsApi()._get_extensions_slice_root_name()  # pylint: disable=protected-access
-        status = shellutil.run_command(["systemctl", "status", unit_name])
-        self.assertIn("Loaded: loaded", status)
-        self.assertIn("Active: active", status)
+        self.assertTrue(os.path.exists(azure_slice_path))
 
-        shellutil.run_command(["systemctl", "stop", unit_name ])
-        shellutil.run_command(["systemctl", "disable", unit_name])
-        os.remove("/etc/systemd/system/{0}".format(unit_name))
-        shellutil.run_command(["systemctl", "daemon-reload"])
+    def test_create_extensions_slice_should_create_unit_file(self):
+        extensions_slice_path = os.path.join(self.tmp_dir, "azure-vmextensions.slice")
+        self.assertFalse(os.path.exists(extensions_slice_path))
 
-    @attr('requires_sudo')
-    def test_create_extension_cgroups_should_create_extension_slice(self):
-        self.assertTrue(i_am_root(), "Test does not run when non-root")
+        with mock_cgroup_commands() as mocks:
+            # Mock out the actual calls to /etc/systemd
+            mocks.add_file(r"^/etc/systemd/system/azure-vmextensions.slice$", extensions_slice_path)
+            SystemdCgroupsApi().create_extensions_slice()
 
-        extension_name = "Microsoft.Azure.DummyExtension-1.0"
-        cgroups = SystemdCgroupsApi().create_extension_cgroups(extension_name)
-        cpu_cgroup, memory_cgroup = cgroups[0], cgroups[1]
-        self.assertEqual(cpu_cgroup.path, "/sys/fs/cgroup/cpu/system.slice/Microsoft.Azure.DummyExtension_1.0")
-        self.assertEqual(memory_cgroup.path, "/sys/fs/cgroup/memory/system.slice/Microsoft.Azure.DummyExtension_1.0")
-
-        unit_name = SystemdCgroupsApi()._get_extension_slice_name(extension_name)  # pylint: disable=protected-access
-        self.assertEqual("system-walinuxagent.extensions-Microsoft.Azure.DummyExtension_1.0.slice", unit_name)
-
-        _, status = shellutil.run_get_output("systemctl status {0}".format(unit_name))
-        self.assertIn("Loaded: loaded", status)
-        self.assertIn("Active: active", status)
-
-        shellutil.run_get_output("systemctl stop {0}".format(unit_name))
-        shellutil.run_get_output("systemctl disable {0}".format(unit_name))
-        os.remove("/etc/systemd/system/{0}".format(unit_name))
-        shellutil.run_get_output("systemctl daemon-reload")
+        self.assertTrue(os.path.exists(extensions_slice_path))
 
     def assert_cgroups_created(self, extension_cgroups):
         self.assertEqual(len(extension_cgroups), 2,
diff --git a/tests/common/test_cgroupconfigurator.py b/tests/common/test_cgroupconfigurator.py
index a0e6b1c4..87a176d0 100644
--- a/tests/common/test_cgroupconfigurator.py
+++ b/tests/common/test_cgroupconfigurator.py
@@ -172,9 +172,9 @@ cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blki
 
         # List of operations to test, and the functions to mock used in order to do verifications
         operations = [
-            [configurator.create_extension_cgroups_root,                     "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.create_extension_cgroups_root"],
-            [lambda: configurator.create_extension_cgroups("A.B.C-1.0.0"),   "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.create_extension_cgroups"],
-            [lambda: configurator.remove_extension_cgroups("A.B.C-1.0.0"),   "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.remove_extension_cgroups"]
+            [configurator.create_extensions_slice, "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.create_extensions_slice"],
+            [lambda: configurator.create_extension_cgroups("A.B.C-1.0.0"),    "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.create_extension_cgroups"],
+            [lambda: configurator.remove_extension_cgroups("A.B.C-1.0.0"),    "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.remove_extension_cgroups"]
         ]
 
         for operation in operations:
@@ -190,7 +190,7 @@ cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blki
         with patch.object(configurator, "disable"):
             # List of operations to test, and the functions to mock in order to raise exceptions
             operations = [
-                [configurator.create_extension_cgroups_root,                     "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.create_extension_cgroups_root"],
+                [configurator.create_extensions_slice, "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.create_extensions_slice"],
                 [lambda: configurator.create_extension_cgroups("A.B.C-1.0.0"),   "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.create_extension_cgroups"],
                 [lambda: configurator.remove_extension_cgroups("A.B.C-1.0.0"),   "azurelinuxagent.common.cgroupapi.SystemdCgroupsApi.remove_extension_cgroups"]
             ]
diff --git a/tests/common/test_cgroupstelemetry.py b/tests/common/test_cgroupstelemetry.py
index 47e3ff0e..932e5efd 100644
--- a/tests/common/test_cgroupstelemetry.py
+++ b/tests/common/test_cgroupstelemetry.py
@@ -20,7 +20,6 @@ import random
 import time
 
 from azurelinuxagent.common.cgroup import CGroup
-from azurelinuxagent.common.cgroupconfigurator import CGroupConfigurator
 from azurelinuxagent.common.cgroupstelemetry import CGroupsTelemetry
 from azurelinuxagent.common.utils import fileutil
 from tests.tools import AgentTestCase, data_dir, patch
@@ -68,10 +67,6 @@ def consume_memory():
     return waste
 
 
-def make_new_cgroup(name="test-cgroup"):
-    return CGroupConfigurator.get_instance().create_extension_cgroups(name)
-
-
 class TestCGroupsTelemetry(AgentTestCase):
     NumSummarizationValues = 7
 
diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py
index b601c4bb..630bb4c4 100644
--- a/tests/ga/test_update.py
+++ b/tests/ga/test_update.py
@@ -20,6 +20,7 @@ from threading import currentThread
 from mock import PropertyMock
 
 from azurelinuxagent.common import conf
+from azurelinuxagent.common.cgroupconfigurator import CGroupConfigurator
 from azurelinuxagent.common.event import EVENTS_DIRECTORY
 from azurelinuxagent.common.exception import ProtocolError, UpdateError, ResourceGoneError
 from azurelinuxagent.common.future import ustr
@@ -38,6 +39,7 @@ from azurelinuxagent.ga.exthandlers import ExtHandlerInstance, HandlerEnvironmen
 from azurelinuxagent.ga.update import GuestAgent, GuestAgentError, MAX_FAILURE, AGENT_MANIFEST_FILE, \
     get_update_handler, ORPHAN_POLL_INTERVAL, AGENT_PARTITION_FILE, AGENT_ERROR_FILE, ORPHAN_WAIT_INTERVAL, \
     CHILD_LAUNCH_RESTART_MAX, CHILD_HEALTH_INTERVAL, UpdateHandler
+from tests.common.mock_cgroup_commands import mock_cgroup_commands
 from tests.protocol.mocks import mock_wire_protocol
 from tests.protocol.mockwiredata import DATA_FILE, DATA_FILE_MULTIPLE_EXT
 from tests.tools import AgentTestCase, call, data_dir, DEFAULT, patch, load_bin_data, load_data, Mock, MagicMock, \
@@ -837,6 +839,44 @@ class TestUpdate(UpdateTestCase):  # pylint: disable=too-many-public-methods
                 stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH,
                 mode)
 
+    def test_ensure_cgroups_initialized_creates_slices(self):
+        try:
+            azure_slice_path = os.path.join(self.tmp_dir, "azure.slice")
+            extensions_slice_path = os.path.join(self.tmp_dir, "azure-vmextensions.slice")
+
+            self.assertFalse(os.path.exists(azure_slice_path))
+            self.assertFalse(os.path.exists(extensions_slice_path))
+
+            CGroupConfigurator._instance = None  # pylint: disable=protected-access
+            with mock_cgroup_commands() as mocks:
+                # Mock out all the actual calls to systemd
+                mocks.add_file(r"^/etc/systemd/system/azure.slice$", azure_slice_path)
+                mocks.add_file(r"^/etc/systemd/system/azure-vmextensions.slice$", extensions_slice_path)
+
+                with patch.object(CGroupConfigurator.get_instance(), "enabled", return_value=True):
+                    self.update_handler._ensure_cgroups_initialized()  # pylint: disable=protected-access
+
+                    # Ensure we created the slice files with proper content if cgroups are enabled
+                    self.assertTrue(os.path.exists(azure_slice_path))
+                    self.assertTrue(os.path.exists(extensions_slice_path))
+                    self.assertEqual(fileutil.read_file(azure_slice_path), """[Unit]
+Description=Slice for Azure VM Agent and Extensions""")
+                    self.assertEqual(fileutil.read_file(extensions_slice_path), """[Unit]
+Description=Slice for Azure VM Extensions""")
+
+                # Clean files up for next assertion
+                os.remove(azure_slice_path)
+                os.remove(extensions_slice_path)
+
+                with patch.object(CGroupConfigurator.get_instance(), "enabled", return_value=False):
+                    self.update_handler._ensure_cgroups_initialized()  # pylint: disable=protected-access
+
+                    # Ensure we don't create the slice files if cgroups are disabled
+                    self.assertFalse(os.path.exists(azure_slice_path))
+                    self.assertFalse(os.path.exists(extensions_slice_path))
+        finally:
+            CGroupConfigurator._instance = None  # pylint: disable=protected-access
+
     def _test_evaluate_agent_health(self, child_agent_index=0):
         self.prepare_agents()