serverSetup.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077
  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 optparse
  18. import os
  19. import re
  20. import shutil
  21. import sys
  22. from ambari_commons.exceptions import FatalException
  23. from ambari_commons.firewall import Firewall
  24. from ambari_commons.inet_utils import force_download_file, download_progress
  25. from ambari_commons.logging_utils import get_silent, print_info_msg, print_warning_msg, print_error_msg
  26. from ambari_commons.os_check import OSConst
  27. from ambari_commons.os_family_impl import OsFamilyFuncImpl, OsFamilyImpl
  28. from ambari_commons.os_utils import copy_files, run_os_command, is_root
  29. from ambari_commons.str_utils import compress_backslashes
  30. from ambari_server.dbConfiguration import DBMSConfigFactory, check_jdbc_drivers
  31. from ambari_server.serverConfiguration import configDefaults, JDKRelease, \
  32. get_ambari_properties, get_full_ambari_classpath, get_is_secure, get_is_persisted, get_java_exe_path, get_JAVA_HOME, \
  33. get_resources_location, get_value_from_properties, read_ambari_user, update_properties, validate_jdk, write_property, \
  34. JAVA_HOME, JAVA_HOME_PROPERTY, JCE_NAME_PROPERTY, JDBC_RCA_URL_PROPERTY, JDBC_URL_PROPERTY, \
  35. JDK_NAME_PROPERTY, JDK_RELEASES, NR_USER_PROPERTY, OS_FAMILY, OS_FAMILY_PROPERTY, OS_TYPE, OS_TYPE_PROPERTY, OS_VERSION, \
  36. SERVICE_PASSWORD_KEY, SERVICE_USERNAME_KEY, VIEWS_DIR_PROPERTY
  37. from ambari_server.serverUtils import is_server_runing
  38. from ambari_server.setupSecurity import adjust_directory_permissions
  39. from ambari_server.userInput import get_YN_input, get_validated_string_input
  40. from ambari_server.utils import locate_file
  41. # selinux commands
  42. GET_SE_LINUX_ST_CMD = locate_file('sestatus', '/usr/sbin')
  43. SE_SETENFORCE_CMD = "setenforce 0"
  44. SE_STATUS_DISABLED = "disabled"
  45. SE_STATUS_ENABLED = "enabled"
  46. SE_MODE_ENFORCING = "enforcing"
  47. SE_MODE_PERMISSIVE = "permissive"
  48. # Non-root user setup commands
  49. NR_USER_COMMENT = "Ambari user"
  50. VIEW_EXTRACT_CMD = "{0} -cp {1} " + \
  51. "org.apache.ambari.server.view.ViewRegistry extract {2} " + \
  52. "> " + configDefaults.SERVER_OUT_FILE + " 2>&1"
  53. MAKE_FILE_EXECUTABLE_CMD = "chmod a+x {0}"
  54. # use --no-same-owner when running as root to prevent uucp as the user (AMBARI-6478)
  55. UNTAR_JDK_ARCHIVE = "tar --no-same-owner -xvf {0}"
  56. JDK_PROMPT = "[{0}] {1}\n"
  57. JDK_VALID_CHOICES = "^[{0}{1:d}]$"
  58. def get_supported_jdbc_drivers():
  59. factory = DBMSConfigFactory()
  60. return factory.get_supported_jdbc_drivers()
  61. JDBC_DB_OPTION_VALUES = get_supported_jdbc_drivers()
  62. #
  63. # Setup security prerequisites
  64. #
  65. def verify_setup_allowed():
  66. if get_silent():
  67. properties = get_ambari_properties()
  68. if properties == -1:
  69. print_error_msg("Error getting ambari properties")
  70. return -1
  71. isSecure = get_is_secure(properties)
  72. if isSecure:
  73. (isPersisted, masterKeyFile) = get_is_persisted(properties)
  74. if not isPersisted:
  75. print "ERROR: Cannot run silent 'setup' with password encryption enabled " \
  76. "and Master Key not persisted."
  77. print "Ambari Server 'setup' exiting."
  78. return 1
  79. return 0
  80. #
  81. # Security enhancements (Linux only)
  82. #
  83. #
  84. # Checks SELinux
  85. #
  86. def check_selinux():
  87. try:
  88. retcode, out, err = run_os_command(GET_SE_LINUX_ST_CMD)
  89. se_status = re.search('(disabled|enabled)', out).group(0)
  90. print "SELinux status is '" + se_status + "'"
  91. if se_status == SE_STATUS_DISABLED:
  92. return 0
  93. else:
  94. try:
  95. se_mode = re.search('(enforcing|permissive)', out).group(0)
  96. except AttributeError:
  97. err = "Error determining SELinux mode. Exiting."
  98. raise FatalException(1, err)
  99. print "SELinux mode is '" + se_mode + "'"
  100. if se_mode == SE_MODE_ENFORCING:
  101. print "Temporarily disabling SELinux"
  102. run_os_command(SE_SETENFORCE_CMD)
  103. print_warning_msg(
  104. "SELinux is set to 'permissive' mode and temporarily disabled.")
  105. ok = get_YN_input("OK to continue [y/n] (y)? ", True)
  106. if not ok:
  107. raise FatalException(1, None)
  108. return 0
  109. except OSError:
  110. print_warning_msg("Could not run {0}: OK".format(GET_SE_LINUX_ST_CMD))
  111. return 0
  112. # No security enhancements in Windows
  113. @OsFamilyFuncImpl(OSConst.WINSRV_FAMILY)
  114. def disable_security_enhancements():
  115. retcode = 0
  116. err = ''
  117. return (retcode, err)
  118. @OsFamilyFuncImpl(OsFamilyImpl.DEFAULT)
  119. def disable_security_enhancements():
  120. print 'Checking SELinux...'
  121. err = ''
  122. retcode = check_selinux()
  123. if not retcode == 0:
  124. err = 'Failed to disable SELinux. Exiting.'
  125. return (retcode, err)
  126. #
  127. # User account creation
  128. #
  129. class AmbariUserChecks(object):
  130. def __init__(self):
  131. self.NR_USER_CHANGE_PROMPT = ""
  132. self.NR_USER_CUSTOMIZE_PROMPT = ""
  133. self.NR_DEFAULT_USER = ""
  134. self.NR_USER_COMMENT = "Ambari user"
  135. def do_checks(self):
  136. try:
  137. user = read_ambari_user()
  138. create_user = False
  139. update_user_setting = False
  140. if user is not None:
  141. create_user = get_YN_input(self.NR_USER_CHANGE_PROMPT.format(user), False)
  142. update_user_setting = create_user # Only if we will create another user
  143. else: # user is not configured yet
  144. update_user_setting = True # Write configuration anyway
  145. create_user = get_YN_input(self.NR_USER_CUSTOMIZE_PROMPT, False)
  146. if not create_user:
  147. user = self.NR_DEFAULT_USER
  148. if create_user:
  149. (retcode, user) = self._create_custom_user()
  150. if retcode != 0:
  151. return retcode
  152. if update_user_setting:
  153. write_property(NR_USER_PROPERTY, user)
  154. adjust_directory_permissions(user)
  155. except OSError as e:
  156. print_error_msg("Failed: %s" % str(e))
  157. return 4
  158. except Exception as e:
  159. print_error_msg("Unexpected error %s" % str(e))
  160. return 1
  161. return 0
  162. def _create_custom_user(self):
  163. pass
  164. @OsFamilyImpl(os_family=OSConst.WINSRV_FAMILY)
  165. class AmbariUserChecksWindows(AmbariUserChecks):
  166. def __init__(self):
  167. super(AmbariUserChecksWindows, self).__init__()
  168. self.NR_USER_CHANGE_PROMPT = "Ambari-server service is configured to run under user '{0}'. Change this setting [y/n] (n)? "
  169. self.NR_USER_CUSTOMIZE_PROMPT = "Customize user account for ambari-server service [y/n] (n)? "
  170. self.NR_DEFAULT_USER = "NT AUTHORITY\SYSTEM"
  171. def _create_custom_user(self):
  172. user = get_validated_string_input(
  173. "Enter user account for ambari-server service ({0}):".format(self.NR_DEFAULT_USER),
  174. self.NR_DEFAULT_USER, None,
  175. "Invalid username.",
  176. False
  177. )
  178. if user == self.NR_DEFAULT_USER:
  179. return 0, user
  180. password = get_validated_string_input("Enter password for user {0}:".format(user), "", None, "Password", True, False)
  181. from ambari_commons.os_windows import UserHelper
  182. uh = UserHelper()
  183. status, message = uh.create_user(user,password)
  184. if status == UserHelper.USER_EXISTS:
  185. print_info_msg("User {0} already exists, make sure that you typed correct password for user, "
  186. "skipping user creation".format(user))
  187. elif status == UserHelper.ACTION_FAILED: # fail
  188. print_warning_msg("Can't create user {0}. Failed with message {1}".format(user, message))
  189. return UserHelper.ACTION_FAILED, None
  190. # setting SeServiceLogonRight to user
  191. status, message = uh.add_user_privilege(user, 'SeServiceLogonRight')
  192. if status == UserHelper.ACTION_FAILED:
  193. print_warning_msg("Can't add SeServiceLogonRight to user {0}. Failed with message {1}".format(user, message))
  194. return UserHelper.ACTION_FAILED, None
  195. print_info_msg("User configuration is done.")
  196. print_warning_msg("When using non SYSTEM user make sure that your user have read\write access to log directories and "
  197. "all server directories. In case of integrated authentication for SQL Server make sure that your "
  198. "user properly configured to use ambari and metric database.")
  199. #storing username and password in os.environ temporary to pass them to service
  200. os.environ[SERVICE_USERNAME_KEY] = user
  201. os.environ[SERVICE_PASSWORD_KEY] = password
  202. return 0, user
  203. @OsFamilyImpl(os_family=OsFamilyImpl.DEFAULT)
  204. class AmbariUserChecksLinux(AmbariUserChecks):
  205. def __init__(self):
  206. super(AmbariUserChecksLinux, self).__init__()
  207. self.NR_USER_CHANGE_PROMPT = "Ambari-server daemon is configured to run under user '{0}'. Change this setting [y/n] (n)? "
  208. self.NR_USER_CUSTOMIZE_PROMPT = "Customize user account for ambari-server daemon [y/n] (n)? "
  209. self.NR_DEFAULT_USER = "root"
  210. self.NR_USERADD_CMD = 'useradd -M --comment "{1}" ' \
  211. '--shell %s -d /var/lib/ambari-server/keys/ {0}' % locate_file('nologin', '/sbin')
  212. def _create_custom_user(self):
  213. user = get_validated_string_input(
  214. "Enter user account for ambari-server daemon (root):",
  215. "root",
  216. "^[a-z_][a-z0-9_-]{1,31}$",
  217. "Invalid username.",
  218. False
  219. )
  220. print_info_msg("Trying to create user {0}".format(user))
  221. command = self.NR_USERADD_CMD.format(user, self.NR_USER_COMMENT)
  222. retcode, out, err = run_os_command(command)
  223. if retcode == 9: # 9 = username already in use
  224. print_info_msg("User {0} already exists, "
  225. "skipping user creation".format(user))
  226. elif retcode != 0: # fail
  227. print_warning_msg("Can't create user {0}. Command {1} "
  228. "finished with {2}: \n{3}".format(user, command, retcode, err))
  229. return retcode, None
  230. print_info_msg("User configuration is done.")
  231. return 0, user
  232. def check_ambari_user():
  233. return AmbariUserChecks().do_checks()
  234. #
  235. # Firewall
  236. #
  237. def check_firewall():
  238. firewall_obj = Firewall().getFirewallObject()
  239. firewall_on = firewall_obj.check_firewall()
  240. if firewall_obj.stderrdata and len(firewall_obj.stderrdata) > 0:
  241. print firewall_obj.stderrdata
  242. if firewall_on:
  243. print_warning_msg("%s is running. Confirm the necessary Ambari ports are accessible. " %
  244. firewall_obj.FIREWALL_SERVICE_NAME +
  245. "Refer to the Ambari documentation for more details on ports.")
  246. ok = get_YN_input("OK to continue [y/n] (y)? ", True)
  247. if not ok:
  248. raise FatalException(1, None)
  249. #
  250. # ## JDK ###
  251. #
  252. class JDKSetup(object):
  253. def __init__(self):
  254. self.JDK_DEFAULT_CONFIGS = []
  255. self.JDK_PROMPT = "[{0}] {1}\n"
  256. self.JDK_CUSTOM_CHOICE_PROMPT = "[{0}] Custom JDK\n==============================================================================\nEnter choice ({1}): "
  257. self.JDK_VALID_CHOICES = "^[{0}{1:d}]$"
  258. self.JDK_MIN_FILESIZE = 5000
  259. self.JAVA_BIN = ""
  260. self.jdk_index = 0
  261. #
  262. # Downloads and installs the JDK and the JCE policy archive
  263. #
  264. def download_and_install_jdk(self, args, properties):
  265. conf_file = properties.fileName
  266. jcePolicyWarn = "JCE Policy files are required for configuring Kerberos security. If you plan to use Kerberos," \
  267. "please make sure JCE Unlimited Strength Jurisdiction Policy Files are valid on all hosts."
  268. if args.java_home:
  269. #java_home was specified among the command-line arguments. Use it as custom JDK location.
  270. if not validate_jdk(args.java_home):
  271. err = "Path to java home " + args.java_home + " or java binary file does not exists"
  272. raise FatalException(1, err)
  273. print_warning_msg("JAVA_HOME " + args.java_home + " must be valid on ALL hosts")
  274. print_warning_msg(jcePolicyWarn)
  275. IS_CUSTOM_JDK = True
  276. properties.process_pair(JAVA_HOME_PROPERTY, args.java_home)
  277. properties.removeOldProp(JDK_NAME_PROPERTY)
  278. properties.removeOldProp(JCE_NAME_PROPERTY)
  279. self._ensure_java_home_env_var_is_set(args.java_home)
  280. self.jdk_index = self.custom_jdk_number
  281. return
  282. java_home_var = get_JAVA_HOME()
  283. if OS_FAMILY == OSConst.WINSRV_FAMILY:
  284. progress_func = None
  285. else:
  286. progress_func = download_progress
  287. if get_silent():
  288. if not java_home_var:
  289. #No java_home_var set, detect if java is already installed
  290. if os.environ.has_key(JAVA_HOME):
  291. args.java_home = os.environ[JAVA_HOME]
  292. properties.process_pair(JAVA_HOME_PROPERTY, args.java_home)
  293. properties.removeOldProp(JDK_NAME_PROPERTY)
  294. properties.removeOldProp(JCE_NAME_PROPERTY)
  295. self._ensure_java_home_env_var_is_set(args.java_home)
  296. self.jdk_index = self.custom_jdk_number
  297. return
  298. else:
  299. # For now, changing the existing JDK to make sure we use a supported one
  300. pass
  301. if java_home_var:
  302. change_jdk = get_YN_input("Do you want to change Oracle JDK [y/n] (n)? ", False)
  303. if not change_jdk:
  304. self._ensure_java_home_env_var_is_set(java_home_var)
  305. self.jdk_index = self.custom_jdk_number
  306. return
  307. #Continue with the normal setup, taking the first listed JDK version as the default option
  308. jdk_num = str(self.jdk_index + 1)
  309. (self.jdks, jdk_choice_prompt, jdk_valid_choices, self.custom_jdk_number) = self._populate_jdk_configs(properties, jdk_num)
  310. jdk_num = get_validated_string_input(
  311. jdk_choice_prompt,
  312. jdk_num,
  313. jdk_valid_choices,
  314. "Invalid number.",
  315. False
  316. )
  317. self.jdk_index = int(jdk_num) - 1
  318. if self.jdk_index == self.custom_jdk_number:
  319. print_warning_msg("JDK must be installed on all hosts and JAVA_HOME must be valid on all hosts.")
  320. print_warning_msg(jcePolicyWarn)
  321. args.java_home = get_validated_string_input("Path to JAVA_HOME: ", None, None, None, False, False)
  322. if not os.path.exists(args.java_home) or not os.path.isfile(os.path.join(args.java_home, "bin", self.JAVA_BIN)):
  323. err = "Java home path or java binary file is unavailable. Please put correct path to java home."
  324. raise FatalException(1, err)
  325. print "Validating JDK on Ambari Server...done."
  326. properties.process_pair(JAVA_HOME_PROPERTY, args.java_home)
  327. properties.removeOldProp(JDK_NAME_PROPERTY)
  328. properties.removeOldProp(JCE_NAME_PROPERTY)
  329. self._ensure_java_home_env_var_is_set(args.java_home)
  330. return
  331. jdk_cfg = self.jdks[self.jdk_index]
  332. resources_dir = get_resources_location(properties)
  333. dest_file = os.path.abspath(os.path.join(resources_dir, jdk_cfg.dest_file))
  334. if os.path.exists(dest_file):
  335. print "JDK already exists, using " + dest_file
  336. else:
  337. ok = get_YN_input("To download the Oracle JDK and the Java Cryptography Extension (JCE) "
  338. "Policy Files you must accept the "
  339. "license terms found at "
  340. "http://www.oracle.com/technetwork/java/javase/"
  341. "terms/license/index.html and not accepting will "
  342. "cancel the Ambari Server setup and you must install the JDK and JCE "
  343. "files manually.\nDo you accept the "
  344. "Oracle Binary Code License Agreement [y/n] (y)? ", True)
  345. if not ok:
  346. print 'Exiting...'
  347. sys.exit(1)
  348. jdk_url = jdk_cfg.url
  349. print 'Downloading JDK from ' + jdk_url + ' to ' + dest_file
  350. self._download_jdk(jdk_url, dest_file, progress_func)
  351. try:
  352. (retcode, out, java_home_dir) = self._install_jdk(dest_file, jdk_cfg)
  353. except Exception, e:
  354. print "Installation of JDK has failed: %s\n" % str(e)
  355. file_exists = os.path.isfile(dest_file)
  356. if file_exists:
  357. ok = get_YN_input("JDK found at " + dest_file + ". "
  358. "Would you like to re-download the JDK [y/n] (y)? ", not get_silent())
  359. if not ok:
  360. err = "Unable to install JDK. Please remove JDK file found at " + \
  361. dest_file + " and re-run Ambari Server setup"
  362. raise FatalException(1, err)
  363. else:
  364. jdk_url = jdk_cfg.url
  365. print 'Re-downloading JDK from ' + jdk_url + ' to ' + dest_file
  366. self._download_jdk(jdk_url, dest_file, progress_func)
  367. print 'Successfully re-downloaded JDK distribution to ' + dest_file
  368. try:
  369. (retcode, out) = self._install_jdk(dest_file, jdk_cfg)
  370. except Exception, e:
  371. print "Installation of JDK was failed: %s\n" % str(e)
  372. err = "Unable to install JDK. Please remove JDK, file found at " + \
  373. dest_file + " and re-run Ambari Server setup"
  374. raise FatalException(1, err)
  375. else:
  376. err = "Unable to install JDK. File " + dest_file + " does not exist, " \
  377. "please re-run Ambari Server setup"
  378. raise FatalException(1, err)
  379. properties.process_pair(JDK_NAME_PROPERTY, jdk_cfg.dest_file)
  380. properties.process_pair(JAVA_HOME_PROPERTY, java_home_dir)
  381. self._ensure_java_home_env_var_is_set(java_home_dir)
  382. def download_and_unpack_jce_policy(self, properties):
  383. err_msg_stdout = "JCE Policy files are required for secure HDP setup. Please ensure " \
  384. " all hosts have the JCE unlimited strength policy 6, files."
  385. resources_dir = get_resources_location(properties)
  386. jdk_cfg = self.jdks[self.jdk_index]
  387. try:
  388. JDKSetup._download_jce_policy(jdk_cfg.jcpol_url, jdk_cfg.dest_jcpol_file, resources_dir, properties)
  389. except FatalException, e:
  390. print err_msg_stdout
  391. print_error_msg("Failed to download JCE policy files:")
  392. if e.reason is not None:
  393. print_error_msg("\nREASON: {0}".format(e.reason))
  394. # TODO: We don't fail installation if _download_jce_policy fails. Is it OK?
  395. print 'Installing JCE policy...'
  396. try:
  397. jdk_path = properties.get_property(JAVA_HOME_PROPERTY)
  398. JDKSetup.unpack_jce_policy(jdk_path, resources_dir, jdk_cfg.dest_jcpol_file)
  399. except FatalException, e:
  400. print err_msg_stdout
  401. print_error_msg("Failed to install JCE policy files:")
  402. if e.reason is not None:
  403. print_error_msg("\nREASON: {0}".format(e.reason))
  404. # TODO: We don't fail installation if _download_jce_policy fails. Is it OK?
  405. @staticmethod
  406. def unpack_jce_policy(jdk_path, resources_dir, jce_packed_file):
  407. jdk_security_path = os.path.abspath(os.path.join(jdk_path, configDefaults.JDK_SECURITY_DIR))
  408. jce_zip_path = os.path.abspath(os.path.join(resources_dir, jce_packed_file))
  409. expand_jce_zip_file(jce_zip_path, jdk_security_path)
  410. def _populate_jdk_configs(self, properties, jdk_num):
  411. if properties.has_key(JDK_RELEASES):
  412. jdk_names = properties[JDK_RELEASES].split(',')
  413. jdks = []
  414. for jdk_name in jdk_names:
  415. jdkR = JDKRelease.from_properties(properties, jdk_name)
  416. jdks.append(jdkR)
  417. else:
  418. jdks = self.JDK_DEFAULT_CONFIGS
  419. n_config = 1
  420. jdk_choice_prompt = ''
  421. jdk_choices = ''
  422. for jdk in jdks:
  423. jdk_choice_prompt += self.JDK_PROMPT.format(n_config, jdk.desc)
  424. jdk_choices += str(n_config)
  425. n_config += 1
  426. jdk_choice_prompt += self.JDK_CUSTOM_CHOICE_PROMPT.format(n_config, jdk_num)
  427. jdk_valid_choices = self.JDK_VALID_CHOICES.format(jdk_choices, n_config)
  428. return jdks, jdk_choice_prompt, jdk_valid_choices, n_config - 1
  429. def _download_jdk(self, jdk_url, dest_file, progress_func = None):
  430. jdk_download_fail_msg = " Failed to download JDK: {0}. Please check that the " \
  431. "JDK is available at {1}. Also you may specify JDK file " \
  432. "location in local filesystem using --jdk-location command " \
  433. "line argument.".format("{0}", jdk_url)
  434. try:
  435. force_download_file(jdk_url, dest_file, progress_func = progress_func)
  436. print 'Successfully downloaded JDK distribution to ' + dest_file
  437. except FatalException:
  438. raise
  439. except Exception, e:
  440. err = jdk_download_fail_msg.format(str(e))
  441. raise FatalException(1, err)
  442. @staticmethod
  443. def _download_jce_policy(jcpol_url, dest_jcpol_file, resources_dir, properties):
  444. dest_file = os.path.abspath(os.path.join(resources_dir, dest_jcpol_file))
  445. if not os.path.exists(dest_file):
  446. print 'Downloading JCE Policy archive from ' + jcpol_url + ' to ' + dest_file
  447. try:
  448. force_download_file(jcpol_url, dest_file)
  449. print 'Successfully downloaded JCE Policy archive to ' + dest_file
  450. except FatalException:
  451. raise
  452. except Exception, e:
  453. err = 'Failed to download JCE Policy archive: ' + str(e)
  454. raise FatalException(1, err)
  455. else:
  456. print "JCE Policy archive already exists, using " + dest_file
  457. properties.process_pair(JCE_NAME_PROPERTY, dest_jcpol_file)
  458. # Base implementation, overriden in the subclasses
  459. def _install_jdk(self, java_inst_file, java_home_dir):
  460. pass
  461. # Base implementation, overriden in the subclasses
  462. def _ensure_java_home_env_var_is_set(self, java_home_dir):
  463. pass
  464. @OsFamilyImpl(os_family=OSConst.WINSRV_FAMILY)
  465. class JDKSetupWindows(JDKSetup):
  466. def __init__(self):
  467. super(JDKSetupWindows, self).__init__()
  468. self.JDK_DEFAULT_CONFIGS = [
  469. JDKRelease("jdk7.67", "Oracle JDK 1.7.67",
  470. "http://public-repo-1.hortonworks.com/ARTIFACTS/jdk-7u67-windows-x64.exe", "jdk-7u67-windows-x64.exe",
  471. "http://public-repo-1.hortonworks.com/ARTIFACTS/UnlimitedJCEPolicyJDK7.zip", "UnlimitedJCEPolicyJDK7.zip",
  472. "C:\\jdk1.7.0_67",
  473. "Creating (jdk.*)/jre")
  474. ]
  475. self.jdks = self.JDK_DEFAULT_CONFIGS
  476. self.custom_jdk_number = len(self.jdks)
  477. self.JAVA_BIN = "java.exe"
  478. def _install_jdk(self, java_inst_file, jdk_cfg):
  479. jdk_inst_dir = jdk_cfg.inst_dir
  480. print "Installing JDK to {0}".format(jdk_inst_dir)
  481. if not os.path.exists(jdk_inst_dir):
  482. os.makedirs(jdk_inst_dir)
  483. if java_inst_file.endswith(".exe"):
  484. (dirname, filename) = os.path.split(java_inst_file)
  485. installLogFilePath = os.path.join(configDefaults.OUT_DIR, filename + "-install.log")
  486. #jre7u67.exe /s INSTALLDIR=<dir> STATIC=1 WEB_JAVA=0 /L \\var\\log\\ambari-server\\jre7u67.exe-install.log
  487. installCmd = [
  488. java_inst_file,
  489. "/s",
  490. "INSTALLDIR=" + jdk_inst_dir,
  491. "STATIC=1",
  492. "WEB_JAVA=0",
  493. "/L",
  494. installLogFilePath
  495. ]
  496. retcode, out, err = run_os_command(installCmd)
  497. #TODO: support .msi file installations
  498. #msiexec.exe jre.msi /s INSTALLDIR=<dir> STATIC=1 WEB_JAVA=0 /L \\var\\log\\ambari-server\\jre7u67-install.log ?
  499. else:
  500. err = "JDK installation failed.Unknown file mask."
  501. raise FatalException(1, err)
  502. if retcode == 1603:
  503. # JDK already installed
  504. print "JDK already installed in {0}".format(jdk_inst_dir)
  505. retcode = 0
  506. else:
  507. if retcode != 0:
  508. err = "Installation of JDK returned exit code %s" % retcode
  509. raise FatalException(retcode, err)
  510. print "Successfully installed JDK to {0}".format(jdk_inst_dir)
  511. # Don't forget to adjust the JAVA_HOME env var
  512. return (retcode, out, jdk_inst_dir)
  513. def _ensure_java_home_env_var_is_set(self, java_home_dir):
  514. if not os.environ.has_key(JAVA_HOME) or os.environ[JAVA_HOME] != java_home_dir:
  515. java_home_dir_unesc = compress_backslashes(java_home_dir)
  516. retcode, out, err = run_os_command("SETX {0} {1} /M".format(JAVA_HOME, java_home_dir_unesc))
  517. if retcode != 0:
  518. print_warning_msg("SETX output: " + out)
  519. print_warning_msg("SETX error output: " + err)
  520. err = "Setting JAVA_HOME failed. Exit code={0}".format(retcode)
  521. raise FatalException(1, err)
  522. os.environ[JAVA_HOME] = java_home_dir
  523. @OsFamilyImpl(os_family=OsFamilyImpl.DEFAULT)
  524. class JDKSetupLinux(JDKSetup):
  525. def __init__(self):
  526. super(JDKSetupLinux, self).__init__()
  527. self.JDK_DEFAULT_CONFIGS = [
  528. JDKRelease("jdk1.8", "Oracle JDK 1.8",
  529. "http://public-repo-1.hortonworks.com/ARTIFACTS/jdk-8u40-linux-x64.tar.gz", "jdk-8u40-linux-x64.tar.gz",
  530. "http://public-repo-1.hortonworks.com/ARTIFACTS/jce_policy-8.zip", "jce_policy-8.zip",
  531. "/usr/jdk64/jdk1.8.0_40",
  532. "(jdk.*)/jre")
  533. ]
  534. self.jdks = self.JDK_DEFAULT_CONFIGS
  535. self.custom_jdk_number = len(self.jdks)
  536. self.JAVA_BIN = "java"
  537. self.CREATE_JDK_DIR_CMD = "/bin/mkdir -p {0}"
  538. self.CHMOD_JDK_DIR_CMD = "chmod a+x {0}"
  539. # use --no-same-owner when running as root to prevent uucp as the user (AMBARI-6478)
  540. self.UNTAR_JDK_ARCHIVE = "tar --no-same-owner -xvf {0}"
  541. def _install_jdk(self, java_inst_file, jdk_cfg):
  542. jdk_inst_dir = jdk_cfg.inst_dir
  543. print "Installing JDK to {0}".format(jdk_inst_dir)
  544. retcode, out, err = run_os_command(self.CREATE_JDK_DIR_CMD.format(jdk_inst_dir))
  545. retcode, out, err = run_os_command(self.CHMOD_JDK_DIR_CMD.format(jdk_inst_dir))
  546. savedPath = os.getcwd()
  547. os.chdir(jdk_inst_dir)
  548. try:
  549. if java_inst_file.endswith(".gz"):
  550. retcode, out, err = run_os_command(self.UNTAR_JDK_ARCHIVE.format(java_inst_file))
  551. else:
  552. err = "JDK installation failed.Unknown file extension."
  553. raise FatalException(1, err)
  554. finally:
  555. os.chdir(savedPath)
  556. if retcode != 0:
  557. err = "Installation of JDK returned exit code %s" % retcode
  558. raise FatalException(retcode, err)
  559. jdk_version = re.search(jdk_cfg.reg_exp, out).group(1)
  560. java_home_dir = os.path.join(jdk_inst_dir, jdk_version)
  561. print "Successfully installed JDK to {0}".format(jdk_inst_dir)
  562. return (retcode, out, java_home_dir)
  563. def _ensure_java_home_env_var_is_set(self, java_home_dir):
  564. #No way to do this in Linux. Best we can is to set the process environment variable.
  565. os.environ[JAVA_HOME] = java_home_dir
  566. def download_and_install_jdk(options):
  567. properties = get_ambari_properties()
  568. if properties == -1:
  569. err = "Error getting ambari properties"
  570. raise FatalException(-1, err)
  571. jdkSetup = JDKSetup()
  572. jdkSetup.download_and_install_jdk(options, properties)
  573. if jdkSetup.jdk_index != jdkSetup.custom_jdk_number:
  574. jdkSetup.download_and_unpack_jce_policy(properties)
  575. update_properties(properties)
  576. return 0
  577. #
  578. # Configures the OS settings in ambari properties.
  579. #
  580. def configure_os_settings():
  581. properties = get_ambari_properties()
  582. if properties == -1:
  583. print_error_msg("Error getting ambari properties")
  584. return -1
  585. try:
  586. conf_os_type = properties[OS_TYPE_PROPERTY]
  587. if conf_os_type != '':
  588. print_info_msg("os_type already set in the properties file")
  589. return 0
  590. except (KeyError):
  591. print_error_msg("os_type is not set in the properties file. Setting it now.")
  592. # to check server/agent compatibility
  593. master_os_family = OS_FAMILY + OS_VERSION
  594. # to check supported os_types
  595. master_os_type = OS_TYPE + OS_VERSION
  596. write_property(OS_FAMILY_PROPERTY, master_os_family)
  597. write_property(OS_TYPE_PROPERTY, master_os_type)
  598. return 0
  599. #
  600. # JDBC
  601. #
  602. def _check_jdbc_options(options):
  603. return (options.jdbc_driver is not None and options.jdbc_db is not None)
  604. def proceedJDBCProperties(args):
  605. if not os.path.isfile(args.jdbc_driver):
  606. err = "File {0} does not exist!".format(args.jdbc_driver)
  607. raise FatalException(1, err)
  608. if args.jdbc_db not in JDBC_DB_OPTION_VALUES:
  609. err = "Unsupported database name {0}. Please see help for more information.".format(args.jdbc_db)
  610. raise FatalException(1, err)
  611. _cache_jdbc_driver(args)
  612. # No JDBC driver caching in Windows at this point. Will cache it along with the integrated authentication dll into a
  613. # zip archive at a later moment.
  614. @OsFamilyFuncImpl(os_family=OSConst.WINSRV_FAMILY)
  615. def _cache_jdbc_driver(args):
  616. pass
  617. #TODO JDBC driver caching almost duplicates the LinuxDBMSConfig._install_jdbc_driver() functionality
  618. @OsFamilyFuncImpl(os_family=OsFamilyImpl.DEFAULT)
  619. def _cache_jdbc_driver(args):
  620. properties = get_ambari_properties()
  621. if properties == -1:
  622. err = "Error getting ambari properties"
  623. raise FatalException(-1, err)
  624. resources_dir = get_resources_location(properties)
  625. symlink_name = args.jdbc_db + "-jdbc-driver.jar"
  626. jdbc_symlink = os.path.join(resources_dir, symlink_name)
  627. path, jdbc_name = os.path.split(args.jdbc_driver)
  628. if os.path.lexists(jdbc_symlink):
  629. os.remove(jdbc_symlink)
  630. if os.path.isfile(os.path.join(resources_dir, jdbc_name)):
  631. os.remove(os.path.join(resources_dir, jdbc_name))
  632. try:
  633. shutil.copy(args.jdbc_driver, resources_dir)
  634. print "Copying {0} to {1}".format(args.jdbc_driver, resources_dir)
  635. except Exception, e:
  636. err = "Can not copy file {0} to {1} due to: {2} . Please check file " \
  637. "permissions and free disk space.".format(args.jdbc_driver, resources_dir, str(e))
  638. raise FatalException(1, err)
  639. os.symlink(os.path.join(resources_dir, jdbc_name), jdbc_symlink)
  640. print "JDBC driver was successfully initialized."
  641. #
  642. # Database
  643. #
  644. # Ask user for database connection properties
  645. def prompt_db_properties(options):
  646. ok = False
  647. if options.must_set_database_options:
  648. ok = get_YN_input("Enter advanced database configuration [y/n] (n)? ", False)
  649. print 'Configuring database...'
  650. factory = DBMSConfigFactory()
  651. options.must_set_database_options = ok
  652. options.database_index = factory.select_dbms(options)
  653. def _setup_database(options):
  654. properties = get_ambari_properties()
  655. if properties == -1:
  656. raise FatalException(-1, "Error getting ambari properties")
  657. factory = DBMSConfigFactory()
  658. dbmsAmbari = factory.create(options, properties, "Ambari")
  659. resultA = dbmsAmbari.configure_database(properties)
  660. # Now save the properties file
  661. if resultA:
  662. update_properties(properties)
  663. dbmsAmbari.setup_database()
  664. def _createDefDbFactory(options):
  665. properties = get_ambari_properties()
  666. if properties == -1:
  667. raise FatalException(-1, "Error getting ambari properties")
  668. if not (properties.getPropertyDict().has_key(JDBC_URL_PROPERTY) and
  669. properties.getPropertyDict().has_key(JDBC_RCA_URL_PROPERTY)):
  670. raise FatalException(-1, "Ambari Server not set up yet. Nothing to reset.")
  671. empty_options = optparse.Values()
  672. empty_options.must_set_database_options = options.must_set_database_options
  673. empty_options.database_index = options.database_index
  674. empty_options.database_host = ""
  675. empty_options.database_port = ""
  676. empty_options.database_name = ""
  677. empty_options.database_windows_auth = False
  678. empty_options.database_username = ""
  679. empty_options.database_password = ""
  680. empty_options.init_db_script_file = ""
  681. empty_options.cleanup_db_script_file = ""
  682. factory = DBMSConfigFactory()
  683. return empty_options, factory, properties
  684. def _reset_database(options):
  685. properties = get_ambari_properties()
  686. if properties == -1:
  687. print_error_msg("Error getting ambari properties")
  688. return -1
  689. factory = DBMSConfigFactory()
  690. dbmsAmbari = factory.create(options, properties)
  691. dbmsAmbari.reset_database()
  692. #
  693. # Extract the system views
  694. #
  695. def extract_views():
  696. java_exe_path = get_java_exe_path()
  697. if java_exe_path is None:
  698. print_error_msg("No JDK found, please run the \"setup\" "
  699. "command to install a JDK automatically or install any "
  700. "JDK manually to " + configDefaults.JDK_INSTALL_DIR)
  701. return 1
  702. properties = get_ambari_properties()
  703. if properties == -1:
  704. print_error_msg("Error getting ambari properties")
  705. return -1
  706. vdir = get_value_from_properties(properties, VIEWS_DIR_PROPERTY, configDefaults.DEFAULT_VIEWS_DIR)
  707. files = [f for f in os.listdir(vdir) if os.path.isfile(os.path.join(vdir,f))]
  708. for f in files:
  709. command = VIEW_EXTRACT_CMD.format(java_exe_path,
  710. get_full_ambari_classpath(), os.path.join(vdir,f))
  711. retcode, stdout, stderr = run_os_command(command)
  712. if retcode == 0:
  713. sys.stdout.write(f + "\n")
  714. elif retcode == 2:
  715. sys.stdout.write("Error extracting " + f + "\n")
  716. else:
  717. sys.stdout.write(".")
  718. sys.stdout.flush()
  719. print_info_msg("Return code from extraction of view archive " + f + ": " +
  720. str(retcode))
  721. sys.stdout.write("\n")
  722. return 0
  723. def expand_jce_zip_file(jce_zip_path, jdk_security_path):
  724. f = None
  725. import zipfile
  726. if os.path.exists(jdk_security_path) and os.path.exists(jce_zip_path):
  727. try:
  728. f = zipfile.ZipFile(jce_zip_path, "r")
  729. zip_members = f.namelist()
  730. for member in zip_members:
  731. if member.endswith(os.sep):
  732. os.makedirs(os.path.join(jdk_security_path, member))
  733. else:
  734. f.extract(member, jdk_security_path)
  735. unziped_jce_path = os.path.split(zip_members[len(zip_members) - 1])[0]
  736. finally:
  737. try:
  738. f.close()
  739. except Exception as e:
  740. err = "Fail during the extraction of {0}.".format(jce_zip_path)
  741. raise FatalException(1, err)
  742. else:
  743. err = "The path {0} or {1} is invalid.".format(jdk_security_path, jce_zip_path)
  744. raise FatalException(1, err)
  745. if unziped_jce_path:
  746. from_path = os.path.join(jdk_security_path, unziped_jce_path)
  747. jce_files = os.listdir(from_path)
  748. for i in range(len(jce_files)):
  749. jce_files[i] = os.path.join(from_path, jce_files[i])
  750. copy_files(jce_files, jdk_security_path)
  751. dir_to_delete = os.path.join(jdk_security_path, unziped_jce_path.split(os.sep)[0])
  752. shutil.rmtree(dir_to_delete)
  753. #
  754. # Setup the Ambari Server.
  755. #
  756. def setup(options):
  757. retcode = verify_setup_allowed()
  758. if not retcode == 0:
  759. raise FatalException(1, None)
  760. if not is_root():
  761. err = configDefaults.MESSAGE_ERROR_SETUP_NOT_ROOT
  762. raise FatalException(4, err)
  763. # proceed jdbc properties if they were set
  764. if _check_jdbc_options(options):
  765. proceedJDBCProperties(options)
  766. return
  767. (retcode, err) = disable_security_enhancements()
  768. if not retcode == 0:
  769. raise FatalException(retcode, err)
  770. #Create ambari user, if needed
  771. retcode = check_ambari_user()
  772. if not retcode == 0:
  773. err = 'Failed to create user. Exiting.'
  774. raise FatalException(retcode, err)
  775. print configDefaults.MESSAGE_CHECK_FIREWALL
  776. check_firewall()
  777. # proceed jdbc properties if they were set
  778. if _check_jdbc_options(options):
  779. proceedJDBCProperties(options)
  780. print 'Checking JDK...'
  781. try:
  782. download_and_install_jdk(options)
  783. except FatalException as e:
  784. err = 'Downloading or installing JDK failed: {0}. Exiting.'.format(e)
  785. raise FatalException(e.code, err)
  786. print 'Completing setup...'
  787. retcode = configure_os_settings()
  788. if not retcode == 0:
  789. err = 'Configure of OS settings in ambari.properties failed. Exiting.'
  790. raise FatalException(retcode, err)
  791. print 'Configuring database...'
  792. prompt_db_properties(options)
  793. #DB setup should be done last after doing any setup.
  794. _setup_database(options)
  795. check_jdbc_drivers(options)
  796. print 'Extracting system views...'
  797. retcode = extract_views()
  798. if not retcode == 0:
  799. err = 'Error while extracting system views. Exiting'
  800. raise FatalException(retcode, err)
  801. # we've already done this, but new files were created so run it one time.
  802. adjust_directory_permissions(read_ambari_user())
  803. #
  804. # Setup the JCE policy for Ambari Server.
  805. #
  806. def setup_jce_policy(args):
  807. if not os.path.exists(args[1]):
  808. err = "Can not run 'setup-jce'. Invalid path {0}.".format(args[1])
  809. raise FatalException(1, err)
  810. properties = get_ambari_properties()
  811. resources_dir = get_resources_location(properties)
  812. zip_path = os.path.split(args[1])
  813. zip_dir = zip_path[0]
  814. if not zip_dir == resources_dir:
  815. try:
  816. shutil.copy(args[1], resources_dir)
  817. except Exception as e:
  818. err = "Fail while trying to copy {0} to {1}. {2}".format(args[1], resources_dir, e)
  819. raise FatalException(1, err)
  820. jdk_path = properties.get_property(JAVA_HOME_PROPERTY)
  821. if not jdk_path or not os.path.exists(jdk_path):
  822. err = "JDK not installed, you need to run 'ambari-server setup' before attempting to install the JCE policy."
  823. raise FatalException(1, err)
  824. zip_name = zip_path[1]
  825. properties.process_pair(JCE_NAME_PROPERTY, zip_name)
  826. print 'Installing JCE policy...'
  827. try:
  828. JDKSetup.unpack_jce_policy(jdk_path, resources_dir, zip_name)
  829. except FatalException as e:
  830. err = 'Installing JCE failed: {0}. Exiting.'.format(e)
  831. raise FatalException(e.code, err)
  832. update_properties(properties)
  833. print 'NOTE: Restart Ambari Server to apply changes' + \
  834. ' ("ambari-server restart|stop|start")'
  835. #
  836. # Resets the Ambari Server.
  837. #
  838. def reset(options):
  839. if not is_root():
  840. err = configDefaults.MESSAGE_ERROR_RESET_NOT_ROOT
  841. raise FatalException(4, err)
  842. status, stateDesc = is_server_runing()
  843. if status:
  844. err = 'Ambari-server must be stopped to reset'
  845. raise FatalException(1, err)
  846. #force reset if silent option provided
  847. if get_silent():
  848. default = "yes"
  849. else:
  850. default = "no"
  851. choice = get_YN_input("**** WARNING **** You are about to reset and clear the "
  852. "Ambari Server database. This will remove all cluster "
  853. "host and configuration information from the database. "
  854. "You will be required to re-configure the Ambari server "
  855. "and re-run the cluster wizard. \n"
  856. "Are you SURE you want to perform the reset "
  857. "[yes/no] ({0})? ".format(default), get_silent())
  858. okToRun = choice
  859. if not okToRun:
  860. err = "Ambari Server 'reset' cancelled"
  861. raise FatalException(1, err)
  862. _reset_database(options)
  863. pass