| """ |
| 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. |
| """ |
| |
| import os, imp, json |
| from unittest import TestCase |
| from mock.mock import patch, MagicMock |
| |
| |
| class TestHAWQ200ServiceAdvisor(TestCase): |
| |
| testDirectory = os.path.dirname(os.path.abspath(__file__)) |
| serviceAdvisorPath = '../../../../main/resources/common-services/HAWQ/2.0.0/service_advisor.py' |
| hawq200ServiceAdvisorPath = os.path.join(testDirectory, serviceAdvisorPath) |
| with open(hawq200ServiceAdvisorPath, 'rb') as fp: |
| service_advisor_impl = imp.load_module('service_advisor_impl', fp, hawq200ServiceAdvisorPath, ('.py', 'rb', imp.PY_SOURCE)) |
| |
| def setUp(self): |
| serviceAdvisorClass = getattr(self.service_advisor_impl, 'HAWQ200ServiceAdvisor') |
| self.serviceAdvisor = serviceAdvisorClass() |
| |
| def fqdn_mock_result(value=None): |
| return 'c6401.ambari.apache.org' if value is None else value |
| |
| def load_json(self, filename): |
| file = os.path.join(self.testDirectory, "../configs", filename) |
| with open(file, 'rb') as f: |
| data = json.load(f) |
| return data |
| |
| def prepareHosts(self, hostsNames): |
| hosts = { "items": [] } |
| for hostName in hostsNames: |
| nextHost = {"Hosts":{"host_name" : hostName}} |
| hosts["items"].append(nextHost) |
| return hosts |
| |
| def getHosts(self, componentsList, componentName): |
| hosts = [component["StackServiceComponents"] for component in componentsList |
| if component["StackServiceComponents"]["component_name"] == componentName] |
| return hosts[0] if len(hosts) > 0 else [] |
| |
| def getHostsFromRecommendations(self, recommendations, componentName): |
| hostGroups = [hostgroup["name"] for hostgroup in recommendations["blueprint"]["host_groups"] if |
| {"name": componentName} in hostgroup["components"]] |
| return set([host["fqdn"] for hostgroup in recommendations["blueprint_cluster_binding"]["host_groups"] if |
| hostgroup["name"] in hostGroups for host in hostgroup["hosts"]]) |
| |
| def getComponentsListFromServices(self, services): |
| componentsListList = [service["components"] for service in services["services"]] |
| return [item for sublist in componentsListList for item in sublist] |
| |
| def getComponentsFromServices(self, services): |
| componentsList = self.getComponentsListFromServices(services) |
| return [component["StackServiceComponents"]["component_name"] for component in componentsList] |
| |
| def getComponentsFromRecommendations(self, recommendations): |
| componentsListList = [hostgroup["components"] for hostgroup in |
| recommendations["recommendations"]["blueprint"]["host_groups"]] |
| return [item["name"] for sublist in componentsListList for item in sublist] |
| |
| def insertHAWQServiceAdvisorInfo(self, services): |
| for service in services["services"]: |
| if service["StackServices"]["service_name"] == 'HAWQ': |
| service["StackServices"]["advisor_name"] = "HAWQ200ServiceAdvisor" |
| service["StackServices"]["advisor_path"] = self.hawq200ServiceAdvisorPath |
| |
| def getDesiredHDFSSiteValues(self, is_secure): |
| hdfs_site_desired_values = { |
| "dfs.allow.truncate" : "true", |
| "dfs.block.access.token.enable" : str(is_secure).lower(), |
| "dfs.block.local-path-access.user" : "gpadmin", |
| "dfs.client.read.shortcircuit" : "true", |
| "dfs.client.use.legacy.blockreader.local" : "false", |
| "dfs.datanode.data.dir.perm" : "750", |
| "dfs.datanode.handler.count" : "60", |
| "dfs.datanode.max.transfer.threads" : "40960", |
| "dfs.namenode.accesstime.precision" : "0", |
| "dfs.support.append" : "true" |
| } |
| return hdfs_site_desired_values |
| |
| @patch("socket.getfqdn") |
| def test_getHostsForMasterComponent(self, getfqdn_mock): |
| getfqdn_mock.return_value = "c6401.ambari.apache.org" |
| |
| services = { |
| "services": [ |
| { |
| "StackServices": { |
| "service_name": "HAWQ" |
| }, |
| "components": [ |
| { |
| "StackServiceComponents": { |
| "component_name": "HAWQMASTER", |
| "hostnames": [ |
| "c6403.ambari.apache.org" |
| ] |
| } |
| }, |
| { |
| "StackServiceComponents": { |
| "component_name": "HAWQSTANDBY", |
| "hostnames": [ |
| ] |
| } |
| } |
| ] |
| } |
| ] |
| } |
| |
| hostsList = ["c6401.ambari.apache.org", "c6402.ambari.apache.org", "c6403.ambari.apache.org", "c6404.ambari.apache.org"] |
| |
| component = { |
| "StackServiceComponents": { |
| "component_name": "HAWQSTANDBY" |
| } |
| } |
| |
| # Case 1: |
| # Ambari Server is placed on c6401.ambari.apache.org |
| # HAWQMASTER is placed on c6403.ambari.apache.org |
| # There are 4 available hosts in the cluster |
| # Recommend HAWQSTANDBY on next available host, c6402.ambari.apache.org |
| standbyHosts = self.serviceAdvisor.getHostsForMasterComponent(services, None, component, hostsList) |
| self.assertEquals(standbyHosts, ["c6402.ambari.apache.org"]) |
| |
| # Case 2: |
| # Ambari Server is placed on c6401.ambari.apache.org |
| # HAWQMASTER is placed on c6402.ambari.apache.org |
| # There are 4 available hosts in the cluster |
| # Recommend HAWQSTANDBY on next available host, c6403.ambari.apache.org |
| services["services"][0]["components"][0]["StackServiceComponents"]["hostnames"] = ["c6402.ambari.apache.org"] |
| standbyHosts = self.serviceAdvisor.getHostsForMasterComponent(services, None, component, hostsList) |
| self.assertEquals(standbyHosts, ["c6403.ambari.apache.org"]) |
| |
| # Case 3: |
| # Ambari Server is placed on c6401.ambari.apache.org |
| # HAWQMASTER is placed on c6402.ambari.apache.org |
| # There are 2 available hosts in the cluster |
| # Recommend HAWQSTANDBY on a host which does not have HAWQMASTER, c6401.ambari.apache.org |
| hostsList = ["c6401.ambari.apache.org", "c6402.ambari.apache.org"] |
| standbyHosts = self.serviceAdvisor.getHostsForMasterComponent(services, None, component, hostsList) |
| self.assertEquals(standbyHosts, ["c6401.ambari.apache.org"]) |
| |
| # Case 4: |
| # Ambari Server is placed on c6401.ambari.apache.org |
| # HAWQMASTER is placed on c6401.ambari.apache.org |
| # There is 1 available host in the cluster |
| # Do not recommend HAWQSTANDBY on a single node cluster |
| hostsList = ["c6401.ambari.apache.org"] |
| services["services"][0]["components"][0]["StackServiceComponents"]["hostnames"] = ["c6401.ambari.apache.org"] |
| standbyHosts = self.serviceAdvisor.getHostsForMasterComponent(services, None, component, hostsList) |
| self.assertEquals(standbyHosts, []) |
| |
| # Case 5: |
| # Ambari Server is placed on c6401.ambari.apache.org |
| # HAWQMASTER is placed on c6402.ambari.apache.org |
| # HAWQSTANDBY is placed on c6401.ambari.apache.org |
| # There are 3 available host in the cluster |
| # Do not change HAWQSTANDBY host according to recommendation since HAWQSTANDBY has already been assigned a host |
| hostsList = ["c6401.ambari.apache.org", "c6402.ambari.apache.org", "c6403.ambari.apache.org"] |
| services["services"][0]["components"][0]["StackServiceComponents"]["hostnames"] = ["c6402.ambari.apache.org"] |
| services["services"][0]["components"][1]["StackServiceComponents"]["hostnames"] = ["c6401.ambari.apache.org"] |
| standbyHosts = self.serviceAdvisor.getHostsForMasterComponent(services, None, component, hostsList) |
| self.assertEquals(standbyHosts, ["c6401.ambari.apache.org"]) |
| |
| |
| def test_getServiceConfigurationRecommendations(self): |
| |
| configurations = { |
| "hawq-sysctl-env": { |
| "properties": { |
| "vm.overcommit_memory": 1, |
| "vm.overcommit_ratio": 50 |
| } |
| }, |
| "hawq-site": { |
| "properties": { |
| "hawq_rm_memory_limit_perseg": "65535MB", |
| "hawq_rm_nvcore_limit_perseg": "16", |
| "hawq_global_rm_type": "yarn", |
| "default_hash_table_bucket_number": 18 |
| } |
| }, |
| "hdfs-site": { |
| "properties": { |
| } |
| }, |
| "core-site": { |
| "properties": { |
| } |
| }, |
| "cluster-env": { |
| "properties": { |
| } |
| } |
| } |
| |
| services = { |
| "services": [ |
| { |
| "StackServices": { |
| "service_name": "HAWQ", |
| "service_version": "2.0", |
| "stack_name": "HDP", |
| "stack_version": "2.3" |
| }, |
| "components": [ |
| { |
| "StackServiceComponents": { |
| "component_name": "HAWQMASTER", |
| "hostnames": [ |
| "c6401.ambari.apache.org" |
| ] |
| } |
| }, |
| { |
| "StackServiceComponents": { |
| "component_name": "HAWQSEGMENT", |
| "hostnames": [ |
| "c6402.ambari.apache.org", |
| "c6404.ambari.apache.org", |
| ] |
| } |
| } |
| ] |
| } |
| ], |
| "configurations": configurations |
| } |
| |
| hosts = { |
| "items": [ |
| { |
| "Hosts": { |
| "host_name": "c6401.ambari.apache.org", |
| "cpu_count" : 2, |
| "total_mem": 33554432 |
| } |
| }, |
| { |
| "Hosts": { |
| "host_name": "c6402.ambari.apache.org", |
| "cpu_count" : 4, |
| "total_mem": 33554433 |
| } |
| }, |
| { |
| "Hosts": { |
| "host_name": "c6403.ambari.apache.org", |
| "cpu_count" : 1, |
| "total_mem": 33554434 |
| } |
| }, |
| { |
| "Hosts": { |
| "host_name": "c6404.ambari.apache.org", |
| "cpu_count" : 2, |
| "total_mem": 33554435 |
| } |
| } |
| ] |
| } |
| |
| ## Test that HDFS parameters required by HAWQ are recommended |
| configurations["cluster-env"]["properties"]["security_enabled"]="false" |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| hdfs_site_desired_values = self.getDesiredHDFSSiteValues(False) |
| for property, value in hdfs_site_desired_values.iteritems(): |
| self.assertEquals(configurations["hdfs-site"]["properties"][property], value) |
| self.assertEquals(configurations["core-site"]["properties"]["ipc.server.listen.queue.size"], "3300") |
| |
| # Kerberos causes 1 property to be recommended differently |
| configurations["cluster-env"]["properties"]["security_enabled"]="true" |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEquals(configurations["hdfs-site"]["properties"]["dfs.block.access.token.enable"], "true") |
| |
| |
| ## Test if hawq_rm_nvcore_limit_perseg is set correctly |
| |
| # Case 1: |
| # HAWQ Hosts Core Count: c6401.ambari.apache.org - 2, c6402.ambari.apache.org - 4, c6404.ambari.apache.org - 2 |
| # hawq_global_rm_type: yarn |
| # Non HAWQ Hosts Core Count: c6401.ambari.apache.org - 1 |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEquals(configurations["hawq-site"]["properties"]["hawq_rm_nvcore_limit_perseg"], "2") |
| |
| ## Test if vm.overcommit_memory is set correctly |
| |
| # Case 1: All machines have total_mem above 32GB (total_mem >= 33554432) |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEquals(configurations["hawq-sysctl-env"]["properties"]["vm.overcommit_memory"], "2") |
| |
| # Case 2: One machine has total_mem below 32GB |
| hosts["items"][0]["Hosts"]["total_mem"] = 33554431 |
| services["configurations"]["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"] = "65535MB" |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEquals(configurations["hawq-sysctl-env"]["properties"]["vm.overcommit_memory"], "1") |
| |
| ## Test if hawq_rm_memory_limit_perseg is set correctly |
| |
| # Case 1: Minimum host memory is ~ 2 GB (2048MB), recommended val must be .75% of 2GB as vm.overcommit_memory = 1 and in MB |
| hosts["items"][0]["Hosts"]["total_mem"] = 2097152 |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"], "1536MB") |
| |
| # Case 2: Minimum host memory is ~ 16 GB, recommended val must be .75% of 16GB as vm.overcommit_memory = 1 and in GB |
| hosts["items"][0]["Hosts"]["total_mem"] = 16777216 |
| hosts["items"][1]["Hosts"]["total_mem"] = 26777216 |
| hosts["items"][3]["Hosts"]["total_mem"] = 36777216 |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"], "12GB") |
| |
| # Case 2: Minimum host memory is ~ 64 GB, recommended val must be .75% of 32GB as vm.overcommit_memory = 2 and in GB |
| hosts["items"][0]["Hosts"]["total_mem"] = 67108864 |
| hosts["items"][1]["Hosts"]["total_mem"] = 77108864 |
| hosts["items"][3]["Hosts"]["total_mem"] = 87108864 |
| services["configurations"]["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"] = "65535MB" |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"], "24GB") |
| |
| # Case 4: Minimum host memory is ~ 512 GB, recommended val must be .85% of 256GB as vm.overcommit_memory = 2 and in GB |
| hosts["items"][0]["Hosts"]["total_mem"] = 536870912 |
| hosts["items"][1]["Hosts"]["total_mem"] = 636870912 |
| hosts["items"][3]["Hosts"]["total_mem"] = 736870912 |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEquals(configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"], "218GB") |
| |
| # Case 5: Minimum host memory is ~ 1024 GB, recommended val must be .95% of 512GB as vm.overcommit_memory = 2 and in GB |
| hosts["items"][0]["Hosts"]["total_mem"] = 1073741824 |
| hosts["items"][1]["Hosts"]["total_mem"] = 2073741824 |
| hosts["items"][3]["Hosts"]["total_mem"] = 3073741824 |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"], "436GB") |
| |
| # Case 6: Minimum host memory is ~ 1024 GB, vm.overcommit_ratio = 75, vm.overcommit_memory = 2 |
| # recommended val must be .95% of (1024*75)/100 and in GB |
| hosts["items"][0]["Hosts"]["total_mem"] = 1073741824 |
| hosts["items"][1]["Hosts"]["total_mem"] = 2073741824 |
| hosts["items"][3]["Hosts"]["total_mem"] = 3073741824 |
| services["configurations"]["hawq-sysctl-env"]["properties"]["vm.overcommit_ratio"] = 75 |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"], "730GB") |
| |
| ## Test if default_hash_table_bucket_number and hawq_rm_nvseg_perquery_perseg_limit are set correctly based on low hawq_rm_memory_limit_perseg |
| |
| # Case 1: When hawq_rm_memory_limit_perseg is between 1GB and 2GB |
| # Set hawq_rm_nvseg_perquery_perseg_limit to 4 and default_hash_table_bucket_number as hawq_rm_nvseg_perquery_perseg_limit * numSegments |
| hosts["items"][0]["Hosts"]["total_mem"] = 2097152 |
| hosts["items"][1]["Hosts"]["total_mem"] = 2097152 |
| hosts["items"][3]["Hosts"]["total_mem"] = 2097152 |
| services["configurations"]["hawq-site"]["properties"]["hawq_global_rm_type"] = "none" |
| services["configurations"]["hawq-site"]["properties"]["hawq_rm_nvseg_perquery_limit"] = "512" |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"], "1152MB") |
| self.assertEqual(configurations["hawq-site"]["properties"]["default_hash_table_bucket_number"], "8") |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_nvseg_perquery_perseg_limit"], "4") |
| |
| # Case 2: When hawq_rm_memory_limit_perseg > 2GB |
| # Set hawq_rm_nvseg_perquery_perseg_limit to 6 and default_hash_table_bucket_number as hawq_rm_nvseg_perquery_perseg_limit * numSegments |
| hosts["items"][0]["Hosts"]["total_mem"] = 1073741824 |
| hosts["items"][1]["Hosts"]["total_mem"] = 2073741824 |
| hosts["items"][3]["Hosts"]["total_mem"] = 3073741824 |
| services["configurations"]["hawq-site"]["properties"]["hawq_rm_nvseg_perquery_limit"] = "512" |
| services["configurations"]["hawq-site"]["properties"]["hawq_rm_nvseg_perquery_perseg_limit"] = "4" |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"], "730GB") |
| self.assertEqual(configurations["hawq-site"]["properties"]["default_hash_table_bucket_number"], "12") |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_nvseg_perquery_perseg_limit"], "6") |
| |
| # Case 3: When hawq_rm_memory_limit_perseg > 2GB |
| # Set hawq_rm_nvseg_perquery_perseg_limit to 8 and default_hash_table_bucket_number as hawq_rm_nvseg_perquery_perseg_limit * numSegments |
| hosts["items"][0]["Hosts"]["total_mem"] = 1073741824 |
| hosts["items"][1]["Hosts"]["total_mem"] = 2073741824 |
| hosts["items"][3]["Hosts"]["total_mem"] = 3073741824 |
| services["configurations"]["hawq-site"]["properties"]["hawq_rm_nvseg_perquery_limit"] = "512" |
| services["configurations"]["hawq-site"]["properties"]["hawq_rm_nvseg_perquery_perseg_limit"] = "8" |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"], "730GB") |
| self.assertEqual(configurations["hawq-site"]["properties"]["default_hash_table_bucket_number"], "12") |
| self.assertEqual(configurations["hawq-site"]["properties"]["hawq_rm_nvseg_perquery_perseg_limit"], "8") |
| |
| ## Test if the properties are set to visible / invisible based on the value of hawq_global_rm_type |
| |
| # Case 1: When hawq_global_rm_type is yarn |
| services["configurations"]["hawq-site"]["properties"]["hawq_global_rm_type"] = "yarn" |
| properties_visible_status = {"hawq_rm_memory_limit_perseg": "false", |
| "hawq_rm_nvcore_limit_perseg": "false", |
| "hawq_rm_yarn_app_name":"true", |
| "hawq_rm_yarn_queue_name": "true", |
| "hawq_rm_yarn_scheduler_address": "true", |
| "hawq_rm_yarn_address": "true"} |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| for property, status in properties_visible_status.iteritems(): |
| self.assertEqual(configurations["hawq-site"]["property_attributes"][property]["visible"], status) |
| |
| # Case 2: When hawq_global_rm_type is none |
| services["configurations"]["hawq-site"]["properties"]["hawq_global_rm_type"] = "none" |
| properties_visible_status = {"hawq_rm_memory_limit_perseg": "true", |
| "hawq_rm_nvcore_limit_perseg": "true", |
| "hawq_rm_yarn_app_name": "false", |
| "hawq_rm_yarn_queue_name": "false", |
| "hawq_rm_yarn_scheduler_address": "false", |
| "hawq_rm_yarn_address": "false"} |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| for property, status in properties_visible_status.iteritems(): |
| self.assertEqual(configurations["hawq-site"]["property_attributes"][property]["visible"], status) |
| |
| ## Test if vm.overcommit_ratio is set to visible / invisible based on the value of vm.overcommit_memory |
| |
| # Case 1: vm.overcommit_ratio should be invisible when overcommit_memory is set as 0 |
| services["configurations"]["hawq-sysctl-env"]["properties"]["vm.overcommit_memory"] = 0 |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-sysctl-env"]["property_attributes"]["vm.overcommit_ratio"]["visible"], "false") |
| |
| # Case 2: vm.overcommit_ratio should be invisible when overcommit_memory is set as 1 |
| services["configurations"]["hawq-sysctl-env"]["properties"]["vm.overcommit_memory"] = 1 |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-sysctl-env"]["property_attributes"]["vm.overcommit_ratio"]["visible"], "false") |
| |
| # Case 3: vm.overcommit_ratio should be visible when overcommit_memory is set as 2 |
| services["configurations"]["hawq-sysctl-env"]["properties"]["vm.overcommit_memory"] = 2 |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, None, services, hosts) |
| self.assertEqual(configurations["hawq-sysctl-env"]["property_attributes"]["vm.overcommit_ratio"]["visible"], "true") |
| |
| |
| def test_createComponentLayoutRecommendations_hawq_3_Hosts(self): |
| """ Test that HAWQSTANDBY is recommended on a 3-node cluster """ |
| |
| services = self.load_json("services-hawq-3-hosts.json") |
| componentNames = self.getComponentsFromServices(services) |
| self.assertTrue('HAWQSTANDBY' in componentNames) |
| |
| hosts = self.load_json("hosts-3-hosts.json") |
| hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]] |
| self.assertEquals(len(hostsList), 3) |
| |
| self.insertHAWQServiceAdvisorInfo(services) |
| recommendations = self.serviceAdvisor.recommendComponentLayout(services, hosts) |
| recommendedComponents = self.getComponentsFromRecommendations(recommendations) |
| self.assertTrue('HAWQMASTER' in recommendedComponents) |
| self.assertTrue('HAWQSTANDBY' in recommendedComponents) |
| self.assertTrue('HAWQSEGMENT' in recommendedComponents) |
| |
| # make sure master components are not collocated |
| componentsListList = [hostgroup["components"] for hostgroup in |
| recommendations["recommendations"]["blueprint"]["host_groups"]] |
| for sublist in componentsListList: |
| hostComponents = [item["name"] for item in sublist] |
| self.assertFalse({'HAWQMASTER', 'HAWQSTANDBY'}.issubset(hostComponents)) |
| |
| def test_createComponentLayoutRecommendations_hawq_1_Host(self): |
| |
| services = self.load_json("services-hawq-3-hosts.json") |
| componentNames = self.getComponentsFromServices(services) |
| self.assertTrue('HAWQSTANDBY' in componentNames) |
| |
| hosts = self.load_json("hosts-1-host.json") |
| hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]] |
| self.assertEquals(len(hostsList), 1) |
| |
| self.insertHAWQServiceAdvisorInfo(services) |
| recommendations = self.serviceAdvisor.recommendComponentLayout(services, hosts) |
| recommendedComponents = self.getComponentsFromRecommendations(recommendations) |
| self.assertTrue('HAWQMASTER' in recommendedComponents) |
| self.assertFalse('HAWQSTANDBY' in recommendedComponents) |
| self.assertTrue('HAWQSEGMENT' in recommendedComponents) |
| |
| def test_createComponentLayoutRecommendations_no_hawq_3_Hosts(self): |
| """ Test no failures when there are no HAWQ components """ |
| |
| services = self.load_json("services-nohawq-3-hosts.json") |
| componentNames = self.getComponentsFromServices(services) |
| self.assertFalse('HAWQMASTER' in componentNames) |
| self.assertFalse('HAWQSTANDBY' in componentNames) |
| self.assertFalse('HAWQSEGMENT' in componentNames) |
| |
| hosts = self.load_json("hosts-3-hosts.json") |
| hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]] |
| self.assertEquals(len(hostsList), 3) |
| |
| self.insertHAWQServiceAdvisorInfo(services) |
| recommendations = self.serviceAdvisor.recommendComponentLayout(services, hosts) |
| recommendedComponents = self.getComponentsFromRecommendations(recommendations) |
| self.assertFalse('HAWQMASTER' in recommendedComponents) |
| self.assertFalse('HAWQSTANDBY' in recommendedComponents) |
| self.assertFalse('HAWQSEGMENT' in recommendedComponents) |
| |
| def test_createComponentLayoutRecommendations_hawqsegment_cluster_install(self): |
| """ Test that HAWQSEGMENT gets recommended correctly during Cluster Install Wizard, when HAWQ is selected for installation """ |
| |
| hosts = self.prepareHosts(["c6401.ambari.apache.org", "c6402.ambari.apache.org", "c6403.ambari.apache.org"]) |
| services = { |
| "services" : [ |
| { |
| "StackServices" : { |
| "service_name" : "HDFS" |
| }, |
| "components" : [ |
| { |
| "StackServiceComponents" : { |
| "cardinality" : "1+", |
| "component_category" : "SLAVE", |
| "component_name" : "DATANODE", |
| "hostnames" : [] |
| } |
| } |
| ] |
| }, |
| { |
| "StackServices" : { |
| "service_name" : "HAWQ" |
| }, |
| "components" : [ |
| { |
| "StackServiceComponents" : { |
| "cardinality" : "1+", |
| "component_category" : "SLAVE", |
| "component_name" : "HAWQSEGMENT", |
| "hostnames" : [] |
| } |
| } |
| ] |
| } |
| ] |
| } |
| |
| hawqSegmentHosts = {"c6401.ambari.apache.org", "c6402.ambari.apache.org", "c6403.ambari.apache.org"} |
| self.insertHAWQServiceAdvisorInfo(services) |
| recommendations = self.serviceAdvisor.createComponentLayoutRecommendations(services, hosts) |
| hostNames = self.getHostsFromRecommendations(recommendations, "HAWQSEGMENT") |
| self.assertEquals(set(hostNames), hawqSegmentHosts) |
| |
| def test_createComponentLayoutRecommendations_hawqsegment_add_service_wizard_to_be_installed(self): |
| """ Test that HAWQSEGMENT gets recommended correctly during Add Service Wizard, when HAWQ is selected for installation """ |
| |
| hosts = self.prepareHosts(["c6401.ambari.apache.org", "c6402.ambari.apache.org", "c6403.ambari.apache.org"]) |
| services = { |
| "services" : [ |
| { |
| "StackServices" : { |
| "service_name" : "HDFS" |
| }, |
| "components" : [ |
| { |
| "StackServiceComponents" : { |
| "cardinality" : "1+", |
| "component_category" : "SLAVE", |
| "component_name" : "DATANODE", |
| "hostnames" : ["c6401.ambari.apache.org", "c6403.ambari.apache.org"] |
| } |
| } |
| ] |
| }, |
| { |
| "StackServices" : { |
| "service_name" : "HAWQ" |
| }, |
| "components" : [ |
| { |
| "StackServiceComponents" : { |
| "cardinality" : "1+", |
| "component_category" : "SLAVE", |
| "component_name" : "HAWQSEGMENT", |
| "hostnames" : [] |
| } |
| } |
| ] |
| } |
| ] |
| } |
| |
| hawqSegmentHosts = {"c6401.ambari.apache.org", "c6403.ambari.apache.org"} |
| self.insertHAWQServiceAdvisorInfo(services) |
| recommendations = self.serviceAdvisor.createComponentLayoutRecommendations(services, hosts) |
| hostNames = self.getHostsFromRecommendations(recommendations, "HAWQSEGMENT") |
| self.assertEquals(set(hostNames), hawqSegmentHosts) |
| |
| def test_createComponentLayoutRecommendations_hawqsegment_add_service_wizard_already_installed(self): |
| """ Test that HAWQSEGMENT does not get recommended during Add Service Wizard, when HAWQ has already been installed """ |
| |
| hosts = self.prepareHosts(["c6401.ambari.apache.org", "c6402.ambari.apache.org", "c6403.ambari.apache.org"]) |
| services = { |
| "services" : [ |
| { |
| "StackServices" : { |
| "service_name" : "HDFS" |
| }, |
| "components" : [ |
| { |
| "StackServiceComponents" : { |
| "cardinality" : "1+", |
| "component_category" : "SLAVE", |
| "component_name" : "DATANODE", |
| "hostnames" : ["c6401.ambari.apache.org", "c6403.ambari.apache.org"] |
| } |
| } |
| ] |
| }, |
| { |
| "StackServices" : { |
| "service_name" : "HAWQ" |
| }, |
| "components" : [ |
| { |
| "StackServiceComponents" : { |
| "cardinality" : "1+", |
| "component_category" : "SLAVE", |
| "component_name" : "HAWQSEGMENT", |
| "hostnames" : ["c6402.ambari.apache.org"] |
| } |
| } |
| ] |
| }, |
| { |
| "StackServices" : { |
| "service_name" : "PXF" |
| }, |
| "components" : [ |
| { |
| "StackServiceComponents" : { |
| "cardinality" : "1+", |
| "component_category" : "SLAVE", |
| "component_name" : "PXF", |
| "hostnames" : [] |
| } |
| } |
| ] |
| } |
| ] |
| } |
| |
| hawqSegmentHosts = {"c6402.ambari.apache.org"} |
| self.insertHAWQServiceAdvisorInfo(services) |
| recommendations = self.serviceAdvisor.createComponentLayoutRecommendations(services, hosts) |
| hostNames = self.getHostsFromRecommendations(recommendations, "HAWQSEGMENT") |
| self.assertEquals(set(hostNames), hawqSegmentHosts) |
| |
| def test_getComponentLayoutValidations_hawqsegment_not_co_located_with_datanode(self): |
| """ Test validation warning for HAWQ segment not colocated with DATANODE """ |
| |
| services = self.load_json("services-normal-hawq-3-hosts.json") |
| hosts = self.load_json("hosts-3-hosts.json") |
| componentsList = self.getComponentsListFromServices(services) |
| |
| hawqsegmentComponent = self.getHosts(componentsList, "HAWQSEGMENT") |
| hawqsegmentComponent["hostnames"] = ['c6401.ambari.apache.org'] |
| datanodeComponent = self.getHosts(componentsList, "DATANODE") |
| datanodeComponent["hostnames"] = ['c6402.ambari.apache.org'] |
| |
| self.insertHAWQServiceAdvisorInfo(services) |
| validations = self.serviceAdvisor.getComponentLayoutValidations(services, hosts) |
| expected = { |
| 'type': 'host-component', |
| 'level': 'WARN', |
| 'component-name': 'HAWQSEGMENT', |
| 'message': 'HAWQ Segment must be installed on all DataNodes. The following 2 host(s) do not satisfy the colocation recommendation: c6401.ambari.apache.org, c6402.ambari.apache.org', |
| } |
| self.assertEquals(validations[0], expected) |
| |
| datanodeComponent["hostnames"] = ['c6401.ambari.apache.org'] |
| validations = self.serviceAdvisor.getComponentLayoutValidations(services, hosts) |
| self.assertEquals(len(validations), 0) |
| |
| |
| @patch('socket.getfqdn', side_effect=fqdn_mock_result) |
| def test_getComponentLayoutValidations_hawq_3_Hosts(self, socket_mock): |
| """ Test layout validations for HAWQ components on a 3-node cluster """ |
| |
| # case-1: normal placement, no warnings |
| services = self.load_json("services-normal-hawq-3-hosts.json") |
| componentsList = self.getComponentsListFromServices(services) |
| hawqMasterHosts = self.getHosts(componentsList, "HAWQMASTER")["hostnames"] |
| hawqStandbyHosts = self.getHosts(componentsList, "HAWQSTANDBY")["hostnames"] |
| self.assertEquals(len(hawqMasterHosts), 1) |
| self.assertEquals(len(hawqStandbyHosts), 1) |
| self.assertNotEquals(hawqMasterHosts[0], hawqStandbyHosts[0]) |
| |
| hosts = self.load_json("hosts-3-hosts.json") |
| hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]] |
| self.assertEquals(len(hostsList), 3) |
| |
| self.insertHAWQServiceAdvisorInfo(services) |
| validations = self.serviceAdvisor.getComponentLayoutValidations(services, hosts) |
| self.assertEquals(len(validations), 0) |
| |
| # case-2: HAWQ masters are collocated |
| services = self.load_json("services-master_standby_colo-3-hosts.json") |
| componentsList = self.getComponentsListFromServices(services) |
| hawqMasterHosts = self.getHosts(componentsList, "HAWQMASTER")["hostnames"] |
| hawqStandbyHosts = self.getHosts(componentsList, "HAWQSTANDBY")["hostnames"] |
| self.assertEquals(len(hawqMasterHosts), 1) |
| self.assertEquals(len(hawqStandbyHosts), 1) |
| self.assertEquals(hawqMasterHosts[0], hawqStandbyHosts[0]) |
| |
| self.insertHAWQServiceAdvisorInfo(services) |
| validations = self.serviceAdvisor.getComponentLayoutValidations(services, hosts) |
| self.assertEquals(len(validations), 1) |
| expected = { |
| 'component-name': 'HAWQSTANDBY', |
| 'message': 'HAWQ Master and HAWQ Standby Master cannot be deployed on the same host.', |
| 'type': 'host-component', |
| 'host': 'c6403.ambari.apache.org', |
| 'level': 'ERROR' |
| } |
| self.assertEquals(validations[0], expected) |
| |
| # case-3: HAWQ Master and Ambari Server are collocated |
| services = self.load_json("services-master_ambari_colo-3-hosts.json") |
| componentsList = self.getComponentsListFromServices(services) |
| hawqMasterHosts = self.getHosts(componentsList, "HAWQMASTER")["hostnames"] |
| hawqStandbyHosts = self.getHosts(componentsList, "HAWQSTANDBY")["hostnames"] |
| self.assertEquals(len(hawqMasterHosts), 1) |
| self.assertEquals(len(hawqStandbyHosts), 1) |
| self.assertNotEquals(hawqMasterHosts[0], hawqStandbyHosts[0]) |
| self.assertEquals(hawqMasterHosts[0], "c6401.ambari.apache.org") |
| |
| self.insertHAWQServiceAdvisorInfo(services) |
| validations = self.serviceAdvisor.getComponentLayoutValidations(services, hosts) |
| self.assertEquals(len(validations), 1) |
| expected = { |
| 'component-name': 'HAWQMASTER', |
| 'message': 'The default Postgres port (5432) on the Ambari Server conflicts with the default HAWQ Masters port. ' + |
| 'If you are using port 5432 for Postgres, you must either deploy the HAWQ Master on a different host ' + |
| 'or configure a different port for the HAWQ Masters in the HAWQ Configuration page.', |
| 'type': 'host-component', |
| 'host': 'c6401.ambari.apache.org', |
| 'level': 'WARN' |
| } |
| self.assertEquals(validations[0], expected) |
| |
| # case-4: HAWQ Standby and Ambari Server are collocated |
| services = self.load_json("services-standby_ambari_colo-3-hosts.json") |
| componentsList = self.getComponentsListFromServices(services) |
| hawqMasterHosts = self.getHosts(componentsList, "HAWQMASTER")["hostnames"] |
| hawqStandbyHosts = self.getHosts(componentsList, "HAWQSTANDBY")["hostnames"] |
| self.assertEquals(len(hawqMasterHosts), 1) |
| self.assertEquals(len(hawqStandbyHosts), 1) |
| self.assertNotEquals(hawqMasterHosts[0], hawqStandbyHosts[0]) |
| self.assertEquals(hawqStandbyHosts[0], "c6401.ambari.apache.org") |
| |
| self.insertHAWQServiceAdvisorInfo(services) |
| validations = self.serviceAdvisor.getComponentLayoutValidations(services, hosts) |
| self.assertEquals(len(validations), 1) |
| expected = { |
| 'component-name': 'HAWQSTANDBY', |
| 'message': 'The default Postgres port (5432) on the Ambari Server conflicts with the default HAWQ Masters port. ' + |
| 'If you are using port 5432 for Postgres, you must either deploy the HAWQ Standby Master on a different host ' + |
| 'or configure a different port for the HAWQ Masters in the HAWQ Configuration page.', |
| 'type': 'host-component', |
| 'host': 'c6401.ambari.apache.org', |
| 'level': 'WARN' |
| } |
| self.assertEquals(validations[0], expected) |
| |
| |
| @patch('socket.getfqdn', side_effect=fqdn_mock_result) |
| def test_getComponentLayoutValidations_nohawq_3_Hosts(self, socket_mock): |
| """ Test no failures when there are no HAWQ components on a 3-node cluster """ |
| |
| # normal placement, no warnings |
| services = self.load_json("services-normal-nohawq-3-hosts.json") |
| componentsList = self.getComponentsListFromServices(services) |
| hawqMasterHosts = self.getHosts(componentsList, "HAWQMASTER") |
| hawqStandbyHosts = self.getHosts(componentsList, "HAWQSTANDBY") |
| self.assertEquals(len(hawqMasterHosts), 0) |
| self.assertEquals(len(hawqStandbyHosts), 0) |
| |
| hosts = self.load_json("hosts-3-hosts.json") |
| hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]] |
| self.assertEquals(len(hostsList), 3) |
| |
| self.insertHAWQServiceAdvisorInfo(services) |
| validations = self.serviceAdvisor.getComponentLayoutValidations(services, hosts) |
| self.assertEquals(len(validations), 0) |
| |
| def test_recommendHAWQConfigurations(self): |
| |
| hosts = { |
| "items": [ |
| { |
| "Hosts": { |
| "host_name": "c6401.ambari.apache.org", |
| "cpu_count" : 2, |
| "total_mem": 33554432 |
| } |
| }, |
| { |
| "Hosts": { |
| "host_name": "c6402.ambari.apache.org", |
| "cpu_count" : 4, |
| "total_mem": 33554433 |
| } |
| }, |
| { |
| "Hosts": { |
| "host_name": "c6403.ambari.apache.org", |
| "cpu_count" : 1, |
| "total_mem": 33554434 |
| } |
| }, |
| { |
| "Hosts": { |
| "host_name": "c6404.ambari.apache.org", |
| "cpu_count" : 2, |
| "total_mem": 33554435 |
| } |
| } |
| ] |
| } |
| |
| # original cluster data with 3 segments |
| services = self.load_json("services-normal-hawq-3-hosts.json") |
| componentsList = self.getComponentsListFromServices(services) |
| hawqSegmentComponent = self.getHosts(componentsList, "HAWQSEGMENT") |
| |
| # setup default configuration values |
| services["configurations"]["hawq-site"] = { |
| "properties": { |
| "default_hash_table_bucket_number": "24", |
| "hawq_rm_nvseg_perquery_limit": "512", |
| "hawq_rm_yarn_address": "localhost:8032", |
| "hawq_rm_yarn_scheduler_address": "localhost:8030", |
| "hawq_global_rm_type": "none", |
| "hawq_rm_nvseg_perquery_perseg_limit": "6" |
| } |
| } |
| |
| services["configurations"]["hdfs-client"] = {"properties": {"output.replace-datanode-on-failure": "true"}} |
| services["configurations"]["hawq-sysctl-env"] = {"properties": {}} |
| |
| services["configurations"]["yarn-site"] = {"properties": {"yarn.resourcemanager.address": "host1:8050", |
| "yarn.resourcemanager.scheduler.address": "host1:8030"}} |
| services["services"].append({"StackServices" : {"service_name" : "YARN"}, "components":[]}) |
| configurations = {} |
| clusterData = {} |
| self.insertHAWQServiceAdvisorInfo(services) |
| |
| # Test 1 - with 3 segments |
| self.assertEquals(len(hawqSegmentComponent["hostnames"]), 3) |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, clusterData, services, hosts) |
| self.assertEquals(configurations["hawq-site"]["properties"]["default_hash_table_bucket_number"], str(3 * 6)) |
| self.assertEquals(configurations["hdfs-client"]["properties"]["output.replace-datanode-on-failure"], "false") |
| |
| # check derived properties |
| self.assertEquals(configurations["hawq-site"]["properties"]["hawq_rm_yarn_address"], "host1:8050") |
| self.assertEquals(configurations["hawq-site"]["properties"]["hawq_rm_yarn_scheduler_address"], "host1:8030") |
| |
| # Test 2 - with 100 segments |
| hawqSegmentComponent["hostnames"] = ["host" + str(i) for i in range(100)] |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, clusterData, services, hosts) |
| self.assertEquals(configurations["hawq-site"]["properties"]["default_hash_table_bucket_number"], str(100 * 5)) |
| self.assertEquals(configurations["hdfs-client"]["properties"]["output.replace-datanode-on-failure"], "true") |
| |
| # Test 3 - with 512 segments |
| hawqSegmentComponent["hostnames"] = ["host" + str(i) for i in range(512)] |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, clusterData, services, hosts) |
| self.assertEquals(configurations["hawq-site"]["properties"]["default_hash_table_bucket_number"], "512") |
| self.assertEquals(configurations["hdfs-client"]["properties"]["output.replace-datanode-on-failure"], "true") |
| |
| # Test 4 - with 513 segments |
| hawqSegmentComponent["hostnames"] = ["host" + str(i) for i in range(513)] |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, clusterData, services, hosts) |
| self.assertEquals(configurations["hawq-site"]["properties"]["default_hash_table_bucket_number"], "512") |
| self.assertEquals(configurations["hdfs-client"]["properties"]["output.replace-datanode-on-failure"], "true") |
| |
| # Test 5 - with no segments |
| configurations = {} |
| services["configurations"]["hawq-site"] = {"properties":{"hawq_global_rm_type": "none"}} |
| hawqSegmentComponent["hostnames"] = [] |
| self.serviceAdvisor.getServiceConfigurationRecommendations(configurations, clusterData, services, hosts) |
| self.assertEquals(configurations["hdfs-client"]["properties"]["output.replace-datanode-on-failure"], "false") |
| self.assertTrue("default_hash_table_bucket_number" not in configurations["hawq-site"]) |
| |
| |
| def test_validateHAWQSiteConfigurations(self): |
| services = self.load_json("services-hawq-3-hosts.json") |
| # setup default configuration values |
| # Test hawq_rm_yarn_address and hawq_rm_scheduler_address are set correctly |
| configurations = services["configurations"] |
| configurations["hawq-site"] = {"properties": {"hawq_rm_yarn_address": "localhost:8032", |
| "hawq_rm_yarn_scheduler_address": "localhost:8030"}} |
| configurations["yarn-site"] = {"properties": {"yarn.resourcemanager.address": "host1:8050", |
| "yarn.resourcemanager.scheduler.address": "host1:8030"}} |
| services["services"].append({"StackServices" : {"service_name" : "YARN"}, "components":[]}) |
| properties = configurations["hawq-site"]["properties"] |
| defaults = {} |
| hosts = {} |
| |
| expected_warnings = { |
| 'hawq_rm_yarn_address': { |
| 'config-type': 'hawq-site', |
| 'message': 'Expected value: host1:8050 (this property should have the same value as the property yarn.resourcemanager.address in yarn-site)', |
| 'type': 'configuration', |
| 'config-name': 'hawq_rm_yarn_address', |
| 'level': 'WARN' |
| }, |
| 'hawq_rm_yarn_scheduler_address': { |
| 'config-type': 'hawq-site', |
| 'message': 'Expected value: host1:8030 (this property should have the same value as the property yarn.resourcemanager.scheduler.address in yarn-site)', |
| 'type': 'configuration', |
| 'config-name': 'hawq_rm_yarn_scheduler_address', |
| 'level': 'WARN' |
| } |
| } |
| |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| problems_dict = {} |
| for problem in problems: |
| problems_dict[problem['config-name']] = problem |
| self.assertEqual(len(problems), 2) |
| self.assertEqual(problems_dict, expected_warnings) |
| |
| # Test hawq_master_directory multiple directories validation |
| configurations["hawq-site"] = {"properties": {"hawq_master_directory": "/data/hawq/master", |
| "hawq_segment_directory": "/data/hawq/segment"}} |
| properties = configurations["hawq-site"]["properties"] |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| problems_dict = {} |
| self.assertEqual(len(problems), 0) |
| expected_warnings = {} |
| self.assertEqual(problems_dict, expected_warnings) |
| |
| configurations["hawq-site"] = {"properties": {"hawq_master_directory": "/data/hawq/master1,/data/hawq/master2", |
| "hawq_segment_directory": "/data/hawq/segment1 /data/hawq/segment2"}} |
| properties = configurations["hawq-site"]["properties"] |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| problems_dict = {} |
| for problem in problems: |
| problems_dict[problem['config-name']] = problem |
| self.assertEqual(len(problems), 2) |
| expected_warnings = { |
| 'hawq_master_directory': { |
| 'config-type': 'hawq-site', |
| 'message': 'Multiple directories for HAWQ Master directory are not allowed.', |
| 'type': 'configuration', |
| 'config-name': 'hawq_master_directory', |
| 'level': 'ERROR' |
| }, |
| 'hawq_segment_directory': { |
| 'config-type': 'hawq-site', |
| 'message': 'Multiple directories for HAWQ Segment directory are not allowed.', |
| 'type': 'configuration', |
| 'config-name': 'hawq_segment_directory', |
| 'level': 'ERROR' |
| } |
| } |
| self.assertEqual(problems_dict, expected_warnings) |
| |
| # Test hawq_global_rm_type validation |
| services = { |
| "services" : [ |
| { |
| "StackServices" : { |
| "service_name" : "HAWQ" |
| }, |
| "components": [] |
| } ], |
| "configurations": |
| { |
| "hawq-site": { |
| "properties": { |
| "hawq_global_rm_type": "yarn" |
| } |
| } |
| } |
| } |
| properties = services["configurations"]["hawq-site"]["properties"] |
| |
| # case 1: hawq_global_rm_type is set as yarn, but YARN service is not installed. Validation error expected. |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, services["configurations"], services, hosts) |
| self.assertEqual(len(problems), 1) |
| expected = { |
| "config-type": "hawq-site", |
| "message": "hawq_global_rm_type must be set to none if YARN service is not installed", |
| "type": "configuration", |
| "config-name": "hawq_global_rm_type", |
| "level": "ERROR" |
| } |
| self.assertEqual(problems[0], expected) |
| |
| # case 2: hawq_global_rm_type is set as yarn, and YARN service is installed. No validation errors expected. |
| services["services"].append({"StackServices" : {"service_name" : "YARN"}, "components":[]}) |
| |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, services["configurations"], services, hosts) |
| self.assertEqual(len(problems), 0) |
| |
| # Test HAWQ Master port conflict with Ambari Server Postgres port |
| |
| # case 1: HAWQ Master is placed on Ambari Server and HAWQ Master port is same as Ambari Server Postgres Port |
| self.serviceAdvisor.isHawqMasterComponentOnAmbariServer = MagicMock(return_value=True) |
| configurations = { |
| "hawq-site": { |
| "properties": |
| {"hawq_master_address_port": "5432"} |
| } |
| } |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| expected = { |
| "config-name": "hawq_master_address_port", |
| "config-type": "hawq-site", |
| "level": "WARN", |
| "message": "The default Postgres port (5432) on the Ambari Server conflicts with the default HAWQ Masters port. " |
| "If you are using port 5432 for Postgres, you must either deploy the HAWQ Masters on a different host " |
| "or configure a different port for the HAWQ Masters in the HAWQ Configuration page.", |
| "type": "configuration"} |
| self.assertEqual(problems[0], expected) |
| |
| # case 2: HAWQ Master is placed on Ambari Server and HAWQ Master port is different from Ambari Server Postgres Port |
| self.serviceAdvisor.isHawqMasterComponentOnAmbariServer = MagicMock(return_value=True) |
| configurations["hawq-site"]["properties"]["hawq_master_address_port"] = "10432" |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 0) |
| |
| # case 3: HAWQ Master is not placed on Ambari Server and HAWQ Master port is same as Ambari Server Postgres Port |
| self.serviceAdvisor.isHawqMasterComponentOnAmbariServer = MagicMock(return_value=False) |
| configurations["hawq-site"]["properties"]["hawq_master_address_port"] = "5432" |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 0) |
| |
| # case 4: HAWQ Master is not placed on Ambari Server and HAWQ Master port is different from Ambari Server Postgres Port |
| self.serviceAdvisor.isHawqMasterComponentOnAmbariServer = MagicMock(return_value=False) |
| configurations["hawq-site"]["properties"]["hawq_master_address_port"] = "10432" |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 0) |
| |
| # -------- test query limits warning ---------- |
| services = { |
| "services": [ |
| { "StackServices": {"service_name": "HAWQ"}, |
| "components": [{ |
| "StackServiceComponents": { |
| "component_name": "HAWQSEGMENT", |
| "hostnames": [] |
| }}] |
| }], |
| "configurations": {} |
| } |
| # setup default configuration values |
| configurations = services["configurations"] |
| configurations["hawq-site"] = { |
| "properties": { |
| "default_hash_table_bucket_number": "600", |
| "hawq_rm_nvseg_perquery_limit": "500", |
| "hawq_rm_nvseg_perquery_perseg_limit": "6" |
| } |
| } |
| properties = configurations["hawq-site"]["properties"] |
| defaults = {} |
| hosts = {} |
| |
| expected = { |
| 'config-type': 'hawq-site', |
| 'message': 'Default buckets for Hash Distributed tables parameter value should not be greater than the value of Virtual Segments Limit per Query (Total) parameter, currently set to 500.', |
| 'type': 'configuration', |
| 'config-name': 'default_hash_table_bucket_number', |
| 'level': 'ERROR' |
| } |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| self.assertEqual(problems[0], expected) |
| |
| configurations["hawq-site"] = { |
| "properties": { |
| "default_hash_table_bucket_number": "500", |
| "hawq_rm_nvseg_perquery_limit": "500" |
| } |
| } |
| properties = configurations["hawq-site"]["properties"] |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 0) |
| |
| configurations["hawq-site"] = { |
| "properties": |
| { |
| "hawq_global_rm_type": "none", |
| "hawq_rm_memory_limit_perseg": "1023MB" |
| } |
| } |
| expected = { |
| 'config-type': 'hawq-site', |
| 'message': 'HAWQ Segment Memory less than 1GB is not sufficient', |
| 'type': 'configuration', |
| 'config-name': 'hawq_global_rm_type', |
| 'level': 'ERROR' |
| } |
| properties = configurations["hawq-site"]["properties"] |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| self.assertEqual(problems[0], expected) |
| |
| configurations["hawq-site"]["properties"]["hawq_rm_memory_limit_perseg"] = "1GB" |
| properties = configurations["hawq-site"]["properties"] |
| problems = self.serviceAdvisor.validateHAWQSiteConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 0) |
| |
| |
| def test_validateHAWQHdfsClientConfigurations(self): |
| services = { |
| "services": [ |
| { "StackServices": {"service_name": "HAWQ"}, |
| "components": [{ |
| "StackServiceComponents": { |
| "component_name": "HAWQSEGMENT", |
| "hostnames": [] |
| }}] |
| }], |
| "configurations": {} |
| } |
| # setup default configuration values |
| configurations = services["configurations"] |
| configurations["hdfs-client"] = {"properties": {"output.replace-datanode-on-failure": "true"}} |
| properties = configurations["hdfs-client"]["properties"] |
| defaults = {} |
| hosts = {} |
| |
| # 1. Try with no hosts |
| expected = { |
| 'config-type': 'hdfs-client', |
| 'message': 'output.replace-datanode-on-failure should be set to false (unchecked) for clusters with 3 or less HAWQ Segments', |
| 'type': 'configuration', |
| 'config-name': 'output.replace-datanode-on-failure', |
| 'level': 'WARN' |
| } |
| |
| problems = self.serviceAdvisor.validateHAWQHdfsClientConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| self.assertEqual(problems[0], expected) |
| |
| # 2. Try with 3 hosts |
| services["services"][0]["components"][0]["StackServiceComponents"]["hostnames"] = ["host1", "host2", "host3"] |
| problems = self.serviceAdvisor.validateHAWQHdfsClientConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| self.assertEqual(problems[0], expected) |
| |
| # 3. Try with 4 hosts - default value |
| services["services"][0]["components"][0]["StackServiceComponents"]["hostnames"] = ["host1", "host2", "host3", "host4"] |
| problems = self.serviceAdvisor.validateHAWQHdfsClientConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 0) |
| |
| # 4. Try with 4 hosts |
| properties = {"output.replace-datanode-on-failure": "false"} |
| expected = { |
| 'config-type': 'hdfs-client', |
| 'message': 'output.replace-datanode-on-failure should be set to true (checked) for clusters with more than 3 HAWQ Segments', |
| 'type': 'configuration', |
| 'config-name': 'output.replace-datanode-on-failure', |
| 'level': 'WARN' |
| } |
| problems = self.serviceAdvisor.validateHAWQHdfsClientConfigurations(properties, defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| self.assertEqual(problems[0], expected) |
| |
| def test_validateHDFSSiteConfigurations(self): |
| services = { |
| "services": [ |
| { "StackServices": {"service_name": "HAWQ"}, |
| "components": [{ |
| "StackServiceComponents": { |
| "component_name": "HAWQSEGMENT", |
| "hostnames": [] |
| }}] |
| }], |
| "configurations": {"hdfs-site": {}, "core-site": {}} |
| } |
| |
| # setup default configuration values for non-kerberos case |
| configurations = services["configurations"] |
| configurations["cluster-env"] = {"properties": {"security_enabled": "false"}} |
| defaults = {} |
| hosts = {} |
| desired_values = self.getDesiredHDFSSiteValues(False) |
| |
| # check all properties setup correctly in hdfs-site |
| configurations["hdfs-site"]["properties"] = desired_values.copy() |
| problems = self.serviceAdvisor.validateHDFSSiteConfigurations(configurations["hdfs-site"]["properties"], defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 0) |
| |
| # check overall number of validations for hdfs-site |
| configurations["hdfs-site"]["properties"] = {} |
| problems = self.serviceAdvisor.validateHDFSSiteConfigurations(configurations["hdfs-site"]["properties"], defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 10) |
| |
| # check individual properties |
| for property in desired_values.keys(): |
| # populate all properties as to desired configuration |
| configurations["hdfs-site"]["properties"] = desired_values.copy() |
| # test when the given property is missing |
| configurations["hdfs-site"]["properties"].pop(property) |
| expected = { |
| 'config-type': 'hdfs-site', |
| 'message': 'HAWQ requires this property to be set to the recommended value of ' + desired_values[property], |
| 'type': 'configuration', |
| 'config-name': property, |
| 'level': 'ERROR' if property == 'dfs.allow.truncate' else 'WARN' |
| } |
| problems = self.serviceAdvisor.validateHDFSSiteConfigurations(configurations["hdfs-site"]["properties"], defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| self.assertEqual(problems[0], expected) |
| |
| # test when the given property has a non-desired value |
| configurations["hdfs-site"]["properties"][property] = "foo" |
| problems = self.serviceAdvisor.validateHDFSSiteConfigurations(configurations["hdfs-site"]["properties"], defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| self.assertEqual(problems[0], expected) |
| |
| # check all properties setup correctly in core-site |
| configurations["core-site"]["properties"] = {"ipc.server.listen.queue.size" : "3300"} |
| problems = self.serviceAdvisor.validateCORESiteConfigurations(configurations["core-site"]["properties"], defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 0) |
| |
| # check overall number of validations for core-site |
| configurations["core-site"]["properties"] = {} |
| problems = self.serviceAdvisor.validateCORESiteConfigurations(configurations["core-site"]["properties"], defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| |
| # check incorrect core-site property |
| expected = { |
| 'config-type': 'core-site', |
| 'message': 'HAWQ requires this property to be set to the recommended value of 3300', |
| 'type': 'configuration', |
| 'config-name': 'ipc.server.listen.queue.size', |
| 'level': 'WARN' |
| } |
| configurations["core-site"]["properties"] = {"ipc.server.listen.queue.size" : "0"} |
| problems = self.serviceAdvisor.validateCORESiteConfigurations(configurations["core-site"]["properties"], defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| self.assertEqual(problems[0], expected) |
| |
| # check missing core-site property |
| configurations["core-site"]["properties"].pop("ipc.server.listen.queue.size") |
| problems = self.serviceAdvisor.validateCORESiteConfigurations(configurations["core-site"]["properties"], defaults, configurations, services, hosts) |
| self.assertEqual(len(problems), 1) |
| self.assertEqual(problems[0], expected) |