|
@@ -0,0 +1,218 @@
|
|
|
+#!/usr/bin/env python
|
|
|
+
|
|
|
+'''
|
|
|
+Licensed to the Apache Software Foundation (ASF) under one
|
|
|
+or more contributor license agreements. See the NOTICE file
|
|
|
+distributed with this work for additional information
|
|
|
+regarding copyright ownership. The ASF licenses this file
|
|
|
+to you 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.
|
|
|
+'''
|
|
|
+
|
|
|
+# System imports
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import logging
|
|
|
+
|
|
|
+from mock.mock import patch
|
|
|
+
|
|
|
+# Local imports
|
|
|
+from stacks.utils.RMFTestCase import *
|
|
|
+import resource_management.libraries.functions.file_system
|
|
|
+
|
|
|
+COMMON_SERVICES_ALERTS_DIR = "HDFS/2.1.0.2.0/package/alerts"
|
|
|
+DATA_DIR_MOUNT_HIST_FILE_PATH = "/etc/hadoop/conf/dfs_data_dir_mount.hist"
|
|
|
+
|
|
|
+file_path = os.path.dirname(os.path.abspath(__file__))
|
|
|
+file_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(file_path)))))
|
|
|
+file_path = os.path.join(file_path, "main", "resources", "common-services", COMMON_SERVICES_ALERTS_DIR)
|
|
|
+
|
|
|
+RESULT_STATE_OK = "OK"
|
|
|
+RESULT_STATE_WARNING = "WARNING"
|
|
|
+RESULT_STATE_CRITICAL = "CRITICAL"
|
|
|
+RESULT_STATE_UNKNOWN = "UNKNOWN"
|
|
|
+
|
|
|
+class TestAlertDataNodeUnmountedDataDir(RMFTestCase):
|
|
|
+
|
|
|
+ def setUp(self):
|
|
|
+ """
|
|
|
+ Import the class under test.
|
|
|
+ Because the class is present in a different folder, append its dir to the system path.
|
|
|
+ Also, shorten the import name and make it a global so the test functions can access it.
|
|
|
+ :return:
|
|
|
+ """
|
|
|
+ self.logger = logging.getLogger()
|
|
|
+ sys.path.append(file_path)
|
|
|
+ global alert
|
|
|
+ import alert_datanode_unmounted_data_dir as alert
|
|
|
+
|
|
|
+ def test_missing_configs(self):
|
|
|
+ """
|
|
|
+ Check that the status is UNKNOWN when configs are missing.
|
|
|
+ """
|
|
|
+ configs = {}
|
|
|
+ [status, messages] = alert.execute(configurations=configs)
|
|
|
+ self.assertEqual(status, RESULT_STATE_UNKNOWN)
|
|
|
+ self.assertTrue(messages is not None and len(messages) == 1)
|
|
|
+ self.assertTrue('is a required parameter for the script' in messages[0])
|
|
|
+
|
|
|
+ configs = {
|
|
|
+ "{{hdfs-site/dfs.datanode.data.dir}}": ""
|
|
|
+ }
|
|
|
+ [status, messages] = alert.execute(configurations=configs)
|
|
|
+ self.assertEqual(status, RESULT_STATE_UNKNOWN)
|
|
|
+ self.assertTrue(messages is not None and len(messages) == 1)
|
|
|
+ self.assertTrue('is a required parameter for the script' in messages[0])
|
|
|
+
|
|
|
+ configs = {
|
|
|
+ "{{hadoop-env/dfs.datanode.data.dir.mount.file}}": DATA_DIR_MOUNT_HIST_FILE_PATH
|
|
|
+ }
|
|
|
+ [status, messages] = alert.execute(configurations=configs)
|
|
|
+ self.assertEqual(status, RESULT_STATE_UNKNOWN)
|
|
|
+ self.assertTrue(messages is not None and len(messages) == 1)
|
|
|
+ self.assertTrue('is a required parameter for the script' in messages[0])
|
|
|
+
|
|
|
+ configs = {
|
|
|
+ "{{hdfs-site/dfs.datanode.data.dir}}": "",
|
|
|
+ "{{hadoop-env/dfs.datanode.data.dir.mount.file}}": DATA_DIR_MOUNT_HIST_FILE_PATH
|
|
|
+ }
|
|
|
+ [status, messages] = alert.execute(configurations=configs)
|
|
|
+ self.assertNotEqual(status, RESULT_STATE_UNKNOWN)
|
|
|
+
|
|
|
+ @patch("resource_management.libraries.functions.file_system.get_mount_point_for_dir")
|
|
|
+ @patch("os.path.exists")
|
|
|
+ @patch("os.path.isdir")
|
|
|
+ def test_mount_history_file_does_not_exist(self, is_dir_mock, exists_mock, get_mount_mock):
|
|
|
+ """
|
|
|
+ Test that the status is WARNING when the data dirs are mounted on root, but the mount history file
|
|
|
+ does not exist.
|
|
|
+ """
|
|
|
+ configs = {
|
|
|
+ "{{hdfs-site/dfs.datanode.data.dir}}": "/grid/0/data",
|
|
|
+ "{{hadoop-env/dfs.datanode.data.dir.mount.file}}": DATA_DIR_MOUNT_HIST_FILE_PATH
|
|
|
+ }
|
|
|
+
|
|
|
+ # Mock calls
|
|
|
+ exists_mock.return_value = False
|
|
|
+ is_dir_mock.return_value = True
|
|
|
+ get_mount_mock.return_value = "/"
|
|
|
+
|
|
|
+ [status, messages] = alert.execute(configurations=configs)
|
|
|
+ self.assertEqual(status, RESULT_STATE_WARNING)
|
|
|
+ self.assertTrue(messages is not None and len(messages) == 1)
|
|
|
+ self.assertTrue("File not found, {0}".format(DATA_DIR_MOUNT_HIST_FILE_PATH) in messages[0])
|
|
|
+
|
|
|
+ @patch("resource_management.libraries.functions.dfs_datanode_helper.get_data_dir_to_mount_from_file")
|
|
|
+ @patch("resource_management.libraries.functions.file_system.get_mount_point_for_dir")
|
|
|
+ @patch("os.path.exists")
|
|
|
+ @patch("os.path.isdir")
|
|
|
+ def test_all_dirs_on_root(self, is_dir_mock, exists_mock, get_mount_mock, get_data_dir_to_mount_from_file_mock):
|
|
|
+ """
|
|
|
+ Test that the status is OK when all drives are mounted on the root partition
|
|
|
+ and this coincides with the expected values.
|
|
|
+ """
|
|
|
+ configs = {
|
|
|
+ "{{hdfs-site/dfs.datanode.data.dir}}": "/grid/0/data,/grid/1/data,/grid/2/data",
|
|
|
+ "{{hadoop-env/dfs.datanode.data.dir.mount.file}}": DATA_DIR_MOUNT_HIST_FILE_PATH
|
|
|
+ }
|
|
|
+
|
|
|
+ # Mock calls
|
|
|
+ exists_mock.return_value = True
|
|
|
+ is_dir_mock.return_value = True
|
|
|
+ get_mount_mock.return_value = "/"
|
|
|
+ get_data_dir_to_mount_from_file_mock.return_value = {"/grid/0/data": "/",
|
|
|
+ "/grid/1/data": "/",
|
|
|
+ "/grid/2/data": "/"}
|
|
|
+
|
|
|
+ [status, messages] = alert.execute(configurations=configs)
|
|
|
+ self.assertEqual(status, RESULT_STATE_OK)
|
|
|
+ self.assertTrue(messages is not None and len(messages) == 1)
|
|
|
+ self.assertTrue("Data dir(s) are fine" in messages[0])
|
|
|
+
|
|
|
+ @patch("resource_management.libraries.functions.dfs_datanode_helper.get_data_dir_to_mount_from_file")
|
|
|
+ @patch("resource_management.libraries.functions.file_system.get_mount_point_for_dir")
|
|
|
+ @patch("os.path.exists")
|
|
|
+ @patch("os.path.isdir")
|
|
|
+ def test_match_expected(self, is_dir_mock, exists_mock, get_mount_mock, get_data_dir_to_mount_from_file_mock):
|
|
|
+ """
|
|
|
+ Test that the status is OK when the mount points match the expected values.
|
|
|
+ """
|
|
|
+ configs = {
|
|
|
+ "{{hdfs-site/dfs.datanode.data.dir}}": "/grid/0/data,/grid/1/data,/grid/2/data",
|
|
|
+ "{{hadoop-env/dfs.datanode.data.dir.mount.file}}": DATA_DIR_MOUNT_HIST_FILE_PATH
|
|
|
+ }
|
|
|
+
|
|
|
+ # Mock calls
|
|
|
+ exists_mock.return_value = True
|
|
|
+ is_dir_mock.return_value = True
|
|
|
+ get_mount_mock.side_effect = ["/device1", "/device2", "/"]
|
|
|
+ get_data_dir_to_mount_from_file_mock.return_value = {"/grid/0/data": "/device1",
|
|
|
+ "/grid/1/data": "/device2",
|
|
|
+ "/grid/2/data": "/"}
|
|
|
+
|
|
|
+ [status, messages] = alert.execute(configurations=configs)
|
|
|
+ self.assertEqual(status, RESULT_STATE_OK)
|
|
|
+ self.assertTrue(messages is not None and len(messages) == 1)
|
|
|
+ self.assertTrue("Data dir(s) are fine" in messages[0])
|
|
|
+
|
|
|
+ @patch("resource_management.libraries.functions.dfs_datanode_helper.get_data_dir_to_mount_from_file")
|
|
|
+ @patch("resource_management.libraries.functions.file_system.get_mount_point_for_dir")
|
|
|
+ @patch("os.path.exists")
|
|
|
+ @patch("os.path.isdir")
|
|
|
+ def test_critical_one_root_one_mounted(self, is_dir_mock, exists_mock, get_mount_mock, get_data_dir_to_mount_from_file_mock):
|
|
|
+ """
|
|
|
+ Test that the status is CRITICAL when the history file is missing
|
|
|
+ and at least one data dir is on a mount and at least one data dir is on the root partition.
|
|
|
+ """
|
|
|
+ configs = {
|
|
|
+ "{{hdfs-site/dfs.datanode.data.dir}}": "/grid/0/data,/grid/1/data,/grid/2/data,/grid/3/data",
|
|
|
+ "{{hadoop-env/dfs.datanode.data.dir.mount.file}}": DATA_DIR_MOUNT_HIST_FILE_PATH
|
|
|
+ }
|
|
|
+
|
|
|
+ # Mock calls
|
|
|
+ exists_mock.return_value = False
|
|
|
+ is_dir_mock.return_value = True
|
|
|
+ # The first 2 data dirs will report an error.
|
|
|
+ get_mount_mock.side_effect = ["/", "/", "/device1", "/device2"]
|
|
|
+
|
|
|
+ [status, messages] = alert.execute(configurations=configs)
|
|
|
+ self.assertEqual(status, RESULT_STATE_CRITICAL)
|
|
|
+ self.assertTrue(messages is not None and len(messages) == 1)
|
|
|
+ self.assertTrue("Detected at least one data dir on a mount point, but these are writing to the root partition: /grid/0/data, /grid/1/data" in messages[0])
|
|
|
+
|
|
|
+ @patch("resource_management.libraries.functions.dfs_datanode_helper.get_data_dir_to_mount_from_file")
|
|
|
+ @patch("resource_management.libraries.functions.file_system.get_mount_point_for_dir")
|
|
|
+ @patch("os.path.exists")
|
|
|
+ @patch("os.path.isdir")
|
|
|
+ def test_critical_unmounted(self, is_dir_mock, exists_mock, get_mount_mock, get_data_dir_to_mount_from_file_mock):
|
|
|
+ """
|
|
|
+ Test that the status is CRITICAL when the history file exists and one of the dirs
|
|
|
+ became unmounted.
|
|
|
+ """
|
|
|
+ configs = {
|
|
|
+ "{{hdfs-site/dfs.datanode.data.dir}}": "/grid/0/data,/grid/1/data,/grid/2/data,/grid/3/data",
|
|
|
+ "{{hadoop-env/dfs.datanode.data.dir.mount.file}}": DATA_DIR_MOUNT_HIST_FILE_PATH
|
|
|
+ }
|
|
|
+
|
|
|
+ # Mock calls
|
|
|
+ exists_mock.return_value = True
|
|
|
+ is_dir_mock.return_value = True
|
|
|
+ get_mount_mock.side_effect = ["/", "/", "/device3", "/device4"]
|
|
|
+ get_data_dir_to_mount_from_file_mock.return_value = {"/grid/0/data": "/", # remained on /
|
|
|
+ "/grid/1/data": "/device2", # became unmounted
|
|
|
+ "/grid/2/data": "/", # became mounted
|
|
|
+ "/grid/3/data": "/device4"} # remained mounted
|
|
|
+
|
|
|
+ [status, messages] = alert.execute(configurations=configs)
|
|
|
+ self.assertEqual(status, RESULT_STATE_CRITICAL)
|
|
|
+ self.assertTrue(messages is not None and len(messages) == 1)
|
|
|
+ self.assertTrue("Detected data dir(s) that became unmounted and are now writing to the root partition: /grid/1/data ." in messages[0])
|