Enables or disables TRIM delete notifications.

Delete notification (also known as trim or unmap) is a feature that
notifies the underlying storage devices that data blocks are no longer
in use so they can be freed.

Implements: blueprint delete-notifications
Co-Authored-By: Micu Matei-Marius <mmicu@cloudbasesolutions.com>

Change-Id: I2a1670f921fce1165df48096c6fde8660b21282e
This commit is contained in:
Alessandro Pilotti 2017-02-01 10:20:33 +02:00
parent dd3cd540a3
commit 278468d2e2
6 changed files with 132 additions and 0 deletions

View File

@ -224,6 +224,10 @@ class GlobalOptions(conf_base.Options):
help='Volume mount points on which a Windows page file needs '
'to be created. E.g.: '
'"\\\\?\\GLOBALROOT\\device\\Harddisk1\\Partition1\\"'),
cfg.BoolOpt(
'trim_enabled', default=False,
help='Enables or disables TRIM delete notifications for '
'the underlying storage device.'),
]
self._cli_options = [

View File

@ -139,3 +139,7 @@ class BaseOSUtils(object):
def set_real_time_clock_utc(self, utc):
raise NotImplementedError()
def enable_trim(self, enable):
"""Enables or disables TRIM delete notifications."""
raise NotImplementedError()

View File

@ -1416,3 +1416,13 @@ class WindowsUtils(base.BaseOSUtils):
0, winreg.KEY_ALL_ACCESS) as key:
winreg.SetValueEx(key, 'PagingFiles', 0,
winreg.REG_MULTI_SZ, values)
def enable_trim(self, enable):
"""Enables or disables TRIM delete notifications."""
args = ["fsutil.exe", "behavior", "set", "disabledeletenotify",
"0" if enable else "1"]
(out, err, ret_val) = self.execute_system32_process(args)
if ret_val:
raise exception.CloudbaseInitException(
'TRIM configurating failed.\nOutput: %(out)s\nError:'
' %(err)s' % {'out': out, 'err': err})

View File

@ -0,0 +1,36 @@
# Copyright 2017 Cloudbase Solutions Srl
#
# 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.
from oslo_log import log as oslo_logging
from cloudbaseinit import conf as cloudbaseinit_conf
from cloudbaseinit.osutils import factory as osutils_factory
from cloudbaseinit.plugins.common import base as plugin_base
CONF = cloudbaseinit_conf.CONF
LOG = oslo_logging.getLogger(__name__)
class TrimConfigPlugin(plugin_base.BasePlugin):
def execute(self, service, shared_data):
osutils = osutils_factory.get_os_utils()
osutils.enable_trim(CONF.trim_enabled)
LOG.info("TRIM enabled status: %s", CONF.trim_enabled)
return plugin_base.PLUGIN_EXECUTION_DONE, False
def get_os_requirements(self):
return 'win32', (6, 1)

View File

@ -2336,3 +2336,28 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
self._winreg_mock.SetValueEx.assert_called_with(
key, 'PagingFiles', 0, self._winreg_mock.REG_MULTI_SZ,
expected_values)
@mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
'.execute_system32_process')
def _test_trim(self, mock_execute_process, err):
if err:
mock_execute_process.return_value = ("fake out", "", 1)
self.assertRaises(exception.CloudbaseInitException,
self._winutils.enable_trim, True)
else:
args = ["fsutil.exe", "behavior",
"set", "disabledeletenotify"]
mock_execute_process.return_value = ("fake out", "fake err", 0)
self._winutils.enable_trim(True)
mock_execute_process.assert_called_with(args + ["0"])
self._winutils.enable_trim(False)
mock_execute_process.assert_called_with(args + ["1"])
def test_trim(self):
self._test_trim(err=False)
def test_trim_exception(self):
self._test_trim(err=True)

View File

@ -0,0 +1,53 @@
# Copyright 2017 Cloudbase Solutions Srl
#
# 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.
import unittest
try:
import unittest.mock as mock
except ImportError:
import mock
from cloudbaseinit.plugins.common import base
from cloudbaseinit.plugins.common import trim
from cloudbaseinit.tests import testutils
class TrimPluginPluginTests(unittest.TestCase):
def setUp(self):
self._trim_plugin = trim.TrimConfigPlugin()
@mock.patch('cloudbaseinit.osutils.factory.get_os_utils')
def _test_trim(self, mock_get_os_utils, status):
shared_data = 'fake_shared_data'
mock_os_utils = mock.Mock()
mock_get_os_utils.return_value = mock_os_utils
with testutils.ConfPatcher('trim_enabled', status):
response = self._trim_plugin.execute(mock.Mock(), shared_data)
mock_os_utils.enable_trim.assert_called_once_with(status)
self.assertEqual(response, (base.PLUGIN_EXECUTION_DONE, False))
def test_trim_enable(self):
self._test_trim(status=True)
def test_trim_disable(self):
self._test_trim(status=False)
def test_get_os_requirements(self):
response = self._trim_plugin.get_os_requirements()
self.assertEqual(response, ('win32', (6, 1)))