serverUpgrade.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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 ambari_simplejson as json # simplejson is much faster comparing to Python 2.6 json module and has the same functions set.
  18. import os
  19. import sys
  20. import shutil
  21. import base64
  22. import urllib2
  23. from ambari_commons.exceptions import FatalException
  24. from ambari_commons.logging_utils import print_info_msg, print_warning_msg, print_error_msg, get_verbose
  25. from ambari_commons.os_utils import is_root, run_os_command
  26. from ambari_server.dbConfiguration import DBMSConfigFactory, check_jdbc_drivers
  27. from ambari_server.properties import Properties
  28. from ambari_server.serverConfiguration import configDefaults, \
  29. check_database_name_property, get_ambari_properties, get_ambari_version, get_full_ambari_classpath, \
  30. get_java_exe_path, get_stack_location, parse_properties_file, read_ambari_user, update_ambari_properties, \
  31. update_database_name_property, get_admin_views_dir, \
  32. AMBARI_PROPERTIES_FILE, IS_LDAP_CONFIGURED, LDAP_PRIMARY_URL_PROPERTY, RESOURCES_DIR_PROPERTY, \
  33. SETUP_OR_UPGRADE_MSG
  34. from ambari_server.setupSecurity import adjust_directory_permissions
  35. from ambari_server.utils import compare_versions
  36. from ambari_server.serverUtils import is_server_runing, get_ambari_server_api_base
  37. from ambari_server.userInput import get_validated_string_input, get_prompt_default, read_password, get_YN_input
  38. # constants
  39. STACK_NAME_VER_SEP = "-"
  40. SCHEMA_UPGRADE_HELPER_CMD = "{0} -cp {1} " + \
  41. "org.apache.ambari.server.upgrade.SchemaUpgradeHelper" + \
  42. " > " + configDefaults.SERVER_OUT_FILE + " 2>&1"
  43. STACK_UPGRADE_HELPER_CMD = "{0} -cp {1} " + \
  44. "org.apache.ambari.server.upgrade.StackUpgradeHelper" + \
  45. " {2} {3} > " + configDefaults.SERVER_OUT_FILE + " 2>&1"
  46. #
  47. # Stack upgrade
  48. #
  49. def upgrade_stack(args):
  50. if not is_root():
  51. err = 'Ambari-server upgradestack should be run with ' \
  52. 'root-level privileges'
  53. raise FatalException(4, err)
  54. check_database_name_property()
  55. try:
  56. stack_id = args[1]
  57. except IndexError:
  58. #stack_id is mandatory
  59. raise FatalException("Invalid number of stack upgrade arguments")
  60. try:
  61. repo_url = args[2]
  62. except IndexError:
  63. repo_url = None
  64. try:
  65. repo_url_os = args[3]
  66. except IndexError:
  67. repo_url_os = None
  68. stack_name, stack_version = stack_id.split(STACK_NAME_VER_SEP)
  69. retcode = run_stack_upgrade(stack_name, stack_version, repo_url, repo_url_os)
  70. if not retcode == 0:
  71. raise FatalException(retcode, 'Stack upgrade failed.')
  72. return retcode
  73. def load_stack_values(version, filename):
  74. import xml.etree.ElementTree as ET
  75. values = {}
  76. root = ET.parse(filename).getroot()
  77. for ostag in root:
  78. ostype = ostag.attrib['type']
  79. for repotag in ostag:
  80. reponametag = repotag.find('reponame')
  81. repoidtag = repotag.find('repoid')
  82. baseurltag = repotag.find('baseurl')
  83. if reponametag is not None and repoidtag is not None and baseurltag is not None:
  84. key = "repo:/" + reponametag.text
  85. key += "/" + version
  86. key += "/" + ostype
  87. key += "/" + repoidtag.text
  88. key += ":baseurl"
  89. values[key] = baseurltag.text
  90. return values
  91. def run_stack_upgrade(stackName, stackVersion, repo_url, repo_url_os):
  92. jdk_path = get_java_exe_path()
  93. if jdk_path is None:
  94. print_error_msg("No JDK found, please run the \"setup\" "
  95. "command to install a JDK automatically or install any "
  96. "JDK manually to " + configDefaults.JDK_INSTALL_DIR)
  97. return 1
  98. stackId = {}
  99. stackId[stackName] = stackVersion
  100. if repo_url is not None:
  101. stackId['repo_url'] = repo_url
  102. if repo_url_os is not None:
  103. stackId['repo_url_os'] = repo_url_os
  104. command = STACK_UPGRADE_HELPER_CMD.format(jdk_path, get_full_ambari_classpath(),
  105. "updateStackId",
  106. "'" + json.dumps(stackId) + "'")
  107. (retcode, stdout, stderr) = run_os_command(command)
  108. print_info_msg("Return code from stack upgrade command, retcode = " + str(retcode))
  109. if retcode > 0:
  110. print_error_msg("Error executing stack upgrade, please check the server logs.")
  111. return retcode
  112. def run_metainfo_upgrade(keyValueMap=None):
  113. jdk_path = get_java_exe_path()
  114. if jdk_path is None:
  115. print_error_msg("No JDK found, please run the \"setup\" "
  116. "command to install a JDK automatically or install any "
  117. "JDK manually to " + configDefaults.JDK_INSTALL_DIR)
  118. retcode = 1
  119. if keyValueMap:
  120. command = STACK_UPGRADE_HELPER_CMD.format(jdk_path, get_full_ambari_classpath(),
  121. 'updateMetaInfo',
  122. "'" + json.dumps(keyValueMap) + "'")
  123. (retcode, stdout, stderr) = run_os_command(command)
  124. print_info_msg("Return code from stack upgrade command, retcode = " + str(retcode))
  125. if retcode > 0:
  126. print_error_msg("Error executing metainfo upgrade, please check the "
  127. "server logs.")
  128. return retcode
  129. #
  130. # Repo upgrade
  131. #
  132. def change_objects_owner(args):
  133. print 'Fixing database objects owner'
  134. properties = Properties() #Dummy, args contains the dbms name and parameters already
  135. factory = DBMSConfigFactory()
  136. dbms = factory.create(args, properties)
  137. dbms.change_db_files_owner()
  138. def upgrade_local_repo(args):
  139. properties = get_ambari_properties()
  140. if properties == -1:
  141. print_error_msg("Error getting ambari properties")
  142. return -1
  143. stack_location = get_stack_location(properties)
  144. stack_root_local = os.path.join(stack_location, "HDPLocal")
  145. if not os.path.exists(stack_root_local):
  146. print_info_msg("HDPLocal stack directory does not exist, skipping")
  147. return
  148. stack_root = os.path.join(stack_location, "HDP")
  149. if not os.path.exists(stack_root):
  150. print_info_msg("HDP stack directory does not exist, skipping")
  151. return
  152. for stack_version_local in os.listdir(stack_root_local):
  153. repo_file_local = os.path.join(stack_root_local, stack_version_local, "repos", "repoinfo.xml.rpmsave")
  154. if not os.path.exists(repo_file_local):
  155. repo_file_local = os.path.join(stack_root_local, stack_version_local, "repos", "repoinfo.xml")
  156. repo_file = os.path.join(stack_root, stack_version_local, "repos", "repoinfo.xml")
  157. print_info_msg("Local repo file: " + repo_file_local)
  158. print_info_msg("Repo file: " + repo_file_local)
  159. metainfo_update_items = {}
  160. if os.path.exists(repo_file_local) and os.path.exists(repo_file):
  161. local_values = load_stack_values(stack_version_local, repo_file_local)
  162. repo_values = load_stack_values(stack_version_local, repo_file)
  163. for k, v in local_values.iteritems():
  164. if repo_values.has_key(k):
  165. local_url = local_values[k]
  166. repo_url = repo_values[k]
  167. if repo_url != local_url:
  168. metainfo_update_items[k] = local_url
  169. run_metainfo_upgrade(metainfo_update_items)
  170. #
  171. # Schema upgrade
  172. #
  173. def run_schema_upgrade():
  174. jdk_path = get_java_exe_path()
  175. if jdk_path is None:
  176. print_error_msg("No JDK found, please run the \"setup\" "
  177. "command to install a JDK automatically or install any "
  178. "JDK manually to " + configDefaults.JDK_INSTALL_DIR)
  179. return 1
  180. print 'Upgrading database schema'
  181. command = SCHEMA_UPGRADE_HELPER_CMD.format(jdk_path, get_full_ambari_classpath())
  182. (retcode, stdout, stderr) = run_os_command(command)
  183. print_info_msg("Return code from schema upgrade command, retcode = " + str(retcode))
  184. if retcode > 0:
  185. print_error_msg("Error executing schema upgrade, please check the server logs.")
  186. else:
  187. print_info_msg('Schema upgrade completed')
  188. return retcode
  189. #
  190. # Upgrades the Ambari Server.
  191. #
  192. def move_user_custom_actions():
  193. print_info_msg('Moving *.py files from custom_actions to custom_actions/scripts')
  194. properties = get_ambari_properties()
  195. if properties == -1:
  196. err = "Error getting ambari properties"
  197. print_error_msg(err)
  198. raise FatalException(-1, err)
  199. try:
  200. resources_dir = properties[RESOURCES_DIR_PROPERTY]
  201. except (KeyError), e:
  202. conf_file = properties.fileName
  203. err = 'Property ' + str(e) + ' is not defined at ' + conf_file
  204. print_error_msg(err)
  205. raise FatalException(1, err)
  206. custom_actions_dir_path = os.path.join(resources_dir, 'custom_actions')
  207. custom_actions_scripts_dir_path = os.path.join(custom_actions_dir_path, 'scripts')
  208. print_info_msg('Moving *.py files from %s to %s' % (custom_actions_dir_path, custom_actions_scripts_dir_path))
  209. try:
  210. for custom_action_file_name in os.listdir(custom_actions_dir_path):
  211. custom_action_file_path = os.path.join(custom_actions_dir_path, custom_action_file_name)
  212. if os.path.isfile(custom_action_file_path) and custom_action_file_path.endswith('.py'):
  213. print_info_msg('Moving %s to %s' % (custom_action_file_path, custom_actions_scripts_dir_path))
  214. shutil.move(custom_action_file_path, custom_actions_scripts_dir_path)
  215. except (OSError, shutil.Error) as e:
  216. err = 'Upgrade failed. Can not move *.py files from %s to %s. ' % (custom_actions_dir_path, custom_actions_scripts_dir_path) + str(e)
  217. print_error_msg(err)
  218. raise FatalException(1, err)
  219. def upgrade(args):
  220. if not is_root():
  221. err = configDefaults.MESSAGE_ERROR_UPGRADE_NOT_ROOT
  222. raise FatalException(4, err)
  223. print 'Updating properties in ' + AMBARI_PROPERTIES_FILE + ' ...'
  224. retcode = update_ambari_properties()
  225. if not retcode == 0:
  226. err = AMBARI_PROPERTIES_FILE + ' file can\'t be updated. Exiting'
  227. raise FatalException(retcode, err)
  228. try:
  229. update_database_name_property(upgrade=True)
  230. except FatalException:
  231. return -1
  232. # Ignore the server version & database options passed via command-line arguments
  233. parse_properties_file(args)
  234. #TODO check database version
  235. change_objects_owner(args)
  236. retcode = run_schema_upgrade()
  237. if not retcode == 0:
  238. print_error_msg("Ambari server upgrade failed. Please look at {0}, for more details.".format(configDefaults.SERVER_LOG_FILE))
  239. raise FatalException(11, 'Schema upgrade failed.')
  240. user = read_ambari_user()
  241. if user is None:
  242. warn = "Can not determine custom ambari user.\n" + SETUP_OR_UPGRADE_MSG
  243. print_warning_msg(warn)
  244. else:
  245. adjust_directory_permissions(user)
  246. # local repo
  247. upgrade_local_repo(args)
  248. # create jdbc symlinks if jdbc drivers are available in resources
  249. check_jdbc_drivers(args)
  250. properties = get_ambari_properties()
  251. if properties == -1:
  252. err = "Error getting ambari properties"
  253. print_error_msg(err)
  254. raise FatalException(-1, err)
  255. # Move *.py files from custom_actions to custom_actions/scripts
  256. # This code exists for historic reasons in which custom action python scripts location changed from Ambari 1.7.0 to 2.0.0
  257. ambari_version = get_ambari_version(properties)
  258. if ambari_version is None:
  259. args.warnings.append("*.py files were not moved from custom_actions to custom_actions/scripts.")
  260. elif compare_versions(ambari_version, "2.0.0") == 0:
  261. move_user_custom_actions()
  262. # Remove ADMIN_VIEW directory for upgrading Admin View on Ambari upgrade from 1.7.0 to 2.0.0
  263. admin_views_dirs = get_admin_views_dir(properties)
  264. for admin_views_dir in admin_views_dirs:
  265. shutil.rmtree(admin_views_dir)
  266. # check if ambari has obsolete LDAP configuration
  267. if properties.get_property(LDAP_PRIMARY_URL_PROPERTY) and not properties.get_property(IS_LDAP_CONFIGURED):
  268. args.warnings.append("Existing LDAP configuration is detected. You must run the \"ambari-server setup-ldap\" command to adjust existing LDAP configuration.")
  269. #
  270. # Set current cluster version (run Finalize during manual RU)
  271. #
  272. def set_current(options):
  273. server_status, pid = is_server_runing()
  274. if not server_status:
  275. err = 'Ambari Server is not running.'
  276. raise FatalException(1, err)
  277. finalize_options = SetCurrentVersionOptions(options)
  278. if finalize_options.no_finalize_options_set():
  279. err = 'Must specify --cluster-name and --version-display-name. Please invoke ambari-server.py --help to print the options.'
  280. raise FatalException(1, err)
  281. admin_login = get_validated_string_input(prompt="Enter Ambari Admin login: ", default=None,
  282. pattern=None, description=None,
  283. is_pass=False, allowEmpty=False)
  284. admin_password = get_validated_string_input(prompt="Enter Ambari Admin password: ", default=None,
  285. pattern=None, description=None,
  286. is_pass=True, allowEmpty=False)
  287. properties = get_ambari_properties()
  288. if properties == -1:
  289. raise FatalException(1, "Failed to read properties file.")
  290. base_url = get_ambari_server_api_base(properties)
  291. url = base_url + "clusters/{0}/stack_versions".format(finalize_options.cluster_name)
  292. admin_auth = base64.encodestring('%s:%s' % (admin_login, admin_password)).replace('\n', '')
  293. request = urllib2.Request(url)
  294. request.add_header('Authorization', 'Basic %s' % admin_auth)
  295. request.add_header('X-Requested-By', 'ambari')
  296. data = {
  297. "ClusterStackVersions": {
  298. "repository_version": finalize_options.desired_repo_version,
  299. "state": "CURRENT"
  300. }
  301. }
  302. if get_verbose():
  303. sys.stdout.write('\nCalling API ' + url + ' : ' + str(data) + '\n')
  304. request.add_data(json.dumps(data))
  305. request.get_method = lambda: 'PUT'
  306. try:
  307. response = urllib2.urlopen(request)
  308. except urllib2.HTTPError, e:
  309. code = e.getcode()
  310. content = e.read()
  311. err = 'Error during setting current version. Http status code - {0}. \n {1}'.format(
  312. code, content)
  313. raise FatalException(1, err)
  314. except Exception as e:
  315. err = 'Setting current version failed. Error details: %s' % e
  316. raise FatalException(1, err)
  317. sys.stdout.write('\nCurrent version successfully updated to ' + finalize_options.desired_repo_version)
  318. sys.stdout.write('\n')
  319. sys.stdout.flush()
  320. class SetCurrentVersionOptions:
  321. def __init__(self, options):
  322. try:
  323. self.cluster_name = options.cluster_name
  324. except AttributeError:
  325. self.cluster_name = None
  326. try:
  327. self.desired_repo_version = options.desired_repo_version
  328. except AttributeError:
  329. self.desired_repo_version = None
  330. def no_finalize_options_set(self):
  331. return self.cluster_name is None or self.desired_repo_version is None