コード例 #1
0
  def create_user(cls, options, is_admin):
    """Create a new user with the parameters given.

        Args:
          options: A Namespace that has fields for each parameter that can be
            passed in via the command-line interface.
          is_admin: A flag to indicate if the user to be created is an admin user
        Raises:
          AppControllerException: If the AppController on the head node crashes.
            When this occurs, the message in the exception contains the reason why
            the AppController crashed.
        """
    secret = LocalState.get_secret_key(options.keyname)
    load_balancer_ip = LocalState.get_host_with_role(
      options.keyname, 'load_balancer')

    username, password = LocalState.get_credentials(is_admin)

    acc = AppControllerClient(load_balancer_ip, secret)

    RemoteHelper.create_user_accounts(
      username, password, load_balancer_ip, options.keyname)

    try:
      if is_admin:
        acc.set_admin_role(username, 'true', cls.ADMIN_CAPABILITIES)
    except Exception as exception:
      AppScaleLogger.warn("Could not grant admin privileges to the user for the " +
        "following reason: {0}".format(str(exception)))
      sys.exit(1)
コード例 #2
0
def _get_stats(keyname, stats_kind, include_lists):
    """
  Returns statistics from Hermes.

  Args:
    keyname: A string representing an identifier from AppScaleFile.
    stats_kind: A string representing a kind of statistics.
    include_lists: A dict representing desired fields.

  Returns:
    A dict of statistics.
    A dict of failures.
  """
    load_balancer_ip = LocalState.get_host_with_role(keyname, 'load_balancer')
    secret = LocalState.get_secret_key(keyname=keyname)
    administration_port = "17441"
    stats_path = "/stats/cluster/{stats_kind}".format(stats_kind=stats_kind)

    headers = {'Appscale-Secret': secret}
    data = {'include_lists': include_lists}
    url = "https://{ip}:{port}{path}".format(ip=load_balancer_ip,
                                             port=administration_port,
                                             path=stats_path)

    try:
        requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
        resp = requests.get(url=url, headers=headers, json=data, verify=False)
        resp.raise_for_status()
    except requests.HTTPError as err:
        AppScaleLogger.warn("Failed to get {stats_kind} stats ({err})".format(
            stats_kind=stats_kind, err=err))
        return {}, {}

    json_body = resp.json()
    return json_body["stats"], json_body["failures"]
コード例 #3
0
  def print_cluster_status(cls, options):
    """
    Gets cluster stats and prints it nicely.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    """
    try:
      load_balancer_ip = LocalState.get_host_with_role(
        options.keyname, 'load_balancer')
      acc = AppControllerClient(
        load_balancer_ip, LocalState.get_secret_key(options.keyname))
      all_private_ips = acc.get_all_private_ips()
      cluster_stats = acc.get_cluster_stats()
    except (faultType, AppControllerException, BadConfigurationException):
      AppScaleLogger.warn("AppScale deployment is probably down")
      raise

    # Convert cluster stats to useful structures
    node_stats = {
      ip: next((n for n in cluster_stats if n["private_ip"] == ip), None)
      for ip in all_private_ips
    }
    apps_dict = next((n["apps"] for n in cluster_stats if n["apps"]), {})
    services = [ServiceInfo(key.split('_')[0], key.split('_')[1], app_info)
                for key, app_info in apps_dict.iteritems()]
    nodes = [NodeStats(ip, node) for ip, node in node_stats.iteritems() if node]
    invisible_nodes = [ip for ip, node in node_stats.iteritems() if not node]

    if options.verbose:
      AppScaleLogger.log("-"*76)
      cls._print_nodes_info(nodes, invisible_nodes)
      cls._print_roles_info(nodes)
    else:
      AppScaleLogger.log("-"*76)

    cls._print_cluster_summary(nodes, invisible_nodes, services)
    cls._print_services(services)
    cls._print_status_alerts(nodes)

    try:
      login_host = acc.get_property('login')['login']
    except KeyError:
      raise AppControllerException('login property not found')

    dashboard = next(
      (service for service in services
       if service.http == RemoteHelper.APP_DASHBOARD_PORT), None)
    if dashboard and dashboard.appservers >= 1:
      AppScaleLogger.success(
        "\nView more about your AppScale deployment at http://{}:{}/status"
        .format(login_host, RemoteHelper.APP_DASHBOARD_PORT)
      )
    else:
      AppScaleLogger.log(
        "\nAs soon as AppScale Dashboard is started you can visit it at "
        "http://{0}:{1}/status and see more about your deployment"
        .format(login_host, RemoteHelper.APP_DASHBOARD_PORT)
      )
コード例 #4
0
    def deploy(self, app, project_id=None):
        """ 'deploy' is a more accessible way to tell an AppScale deployment to run a
    Google App Engine application than 'appscale-upload-app'. It calls that
    command with the configuration options found in the AppScalefile in the
    current working directory.

    Args:
      app: The path (absolute or relative) to the Google App Engine application
        that should be uploaded.
      project_id: Which project ID to use to deploy the application.
    Returns:
      A tuple containing the host and port where the application is serving
        traffic from.
    Raises:
      AppScalefileException: If there is no AppScalefile in the current working
      directory.
    """
        contents = self.read_appscalefile()

        # Construct an upload-app command from the file's contents
        command = []
        contents_as_yaml = yaml.safe_load(contents)
        if 'keyname' in contents_as_yaml:
            command.append("--keyname")
            command.append(contents_as_yaml['keyname'])

        if 'test' in contents_as_yaml and contents_as_yaml['test'] == True:
            command.append("--test")

        if 'verbose' in contents_as_yaml and contents_as_yaml[
                'verbose'] == True:
            command.append("--verbose")

        command.append("--file")
        command.append(app)

        if project_id is not None:
            command.append("--project")
            command.append(project_id)

        # Finally, exec the command. Don't worry about validating it -
        # appscale-upload-app will do that for us.
        options = ParseArgs(command, "appscale-upload-app").args
        login_host, http_port = AppScaleTools.upload_app(options)
        AppScaleTools.update_indexes(options.file, options.keyname,
                                     options.project)
        AppScaleTools.update_cron(options.file, options.keyname,
                                  options.project)
        AppScaleTools.update_queues(options.file, options.keyname,
                                    options.project)
        try:
            AppScaleTools.update_dispatch(options.file, options.keyname,
                                          options.project)
        except (AdminError, AppScaleException) as e:
            AppScaleLogger.warn(
                'Request to update dispatch failed, if your '
                'dispatch references undeployed services, ignore '
                'this exception: {}'.format(e))
        return login_host, http_port
コード例 #5
0
  def update_queues(cls, source_location, keyname, project_id):
    """ Updates a project's queues from the configuration file.

    Args:
      source_location: A string specifying the location of the source code.
      keyname: A string specifying the key name.
      project_id: A string specifying the project ID.
    """
    if cls.TAR_GZ_REGEX.search(source_location):
      fetch_function = utils.config_from_tar_gz
      version = Version.from_tar_gz(source_location)
    elif cls.ZIP_REGEX.search(source_location):
      fetch_function = utils.config_from_zip
      version = Version.from_zip(source_location)
    elif os.path.isdir(source_location):
      fetch_function = utils.config_from_dir
      version = Version.from_directory(source_location)
    elif source_location.endswith('.yaml'):
      fetch_function = utils.config_from_dir
      version = Version.from_yaml_file(source_location)
      source_location = os.path.dirname(source_location)
    else:
      raise BadConfigurationException(
        '{} must be a directory, tar.gz, or zip'.format(source_location))

    if project_id:
      version.project_id = project_id

    queue_config = fetch_function('queue.yaml', source_location)
    if queue_config is None:
      queue_config = fetch_function('queue.xml', source_location)
      # If the source does not have a queue configuration file, do nothing.
      if queue_config is None:
        return

      queues = utils.queues_from_xml(queue_config)
    else:
      queues = yaml.safe_load(queue_config)

    AppScaleLogger.log('Updating queues')

    for queue in queues.get('queue', []):
      if 'bucket_size' in queue or 'max_concurrent_requests' in queue:
        AppScaleLogger.warn('Queue configuration uses unsupported rate options'
                            ' (bucket size or max concurrent requests)')
        break

    load_balancer_ip = LocalState.get_host_with_role(keyname, 'load_balancer')
    secret_key = LocalState.get_secret_key(keyname)
    admin_client = AdminClient(load_balancer_ip, secret_key)
    admin_client.update_queues(version.project_id, queues)
コード例 #6
0
def print_failures(failures):
    """
  Prints a failure list.

  Args:
    failures: A dict in which each key is a kind of statistics and
      value if a failure list.
  """
    stats_kinds = {"nodes": "Node", "processes": "Process", "proxies": "Proxy"}

    AppScaleLogger.warn("There are some failures while getting stats:")
    for kind, fails in failures.iteritems():
        for ip, failure in fails.iteritems():
            AppScaleLogger.warn(
                "  {stats_kind} stats from {ip}: {failure}".format(
                    stats_kind=stats_kinds[kind], ip=ip, failure=failure))
コード例 #7
0
  def _print_status_alerts(cls, nodes):
    """ Detects if there are hardware issues in the cluster and prints
        all detected problems
    Args:
      nodes: a list of NodeStats
    """
    hardware_alerts = []
    for node in nodes:
      # Check disk space
      db_roles = ["db_master", "db_slave", "database"]
      is_db_node = any(role in node.roles for role in db_roles)
      partition = node.disk.most_loaded
      if is_db_node and partition.free_percent < MIN_FREE_DISK_DB:
        msg = ("Only {free:.1f}% of '{part}' partition at db node is free"
               .format(free=partition.free_percent, part=partition.mountpoint))
        hardware_alerts.append((node, msg))
      elif partition.free_percent < MIN_FREE_DISK:
        msg = ("Only {free:.1f}% of '{part}' partition is free"
               .format(free=partition.free_percent, part=partition.mountpoint))
        hardware_alerts.append((node, msg))

      # Check memory
      if node.memory.available_percent < MIN_AVAILABLE_MEMORY:
        msg = ("Only {available:.1f}% of memory is available"
               .format(available=node.memory.available_percent))
        hardware_alerts.append((node, msg))

      # Check average load
      is_overloaded = any((
        node.loadavg.last_1_min / node.cpu.count > MAX_LOADAVG,
        node.loadavg.last_5_min / node.cpu.count > MAX_LOADAVG,
        node.loadavg.last_15_min / node.cpu.count > MAX_LOADAVG,
      ))
      if is_overloaded:
        msg = ("Average load is too high for {} CPUs: {:.1f} {:.1f} {:.1f}"
               .format(node.cpu.count, node.loadavg.last_1_min,
                       node.loadavg.last_5_min,
                       node.loadavg.last_15_min))
        hardware_alerts.append((node, msg))

    if hardware_alerts:
      AppScaleLogger.warn("\nSome nodes are in alarm state:")
      header = ("PUBLIC IP", "PRIVATE IP", "ALERT MESSAGE")
      table = ((n.public_ip, n.private_ip, msg) for n, msg in hardware_alerts)
      AppScaleLogger.warn(tabulate(table, headers=header, tablefmt="plain"))
コード例 #8
0
def _get_stats(keyname, stats_kind, include_lists):
  """
  Returns statistics from Hermes.

  Args:
    keyname: A string representing an identifier from AppScaleFile.
    stats_kind: A string representing a kind of statistics.
    include_lists: A dict representing desired fields.

  Returns:
    A dict of statistics.
    A dict of failures.
  """
  load_balancer_ip = LocalState.get_host_with_role(keyname, 'load_balancer')
  secret = LocalState.get_secret_key(keyname=keyname)
  administration_port = "17441"
  stats_path = "/stats/cluster/{stats_kind}".format(stats_kind=stats_kind)

  headers = {'Appscale-Secret': secret}
  data = {'include_lists': include_lists}
  url = "https://{ip}:{port}{path}".format(
    ip=load_balancer_ip,
    port=administration_port,
    path=stats_path
  )

  try:
    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
    resp = requests.get(
      url=url,
      headers=headers,
      json=data,
      verify=False
    )
    resp.raise_for_status()
  except requests.HTTPError as err:
    AppScaleLogger.warn(
      "Failed to get {stats_kind} stats ({err})"
      .format(stats_kind=stats_kind, err=err)
    )
    return {}, {}

  json_body = resp.json()
  return json_body["stats"], json_body["failures"]
コード例 #9
0
  def _print_cluster_summary(cls, nodes, invisible_nodes, services):
    """ Prints summary about deployment state
    Args:
      nodes: a list of NodeStats
      invisible_nodes: IPs of nodes which didn't report its status yet
      services: a list of ServiceInfo objects
    """
    loaded = sum(1 for node in nodes if node.is_loaded)
    initialized = sum(1 for node in nodes if node.is_initialized)
    started_services = sum(1 for service in services if service.appservers > 0)
    total = len(nodes)

    if invisible_nodes:
      # We don't have full information about cluster
      AppScaleLogger.warn(
        "\nThere are {nodes} nodes that didn't report it's state"
        .format(nodes=len(invisible_nodes))
      )
      if nodes:
        AppScaleLogger.log(
          "Available stats for {n} nodes: {init} are initialized, {loaded} "
          "are loaded, {started} of {services} services are started".format(
            init=initialized, loaded=loaded, n=total, started=started_services,
            services=len(services))
        )
      else:
        AppScaleLogger.log("No stats is available yet.")
      return

    if (loaded < total or initialized < total or
        started_services < len(services)):
      AppScaleLogger.log(
        "\nAppScale is starting: {init} of {n} nodes are initialized, {loaded} "
        "of {n} nodes are loaded, {started} of {services} services are started"
        .format(init=initialized, loaded=loaded, n=total,
                started=started_services, services=len(services))
      )
    else:
      AppScaleLogger.success(
        "\nAppScale is up. All {n} nodes are loaded".format(n=total)
      )
コード例 #10
0
def print_failures(failures):
  """
  Prints a failure list.

  Args:
    failures: A dict in which each key is a kind of statistics and
      value if a failure list.
  """
  stats_kinds = {
    "nodes": "Node",
    "processes": "Process",
    "proxies": "Proxy"
  }

  AppScaleLogger.warn("There are some failures while getting stats:")
  for kind, fails in failures.iteritems():
    for ip, failure in fails.iteritems():
      AppScaleLogger.warn(
        "  {stats_kind} stats from {ip}: {failure}".format(
          stats_kind=stats_kinds[kind], ip=ip, failure=failure
        )
      )
コード例 #11
0
  def reset_password(cls, options):
    """Resets a user's password the currently running AppScale deployment.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    """
    secret = LocalState.get_secret_key(options.keyname)
    load_balancer_ip = LocalState.get_host_with_role(
      options.keyname, 'load_balancer')
    username, password = LocalState.get_credentials(is_admin=False)
    encrypted_password = LocalState.encrypt_password(username, password)

    acc = AppControllerClient(load_balancer_ip, secret)

    try:
      acc.reset_password(username, encrypted_password)
      AppScaleLogger.success("The password was successfully changed for the " \
        "given user.")
    except Exception as exception:
      AppScaleLogger.warn("Could not change the user's password for the " + \
        "following reason: {0}".format(str(exception)))
      sys.exit(1)
コード例 #12
0
  def upload_app(cls, options):
    """Uploads the given App Engine application into AppScale.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    Returns:
      A tuple containing the host and port where the application is serving
        traffic from.
    """
    custom_service_yaml = None
    if cls.TAR_GZ_REGEX.search(options.file):
      file_location = LocalState.extract_tgz_app_to_dir(options.file)
      created_dir = True
      version = Version.from_tar_gz(options.file)
    elif cls.ZIP_REGEX.search(options.file):
      file_location = LocalState.extract_zip_app_to_dir(options.file)
      created_dir = True
      version = Version.from_zip(options.file)
    elif os.path.isdir(options.file):
      file_location = options.file
      created_dir = False
      version = Version.from_directory(options.file)
    elif options.file.endswith('.yaml'):
      file_location = os.path.dirname(options.file)
      created_dir = False
      version = Version.from_yaml_file(options.file)
      custom_service_yaml = options.file
    else:
      raise AppEngineConfigException('{0} is not a tar.gz file, a zip file, ' \
        'or a directory. Please try uploading either a tar.gz file, a zip ' \
        'file, or a directory.'.format(options.file))

    if options.project:
      if version.runtime == 'java':
        raise BadConfigurationException("AppScale doesn't support --project for"
          "Java yet. Please specify the application id in appengine-web.xml.")

      version.project_id = options.project

    if version.project_id is None:
      if version.config_type == 'app.yaml':
        message = 'Specify --project or define "application" in your app.yaml'
      else:
        message = 'Define "application" in your appengine-web.xml'

      raise AppEngineConfigException(message)

    # Let users know that versions are not supported yet.
    AppEngineHelper.warn_if_version_defined(version, options.test)

    AppEngineHelper.validate_app_id(version.project_id)

    extras = {}
    if version.runtime == 'go':
      extras = LocalState.get_extra_go_dependencies(options.file, options.test)

    if (version.runtime == 'java'
        and AppEngineHelper.is_sdk_mismatch(file_location)):
      AppScaleLogger.warn(
        'AppScale did not find the correct SDK jar versions in your app. The '
        'current supported SDK version is '
        '{}.'.format(AppEngineHelper.SUPPORTED_SDK_VERSION))

    head_node_public_ip = LocalState.get_host_with_role(
      options.keyname, 'shadow')
    secret_key = LocalState.get_secret_key(options.keyname)
    admin_client = AdminClient(head_node_public_ip, secret_key)

    remote_file_path = RemoteHelper.copy_app_to_host(
      file_location, version.project_id, options.keyname,
      extras, custom_service_yaml)

    AppScaleLogger.log(
      'Deploying service {} for {}'.format(version.service_id,
                                           version.project_id))
    operation_id = admin_client.create_version(version, remote_file_path)

    # now that we've told the AppController to start our app, find out what port
    # the app is running on and wait for it to start serving
    AppScaleLogger.log("Please wait for your app to start serving.")

    deadline = time.time() + cls.MAX_OPERATION_TIME
    while True:
      if time.time() > deadline:
        raise AppScaleException('The deployment operation took too long.')
      operation = admin_client.get_operation(version.project_id, operation_id)
      if not operation['done']:
        time.sleep(1)
        continue

      if 'error' in operation:
        raise AppScaleException(operation['error']['message'])
      version_url = operation['response']['versionUrl']
      break

    AppScaleLogger.success(
      'Your app can be reached at the following URL: {}'.format(version_url))

    if created_dir:
      shutil.rmtree(file_location)

    match = re.match('http://(.+):(\d+)', version_url)
    login_host = match.group(1)
    http_port = int(match.group(2))
    return login_host, http_port
コード例 #13
0
  def terminate_instances(cls, options):
    """Stops all services running in an AppScale deployment, and in cloud
    deployments, also powers off the instances previously spawned.

    Raises:
      AppScaleException: If AppScale is not running, and thus can't be
      terminated.
    """
    try:
      infrastructure = LocalState.get_infrastructure(options.keyname)
    except IOError:
      raise AppScaleException("Cannot find AppScale's configuration for keyname {0}".
        format(options.keyname))

    if infrastructure == "xen" and options.terminate:
      raise AppScaleException("Terminate option is invalid for cluster mode.")

    if infrastructure == "xen" or not options.terminate:
      # We are in cluster mode: let's check if AppScale is running.
      if not os.path.exists(LocalState.get_secret_key_location(options.keyname)):
        raise AppScaleException("AppScale is not running with the keyname {0}".
          format(options.keyname))

    # Stop gracefully the AppScale deployment.
    try:
      RemoteHelper.terminate_virtualized_cluster(options.keyname,
                                                 options.clean)
    except (IOError, AppScaleException, AppControllerException,
            BadConfigurationException) as e:
      if not (infrastructure in InfrastructureAgentFactory.VALID_AGENTS and
            options.terminate):
        raise

      if options.test:
        AppScaleLogger.warn(e)
      else:
        AppScaleLogger.verbose(e)
        if isinstance(e, AppControllerException):
          response = raw_input(
            'AppScale may not have shut down properly, are you sure you want '
            'to continue terminating? (y/N) ')
        else:
          response = raw_input(
            'AppScale could not find the configuration files for this '
            'deployment, are you sure you want to continue terminating? '
            '(y/N) ')
        if response.lower() not in ['y', 'yes']:
          raise AppScaleException("Cancelled cloud termination.")


    # And if we are on a cloud infrastructure, terminate instances if
    # asked.
    if (infrastructure in InfrastructureAgentFactory.VALID_AGENTS and
          options.terminate):
      RemoteHelper.terminate_cloud_infrastructure(options.keyname)
    elif infrastructure in InfrastructureAgentFactory.VALID_AGENTS and not \
        options.terminate:
      AppScaleLogger.log("AppScale did not terminate any of your cloud "
                         "instances, to terminate them run 'appscale "
                         "down --terminate'")
    if options.clean:
      LocalState.clean_local_metadata(keyname=options.keyname)
コード例 #14
0
  def run_instances(cls, options):
    """Starts a new AppScale deployment with the parameters given.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    Raises:
      AppControllerException: If the AppController on the head node crashes.
        When this occurs, the message in the exception contains the reason why
        the AppController crashed.
      BadConfigurationException: If the user passes in options that are not
        sufficient to start an AppScale deployment (e.g., running on EC2 but
        not specifying the AMI to use), or if the user provides us
        contradictory options (e.g., running on EC2 but not specifying EC2
        credentials).
    """
    LocalState.make_appscale_directory()
    LocalState.ensure_appscale_isnt_running(options.keyname, options.force)
    node_layout = NodeLayout(options)

    if options.infrastructure:
      if (not options.test and not options.force and
          not (options.disks or node_layout.are_disks_used())):
        LocalState.ensure_user_wants_to_run_without_disks()

    reduced_version = '.'.join(x for x in APPSCALE_VERSION.split('.')[:2])
    AppScaleLogger.log("Starting AppScale " + reduced_version)

    my_id = str(uuid.uuid4())
    AppScaleLogger.remote_log_tools_state(options, my_id, "started",
      APPSCALE_VERSION)

    head_node = node_layout.head_node()
    # Start VMs in cloud via cloud agent.
    if options.infrastructure:
      node_layout = RemoteHelper.start_all_nodes(options, node_layout)

      # Enables root logins and SSH access on the head node.
      RemoteHelper.enable_root_ssh(options, head_node.public_ip)
    AppScaleLogger.verbose("Node Layout: {}".format(node_layout.to_list()))

    # Ensure all nodes are compatible.
    RemoteHelper.ensure_machine_is_compatible(
      head_node.public_ip, options.keyname)

    # Use rsync to move custom code into the deployment.
    if options.rsync_source:
      AppScaleLogger.log("Copying over local copy of AppScale from {0}".
        format(options.rsync_source))
      RemoteHelper.rsync_files(head_node.public_ip, options.keyname,
                               options.rsync_source)

    # Start services on head node.
    RemoteHelper.start_head_node(options, my_id, node_layout)

    # Write deployment metadata to disk (facilitates SSH operations, etc.)
    db_master = node_layout.db_master().private_ip
    head_node = node_layout.head_node().public_ip
    LocalState.update_local_metadata(options, db_master, head_node)

    # Copy the locations.json to the head node
    RemoteHelper.copy_local_metadata(node_layout.head_node().public_ip,
                                     options.keyname)

    # Wait for services on head node to start.
    secret_key = LocalState.get_secret_key(options.keyname)
    acc = AppControllerClient(head_node, secret_key)
    try:
      while not acc.is_initialized():
        AppScaleLogger.log('Waiting for head node to initialize...')
        # This can take some time in particular the first time around, since
        # we will have to initialize the database.
        time.sleep(cls.SLEEP_TIME*3)
    except socket.error as socket_error:
      AppScaleLogger.warn('Unable to initialize AppController: {}'.
                          format(socket_error.message))
      message = RemoteHelper.collect_appcontroller_crashlog(
        head_node, options.keyname)
      raise AppControllerException(message)

    # Set up admin account.
    try:
      # We don't need to have any exception information here: we do expect
      # some anyway while the UserAppServer is coming up.
      acc.does_user_exist("non-existent-user", True)
    except Exception:
      AppScaleLogger.log('UserAppServer not ready yet. Retrying ...')
      time.sleep(cls.SLEEP_TIME)

    if options.admin_user and options.admin_pass:
      AppScaleLogger.log("Using the provided admin username/password")
      username, password = options.admin_user, options.admin_pass
    elif options.test:
      AppScaleLogger.log("Using default admin username/password")
      username, password = LocalState.DEFAULT_USER, LocalState.DEFAULT_PASSWORD
    else:
      username, password = LocalState.get_credentials()

    RemoteHelper.create_user_accounts(username, password, head_node,
                                      options.keyname)
    acc.set_admin_role(username, 'true', cls.ADMIN_CAPABILITIES)

    # Wait for machines to finish loading and AppScale Dashboard to be deployed.
    RemoteHelper.wait_for_machines_to_finish_loading(head_node, options.keyname)

    try:
      login_host = acc.get_property('login')['login']
    except KeyError:
      raise AppControllerException('login property not found')

    RemoteHelper.sleep_until_port_is_open(
      login_host, RemoteHelper.APP_DASHBOARD_PORT)

    AppScaleLogger.success("AppScale successfully started!")
    AppScaleLogger.success(
      'View status information about your AppScale deployment at '
      'http://{}:{}'.format(login_host, RemoteHelper.APP_DASHBOARD_PORT))
    AppScaleLogger.remote_log_tools_state(options, my_id,
      "finished", APPSCALE_VERSION)
コード例 #15
0
  def gather_logs(cls, options):
    """Collects logs from each machine in the currently running AppScale
    deployment.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    """
    location = os.path.abspath(options.location)
    # First, make sure that the place we want to store logs doesn't
    # already exist.
    if os.path.exists(location):
      raise AppScaleException("Can't gather logs, as the location you " + \
        "specified, {}, already exists.".format(location))

    load_balancer_ip = LocalState.get_host_with_role(
      options.keyname, 'load_balancer')
    secret = LocalState.get_secret_key(options.keyname)
    acc = AppControllerClient(load_balancer_ip, secret)

    try:
      all_ips = acc.get_all_public_ips()
    except socket.error:  # Occurs when the AppController has failed.
      AppScaleLogger.warn("Couldn't get an up-to-date listing of the " + \
        "machines in this AppScale deployment. Using our locally cached " + \
        "info instead.")
      all_ips = LocalState.get_all_public_ips(options.keyname)

    # Get information about roles and public IPs
    # for creating navigation symlinks in gathered logs
    try:
      nodes_info = acc.get_role_info()
    except socket.error:  # Occurs when the AppController has failed.
      AppScaleLogger.warn("Couldn't get an up-to-date nodes info. "
                          "Using our locally cached info instead.")
      nodes_info = LocalState.get_local_nodes_info(options.keyname)
    nodes_dict = {node['public_ip']: node for node in nodes_info}

    # do the mkdir after we get the secret key, so that a bad keyname will
    # cause the tool to crash and not create this directory
    os.mkdir(location)

    # make dir for private IP navigation links
    private_ips_dir = os.path.join(location, 'symlinks', 'private-ips')
    utils.mkdir(private_ips_dir)

    # The log paths that we collect logs from.
    log_paths = [
      {'remote': '/opt/cassandra/cassandra/logs/*', 'local': 'cassandra'},
      {'remote': '/var/log/appscale'},
      {'remote': '/var/log/haproxy.log*'},
      {'remote': '/var/log/kern.log*'},
      {'remote': '/var/log/nginx'},
      {'remote': '/var/log/rabbitmq/*', 'local': 'rabbitmq'},
      {'remote': '/var/log/syslog*'},
      {'remote': '/var/log/zookeeper'}
    ]

    failures = False
    for public_ip in all_ips:
      # Get the logs from each node, and store them in our local directory
      local_dir = os.path.join(location, public_ip)
      utils.mkdir(local_dir)
      local_link = os.path.join('..', '..', public_ip)

      # Create symlinks for easier navigation in gathered logs
      node_info = nodes_dict.get(public_ip)
      if node_info:
        private_ip_dir = os.path.join(private_ips_dir, node_info["private_ip"])
        os.symlink(local_link, private_ip_dir)
        for role in node_info['roles']:
          role_dir = os.path.join(location, 'symlinks', role)
          utils.mkdir(role_dir)
          os.symlink(local_link, os.path.join(role_dir, public_ip))

      for log_path in log_paths:
        sub_dir = local_dir

        if 'local' in log_path:
          sub_dir = os.path.join(local_dir, log_path['local'])
          utils.mkdir(sub_dir)

        try:
          RemoteHelper.scp_remote_to_local(
            public_ip, options.keyname, log_path['remote'], sub_dir
          )
        except ShellException as shell_exception:
          failures = True
          AppScaleLogger.warn('Unable to collect logs from {} for host {}'.
                              format(log_path['remote'], public_ip))
          AppScaleLogger.verbose(
            'Encountered exception: {}'.format(str(shell_exception)))

    if failures:
      AppScaleLogger.log("Done copying to {}. There were failures while "
                         "collecting AppScale logs.".format(location))
    else:
      AppScaleLogger.success("Successfully collected all AppScale logs into "
                             "{}".format(location))