123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- #!/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 glob
- import os
- from ambari_commons import OSConst
- from ambari_commons.exceptions import FatalException
- from ambari_commons.logging_utils import get_silent, print_error_msg, print_info_msg, print_warning_msg, set_silent
- from ambari_commons.os_family_impl import OsFamilyImpl
- from ambari_commons.str_utils import cbool
- from ambari_server.serverConfiguration import decrypt_password_for_alias, get_ambari_properties, get_is_secure, \
- get_resources_location, get_value_from_properties, is_alias_string, \
- JDBC_PASSWORD_PROPERTY, JDBC_RCA_PASSWORD_ALIAS, PRESS_ENTER_MSG
- from ambari_server.userInput import get_validated_string_input
- #Database settings
- DB_STATUS_RUNNING_DEFAULT = "running"
- SETUP_DB_CONNECT_TIMEOUT = 5
- SETUP_DB_CONNECT_ATTEMPTS = 3
- USERNAME_PATTERN = "^[a-zA-Z_][a-zA-Z0-9_\-]*$"
- PASSWORD_PATTERN = "^[a-zA-Z0-9_-]*$"
- DATABASE_NAMES = ["postgres", "oracle", "mysql", "mssql"]
- DATABASE_FULL_NAMES = {"oracle": "Oracle", "mysql": "MySQL", "mssql": "Microsoft SQL Server", "postgres": "PostgreSQL"}
- AMBARI_DATABASE_NAME = "ambari"
- AMBARI_DATABASE_TITLE = "ambari"
- STORAGE_TYPE_LOCAL = 'local'
- STORAGE_TYPE_REMOTE = 'remote'
- DEFAULT_USERNAME = "ambari"
- DEFAULT_PASSWORD = "bigdata"
- #
- # Database configuration helper classes
- #
- class DBMSDesc:
- def __init__(self, i_dbms_key, i_storage_key, i_dbms_name, i_storage_name, i_fn_create_config):
- self.dbms_key = i_dbms_key
- self.storage_key = i_storage_key
- self.dbms_name = i_dbms_name
- self.storage_name = i_storage_name
- self.fn_create_config = i_fn_create_config
- def create_config(self, options, properties, dbId):
- return self.fn_create_config(options, properties, self.storage_key, dbId)
- 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.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
- class DbAuthenticationKeys:
- def __init__(self, i_user_name_key, i_password_key, i_password_alias, i_password_filename):
- 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
- #
- # Database configuration base class
- #
- class DBMSConfig(object):
- def __init__(self, options, properties, storage_type):
- """
- #Just load the defaults. The derived classes will be able to modify them later
- """
- self.persistence_type = storage_type
- self.dbms = ""
- self.driver_class_name = ""
- self.driver_file_name = ""
- self.driver_symlink_name = ""
- self.database_host = ""
- self.database_port = ""
- self.database_name = ""
- self.database_username = ""
- self.db_title = AMBARI_DATABASE_TITLE
- self.must_set_database_options = DBMSConfig._init_member_with_default(options, "must_set_database_options", False)
- self.JDBC_DRIVER_INSTALL_MSG = 'Before starting Ambari Server, you must install the JDBC driver.'
- self.isSecure = get_is_secure(properties)
- pass
- #
- # Public methods
- #
- #
- # Main method. Configures the database according to the options and the existing properties.
- #
- def configure_database(self, properties):
- result = self._prompt_db_properties()
- if result:
- #DB setup should be done last after doing any setup.
- if self._is_local_database():
- self._setup_local_server(properties)
- else:
- self._setup_remote_server(properties)
- return result
- def setup_database(self):
- print 'Configuring {0} database...'.format(self.db_title)
- #DB setup should be done last after doing any setup.
- if self._is_local_database():
- self._setup_local_database()
- else:
- self._setup_remote_database()
- pass
- def reset_database(self):
- if self._is_local_database():
- self._reset_local_database()
- else:
- self._reset_remote_database()
- pass
- def ensure_jdbc_driver_installed(self, properties):
- (result, msg) = self._prompt_jdbc_driver_install(properties)
- if result == -1:
- print_error_msg(msg)
- raise FatalException(-1, msg)
- if result != 1:
- result = self._install_jdbc_driver(properties, result)
- return cbool(result)
- def change_db_files_owner(self):
- if self._is_local_database():
- retcode = self._change_db_files_owner()
- if not retcode == 0:
- raise FatalException(20, 'Unable to change owner of database objects')
- #
- # Private implementation
- #
- @staticmethod
- def _read_password_from_properties(properties):
- database_password = DEFAULT_PASSWORD
- password_file = get_value_from_properties(properties, JDBC_PASSWORD_PROPERTY, "")
- if password_file:
- if is_alias_string(password_file):
- database_password = decrypt_password_for_alias(properties, JDBC_RCA_PASSWORD_ALIAS)
- else:
- if os.path.isabs(password_file) and os.path.exists(password_file):
- with open(password_file, 'r') as file:
- database_password = file.read()
- return database_password
- @staticmethod
- def _init_member_with_default(options, attr_name, default_val):
- options_val = getattr(options, attr_name, None)
- val = options_val if options_val is not None and options_val is not "" else default_val
- return val
- @staticmethod
- def _init_member_with_properties(options, attr_name, properties, property_key):
- options_val = getattr(options, attr_name, None)
- if options_val is None or options_val is "":
- options_val = get_value_from_properties(properties, property_key, None)
- return options_val
- @staticmethod
- def _init_member_with_prop_default(options, attr_name, properties, property_key, default_val):
- val = DBMSConfig._init_member_with_properties(options, attr_name, properties, property_key)
- if val is None or val is "":
- val = default_val
- return val
- #
- # Checks if options determine local DB configuration
- #
- def _is_local_database(self):
- return self.persistence_type == STORAGE_TYPE_LOCAL
- def _prompt_db_properties(self):
- #if WINDOWS
- # prompt for SQL Server host and instance name
- #else
- # go the classic Linux way
- #linux_prompt_db_properties(args)
- return False
- def _setup_local_server(self, properties):
- pass
- def _setup_local_database(self):
- pass
- def _reset_local_database(self):
- pass
- def _setup_remote_server(self, properties):
- pass
- def _setup_remote_database(self):
- pass
- def _reset_remote_database(self):
- pass
- def _prompt_jdbc_driver_install(self, properties):
- result = self._is_jdbc_driver_installed(properties)
- if result == -1:
- if get_silent():
- print_error_msg(self.JDBC_DRIVER_INSTALL_MSG)
- else:
- print_warning_msg(self.JDBC_DRIVER_INSTALL_MSG)
- raw_input(PRESS_ENTER_MSG)
- result = self._is_jdbc_driver_installed(properties)
- return (result, self.JDBC_DRIVER_INSTALL_MSG)
- def _is_jdbc_driver_installed(self, properties):
- return 1
- def _install_jdbc_driver(self, properties, files_list):
- return False
- def ensure_dbms_is_running(self, options, properties, scmStatus=None):
- pass
- def _change_db_files_owner(self, args):
- return 0
- #
- # Database configuration factory base class
- #
- class DBMSConfigFactory(object):
- def select_dbms(self, options):
- '''
- # Base declaration of the DBMS selection method.
- :return: DBMS index in the descriptor table
- '''
- pass
- def create(self, options, properties, dbId = "Ambari"):
- """
- # Base declaration of the factory method. The outcome of the derived implementations
- # is expected to be a subclass of DBMSConfig.
- # properties = property bag that will ultimately define the type of database. Since
- # right now in Windows we only support SQL Server, this argument is not yet used.
- # dbId = additional information, that helps distinguish between various database connections, if applicable
- """
- pass
- def get_supported_dbms(self):
- return []
- def get_supported_jdbc_drivers(self):
- return []
- #
- # Database configuration factory for Windows
- #
- @OsFamilyImpl(os_family=OSConst.WINSRV_FAMILY)
- class DBMSConfigFactoryWindows(DBMSConfigFactory):
- def __init__(self):
- from ambari_server.dbConfiguration_windows import DATABASE_DBMS_MSSQL
- self.DBMS_KEYS_LIST = [
- DATABASE_DBMS_MSSQL
- ]
- def select_dbms(self, options):
- # For now, we only support SQL Server in Windows, in remote mode.
- return 0
- def create(self, options, properties, dbId = "Ambari"):
- """
- # Windows implementation of the factory method. The outcome of the derived implementations
- # is expected to be a subclass of DBMSConfig.
- # properties = property bag that will ultimately define the type of database. Since
- # right now in Windows we only support SQL Server, this argument is not yet used.
- # dbId = additional information, that helps distinguish between various database connections, if applicable
- """
- from ambari_server.dbConfiguration_windows import createMSSQLConfig
- return createMSSQLConfig(options, properties, STORAGE_TYPE_REMOTE, dbId)
- def get_supported_dbms(self):
- return self.DBMS_KEYS_LIST
- def get_supported_jdbc_drivers(self):
- return self.DBMS_KEYS_LIST
- #
- # Database configuration factory for Linux
- #
- @OsFamilyImpl(os_family=OsFamilyImpl.DEFAULT)
- class DBMSConfigFactoryLinux(DBMSConfigFactory):
- def __init__(self):
- from ambari_server.dbConfiguration_linux import createPGConfig, createOracleConfig, createMySQLConfig, createMSSQLConfig
- self.DBMS_KEYS_LIST = [
- 'embedded',
- 'oracle',
- 'mysql',
- 'postgres',
- 'mssql'
- ]
- self.DRIVER_KEYS_LIST = [
- 'oracle',
- 'mysql',
- 'postgres',
- 'mssql',
- 'hsqldb'
- ]
- self.DBMS_LIST = [
- DBMSDesc(self.DBMS_KEYS_LIST[3], STORAGE_TYPE_LOCAL, 'PostgreSQL', 'Embedded', createPGConfig),
- DBMSDesc(self.DBMS_KEYS_LIST[1], STORAGE_TYPE_REMOTE, 'Oracle', '', createOracleConfig),
- DBMSDesc(self.DBMS_KEYS_LIST[2], STORAGE_TYPE_REMOTE, 'MySQL', '', createMySQLConfig),
- DBMSDesc(self.DBMS_KEYS_LIST[3], STORAGE_TYPE_REMOTE, 'PostgreSQL', '', createPGConfig),
- DBMSDesc(self.DBMS_KEYS_LIST[4], STORAGE_TYPE_REMOTE, 'Microsoft SQL Server', 'Tech Preview', createMSSQLConfig)
- ]
- self.DBMS_DICT = \
- {
- '-' : 0,
- '-' + STORAGE_TYPE_LOCAL : 0,
- self.DBMS_KEYS_LIST[0] + '-' : 0,
- self.DBMS_KEYS_LIST[2] + '-' : 2,
- self.DBMS_KEYS_LIST[2] + '-' + STORAGE_TYPE_REMOTE : 2,
- self.DBMS_KEYS_LIST[4] + '-' : 4,
- self.DBMS_KEYS_LIST[4] + '-' + STORAGE_TYPE_REMOTE : 4,
- self.DBMS_KEYS_LIST[1] + '-' : 1,
- self.DBMS_KEYS_LIST[1] + '-' + STORAGE_TYPE_REMOTE : 1,
- self.DBMS_KEYS_LIST[3] + '-' : 3,
- self.DBMS_KEYS_LIST[3] + '-' + STORAGE_TYPE_LOCAL : 0,
- self.DBMS_KEYS_LIST[3] + '-' + STORAGE_TYPE_REMOTE : 3,
- }
- self.DBMS_PROMPT_PATTERN = "[{0}] - {1}{2}\n"
- self.DBMS_CHOICE_PROMPT_PATTERN = "==============================================================================\n" \
- "Enter choice ({0}): "
- self.JDK_VALID_CHOICES_PATTERN = "^[{0}]$"
- def select_dbms(self, options):
- try:
- dbms_index = options.database_index
- except AttributeError:
- dbms_index = self._get_default_dbms_index(options)
- if options.must_set_database_options:
- n_dbms = 1
- dbms_choice_prompt = "==============================================================================\n" \
- "Choose one of the following options:\n"
- dbms_choices = ''
- for desc in self.DBMS_LIST:
- if len(desc.storage_name) > 0:
- dbms_storage = " ({0})".format(desc.storage_name)
- else:
- dbms_storage = ""
- dbms_choice_prompt += self.DBMS_PROMPT_PATTERN.format(n_dbms, desc.dbms_name, dbms_storage)
- dbms_choices += str(n_dbms)
- n_dbms += 1
- database_num = str(dbms_index + 1)
- dbms_choice_prompt += self.DBMS_CHOICE_PROMPT_PATTERN.format(database_num)
- dbms_valid_choices = self.JDK_VALID_CHOICES_PATTERN.format(dbms_choices)
- database_num = get_validated_string_input(
- dbms_choice_prompt,
- database_num,
- dbms_valid_choices,
- "Invalid number.",
- False
- )
- dbms_index = int(database_num) - 1
- if dbms_index >= n_dbms:
- print_info_msg('Unknown db option, default to {0} {1}.'.format(
- self.DBMS_LIST[0].storage_name, self.DBMS_LIST[0].dbms_name))
- dbms_index = 0
- return dbms_index
- def create(self, options, properties, dbId = "Ambari"):
- """
- # Linux implementation of the factory method. The outcome of the derived implementations
- # is expected to be a subclass of DBMSConfig.
- # properties = property bag that will ultimately define the type of database. Supported types are
- # MySQL, MSSQL, Oracle and PostgreSQL.
- # dbId = additional information, that helps distinguish between various database connections, if applicable
- """
- try:
- index = options.database_index
- except AttributeError:
- index = options.database_index = self._get_default_dbms_index(options)
- desc = self.DBMS_LIST[index]
- options.persistence_type = desc.storage_key
- dbmsConfig = desc.create_config(options, properties, dbId)
- return dbmsConfig
- def get_supported_dbms(self):
- return self.DBMS_KEYS_LIST
- def get_supported_jdbc_drivers(self):
- return self.DRIVER_KEYS_LIST
- def _get_default_dbms_index(self, options):
- try:
- dbms_name = options.dbms
- if not dbms_name:
- dbms_name = ""
- except AttributeError:
- dbms_name = ""
- try:
- persistence_type = options.persistence_type
- if not persistence_type:
- persistence_type = ""
- except AttributeError:
- persistence_type = ""
- try:
- def_index = self.DBMS_DICT[dbms_name + "-" + persistence_type]
- except KeyError:
- # Unsupported database type (e.g. local Oracle, MySQL or MSSQL)
- raise FatalException(15, "Invalid database selection: {0} {1}".format(
- getattr(options, "persistence_type", ""), getattr(options, "options.dbms", "")))
- return def_index
- def check_jdbc_drivers(args):
- # create jdbc symlinks if jdbc drivers are available in resources
- properties = get_ambari_properties()
- if properties == -1:
- err = "Error getting ambari properties"
- print_error_msg(err)
- raise FatalException(-1, err)
- resources_dir = get_resources_location(properties)
- try:
- db_idx_orig = args.database_index
- except AttributeError:
- db_idx_orig = None
- factory = DBMSConfigFactory()
- # AMBARI-5696 Validate the symlinks for each supported driver, in case various back-end HDP services happen to
- # use different DBMSes
- # This is skipped on Windows
- db_idx = 1
- try:
- while db_idx < len(factory.get_supported_dbms()):
- args.database_index = db_idx
- dbms = factory.create(args, properties)
- if dbms.driver_symlink_name:
- jdbc_file_path = os.path.join(resources_dir, dbms.driver_file_name)
- if os.path.isfile(jdbc_file_path):
- jdbc_symlink = os.path.join(resources_dir, dbms.driver_symlink_name)
- if os.path.lexists(jdbc_symlink):
- os.remove(jdbc_symlink)
- os.symlink(jdbc_file_path, jdbc_symlink)
- db_idx += 1
- finally:
- args.database_index = db_idx_orig
- #Check the JDBC driver status
- #If not found abort
- #Get SQL Server service status from SCM
- #If 'stopped' then start it
- #Wait until the status is 'started' or a configured timeout elapses
- #If the timeout has been reached, bail out with exception
- def ensure_dbms_is_running(options, properties, scmStatus=None):
- factory = DBMSConfigFactory()
- dbms = factory.create(options, properties)
- result = dbms._is_jdbc_driver_installed(properties)
- if result == -1:
- raise FatalException(-1, "JDBC driver is not installed. Run ambari-server setup and try again.")
- dbms.ensure_dbms_is_running(options, properties, scmStatus)
- def ensure_jdbc_driver_is_installed(options, properties):
- factory = DBMSConfigFactory()
- dbms = factory.create(options, properties)
- result = dbms._is_jdbc_driver_installed(properties)
- if result == -1:
- raise FatalException(-1, dbms.JDBC_DRIVER_INSTALL_MSG)
|