def run_instances(options):
  layout_options = {
    cli.OPTION_INFRASTRUCTURE : options.infrastructure,
    cli.OPTION_DATABASE : options.database,
    cli.OPTION_MIN_IMAGES : options.min,
    cli.OPTION_MAX_IMAGES : options.max,
    cli.OPTION_REPLICATION : options.replication,
    cli.OPTION_READ_FACTOR : options.read_q,
    cli.OPTION_WRITE_FACTOR : options.write_q
  }
  node_layout = NodeLayout(options.ips, layout_options)

  if options.file is not None:
    app_info = commons.get_app_info(options.file, options.database)
  else:
    app_info = ( 'none' )

  appscale_dir = os.path.expanduser(APPSCALE_DIR)
  if not os.path.exists(appscale_dir):
    os.mkdir(appscale_dir)

  if options.infrastructure:
    cloud.validate(options.infrastructure, options.machine)
    print 'Starting AppScale over', options.infrastructure
  else:
    print 'Starting AppScale in a non-cloud environment'

  secret_key_file = os.path.join(APPSCALE_DIR, options.keyname + '.secret')
  secret_key = commons.generate_secret_key(secret_key_file)

  if cloud.is_valid_cloud_type(options.infrastructure):
    cloud.configure_security(options.infrastructure, options.keyname,
      options.group, appscale_dir)
    instance_info = cloud.spawn_head_node(options.infrastructure, options.keyname,
      options.group, options.machine, options.instance_type)
    head_node = instance_info[0][0]
  else:
    head_node = node_layout.get_head_node()
    instance_info = ([head_node.id], [head_node.id], ['virtual_node'])

  locations = []
  for i in range(len(instance_info[0])):
    location = instance_info[0][i] + ':' + instance_info[1][i] + \
               ':' + instance_info[2][i]
    locations.append(location)

  named_key_loc = os.path.join(appscale_dir, options.keyname + '.key')
  named_backup_key_loc = os.path.join(appscale_dir, options.keyname + '.private')
  ssh_key = None
  key_exists = False
  for key in (named_key_loc, named_backup_key_loc):
    if os.path.exists(key):
      key_exists = True
      if commons.is_ssh_key_valid(key, head_node.id):
        ssh_key = key
        break

  if not key_exists:
    msg = 'Unable to find a SSH key to login to AppScale nodes'
    raise AppScaleToolsException(msg)
  elif ssh_key is None:
    msg = 'Unable to login to AppScale nodes with the available SSH keys'
    raise AppScaleToolsException(msg)

  # TODO: Ensure image is AppScale

  if options.scp is not None:
    commons.copy_appscale_source(options.scp, head_node.id, ssh_key)

  remote_key_file = '/root/.appscale/%s.key' % options.keyname
  commons.scp_file(ssh_key, remote_key_file, head_node.id, ssh_key)

  credentials = {
    'table' : options.database,
    'hostname' : head_node.id,
    'keyname' : options.keyname,
    'keypath' : ssh_key,
    'replication' : node_layout.replication,
    'appengine' : options.app_engines,
    'autoscale' : options.auto_scale,
    'group' : options.group
  }
  if options.database == 'voldemort':
    credentials['voldemortr'] = node_layout.read_factor
    credentials['voldemortw'] = node_layout.write_factor
  elif options.database == 'simpledb':
    credentials['SIMPLEDB_ACCESS_KEY'] = os.environ['SIMPLEDB_ACCESS_KEY']
    credentials['SIMPLEDB_SECRET_KEY'] = os.environ['SIMPLEDB_SECRET_KEY']

  if cloud.is_valid_cloud_type(options.infrastructure):
    cloud_credentials = cloud.get_cloud_env_variables(options.infrastructure)
    for key, value in cloud_credentials.items():
      credentials[key] = value

  if options.restore_from_tar:
    db_backup = '/root/db-backup.tar.gz'
    credentials['restore_from_tar'] = db_backup
    commons.scp_file(options.restore_from_tar, db_backup, head_node.id, ssh_key)

  if options.restore_neptune_info:
    neptune_info = '/etc/appscale/neptune_info.txt'
    commons.scp_file(options.restore_neptune_info, neptune_info,
      head_node.id, ssh_key)

  print 'Head node successfully initialized at', head_node.id

  # TODO: copy keys

  god_file = '/tmp/controller.god'
  commons.scp_file('resources/controller.god', god_file, head_node.id, ssh_key)
  commons.run_remote_command('god load ' + god_file, head_node.id, ssh_key)
  commons.run_remote_command('god start controller', head_node.id, ssh_key)

  client = AppControllerClient(head_node.id, secret_key)
  client.set_parameters(locations, credentials, app_info[0])
 def __is_simple_format(self):
   if self.yaml is None:
     return is_valid_cloud_type(self.infrastructure)
   else:
     return set(self.yaml.keys()).issubset(self.SIMPLE_FORMAT_KEYS)
  def __populate_simple_format(self):
    cloud = is_valid_cloud_type(self.infrastructure)
    if self.yaml is None:
      if cloud:
        if self.min_images is None or self.max_images is None:
          raise AppScaleToolsException(
            'Both min and max options must be specified when no '
            'input yaml is provided', self.ERROR_MISSING_REQUIRED_OPTIONS)
        else:
          self.yaml = self.__generate_default_layout()
      else:
        raise AppScaleToolsException(
          'Input yaml must be provided for deployments on virtualized clusters',
          self.ERROR_MISSING_INPUT_YAML)
    else:
      all_ips = commons.flatten(self.yaml.values())
      unique_ips = set(all_ips)
      if len(all_ips) != len(unique_ips):
        msg = 'Duplicate IP addresses found in input yaml'
        raise AppScaleToolsException(msg, self.ERROR_DUPLICATE_IPS)

    nodes = []
    for role,ips in self.yaml.items():
      if ips is None:
        continue

      if isinstance(ips, str):
        ips = [ ips ]

      for ip in ips:
        node = SimpleAppScaleNode(ip, [role])
        if node.has_role(ROLE_SHADOW):
          node.add_role(ROLE_DATABASE_MASTER)
          node.add_role(ROLE_RABBITMQ_MASTER)
        else:
          node.add_role(ROLE_DATABASE_SLAVE)
          node.add_role(ROLE_RABBITMQ_SLAVE)
        node.validate()

        if cloud and not re.match(self.NODE_ID_REGEX, node.id):
          raise AppScaleToolsException('Invalid cloud node ID: %s. '
                                       'Cloud node IDs must be in the '
                                       'format node-{ID}.' % node.id)
        elif not cloud and not re.match(self.IP_REGEX, node.id):
          raise AppScaleToolsException('Invalid virtualized node ID: %s. '
                                       'Virtualized node IDs must be valid IP '
                                       'addresses.' % node.id)
        else:
          nodes.append(node)

    if len(nodes) == 1:
      nodes[0].add_role(ROLE_APPENGINE)
      nodes[0].add_role(ROLE_MEMCACHE)

    controllers = 0
    for node in nodes:
      if node.has_role(ROLE_SHADOW):
        controllers += 1

    if controllers > 1:
      raise AppScaleToolsException('Only one controller node is allowed',
        self.ERROR_MULTIPLE_CONTROLLERS)
    elif not controllers:
      raise AppScaleToolsException('No controller node has been assigned',
        self.ERROR_NO_CONTROLLER)

    if not self.skip_replication:
      self.__validate_data_replication(nodes)
    self.nodes = nodes
  def __populate_advanced_format(self):
    nodes = {}
    for role, ips in self.yaml.items():
      if ips is None:
        continue

      if isinstance(ips, str):
        ips = [ ips ]

      index = 0
      for ip in ips:
        if not nodes.has_key(ip):
          node = AdvancedAppScaleNode(ip)
        else:
          node = nodes[ip]

        if role == ROLE_DATABASE:
          if not index:
            node.add_role(ROLE_DATABASE_MASTER)
          else:
            node.add_role(ROLE_DATABASE_SLAVE)
        elif role == ROLE_DATABASE_MASTER:
          node.add_role(ROLE_ZOOKEEPER)
          node.add_role(role)
        elif role == ROLE_RABBITMQ:
          if not index:
            node.add_role(ROLE_RABBITMQ_MASTER)
          else:
            node.add_role(ROLE_RABBITMQ_SLAVE)
        else:
          node.add_role(role)
        nodes[ip] = node
        index += 1

    nodes = nodes.values()
    cloud = is_valid_cloud_type(self.infrastructure)
    for node in nodes:
      if cloud and not re.match(self.NODE_ID_REGEX, node.id):
        raise AppScaleToolsException('Invalid cloud node ID: %s. '
                                     'Cloud node IDs must be in the '
                                     'format node-{ID}.' % node.id)
      elif not cloud and not re.match(self.IP_REGEX, node.id):
        raise AppScaleToolsException('Invalid virtualized node ID: %s. '
                                     'Virtualized node IDs must be valid IP '
                                     'addresses.' % node.id)

    controllers = 0
    memcache_nodes = 0
    zk_nodes = 0
    rabbit_mq_nodes = 0
    db_nodes = 0
    master_node = None
    login_node = None
    app_engine_nodes = []
    for node in nodes:
      if node.has_role(ROLE_SHADOW):
        master_node = node
        controllers += 1
      if node.has_role(ROLE_LOGIN):
        login_node = node
      if node.has_role(ROLE_APPENGINE):
        app_engine_nodes.append(node)
      if node.has_role(ROLE_MEMCACHE):
        memcache_nodes += 1
      if node.has_role(ROLE_ZOOKEEPER):
        zk_nodes += 1
      if node.has_role(ROLE_RABBITMQ):
        rabbit_mq_nodes += 1
      if node.has_role(ROLE_DATABASE):
        db_nodes += 1

    if controllers > 1:
      raise AppScaleToolsException('Only one controller node is allowed')
    elif not controllers:
      raise AppScaleToolsException('No controller node has been assigned')

    if not app_engine_nodes:
      raise AppScaleToolsException('Not enough appengine nodes were provided')

    if not login_node:
      master_node.add_role(ROLE_LOGIN)

    if not memcache_nodes:
      for app_engine in app_engine_nodes:
        app_engine.add_role(ROLE_MEMCACHE)

    if not zk_nodes:
      master_node.add_role(ROLE_ZOOKEEPER)

    if not rabbit_mq_nodes:
      master_node.add_role(ROLE_RABBITMQ)
      master_node.add_role(ROLE_RABBITMQ_MASTER)

    for node in nodes:
      if node.has_role(ROLE_APPENGINE) and not node.has_role(ROLE_RABBITMQ):
        node.add_role(ROLE_RABBITMQ_SLAVE)

    if cloud:
      if self.min_images is None: self.min_images = len(nodes)
      if self.max_images is None: self.max_images = len(nodes)
      if len(nodes) < self.min_images:
        raise AppScaleToolsException('Too few nodes were provided. %s '
                                     'provided, but %s is the minimum.' %
                                     (len(nodes), self.min_images))
      if len(nodes) > self.max_images:
        raise AppScaleToolsException('Too many nodes were provided. %s '
                                     'provided, but %s is the maximum.' %
                                     (len(nodes), self.max_images))

    if not self.skip_replication:
      self.__validate_data_replication(nodes)
    self.nodes = nodes