Repository: ambari Updated Branches: refs/heads/trunk 6c21b0942 -> e3c9aa7a4
AMBARI-8478. Falcon service components should indicate security state. (robert levas via jaimin) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e3c9aa7a Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e3c9aa7a Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e3c9aa7a Branch: refs/heads/trunk Commit: e3c9aa7a4336a43be61342c80395b45288979d5f Parents: 6c21b09 Author: Jaimin Jetly <jai...@hortonworks.com> Authored: Tue Dec 30 11:01:50 2014 -0800 Committer: Jaimin Jetly <jai...@hortonworks.com> Committed: Tue Dec 30 11:01:50 2014 -0800 ---------------------------------------------------------------------- .../libraries/functions/security_commons.py | 52 +++++---- .../0.5.0.2.1/package/scripts/falcon_client.py | 9 ++ .../0.5.0.2.1/package/scripts/falcon_server.py | 62 +++++++++++ .../0.5.0.2.1/package/scripts/status_params.py | 11 ++ .../HDFS/2.1.0.2.0/package/scripts/datanode.py | 15 ++- .../2.1.0.2.0/package/scripts/hdfs_client.py | 15 +-- .../2.1.0.2.0/package/scripts/journalnode.py | 16 +-- .../HDFS/2.1.0.2.0/package/scripts/namenode.py | 16 +-- .../HDFS/2.1.0.2.0/package/scripts/snamenode.py | 16 +-- .../2.1.0.2.0/package/scripts/zkfc_slave.py | 16 +-- .../stacks/2.1/FALCON/test_falcon_client.py | 25 +++++ .../stacks/2.1/FALCON/test_falcon_server.py | 106 +++++++++++++++++++ .../src/test/python/stacks/utils/RMFTestCase.py | 8 +- 13 files changed, 300 insertions(+), 67 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-common/src/main/python/resource_management/libraries/functions/security_commons.py ---------------------------------------------------------------------- diff --git a/ambari-common/src/main/python/resource_management/libraries/functions/security_commons.py b/ambari-common/src/main/python/resource_management/libraries/functions/security_commons.py index d3cd1a2..535a53b 100644 --- a/ambari-common/src/main/python/resource_management/libraries/functions/security_commons.py +++ b/ambari-common/src/main/python/resource_management/libraries/functions/security_commons.py @@ -23,6 +23,8 @@ from tempfile import mkstemp import os import json +FILE_TYPE_XML = 'XML' +FILE_TYPE_PROPERTIES = 'PROPERTIES' def validate_security_config_properties(params, configuration_rules): """ @@ -103,29 +105,47 @@ def build_expectations(config_file, value_checks, empty_checks, read_checks): def get_params_from_filesystem(conf_dir, config_files): """ Used to retrieve properties from xml config files and build a dict + + The dictionary of configuration files to file types should contain one of the following values" + 'XML' + 'PROPERTIES' + :param conf_dir: directory where the configuration files sit - :param config_files: list of configuration file names - :return: + :param config_files: dictionary of configuration file names to (supported) file types + :return: a dictionary of config-type to a dictionary of key/value pairs for """ result = {} from xml.etree import ElementTree as ET - - for config_file in config_files: - configuration = ET.parse(conf_dir + os.sep + config_file) - props = configuration.getroot().getchildren() - config_file_id = config_file[:-4] if len(config_file) > 4 else config_file - result[config_file_id] = {} - for prop in props: - result[config_file_id].update({prop[0].text: prop[1].text}) + import ConfigParser, StringIO + for config_file, file_type in config_files.iteritems(): + file_name, file_ext = os.path.splitext(config_file) + + if file_type == FILE_TYPE_XML: + configuration = ET.parse(conf_dir + os.sep + config_file) + props = configuration.getroot().getchildren() + config_file_id = file_name if file_name else config_file + result[config_file_id] = {} + for prop in props: + result[config_file_id].update({prop[0].text: prop[1].text}) + + elif file_type == FILE_TYPE_PROPERTIES: + with open(conf_dir + os.sep + config_file, 'r') as f: + config_string = '[root]\n' + f.read() + ini_fp = StringIO.StringIO(config_string) + config = ConfigParser.RawConfigParser() + config.readfp(ini_fp) + props = config.items('root') + result[file_name] = {} + for key, value in props: + result[file_name].update({key : value}) return result def cached_kinit_executor(kinit_path, exec_user, keytab_file, principal, hostname, temp_dir, - expiration_time): + expiration_time=5): """ Main cached kinit executor - Uses a temporary file on the FS to cache executions. Each command will have its own file and only one entry (last successful execution) will be stored - :return: """ key = str(hash("%s|%s" % (principal, keytab_file))) filename = key + "_tmp.txt" @@ -151,15 +171,13 @@ def cached_kinit_executor(kinit_path, exec_user, keytab_file, principal, hostnam cache_file.write("{}") if (not output) or (key not in output) or ("last_successful_execution" not in output[key]): - return new_cached_exec(key, file_path, kinit_path, exec_user, keytab_file, principal, hostname) + new_cached_exec(key, file_path, kinit_path, exec_user, keytab_file, principal, hostname) else: last_run_time = output[key]["last_successful_execution"] now = datetime.now() if (now - datetime.strptime(last_run_time, "%Y-%m-%d %H:%M:%S.%f") > timedelta( minutes=expiration_time)): - return new_cached_exec(key, file_path, kinit_path, exec_user, keytab_file, principal, hostname) - else: - return True + new_cached_exec(key, file_path, kinit_path, exec_user, keytab_file, principal, hostname) def new_cached_exec(key, file_path, kinit_path, exec_user, keytab_file, principal, hostname): @@ -180,5 +198,3 @@ def new_cached_exec(key, file_path, kinit_path, exec_user, keytab_file, principa json.dump(result, cache_file) finally: os.remove(temp_kinit_cache_file) - - return True http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_client.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_client.py b/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_client.py index 6f1cd56..23260df 100644 --- a/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_client.py +++ b/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_client.py @@ -50,5 +50,14 @@ class FalconClient(Script): Execute(format("hdp-select set hadoop-client {version}")) + def security_status(self, env): + import status_params + env.set_params(status_params) + + if status_params.security_enabled: + self.put_structured_out({"securityState": "SECURED_KERBEROS"}) + else: + self.put_structured_out({"securityState": "UNSECURED"}) + if __name__ == "__main__": FalconClient().execute() http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_server.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_server.py b/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_server.py index 536bd49..52a6f2b 100644 --- a/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_server.py +++ b/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/falcon_server.py @@ -21,6 +21,9 @@ import falcon_server_upgrade from resource_management import * from resource_management.libraries.functions.version import * +from resource_management.libraries.functions.security_commons import build_expectations, \ + cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties, \ + FILE_TYPE_PROPERTIES from falcon import falcon class FalconServer(Script): @@ -80,6 +83,65 @@ class FalconServer(Script): Execute(format("hdp-select set falcon-server {version}")) falcon_server_upgrade.pre_start_restore() + def security_status(self, env): + import status_params + env.set_params(status_params) + if status_params.security_enabled: + props_value_check = {"*.falcon.authentication.type": "kerberos", + "*.falcon.http.authentication.type": "kerberos"} + props_empty_check = ["*.falcon.service.authentication.kerberos.principal", + "*.falcon.service.authentication.kerberos.keytab", + "*.falcon.http.authentication.kerberos.principal", + "*.falcon.http.authentication.kerberos.keytab"] + props_read_check = ["*.falcon.service.authentication.kerberos.keytab", + "*.falcon.http.authentication.kerberos.keytab"] + falcon_startup_props = build_expectations('startup', props_value_check, props_empty_check, + props_read_check) + + falcon_expectations ={} + falcon_expectations.update(falcon_startup_props) + + security_params = get_params_from_filesystem('/etc/falcon/conf', + {'startup.properties': FILE_TYPE_PROPERTIES}) + result_issues = validate_security_config_properties(security_params, falcon_expectations) + if not result_issues: # If all validations passed successfully + try: + # Double check the dict before calling execute + if ( 'startup' not in security_params + or '*.falcon.service.authentication.kerberos.keytab' not in security_params['startup'] + or '*.falcon.service.authentication.kerberos.principal' not in security_params['startup']) \ + or '*.falcon.http.authentication.kerberos.keytab' not in security_params['startup'] \ + or '*.falcon.http.authentication.kerberos.principal' not in security_params['startup']: + self.put_structured_out({"securityState": "UNSECURED"}) + self.put_structured_out( + {"securityIssuesFound": "Keytab file or principal are not set property."}) + return + + cached_kinit_executor(status_params.kinit_path_local, + status_params.falcon_user, + security_params['startup']['*.falcon.service.authentication.kerberos.keytab'], + security_params['startup']['*.falcon.service.authentication.kerberos.principal'], + status_params.hostname, + status_params.tmp_dir) + cached_kinit_executor(status_params.kinit_path_local, + status_params.falcon_user, + security_params['startup']['*.falcon.http.authentication.kerberos.keytab'], + security_params['startup']['*.falcon.http.authentication.kerberos.principal'], + status_params.hostname, + status_params.tmp_dir) + self.put_structured_out({"securityState": "SECURED_KERBEROS"}) + except Exception as e: + self.put_structured_out({"securityState": "ERROR"}) + self.put_structured_out({"securityStateErrorInfo": str(e)}) + else: + issues = [] + for cf in result_issues: + issues.append("Configuration file %s did not pass the validation. Reason: %s" % (cf, result_issues[cf])) + self.put_structured_out({"securityIssuesFound": ". ".join(issues)}) + self.put_structured_out({"securityState": "UNSECURED"}) + else: + self.put_structured_out({"securityState": "UNSECURED"}) + if __name__ == "__main__": FalconServer().execute() http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/status_params.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/status_params.py b/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/status_params.py index 6ebb35f..facc983 100644 --- a/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/status_params.py +++ b/ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/package/scripts/status_params.py @@ -22,3 +22,14 @@ from resource_management import * config = Script.get_config() falcon_pid_dir = config['configurations']['falcon-env']['falcon_pid_dir'] server_pid_file = format('{falcon_pid_dir}/falcon.pid') + +# Security related/required params +hostname = config['hostname'] +security_enabled = config['configurations']['cluster-env']['security_enabled'] +hadoop_conf_dir = "/etc/hadoop/conf" +kinit_path_local = functions.get_kinit_path(["/usr/bin", "/usr/kerberos/bin", "/usr/sbin"]) +tmp_dir = Script.get_tmp_dir() +falcon_conf_dir_prefix = "/etc/falcon" +falcon_conf_dir = format("{falcon_conf_dir_prefix}/conf") +hdfs_user = config['configurations']['hadoop-env']['hdfs_user'] +falcon_user = config['configurations']['falcon-env']['falcon_user'] http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/datanode.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/datanode.py b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/datanode.py index 8bce423..237ef07 100644 --- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/datanode.py +++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/datanode.py @@ -22,7 +22,7 @@ from resource_management import * from resource_management.libraries.functions.version import compare_versions, \ format_hdp_stack_version from resource_management.libraries.functions.security_commons import build_expectations, \ - cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties + cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties, FILE_TYPE_XML from hdfs import hdfs @@ -108,7 +108,8 @@ class DataNode(Script): hdfs_expectations.update(hdfs_site_expectations) security_params = get_params_from_filesystem(status_params.hadoop_conf_dir, - ['core-site.xml', 'hdfs-site.xml']) + {'core-site.xml': FILE_TYPE_XML, + 'hdfs-site.xml': FILE_TYPE_XML}) result_issues = validate_security_config_properties(security_params, hdfs_expectations) if not result_issues: # If all validations passed successfully try: @@ -126,18 +127,16 @@ class DataNode(Script): security_params['hdfs-site']['dfs.datanode.keytab.file'], security_params['hdfs-site']['dfs.datanode.kerberos.principal'], status_params.hostname, - status_params.tmp_dir, - 30) + status_params.tmp_dir) self.put_structured_out({"securityState": "SECURED_KERBEROS"}) except Exception as e: self.put_structured_out({"securityState": "ERROR"}) self.put_structured_out({"securityStateErrorInfo": str(e)}) else: - issues = "" + issues = [] for cf in result_issues: - issues += "Configuration file " + cf + " did not pass the validation. Reason: " + \ - result_issues[cf] - self.put_structured_out({"securityIssuesFound": issues}) + issues.append("Configuration file %s did not pass the validation. Reason: %s" % (cf, result_issues[cf])) + self.put_structured_out({"securityIssuesFound": ". ".join(issues)}) self.put_structured_out({"securityState": "UNSECURED"}) http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/hdfs_client.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/hdfs_client.py b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/hdfs_client.py index cf93c2f..59be175 100644 --- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/hdfs_client.py +++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/hdfs_client.py @@ -19,7 +19,8 @@ limitations under the License. from resource_management import * from resource_management.libraries.functions.security_commons import build_expectations, \ - cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties + cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties, \ + FILE_TYPE_XML from hdfs import hdfs from utils import service @@ -70,7 +71,8 @@ class HdfsClient(Script): hdfs_expectations ={} hdfs_expectations.update(core_site_expectations) - security_params = get_params_from_filesystem(status_params.hadoop_conf_dir, ['core-site.xml']) + security_params = get_params_from_filesystem(status_params.hadoop_conf_dir, + {'core-site.xml': FILE_TYPE_XML}) result_issues = validate_security_config_properties(security_params, hdfs_expectations) if not result_issues: # If all validations passed successfully if status_params.hdfs_user_principal or status_params.hdfs_user_keytab: @@ -80,8 +82,7 @@ class HdfsClient(Script): status_params.hdfs_user_keytab, status_params.hdfs_user_principal, status_params.hostname, - status_params.tmp_dir, - 30) + status_params.tmp_dir) self.put_structured_out({"securityState": "SECURED_KERBEROS"}) except Exception as e: self.put_structured_out({"securityState": "ERROR"}) @@ -90,10 +91,10 @@ class HdfsClient(Script): self.put_structured_out({"securityIssuesFound": "hdfs principal and/or keytab file is not specified"}) self.put_structured_out({"securityState": "UNSECURED"}) else: - issues="" + issues = [] for cf in result_issues: - issues+="Configuration file " + cf + " did not pass the validation. Reason: " + result_issues[cf] - self.put_structured_out({"securityIssuesFound": issues}) + issues.append("Configuration file %s did not pass the validation. Reason: %s" % (cf, result_issues[cf])) + self.put_structured_out({"securityIssuesFound": ". ".join(issues)}) self.put_structured_out({"securityState": "UNSECURED"}) else: http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/journalnode.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/journalnode.py b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/journalnode.py index 60e91ce..addf0e1 100644 --- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/journalnode.py +++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/journalnode.py @@ -22,7 +22,8 @@ from resource_management.libraries.functions.version import compare_versions, \ format_hdp_stack_version from resource_management.libraries.functions.format import format from resource_management.libraries.functions.security_commons import build_expectations, \ - cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties + cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties, \ + FILE_TYPE_XML from utils import service from hdfs import hdfs @@ -110,7 +111,8 @@ class JournalNode(Script): hdfs_expectations.update(hdfs_site_expectations) hdfs_expectations.update(core_site_expectations) - security_params = get_params_from_filesystem(status_params.hadoop_conf_dir, ['core-site.xml']) + security_params = get_params_from_filesystem(status_params.hadoop_conf_dir, + {'core-site.xml': FILE_TYPE_XML}) result_issues = validate_security_config_properties(security_params, hdfs_expectations) if not result_issues: # If all validations passed successfully try: @@ -128,18 +130,16 @@ class JournalNode(Script): security_params['hdfs-site']['dfs.journalnode.kerberos.keytab.file'], security_params['hdfs-site']['dfs.journalnode.kerberos.principal'], status_params.hostname, - status_params.tmp_dir, - 30) + status_params.tmp_dir) self.put_structured_out({"securityState": "SECURED_KERBEROS"}) except Exception as e: self.put_structured_out({"securityState": "ERROR"}) self.put_structured_out({"securityStateErrorInfo": str(e)}) else: - issues = "" + issues = [] for cf in result_issues: - issues += "Configuration file " + cf + " did not pass the validation. Reason: " + \ - result_issues[cf] - self.put_structured_out({"securityIssuesFound": issues}) + issues.append("Configuration file %s did not pass the validation. Reason: %s" % (cf, result_issues[cf])) + self.put_structured_out({"securityIssuesFound": ". ".join(issues)}) self.put_structured_out({"securityState": "UNSECURED"}) http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py index e8dfe16..a2b7c53 100644 --- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py +++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py @@ -25,7 +25,8 @@ from datetime import datetime from resource_management import * from resource_management.libraries.functions.security_commons import build_expectations, \ - cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties + cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties, \ + FILE_TYPE_XML from resource_management.libraries.functions.version import compare_versions, \ format_hdp_stack_version from resource_management.libraries.functions.format import format @@ -120,7 +121,8 @@ class NameNode(Script): hdfs_expectations.update(hdfs_site_expectations) security_params = get_params_from_filesystem(status_params.hadoop_conf_dir, - ['core-site.xml', 'hdfs-site.xml']) + {'core-site.xml': FILE_TYPE_XML, + 'hdfs-site.xml': FILE_TYPE_XML}) result_issues = validate_security_config_properties(security_params, hdfs_expectations) if not result_issues: # If all validations passed successfully try: @@ -138,18 +140,16 @@ class NameNode(Script): security_params['hdfs-site']['dfs.namenode.keytab.file'], security_params['hdfs-site']['dfs.namenode.kerberos.principal'], status_params.hostname, - status_params.tmp_dir, - 30) + status_params.tmp_dir) self.put_structured_out({"securityState": "SECURED_KERBEROS"}) except Exception as e: self.put_structured_out({"securityState": "ERROR"}) self.put_structured_out({"securityStateErrorInfo": str(e)}) else: - issues = "" + issues = [] for cf in result_issues: - issues += "Configuration file " + cf + " did not pass the validation. Reason: " + \ - result_issues[cf] - self.put_structured_out({"securityIssuesFound": issues}) + issues.append("Configuration file %s did not pass the validation. Reason: %s" % (cf, result_issues[cf])) + self.put_structured_out({"securityIssuesFound": ". ".join(issues)}) self.put_structured_out({"securityState": "UNSECURED"}) http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/snamenode.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/snamenode.py b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/snamenode.py index 9900a7e..01d2dfb 100644 --- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/snamenode.py +++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/snamenode.py @@ -19,7 +19,8 @@ limitations under the License. from resource_management import * from resource_management.libraries.functions.security_commons import build_expectations, \ - cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties + cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties, \ + FILE_TYPE_XML from hdfs_snamenode import snamenode from hdfs import hdfs @@ -88,7 +89,8 @@ class SNameNode(Script): hdfs_expectations.update(hdfs_site_expectations) security_params = get_params_from_filesystem(status_params.hadoop_conf_dir, - ['core-site.xml', 'hdfs-site.xml']) + {'core-site.xml': FILE_TYPE_XML, + 'hdfs-site.xml': FILE_TYPE_XML}) result_issues = validate_security_config_properties(security_params, hdfs_expectations) if not result_issues: # If all validations passed successfully try: @@ -107,18 +109,16 @@ class SNameNode(Script): security_params['hdfs-site'][ 'dfs.secondary.namenode.kerberos.principal'], status_params.hostname, - status_params.tmp_dir, - 30) + status_params.tmp_dir) self.put_structured_out({"securityState": "SECURED_KERBEROS"}) except Exception as e: self.put_structured_out({"securityState": "ERROR"}) self.put_structured_out({"securityStateErrorInfo": str(e)}) else: - issues = "" + issues = [] for cf in result_issues: - issues += "Configuration file " + cf + " did not pass the validation. Reason: " + \ - result_issues[cf] - self.put_structured_out({"securityIssuesFound": issues}) + issues.append("Configuration file %s did not pass the validation. Reason: %s" % (cf, result_issues[cf])) + self.put_structured_out({"securityIssuesFound": ". ".join(issues)}) self.put_structured_out({"securityState": "UNSECURED"}) http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/zkfc_slave.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/zkfc_slave.py b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/zkfc_slave.py index 0e5d666..c5b0b1b 100644 --- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/zkfc_slave.py +++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/zkfc_slave.py @@ -20,7 +20,8 @@ limitations under the License. from resource_management import * from resource_management.libraries.functions.check_process_status import check_process_status from resource_management.libraries.functions.security_commons import build_expectations, \ - cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties + cached_kinit_executor, get_params_from_filesystem, validate_security_config_properties, \ + FILE_TYPE_XML import utils # this is needed to avoid a circular dependency since utils.py calls this class from hdfs import hdfs @@ -90,7 +91,8 @@ class ZkfcSlave(Script): hdfs_expectations = {} hdfs_expectations.update(core_site_expectations) - security_params = get_params_from_filesystem(status_params.hadoop_conf_dir, ['core-site.xml']) + security_params = get_params_from_filesystem(status_params.hadoop_conf_dir, + {'core-site.xml': FILE_TYPE_XML}) result_issues = validate_security_config_properties(security_params, hdfs_expectations) if not result_issues: # If all validations passed successfully if status_params.hdfs_user_principal or status_params.hdfs_user_keytab: @@ -100,8 +102,7 @@ class ZkfcSlave(Script): status_params.hdfs_user_keytab, status_params.hdfs_user_principal, status_params.hostname, - status_params.tmp_dir, - 30) + status_params.tmp_dir) self.put_structured_out({"securityState": "SECURED_KERBEROS"}) except Exception as e: self.put_structured_out({"securityState": "ERROR"}) @@ -111,11 +112,10 @@ class ZkfcSlave(Script): {"securityIssuesFound": "hdfs principal and/or keytab file is not specified"}) self.put_structured_out({"securityState": "UNSECURED"}) else: - issues = "" + issues = [] for cf in result_issues: - issues += "Configuration file " + cf + " did not pass the validation. Reason: " + \ - result_issues[cf] - self.put_structured_out({"securityIssuesFound": issues}) + issues.append("Configuration file %s did not pass the validation. Reason: %s" % (cf, result_issues[cf])) + self.put_structured_out({"securityIssuesFound": ". ".join(issues)}) self.put_structured_out({"securityState": "UNSECURED"}) else: self.put_structured_out({"securityState": "UNSECURED"}) http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_client.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_client.py b/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_client.py index 267e1ab..85ffba8 100644 --- a/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_client.py +++ b/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_client.py @@ -18,6 +18,7 @@ See the License for the specific language governing permissions and limitations under the License. ''' +from mock.mock import patch from stacks.utils.RMFTestCase import * @@ -73,3 +74,27 @@ class TestFalconClient(RMFTestCase): owner = 'falcon' ) self.assertNoMoreResources() + + @patch("resource_management.libraries.script.Script.put_structured_out") + def test_security_status(self, put_structured_out_mock): + # Test that function works when is called with correct parameters + self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/falcon_client.py", + classname="FalconClient", + command="security_status", + config_file="secured.json", + hdp_stack_version = self.STACK_VERSION, + target = RMFTestCase.TARGET_COMMON_SERVICES + ) + + put_structured_out_mock.assert_called_with({"securityState": "SECURED_KERBEROS"}) + + # Testing with security_enable = false + self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/falcon_client.py", + classname="FalconClient", + command="security_status", + config_file="default.json", + hdp_stack_version = self.STACK_VERSION, + target = RMFTestCase.TARGET_COMMON_SERVICES + ) + + put_structured_out_mock.assert_called_with({"securityState": "UNSECURED"}) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_server.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_server.py b/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_server.py index 9b8579a..2e37e65 100644 --- a/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_server.py +++ b/ambari-server/src/test/python/stacks/2.1/FALCON/test_falcon_server.py @@ -172,4 +172,110 @@ class TestFalconServer(RMFTestCase): # 4 calls to tarfile.open (2 directories * read + write) self.assertTrue(tarfile_open_mock.called) self.assertEqual(tarfile_open_mock.call_count,4) + + @patch("resource_management.libraries.functions.security_commons.build_expectations") + @patch("resource_management.libraries.functions.security_commons.get_params_from_filesystem") + @patch("resource_management.libraries.functions.security_commons.validate_security_config_properties") + @patch("resource_management.libraries.functions.security_commons.cached_kinit_executor") + @patch("resource_management.libraries.script.Script.put_structured_out") + def test_security_status(self, put_structured_out_mock, cached_kinit_executor_mock, validate_security_config_mock, get_params_mock, build_exp_mock): + # Test that function works when is called with correct parameters + import status_params + security_params = {} + security_params['startup'] = {} + security_params['startup']['*.falcon.service.authentication.kerberos.keytab'] = 'path/to/falcon/service/keytab' + security_params['startup']['*.falcon.service.authentication.kerberos.principal'] = 'falcon_service_keytab' + security_params['startup']['*.falcon.http.authentication.kerberos.keytab'] = 'path/to/falcon/http/keytab' + security_params['startup']['*.falcon.http.authentication.kerberos.principal'] = 'falcon_http_principal' + result_issues = [] + props_value_check = {"*.falcon.authentication.type": "kerberos", + "*.falcon.http.authentication.type": "kerberos"} + props_empty_check = ["*.falcon.service.authentication.kerberos.principal", + "*.falcon.service.authentication.kerberos.keytab", + "*.falcon.http.authentication.kerberos.principal", + "*.falcon.http.authentication.kerberos.keytab"] + + props_read_check = ["*.falcon.service.authentication.kerberos.keytab", + "*.falcon.http.authentication.kerberos.keytab"] + + get_params_mock.return_value = security_params + validate_security_config_mock.return_value = result_issues + + self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/falcon_server.py", + classname="FalconServer", + command="security_status", + config_file="secured.json", + hdp_stack_version = self.STACK_VERSION, + target = RMFTestCase.TARGET_COMMON_SERVICES + ) + + get_params_mock.assert_called_with('/etc/falcon/conf', {'startup.properties': 'PROPERTIES'}) + build_exp_mock.assert_called_with('startup', props_value_check, props_empty_check, props_read_check) + put_structured_out_mock.assert_called_with({"securityState": "SECURED_KERBEROS"}) + self.assertTrue(cached_kinit_executor_mock.call_count, 2) + cached_kinit_executor_mock.assert_called_with(status_params.kinit_path_local, + status_params.falcon_user, + security_params['startup']['*.falcon.http.authentication.kerberos.keytab'], + security_params['startup']['*.falcon.http.authentication.kerberos.principal'], + status_params.hostname, + status_params.tmp_dir) + + # Testing that the exception throw by cached_executor is caught + cached_kinit_executor_mock.reset_mock() + cached_kinit_executor_mock.side_effect = Exception("Invalid command") + + try: + self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/falcon_server.py", + classname="FalconServer", + command="security_status", + config_file="secured.json", + hdp_stack_version = self.STACK_VERSION, + target = RMFTestCase.TARGET_COMMON_SERVICES + ) + except: + self.assertTrue(True) + + # Testing with a security_params which doesn't contains startup + empty_security_params = {} + cached_kinit_executor_mock.reset_mock() + get_params_mock.reset_mock() + put_structured_out_mock.reset_mock() + get_params_mock.return_value = empty_security_params + + self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/falcon_server.py", + classname="FalconServer", + command="security_status", + config_file="secured.json", + hdp_stack_version = self.STACK_VERSION, + target = RMFTestCase.TARGET_COMMON_SERVICES + ) + put_structured_out_mock.assert_called_with({"securityIssuesFound": "Keytab file or principal are not set property."}) + + # Testing with not empty result_issues + result_issues_with_params = {} + result_issues_with_params['startup']="Something bad happened" + + validate_security_config_mock.reset_mock() + get_params_mock.reset_mock() + validate_security_config_mock.return_value = result_issues_with_params + get_params_mock.return_value = security_params + + self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/falcon_server.py", + classname="FalconServer", + command="security_status", + config_file="secured.json", + hdp_stack_version = self.STACK_VERSION, + target = RMFTestCase.TARGET_COMMON_SERVICES + ) + put_structured_out_mock.assert_called_with({"securityState": "UNSECURED"}) + + # Testing with security_enable = false + self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/falcon_server.py", + classname="FalconServer", + command="security_status", + config_file="default.json", + hdp_stack_version = self.STACK_VERSION, + target = RMFTestCase.TARGET_COMMON_SERVICES + ) + put_structured_out_mock.assert_called_with({"securityState": "UNSECURED"}) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/e3c9aa7a/ambari-server/src/test/python/stacks/utils/RMFTestCase.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/stacks/utils/RMFTestCase.py b/ambari-server/src/test/python/stacks/utils/RMFTestCase.py index b8e819d..4e48f11 100644 --- a/ambari-server/src/test/python/stacks/utils/RMFTestCase.py +++ b/ambari-server/src/test/python/stacks/utils/RMFTestCase.py @@ -116,8 +116,12 @@ class RMFTestCase(TestCase): # Reload params import, otherwise it won't change properties during next import if 'params' in sys.modules: - del(sys.modules["params"]) - + del(sys.modules["params"]) + + # Reload status_params import, otherwise it won't change properties during next import + if 'status_params' in sys.modules: + del(sys.modules["status_params"]) + # run with Environment(basedir, test_mode=True) as RMFTestCase.env: with patch('resource_management.core.shell.checked_call', return_value=shell_mock_value): # we must always mock any shell calls