123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- #!/usr/bin/env python
- '''
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- '''
- import os
- import socket
- import string
- from ambari_commons.exceptions import FatalException
- from ambari_commons.logging_utils import print_info_msg, print_warning_msg
- from ambari_commons.os_utils import search_file, run_os_command
- from ambari_commons.os_windows import WinServiceController
- from ambari_commons.str_utils import compress_backslashes, ensure_double_backslashes
- from ambari_server.serverConfiguration import JDBC_DRIVER_PROPERTY, JDBC_DRIVER_PATH_PROPERTY, JDBC_URL_PROPERTY, \
- JDBC_DATABASE_PROPERTY, JDBC_DATABASE_NAME_PROPERTY, \
- JDBC_HOSTNAME_PROPERTY, JDBC_PORT_PROPERTY, JDBC_USE_INTEGRATED_AUTH_PROPERTY, JDBC_USER_NAME_PROPERTY, JDBC_PASSWORD_PROPERTY, \
- JDBC_PASSWORD_FILENAME, \
- JDBC_RCA_DRIVER_PROPERTY, JDBC_RCA_URL_PROPERTY, JDBC_RCA_DATABASE_PROPERTY, JDBC_RCA_SCHEMA_PROPERTY, \
- JDBC_RCA_HOSTNAME_PROPERTY, JDBC_RCA_PORT_PROPERTY, JDBC_RCA_USE_INTEGRATED_AUTH_PROPERTY, \
- JDBC_RCA_USER_NAME_PROPERTY, JDBC_RCA_PASSWORD_FILE_PROPERTY, JDBC_RCA_PASSWORD_FILENAME, JDBC_RCA_PASSWORD_ALIAS, \
- PERSISTENCE_TYPE_PROPERTY, \
- JDBC_METRICS_DRIVER_PROPERTY, JDBC_METRICS_URL_PROPERTY, \
- JDBC_METRICS_DATABASE_PROPERTY, METRICS_DATABASE_NAME, JDBC_METRICS_SCHEMA_PROPERTY, \
- JDBC_METRICS_HOSTNAME_PROPERTY, JDBC_METRICS_PORT_PROPERTY, \
- JDBC_METRICS_USE_INTEGRATED_AUTH_PROPERTY, JDBC_METRICS_USER_NAME_PROPERTY, JDBC_METRICS_PASSWORD_PROPERTY, \
- JDBC_METRICS_PASSWORD_FILENAME, JDBC_METRICS_PASSWORD_ALIAS, \
- METRICS_PERSISTENCE_TYPE_PROPERTY, \
- PRESS_ENTER_MSG
- from ambari_server.setupSecurity import encrypt_password, store_password_file
- from dbConfiguration import DBMSConfig, DB_STATUS_RUNNING_DEFAULT
- from userInput import get_validated_string_input
- #Import the SQL Server libraries
- # SQL Server settings
- DBPATH = 'C:\\Program Files\\Microsoft SQL Server\\MSSQL12.SQLEXPRESS\\MSSQL\\DATA\\'
- # DBPATH = 'C:\\Program Files\\Microsoft SQL Server\\MSSQL10_50.MSSQLSERVER\\MSSQL\\DATA\\'
- DATABASE_DBMS = "sqlserver"
- DATABASE_DRIVER_NAME = "com.microsoft.sqlserver.jdbc.SQLServerDriver"
- LOCAL_DATABASE_SERVER = "localhost\\SQLEXPRESS"
- AMBARI_DATABASE_NAME = "ambari"
- class DbPropKeys:
- def __init__(self, i_dbms_key, i_driver_key, i_server_key, i_port_key, i_db_name_key, i_db_url_key):
- self.reset(i_dbms_key, i_driver_key, i_server_key, i_port_key, i_db_name_key, i_db_url_key)
- pass
- def reset(self, i_dbms_key, i_driver_key, i_server_key, i_port_key, i_db_name_key, i_db_url_key):
- self.dbms_key = i_dbms_key
- self.driver_key = i_driver_key
- self.server_key = i_server_key
- self.port_key = i_port_key
- self.db_name_key = i_db_name_key
- self.db_url_key = i_db_url_key
- pass
- class AuthenticationKeys:
- def __init__(self, i_integrated_auth_key, i_user_name_key, i_password_key, i_password_alias, i_password_filename):
- self.reset(i_integrated_auth_key, i_user_name_key, i_password_key, i_password_alias, i_password_filename)
- pass
- def reset(self, i_integrated_auth_key, i_user_name_key, i_password_key, i_password_alias, i_password_filename):
- self.integrated_auth_key = i_integrated_auth_key
- self.user_name_key = i_user_name_key
- self.password_key = i_password_key
- self.password_alias = i_password_alias
- self.password_filename = i_password_filename
- pass
- # SQL Server configuration and setup
- class SQLServerConfig(DBMSConfig):
- def __init__(self, options, properties):
- super(SQLServerConfig, self).__init__(options, properties)
- """
- #Just load the defaults. The derived classes will be able to modify them later
- """
- self.dbms = DATABASE_DBMS
- self.driver_name = DATABASE_DRIVER_NAME
- # The values from options supersede the values from properties
- self.database_host = options.database_host if options.database_host is not None and options.database_host is not "" else \
- properties.get_property(self.dbPropKeys.server_key)
- try:
- if self.database_host is None or self.database_host is "":
- self.database_host = options.default_database_host
- else:
- self.database_host = compress_backslashes(self.database_host)
- except:
- self.database_host = "localhost\\SQLEXPRESS"
- pass
- self.database_port = options.database_port if options.database_port is not None and options.database_port is not "" else \
- properties.get_property(self.dbPropKeys.port_key)
- self.database_name = options.database_name if options.database_name is not None and options.database_name is not "" else \
- properties.get_property(self.dbPropKeys.db_name_key)
- self.use_windows_authentication = options.database_windows_auth if options.database_windows_auth is True else \
- properties.get_property(self.dbAuthKeys.integrated_auth_key)
- self.database_username = options.database_username if options.database_username is not None and options.database_username is not "" \
- else properties.get_property(self.dbAuthKeys.user_name_key)
- self.database_password = options.database_password if options.database_password is not None and options.database_password is not "" \
- else ""
- self.password_file = properties[self.dbAuthKeys.password_key]
- self.database_url = self._build_sql_server_connection_string()
- self.persistence_property = None
- self.db_title = ""
- self.env_var_db_name = ""
- self.env_var_db_log_name = ""
- self.env_var_db_owner = ""
- self.init_script_file = ""
- self.drop_tables_script_file = ""
- #
- # No local DB configuration supported
- #
- def _is_local_database(self):
- return False
- def _is_jdbc_driver_installed(self, properties):
- """
- #Attempt to load the sqljdbc4.jar and sqljdbc_auth.dll. This will automatically scan the PATH.
- :param None
- :rtype : bool
- """
- paths = "." + os.pathsep + os.environ["PATH"]
- # Find the jar by attempting to load it as a resource dll
- driver_path = search_file("sqljdbc4.jar", paths)
- if not driver_path:
- return 0
- auth_dll_path = search_file("sqljdbc_auth.dll", paths)
- if not auth_dll_path:
- return 0
- try:
- driver_path = properties[JDBC_DRIVER_PATH_PROPERTY]
- if driver_path is None or driver_path is "":
- return 0
- except Exception:
- # No such attribute set
- return 0
- return 1
- def get_jdbc_driver_path(self):
- paths = "." + os.pathsep + os.environ["PATH"]
- # Find the jar by attempting to load it as a resource dll
- driver_path = search_file("sqljdbc4.jar", paths)
- return driver_path
- def configure_database_password(showDefault=True):
- #No password needed, using SQL Server integrated authentication
- pass
- def _prompt_db_properties(self):
- if self.silent:
- # All the settings are supposed to be retrieved from the command-line parameters
- return True
- #prompt for SQL Server host and instance name
- hostname_prompt = "SQL Server host and instance for the {} database: ({}) ".format(self.db_title, self.database_host)
- self.database_host = get_validated_string_input(hostname_prompt, self.database_host, None, None, False, True)
- #prompt for SQL Server authentication method
- if (not self.use_windows_authentication is None and self.use_windows_authentication.lower() == "true") or \
- self.database_username is None or self.database_username == "":
- auth_option_default = '1'
- else:
- auth_option_default = '2'
- user_prompt = \
- "[1] - Use SQL Server integrated authentication\n[2] - Use username+password authentication\n" \
- "Enter choice ({}): ".format(auth_option_default)
- auth_option = get_validated_string_input(user_prompt,
- auth_option_default,
- "^[12]$",
- "Invalid number.",
- False
- )
- if str(auth_option) == '1':
- self.use_windows_authentication = True
- self.database_password = None
- else:
- self.use_windows_authentication = False
- user_prompt = "SQL Server user name for the {} database: ({}) ".format(self.db_title, self.database_username)
- username = get_validated_string_input(user_prompt, self.database_username, None, "User name", False,
- False)
- self.database_username = username
- user_prompt = "SQL Server password for the {} database: ".format(self.db_title)
- password = get_validated_string_input(user_prompt, "", None, "Password", True, False)
- self.database_password = password
- self.database_url = self._build_sql_server_connection_string()
- return True
- def _setup_remote_server(self, properties):
- properties.removeOldProp(self.dbPropKeys.port_key)
- properties.removeOldProp(self.dbAuthKeys.integrated_auth_key)
- properties.removeOldProp(self.dbAuthKeys.user_name_key)
- properties.removeOldProp(self.dbAuthKeys.password_key)
- properties.process_pair(self.persistence_property, 'remote')
- properties.process_pair(self.dbPropKeys.dbms_key, self.dbms)
- properties.process_pair(self.dbPropKeys.driver_key, self.driver_name)
- properties.process_pair(self.dbPropKeys.server_key, ensure_double_backslashes(self.database_host))
- if self.database_port is not None and self.database_port != "":
- properties.process_pair(self.dbPropKeys.port_key, self.database_port)
- properties.process_pair(self.dbPropKeys.db_name_key, self.database_name)
- self._store_db_auth_config(properties, self.dbAuthKeys)
- properties.process_pair(self.dbPropKeys.db_url_key, self.database_url)
- pass
- def _setup_remote_database(self):
- print 'Populating {} database structure...'.format(self.db_title)
- self._populate_database_structure()
- def _reset_remote_database(self):
- print 'Resetting {} database structure...'.format(self.db_title)
- self._populate_database_structure()
- def _prompt_jdbc_driver_install(self, properties):
- result = False
- msg = 'Before starting Ambari Server, you must install the SQL Server JDBC driver.'
- if not self.silent:
- print_warning_msg(msg)
- raw_input(PRESS_ENTER_MSG)
- result = self._is_jdbc_driver_installed(properties)
- return (result, msg)
- def _install_jdbc_driver(self, options, properties):
- try:
- driver_path = properties[JDBC_DRIVER_PATH_PROPERTY]
- except Exception:
- # No such attribute set
- driver_path = None
- if driver_path is None or driver_path == "":
- driver_path = self.get_jdbc_driver_path()
- properties.process_pair(JDBC_DRIVER_PATH_PROPERTY, driver_path)
- return True
- return False
- def ensure_dbms_is_running(self, options, properties, scmStatus=None):
- """
- :param scmStatus : SvcStatusCallback
- :rtype : None
- """
- db_host_components = self.database_host.split("\\")
- if len(db_host_components) == 1:
- db_machine = self.database_host
- sql_svc_name = "MSSQLServer"
- else:
- db_machine = db_host_components[0]
- sql_svc_name = "MSSQL$" + db_host_components[1]
- if db_machine == "localhost" or db_machine.lower() == os.getenv("COMPUTERNAME").lower() or \
- db_machine.lower() == socket.getfqdn().lower():
- #TODO: Configure the SQL Server service name in ambari.properties
- ret = WinServiceController.EnsureServiceIsStarted(sql_svc_name)
- if 0 != ret:
- raise FatalException(-1, "Error starting SQL Server: " + string(ret))
- if scmStatus is not None:
- scmStatus.reportStartPending()
- ret = WinServiceController.EnsureServiceIsStarted("SQLBrowser") #The SQL Server JDBC driver needs this one
- if 0 != ret:
- raise FatalException(-1, "Error starting SQL Server Browser: " + string(ret))
- pass
- def _build_sql_server_connection_string(self):
- databaseUrl = "jdbc:sqlserver://{}".format(ensure_double_backslashes(self.database_host))
- if self.database_port is not None and self.database_port != "":
- databaseUrl += ":{}".format(self.database_port)
- databaseUrl += ";databaseName={}".format(self.database_name)
- if(self.use_windows_authentication):
- databaseUrl += ";integratedSecurity=true"
- #No need to append the username and password, the Ambari server adds them by itself when connecting to the database
- return databaseUrl
- def _store_db_auth_config(self, properties, keys):
- if (self.use_windows_authentication):
- properties.process_pair(keys.integrated_auth_key, "True")
- properties.removeProp(keys.password_key)
- else:
- properties.process_pair(keys.integrated_auth_key, "False")
- properties.process_pair(keys.user_name_key, self.database_username)
- if self.isSecure:
- encrypted_password = encrypt_password(keys.password_alias, self.database_password)
- if self.database_password != encrypted_password:
- properties.process_pair(keys.password_key, encrypted_password)
- else:
- passwordFile = store_password_file(self.database_password, keys.password_filename)
- properties.process_pair(keys.password_key, passwordFile)
- def _populate_database_structure(self):
- # Setup DB
- os.environ[self.env_var_db_name] = self.database_name
- os.environ[self.env_var_db_log_name] = self.database_name + '_log'
- os.environ[self.env_var_db_owner] = 'hadoop'
- # Don't create the database, assume it already exists. Just clear out the known tables structure
- SQLServerConfig._execute_db_script(self.database_host, self.drop_tables_script_file)
- # Init DB
- SQLServerConfig._execute_db_script(self.database_host, self.init_script_file)
- pass
- @staticmethod
- def _execute_db_script(databaseHost, databaseScript):
- dbCmd = 'sqlcmd -S {} -i {}'.format(databaseHost, databaseScript)
- retCode, outData, errData = run_os_command(['cmd', '/C', dbCmd])
- if not retCode == 0:
- err = 'Running database create script failed. Error output: {} Output: {} Exiting.'.format(errData, outData)
- raise FatalException(retCode, err)
- print_info_msg("sqlcmd output:")
- print_info_msg(outData)
- pass
- # SQL Server Ambari database configuration and setup
- class SQLServerAmbariDBConfig(SQLServerConfig):
- def __init__(self, options, properties):
- self.dbPropKeys = DbPropKeys(
- JDBC_DATABASE_PROPERTY,
- JDBC_DRIVER_PROPERTY,
- JDBC_HOSTNAME_PROPERTY,
- JDBC_PORT_PROPERTY,
- JDBC_DATABASE_NAME_PROPERTY,
- JDBC_URL_PROPERTY)
- self.dbAuthKeys = AuthenticationKeys(
- JDBC_USE_INTEGRATED_AUTH_PROPERTY,
- JDBC_USER_NAME_PROPERTY,
- JDBC_PASSWORD_PROPERTY,
- JDBC_RCA_PASSWORD_ALIAS,
- JDBC_PASSWORD_FILENAME
- )
- super(SQLServerAmbariDBConfig, self).__init__(options, properties)
- if self.database_name is None or self.database_name is "":
- self.database_name = AMBARI_DATABASE_NAME
- self.persistence_property = PERSISTENCE_TYPE_PROPERTY
- self.db_title = "ambari"
- self.env_var_db_name ='AMBARIDBNAME'
- self.env_var_db_log_name = 'AMBARIDBLOGNAME'
- self.env_var_db_owner = 'AMBARIDBOWNER'
- # The values from options supersede the values from properties
- if options.init_db_script_file is not None and options.init_db_script_file is not "":
- self.init_script_file = compress_backslashes(options.init_db_script_file)
- else:
- self.init_script_file = "resources" + os.path.sep + "Ambari-DDL-SQLServer-CREATE.sql"
- if options.cleanup_db_script_file is not None and options.cleanup_db_script_file is not "":
- self.drop_tables_script_file = compress_backslashes(options.cleanup_db_script_file)
- else:
- self.drop_tables_script_file = "resources" + os.path.sep + "Ambari-DDL-SQLServer-DROP.sql"
- pass
- def _setup_remote_server(self, properties):
- super(SQLServerAmbariDBConfig, self)._setup_remote_server(properties)
- properties.process_pair(JDBC_RCA_DRIVER_PROPERTY, self.driver_name)
- properties.process_pair(JDBC_RCA_HOSTNAME_PROPERTY, ensure_double_backslashes(self.database_host))
- if self.database_port is not None and self.database_port != "":
- properties.process_pair(JDBC_RCA_PORT_PROPERTY, self.database_port)
- properties.process_pair(JDBC_RCA_SCHEMA_PROPERTY, self.database_name)
- authKeys = AuthenticationKeys(
- JDBC_RCA_USE_INTEGRATED_AUTH_PROPERTY,
- JDBC_RCA_USER_NAME_PROPERTY,
- JDBC_RCA_PASSWORD_FILE_PROPERTY,
- JDBC_RCA_PASSWORD_ALIAS,
- JDBC_PASSWORD_FILENAME
- )
- self._store_db_auth_config(properties, authKeys)
- properties.process_pair(JDBC_RCA_URL_PROPERTY, self.database_url)
- pass
- # SQL Server Metrics database configuration and setup
- class SQLServerMetricsDBConfig(SQLServerConfig):
- def __init__(self, options, properties):
- self.dbPropKeys = DbPropKeys(
- JDBC_METRICS_DATABASE_PROPERTY,
- JDBC_METRICS_DRIVER_PROPERTY,
- JDBC_METRICS_HOSTNAME_PROPERTY,
- JDBC_METRICS_PORT_PROPERTY,
- JDBC_METRICS_SCHEMA_PROPERTY,
- JDBC_METRICS_URL_PROPERTY)
- self.dbAuthKeys = AuthenticationKeys(
- JDBC_METRICS_USE_INTEGRATED_AUTH_PROPERTY,
- JDBC_METRICS_USER_NAME_PROPERTY,
- JDBC_METRICS_PASSWORD_PROPERTY,
- JDBC_METRICS_PASSWORD_ALIAS,
- JDBC_METRICS_PASSWORD_FILENAME
- )
- super(SQLServerMetricsDBConfig, self).__init__(options, properties)
- self.database_name = METRICS_DATABASE_NAME
- self.persistence_property = METRICS_PERSISTENCE_TYPE_PROPERTY
- self.db_title = "metrics"
- self.env_var_db_name ='METRICSDBNAME'
- self.env_var_db_log_name = 'METRICSDBLOGNAME'
- self.env_var_db_owner = 'METRICSDBOWNER'
- if options.init_metrics_db_script_file is not None and options.init_metrics_db_script_file is not "":
- self.init_script_file = compress_backslashes(options.init_db_script_file)
- else:
- self.init_script_file = "resources" + os.sep + "Hadoop-Metrics-SQLServer-CREATE.sql"
- if options.cleanup_metrics_db_script_file is not None and options.cleanup_metrics_db_script_file is not "":
- self.drop_tables_script_file = compress_backslashes(options.cleanup_db_script_file)
- else:
- self.drop_tables_script_file = "resources" + os.sep + "Hadoop-Metrics-SQLServer-DROP.sql"
- pass
- # SQL Server database
- class SQLServerDatabase:
- def __init__(self):
- #Init the database connection here
- pass
- def get_running_status(self):
- #if the connection is active, return running
- #else return stopped
- return DB_STATUS_RUNNING_DEFAULT
|