summaryrefslogtreecommitdiff
path: root/ambari-server/src/main/python/ambari-server-windows.py
blob: 039069c6ab21132af1783fcacbd8806b83404d7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
#!/usr/bin/env python

'''
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'''

import optparse

from ambari_commons.ambari_service import AmbariService
from ambari_commons.logging_utils import *
from ambari_commons.os_utils import remove_file
from ambari_commons.os_windows import SvcStatusCallback

from ambari_server import utils
from ambari_server.dbConfiguration import DBMSConfig
from ambari_server.resourceFilesKeeper import ResourceFilesKeeper, KeeperException
from ambari_server.serverConfiguration import *
from ambari_server.serverSetup import setup, reset, is_server_running, upgrade
from ambari_server.setupActions import *
from ambari_server.setupSecurity import *
from ambari_server.serverSetup_windows import SERVICE_PASSWORD_KEY, SERVICE_USERNAME_KEY

# debug settings
SERVER_START_DEBUG = False
SUSPEND_START_MODE = False

# server commands
ambari_provider_module_option = ""
ambari_provider_module = os.environ.get('AMBARI_PROVIDER_MODULE')

#Common setup or upgrade message
SETUP_OR_UPGRADE_MSG = "- If this is a new setup, then run the \"ambari-server setup\" command to create the user\n" \
"- If this is an upgrade of an existing setup, run the \"ambari-server upgrade\" command.\n" \
"Refer to the Ambari documentation for more information on setup and upgrade."

AMBARI_SERVER_DIE_MSG = "Ambari Server java process died with exitcode {0}. Check {1} for more information."

if ambari_provider_module is not None:
  ambari_provider_module_option = "-Dprovider.module.class=" +\
                                  ambari_provider_module + " "

SERVER_START_CMD = \
                 "-server -XX:NewRatio=3 "\
                 "-XX:+UseConcMarkSweepGC " +\
                 "-XX:-UseGCOverheadLimit -XX:CMSInitiatingOccupancyFraction=60 " +\
                 ambari_provider_module_option +\
                 os.getenv('AMBARI_JVM_ARGS', '-Xms512m -Xmx2048m') +\
                 " -cp {0}" +\
                 " org.apache.ambari.server.controller.AmbariServer"
SERVER_START_CMD_DEBUG = \
                       "-server -XX:NewRatio=2 -XX:+UseConcMarkSweepGC " +\
                       ambari_provider_module_option +\
                       os.getenv('AMBARI_JVM_ARGS', '-Xms512m -Xmx2048m') +\
                       " -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,"\
                       "server=y,suspend={1} -cp {0}" +\
                       " org.apache.ambari.server.controller.AmbariServer"
SERVER_SEARCH_PATTERN = "org.apache.ambari.server.controller.AmbariServer"

SERVER_INIT_TIMEOUT = 5
SERVER_START_TIMEOUT = 10

PID_NAME = "ambari-server.pid"
EXITCODE_NAME = "ambari-server.exitcode"

SERVER_VERSION_FILE_PATH = "server.version.file"

# linux open-file limit
ULIMIT_OPEN_FILES_KEY = 'ulimit.open.files'
ULIMIT_OPEN_FILES_DEFAULT = 10000


class AmbariServerService(AmbariService):
  AmbariService._svc_name_ = "Ambari Server"
  AmbariService._svc_display_name_ = "Ambari Server"
  AmbariService._svc_description_ = "Ambari Server"

  AmbariService._AdjustServiceVersion()

  def SvcDoRun(self):
    scmStatus = SvcStatusCallback(self)

    properties = get_ambari_properties()
    self.options.verbose = get_value_from_properties(properties, VERBOSE_OUTPUT_KEY, self.options.verbose)
    self.options.debug = get_value_from_properties(properties, DEBUG_MODE_KEY, self.options.debug)
    self.options.suspend_start = get_value_from_properties(properties, SUSPEND_START_MODE_KEY, self.options.suspend_start)

    self.redirect_output_streams()

    childProc = server_process_main(self.options, scmStatus)

    if not self._StopOrWaitForChildProcessToFinish(childProc):
      return

    pid_file_path = PID_DIR + os.sep + PID_NAME
    remove_file(pid_file_path)
    pass

  def _InitOptionsParser(self):
    return init_options_parser()

  def redirect_output_streams(self):
    properties = get_ambari_properties()

    outFilePath = properties[SERVER_OUT_FILE_KEY]
    if (outFilePath is None or outFilePath == ""):
      outFilePath = SERVER_OUT_FILE

    self._RedirectOutputStreamsToFile(outFilePath)
    pass

def ctrlHandler(ctrlType):
  AmbariServerService.DefCtrlCHandler()
  return True

def svcsetup():
  AmbariServerService.set_ctrl_c_handler(ctrlHandler)
  # we don't save password between 'setup' runs, so we can't run Install every time. We run 'setup' only if user and
  # password provided or if service not installed
  if (SERVICE_USERNAME_KEY in os.environ and SERVICE_PASSWORD_KEY in os.environ):
    AmbariServerService.Install(username=os.environ[SERVICE_USERNAME_KEY], password=os.environ[SERVICE_PASSWORD_KEY])
  elif AmbariServerService.QueryStatus() == "not installed":
    AmbariServerService.Install()
  pass

#
# Starts the Ambari Server as a standalone process.
# args:
#  <no arguments> = start the server as a process. For now, there is no restrictions for the number of server instances
#     that can run like this.
#  -s, --single-instance = Reserved for future use. When starting the server as a process, ensure only one instance of the process is running.
#     If this is the second instance of the process, the function fails.
#
def start(options):
  AmbariServerService.set_ctrl_c_handler(ctrlHandler)

  #Run as a normal process. Invoke the ServiceMain directly.
  childProc = server_process_main(options)

  childProc.wait()

  pid_file_path = PID_DIR + os.sep + PID_NAME
  remove_file(pid_file_path)

#
# Starts the Ambari Server as a service.
# Start the server in normal mode, as a Windows service. If the Ambari server is
#     not registered as a service, the function fails. By default, only one instance of the service can
#     possibly run.
#
def svcstart():
  AmbariServerService.Start()
  pass

def server_process_main(options, scmStatus=None):
  # set verbose
  try:
    global VERBOSE
    VERBOSE = options.verbose
  except AttributeError:
    pass

  # set silent
  try:
    global SILENT
    SILENT = options.silent
  except AttributeError:
    pass

  # debug mode
  try:
    global DEBUG_MODE
    DEBUG_MODE = options.debug
  except AttributeError:
    pass

  # stop Java process at startup?
  try:
    global SUSPEND_START_MODE
    SUSPEND_START_MODE = options.suspend_start
  except AttributeError:
    pass

  if not utils.check_reverse_lookup():
    print_warning_msg("The hostname was not found in the reverse DNS lookup. "
                      "This may result in incorrect behavior. "
                      "Please check the DNS setup and fix the issue.")

  properties = get_ambari_properties()

  print_info_msg("Ambari Server is not running...")

  conf_dir = get_conf_dir()
  jdk_path = find_jdk()
  if jdk_path is None:
    err = "No JDK found, please run the \"ambari-server setup\" " \
                    "command to install a JDK automatically or install any " \
                    "JDK manually to " + JDK_INSTALL_DIR
    raise FatalException(1, err)

  # Preparations

  result = ensure_dbms_is_running(options, properties, scmStatus)
  if result == -1:
    raise FatalException(-1, "Unable to connect to the database")

  if scmStatus is not None:
    scmStatus.reportStartPending()

  ensure_resources_are_organized(properties)

  if scmStatus is not None:
    scmStatus.reportStartPending()

  environ = os.environ.copy()
  ensure_server_security_is_configured(properties, environ)

  if scmStatus is not None:
    scmStatus.reportStartPending()

  conf_dir = os.path.abspath(conf_dir) + os.pathsep + get_ambari_classpath()
  if conf_dir.find(' ') != -1:
    conf_dir = '"' + conf_dir + '"'

  java_exe = jdk_path + os.sep + JAVA_EXE_SUBPATH
  pidfile = PID_DIR + os.sep + PID_NAME
  command_base = SERVER_START_CMD_DEBUG if (DEBUG_MODE or SERVER_START_DEBUG) else SERVER_START_CMD
  suspend_mode = 'y' if SUSPEND_START_MODE else 'n'
  command = command_base.format(conf_dir, suspend_mode)
  if not os.path.exists(PID_DIR):
    os.makedirs(PID_DIR, 0755)

  set_open_files_limit(get_ulimit_open_files());

  #Ignore the requirement to run as root. In Windows, by default the child process inherits the security context
  # and the environment from the parent process.
  param_list = java_exe + " " + command

  print_info_msg("Running server: " + str(param_list))
  procJava = subprocess.Popen(param_list, env=environ)

  #wait for server process for SERVER_START_TIMEOUT seconds
  print "Waiting for server start..."

  pidJava = procJava.pid
  if pidJava <= 0:
    procJava.terminate()
    exitcode = procJava.returncode
    exitfile = os.path.join(PID_DIR, EXITCODE_NAME)
    utils.save_pid(exitcode, exitfile)

    if scmStatus is not None:
      scmStatus.reportStopPending()

    raise FatalException(-1, AMBARI_SERVER_DIE_MSG.format(exitcode, SERVER_OUT_FILE))
  else:
    utils.save_pid(pidJava, pidfile)
    print "Server PID at: "+pidfile
    print "Server out at: "+SERVER_OUT_FILE
    print "Server log at: "+SERVER_LOG_FILE

  if scmStatus is not None:
    scmStatus.reportStarted()

  return procJava

#Check the JDBC driver status
#If not found abort
#Get SQL Server service status from SCM
#If 'stopped' then start it
#Wait until the status is 'started' or a configured timeout elapses
#If the timeout has been reached, bail out with exception
def ensure_dbms_is_running(options, properties, scmStatus):
  dbms = DBMSConfig.create(options, properties, "Ambari")
  if not dbms._is_jdbc_driver_installed(properties):
    raise FatalException(-1, "JDBC driver is not installed. Run ambari-server setup and try again.")

  dbms.ensure_dbms_is_running(options, properties, scmStatus)

  dbms2 = DBMSConfig.create(options, properties, "Metrics")
  if dbms2.database_host.lower() != dbms.database_host.lower():
    dbms2.ensure_dbms_is_running(options, properties, scmStatus)
  pass

def ensure_resources_are_organized(properties):
  resources_location = get_resources_location(properties)
  resource_files_keeper = ResourceFilesKeeper(resources_location)
  try:
    print "Organizing resource files at {0}...".format(resources_location,
                                                       verbose=VERBOSE)
    resource_files_keeper.perform_housekeeping()
  except KeeperException, ex:
    msg = "Can not organize resource files at {0}: {1}".format(
      resources_location, str(ex))
    raise FatalException(-1, msg)


def ensure_server_security_is_configured(properties, environ):
  pass


#
# Stops the Ambari Server.
#
def svcstop():
  AmbariServerService.Stop()


### Stack upgrade ###

#def upgrade_stack(args, stack_id, repo_url=None, repo_url_os=None):


def get_resources_location(properties):
  res_location = properties[RESOURCES_DIR_PROPERTY]
  if res_location is None:
    res_location = RESOURCES_DIR_DEFAULT
  return res_location
#  pass

def get_stack_location(properties):
  stack_location = properties[STACK_LOCATION_KEY]
  if stack_location is None:
    stack_location = STACK_LOCATION_DEFAULT
  return stack_location
#  pass


#
# The Ambari Server status.
#
def svcstatus(options):
  options.exit_message = None

  statusStr = AmbariServerService.QueryStatus()
  print "Ambari Server is " + statusStr


def get_ulimit_open_files():
  properties = get_ambari_properties()
  if properties == -1:
    print "Error reading ambari properties"
    return None

  open_files = int(properties[ULIMIT_OPEN_FILES_KEY])
  if open_files > 0:
    return open_files
  else:
    return ULIMIT_OPEN_FILES_DEFAULT


def init_options_parser():
  parser = optparse.OptionParser(usage="usage: %prog action [options] [stack_id os]", )
  #parser.add_option('-i', '--create-db-script-file', dest="create_db_script_file",
  #                  default="resources" + os.sep + "Ambari-DDL-SQLServer-CREATELOCAL.sql",
  #                  help="File with database creation script")
  parser.add_option('-f', '--init-script-file', dest="init_db_script_file",
                    default="resources" + os.sep + "Ambari-DDL-SQLServer-CREATE.sql",
                    help="File with database setup script")
  parser.add_option('-r', '--drop-script-file', dest="cleanup_db_script_file",
                    default="resources" + os.sep + "Ambari-DDL-SQLServer-DROP.sql",
                    help="File with database cleanup script")
  parser.add_option('-j', '--java-home', dest="java_home", default=None,
                    help="Use specified java_home.  Must be valid on all hosts")
  parser.add_option("-v", "--verbose",
                    action="store_true", dest="verbose", default=False,
                    help="Print verbose status messages")
  parser.add_option("-s", "--silent",
                    action="store_true", dest="silent", default=False,
                    help="Silently accepts default prompt values")
  parser.add_option('-g', '--debug', action="store_true", dest='debug', default=False,
                    help="Start ambari-server in debug mode")
  parser.add_option('-y', '--suspend-start', action="store_true", dest='suspend_start', default=False,
                    help="Freeze ambari-server Java process at startup in debug mode")

  parser.add_option('-a', '--databasehost', dest="database_host", default=None,
                    help="Hostname of database server")
  parser.add_option('-n', '--databaseport', dest="database_port", default=None,
                    help="Database server listening port")
  parser.add_option('-d', '--databasename', dest="database_name", default=None,
                    help="Database/Schema/Service name or ServiceID")
  parser.add_option('-w', '--windowsauth', action="store_true", dest="database_windows_auth", default=None,
                    help="Integrated Windows authentication")
  parser.add_option('-u', '--databaseusername', dest="database_username", default=None,
                    help="Database user login")
  parser.add_option('-p', '--databasepassword', dest="database_password", default=None,
                    help="Database user password")

  parser.add_option('-t', '--init-metrics-script-file', dest="init_metrics_db_script_file", default=None,
                    help="File with metrics database setup script")
  parser.add_option('-c', '--drop-metrics-script-file', dest="cleanup_metrics_db_script_file", default=None,
                    help="File with metrics database cleanup script")

  parser.add_option('-m', '--metricsdatabasehost', dest="metrics_database_host", default=None,
                    help="Hostname of metrics database server")
  parser.add_option('-o', '--metricsdatabaseport', dest="metrics_database_port", default=None,
                    help="Metrics database server listening port")
  parser.add_option('-e', '--metricsdatabasename', dest="metrics_database_name", default=None,
                    help="Metrics database/Schema/Service name or ServiceID")
  parser.add_option('-z', '--metricswindowsauth', action="store_true", dest="metrics_database_windows_auth", default=None,
                    help="Integrated Windows authentication for the metrics database")
  parser.add_option('-q', '--metricsdatabaseusername', dest="metrics_database_username", default=None,
                    help="Metrics database user login")
  parser.add_option('-l', '--metricsdatabasepassword', dest="metrics_database_password", default=None,
                    help="Metrics database user password")

  parser.add_option('--jdbc-driver', default=None, dest="jdbc_driver",
                    help="Specifies the path to the JDBC driver JAR file for the " \
                         "database type specified with the --jdbc-db option. Used only with --jdbc-db option.")
  # -b, -i, -k and -x the remaining available short options
  # -h reserved for help
  return parser

def are_cmd_line_db_args_blank(options):
  if (options.database_host is None \
    and options.database_name is None \
    and options.database_windows_auth is None \
    and options.database_username is None \
    and options.database_password is None \
    and options.metrics_database_host is None \
    and options.metrics_database_name is None \
    and options.metrics_database_windows_auth is None \
    and options.metrics_database_username is None \
    and options.metrics_database_password is None):
    return True
  return False


def are_db_auth_options_ok(db_windows_auth, db_username, db_password):
  if db_windows_auth is True:
    return True
  else:
    if db_username is not None and db_username is not "" and db_password is not None and db_password is not "":
      return True
  return False

def are_cmd_line_db_args_valid(options):
  if (options.database_host is not None and options.database_host is not "" \
      #and options.database_name is not None \         # ambari by default is ok
      and are_db_auth_options_ok(options.database_windows_auth,
                               options.database_username,
                               options.database_password) \
    and options.metrics_database_host is not None and options.metrics_database_host is not "" \
    #and options.metrics_database_name is not None \  # HadoopMetrics by default is ok
    and are_db_auth_options_ok(options.metrics_database_windows_auth,
                               options.metrics_database_username,
                               options.metrics_database_password)):
    return True
  return False


def setup_security(args):
  need_restart = True
  #Print menu options
  print '=' * 75
  print 'Choose one of the following options: '
  print '  [1] Enable HTTPS for Ambari server.'
  print '  [2] Encrypt passwords stored in ambari.properties file.'
  print '  [3] Setup Ambari kerberos JAAS configuration.'
  print '=' * 75
  choice = get_validated_string_input('Enter choice, (1-3): ', '0', '[1-3]',
                                      'Invalid choice', False, False)

  if choice == '1':
    need_restart = setup_https(args)
  elif choice == '2':
    setup_master_key()
  elif choice == '3':
    setup_ambari_krb5_jaas()
  else:
    raise FatalException('Unknown option for setup-security command.')

  return need_restart

#
# Main.
#
def main():
  parser = init_options_parser()
  (options, args) = parser.parse_args()

  #perform checks
  options.warnings = []
  options.must_set_database_options = False

  if are_cmd_line_db_args_blank(options):
    options.must_set_database_options = True
    #TODO Silent is invalid here, right?

  elif not are_cmd_line_db_args_valid(options):
    parser.error('All database options should be set. Please see help for the options.')

  ## jdbc driver and db options validation
  #if options.jdbc_driver is None and options.jdbc_db is not None:
  #  parser.error("Option --jdbc-db is used only in pair with --jdbc-driver")
  #elif options.jdbc_driver is not None and options.jdbc_db is None:
  #  parser.error("Option --jdbc-driver is used only in pair with --jdbc-db")

  if options.debug:
    sys.frozen = 'windows_exe' # Fake py2exe so we can debug

  if len(args) == 0:
    print parser.print_help()
    parser.error("No action entered")

  action = args[0]

  if action == UPGRADE_STACK_ACTION:
    possible_args_numbers = [2,4] # OR
  else:
    possible_args_numbers = [1]

  matches = 0
  for args_number_required in possible_args_numbers:
    matches += int(len(args) == args_number_required)

  if matches == 0:
    print parser.print_help()
    possible_args = ' or '.join(str(x) for x in possible_args_numbers)
    parser.error("Invalid number of arguments. Entered: " + str(len(args)) + ", required: " + possible_args)

  options.exit_message = "Ambari Server '%s' completed successfully." % action
  need_restart = True
  try:
    if action == SETUP_ACTION:
      setup(options)
      svcsetup()
    elif action == START_ACTION:
      svcstart()
    elif action == PSTART_ACTION:
      start(options)
    elif action == STOP_ACTION:
      svcstop()
    elif action == RESET_ACTION:
      reset(options, AmbariServerService)
    elif action == STATUS_ACTION:
      svcstatus(options)
    elif action == UPGRADE_ACTION:
      upgrade(options)
#    elif action == UPGRADE_STACK_ACTION:
#      stack_id = args[1]
#      repo_url = None
#      repo_url_os = None
#
#      if len(args) > 2:
#        repo_url = args[2]
#      if len(args) > 3:
#        repo_url_os = args[3]
#
#      upgrade_stack(options, stack_id, repo_url, repo_url_os)
    elif action == LDAP_SETUP_ACTION:
      setup_ldap()
    elif action == SETUP_SECURITY_ACTION:
      need_restart = setup_security(options)
    else:
      parser.error("Invalid action")

    if action in ACTION_REQUIRE_RESTART and need_restart:
      status, stateDesc = is_server_running(AmbariServerService)
      if status:
        print 'NOTE: Restart Ambari Server to apply changes' + \
              ' ("ambari-server restart|stop|start")'

    if options.warnings:
      for warning in options.warnings:
        print_warning_msg(warning)
        pass
      options.exit_message = "Ambari Server '%s' completed with warnings." % action
      pass
  except FatalException as e:
    if e.reason is not None:
      print_error_msg("Exiting with exit code {0}. \nREASON: {1}".format(e.code, e.reason))
    sys.exit(e.code)
  except NonFatalException as e:
    options.exit_message = "Ambari Server '%s' completed with warnings." % action
    if e.reason is not None:
      print_warning_msg(e.reason)

  if options.exit_message is not None:
    print options.exit_message


if __name__ == "__main__":
  try:
    main()
  except (KeyboardInterrupt, EOFError):
    print("\nAborting ... Keyboard Interrupt.")
    sys.exit(1)