예제 #1
0
    def validate_machine_image(self):
        """Checks with the given cloud (if running in a cloud) to ensure that the
    user-specified ami/emi exists, aborting if it does not.

    Raises:
      BadConfigurationException: If the given machine image does not exist.
    """
        if not self.args.infrastructure:
            return

        cloud_agent = InfrastructureAgentFactory.create_agent(
            self.args.infrastructure)
        params = cloud_agent.get_params_from_args(self.args)

        if not cloud_agent.does_image_exist(params):
            raise BadConfigurationException(
                "Couldn't find the given machine image.")

        if not cloud_agent.does_zone_exist(params):
            raise BadConfigurationException("Couldn't find the given zone.")

        # Make sure that if the user gives us an Elastic IP / static IP, that they
        # actually own it.
        if self.args.static_ip:
            if not cloud_agent.does_address_exist(params):
                raise BadConfigurationException(
                    "Couldn't find the given static IP.")

        if not self.args.disks:
            return

        for disk in set(self.args.disks.values()):
            if not cloud_agent.does_disk_exist(params, disk):
                raise BadConfigurationException(
                    "Couldn't find disk {0}".format(disk))
예제 #2
0
    def validate_num_of_vms_flags(self):
        """Validates the values given to us by the user relating to the
    number of virtual machines we spawn in a cloud deployment.

    Raises:
      BadConfigurationException: If the values for the min or max
        flags are invalid.
    """
        if self.args.ips:
            return

        # if min is not set and max is, set min == max
        if self.args.min is None and self.args.max:
            self.args.min = self.args.max

        if self.args.ips:
            if not os.path.exists(self.args.ips):
                raise BadConfigurationException(
                    "The given ips.yaml file didn't exist.")
        elif self.args.ips_layout:
            self.args.ips = yaml.safe_load(
                base64.b64decode(self.args.ips_layout))
        else:
            if self.args.min < 1:
                raise BadConfigurationException("Min cannot be less than 1.")

            if self.args.max < 1:
                raise BadConfigurationException("Max cannot be less than 1.")

            if self.args.min > self.args.max:
                raise BadConfigurationException("Min cannot exceed max.")
예제 #3
0
    def validate_developer_flags(self):
        """Validates the flags that correspond to flags typically used only by
    AppScale developers, such as automatically setting administrator e-mails
    and passwords.

    Raises:
      BadConfigurationException: If admin_user, admin_pass, and test are all
        set, or if admin_user (or admin_pass) is set but the other isn't. This
        exception can also be thrown if user_commands is not a list.
    """
        if self.args.user_commands:
            self.args.user_commands = yaml.safe_load(
                base64.b64decode(self.args.user_commands))
            if not isinstance(self.args.user_commands, list):
                raise BadConfigurationException(
                    "user_commands must be a list. " +
                    "Please make it a list and try again.")
        else:
            self.args.user_commands = []

        if self.args.admin_user and not self.args.admin_pass:
            raise BadConfigurationException("If admin_user is set, admin_pass " + \
              "must also be set.")
        if self.args.admin_pass and not self.args.admin_user:
            raise BadConfigurationException("If admin_pass is set, admin_user " + \
              "must also be set.")
        if self.args.admin_user and self.args.admin_pass and self.args.test:
            raise BadConfigurationException("Cannot set admin_user, " + \
              "admin_pass, and test.")
예제 #4
0
    def add_instances(cls, options):
        """Adds additional machines to an AppScale deployment.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    """
        if 'master' in options.ips.keys():
            raise BadConfigurationException("Cannot add master nodes to an " + \
              "already running AppScale deployment.")

        # Skip checking for -n (replication) because we don't allow the user
        # to specify it here (only allowed in run-instances).
        additional_nodes_layout = NodeLayout(options)

        # In virtualized cluster deployments, we need to make sure that the user
        # has already set up SSH keys.
        if LocalState.get_from_yaml(options.keyname,
                                    'infrastructure') == "xen":
            for ip in options.ips.values():
                # throws a ShellException if the SSH key doesn't work
                RemoteHelper.ssh(ip, options.keyname, "ls", options.verbose)

        # Finally, find an AppController and send it a message to add
        # the given nodes with the new roles.
        AppScaleLogger.log("Sending request to add instances")
        login_ip = LocalState.get_login_host(options.keyname)
        acc = AppControllerClient(login_ip,
                                  LocalState.get_secret_key(options.keyname))
        acc.start_roles_on_nodes(json.dumps(options.ips))

        # TODO(cgb): Should we wait for the new instances to come up and get
        # initialized?
        AppScaleLogger.success("Successfully sent request to add instances " + \
          "to this AppScale deployment.")
예제 #5
0
    def rsync_files(cls, host, keyname, local_appscale_dir, is_verbose):
        """Copies over an AppScale source directory from this machine to the
    specified host.

    Args:
      host: A str representing a host that should be accessible from this
        machine.
      keyname: A str representing the name of the SSH keypair that can log into
        the specified machine.
      local_appscale_dir: A str representing the path on the local filesystem
        where the AppScale source to copy over can be found.
      is_verbose: A bool that indicates if we should print the rsync commands
        we exec to stdout.
    Raises:
      BadConfigurationException: If local_appscale_dir does not exist locally,
        or if any of the standard AppScale module folders do not exist.
    """
        ssh_key = LocalState.get_key_path_from_name(keyname)
        local_path = os.path.expanduser(local_appscale_dir)
        if not os.path.exists(local_path):
            raise BadConfigurationException("The location you specified to copy " \
              "from, {0}, doesn't exist.".format(local_path))
        LocalState.shell("rsync -e 'ssh -i {0} {1}' -arv "
          "--exclude='AppDB/logs/*' " \
          "--exclude='AppDB/cassandra/cassandra/*' " \
          "{2}/* root@{3}:/root/appscale/".format(ssh_key, cls.SSH_OPTIONS,
          local_path, host), is_verbose)
예제 #6
0
  def ensure_appscale_isnt_running(cls, keyname, force):
    """Checks the secret key file to see if AppScale is running, and
    aborts if it is.

    Args:
      keyname: The keypair name that is used to identify AppScale deployments.
      force: A bool that is used to run AppScale even if the secret key file
        is present.
    Raises:
      BadConfigurationException: If AppScale is already running.
    """
    if force:
      return

    if os.path.exists(cls.get_secret_key_location(keyname)):
      try:
        login_host = cls.get_login_host(keyname)
        secret_key = cls.get_secret_key(keyname)
      except (IOError, AppScaleException, BadConfigurationException):
        # If we don't have the locations files, we are not running.
        return

      acc = AppControllerClient(login_host, secret_key)
      try:
        acc.get_all_public_ips()
      except AppControllerException:
        # AC is not running, so we assume appscale is not up and running.
        AppScaleLogger.log("AppController not running on login node.")
      else:
        raise BadConfigurationException("AppScale is already running. Terminate" +
          " it, set 'force: True' in your AppScalefile, or use the --force flag" +
          " to run anyways.")
예제 #7
0
    def invalid(self, message):
        """ Wrapper that NodeLayout validation aspects call when the given layout
      is invalid.

    Raises: BadConfigurationException with the given message.
    """
        raise BadConfigurationException(message)
예제 #8
0
  def get_local_nodes_info(cls, keyname):
    """Reads the JSON-encoded metadata on disk and returns a list using the
    key 'node_info' that indicates which machines run each API service in
    this AppScale deployment.

    Args:
      keyname: A str that represents an SSH keypair name, uniquely identifying
        this AppScale deployment.
    Returns:
      A list of dicts, where each dict contains information on a single machine
      in this AppScale deployment.
    Raises:
      BadConfigurationException: If there is no JSON-encoded metadata file
        named after the given keyname.
    """
    try:
      with open(cls.get_locations_json_location(keyname), 'r') as file_handle:
        file_contents = json.loads(file_handle.read())
        if isinstance(file_contents, list):
          cls.upgrade_json_file(keyname)
          file_handle.seek(0)
          file_contents = json.loads(file_handle.read())
        return file_contents.get('node_info', [])
    except IOError:
      raise BadConfigurationException("Couldn't read from locations file, "
                                      "AppScale may not be running with "
                                      "keyname {0}".format(keyname))
예제 #9
0
  def clean_local_metadata(cls, keyname):
    """Takes the existing JSON-encoded metadata on disk and assigns all nodes
    besides load_balancers (because of public ips) to "open".

    Args:
      keyname: A str that represents an SSH keypair name, uniquely identifying
        this AppScale deployment.
    Raises:
      BadConfigurationException: If there is no JSON-encoded metadata file
        named after the given keyname.
    """
    try:
      with open(cls.get_locations_json_location(keyname), 'r+') as file_handle:
        file_contents = yaml.safe_load(file_handle.read())
        # Compatibility support for previous versions of locations file.
        if isinstance(file_contents, list):
          cls.upgrade_json_file(keyname)
          file_handle.seek(0)
          file_contents = json.loads(file_handle.read())
        cleaned_nodes = []
        for node in file_contents.get('node_info'):
          if 'load_balancer' not in node.get('jobs'):
            node['jobs'] = ['open']
          cleaned_nodes.append(node)
        file_contents['node_info'] = cleaned_nodes
        # Now we write the JSON file after our changes.
        file_handle.seek(0)
        file_handle.truncate()
        file_handle.write(json.dumps(file_contents))
    except IOError:
      raise BadConfigurationException("Couldn't read from locations file.")
예제 #10
0
    def init(self, environment):
        """ Writes an AppScalefile in the local directory, that contains common
    configuration parameters.

    Args:
      environment: A str that indicates whether the AppScalefile to write should
      be tailed to a 'cloud' environment or a 'cluster' environment.

    Raises:
      AppScalefileException: If there already is an AppScalefile in the local
      directory.
    """
        # first, make sure there isn't already an AppScalefile in this
        # directory
        appscalefile_location = self.get_appscalefile_location()
        if os.path.exists(appscalefile_location):
            raise AppScalefileException(
                "There is already an AppScalefile" +
                " in this directory. Please remove it and run 'appscale init'"
                + " again to generate a new AppScalefile.")

        # next, see if we're making a cloud template file or a cluster
        # template file
        if environment == 'cloud':
            template_file = self.TEMPLATE_CLOUD_APPSCALEFILE
        elif environment == 'cluster':
            template_file = self.TEMPLATE_CLUSTER_APPSCALEFILE
        else:
            raise BadConfigurationException(
                "The environment you specified " +
                "was invalid. Valid environments are 'cloud' and " +
                "'cluster'.")

        # finally, copy the template AppScalefile there
        shutil.copy(template_file, appscalefile_location)
예제 #11
0
  def valid_ssh_key(self, config):
    """ Determines whether or not we should call appscale-add-keypair, by
    collecting all the IP addresses in the given IPs layout and attempting to
    SSH to each of them with the specified keyname.

    Args:
      config: A dictionary that includes the IPs layout (which itself is a dict
        mapping role names to IPs) and, optionally, the keyname to use.

    Returns:
      A bool indicating whether or not the specified keyname can be used to log
      into each IP address without a password.

    Raises:
      BadConfigurationException: If the IPs layout was not a dictionary.
    """
    keyname = config["keyname"]
    verbose = config.get('verbose', False)

    if not isinstance(config["ips_layout"], dict):
      raise BadConfigurationException("ips_layout should be a dictionary. " \
        "Please fix it and try again.")

    ssh_key_location = self.APPSCALE_DIRECTORY + keyname + ".key"
    if not os.path.exists(ssh_key_location):
      return False

    all_ips = self.get_all_ips(config["ips_layout"])
    for ip in all_ips:
      if not self.can_ssh_to_ip(ip, keyname, verbose):
        return False

    return True
예제 #12
0
    def valid_ssh_key(self, config, run_instances_opts):
        """ Checks if the tools can log into the head node with the current key.

    Args:
      config: A dictionary that includes the IPs layout (which itself is a dict
        mapping role names to IPs) and, optionally, the keyname to use.
      run_instances_opts: The arguments parsed from the appscale-run-instances
        command.

    Returns:
      A bool indicating whether or not the specified keyname can be used to log
      into the head node.

    Raises:
      BadConfigurationException: If the IPs layout was not a dictionary.
    """
        keyname = config['keyname']
        verbose = config.get('verbose', False)

        if not isinstance(config['ips_layout'], dict):
            raise BadConfigurationException(
                'ips_layout should be a dictionary. Please fix it and try again.'
            )

        ssh_key_location = self.APPSCALE_DIRECTORY + keyname + ".key"
        if not os.path.exists(ssh_key_location):
            return False

        all_ips = LocalState.get_all_public_ips(keyname)

        # If a login node is defined, use that to communicate with other nodes.
        node_layout = NodeLayout(run_instances_opts)
        head_node = node_layout.head_node()
        if head_node is not None:
            remote_key = '{}/ssh.key'.format(RemoteHelper.CONFIG_DIR)
            try:
                RemoteHelper.scp(head_node.public_ip, keyname,
                                 ssh_key_location, remote_key, verbose)
            except ShellException:
                return False

            for ip in all_ips:
                ssh_to_ip = 'ssh -i {key} -o StrictHostkeyChecking=no root@{ip} true'\
                  .format(key=remote_key, ip=ip)
                try:
                    RemoteHelper.ssh(head_node.public_ip,
                                     keyname,
                                     ssh_to_ip,
                                     verbose,
                                     user='******')
                except ShellException:
                    return False
            return True

        for ip in all_ips:
            if not self.can_ssh_to_ip(ip, keyname, verbose):
                return False

        return True
예제 #13
0
  def validate_admin_flags(self):
    """Validates the flags that correspond to setting administrator e-mails
    and passwords.

    Raises:
      BadConfigurationException: If admin_user, admin_pass, and test are all
        set, or if admin_user (or admin_pass) is set but the other isn't.
    """
    if self.args.admin_user and not self.args.admin_pass:
      raise BadConfigurationException("If admin_user is set, admin_pass " + \
        "must also be set.")
    if self.args.admin_pass and not self.args.admin_user:
      raise BadConfigurationException("If admin_pass is set, admin_user " + \
        "must also be set.")
    if self.args.admin_user and self.args.admin_pass and self.args.test:
      raise BadConfigurationException("Cannot set admin_user, " + \
        "admin_pass, and test.")
예제 #14
0
    def add_keypair(cls, options):
        """Sets up passwordless SSH login to the machines used in a virtualized
    cluster deployment.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    """
        LocalState.require_ssh_commands(options.auto, options.verbose)
        LocalState.make_appscale_directory()

        path = LocalState.LOCAL_APPSCALE_PATH + options.keyname
        if options.add_to_existing:
            public_key = path + ".pub"
            private_key = path
        else:
            public_key, private_key = LocalState.generate_rsa_key(
                options.keyname, options.verbose)

        if options.auto:
            if 'root_password' in options:
                AppScaleLogger.log("Using the provided root password to log into " + \
                  "your VMs.")
                password = options.root_password
            else:
                AppScaleLogger.log("Please enter the password for the root user on" + \
                  " your VMs:")
                password = getpass.getpass()

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

        all_ips = [node.public_ip for node in node_layout.nodes]
        for ip in all_ips:
            # first, set up passwordless ssh
            AppScaleLogger.log(
                "Executing ssh-copy-id for host: {0}".format(ip))
            if options.auto:
                LocalState.shell(
                    "{0} root@{1} {2} {3}".format(cls.EXPECT_SCRIPT, ip,
                                                  private_key, password),
                    options.verbose)
            else:
                LocalState.shell(
                    "ssh-copy-id -i {0} root@{1}".format(private_key, ip),
                    options.verbose)

            # next, copy over the ssh keypair we generate
            RemoteHelper.scp(ip, options.keyname, public_key,
                             '/root/.ssh/id_rsa.pub', options.verbose)
            RemoteHelper.scp(ip, options.keyname, private_key,
                             '/root/.ssh/id_rsa', options.verbose)

        AppScaleLogger.success("Generated a new SSH key for this deployment " + \
          "at {0}".format(private_key))
예제 #15
0
  def add_keypair(cls, options):
    """Sets up passwordless SSH login to the machines used in a virtualized
    cluster deployment.

    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    Raises:
      AppScaleException: If any of the machines named in the ips_layout are
        not running, or do not have the SSH daemon running.
    """
    LocalState.require_ssh_commands(options.auto, options.verbose)
    LocalState.make_appscale_directory()

    path = LocalState.LOCAL_APPSCALE_PATH + options.keyname
    if options.add_to_existing:
      public_key = path + ".pub"
      private_key = path
    else:
      public_key, private_key = LocalState.generate_rsa_key(options.keyname,
        options.verbose)

    if options.auto:
      if 'root_password' in options:
        AppScaleLogger.log("Using the provided root password to log into " + \
          "your VMs.")
        password = options.root_password
      else:
        AppScaleLogger.log("Please enter the password for the root user on" + \
          " your VMs:")
        password = getpass.getpass()

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

    all_ips = [node.public_ip for node in node_layout.nodes]
    for ip in all_ips:
      # first, make sure ssh is actually running on the host machine
      if not RemoteHelper.is_port_open(ip, RemoteHelper.SSH_PORT,
        options.verbose):
        raise AppScaleException("SSH does not appear to be running at {0}. " \
          "Is the machine at {0} up and running? Make sure your IPs are " \
          "correct!".format(ip))

      # next, set up passwordless ssh
      AppScaleLogger.log("Executing ssh-copy-id for host: {0}".format(ip))
      if options.auto:
        LocalState.shell("{0} root@{1} {2} {3}".format(cls.EXPECT_SCRIPT, ip,
          private_key, password), options.verbose)
      else:
        LocalState.shell("ssh-copy-id -i {0} root@{1}".format(private_key, ip),
          options.verbose)

    AppScaleLogger.success("Generated a new SSH key for this deployment " + \
      "at {0}".format(private_key))
예제 #16
0
  def validate_database_flags(self):
    """Validates the values given to us by the user for any flag
    relating to the database used.

    Raises:
      BadConfigurationException: If the values for any of the
        database flags are not valid.
    """
    if self.args.replication is not None and self.args.replication < 1:
      raise BadConfigurationException("Replication factor must exceed 0.")
예제 #17
0
  def upgrade_json_file(cls, keyname):
    """Upgrades the JSON file from the other version where it is a list by
    reading the JSON file, reading the YAML file, creating a dictionary in
    the "new" format and writing that to the JSON file, and then removing the
    YAML file.

    Args:
      keyname: A str that represents an SSH keypair name, uniquely identifying
        this AppScale deployment.
    Raises:
      BadConfigurationException: If there is no JSON-encoded metadata file,
        or there is no YAML-encoded metadata file, or the JSON file couldn't be
        written to.
    """
    try:
      # Open, read, and store the JSON metadata.

      with open(cls.get_locations_json_location(keyname), 'r') as file_handle:
        role_info = json.loads(file_handle.read())

      # If this method is running, there should be a YAML metadata file.

      yaml_locations = "{0}locations-{1}.yaml".format(cls.LOCAL_APPSCALE_PATH,
                                                      keyname)

      # Open, read, and store the YAML metadata.

      with open(yaml_locations, 'r') as yaml_handle:
        locations_yaml_contents = yaml.safe_load(yaml_handle.read())

      # Create a dictionary with the information from both the YAML and JSON
      # metadata.

      locations_json = {
        'node_info': role_info,
        'infrastructure_info': locations_yaml_contents
      }

      # Write the new format to the JSON metadata file.

      with open(cls.get_locations_json_location(keyname), 'w') as file_handle:
        file_handle.write(json.dumps(locations_json))

      # Remove the YAML file because all information from it should be in the
      # JSON file now. At this point any failures would have raised the
      # Exception.

      if os.path.exists(yaml_locations):
        os.remove(yaml_locations)
    except IOError:
      raise BadConfigurationException("Couldn't upgrade locations json "
                                      "file, AppScale may not be running with"
                                      " keyname {0}".format(keyname))
예제 #18
0
    def shell_check(self, argument):
        """ Checks for special characters in arguments that are part of shell
    commands.

    Args:
      argument: A str, the argument to be checked.
    Raises:
      BadConfigurationException if single quotes are present in argument.
    """
        if '\'' in argument:
            raise BadConfigurationException("Single quotes (') are not allowed " + \
              "in filenames.")
예제 #19
0
    def upgrade(cls, options):
        """ Upgrades the deployment to the latest AppScale version.
    Args:
      options: A Namespace that has fields for each parameter that can be
        passed in via the command-line interface.
    """
        node_layout = NodeLayout(options)
        if not node_layout.is_valid():
            raise BadConfigurationException(
                'Your ips_layout is invalid:\n{}'.format(node_layout.errors()))

        latest_tools = APPSCALE_VERSION
        try:
            AppScaleLogger.log(
                'Checking if an update is available for appscale-tools')
            latest_tools = latest_tools_version()
        except:
            # Prompt the user if version metadata can't be fetched.
            if not options.test:
                response = raw_input(
                    'Unable to check for the latest version of appscale-tools. Would '
                    'you like to continue upgrading anyway? (y/N) ')
                if response.lower() not in ['y', 'yes']:
                    raise AppScaleException('Cancelled AppScale upgrade.')

        if latest_tools > APPSCALE_VERSION:
            raise AppScaleException(
                "There is a newer version ({}) of appscale-tools available. Please "
                "upgrade the tools package before running 'appscale upgrade'.".
                format(latest_tools))

        master_ip = node_layout.head_node().public_ip
        upgrade_version_available = cls.get_upgrade_version_available()

        current_version = RemoteHelper.get_host_appscale_version(
            master_ip, options.keyname, options.verbose)

        # Don't run bootstrap if current version is later that the most recent
        # public one. Covers cases of revoked versions/tags and ensures we won't
        # try to downgrade the code.
        if current_version >= upgrade_version_available:
            AppScaleLogger.log(
                'AppScale is already up to date. Skipping code upgrade.')
            AppScaleLogger.log(
                'Running upgrade script to check if any other upgrades are needed.'
            )
            cls.shut_down_appscale_if_running(options)
            cls.run_upgrade_script(options, node_layout)
            return

        cls.shut_down_appscale_if_running(options)
        cls.upgrade_appscale(options, node_layout)
예제 #20
0
    def rsync_files(cls, host, keyname, local_appscale_dir, is_verbose):
        """Copies over an AppScale source directory from this machine to the
    specified host.

    Args:
      host: A str representing a host that should be accessible from this
        machine.
      keyname: A str representing the name of the SSH keypair that can log into
        the specified machine.
      local_appscale_dir: A str representing the path on the local filesystem
        where the AppScale source to copy over can be found.
      is_verbose: A bool that indicates if we should print the rsync commands
        we exec to stdout.
    Raises:
      BadConfigurationException: If local_appscale_dir does not exist locally,
        or if any of the standard AppScale module folders do not exist.
    """
        ssh_key = LocalState.get_key_path_from_name(keyname)
        appscale_dirs = [
            "lib", "AppController", "AppManager", "AppServer",
            "AppServer_Java", "AppDashboard", "InfrastructureManager",
            "AppTaskQueue", "XMPPReceiver"
        ]
        for dir_name in appscale_dirs:
            local_path = os.path.expanduser(
                local_appscale_dir) + os.sep + dir_name
            if not os.path.exists(local_path):
                raise BadConfigurationException("The location you specified to copy " \
                  "from, {0}, doesn't contain a {1} folder.".format(local_appscale_dir,
                  local_path))
            LocalState.shell("rsync -e 'ssh -i {0} {1}' -arv {2}/* "\
              "root@{3}:/root/appscale/{4}".format(ssh_key, cls.SSH_OPTIONS,
              local_path, host, dir_name), is_verbose)

        # Rsync AppDB separately, as it has a lot of paths we may need to exclude
        # (e.g., built database binaries).
        local_app_db = os.path.expanduser(
            local_appscale_dir) + os.sep + "AppDB/*"
        LocalState.shell("rsync -e 'ssh -i {0} {1}' -arv --exclude='logs/*' " \
          "--exclude='hadoop-*' --exclude='hbase/hbase-*' " \
          "--exclude='cassandra/cassandra/*' {2} root@{3}:/root/appscale/AppDB" \
          .format(ssh_key, cls.SSH_OPTIONS, local_app_db, host), is_verbose)

        # And rsync the firewall configuration file separately, as it's not a
        # directory (which the above all are).
        local_firewall = os.path.expanduser(local_appscale_dir) + os.sep + \
          "firewall.conf"
        LocalState.shell("rsync -e 'ssh -i {0} {1}' -arv {2} root@{3}:" \
          "/root/appscale/firewall.conf".format(ssh_key, cls.SSH_OPTIONS,
          local_firewall, host), is_verbose)
예제 #21
0
    def validate_environment_flags(self):
        """Validates flags dealing with setting environment variables.

    Raises:
      BadConfigurationException: If the user gives us either EC2_ACCESS_KEY
        or EC2_SECRET_KEY, but forgets to also specify the other.
    """
        if self.args.EC2_ACCESS_KEY and not self.args.EC2_SECRET_KEY:
            raise BadConfigurationException("When specifying EC2_ACCESS_KEY, " + \
              "EC2_SECRET_KEY must also be specified.")

        if self.args.EC2_SECRET_KEY and not self.args.EC2_ACCESS_KEY:
            raise BadConfigurationException("When specifying EC2_SECRET_KEY, " + \
              "EC2_ACCESS_KEY must also be specified.")

        if self.args.EC2_ACCESS_KEY:
            os.environ['EC2_ACCESS_KEY'] = self.args.EC2_ACCESS_KEY

        if self.args.EC2_SECRET_KEY:
            os.environ['EC2_SECRET_KEY'] = self.args.EC2_SECRET_KEY

        if self.args.EC2_URL:
            os.environ['EC2_URL'] = self.args.EC2_URL
예제 #22
0
  def validate_machine_image(self):
    """Checks with the given cloud (if running in a cloud) to ensure that the
    user-specified ami/emi exists, aborting if it does not.

    Raises:
      BadConfigurationException: If the given machine image does not exist.
    """
    if not self.args.infrastructure:
      return

    cloud_agent = InfrastructureAgentFactory.create_agent(
      self.args.infrastructure)
    params = cloud_agent.get_params_from_args(self.args)
    if not cloud_agent.does_image_exist(params):
      raise BadConfigurationException("Couldn't find the given machine image.")
예제 #23
0
  def get_secret_key(cls, keyname):
    """Retrieves the secret key, used to authenticate AppScale services.

    Args:
      keyname: A str representing the SSH keypair name used for this AppScale
        deployment.
    Returns:
      A str containing the secret key.
    Raises:
      BadConfigurationException: if the secret key file is not found.
    """
    try:
      with open(cls.get_secret_key_location(keyname), 'r') as file_handle:
        return file_handle.read()
    except IOError:
     raise BadConfigurationException(
       "Couldn't find secret key for keyname {}.".format(keyname))
예제 #24
0
  def ensure_appscale_isnt_running(cls, keyname, force):
    """Checks the secret key file to see if AppScale is running, and
    aborts if it is.

    Args:
      keyname: The keypair name that is used to identify AppScale deployments.
      force: A bool that is used to run AppScale even if the secret key file
        is present.
    Raises:
      BadConfigurationException: If AppScale is already running.
    """
    if force:
      return

    if os.path.exists(cls.get_secret_key_location(keyname)):
      raise BadConfigurationException("AppScale is already running. Terminate" +
        " it or use the --force flag to run anyways.")
예제 #25
0
    def clean(self):
        """'clean' provides a mechanism that will forcefully shut down all AppScale-
    related services on virtual machines in a cluster deployment.

    Returns:
      A list of the IP addresses where AppScale was shut down.

    Raises:
      AppScalefileException: If there is no AppScalefile in the current working
        directory.
      BadConfigurationException: If this method is invoked and the AppScalefile
        indicates that a cloud deployment is being used.
    """
        contents = self.read_appscalefile()

        contents_as_yaml = yaml.safe_load(contents)
        if 'ips_layout' not in contents_as_yaml:
            raise BadConfigurationException("Cannot use 'appscale clean' in a " \
              "cloud deployment.")

        if 'verbose' in contents_as_yaml and contents_as_yaml[
                'verbose'] == True:
            is_verbose = contents_as_yaml['verbose']
        else:
            is_verbose = False

        if 'keyname' in contents_as_yaml:
            keyname = contents_as_yaml['keyname']
        else:
            keyname = 'appscale'

        all_ips = self.get_all_ips(contents_as_yaml["ips_layout"])
        for ip in all_ips:
            RemoteHelper.ssh(ip, keyname, self.TERMINATE, is_verbose)

        try:
            LocalState.cleanup_appscale_files(keyname)
        except Exception:
            pass

        AppScaleLogger.success(
            "Successfully shut down your AppScale deployment.")

        return all_ips
예제 #26
0
    def validate_appengine_flags(self):
        """Validates the values given to us by the user for any flag relating to
    the number of AppServers to launch per App Engine app.

    Raises:
      BadConfigurationException: If the value for the --appengine flag is
        invalid.
    """
        if self.args.appengine:
            if self.args.appengine < 1:
                raise BadConfigurationException("Number of application servers " + \
                  "must exceed zero.")

            self.args.autoscale = False
        elif self.args.autoscale:
            self.args.appengine = 1
        else:  # neither are set
            self.args.appengine = 1
            self.args.autoscale = True
예제 #27
0
    def validate_appengine_flags(self):
        """Validates the values given to us by the user for any flag relating to
    the number of AppServers to launch per App Engine app.

    Raises:
      BadConfigurationException: If the value for the --appengine flag is
        invalid.
    """
        # Check that appengine is greater then 1, and the set defaults for
        # min_appservers and autoscale in case they are not defined.
        if self.args.default_min_appservers:
            if self.args.default_min_appservers < 1:
                raise BadConfigurationException("Number of application servers " + \
                                                "must exceed zero.")
        else:
            self.args.default_min_appservers = 1

        if not self.args.autoscale:
            self.args.autoscale = True
예제 #28
0
  def get_local_nodes_info(cls, keyname):
    """Reads the JSON-encoded metadata on disk and returns a list that indicates
    which machines run each API service in this AppScale deployment.

    Args:
      keyname: A str that represents an SSH keypair name, uniquely identifying
        this AppScale deployment.
    Returns:
      A list of dicts, where each dict contains information on a single machine
      in this AppScale deployment.
    Raises:
      BadConfigurationException: If there is no JSON-encoded metadata file
        named after the given keyname.
    """
    if not os.path.exists(cls.get_locations_json_location(keyname)):
      raise BadConfigurationException("AppScale does not appear to be " + \
        "running with keyname {0}".format(keyname))

    with open(cls.get_locations_json_location(keyname), 'r') as file_handle:
      return json.loads(file_handle.read())
예제 #29
0
    def require_ssh_commands(cls, needs_expect, is_verbose):
        """Checks to make sure the commands needed to set up passwordless SSH
    access are installed on this machine.

    Args:
      needs_expect: A bool that indicates if we should also check for the
        'expect' command.
      is_verbose: A bool that indicates if we should print how we check for
        each command to stdout.
    Raises:
      BadConfigurationException: If any of the required commands aren't present
        on this machine.
    """
        required_commands = ['ssh-keygen', 'ssh-copy-id']
        if needs_expect:
            required_commands.append('expect')

        for command in required_commands:
            try:
                cls.shell("hash {0}".format(command), is_verbose)
            except ShellException:
                raise BadConfigurationException(
                    "Couldn't find {0} in your PATH.".format(command))
예제 #30
0
  def get_infrastructure_option(cls, tag, keyname):
    """Reads the JSON-encoded metadata on disk and returns the value for
    the key 'tag' from the dictionary retrieved using the key
    'infrastructure_info'.

    Args:
      keyname: A str that indicates the name of the SSH keypair that
        uniquely identifies this AppScale deployment.
      tag: A str that indicates what we should look for in the
        infrastructure_info dictionary, this tag retrieves an option that was
        passed to AppScale at runtime.
    """
    try:
      with open(cls.get_locations_json_location(keyname), 'r') as file_handle:
        file_contents = yaml.safe_load(file_handle.read())
        if isinstance(file_contents, list):
          cls.upgrade_json_file(keyname)
          file_handle.seek(0)
          file_contents = yaml.safe_load(file_handle.read())
        return file_contents.get('infrastructure_info', {}).get(tag)
    except IOError:
      raise BadConfigurationException("Couldn't read from locations file, "
                                      "AppScale may not be running with "
                                      "keyname {0}".format(keyname))