def create_user_accounts(cls, email, password, public_ip, keyname):
    """Registers two new user accounts with the UserAppServer.

    One account is the standard account that users log in with (via their
    e-mail address. The other is their XMPP account, so that they can log into
    any jabber-compatible service and send XMPP messages to their application
    (and receive them).

    Args:
      email: The e-mail address that should be registered for the user's
        standard account.
      password: The password that should be used for both the standard and XMPP
        accounts.
      public_ip: The location where the AppController can be found.
      keyname: The name of the SSH keypair used for this AppScale deployment.
    """
    acc = AppControllerClient(public_ip, LocalState.get_secret_key(keyname))

    is_new_user = False
    # first, create the standard account
    encrypted_pass = LocalState.encrypt_password(email, password)
    if acc.does_user_exist(email):
      AppScaleLogger.log("User {0} already exists, so not creating it again.".
        format(email))
    else:
      acc.create_user(email, encrypted_pass)
      is_new_user = True

    # next, create the XMPP account. if the user's e-mail is [email protected], then that
    # means their XMPP account name is a@login_ip
    username_regex = re.compile('\A(.*)@')
    username = username_regex.match(email).groups()[0]

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

    xmpp_user = "******".format(username, login_host)
    xmpp_pass = LocalState.encrypt_password(xmpp_user, password)

    is_xmpp_user_exist = acc.does_user_exist(xmpp_user)

    if is_xmpp_user_exist and is_new_user:
      AppScaleLogger.log("XMPP User {0} conflict!".format(xmpp_user))

      generated_xmpp_username = LocalState.generate_xmpp_username(username)
      xmpp_user = "******".format(generated_xmpp_username, login_host)
      xmpp_pass = LocalState.encrypt_password(xmpp_user, password)

      acc.create_user(xmpp_user, xmpp_pass)
    elif is_xmpp_user_exist and not is_new_user:
      AppScaleLogger.log("XMPP User {0} already exists, so not creating it again.".format(xmpp_user))
    else:
      acc.create_user(xmpp_user, xmpp_pass)
    AppScaleLogger.log("Your XMPP username is {0}".format(xmpp_user))
  def create_user_accounts(cls, email, password, public_ip, keyname,
    clear_datastore):
    """Registers two new user accounts with the UserAppServer.

    One account is the standard account that users log in with (via their
    e-mail address. The other is their XMPP account, so that they can log into
    any jabber-compatible service and send XMPP messages to their application
    (and receive them).

    Args:
      email: The e-mail address that should be registered for the user's
        standard account.
      password: The password that should be used for both the standard and XMPP
        accounts.
      public_ip: The location where the AppController can be found.
      keyname: The name of the SSH keypair used for this AppScale deployment.
      clear_datastore: A bool that indicates if we expect the datastore to be
        emptied, and thus not contain any user accounts.
    """
    acc = AppControllerClient(public_ip, LocalState.get_secret_key(keyname))

    # first, create the standard account
    encrypted_pass = LocalState.encrypt_password(email, password)
    if not clear_datastore and acc.does_user_exist(email):
      AppScaleLogger.log("User {0} already exists, so not creating it again.".
        format(email))
    else:
      acc.create_user(email, encrypted_pass)

    # next, create the XMPP account. if the user's e-mail is [email protected], then that
    # means their XMPP account name is a@login_ip
    username_regex = re.compile('\A(.*)@')
    username = username_regex.match(email).groups()[0]
    xmpp_user = "******".format(username, LocalState.get_login_host(keyname))
    xmpp_pass = LocalState.encrypt_password(xmpp_user, password)

    if not clear_datastore and acc.does_user_exist(xmpp_user):
      AppScaleLogger.log(
        "XMPP User {0} already exists, so not creating it again.".
        format(xmpp_user))
    else:
      acc.create_user(xmpp_user, xmpp_pass)
    AppScaleLogger.log("Your XMPP username is {0}".format(xmpp_user))
    def create_user_accounts(cls, email, password, public_ip, keyname):
        """Registers two new user accounts with the UserAppServer.

    One account is the standard account that users log in with (via their
    e-mail address. The other is their XMPP account, so that they can log into
    any jabber-compatible service and send XMPP messages to their application
    (and receive them).

    Args:
      email: The e-mail address that should be registered for the user's
        standard account.
      password: The password that should be used for both the standard and XMPP
        accounts.
      public_ip: The location where the AppController can be found.
      keyname: The name of the SSH keypair used for this AppScale deployment.
    """
        acc = AppControllerClient(public_ip,
                                  LocalState.get_secret_key(keyname))

        # first, create the standard account
        encrypted_pass = LocalState.encrypt_password(email, password)
        if acc.does_user_exist(email):
            AppScaleLogger.log(
                "User {0} already exists, so not creating it again.".format(
                    email))
        else:
            acc.create_user(email, encrypted_pass)

        # next, create the XMPP account. if the user's e-mail is [email protected], then that
        # means their XMPP account name is a@login_ip
        username_regex = re.compile('\A(.*)@')
        username = username_regex.match(email).groups()[0]
        xmpp_user = "******".format(username,
                                     LocalState.get_login_host(keyname))
        xmpp_pass = LocalState.encrypt_password(xmpp_user, password)

        if acc.does_user_exist(xmpp_user):
            AppScaleLogger.log(
                "XMPP User {0} already exists, so not creating it again.".
                format(xmpp_user))
        else:
            acc.create_user(xmpp_user, xmpp_pass)
        AppScaleLogger.log("Your XMPP username is {0}".format(xmpp_user))
Beispiel #4
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.
    """
        if cls.TAR_GZ_REGEX.search(options.file):
            file_location = LocalState.extract_tgz_app_to_dir(
                options.file, options.verbose)
            created_dir = True
        elif cls.ZIP_REGEX.search(options.file):
            file_location = LocalState.extract_zip_app_to_dir(
                options.file, options.verbose)
            created_dir = True
        elif os.path.isdir(options.file):
            file_location = options.file
            created_dir = False
        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))

        try:
            app_id = AppEngineHelper.get_app_id_from_app_config(file_location)
        except AppEngineConfigException as config_error:
            AppScaleLogger.log(config_error)
            if 'yaml' in str(config_error):
                raise config_error

            # Java App Engine users may have specified their war directory. In that
            # case, just move up one level, back to the app's directory.
            file_location = file_location + os.sep + ".."
            app_id = AppEngineHelper.get_app_id_from_app_config(file_location)

        app_language = AppEngineHelper.get_app_runtime_from_app_config(
            file_location)
        AppEngineHelper.validate_app_id(app_id)

        if app_language == 'java':
            if 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 ' + AppEngineHelper.SUPPORTED_SDK_VERSION +
                    '.')

        login_host = LocalState.get_login_host(options.keyname)
        secret_key = LocalState.get_secret_key(options.keyname)
        acc = AppControllerClient(login_host, secret_key)

        if options.test:
            username = LocalState.DEFAULT_USER
        elif options.email:
            username = options.email
        else:
            username = LocalState.get_username_from_stdin(is_admin=False)

        if not acc.does_user_exist(username):
            password = LocalState.get_password_from_stdin()
            RemoteHelper.create_user_accounts(username,
                                              password,
                                              login_host,
                                              options.keyname,
                                              clear_datastore=False)

        app_exists = acc.does_app_exist(app_id)
        app_admin = acc.get_app_admin(app_id)
        if app_admin is not None and username != app_admin:
            raise AppScaleException("The given user doesn't own this application" + \
              ", so they can't upload an app with that application ID. Please " + \
              "change the application ID and try again.")

        if app_exists:
            AppScaleLogger.log(
                "Uploading new version of app {0}".format(app_id))
        else:
            AppScaleLogger.log(
                "Uploading initial version of app {0}".format(app_id))
            acc.reserve_app_id(username, app_id, app_language)

        # Ignore all .pyc files while tarring.
        if app_language == 'python27':
            AppScaleLogger.log("Ignoring .pyc files")

        remote_file_path = RemoteHelper.copy_app_to_host(
            file_location, options.keyname, options.verbose)

        acc.done_uploading(app_id, remote_file_path)
        acc.update([app_id])

        # 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.")

        if app_exists:
            time.sleep(20)  # give the AppController time to restart the app

        # Makes a call to the AppController to get all the stats and looks
        # through them for the http port the app can be reached on.
        sleep_time = 2 * cls.SLEEP_TIME
        current_app = None
        for i in range(cls.MAX_RETRIES):
            try:
                result = acc.get_all_stats()
                json_result = json.loads(result)
                apps_result = json_result['apps']
                current_app = apps_result[app_id]
                http_port = current_app['http']
                break
            except ValueError:
                pass
            except KeyError:
                pass
            AppScaleLogger.verbose("Waiting {0} second(s) for a port to be assigned to {1}".\
              format(sleep_time, app_id), options.verbose)
            time.sleep(sleep_time)
        if not current_app:
            raise AppScaleException(
                "Unable to get the serving port for the application.")

        RemoteHelper.sleep_until_port_is_open(login_host, http_port,
                                              options.verbose)
        AppScaleLogger.success(
            "Your app can be reached at the following URL: " +
            "http://{0}:{1}".format(login_host, http_port))

        if created_dir:
            shutil.rmtree(file_location)

        return (login_host, http_port)
Beispiel #5
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)

        if options.infrastructure:
            if not options.disks and not options.test and not options.force:
                LocalState.ensure_user_wants_to_run_without_disks()
            AppScaleLogger.log("Starting AppScale " + APPSCALE_VERSION +
                               " over the " + options.infrastructure +
                               " cloud.")
        else:
            AppScaleLogger.log("Starting AppScale " + APPSCALE_VERSION +
                               " over a virtualized cluster.")
        my_id = str(uuid.uuid4())
        AppScaleLogger.remote_log_tools_state(options, my_id, "started",
                                              APPSCALE_VERSION)

        node_layout = NodeLayout(options)
        if not node_layout.is_valid():
            raise BadConfigurationException("There were errors with your " + \
              "placement strategy:\n{0}".format(str(node_layout.errors())))

        public_ip, instance_id = RemoteHelper.start_head_node(
            options, my_id, node_layout)
        AppScaleLogger.log(
            "\nPlease wait for AppScale to prepare your machines " +
            "for use. This can take few minutes.")

        # Write our metadata as soon as possible to let users SSH into those
        # machines via 'appscale ssh'.
        LocalState.update_local_metadata(options, node_layout, public_ip,
                                         instance_id)
        RemoteHelper.copy_local_metadata(public_ip, options.keyname,
                                         options.verbose)

        acc = AppControllerClient(public_ip,
                                  LocalState.get_secret_key(options.keyname))

        # Let's now wait till the server is initialized.
        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)

        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 as exception:
            AppScaleLogger.log('UserAppServer not ready yet. Retrying ...')
            time.sleep(cls.SLEEP_TIME)

        # Update our metadata again so that users can SSH into other boxes that
        # may have been started.
        LocalState.update_local_metadata(options, node_layout, public_ip,
                                         instance_id)
        RemoteHelper.copy_local_metadata(public_ip, options.keyname,
                                         options.verbose)

        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, public_ip,
                                          options.keyname,
                                          options.clear_datastore)
        acc.set_admin_role(username, 'true', cls.ADMIN_CAPABILITIES)

        RemoteHelper.wait_for_machines_to_finish_loading(
            public_ip, options.keyname)
        # Finally, update our metadata once we know that all of the machines are
        # up and have started all their API services.
        LocalState.update_local_metadata(options, node_layout, public_ip,
                                         instance_id)
        RemoteHelper.copy_local_metadata(public_ip, options.keyname,
                                         options.verbose)

        RemoteHelper.sleep_until_port_is_open(
            LocalState.get_login_host(options.keyname),
            RemoteHelper.APP_DASHBOARD_PORT, options.verbose)
        AppScaleLogger.success("AppScale successfully started!")
        AppScaleLogger.success("View status information about your AppScale " + \
          "deployment at http://{0}:{1}/status".format(LocalState.get_login_host(
          options.keyname), RemoteHelper.APP_DASHBOARD_PORT))
        AppScaleLogger.remote_log_tools_state(options, my_id, "finished",
                                              APPSCALE_VERSION)
  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)
    if options.infrastructure:
      if not options.disks and not options.test and not options.force:
        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)

    node_layout = NodeLayout(options)
    if not node_layout.is_valid():
      raise BadConfigurationException("There were errors with your " + \
                                      "placement strategy:\n{0}".format(str(node_layout.errors())))

    head_node = node_layout.head_node()
    # Start VMs in cloud via cloud agent.
    if options.infrastructure:
      instance_ids, public_ips, private_ips = RemoteHelper.start_all_nodes(
        options, len(node_layout.nodes))
      AppScaleLogger.log("\nPlease wait for AppScale to prepare your machines "
                         "for use. This can take few minutes.")

      # Set newly obtained node layout info for this deployment.
      for i, _ in enumerate(instance_ids):
        node_layout.nodes[i].public_ip = public_ips[i]
        node_layout.nodes[i].private_ip = private_ips[i]
        node_layout.nodes[i].instance_id = instance_ids[i]

      # 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()),
                           options.verbose)

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

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

    # 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, options.verbose)

    # 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, options.verbose)
      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)
    RemoteHelper.sleep_until_port_is_open(LocalState.get_login_host(
      options.keyname), RemoteHelper.APP_DASHBOARD_PORT, options.verbose)

    AppScaleLogger.success("AppScale successfully started!")
    AppScaleLogger.success("View status information about your AppScale " + \
                           "deployment at http://{0}:{1}".format(LocalState.get_login_host(
                           options.keyname), RemoteHelper.APP_DASHBOARD_PORT))
    AppScaleLogger.remote_log_tools_state(options, my_id,
      "finished", APPSCALE_VERSION)
  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.
    """
    if cls.TAR_GZ_REGEX.search(options.file):
      file_location = LocalState.extract_tgz_app_to_dir(options.file,
        options.verbose)
      created_dir = True
    elif cls.ZIP_REGEX.search(options.file):
      file_location = LocalState.extract_zip_app_to_dir(options.file,
        options.verbose)
      created_dir = True
    elif os.path.isdir(options.file):
      file_location = options.file
      created_dir = False
    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))

    try:
      app_id = AppEngineHelper.get_app_id_from_app_config(file_location)
    except AppEngineConfigException as config_error:
      AppScaleLogger.log(config_error)
      if 'yaml' in str(config_error):
        raise config_error

      # Java App Engine users may have specified their war directory. In that
      # case, just move up one level, back to the app's directory.
      file_location = file_location + os.sep + ".."
      app_id = AppEngineHelper.get_app_id_from_app_config(file_location)

    app_language = AppEngineHelper.get_app_runtime_from_app_config(
      file_location)
    AppEngineHelper.validate_app_id(app_id)

    if app_language == 'java':
      if 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 ' + AppEngineHelper.SUPPORTED_SDK_VERSION + '.')

    login_host = LocalState.get_login_host(options.keyname)
    secret_key = LocalState.get_secret_key(options.keyname)
    acc = AppControllerClient(login_host, secret_key)

    if options.test:
      username = LocalState.DEFAULT_USER
    elif options.email:
      username = options.email
    else:
      username = LocalState.get_username_from_stdin(is_admin=False)

    if not acc.does_user_exist(username):
      password = LocalState.get_password_from_stdin()
      RemoteHelper.create_user_accounts(username, password,
        login_host, options.keyname, clear_datastore=False)

    app_exists = acc.does_app_exist(app_id)
    app_admin = acc.get_app_admin(app_id)
    if app_admin is not None and username != app_admin:
      raise AppScaleException("The given user doesn't own this application" + \
        ", so they can't upload an app with that application ID. Please " + \
        "change the application ID and try again.")

    if app_exists:
      AppScaleLogger.log("Uploading new version of app {0}".format(app_id))
    else:
      AppScaleLogger.log("Uploading initial version of app {0}".format(app_id))
      acc.reserve_app_id(username, app_id, app_language)

    # Ignore all .pyc files while tarring.
    if app_language == 'python27':
      AppScaleLogger.log("Ignoring .pyc files")

    remote_file_path = RemoteHelper.copy_app_to_host(file_location,
      options.keyname, options.verbose)

    acc.done_uploading(app_id, remote_file_path)
    acc.update([app_id])

    # 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.")

    if app_exists:
      time.sleep(20)  # give the AppController time to restart the app

    # Makes a call to the AppController to get all the stats and looks
    # through them for the http port the app can be reached on.
    sleep_time = 2 * cls.SLEEP_TIME
    current_app = None
    for i in range(cls.MAX_RETRIES):
      try:
        result = acc.get_all_stats()
        json_result = json.loads(result)
        apps_result = json_result['apps']
        current_app = apps_result[app_id]
        http_port = current_app['http']
        break
      except ValueError:
        pass
      except KeyError:
        pass
      AppScaleLogger.verbose("Waiting {0} second(s) for a port to be assigned to {1}".\
        format(sleep_time, app_id), options.verbose)
      time.sleep(sleep_time)
    if not current_app:
      raise AppScaleException("Unable to get the serving port for the application.")

    RemoteHelper.sleep_until_port_is_open(login_host, http_port, options.verbose)
    AppScaleLogger.success("Your app can be reached at the following URL: " +
      "http://{0}:{1}".format(login_host, http_port))

    if created_dir:
      shutil.rmtree(file_location)

    return (login_host, http_port)
  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)

    if options.infrastructure:
      if not options.disks and not options.test and not options.force:
        LocalState.ensure_user_wants_to_run_without_disks()
      AppScaleLogger.log("Starting AppScale " + APPSCALE_VERSION +
        " over the " + options.infrastructure + " cloud.")
    else:
      AppScaleLogger.log("Starting AppScale " + APPSCALE_VERSION +
        " over a virtualized cluster.")
    my_id = str(uuid.uuid4())
    AppScaleLogger.remote_log_tools_state(options, my_id, "started",
      APPSCALE_VERSION)

    node_layout = NodeLayout(options)
    if not node_layout.is_valid():
      raise BadConfigurationException("There were errors with your " + \
        "placement strategy:\n{0}".format(str(node_layout.errors())))

    public_ip, instance_id = RemoteHelper.start_head_node(options, my_id,
      node_layout)
    AppScaleLogger.log("\nPlease wait for AppScale to prepare your machines " +
      "for use. This can take few minutes.")

    # Write our metadata as soon as possible to let users SSH into those
    # machines via 'appscale ssh'.
    LocalState.update_local_metadata(options, node_layout, public_ip,
      instance_id)
    RemoteHelper.copy_local_metadata(public_ip, options.keyname,
      options.verbose)

    acc = AppControllerClient(public_ip, LocalState.get_secret_key(
      options.keyname))

    # Let's now wait till the server is initialized.
    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)

    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 as exception:
      AppScaleLogger.log('UserAppServer not ready yet. Retrying ...')
      time.sleep(cls.SLEEP_TIME)

    # Update our metadata again so that users can SSH into other boxes that
    # may have been started.
    LocalState.update_local_metadata(options, node_layout, public_ip,
      instance_id)
    RemoteHelper.copy_local_metadata(public_ip, options.keyname,
      options.verbose)

    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, public_ip,
      options.keyname, options.clear_datastore)
    acc.set_admin_role(username, 'true', cls.ADMIN_CAPABILITIES)

    RemoteHelper.wait_for_machines_to_finish_loading(public_ip, options.keyname)
    # Finally, update our metadata once we know that all of the machines are
    # up and have started all their API services.
    LocalState.update_local_metadata(options, node_layout, public_ip,
      instance_id)
    RemoteHelper.copy_local_metadata(public_ip, options.keyname,
      options.verbose)

    RemoteHelper.sleep_until_port_is_open(LocalState.get_login_host(
      options.keyname), RemoteHelper.APP_DASHBOARD_PORT, options.verbose)
    AppScaleLogger.success("AppScale successfully started!")
    AppScaleLogger.success("View status information about your AppScale " + \
      "deployment at http://{0}:{1}/status".format(LocalState.get_login_host(
      options.keyname), RemoteHelper.APP_DASHBOARD_PORT))
    AppScaleLogger.remote_log_tools_state(options, my_id,
      "finished", APPSCALE_VERSION)
    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)
        if options.infrastructure:
            if not options.disks and not options.test and not options.force:
                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)

        node_layout = NodeLayout(options)
        if not node_layout.is_valid():
            raise BadConfigurationException(
                "There were errors with your " + "placement strategy:\n{0}".format(str(node_layout.errors()))
            )

        head_node = node_layout.head_node()
        # Start VMs in cloud via cloud agent.
        if options.infrastructure:
            instance_ids, public_ips, private_ips = RemoteHelper.start_all_nodes(options, len(node_layout.nodes))
            AppScaleLogger.log(
                "\nPlease wait for AppScale to prepare your machines " "for use. This can take few minutes."
            )

            # Set newly obtained node layout info for this deployment.
            for i, _ in enumerate(instance_ids):
                node_layout.nodes[i].public_ip = public_ips[i]
                node_layout.nodes[i].private_ip = private_ips[i]
                node_layout.nodes[i].instance_id = instance_ids[i]

            # 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()), options.verbose)

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

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

        # 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, options.verbose)

        # 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, options.verbose)
            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)
        RemoteHelper.sleep_until_port_is_open(
            LocalState.get_login_host(options.keyname), RemoteHelper.APP_DASHBOARD_PORT, options.verbose
        )

        AppScaleLogger.success("AppScale successfully started!")
        AppScaleLogger.success(
            "View status information about your AppScale "
            + "deployment at http://{0}:{1}".format(
                LocalState.get_login_host(options.keyname), RemoteHelper.APP_DASHBOARD_PORT
            )
        )
        AppScaleLogger.remote_log_tools_state(options, my_id, "finished", APPSCALE_VERSION)