Repository: ambari Updated Branches: refs/heads/branch-2.4 4a45faaff -> 45990d240
AMBARI-17888. Create configuration flag to prevent changing of directory permissions. Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/45990d24 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/45990d24 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/45990d24 Branch: refs/heads/branch-2.4 Commit: 45990d24089fba12e61dbcec4719dc800f02ba9b Parents: 4a45faa Author: Siddharth Wagle <swa...@hortonworks.com> Authored: Thu Jul 28 11:11:20 2016 -0700 Committer: Siddharth Wagle <swa...@hortonworks.com> Committed: Thu Jul 28 11:11:20 2016 -0700 ---------------------------------------------------------------------- .../resource_management/TestDatanodeHelper.py | 63 +++++++++- .../libraries/functions/mounted_dirs_helper.py | 122 +++++++++++-------- .../HDP/2.0.6/configuration/cluster-env.xml | 6 +- .../python/stacks/2.0.6/configs/default.json | 2 +- .../python/stacks/2.0.6/configs/secured.json | 2 +- .../2.5/configs/ranger-admin-default.json | 2 +- .../2.5/configs/ranger-admin-secured.json | 2 +- .../stacks/2.5/configs/ranger-kms-default.json | 2 +- .../stacks/2.5/configs/ranger-kms-secured.json | 2 +- 9 files changed, 136 insertions(+), 67 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/45990d24/ambari-agent/src/test/python/resource_management/TestDatanodeHelper.py ---------------------------------------------------------------------- diff --git a/ambari-agent/src/test/python/resource_management/TestDatanodeHelper.py b/ambari-agent/src/test/python/resource_management/TestDatanodeHelper.py index 9fa986b..f22c8e9 100644 --- a/ambari-agent/src/test/python/resource_management/TestDatanodeHelper.py +++ b/ambari-agent/src/test/python/resource_management/TestDatanodeHelper.py @@ -24,6 +24,7 @@ from mock.mock import Mock, MagicMock, patch from resource_management.libraries.functions import mounted_dirs_helper from resource_management.core.logger import Logger +from resource_management.core.exceptions import Fail from resource_management import Directory from resource_management.libraries.script.script import Script @@ -52,7 +53,7 @@ def fake_create_dir(directory): print "Fake function to create directory {0}".format(directory) -@patch.object(Script, "get_config", new=MagicMock(return_value={'configurations':{'cluster-env': {'ignore_bad_mounts': False}}})) +@patch.object(Script, "get_config", new=MagicMock(return_value={'configurations':{'cluster-env': {'ignore_bad_mounts': False, 'manage_dirs_on_root': True, 'one_dir_per_partition': False}}})) class TestDatanodeHelper(TestCase): """ Test the functionality of the dfs_datanode_helper.py @@ -90,9 +91,9 @@ class TestDatanodeHelper(TestCase): for (name, args, kwargs) in log_error.mock_calls: print args[0] - log_info.assert_any_call("Forcefully creating directory: /grid/0/data") - log_info.assert_any_call("Forcefully creating directory: /grid/1/data") - log_info.assert_any_call("Forcefully creating directory: /GRID/2/Data/") + log_info.assert_any_call("Forcefully ensuring existence and permissions of the directory: /grid/0/data") + log_info.assert_any_call("Forcefully ensuring existence and permissions of the directory: /grid/1/data") + log_info.assert_any_call("Forcefully ensuring existence and permissions of the directory: /GRID/2/Data/") self.assertEquals(0, log_error.call_count) @@ -128,7 +129,10 @@ class TestDatanodeHelper(TestCase): error_msg = "".join(error_logs) self.assertEquals(1, log_error.call_count) - self.assertTrue("Directory /grid/2/data does not exist and became unmounted from /dev2" in error_msg) + self.assertTrue("Directory /grid/2/data became unmounted from /dev2 . Current mount point: / ." + " Please ensure that mounts are healthy. If the mount change was intentional, you can update the contents of " + "/var/lib/ambari-agent/data/datanode/dfs_data_dir_mount.hist." in error_msg) + @patch("resource_management.libraries.functions.mounted_dirs_helper.Directory") @patch.object(Logger, "info") @@ -166,3 +170,52 @@ class TestDatanodeHelper(TestCase): def test_get_mounts_with_multiple_data_dirs(self): self.assertEquals([], mounted_dirs_helper.get_mounts_with_multiple_data_dirs(["/", "/hodoop", "/tmp"], "/hadoop/data,/tmp")) self.assertEquals([("/", ["/hadoop/data", "/tmp"])], mounted_dirs_helper.get_mounts_with_multiple_data_dirs(["/"], "/hadoop/data,/tmp")) + + def test_may_manage_folder(self): + # root, no history file, manage_dirs_on_root = True + # folder should be managed + dirs_unmounted=set() + self.assertEquals(True, mounted_dirs_helper._may_manage_folder(dir_='/grid/0/data', last_mount_point_for_dir=None, is_non_root_dir=False, dirs_unmounted=dirs_unmounted, error_messages = [], manage_dirs_on_root = True, curr_mount_point = '/')) + self.assertSetEqual(dirs_unmounted, set()) + + # root, no history file, manage_dirs_on_root = False + # folder should not be managed + dirs_unmounted=set() + self.assertEquals(False, mounted_dirs_helper._may_manage_folder(dir_='/grid/0/data', last_mount_point_for_dir=None, is_non_root_dir=False, dirs_unmounted=dirs_unmounted, error_messages = [], manage_dirs_on_root = False, curr_mount_point = '/')) + self.assertSetEqual(dirs_unmounted, set(['/grid/0/data'])) + + # non root, no history file, manage_dirs_on_root = False + # folder should be managed + dirs_unmounted=set() + self.assertEquals(True, mounted_dirs_helper._may_manage_folder(dir_='/grid/0/data', last_mount_point_for_dir=None, is_non_root_dir=True, dirs_unmounted=dirs_unmounted, error_messages = [], manage_dirs_on_root = False, curr_mount_point = '/')) + self.assertSetEqual(dirs_unmounted, set()) + + # unmounted to root, manage_dirs_on_root = True + # folder should not be managed + dirs_unmounted=set() + self.assertEquals(False, mounted_dirs_helper._may_manage_folder('/grid/0/data', '/grid/0', True, dirs_unmounted, [], False, '/')) + self.assertSetEqual(dirs_unmounted, set(['/grid/0/data'])) + + # unmounted to root, manage_dirs_on_root = False + # folder should not be managed + dirs_unmounted=set() + self.assertEquals(False, mounted_dirs_helper._may_manage_folder(dir_='/grid/0/data', last_mount_point_for_dir='/grid/0/data', is_non_root_dir=False, dirs_unmounted=dirs_unmounted, error_messages = [], manage_dirs_on_root = False, curr_mount_point = '/')) + self.assertSetEqual(dirs_unmounted, set(['/grid/0/data'])) + + # same mount = root, manage_dirs_on_root = False + # folder should not be managed + dirs_unmounted=set() + self.assertEquals(False, mounted_dirs_helper._may_manage_folder(dir_='/grid/0/data', last_mount_point_for_dir='/', is_non_root_dir=False, dirs_unmounted=dirs_unmounted, error_messages = [], manage_dirs_on_root = False, curr_mount_point = '/')) + self.assertSetEqual(dirs_unmounted, set()) + + # same mount = root, manage_dirs_on_root = True + # folder should be managed + dirs_unmounted=set() + self.assertEquals(True, mounted_dirs_helper._may_manage_folder(dir_='/grid/0/data', last_mount_point_for_dir='/', is_non_root_dir=False, dirs_unmounted=dirs_unmounted, error_messages = [], manage_dirs_on_root = True, curr_mount_point = '/')) + self.assertSetEqual(dirs_unmounted, set()) + + # mount changed to non root, manage_dirs_on_root = False + # folder should not be managed + dirs_unmounted=set() + self.assertEquals(False, mounted_dirs_helper._may_manage_folder('/grid/0/data', '/', True, dirs_unmounted, [], False, '/grid/0')) + self.assertSetEqual(dirs_unmounted, set(['/grid/0/data'])) http://git-wip-us.apache.org/repos/asf/ambari/blob/45990d24/ambari-common/src/main/python/resource_management/libraries/functions/mounted_dirs_helper.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/resource_management/libraries/functions/mounted_dirs_helper.py b/ambari-common/src/main/python/resource_management/libraries/functions/mounted_dirs_helper.py index ba59a92..61c9768 100644 --- a/ambari-common/src/main/python/resource_management/libraries/functions/mounted_dirs_helper.py +++ b/ambari-common/src/main/python/resource_management/libraries/functions/mounted_dirs_helper.py @@ -28,7 +28,7 @@ from resource_management.libraries.functions.file_system import get_mount_point_ from resource_management.core.logger import Logger from resource_management.core.resources.system import Directory from resource_management.core.exceptions import Fail -from resource_management.libraries.script.script import Script +from resource_management.libraries.functions.default import default DIR_TO_MOUNT_HEADER = """ # This file keeps track of the last known mount-point for each dir. @@ -89,7 +89,7 @@ def handle_mounted_dirs(func, dirs_string, history_filename, update_cache=True): Directory(os.path.dirname(history_filename), create_parents = True, mode=0755, - ) + ) # Get the dirs that Ambari knows about and their last known mount point prev_dir_to_mount_point = get_dir_to_mount_from_file(history_filename) @@ -99,16 +99,16 @@ def handle_mounted_dirs(func, dirs_string, history_filename, update_cache=True): # If a dir was previously on / and is now mounted on a drive, we should store that too. dir_to_mount_point = prev_dir_to_mount_point.copy() - # This should typically be False for customers, but True the first time. - allowed_to_create_any_dir = False + # This should typically be True after first DataNode start, but False the first time. + history_file_exists = True if history_filename is None: - allowed_to_create_any_dir = True - Logger.warning("handle_mounted_dirs is allowed to create any directory since history_file.file property is null.") + history_file_exists = False + Logger.warning("History_file.file property is null.") else: if not os.path.exists(history_filename): - allowed_to_create_any_dir = True - Logger.warning("handle_mounted_dirs is allowed to create any directory since history_file property has file %s and it does not exist." % history_filename) + history_file_exists = False + Logger.warning("History_file property has file %s and it does not exist." % history_filename) valid_dirs = [] # dirs that have been normalized error_messages = [] # list of error messages to report at the end @@ -127,50 +127,36 @@ def handle_mounted_dirs(func, dirs_string, history_filename, update_cache=True): valid_existing_dirs.append(dir) used_mounts = set([get_mount_point_for_dir(dir) for dir in valid_existing_dirs]) - - for dir in valid_dirs: - if not dir in valid_existing_dirs: - may_create_this_dir = allowed_to_create_any_dir - last_mount_point_for_dir = None - - curr_mount_point = get_mount_point_for_dir(dir) - # This means that create_this_dir will stay false if the directory became unmounted. - # In other words, allow creating if it was already on /, or it's currently not on / - is_non_root_dir = (curr_mount_point is not None and curr_mount_point != "/") - - # Determine if should be allowed to create the dir directory. - # Either first time, became unmounted, or was just mounted on a drive - if not may_create_this_dir: - last_mount_point_for_dir = prev_dir_to_mount_point[dir] if dir in prev_dir_to_mount_point else None - - if last_mount_point_for_dir is None: - may_create_this_dir = (is_non_root_dir or Script.get_config()['configurations']['cluster-env']['create_dirs_on_root']) - else: - may_create_this_dir = (last_mount_point_for_dir == "/" or is_non_root_dir) - - if may_create_this_dir and Script.get_config()['configurations']['cluster-env']['ignore_bad_mounts']: - Logger.warning("Not creating {0} as cluster-env/ignore_bad_mounts is enabled.".format(dir)) - may_create_this_dir = False - - if may_create_this_dir and curr_mount_point in used_mounts: - message = "Trying to create another directory on the following mount: " + curr_mount_point - if Script.get_config()['configurations']['cluster-env']['one_dir_per_partition']: - raise Fail(message + " . Please turn off cluster-env/one_dir_per_partition or handle the situation manually.") + ignore_bad_mounts = default('/configurations/cluster-env/ignore_bad_mounts', False) + manage_dirs_on_root = default('/configurations/cluster-env/manage_dirs_on_root', True) + + for dir_ in valid_dirs: + last_mount_point_for_dir = prev_dir_to_mount_point.get(dir_, None) if history_file_exists else None + curr_mount_point = get_mount_point_for_dir(dir_) + is_non_root_dir = curr_mount_point is not None and curr_mount_point != "/" + folder_exists = dir_ in valid_existing_dirs + + if not folder_exists and ignore_bad_mounts: + Logger.debug("The directory {0} doesn't exist.".format(dir_)) + Logger.warning("Not creating {0} as cluster-env/ignore_bad_mounts is enabled.".format(dir_)) + may_manage_this_dir = False + else: + may_manage_this_dir = _may_manage_folder(dir_, last_mount_point_for_dir, is_non_root_dir, dirs_unmounted, error_messages, manage_dirs_on_root, curr_mount_point) + + if may_manage_this_dir and dir_ not in valid_existing_dirs and curr_mount_point in used_mounts: + if default('/configurations/cluster-env/one_dir_per_partition', False): + may_manage_this_dir = False + Logger.warning("Skipping creation of another directory on the following mount: " + curr_mount_point + " . Please turn off cluster-env/one_dir_per_partition or handle the situation manually.") else: - Logger.warning(message) - - if may_create_this_dir: - Logger.info("Forcefully creating directory: {0}".format(dir)) + Logger.warning("Trying to create another directory on the following mount: " + curr_mount_point) + + if may_manage_this_dir: + Logger.info("Forcefully ensuring existence and permissions of the directory: {0}".format(dir_)) + # Call the function + func(dir_) + used_mounts.add(curr_mount_point) - # Call the function - func(dir) - else: - # Additional check that wasn't allowed to create this dir and became unmounted. - if last_mount_point_for_dir is not None: - dirs_unmounted.add(dir) - msg = "Directory {0} does not exist and became unmounted from {1} .".format(dir, last_mount_point_for_dir) - error_messages.append(msg) pass # This is set to false during unit tests. @@ -183,14 +169,12 @@ def handle_mounted_dirs(func, dirs_string, history_filename, update_cache=True): if os.path.isdir(dir) and dir not in dirs_unmounted: curr_mount_point = get_mount_point_for_dir(dir) dir_to_mount_point[dir] = curr_mount_point - func(dir) if error_messages and len(error_messages) > 0: - header = " ERROR ".join(["*****"] * 6) + header = " WARNING ".join(["*****"] * 6) header = "\n" + "\n".join([header, ] * 3) + "\n" msg = " ".join(error_messages) + \ - " Please remount the dir(s) and run this command again. To ignore this failure and allow writing to the " \ - "root partition, either update the contents of {0}, or delete that file.".format(history_filename) + " Please ensure that mounts are healthy. If the mount change was intentional, you can update the contents of {0}.".format(history_filename) Logger.error(header + msg + header) dir_to_mount = DIR_TO_MOUNT_HEADER @@ -199,6 +183,38 @@ def handle_mounted_dirs(func, dirs_string, history_filename, update_cache=True): return dir_to_mount +def _may_manage_folder(dir_, last_mount_point_for_dir, is_non_root_dir, dirs_unmounted, error_messages, manage_dirs_on_root, curr_mount_point): + may_manage_this_dir = True + if last_mount_point_for_dir is None: + if is_non_root_dir: + may_manage_this_dir = True + else: + # root mount + if manage_dirs_on_root: + may_manage_this_dir = True + else: + Logger.warning("Will not manage the directory {0} since it's on root mount and cluster-env/manage_dirs_on_root == {1}".format(dir_, str(manage_dirs_on_root))) + may_manage_this_dir = False + # Do not add to the history file: + dirs_unmounted.add(dir_) + else: + Logger.debug("Last mount for {0} in the history file is {1}".format(dir_, str(last_mount_point_for_dir))) + if last_mount_point_for_dir == curr_mount_point: + if is_non_root_dir or manage_dirs_on_root: + Logger.debug("Will manage {0} since it's on the same mount point: {1}".format(dir_, str(last_mount_point_for_dir))) + may_manage_this_dir = True + else: + Logger.warning("Will not manage {0} since it's on the root mount point and cluster-env/manage_dirs_on_root == {1}".format(dir_, str(manage_dirs_on_root))) + may_manage_this_dir = False + else: + may_manage_this_dir = False + dirs_unmounted.add(dir_) + + msg = "Directory {0} became unmounted from {1} . Current mount point: {2} .".format(dir_, last_mount_point_for_dir, curr_mount_point) + error_messages.append(msg) + Logger.warning(msg) + return may_manage_this_dir + def get_mounts_with_multiple_data_dirs(mount_points, dirs): """ Returns a list with (mount, dir_list) for mounts with multiple dirs. http://git-wip-us.apache.org/repos/asf/ambari/blob/45990d24/ambari-server/src/main/resources/stacks/HDP/2.0.6/configuration/cluster-env.xml ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/stacks/HDP/2.0.6/configuration/cluster-env.xml b/ambari-server/src/main/resources/stacks/HDP/2.0.6/configuration/cluster-env.xml index 89e05d7..81cb175 100644 --- a/ambari-server/src/main/resources/stacks/HDP/2.0.6/configuration/cluster-env.xml +++ b/ambari-server/src/main/resources/stacks/HDP/2.0.6/configuration/cluster-env.xml @@ -218,14 +218,14 @@ gpgcheck=0</value> <on-ambari-upgrade add="true"/> </property> <property> - <name>create_dirs_on_root</name> + <name>manage_dirs_on_root</name> <value>true</value> - <description>For properties handled by handle_mounted_dirs this will make Ambari to create not-existent unknown directories on / partition</description> + <description>For properties handled by handle_mounted_dirs this will make Ambari to manage (create and set permissions) unknown directories on / partition</description> <on-ambari-upgrade add="true"/> </property> <property> <name>one_dir_per_partition</name> - <value>true</value> + <value>false</value> <description>For properties handled by handle_mounted_dirs this will make Ambari </description> <on-ambari-upgrade add="true"/> </property> http://git-wip-us.apache.org/repos/asf/ambari/blob/45990d24/ambari-server/src/test/python/stacks/2.0.6/configs/default.json ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/stacks/2.0.6/configs/default.json b/ambari-server/src/test/python/stacks/2.0.6/configs/default.json index 696b2de..28624fd 100644 --- a/ambari-server/src/test/python/stacks/2.0.6/configs/default.json +++ b/ambari-server/src/test/python/stacks/2.0.6/configs/default.json @@ -602,7 +602,7 @@ "metrics_collector_vip_port": "6189", "override_uid" : "true", "fetch_nonlocal_groups": "true", - "create_dirs_on_root": "true", + "manage_dirs_on_root": "true", "ignore_bad_mounts": "false" }, "hbase-env": { http://git-wip-us.apache.org/repos/asf/ambari/blob/45990d24/ambari-server/src/test/python/stacks/2.0.6/configs/secured.json ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/stacks/2.0.6/configs/secured.json b/ambari-server/src/test/python/stacks/2.0.6/configs/secured.json index 7a58ec5..73faf08 100644 --- a/ambari-server/src/test/python/stacks/2.0.6/configs/secured.json +++ b/ambari-server/src/test/python/stacks/2.0.6/configs/secured.json @@ -615,7 +615,7 @@ "user_group": "hadoop", "smokeuser_keytab": "/etc/security/keytabs/smokeuser.headless.keytab", "kinit_path_local": "/usr/bin", - "create_dirs_on_root": "true", + "manage_dirs_on_root": "true", "ignore_bad_mounts": "false" }, "hadoop-env": { http://git-wip-us.apache.org/repos/asf/ambari/blob/45990d24/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-default.json ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-default.json b/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-default.json index 934007b..9836e73 100644 --- a/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-default.json +++ b/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-default.json @@ -683,7 +683,7 @@ "alerts_repeat_tolerance": "1", "smokeuser_keytab": "/etc/security/keytabs/smokeuser.headless.keytab", "kerberos_domain": "EXAMPLE.COM", - "create_dirs_on_root": "true", + "manage_dirs_on_root": "true", "recovery_lifetime_max_count": "1024", "recovery_type": "AUTO_START", "ignore_bad_mounts": "false", http://git-wip-us.apache.org/repos/asf/ambari/blob/45990d24/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-secured.json ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-secured.json b/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-secured.json index daf6e80..e8c8199 100644 --- a/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-secured.json +++ b/ambari-server/src/test/python/stacks/2.5/configs/ranger-admin-secured.json @@ -750,7 +750,7 @@ "alerts_repeat_tolerance": "1", "smokeuser_keytab": "/etc/security/keytabs/smokeuser.headless.keytab", "kerberos_domain": "EXAMPLE.COM", - "create_dirs_on_root": "true", + "manage_dirs_on_root": "true", "recovery_lifetime_max_count": "1024", "recovery_type": "AUTO_START", "ignore_bad_mounts": "false", http://git-wip-us.apache.org/repos/asf/ambari/blob/45990d24/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-default.json ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-default.json b/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-default.json index d1365ac..5f7185c 100644 --- a/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-default.json +++ b/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-default.json @@ -681,7 +681,7 @@ "alerts_repeat_tolerance": "1", "smokeuser_keytab": "/etc/security/keytabs/smokeuser.headless.keytab", "kerberos_domain": "EXAMPLE.COM", - "create_dirs_on_root": "true", + "manage_dirs_on_root": "true", "recovery_lifetime_max_count": "1024", "recovery_type": "AUTO_START", "ignore_bad_mounts": "false", http://git-wip-us.apache.org/repos/asf/ambari/blob/45990d24/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-secured.json ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-secured.json b/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-secured.json index 7a68a07..acc5c21 100644 --- a/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-secured.json +++ b/ambari-server/src/test/python/stacks/2.5/configs/ranger-kms-secured.json @@ -851,7 +851,7 @@ "alerts_repeat_tolerance": "1", "smokeuser_keytab": "/etc/security/keytabs/smokeuser.headless.keytab", "kerberos_domain": "EXAMPLE.COM", - "create_dirs_on_root": "true", + "manage_dirs_on_root": "true", "recovery_lifetime_max_count": "1024", "recovery_type": "AUTO_START", "ignore_bad_mounts": "false",