72e22a94ed
This fixes up the test to mock the datetime calls specifically in the test that needs to mock them, rather than the prior approach which was redefining the behavior of the standard library function globally. Closes-Bug: #2045588 Change-Id: I9c585c91c5527a61e4a8dbcba0a8a01108cd9b2e
406 lines
19 KiB
Python
406 lines
19 KiB
Python
# Copyright 2017 Canonical Ltd
|
|
#
|
|
# 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 datetime
|
|
import sys
|
|
import unittest
|
|
import urllib
|
|
|
|
from unittest.mock import (
|
|
patch,
|
|
mock_open,
|
|
MagicMock,
|
|
Mock,
|
|
PropertyMock,
|
|
)
|
|
|
|
sys.path.append('files/nrpe-external-master')
|
|
from check_swift_storage import (
|
|
check_md5,
|
|
check_replication,
|
|
generate_md5,
|
|
repl_last_timestamp,
|
|
)
|
|
|
|
|
|
STATUS_OK = 0
|
|
STATUS_WARN = 1
|
|
STATUS_CRIT = 2
|
|
STATUS_UNKNOWN = 3
|
|
|
|
|
|
class CheckSwiftStorageTestCase(unittest.TestCase):
|
|
def test_generate_md5(self):
|
|
"""
|
|
Ensure md5 checksum is generated from a file content
|
|
"""
|
|
with patch("builtins.open", mock_open(read_data=b'data')) as \
|
|
mock_file:
|
|
result = generate_md5('path/to/file')
|
|
mock_file.assert_called_with('path/to/file', 'rb')
|
|
# md5 hash for 'data' string
|
|
self.assertEqual(result,
|
|
'8d777f385d3dfec8815d20f7496026dc')
|
|
|
|
@patch('urllib.request.urlopen')
|
|
def test_check_md5_unknown_urlerror(self, mock_urlopen):
|
|
"""
|
|
Force urllib.request.URLError to test try-except
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
url = '{}ringmd5'.format(base_url)
|
|
error = 'connection refused'
|
|
mock_urlopen.side_effect = (urllib
|
|
.error
|
|
.URLError(Mock(return_value=error)))
|
|
result = check_md5(base_url)
|
|
self.assertEqual(result,
|
|
[(STATUS_UNKNOWN,
|
|
"Can't open url: {}".format(url))])
|
|
|
|
@patch('urllib.request.urlopen')
|
|
def test_check_md5_unknown_valueerror1(self, mock_urlopen):
|
|
"""
|
|
Force ValueError on urllib.error.URLError to test try-except
|
|
"""
|
|
base_url = 'asdfasdf'
|
|
url = '{}ringmd5'.format(base_url)
|
|
mock_urlopen.side_effect = ValueError(Mock(return_value=''))
|
|
result = check_md5(base_url)
|
|
mock_urlopen.assert_called_with(url)
|
|
self.assertEqual(result,
|
|
[(STATUS_UNKNOWN,
|
|
"Can't parse status data")])
|
|
|
|
@patch('urllib.request.urlopen')
|
|
def test_check_md5_unknown_valueerror2(self, mock_urlopen):
|
|
"""
|
|
Force ValueError on json to test try-catch
|
|
"""
|
|
jdata = PropertyMock(return_value=b'X')
|
|
mock_urlopen.return_value = MagicMock(read=jdata)
|
|
result = check_md5('.')
|
|
mock_urlopen.assert_called_with('.ringmd5')
|
|
self.assertEqual(result,
|
|
[(STATUS_UNKNOWN,
|
|
"Can't parse status data")])
|
|
|
|
@patch('check_swift_storage.generate_md5')
|
|
def test_check_md5_unknown_ioerror(self, mock_generate_md5):
|
|
"""
|
|
Force IOError (reading file) to test try-catch
|
|
"""
|
|
jdata = b'{"/etc/swift/object.ring.gz": ' \
|
|
b'"6b4f3a0ef3731f18291ecd053ce0d9b6", ' \
|
|
b'"/etc/swift/account.ring.gz": ' \
|
|
b'"93fc4ae496a7343362ebf13988a137e7", ' \
|
|
b'"/etc/swift/container.ring.gz": ' \
|
|
b'"0ea1ec9585ef644ce2b5c5b1dced4128"}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_generate_md5.side_effect = IOError()
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_md5('.')
|
|
mock_urlopen.assert_called_with('.ringmd5')
|
|
expected_result = [(STATUS_UNKNOWN,
|
|
"Can't open ringfile "
|
|
"/etc/swift/{}.ring.gz".format(name))
|
|
for name in ('object', 'account', 'container')]
|
|
self.assertEqual(result, expected_result)
|
|
|
|
@patch('check_swift_storage.generate_md5')
|
|
def test_check_md5_crit_md5sum_mismatch(self, mock_generate_md5):
|
|
"""
|
|
Ensure md5 checksums match, STATUS_CRIT
|
|
"""
|
|
jdata = b'{"/etc/swift/object.ring.gz": ' \
|
|
b'"6b4f3a0ef3731f18291ecd053ce0d9b6", ' \
|
|
b'"/etc/swift/account.ring.gz": ' \
|
|
b'"93fc4ae496a7343362ebf13988a137e7", ' \
|
|
b'"/etc/swift/container.ring.gz": ' \
|
|
b'"0ea1ec9585ef644ce2b5c5b1dced4128"}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_generate_md5.return_value = 'xxxx'
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_md5('.')
|
|
mock_urlopen.assert_called_with('.ringmd5')
|
|
expected_result = [(STATUS_CRIT,
|
|
'Ringfile /etc/swift/{}.ring.gz '
|
|
'MD5 sum mismatch'.format(name))
|
|
for name in ('object', 'account', 'container')]
|
|
self.assertEqual(result, expected_result)
|
|
|
|
@patch('check_swift_storage.generate_md5')
|
|
def test_check_md5_ok(self, mock_generate_md5):
|
|
"""
|
|
Ensure md5 checksums match, STATUS_OK
|
|
"""
|
|
jdata = b'{"/etc/swift/object.ring.gz": ' \
|
|
b'"6b4f3a0ef3731f18291ecd053ce0d9b6", ' \
|
|
b'"/etc/swift/account.ring.gz": ' \
|
|
b'"6b4f3a0ef3731f18291ecd053ce0d9b6", ' \
|
|
b'"/etc/swift/container.ring.gz": ' \
|
|
b'"6b4f3a0ef3731f18291ecd053ce0d9b6"}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_generate_md5.return_value = '6b4f3a0ef3731f18291ecd053ce0d9b6'
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_md5('.')
|
|
mock_urlopen.assert_called_with('.ringmd5')
|
|
self.assertEqual(result,
|
|
[(STATUS_OK, 'OK')])
|
|
|
|
@patch('urllib.request.urlopen')
|
|
def test_check_replication_unknown_urlerror(self, mock_urlopen):
|
|
"""
|
|
Force urllib.error.URLError to test try-catch
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
url = '{}replication/{}'
|
|
error = 'connection refused'
|
|
mock_urlopen.side_effect = (urllib
|
|
.error
|
|
.URLError(Mock(return_value=error)))
|
|
result = check_replication(base_url, 60)
|
|
expected_result = [(STATUS_UNKNOWN,
|
|
"Can't open url: "
|
|
"{}".format(url.format(base_url, name)))
|
|
for name in ('account', 'object', 'container')]
|
|
self.assertEqual(result, expected_result)
|
|
|
|
@patch('urllib.request.urlopen')
|
|
def test_check_replication_unknown_valueerror1(self, mock_urlopen):
|
|
"""
|
|
Force ValueError on urllib.error to test try-catch
|
|
"""
|
|
base_url = '.'
|
|
mock_urlopen.side_effect = ValueError(Mock(return_value=''))
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result,
|
|
3*[(STATUS_UNKNOWN,
|
|
"Can't parse status data")])
|
|
|
|
@patch('urllib.request.urlopen')
|
|
def test_check_replication_unknown_valueerror2(self, mock_urlopen):
|
|
"""
|
|
Force ValueError on json to test try-catch
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
jdata = PropertyMock(return_value=b'X')
|
|
mock_urlopen.return_value = MagicMock(read=jdata)
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result,
|
|
3*[(STATUS_UNKNOWN,
|
|
"Can't parse status data")])
|
|
|
|
@patch('check_swift_storage.repl_last_timestamp')
|
|
def test_check_replication_crit_lag_notworking(self, mock_timestamp):
|
|
"""
|
|
Catch NULL replication value, STATUS_CRIT
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
jdata = b'{"replication_last": 1493299546.629282, ' \
|
|
b'"replication_stats": {"no_change": 0, "rsync": 0, ' \
|
|
b'"success": 0, "failure": 0, "attempted": 0, "ts_repl": 0, ' \
|
|
b'"remove": 0, "remote_merge": 0, "diff_capped": 0, ' \
|
|
b'"start": 1493299546.621624, "hashmatch": 0, "diff": 0, ' \
|
|
b'"empty": 0}, "replication_time": 0.0076580047607421875}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_timestamp.return_value = (None, 0)
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result,
|
|
[(STATUS_CRIT,
|
|
"'{}' replication lag not working "
|
|
"(perms issue? check syslog)".format(repl))
|
|
for repl in ('account', 'object', 'container')])
|
|
|
|
@patch('check_swift_storage.repl_last_timestamp')
|
|
def test_check_replication_crit_lag(self, mock_timestamp):
|
|
"""
|
|
Replication lag over CRIT threshold, STATUS_CRIT
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
jdata = b'{"replication_last": 1493299546.629282, ' \
|
|
b'"replication_stats": {"no_change": 0, "rsync": 0, ' \
|
|
b'"success": 0, "failure": 0, "attempted": 0, "ts_repl": 0, ' \
|
|
b'"remove": 0, "remote_merge": 0, "diff_capped": 0, ' \
|
|
b'"start": 1493299546.621624, "hashmatch": 0, "diff": 0, ' \
|
|
b'"empty": 0}, "replication_time": 0.0076580047607421875}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_timestamp.return_value = (MagicMock(days=0, seconds=12), 0)
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result,
|
|
[(STATUS_CRIT,
|
|
"'{}' replication lag is "
|
|
"12 seconds".format(repl))
|
|
for repl in ('account', 'object', 'container')])
|
|
|
|
@patch('check_swift_storage.repl_last_timestamp')
|
|
def test_check_replication_crit_failures(self, mock_timestamp):
|
|
"""
|
|
Replication failures over CRIT threshold, STATUS_CRIT
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
jdata = b'{"replication_last": 1493299546.629282, ' \
|
|
b'"replication_stats": {"no_change": 0, "rsync": 0, ' \
|
|
b'"success": 0, "failure": 0, "attempted": 0, "ts_repl": 0, ' \
|
|
b'"remove": 0, "remote_merge": 0, "diff_capped": 0, ' \
|
|
b'"start": 1493299546.621624, "hashmatch": 0, "diff": 0, ' \
|
|
b'"empty": 0}, "replication_time": 0.0076580047607421875}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_timestamp.return_value = (MagicMock(days=0, seconds=0), 12)
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result,
|
|
3*[(STATUS_CRIT, "12 replication failures")])
|
|
|
|
@patch('check_swift_storage.repl_last_timestamp')
|
|
def test_check_replication_crit_null_failures(self, mock_timestamp):
|
|
"""
|
|
Catch NULL value on failures stats, STATUS_CRIT
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
jdata = b'{"replication_last": 1493299546.629282, ' \
|
|
b'"replication_stats": {"no_change": 0, "rsync": 0, ' \
|
|
b'"success": 0, "failure": 0, "attempted": 0, "ts_repl": 0, ' \
|
|
b'"remove": 0, "remote_merge": 0, "diff_capped": 0, ' \
|
|
b'"start": 1493299546.621624, "hashmatch": 0, "diff": 0, ' \
|
|
b'"empty": 0}, "replication_time": 0.0076580047607421875}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_timestamp.return_value = (MagicMock(days=0, seconds=0), -1)
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result,
|
|
3*[(STATUS_CRIT,
|
|
"replication failures counter is NULL "
|
|
"(check syslog)")])
|
|
|
|
@patch('check_swift_storage.repl_last_timestamp')
|
|
def test_check_replication_warn_lag(self, mock_timestamp):
|
|
"""
|
|
Replication lag over WARN threshold (below CRIT), STATUS_WARN
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
jdata = b'{"replication_last": 1493299546.629282, ' \
|
|
b'"replication_stats": {"no_change": 0, "rsync": 0, ' \
|
|
b'"success": 0, "failure": 0, "attempted": 0, "ts_repl": 0, ' \
|
|
b'"remove": 0, "remote_merge": 0, "diff_capped": 0, ' \
|
|
b'"start": 1493299546.621624, "hashmatch": 0, "diff": 0, ' \
|
|
b'"empty": 0}, "replication_time": 0.0076580047607421875}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_timestamp.return_value = (MagicMock(days=0, seconds=5), 0)
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result,
|
|
[(STATUS_WARN,
|
|
"'{}' replication lag is "
|
|
"5 seconds".format(repl))
|
|
for repl in ('account', 'object', 'container')])
|
|
|
|
@patch('check_swift_storage.repl_last_timestamp')
|
|
def test_check_replication_crit_day_plus_lag(self, mock_timestamp):
|
|
"""
|
|
Replication lag CRITS with day wrap, STATUS_CRIT
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
jdata = b'{"replication_last": 1493299546.629282, ' \
|
|
b'"replication_stats": {"no_change": 0, "rsync": 0, ' \
|
|
b'"success": 0, "failure": 0, "attempted": 0, "ts_repl": 0, ' \
|
|
b'"remove": 0, "remote_merge": 0, "diff_capped": 0, ' \
|
|
b'"start": 1493299546.621624, "hashmatch": 0, "diff": 0, ' \
|
|
b'"empty": 0}, "replication_time": 0.0076580047607421875}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_timestamp.return_value = (MagicMock(days=2, seconds=5), 0)
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result,
|
|
[(STATUS_CRIT,
|
|
"'{}' replication lag is "
|
|
"172805 seconds".format(repl))
|
|
for repl in ('account', 'object', 'container')])
|
|
|
|
@patch('check_swift_storage.repl_last_timestamp')
|
|
def test_check_replication_warn_failures(self, mock_timestamp):
|
|
"""
|
|
Replication failures over WARN threshold (below CRIT), STATUS_WARN
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
jdata = b'{"replication_last": 1493299546.629282, ' \
|
|
b'"replication_stats": {"no_change": 0, "rsync": 0, ' \
|
|
b'"success": 0, "failure": 0, "attempted": 0, "ts_repl": 0, ' \
|
|
b'"remove": 0, "remote_merge": 0, "diff_capped": 0, ' \
|
|
b'"start": 1493299546.621624, "hashmatch": 0, "diff": 0, ' \
|
|
b'"empty": 0}, "replication_time": 0.0076580047607421875}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_timestamp.return_value = (MagicMock(days=0, seconds=0), 5)
|
|
# with patch('urllib2.urlopen') as mock_urlopen:
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result,
|
|
3*[(STATUS_WARN, "5 replication failures")])
|
|
|
|
@patch('check_swift_storage.repl_last_timestamp')
|
|
def test_check_replication_ok(self, mock_timestamp):
|
|
"""
|
|
Replication lag and number of failures are below WARN threshold,
|
|
STATUS_OK
|
|
"""
|
|
base_url = 'http://localhost:6000/recon/'
|
|
jdata = b'{"replication_last": 1493299546.629282, ' \
|
|
b'"replication_stats": {"no_change": 0, "rsync": 0, ' \
|
|
b'"success": 0, "failure": 0, "attempted": 0, "ts_repl": 0, ' \
|
|
b'"remove": 0, "remote_merge": 0, "diff_capped": 0, ' \
|
|
b'"start": 1493299546.621624, "hashmatch": 0, "diff": 0, ' \
|
|
b'"empty": 0}, "replication_time": 0.0076580047607421875}'
|
|
pmock_jdata = PropertyMock(return_value=jdata)
|
|
mock_timestamp.return_value = (MagicMock(days=0, seconds=0), 0)
|
|
with patch('urllib.request.urlopen') as mock_urlopen:
|
|
mock_urlopen.return_value = MagicMock(read=pmock_jdata)
|
|
result = check_replication(base_url, [4, 10, 4, 10])
|
|
self.assertEqual(result, [(STATUS_OK, 'OK')])
|
|
|
|
@patch('check_swift_storage.datetime')
|
|
def test_repl_last_timestamp(self, mock_datetime):
|
|
"""
|
|
Calculates delta between NOW and last replication date
|
|
Also gathers the number of failures
|
|
"""
|
|
# 1493299546.629282
|
|
mock_datetime.datetime.now.return_value = (
|
|
datetime.datetime(2017, 4, 27, 13, 25, 46, 629282)
|
|
)
|
|
mock_datetime.datetime.fromtimestamp.side_effect = (
|
|
datetime.datetime.utcfromtimestamp
|
|
)
|
|
|
|
jdata = {u'replication_last': 1493299546.629282, u'replication_stats':
|
|
{u'no_change': 0, u'rsync': 0, u'success': 0, u'start':
|
|
1493299546.621624, u'attempted': 0, u'ts_repl': 0, u'remove':
|
|
0, u'remote_merge': 0, u'diff_capped': 0, u'failure': 0,
|
|
u'hashmatch': 0, u'diff': 0, u'empty': 0},
|
|
u'replication_time': 0.0076580047607421875}
|
|
result = repl_last_timestamp(jdata)
|
|
self.assertEqual(result,
|
|
(datetime.timedelta(0), 0))
|