mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #33984 from jfindlay/disk_capacity
Add docs and tests to disk state
This commit is contained in:
commit
53baae6eb1
2 changed files with 225 additions and 56 deletions
|
@ -2,7 +2,34 @@
|
|||
'''
|
||||
Disk monitoring state
|
||||
|
||||
Monitor the state of disk resources
|
||||
Monitor the state of disk resources.
|
||||
|
||||
The ``disk.status`` function can be used to report that the used space of a
|
||||
filesystem is within the specified limits.
|
||||
|
||||
.. code-block:: sls
|
||||
|
||||
used_space:
|
||||
disk.status:
|
||||
- name: /dev/xda1
|
||||
- maximum: 79%
|
||||
- minumum: 11%
|
||||
|
||||
It can be used with an ``onfail`` requisite, for example, to take additional
|
||||
action in response to or in preparation for other states.
|
||||
|
||||
.. code-block:: sls
|
||||
|
||||
storage_threshold:
|
||||
disk.status:
|
||||
- name: /dev/xda1
|
||||
- maximum: 97%
|
||||
|
||||
clear_cache:
|
||||
cmd.run:
|
||||
- name: rm -r /var/cache/app
|
||||
- onfail:
|
||||
- disk: storage_threshold
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
@ -14,9 +41,37 @@ __monitor__ = [
|
|||
]
|
||||
|
||||
|
||||
def _validate_percent(name, value):
|
||||
'''
|
||||
Validate ``name`` as an integer in the range [0, 100]
|
||||
'''
|
||||
comment = ''
|
||||
# Must be integral
|
||||
try:
|
||||
if isinstance(value, string_types):
|
||||
value = value.strip('%')
|
||||
value = int(value)
|
||||
except (TypeError, ValueError):
|
||||
comment += '{0} must be an integer '.format(name)
|
||||
# Must be in percent range
|
||||
else:
|
||||
if value < 0 or value > 100:
|
||||
comment += '{0} must be in the range [0, 100] '.format(name)
|
||||
return value, comment
|
||||
|
||||
|
||||
def status(name, maximum=None, minimum=None):
|
||||
'''
|
||||
Return the current disk usage stats for the named mount point
|
||||
|
||||
name
|
||||
Filesystem with which to check used space
|
||||
|
||||
minimum
|
||||
The required minimum amount of used space in percent
|
||||
|
||||
maximum
|
||||
The required maximum amount of used space in percent
|
||||
'''
|
||||
# Monitoring state, no changes will be made so no test interface needed
|
||||
ret = {'name': name,
|
||||
|
@ -26,38 +81,36 @@ def status(name, maximum=None, minimum=None):
|
|||
'data': {}} # Data field for monitoring state
|
||||
|
||||
data = __salt__['disk.usage']()
|
||||
|
||||
# Validate name
|
||||
if name not in data:
|
||||
ret['result'] = False
|
||||
ret['comment'] += 'Named disk mount not present '
|
||||
return ret
|
||||
# Validate extrema
|
||||
if maximum:
|
||||
try:
|
||||
if isinstance(maximum, string_types):
|
||||
maximum = int(maximum.strip('%'))
|
||||
except Exception:
|
||||
ret['comment'] += 'Max argument must be an integer '
|
||||
maximum, comment = _validate_percent('maximum', maximum)
|
||||
ret['comment'] += comment
|
||||
if minimum:
|
||||
try:
|
||||
if isinstance(minimum, string_types):
|
||||
minimum = int(minimum.strip('%'))
|
||||
except Exception:
|
||||
ret['comment'] += 'Min argument must be an integer '
|
||||
if minimum and maximum:
|
||||
minimum, comment = _validate_percent('minimum', minimum)
|
||||
ret['comment'] += comment
|
||||
if minimum is not None and maximum is not None:
|
||||
if minimum >= maximum:
|
||||
ret['comment'] += 'Min must be less than max'
|
||||
ret['comment'] += 'Min must be less than max '
|
||||
if ret['comment']:
|
||||
return ret
|
||||
cap = int(data[name]['capacity'].strip('%'))
|
||||
|
||||
capacity = int(data[name]['capacity'].strip('%'))
|
||||
ret['data'] = data[name]
|
||||
if minimum:
|
||||
if cap < minimum:
|
||||
ret['comment'] = 'Disk is below minimum of {0} at {1}'.format(
|
||||
minimum, cap)
|
||||
if minimum is not None:
|
||||
if capacity < minimum:
|
||||
ret['comment'] = 'Disk used space is below minimum of {0}% at {1}%'.format(
|
||||
minimum, capacity)
|
||||
return ret
|
||||
if maximum:
|
||||
if cap > maximum:
|
||||
ret['comment'] = 'Disk is above maximum of {0} at {1}'.format(
|
||||
maximum, cap)
|
||||
if maximum is not None:
|
||||
if capacity > maximum:
|
||||
ret['comment'] = 'Disk used space is above maximum of {0}% at {1}%'.format(
|
||||
maximum, capacity)
|
||||
return ret
|
||||
ret['comment'] = 'Disk in acceptable range'
|
||||
ret['result'] = True
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
Tests for disk state
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
@ -26,46 +26,162 @@ disk.__salt__ = {}
|
|||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class DiskTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.disk
|
||||
Test disk state
|
||||
'''
|
||||
# 'status' function tests: 1
|
||||
def setUp(self):
|
||||
'''
|
||||
setup common test info
|
||||
'''
|
||||
self.mock_data = {
|
||||
'/': {
|
||||
'1K-blocks': '41147472',
|
||||
'available': '37087976',
|
||||
'capacity': '6%',
|
||||
'filesystem': '/dev/xvda1',
|
||||
'used': '2172880'},
|
||||
'/dev': {
|
||||
'1K-blocks': '10240',
|
||||
'available': '10240',
|
||||
'capacity': '0%',
|
||||
'filesystem': 'udev',
|
||||
'used': '0'},
|
||||
'/run': {
|
||||
'1K-blocks': '410624',
|
||||
'available': '379460',
|
||||
'capacity': '8%',
|
||||
'filesystem': 'tmpfs',
|
||||
'used': '31164'},
|
||||
'/sys/fs/cgroup': {
|
||||
'1K-blocks': '1026556',
|
||||
'available': '1026556',
|
||||
'capacity': '0%',
|
||||
'filesystem': 'tmpfs',
|
||||
'used': '0'}
|
||||
}
|
||||
self.__salt__ = {
|
||||
'disk.usage': MagicMock(return_value=self.mock_data),
|
||||
}
|
||||
|
||||
def test_status_missing(self):
|
||||
'''
|
||||
Test disk.status when name not found
|
||||
'''
|
||||
mock_fs = '/mnt/cheese'
|
||||
mock_ret = {'name': mock_fs,
|
||||
'result': False,
|
||||
'comment': 'Named disk mount not present ',
|
||||
'changes': {},
|
||||
'data': {}}
|
||||
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
ret = disk.status(mock_fs)
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
def test_status_type_error(self):
|
||||
'''
|
||||
Test disk.status with incorrectly formatted arguments
|
||||
'''
|
||||
mock_fs = '/'
|
||||
mock_ret = {'name': mock_fs,
|
||||
'result': False,
|
||||
'comment': 'maximum must be an integer ',
|
||||
'changes': {},
|
||||
'data': {}}
|
||||
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
ret = disk.status(mock_fs, maximum=r'e^{i\pi}')
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
mock_ret['comment'] = 'minimum must be an integer '
|
||||
ret = disk.status(mock_fs, minimum=r'\cos\pi + i\sin\pi')
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
def test_status_range_error(self):
|
||||
'''
|
||||
Test disk.status with excessive extrema
|
||||
'''
|
||||
mock_fs = '/'
|
||||
mock_ret = {'name': mock_fs,
|
||||
'result': False,
|
||||
'comment': 'maximum must be in the range [0, 100] ',
|
||||
'changes': {},
|
||||
'data': {}}
|
||||
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
ret = disk.status(mock_fs, maximum='-1')
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
mock_ret['comment'] = 'minimum must be in the range [0, 100] '
|
||||
ret = disk.status(mock_fs, minimum='101')
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
def test_status_inverted_range(self):
|
||||
'''
|
||||
Test disk.status when minimum > maximum
|
||||
'''
|
||||
mock_fs = '/'
|
||||
mock_ret = {'name': mock_fs,
|
||||
'result': False,
|
||||
'comment': 'Min must be less than max ',
|
||||
'changes': {},
|
||||
'data': {}}
|
||||
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
ret = disk.status(mock_fs, maximum='0', minimum='1')
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
def test_status_threshold(self):
|
||||
'''
|
||||
Test disk.status when filesystem triggers thresholds
|
||||
'''
|
||||
mock_min = 100
|
||||
mock_max = 0
|
||||
mock_fs = '/'
|
||||
mock_used = int(self.mock_data[mock_fs]['capacity'].strip('%'))
|
||||
mock_ret = {'name': mock_fs,
|
||||
'result': False,
|
||||
'comment': '',
|
||||
'changes': {},
|
||||
'data': self.mock_data[mock_fs]}
|
||||
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
mock_ret['comment'] = 'Disk used space is below minimum of {0}% at {1}%'.format(
|
||||
mock_min,
|
||||
mock_used
|
||||
)
|
||||
ret = disk.status(mock_fs, minimum=mock_min)
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
mock_ret['comment'] = 'Disk used space is above maximum of {0}% at {1}%'.format(
|
||||
mock_max,
|
||||
mock_used
|
||||
)
|
||||
ret = disk.status(mock_fs, maximum=mock_max)
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
def test_status(self):
|
||||
'''
|
||||
Test to return the current disk usage stats for the named mount point
|
||||
Test disk.status when filesystem meets thresholds
|
||||
'''
|
||||
name = 'mydisk'
|
||||
mock_min = 0
|
||||
mock_max = 100
|
||||
mock_fs = '/'
|
||||
mock_ret = {'name': mock_fs,
|
||||
'result': True,
|
||||
'comment': 'Disk in acceptable range',
|
||||
'changes': {},
|
||||
'data': self.mock_data[mock_fs]}
|
||||
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'comment': '',
|
||||
'changes': {},
|
||||
'data': {}}
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
ret = disk.status(mock_fs, minimum=mock_min)
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
mock = MagicMock(side_effect=[[], [name], {name: {'capacity': '8 %'}},
|
||||
{name: {'capacity': '22 %'}},
|
||||
{name: {'capacity': '15 %'}}])
|
||||
with patch.dict(disk.__salt__, {'disk.usage': mock}):
|
||||
comt = ('Named disk mount not present ')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(disk.status(name), ret)
|
||||
|
||||
comt = ('Min must be less than max')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(disk.status(name, '10 %', '20 %'), ret)
|
||||
|
||||
comt = ('Disk is below minimum of 10 at 8')
|
||||
ret.update({'comment': comt, 'data': {'capacity': '8 %'}})
|
||||
self.assertDictEqual(disk.status(name, '20 %', '10 %'), ret)
|
||||
|
||||
comt = ('Disk is above maximum of 20 at 22')
|
||||
ret.update({'comment': comt, 'data': {'capacity': '22 %'}})
|
||||
self.assertDictEqual(disk.status(name, '20 %', '10 %'), ret)
|
||||
|
||||
comt = ('Disk in acceptable range')
|
||||
ret.update({'comment': comt, 'result': True,
|
||||
'data': {'capacity': '15 %'}})
|
||||
self.assertDictEqual(disk.status(name, '20 %', '10 %'), ret)
|
||||
with patch.dict(disk.__salt__, self.__salt__):
|
||||
ret = disk.status(mock_fs, maximum=mock_max)
|
||||
self.assertEqual(ret, mock_ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Add table
Reference in a new issue