dbConfiguration_windows.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. #!/usr/bin/env python
  2. '''
  3. Licensed to the Apache Software Foundation (ASF) under one
  4. or more contributor license agreements. See the NOTICE file
  5. distributed with this work for additional information
  6. regarding copyright ownership. The ASF licenses this file
  7. to you under the Apache License, Version 2.0 (the
  8. "License"); you may not use this file except in compliance
  9. with the License. You may obtain a copy of the License at
  10. http://www.apache.org/licenses/LICENSE-2.0
  11. Unless required by applicable law or agreed to in writing, software
  12. distributed under the License is distributed on an "AS IS" BASIS,
  13. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. See the License for the specific language governing permissions and
  15. limitations under the License.
  16. '''
  17. import os
  18. import socket
  19. import string
  20. from ambari_commons.exceptions import FatalException
  21. from ambari_commons.logging_utils import print_info_msg, print_warning_msg
  22. from ambari_commons.os_utils import search_file, run_os_command
  23. from ambari_commons.os_windows import WinServiceController
  24. from ambari_commons.str_utils import compress_backslashes, ensure_double_backslashes
  25. from ambari_server.serverConfiguration import JDBC_DRIVER_PROPERTY, JDBC_DRIVER_PATH_PROPERTY, JDBC_URL_PROPERTY, \
  26. JDBC_DATABASE_PROPERTY, JDBC_DATABASE_NAME_PROPERTY, \
  27. JDBC_HOSTNAME_PROPERTY, JDBC_PORT_PROPERTY, JDBC_USE_INTEGRATED_AUTH_PROPERTY, JDBC_USER_NAME_PROPERTY, JDBC_PASSWORD_PROPERTY, \
  28. JDBC_PASSWORD_FILENAME, \
  29. JDBC_RCA_DRIVER_PROPERTY, JDBC_RCA_URL_PROPERTY, JDBC_RCA_DATABASE_PROPERTY, JDBC_RCA_SCHEMA_PROPERTY, \
  30. JDBC_RCA_HOSTNAME_PROPERTY, JDBC_RCA_PORT_PROPERTY, JDBC_RCA_USE_INTEGRATED_AUTH_PROPERTY, \
  31. JDBC_RCA_USER_NAME_PROPERTY, JDBC_RCA_PASSWORD_FILE_PROPERTY, JDBC_RCA_PASSWORD_FILENAME, JDBC_RCA_PASSWORD_ALIAS, \
  32. PERSISTENCE_TYPE_PROPERTY, \
  33. JDBC_METRICS_DRIVER_PROPERTY, JDBC_METRICS_URL_PROPERTY, \
  34. JDBC_METRICS_DATABASE_PROPERTY, METRICS_DATABASE_NAME, JDBC_METRICS_SCHEMA_PROPERTY, \
  35. JDBC_METRICS_HOSTNAME_PROPERTY, JDBC_METRICS_PORT_PROPERTY, \
  36. JDBC_METRICS_USE_INTEGRATED_AUTH_PROPERTY, JDBC_METRICS_USER_NAME_PROPERTY, JDBC_METRICS_PASSWORD_PROPERTY, \
  37. JDBC_METRICS_PASSWORD_FILENAME, JDBC_METRICS_PASSWORD_ALIAS, \
  38. METRICS_PERSISTENCE_TYPE_PROPERTY, \
  39. PRESS_ENTER_MSG
  40. from ambari_server.setupSecurity import encrypt_password, store_password_file
  41. from dbConfiguration import DBMSConfig, DB_STATUS_RUNNING_DEFAULT
  42. from userInput import get_validated_string_input
  43. #Import the SQL Server libraries
  44. # SQL Server settings
  45. DBPATH = 'C:\\Program Files\\Microsoft SQL Server\\MSSQL12.SQLEXPRESS\\MSSQL\\DATA\\'
  46. # DBPATH = 'C:\\Program Files\\Microsoft SQL Server\\MSSQL10_50.MSSQLSERVER\\MSSQL\\DATA\\'
  47. DATABASE_DBMS = "sqlserver"
  48. DATABASE_DRIVER_NAME = "com.microsoft.sqlserver.jdbc.SQLServerDriver"
  49. LOCAL_DATABASE_SERVER = "localhost\\SQLEXPRESS"
  50. AMBARI_DATABASE_NAME = "ambari"
  51. class DbPropKeys:
  52. def __init__(self, i_dbms_key, i_driver_key, i_server_key, i_port_key, i_db_name_key, i_db_url_key):
  53. self.reset(i_dbms_key, i_driver_key, i_server_key, i_port_key, i_db_name_key, i_db_url_key)
  54. pass
  55. def reset(self, i_dbms_key, i_driver_key, i_server_key, i_port_key, i_db_name_key, i_db_url_key):
  56. self.dbms_key = i_dbms_key
  57. self.driver_key = i_driver_key
  58. self.server_key = i_server_key
  59. self.port_key = i_port_key
  60. self.db_name_key = i_db_name_key
  61. self.db_url_key = i_db_url_key
  62. pass
  63. class AuthenticationKeys:
  64. def __init__(self, i_integrated_auth_key, i_user_name_key, i_password_key, i_password_alias, i_password_filename):
  65. self.reset(i_integrated_auth_key, i_user_name_key, i_password_key, i_password_alias, i_password_filename)
  66. pass
  67. def reset(self, i_integrated_auth_key, i_user_name_key, i_password_key, i_password_alias, i_password_filename):
  68. self.integrated_auth_key = i_integrated_auth_key
  69. self.user_name_key = i_user_name_key
  70. self.password_key = i_password_key
  71. self.password_alias = i_password_alias
  72. self.password_filename = i_password_filename
  73. pass
  74. # SQL Server configuration and setup
  75. class SQLServerConfig(DBMSConfig):
  76. def __init__(self, options, properties):
  77. super(SQLServerConfig, self).__init__(options, properties)
  78. """
  79. #Just load the defaults. The derived classes will be able to modify them later
  80. """
  81. self.dbms = DATABASE_DBMS
  82. self.driver_name = DATABASE_DRIVER_NAME
  83. # The values from options supersede the values from properties
  84. self.database_host = options.database_host if options.database_host is not None and options.database_host is not "" else \
  85. properties.get_property(self.dbPropKeys.server_key)
  86. try:
  87. if self.database_host is None or self.database_host is "":
  88. self.database_host = options.default_database_host
  89. else:
  90. self.database_host = compress_backslashes(self.database_host)
  91. except:
  92. self.database_host = "localhost\\SQLEXPRESS"
  93. pass
  94. self.database_port = options.database_port if options.database_port is not None and options.database_port is not "" else \
  95. properties.get_property(self.dbPropKeys.port_key)
  96. self.database_name = options.database_name if options.database_name is not None and options.database_name is not "" else \
  97. properties.get_property(self.dbPropKeys.db_name_key)
  98. self.use_windows_authentication = options.database_windows_auth if options.database_windows_auth is True else \
  99. properties.get_property(self.dbAuthKeys.integrated_auth_key)
  100. self.database_username = options.database_username if options.database_username is not None and options.database_username is not "" \
  101. else properties.get_property(self.dbAuthKeys.user_name_key)
  102. self.database_password = options.database_password if options.database_password is not None and options.database_password is not "" \
  103. else ""
  104. self.password_file = properties[self.dbAuthKeys.password_key]
  105. self.database_url = self._build_sql_server_connection_string()
  106. self.persistence_property = None
  107. self.db_title = ""
  108. self.env_var_db_name = ""
  109. self.env_var_db_log_name = ""
  110. self.env_var_db_owner = ""
  111. self.init_script_file = ""
  112. self.drop_tables_script_file = ""
  113. #
  114. # No local DB configuration supported
  115. #
  116. def _is_local_database(self):
  117. return False
  118. def _is_jdbc_driver_installed(self, properties):
  119. """
  120. #Attempt to load the sqljdbc4.jar and sqljdbc_auth.dll. This will automatically scan the PATH.
  121. :param None
  122. :rtype : bool
  123. """
  124. paths = "." + os.pathsep + os.environ["PATH"]
  125. # Find the jar by attempting to load it as a resource dll
  126. driver_path = search_file("sqljdbc4.jar", paths)
  127. if not driver_path:
  128. return 0
  129. auth_dll_path = search_file("sqljdbc_auth.dll", paths)
  130. if not auth_dll_path:
  131. return 0
  132. try:
  133. driver_path = properties[JDBC_DRIVER_PATH_PROPERTY]
  134. if driver_path is None or driver_path is "":
  135. return 0
  136. except Exception:
  137. # No such attribute set
  138. return 0
  139. return 1
  140. def get_jdbc_driver_path(self):
  141. paths = "." + os.pathsep + os.environ["PATH"]
  142. # Find the jar by attempting to load it as a resource dll
  143. driver_path = search_file("sqljdbc4.jar", paths)
  144. return driver_path
  145. def configure_database_password(showDefault=True):
  146. #No password needed, using SQL Server integrated authentication
  147. pass
  148. def _prompt_db_properties(self):
  149. if self.silent:
  150. # All the settings are supposed to be retrieved from the command-line parameters
  151. return True
  152. #prompt for SQL Server host and instance name
  153. hostname_prompt = "SQL Server host and instance for the {} database: ({}) ".format(self.db_title, self.database_host)
  154. self.database_host = get_validated_string_input(hostname_prompt, self.database_host, None, None, False, True)
  155. #prompt for SQL Server authentication method
  156. if (not self.use_windows_authentication is None and self.use_windows_authentication.lower() == "true") or \
  157. self.database_username is None or self.database_username == "":
  158. auth_option_default = '1'
  159. else:
  160. auth_option_default = '2'
  161. user_prompt = \
  162. "[1] - Use SQL Server integrated authentication\n[2] - Use username+password authentication\n" \
  163. "Enter choice ({}): ".format(auth_option_default)
  164. auth_option = get_validated_string_input(user_prompt,
  165. auth_option_default,
  166. "^[12]$",
  167. "Invalid number.",
  168. False
  169. )
  170. if str(auth_option) == '1':
  171. self.use_windows_authentication = True
  172. self.database_password = None
  173. else:
  174. self.use_windows_authentication = False
  175. user_prompt = "SQL Server user name for the {} database: ({}) ".format(self.db_title, self.database_username)
  176. username = get_validated_string_input(user_prompt, self.database_username, None, "User name", False,
  177. False)
  178. self.database_username = username
  179. user_prompt = "SQL Server password for the {} database: ".format(self.db_title)
  180. password = get_validated_string_input(user_prompt, "", None, "Password", True, False)
  181. self.database_password = password
  182. self.database_url = self._build_sql_server_connection_string()
  183. return True
  184. def _setup_remote_server(self, properties):
  185. properties.removeOldProp(self.dbPropKeys.port_key)
  186. properties.removeOldProp(self.dbAuthKeys.integrated_auth_key)
  187. properties.removeOldProp(self.dbAuthKeys.user_name_key)
  188. properties.removeOldProp(self.dbAuthKeys.password_key)
  189. properties.process_pair(self.persistence_property, 'remote')
  190. properties.process_pair(self.dbPropKeys.dbms_key, self.dbms)
  191. properties.process_pair(self.dbPropKeys.driver_key, self.driver_name)
  192. properties.process_pair(self.dbPropKeys.server_key, ensure_double_backslashes(self.database_host))
  193. if self.database_port is not None and self.database_port != "":
  194. properties.process_pair(self.dbPropKeys.port_key, self.database_port)
  195. properties.process_pair(self.dbPropKeys.db_name_key, self.database_name)
  196. self._store_db_auth_config(properties, self.dbAuthKeys)
  197. properties.process_pair(self.dbPropKeys.db_url_key, self.database_url)
  198. pass
  199. def _setup_remote_database(self):
  200. print 'Populating {} database structure...'.format(self.db_title)
  201. self._populate_database_structure()
  202. def _reset_remote_database(self):
  203. print 'Resetting {} database structure...'.format(self.db_title)
  204. self._populate_database_structure()
  205. def _prompt_jdbc_driver_install(self, properties):
  206. result = False
  207. msg = 'Before starting Ambari Server, you must install the SQL Server JDBC driver.'
  208. if not self.silent:
  209. print_warning_msg(msg)
  210. raw_input(PRESS_ENTER_MSG)
  211. result = self._is_jdbc_driver_installed(properties)
  212. return (result, msg)
  213. def _install_jdbc_driver(self, options, properties):
  214. try:
  215. driver_path = properties[JDBC_DRIVER_PATH_PROPERTY]
  216. except Exception:
  217. # No such attribute set
  218. driver_path = None
  219. if driver_path is None or driver_path == "":
  220. driver_path = self.get_jdbc_driver_path()
  221. properties.process_pair(JDBC_DRIVER_PATH_PROPERTY, driver_path)
  222. return True
  223. return False
  224. def ensure_dbms_is_running(self, options, properties, scmStatus=None):
  225. """
  226. :param scmStatus : SvcStatusCallback
  227. :rtype : None
  228. """
  229. db_host_components = self.database_host.split("\\")
  230. if len(db_host_components) == 1:
  231. db_machine = self.database_host
  232. sql_svc_name = "MSSQLServer"
  233. else:
  234. db_machine = db_host_components[0]
  235. sql_svc_name = "MSSQL$" + db_host_components[1]
  236. if db_machine == "localhost" or db_machine.lower() == os.getenv("COMPUTERNAME").lower() or \
  237. db_machine.lower() == socket.getfqdn().lower():
  238. #TODO: Configure the SQL Server service name in ambari.properties
  239. ret = WinServiceController.EnsureServiceIsStarted(sql_svc_name)
  240. if 0 != ret:
  241. raise FatalException(-1, "Error starting SQL Server: " + string(ret))
  242. if scmStatus is not None:
  243. scmStatus.reportStartPending()
  244. ret = WinServiceController.EnsureServiceIsStarted("SQLBrowser") #The SQL Server JDBC driver needs this one
  245. if 0 != ret:
  246. raise FatalException(-1, "Error starting SQL Server Browser: " + string(ret))
  247. pass
  248. def _build_sql_server_connection_string(self):
  249. databaseUrl = "jdbc:sqlserver://{}".format(ensure_double_backslashes(self.database_host))
  250. if self.database_port is not None and self.database_port != "":
  251. databaseUrl += ":{}".format(self.database_port)
  252. databaseUrl += ";databaseName={}".format(self.database_name)
  253. if(self.use_windows_authentication):
  254. databaseUrl += ";integratedSecurity=true"
  255. #No need to append the username and password, the Ambari server adds them by itself when connecting to the database
  256. return databaseUrl
  257. def _store_db_auth_config(self, properties, keys):
  258. if (self.use_windows_authentication):
  259. properties.process_pair(keys.integrated_auth_key, "True")
  260. properties.removeProp(keys.password_key)
  261. else:
  262. properties.process_pair(keys.integrated_auth_key, "False")
  263. properties.process_pair(keys.user_name_key, self.database_username)
  264. if self.isSecure:
  265. encrypted_password = encrypt_password(keys.password_alias, self.database_password)
  266. if self.database_password != encrypted_password:
  267. properties.process_pair(keys.password_key, encrypted_password)
  268. else:
  269. passwordFile = store_password_file(self.database_password, keys.password_filename)
  270. properties.process_pair(keys.password_key, passwordFile)
  271. def _populate_database_structure(self):
  272. # Setup DB
  273. os.environ[self.env_var_db_name] = self.database_name
  274. os.environ[self.env_var_db_log_name] = self.database_name + '_log'
  275. os.environ[self.env_var_db_owner] = 'hadoop'
  276. # Don't create the database, assume it already exists. Just clear out the known tables structure
  277. SQLServerConfig._execute_db_script(self.database_host, self.drop_tables_script_file)
  278. # Init DB
  279. SQLServerConfig._execute_db_script(self.database_host, self.init_script_file)
  280. pass
  281. @staticmethod
  282. def _execute_db_script(databaseHost, databaseScript):
  283. dbCmd = 'sqlcmd -S {} -i {}'.format(databaseHost, databaseScript)
  284. retCode, outData, errData = run_os_command(['cmd', '/C', dbCmd])
  285. if not retCode == 0:
  286. err = 'Running database create script failed. Error output: {} Output: {} Exiting.'.format(errData, outData)
  287. raise FatalException(retCode, err)
  288. print_info_msg("sqlcmd output:")
  289. print_info_msg(outData)
  290. pass
  291. # SQL Server Ambari database configuration and setup
  292. class SQLServerAmbariDBConfig(SQLServerConfig):
  293. def __init__(self, options, properties):
  294. self.dbPropKeys = DbPropKeys(
  295. JDBC_DATABASE_PROPERTY,
  296. JDBC_DRIVER_PROPERTY,
  297. JDBC_HOSTNAME_PROPERTY,
  298. JDBC_PORT_PROPERTY,
  299. JDBC_DATABASE_NAME_PROPERTY,
  300. JDBC_URL_PROPERTY)
  301. self.dbAuthKeys = AuthenticationKeys(
  302. JDBC_USE_INTEGRATED_AUTH_PROPERTY,
  303. JDBC_USER_NAME_PROPERTY,
  304. JDBC_PASSWORD_PROPERTY,
  305. JDBC_RCA_PASSWORD_ALIAS,
  306. JDBC_PASSWORD_FILENAME
  307. )
  308. super(SQLServerAmbariDBConfig, self).__init__(options, properties)
  309. if self.database_name is None or self.database_name is "":
  310. self.database_name = AMBARI_DATABASE_NAME
  311. self.persistence_property = PERSISTENCE_TYPE_PROPERTY
  312. self.db_title = "ambari"
  313. self.env_var_db_name ='AMBARIDBNAME'
  314. self.env_var_db_log_name = 'AMBARIDBLOGNAME'
  315. self.env_var_db_owner = 'AMBARIDBOWNER'
  316. # The values from options supersede the values from properties
  317. if options.init_db_script_file is not None and options.init_db_script_file is not "":
  318. self.init_script_file = compress_backslashes(options.init_db_script_file)
  319. else:
  320. self.init_script_file = "resources" + os.path.sep + "Ambari-DDL-SQLServer-CREATE.sql"
  321. if options.cleanup_db_script_file is not None and options.cleanup_db_script_file is not "":
  322. self.drop_tables_script_file = compress_backslashes(options.cleanup_db_script_file)
  323. else:
  324. self.drop_tables_script_file = "resources" + os.path.sep + "Ambari-DDL-SQLServer-DROP.sql"
  325. pass
  326. def _setup_remote_server(self, properties):
  327. super(SQLServerAmbariDBConfig, self)._setup_remote_server(properties)
  328. properties.process_pair(JDBC_RCA_DRIVER_PROPERTY, self.driver_name)
  329. properties.process_pair(JDBC_RCA_HOSTNAME_PROPERTY, ensure_double_backslashes(self.database_host))
  330. if self.database_port is not None and self.database_port != "":
  331. properties.process_pair(JDBC_RCA_PORT_PROPERTY, self.database_port)
  332. properties.process_pair(JDBC_RCA_SCHEMA_PROPERTY, self.database_name)
  333. authKeys = AuthenticationKeys(
  334. JDBC_RCA_USE_INTEGRATED_AUTH_PROPERTY,
  335. JDBC_RCA_USER_NAME_PROPERTY,
  336. JDBC_RCA_PASSWORD_FILE_PROPERTY,
  337. JDBC_RCA_PASSWORD_ALIAS,
  338. JDBC_PASSWORD_FILENAME
  339. )
  340. self._store_db_auth_config(properties, authKeys)
  341. properties.process_pair(JDBC_RCA_URL_PROPERTY, self.database_url)
  342. pass
  343. # SQL Server Metrics database configuration and setup
  344. class SQLServerMetricsDBConfig(SQLServerConfig):
  345. def __init__(self, options, properties):
  346. self.dbPropKeys = DbPropKeys(
  347. JDBC_METRICS_DATABASE_PROPERTY,
  348. JDBC_METRICS_DRIVER_PROPERTY,
  349. JDBC_METRICS_HOSTNAME_PROPERTY,
  350. JDBC_METRICS_PORT_PROPERTY,
  351. JDBC_METRICS_SCHEMA_PROPERTY,
  352. JDBC_METRICS_URL_PROPERTY)
  353. self.dbAuthKeys = AuthenticationKeys(
  354. JDBC_METRICS_USE_INTEGRATED_AUTH_PROPERTY,
  355. JDBC_METRICS_USER_NAME_PROPERTY,
  356. JDBC_METRICS_PASSWORD_PROPERTY,
  357. JDBC_METRICS_PASSWORD_ALIAS,
  358. JDBC_METRICS_PASSWORD_FILENAME
  359. )
  360. super(SQLServerMetricsDBConfig, self).__init__(options, properties)
  361. self.database_name = METRICS_DATABASE_NAME
  362. self.persistence_property = METRICS_PERSISTENCE_TYPE_PROPERTY
  363. self.db_title = "metrics"
  364. self.env_var_db_name ='METRICSDBNAME'
  365. self.env_var_db_log_name = 'METRICSDBLOGNAME'
  366. self.env_var_db_owner = 'METRICSDBOWNER'
  367. if options.init_metrics_db_script_file is not None and options.init_metrics_db_script_file is not "":
  368. self.init_script_file = compress_backslashes(options.init_db_script_file)
  369. else:
  370. self.init_script_file = "resources" + os.sep + "Hadoop-Metrics-SQLServer-CREATE.sql"
  371. if options.cleanup_metrics_db_script_file is not None and options.cleanup_metrics_db_script_file is not "":
  372. self.drop_tables_script_file = compress_backslashes(options.cleanup_db_script_file)
  373. else:
  374. self.drop_tables_script_file = "resources" + os.sep + "Hadoop-Metrics-SQLServer-DROP.sql"
  375. pass
  376. # SQL Server database
  377. class SQLServerDatabase:
  378. def __init__(self):
  379. #Init the database connection here
  380. pass
  381. def get_running_status(self):
  382. #if the connection is active, return running
  383. #else return stopped
  384. return DB_STATUS_RUNNING_DEFAULT