dbConfiguration_linux.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  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 fileinput
  18. import glob
  19. import os
  20. import re
  21. import shutil
  22. import socket
  23. import subprocess
  24. import sys
  25. import time
  26. from ambari_commons import OSCheck, OSConst
  27. from ambari_commons.logging_utils import get_silent, get_verbose, print_error_msg, print_info_msg, print_warning_msg
  28. from ambari_commons.exceptions import NonFatalException, FatalException
  29. from ambari_commons.os_utils import copy_files, find_in_path, is_root, remove_file, run_os_command
  30. from ambari_server.dbConfiguration import DBMSConfig, USERNAME_PATTERN, SETUP_DB_CONNECT_ATTEMPTS, \
  31. SETUP_DB_CONNECT_TIMEOUT, STORAGE_TYPE_LOCAL, DEFAULT_USERNAME, DEFAULT_PASSWORD
  32. from ambari_server.serverConfiguration import get_ambari_properties, get_value_from_properties, configDefaults, \
  33. OS_TYPE, AMBARI_PROPERTIES_FILE, RESOURCES_DIR_PROPERTY, \
  34. JDBC_DATABASE_PROPERTY, JDBC_DATABASE_NAME_PROPERTY, JDBC_POSTGRES_SCHEMA_PROPERTY, \
  35. JDBC_HOSTNAME_PROPERTY, JDBC_PORT_PROPERTY, \
  36. JDBC_USER_NAME_PROPERTY, JDBC_PASSWORD_PROPERTY, JDBC_PASSWORD_FILENAME, \
  37. JDBC_DRIVER_PROPERTY, JDBC_URL_PROPERTY, \
  38. JDBC_RCA_USER_NAME_PROPERTY, JDBC_RCA_PASSWORD_ALIAS, JDBC_RCA_PASSWORD_FILE_PROPERTY, \
  39. JDBC_RCA_DRIVER_PROPERTY, JDBC_RCA_URL_PROPERTY, \
  40. PERSISTENCE_TYPE_PROPERTY, encrypt_password, store_password_file
  41. from ambari_server.userInput import get_YN_input, get_validated_string_input, read_password
  42. from ambari_server.utils import get_postgre_hba_dir, get_postgre_running_status
  43. ORACLE_DB_ID_TYPES = ["Service Name", "SID"]
  44. ORACLE_SNAME_PATTERN = "jdbc:oracle:thin:@.+:.+:.+"
  45. JDBC_PROPERTIES_PREFIX = "server.jdbc.properties."
  46. class LinuxDBMSConfig(DBMSConfig):
  47. def __init__(self, options, properties, storage_type):
  48. super(LinuxDBMSConfig, self).__init__(options, properties, storage_type)
  49. #Init the database configuration data here, if any
  50. self.dbms_full_name = ""
  51. # The values from options supersede the values from properties
  52. self.database_host = DBMSConfig._init_member_with_prop_default(options, "database_host",
  53. properties, JDBC_HOSTNAME_PROPERTY, "localhost")
  54. #self.database_port is set in the subclasses
  55. self.database_name = DBMSConfig._init_member_with_prop_default(options, "database_name",
  56. properties, JDBC_DATABASE_NAME_PROPERTY, configDefaults.DEFAULT_DB_NAME)
  57. self.database_username = DBMSConfig._init_member_with_prop_default(options, "database_username",
  58. properties, JDBC_USER_NAME_PROPERTY, DEFAULT_USERNAME)
  59. self.database_password = getattr(options, "database_password", "")
  60. if not self.database_password:
  61. self.database_password = DBMSConfig._read_password_from_properties(properties)
  62. self.database_url_pattern = ""
  63. self.database_url_pattern_alt = ""
  64. self.database_storage_name = ""
  65. self.sid_or_sname = "sname"
  66. self.init_script_file = ""
  67. self.drop_tables_script_file = ""
  68. self.client_tool_usage_pattern = ""
  69. self.jdbc_extra_params = []
  70. def _prompt_db_properties(self):
  71. if self.must_set_database_options:
  72. if self.persistence_type != STORAGE_TYPE_LOCAL:
  73. self.database_host = get_validated_string_input(
  74. "Hostname (" + self.database_host + "): ",
  75. self.database_host,
  76. "^[a-zA-Z0-9.\-]*$",
  77. "Invalid hostname.",
  78. False
  79. )
  80. self.database_port = get_validated_string_input(
  81. "Port (" + self.database_port + "): ",
  82. self.database_port,
  83. "^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$",
  84. "Invalid port.",
  85. False
  86. )
  87. if not self._configure_database_name():
  88. return False
  89. # Username is common for Oracle/MySQL/Postgres
  90. self.database_username = get_validated_string_input(
  91. 'Username (' + self.database_username + '): ',
  92. self.database_username,
  93. USERNAME_PATTERN,
  94. "Invalid characters in username. Start with _ or alpha "
  95. "followed by alphanumeric or _ or - characters",
  96. False
  97. )
  98. self.database_password = LinuxDBMSConfig._configure_database_password(True)
  99. self._display_db_properties()
  100. return True
  101. # Supporting remote server for all the DB types. Supporting local server only for PostgreSQL.
  102. def _setup_remote_server(self, args):
  103. self._store_remote_properties(args)
  104. def _setup_remote_database(self):
  105. properties = get_ambari_properties()
  106. if properties == -1:
  107. err = 'Error getting ambari properties'
  108. print_error_msg(err)
  109. raise FatalException(-1, err)
  110. if self.ensure_jdbc_driver_installed(properties):
  111. print 'Configuring remote database connection properties...'
  112. retcode = self._setup_remote_db()
  113. if retcode == -1:
  114. err = "Remote database setup aborted."
  115. raise NonFatalException(err)
  116. if not retcode == 0:
  117. err = 'Error while configuring connection properties. Exiting'
  118. raise FatalException(retcode, err)
  119. def _reset_remote_database(self):
  120. client_usage_cmd_drop = self._get_remote_script_line(self.drop_tables_script_file)
  121. client_usage_cmd_init = self._get_remote_script_line(self.init_script_file)
  122. print_warning_msg('To reset Ambari Server schema ' +
  123. 'you must run the following DDL against the database to '
  124. + 'drop the schema:' + os.linesep + client_usage_cmd_drop
  125. + os.linesep + 'Then you must run the following DDL ' +
  126. 'against the database to create the schema: ' + os.linesep +
  127. client_usage_cmd_init + os.linesep)
  128. def _install_jdbc_driver(self, properties, files_list):
  129. if type(files_list) is not int:
  130. print 'Copying JDBC drivers to server resources...'
  131. try:
  132. resources_dir = properties[RESOURCES_DIR_PROPERTY]
  133. except KeyError:
  134. print_error_msg("There is no value for " + RESOURCES_DIR_PROPERTY + "in " + AMBARI_PROPERTIES_FILE)
  135. return False
  136. db_name = self.dbms_full_name.lower()
  137. symlink_name = db_name + "-jdbc-driver.jar"
  138. jdbc_symlink = os.path.join(resources_dir, symlink_name)
  139. db_default_driver_path = os.path.join(configDefaults.JAVA_SHARE_PATH, self.driver_file_name)
  140. if os.path.lexists(jdbc_symlink):
  141. os.remove(jdbc_symlink)
  142. copy_status = copy_files(files_list, resources_dir)
  143. if not copy_status == 0:
  144. raise FatalException(-1, "Failed to copy JDBC drivers to server resources")
  145. if db_default_driver_path in files_list:
  146. os.symlink(os.path.join(resources_dir, self.driver_file_name), jdbc_symlink)
  147. else:
  148. if files_list == -1:
  149. return False
  150. return True
  151. def _configure_database_name(self):
  152. return True
  153. def _get_remote_script_line(self, scriptFile):
  154. return None
  155. @staticmethod
  156. def _configure_database_password(showDefault=True):
  157. passwordDefault = DEFAULT_PASSWORD
  158. if showDefault:
  159. passwordPrompt = 'Enter Database Password (' + passwordDefault + '): '
  160. else:
  161. passwordPrompt = 'Enter Database Password: '
  162. passwordPattern = "^[a-zA-Z0-9_-]*$"
  163. passwordDescr = "Invalid characters in password. Use only alphanumeric or " \
  164. "_ or - characters"
  165. password = read_password(passwordDefault, passwordPattern, passwordPrompt,
  166. passwordDescr)
  167. return password
  168. @staticmethod
  169. def _get_validated_db_name(database_storage_name, database_name):
  170. return get_validated_string_input(
  171. database_storage_name + " name ("
  172. + database_name + "): ",
  173. database_name,
  174. ".*",
  175. "Invalid " + database_storage_name.lower() + " name.",
  176. False
  177. )
  178. def _display_db_properties(self):
  179. print_info_msg('Using database options: {database},{host},{port},{schema},{user},{password}'.format(
  180. database=self.dbms,
  181. host=self.database_host,
  182. port=self.database_port,
  183. schema=self.database_name,
  184. user=self.database_username,
  185. password=self.database_password
  186. ))
  187. #Check if required jdbc drivers present
  188. @staticmethod
  189. def _find_jdbc_driver(jdbc_pattern):
  190. drivers = []
  191. drivers.extend(glob.glob(configDefaults.JAVA_SHARE_PATH + os.sep + jdbc_pattern))
  192. if drivers:
  193. return drivers
  194. return -1
  195. # Let the console user initialize the remote database schema
  196. def _setup_remote_db(self):
  197. setup_msg = "Before starting Ambari Server, you must run the following DDL " \
  198. "against the database to create the schema: {0}".format(self.init_script_file)
  199. print_warning_msg(setup_msg)
  200. proceed = get_YN_input("Proceed with configuring remote database connection properties [y/n] (y)? ", True)
  201. retCode = 0 if proceed else -1
  202. return retCode
  203. def _store_password_property(self, properties, property_name):
  204. properties.process_pair(property_name,
  205. store_password_file(self.database_password, JDBC_PASSWORD_FILENAME))
  206. if self.isSecure:
  207. encrypted_password = encrypt_password(JDBC_RCA_PASSWORD_ALIAS, self.database_password)
  208. if encrypted_password != self.database_password:
  209. properties.process_pair(property_name, encrypted_password)
  210. # Store set of properties for remote database connection
  211. def _store_remote_properties(self, properties):
  212. properties.process_pair(PERSISTENCE_TYPE_PROPERTY, self.persistence_type)
  213. properties.process_pair(JDBC_DATABASE_PROPERTY, self.dbms)
  214. properties.process_pair(JDBC_HOSTNAME_PROPERTY, self.database_host)
  215. properties.process_pair(JDBC_PORT_PROPERTY, self.database_port)
  216. properties.process_pair(JDBC_DATABASE_NAME_PROPERTY, self.database_name)
  217. properties.process_pair(JDBC_DRIVER_PROPERTY, self.driver_class_name)
  218. # fully qualify the hostname to make sure all the other hosts can connect
  219. # to the jdbc hostname since its passed onto the agents for RCA
  220. jdbc_hostname = self.database_host
  221. if (self.database_host == "localhost"):
  222. jdbc_hostname = socket.getfqdn()
  223. connectionStringFormat = self.database_url_pattern
  224. if self.sid_or_sname == "sid":
  225. connectionStringFormat = self.database_url_pattern_alt
  226. properties.process_pair(JDBC_URL_PROPERTY, connectionStringFormat.format(jdbc_hostname, self.database_port, self.database_name))
  227. properties.process_pair(JDBC_USER_NAME_PROPERTY, self.database_username)
  228. self._store_password_property(properties, JDBC_PASSWORD_PROPERTY)
  229. # save any other defined properties to pass to JDBC
  230. for pair in self.jdbc_extra_params:
  231. properties.process_pair(JDBC_PROPERTIES_PREFIX + pair[0], pair[1])
  232. properties.process_pair(JDBC_RCA_DRIVER_PROPERTY, self.driver_class_name)
  233. properties.process_pair(JDBC_RCA_URL_PROPERTY, self.database_url_pattern.format(jdbc_hostname, self.database_port, self.database_name))
  234. properties.process_pair(JDBC_RCA_USER_NAME_PROPERTY, self.database_username)
  235. self._store_password_property(properties, JDBC_RCA_PASSWORD_FILE_PROPERTY)
  236. # PostgreSQL configuration and setup
  237. class PGConfig(LinuxDBMSConfig):
  238. # PostgreSQL settings
  239. SETUP_DB_CMD = ['su', '-', 'postgres',
  240. '--command=psql -f {0} -v username=\'"{1}"\' -v password="\'{2}\'" -v dbname="{3}"']
  241. UPGRADE_STACK_CMD = ['su', 'postgres',
  242. '--command=psql -f {0} -v stack_name="\'{1}\'" -v stack_version="\'{2}\'" -v dbname="{3}"']
  243. CHANGE_OWNER_COMMAND = ['su', '-', 'postgres',
  244. '--command=/var/lib/ambari-server/resources/scripts/change_owner.sh -d {0} -s {1} -o {2}']
  245. PG_ERROR_BLOCKED = "is being accessed by other users"
  246. PG_STATUS_RUNNING = None
  247. SERVICE_CMD = "/usr/bin/env service"
  248. PG_SERVICE_NAME = "postgresql"
  249. PG_HBA_DIR = None
  250. PG_ST_CMD = "%s %s status" % (SERVICE_CMD, PG_SERVICE_NAME)
  251. if os.path.isfile("/usr/bin/postgresql-setup"):
  252. PG_INITDB_CMD = "/usr/bin/postgresql-setup initdb"
  253. else:
  254. PG_INITDB_CMD = "%s %s initdb" % (SERVICE_CMD, PG_SERVICE_NAME)
  255. PG_START_CMD = "%s %s start" % (SERVICE_CMD, PG_SERVICE_NAME)
  256. PG_RESTART_CMD = "%s %s restart" % (SERVICE_CMD, PG_SERVICE_NAME)
  257. PG_HBA_RELOAD_CMD = "%s %s reload" % (SERVICE_CMD, PG_SERVICE_NAME)
  258. PG_HBA_CONF_FILE = None
  259. PG_HBA_CONF_FILE_BACKUP = None
  260. POSTGRESQL_CONF_FILE = None
  261. POSTGRES_EMBEDDED_INIT_FILE = "/var/lib/ambari-server/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql"
  262. POSTGRES_EMBEDDED_DROP_FILE = "/var/lib/ambari-server/resources/Ambari-DDL-Postgres-EMBEDDED-DROP.sql"
  263. POSTGRES_INIT_FILE = "/var/lib/ambari-server/resources/Ambari-DDL-Postgres-CREATE.sql"
  264. POSTGRES_DROP_FILE = "/var/lib/ambari-server/resources/Ambari-DDL-Postgres-DROP.sql"
  265. def __init__(self, options, properties, storage_type):
  266. super(PGConfig, self).__init__(options, properties, storage_type)
  267. #Init the database configuration data here, if any
  268. self.dbms = "postgres"
  269. self.dbms_full_name = "PostgreSQL"
  270. self.driver_class_name = "org.postgresql.Driver"
  271. self.driver_file_name = "postgresql-jdbc.jar"
  272. self.database_storage_name = "Database"
  273. # PostgreSQL seems to require additional schema coordinates
  274. self.postgres_schema = DBMSConfig._init_member_with_prop_default(options, "postgres_schema",
  275. properties, JDBC_POSTGRES_SCHEMA_PROPERTY, self.database_name)
  276. self.database_port = DBMSConfig._init_member_with_prop_default(options, "database_port",
  277. properties, JDBC_PORT_PROPERTY, "5432")
  278. self.database_url_pattern = "jdbc:postgresql://{0}:{1}/{2}"
  279. self.database_url_pattern_alt = "jdbc:postgresql://{0}:{1}/{2}"
  280. self.JDBC_DRIVER_INSTALL_MSG = 'Before starting Ambari Server, ' \
  281. 'you must copy the {0} JDBC driver JAR file to {1}.'.format(
  282. self.dbms_full_name, configDefaults.JAVA_SHARE_PATH)
  283. self._is_user_changed = False
  284. if self.persistence_type == STORAGE_TYPE_LOCAL:
  285. PGConfig.PG_STATUS_RUNNING = get_postgre_running_status(OS_TYPE)
  286. PGConfig.PG_HBA_DIR = get_postgre_hba_dir(OSCheck.get_os_family())
  287. PGConfig.PG_HBA_CONF_FILE = os.path.join(PGConfig.PG_HBA_DIR, "pg_hba.conf")
  288. PGConfig.PG_HBA_CONF_FILE_BACKUP = os.path.join(PGConfig.PG_HBA_DIR, "pg_hba_bak.conf.old")
  289. PGConfig.POSTGRESQL_CONF_FILE = os.path.join(PGConfig.PG_HBA_DIR, "postgresql.conf")
  290. postgres_init_file_default = PGConfig.POSTGRES_EMBEDDED_INIT_FILE
  291. postgres_drop_file_default = PGConfig.POSTGRES_EMBEDDED_DROP_FILE
  292. else:
  293. postgres_init_file_default = PGConfig.POSTGRES_INIT_FILE
  294. postgres_drop_file_default = PGConfig.POSTGRES_DROP_FILE
  295. self.init_script_file = DBMSConfig._init_member_with_default(options, "init_script_file",
  296. postgres_init_file_default)
  297. self.drop_tables_script_file = DBMSConfig._init_member_with_default(options, "drop_script_file",
  298. postgres_drop_file_default)
  299. self.client_tool_usage_pattern = 'su -postgres --command=psql -f {0} -v username=\'"{1}"\' -v password="\'{2}\'"'
  300. #
  301. # Public methods
  302. #
  303. def ensure_dbms_is_running(self, options, properties, scmStatus=None):
  304. if self._is_local_database():
  305. if is_root():
  306. (pg_status, retcode, out, err) = PGConfig._check_postgre_up()
  307. if not retcode == 0:
  308. err = 'Unable to start PostgreSQL server. Status {0}. {1}. Exiting'.format(pg_status, err)
  309. raise FatalException(retcode, err)
  310. else:
  311. print "Unable to check PostgreSQL server status when starting " \
  312. "without root privileges."
  313. print "Please do not forget to start PostgreSQL server."
  314. #
  315. # Private implementation
  316. #
  317. # Supporting remote server for all the DB types. Supporting local server only for PostgreSQL.
  318. def _setup_local_server(self, properties):
  319. # check if jdbc user is changed
  320. self._is_user_changed = PGConfig._is_jdbc_user_changed(self.database_username)
  321. print 'Default properties detected. Using built-in database.'
  322. self._store_local_properties(properties)
  323. def _setup_local_database(self):
  324. print 'Checking PostgreSQL...'
  325. (pg_status, retcode, out, err) = PGConfig._check_postgre_up()
  326. if not retcode == 0:
  327. err = 'Unable to start PostgreSQL server. Exiting'
  328. raise FatalException(retcode, err)
  329. print 'Configuring local database...'
  330. retcode, out, err = self._setup_db()
  331. if not retcode == 0:
  332. err = 'Running database init script failed. Exiting.'
  333. raise FatalException(retcode, err)
  334. if self._is_user_changed:
  335. #remove backup for pg_hba in order to reconfigure postgres
  336. remove_file(PGConfig.PG_HBA_CONF_FILE_BACKUP)
  337. print 'Configuring PostgreSQL...'
  338. retcode, out, err = self._configure_postgres()
  339. if not retcode == 0:
  340. err = 'Unable to configure PostgreSQL server. Exiting'
  341. raise FatalException(retcode, err)
  342. def _reset_local_database(self):
  343. #force reset if silent option provided
  344. if get_silent():
  345. default = "yes"
  346. else:
  347. default = "no"
  348. # Run automatic reset only for embedded DB
  349. okToRun = get_YN_input("Confirm server reset [yes/no]({0})? ".format(default), get_silent())
  350. if not okToRun:
  351. err = "Ambari Server 'reset' cancelled"
  352. raise FatalException(1, err)
  353. print "Resetting the Server database..."
  354. dbname = self.database_name
  355. filename = self.drop_tables_script_file
  356. username = self.database_username
  357. password = self.database_password
  358. command = PGConfig.SETUP_DB_CMD[:]
  359. command[-1] = command[-1].format(filename, username, password, dbname)
  360. drop_retcode, drop_outdata, drop_errdata = run_os_command(command)
  361. if not drop_retcode == 0:
  362. raise FatalException(1, drop_errdata)
  363. if drop_errdata and PGConfig.PG_ERROR_BLOCKED in drop_errdata:
  364. raise FatalException(1, "Database is in use. Please, make sure all connections to the database are closed")
  365. if drop_errdata and get_verbose():
  366. print_warning_msg(drop_errdata)
  367. print_info_msg("About to run database setup")
  368. retcode, outdata, errdata = self._setup_db()
  369. if errdata and get_verbose():
  370. print_warning_msg(errdata)
  371. if (errdata and 'ERROR' in errdata.upper()) or (drop_errdata and 'ERROR' in drop_errdata.upper()):
  372. err = "Non critical error in DDL"
  373. if not get_verbose():
  374. err += ", use --verbose for more information"
  375. raise NonFatalException(err)
  376. def _reset_remote_database(self):
  377. super(PGConfig, self)._reset_remote_database()
  378. raise NonFatalException("Please set DB password to PGPASSWORD env variable before running DDL`s!")
  379. def _is_jdbc_driver_installed(self, properties):
  380. return 0
  381. def _configure_database_name(self):
  382. self.database_name = LinuxDBMSConfig._get_validated_db_name(self.database_storage_name, self.database_name)
  383. self.postgres_schema = PGConfig._get_validated_db_schema(self.postgres_schema)
  384. return True
  385. def _get_remote_script_line(self, scriptFile):
  386. os.environ["PGPASSWORD"] = self.database_password
  387. return "psql -h {0} -p {1} -d {2} -U {3} -f {4} -v username='{3}'".format(
  388. self.database_host,
  389. self.database_port,
  390. self.database_name,
  391. self.database_username,
  392. scriptFile
  393. )
  394. @staticmethod
  395. def _get_validated_db_schema(postgres_schema):
  396. return get_validated_string_input(
  397. "Postgres schema (" + postgres_schema + "): ",
  398. postgres_schema,
  399. "^[a-zA-Z0-9_\-]*$",
  400. "Invalid schema name.",
  401. False, allowEmpty=True
  402. )
  403. # Check if jdbc user is changed
  404. @staticmethod
  405. def _is_jdbc_user_changed(database_username):
  406. properties = get_ambari_properties()
  407. if properties == -1:
  408. print_error_msg("Error getting ambari properties")
  409. return None
  410. previos_user = get_value_from_properties(properties, JDBC_USER_NAME_PROPERTY, "")
  411. if previos_user and database_username:
  412. if previos_user != database_username:
  413. return True
  414. else:
  415. return False
  416. return None
  417. # Store local database connection properties
  418. def _store_local_properties(self, properties):
  419. properties.removeOldProp(JDBC_DATABASE_PROPERTY)
  420. properties.removeOldProp(JDBC_DATABASE_NAME_PROPERTY)
  421. properties.removeOldProp(JDBC_POSTGRES_SCHEMA_PROPERTY)
  422. properties.removeOldProp(JDBC_HOSTNAME_PROPERTY)
  423. properties.removeOldProp(JDBC_RCA_DRIVER_PROPERTY)
  424. properties.removeOldProp(JDBC_RCA_URL_PROPERTY)
  425. properties.removeOldProp(JDBC_PORT_PROPERTY)
  426. properties.removeOldProp(JDBC_DRIVER_PROPERTY)
  427. properties.removeOldProp(JDBC_URL_PROPERTY)
  428. # Store the properties
  429. properties.process_pair(PERSISTENCE_TYPE_PROPERTY, self.persistence_type)
  430. properties.process_pair(JDBC_DATABASE_PROPERTY, self.dbms)
  431. properties.process_pair(JDBC_DATABASE_NAME_PROPERTY, self.database_name)
  432. properties.process_pair(JDBC_POSTGRES_SCHEMA_PROPERTY, self.postgres_schema)
  433. properties.process_pair(JDBC_USER_NAME_PROPERTY, self.database_username)
  434. self._store_password_property(properties, JDBC_PASSWORD_PROPERTY)
  435. @staticmethod
  436. def _get_postgre_status():
  437. retcode, out, err = run_os_command(PGConfig.PG_ST_CMD)
  438. try:
  439. pg_status = re.search('(stopped|running)', out, re.IGNORECASE).group(0).lower()
  440. except AttributeError:
  441. pg_status = None
  442. return pg_status, retcode, out, err
  443. @staticmethod
  444. def _check_postgre_up():
  445. pg_status, retcode, out, err = PGConfig._get_postgre_status()
  446. if pg_status == PGConfig.PG_STATUS_RUNNING:
  447. print_info_msg("PostgreSQL is running")
  448. return pg_status, 0, out, err
  449. else:
  450. # run initdb only on non ubuntu systems as ubuntu does not have initdb cmd.
  451. if OS_TYPE != OSConst.OS_UBUNTU:
  452. print "Running initdb: This may take upto a minute."
  453. retcode, out, err = run_os_command(PGConfig.PG_INITDB_CMD)
  454. if retcode == 0:
  455. print out
  456. print "About to start PostgreSQL"
  457. try:
  458. process = subprocess.Popen(PGConfig.PG_START_CMD.split(' '),
  459. stdout=subprocess.PIPE,
  460. stdin=subprocess.PIPE,
  461. stderr=subprocess.PIPE
  462. )
  463. if OS_TYPE == OSConst.OS_SUSE:
  464. time.sleep(20)
  465. result = process.poll()
  466. print_info_msg("Result of postgres start cmd: " + str(result))
  467. if result is None:
  468. process.kill()
  469. pg_status, retcode, out, err = PGConfig._get_postgre_status()
  470. else:
  471. retcode = result
  472. else:
  473. out, err = process.communicate()
  474. retcode = process.returncode
  475. pg_status, retcode, out, err = PGConfig._get_postgre_status()
  476. if pg_status == PGConfig.PG_STATUS_RUNNING:
  477. print_info_msg("Postgres process is running. Returning...")
  478. return pg_status, 0, out, err
  479. except (Exception), e:
  480. pg_status, retcode, out, err = PGConfig._get_postgre_status()
  481. if pg_status == PGConfig.PG_STATUS_RUNNING:
  482. return pg_status, 0, out, err
  483. else:
  484. print_error_msg("Postgres start failed. " + str(e))
  485. return pg_status, retcode, out, err
  486. def _setup_db(self):
  487. #password access to ambari-server and mapred
  488. dbname = self.database_name
  489. scriptFile = PGConfig.POSTGRES_EMBEDDED_INIT_FILE
  490. username = self.database_username
  491. password = self.database_password
  492. #setup DB
  493. command = PGConfig.SETUP_DB_CMD[:]
  494. command[-1] = command[-1].format(scriptFile, username, password, dbname)
  495. for i in range(SETUP_DB_CONNECT_ATTEMPTS):
  496. sys.stdout.write('Connecting to local database...')
  497. retcode, outdata, errdata = run_os_command(command)
  498. if retcode == 0:
  499. print 'done.'
  500. return retcode, outdata, errdata
  501. timeOutMsg = 'connection timed out'
  502. if (i+1) < SETUP_DB_CONNECT_ATTEMPTS:
  503. timeOutMsg += '...retrying (%d)' % (i+1)
  504. print timeOutMsg
  505. time.sleep(SETUP_DB_CONNECT_TIMEOUT)
  506. print 'unable to connect to database'
  507. print_error_msg(errdata)
  508. return retcode, outdata, errdata
  509. @staticmethod
  510. def _configure_pg_hba_ambaridb_users(conf_file, database_username):
  511. with open(conf_file, "a") as pgHbaConf:
  512. pgHbaConf.write("\n")
  513. pgHbaConf.write("local all " + database_username +
  514. ",mapred md5")
  515. pgHbaConf.write("\n")
  516. pgHbaConf.write("host all " + database_username +
  517. ",mapred 0.0.0.0/0 md5")
  518. pgHbaConf.write("\n")
  519. pgHbaConf.write("host all " + database_username +
  520. ",mapred ::/0 md5")
  521. pgHbaConf.write("\n")
  522. retcode, out, err = run_os_command(PGConfig.PG_HBA_RELOAD_CMD)
  523. if not retcode == 0:
  524. raise FatalException(retcode, err)
  525. @staticmethod
  526. def _configure_pg_hba_postgres_user():
  527. postgresString = "all postgres"
  528. for line in fileinput.input(PGConfig.PG_HBA_CONF_FILE, inplace=1):
  529. print re.sub('all\s*all', postgresString, line),
  530. os.chmod(PGConfig.PG_HBA_CONF_FILE, 0644)
  531. @staticmethod
  532. def _configure_postgresql_conf():
  533. listenAddress = "listen_addresses = '*' #"
  534. for line in fileinput.input(PGConfig.POSTGRESQL_CONF_FILE, inplace=1):
  535. print re.sub('#+listen_addresses.*?(#|$)', listenAddress, line),
  536. os.chmod(PGConfig.POSTGRESQL_CONF_FILE, 0644)
  537. def _configure_postgres(self):
  538. if os.path.isfile(PGConfig.PG_HBA_CONF_FILE):
  539. if not os.path.isfile(PGConfig.PG_HBA_CONF_FILE_BACKUP):
  540. shutil.copyfile(PGConfig.PG_HBA_CONF_FILE, PGConfig.PG_HBA_CONF_FILE_BACKUP)
  541. else:
  542. #Postgres has been configured before, must not override backup
  543. print "Backup for pg_hba found, reconfiguration not required"
  544. return 0, "", ""
  545. PGConfig._configure_pg_hba_postgres_user()
  546. PGConfig._configure_pg_hba_ambaridb_users(PGConfig.PG_HBA_CONF_FILE, self.database_username)
  547. os.chmod(PGConfig.PG_HBA_CONF_FILE, 0644)
  548. PGConfig._configure_postgresql_conf()
  549. #restart postgresql if already running
  550. pg_status, retcode, out, err = PGConfig._get_postgre_status()
  551. if pg_status == PGConfig.PG_STATUS_RUNNING:
  552. retcode, out, err = PGConfig._restart_postgres()
  553. return retcode, out, err
  554. return 0, "", ""
  555. @staticmethod
  556. def _restart_postgres():
  557. print "Restarting PostgreSQL"
  558. process = subprocess.Popen(PGConfig.PG_RESTART_CMD.split(' '),
  559. stdout=subprocess.PIPE,
  560. stdin=subprocess.PIPE,
  561. stderr=subprocess.PIPE
  562. )
  563. time.sleep(5)
  564. result = process.poll()
  565. if result is None:
  566. print_info_msg("Killing restart PostgresSQL process")
  567. process.kill()
  568. pg_status, retcode, out, err = PGConfig._get_postgre_status()
  569. # SUSE linux set status of stopped postgresql proc to unused
  570. if pg_status == "unused" or pg_status == "stopped":
  571. print_info_msg("PostgreSQL is stopped. Restarting ...")
  572. retcode, out, err = run_os_command(PGConfig.PG_START_CMD)
  573. return retcode, out, err
  574. return 0, "", ""
  575. def _store_remote_properties(self, properties):
  576. super(PGConfig, self)._store_remote_properties(properties)
  577. properties.process_pair(JDBC_POSTGRES_SCHEMA_PROPERTY, self.postgres_schema)
  578. def _change_db_files_owner(self):
  579. database_name = self.database_name
  580. new_owner = self.database_username
  581. if '"' not in new_owner:
  582. #wrap to allow old username "ambari-server", postgres only
  583. new_owner = '\'"{0}"\''.format(new_owner)
  584. pass
  585. command = PGConfig.CHANGE_OWNER_COMMAND[:]
  586. command[-1] = command[-1].format(database_name, 'ambari', new_owner)
  587. retcode, stdout, stderr = run_os_command(command)
  588. if not retcode == 0:
  589. if get_verbose():
  590. if stderr:
  591. print_error_msg("stderr:\n" + stderr.strip())
  592. if stdout:
  593. print_error_msg("stdout:\n" + stdout.strip())
  594. else:
  595. print_info_msg('Fixed database objects owner')
  596. return retcode
  597. def createPGConfig(options, properties, storage_type, dbId):
  598. return PGConfig(options, properties, storage_type)
  599. class OracleConfig(LinuxDBMSConfig):
  600. def __init__(self, options, properties, storage_type):
  601. super(OracleConfig, self).__init__(options, properties, storage_type)
  602. #Init the database configuration data here, if any
  603. self.dbms = "oracle"
  604. self.dbms_full_name = "Oracle"
  605. self.driver_class_name = "oracle.jdbc.driver.OracleDriver"
  606. self.driver_file_name = "ojdbc6.jar"
  607. self.driver_symlink_name = "oracle-jdbc-driver.jar"
  608. self.database_storage_name = "Service"
  609. if (hasattr(options, 'sid_or_sname') and options.sid_or_sname == "sname") or \
  610. (hasattr(options, 'jdbc_url') and options.jdbc_url and re.match(ORACLE_SNAME_PATTERN, options.jdbc_url)):
  611. print_info_msg("using SERVICE_NAME instead of SID for Oracle")
  612. self.sid_or_sname = "service_name"
  613. self.database_port = DBMSConfig._init_member_with_prop_default(options, "database_port",
  614. properties, JDBC_PORT_PROPERTY, "1521")
  615. self.database_url_pattern = "jdbc:oracle:thin:@{0}:{1}/{2}"
  616. self.database_url_pattern_alt = "jdbc:oracle:thin:@{0}:{1}:{2}"
  617. self.JDBC_DRIVER_INSTALL_MSG = 'Before starting Ambari Server, ' \
  618. 'you must copy the {0} JDBC driver JAR file to {1}.'.format(
  619. self.dbms_full_name, configDefaults.JAVA_SHARE_PATH)
  620. self.init_script_file = "/var/lib/ambari-server/resources/Ambari-DDL-Oracle-CREATE.sql'"
  621. self.drop_tables_script_file = "/var/lib/ambari-server/resources/Ambari-DDL-Oracle-DROP.sql"
  622. self.client_tool_usage_pattern = 'sqlplus {1}/{2} < {0}'
  623. self.jdbc_extra_params = [
  624. ["oracle.net.CONNECT_TIMEOUT", "2000"], # socket level timeout
  625. ["oracle.net.READ_TIMEOUT", "2000"], # socket level timeout
  626. ["oracle.jdbc.ReadTimeout", "8000"] # query fetch timeout
  627. ]
  628. #
  629. # Private implementation
  630. #
  631. def _reset_remote_database(self):
  632. super(OracleConfig, self)._reset_remote_database()
  633. raise NonFatalException("Please replace '*' symbols with password before running DDL`s!")
  634. def _is_jdbc_driver_installed(self, properties):
  635. return LinuxDBMSConfig._find_jdbc_driver("*ojdbc*.jar")
  636. def _configure_database_name(self):
  637. if self.persistence_type != STORAGE_TYPE_LOCAL:
  638. # Oracle uses service name or service id
  639. idType = "1"
  640. idType = get_validated_string_input(
  641. "Select Oracle identifier type:\n1 - " + ORACLE_DB_ID_TYPES[0] +
  642. "\n2 - " + ORACLE_DB_ID_TYPES[1] + "\n(" + idType + "): ",
  643. idType,
  644. "^[12]$",
  645. "Invalid number.",
  646. False
  647. )
  648. if idType == "2":
  649. self.sid_or_sname = "sid"
  650. IDTYPE_INDEX = int(idType) - 1
  651. self.database_name = OracleConfig._get_validated_service_name(self.database_name,
  652. IDTYPE_INDEX)
  653. else:
  654. self.database_name = LinuxDBMSConfig._get_validated_db_name(self.database_storage_name, self.database_name)
  655. return True
  656. def _get_remote_script_line(self, scriptFile):
  657. # Detect the existing sqlplus flavor
  658. try:
  659. find_in_path("sqlplus64")
  660. tool = "sqlplus64"
  661. except:
  662. tool = "sqlplus"
  663. ORACLE_EXEC_ARGS = "{0} -S -L '{1}/{2}@(description=(address=(protocol=TCP)(host={3})(port={4}))(connect_data=({7}={5})))' @{6} {1}"
  664. return ORACLE_EXEC_ARGS.format(
  665. tool,
  666. self.database_username,
  667. self.database_password,
  668. self.database_host,
  669. self.database_port,
  670. self.database_name,
  671. scriptFile,
  672. self.sid_or_sname
  673. )
  674. @staticmethod
  675. def _get_validated_service_name(service_name, index):
  676. return get_validated_string_input(
  677. ORACLE_DB_ID_TYPES[index] + " (" + service_name + "): ",
  678. service_name,
  679. ".*",
  680. "Invalid " + ORACLE_DB_ID_TYPES[index] + ".",
  681. False
  682. )
  683. def createOracleConfig(options, properties, storage_type, dbId):
  684. return OracleConfig(options, properties, storage_type)
  685. class MySQLConfig(LinuxDBMSConfig):
  686. def __init__(self, options, properties, storage_type):
  687. super(MySQLConfig, self).__init__(options, properties, storage_type)
  688. #Init the database configuration data here, if any
  689. self.dbms = "mysql"
  690. self.dbms_full_name = "MySQL"
  691. self.driver_class_name = "com.mysql.jdbc.Driver"
  692. self.driver_file_name = "mysql-connector-java.jar"
  693. self.driver_symlink_name = "mysql-jdbc-driver.jar"
  694. self.database_storage_name = "Database"
  695. self.database_port = DBMSConfig._init_member_with_prop_default(options, "database_port",
  696. properties, JDBC_PORT_PROPERTY, "3306")
  697. self.database_url_pattern = "jdbc:mysql://{0}:{1}/{2}"
  698. self.database_url_pattern_alt = "jdbc:mysql://{0}:{1}/{2}"
  699. self.JDBC_DRIVER_INSTALL_MSG = 'Before starting Ambari Server, ' \
  700. 'you must copy the {0} JDBC driver JAR file to {1}.'.format(
  701. self.dbms_full_name, configDefaults.JAVA_SHARE_PATH)
  702. self.init_script_file = "/var/lib/ambari-server/resources/Ambari-DDL-MySQL-CREATE.sql"
  703. self.drop_tables_script_file = "/var/lib/ambari-server/resources/Ambari-DDL-MySQL-DROP.sql"
  704. self.client_tool_usage_pattern = 'mysql --user={1} --password={2} {3}<{0}'
  705. #
  706. # Private implementation
  707. #
  708. def _reset_remote_database(self):
  709. super(MySQLConfig, self)._reset_remote_database()
  710. raise NonFatalException("Please replace '*' symbols with password before running DDL`s!")
  711. def _is_jdbc_driver_installed(self, properties):
  712. return LinuxDBMSConfig._find_jdbc_driver("*mysql*.jar")
  713. def _configure_database_name(self):
  714. self.database_name = LinuxDBMSConfig._get_validated_db_name(self.database_storage_name, self.database_name)
  715. return True
  716. def _get_remote_script_line(self, scriptFile):
  717. MYSQL_INIT_SCRIPT = '/var/lib/ambari-server/resources/Ambari-DDL-MySQL-CREATE.sql'
  718. MYSQL_EXEC_ARGS_WITH_USER_VARS = "mysql --host={0} --port={1} --user={2} --password={3} {4} " \
  719. "-e\"set @schema=\'{4}\'; set @username=\'{2}\'; source {5};\""
  720. MYSQL_EXEC_ARGS_WO_USER_VARS = "mysql --force --host={0} --port={1} --user={2} --password={3} --database={4} < {5} 2> /dev/null"
  721. MYSQL_EXEC_ARGS = MYSQL_EXEC_ARGS_WO_USER_VARS if MYSQL_INIT_SCRIPT == scriptFile else MYSQL_EXEC_ARGS_WITH_USER_VARS
  722. return MYSQL_EXEC_ARGS.format(
  723. self.database_host,
  724. self.database_port,
  725. self.database_username,
  726. self.database_password,
  727. self.database_name,
  728. scriptFile
  729. )
  730. def createMySQLConfig(options, properties, storage_type, dbId):
  731. return MySQLConfig(options, properties, storage_type)