def validate_infrastructure_flags(self): """Validates flags corresponding to cloud infrastructures. Raises: BadConfigurationException: If the value given to us for infrastructure-related flags were invalid. """ if not self.args.infrastructure: # Make sure we didn't get a machine flag, since that's infrastructure-only if self.args.machine: raise BadConfigurationException("Cannot specify a machine image " + \ "when infrastructure is not specified.") # Also make sure they gave us a valid availability zone. if self.args.zone: raise BadConfigurationException( "Cannot specify an availability zone " + "when infrastructure is not specified.") # Fail if the user is trying to use AWS Spot Instances on a virtualized # cluster. if self.args.use_spot_instances or self.args.max_spot_price: raise BadConfigurationException("Can't run spot instances when " + \ "when infrastructure is not specified.") # Fail if the user is trying to use persistent disks on a virtualized # cluster. if self.args.disks: raise BadConfigurationException("Can't specify persistent disks " + \ "when infrastructure is not specified.") # Fail if the user is trying to use an Elastic IP / Static IP on a # virtualized cluster. if self.args.static_ip: raise BadConfigurationException("Can't specify a static IP " + \ "when infrastructure is not specified.") return # Make sure the user gave us an ami/emi if running in a cloud. if not self.args.machine: raise BadConfigurationException( "Need a machine image (ami) " + "when running in a cloud infrastructure.") # Also make sure they gave us an availability zone if they want to use # persistent disks. if self.args.disks and not self.args.zone: raise BadConfigurationException( "Need an availability zone specified " + "when persistent disks are specified.") # In Google Compute Engine, we have to specify the availability zone. if self.args.infrastructure == 'gce' and not self.args.zone: self.args.zone = GCEAgent.DEFAULT_ZONE # If the user wants to use spot instances in a cloud, make sure that it's # EC2 (since Euca doesn't have spot instances). if self.args.infrastructure != 'ec2' and (self.args.use_spot_instances or \ self.args.max_spot_price): raise BadConfigurationException("Can't run spot instances unless " + \ "Amazon EC2 is the infrastructure used.") # If the user does want to set a max spot price, make sure they told us that # they want to use spot instances in the first place. if self.args.max_spot_price and not self.args.use_spot_instances: raise BadConfigurationException("Can't have a max spot instance price" + \ " if --use_spot_instances is not set.") # If the user does want to use persistent disks, make sure they specified # them in the right format, a dictionary mapping node IDs to disk names. if self.args.disks: self.args.disks = yaml.safe_load(base64.b64decode(self.args.disks)) if not isinstance(self.args.disks, dict): raise BadConfigurationException("--disks must be a dict, but was a " \ "{0}".format(type(self.args.disks))) if not self.args.instance_type: raise BadConfigurationException("Cannot start a cloud instance without " \ "the instance type.") if self.args.instance_type in self.DISALLOWED_INSTANCE_TYPES and \ not (self.args.force or self.args.test): LocalState.confirm_or_abort("The {0} instance type does not have " \ "enough RAM to run Cassandra in a production setting. Please " \ "consider using a larger instance type.".format( self.args.instance_type)) if self.args.infrastructure == 'azure': if not self.args.azure_subscription_id: raise BadConfigurationException("Cannot start an Azure instance without " \ "the Subscription ID.") if not self.args.azure_app_id: raise BadConfigurationException("Cannot authenticate an Azure instance " \ "without the App ID.") if not self.args.azure_app_secret_key: raise BadConfigurationException("Cannot authenticate an Azure instance " \ "without the App Secret Key.") if not self.args.azure_tenant_id: raise BadConfigurationException("Cannot authenticate an Azure instance " \ "without the Tenant ID.")
def down(self, clean=False, terminate=False): """ 'down' provides a nicer experience for users than the appscale-terminate-instances command, by using the configuration options present in the AppScalefile found in the current working directory. Args: clean: A boolean to indicate if the deployment data and metadata needs to be clean. This will clear the datastore. terminate: A boolean to indicate if instances needs to be terminated (valid only if we spawn instances at start). Raises: AppScalefileException: If there is no AppScalefile in the current working directory. """ contents = self.read_appscalefile() # Construct a terminate-instances command from the file's contents command = [] contents_as_yaml = yaml.safe_load(contents) if 'verbose' in contents_as_yaml and contents_as_yaml[ 'verbose'] == True: is_verbose = contents_as_yaml['verbose'] command.append("--verbose") else: is_verbose = False if 'keyname' in contents_as_yaml: keyname = contents_as_yaml['keyname'] command.append("--keyname") command.append(contents_as_yaml['keyname']) else: keyname = 'appscale' if "EC2_ACCESS_KEY" in contents_as_yaml: os.environ["EC2_ACCESS_KEY"] = contents_as_yaml["EC2_ACCESS_KEY"] if "EC2_SECRET_KEY" in contents_as_yaml: os.environ["EC2_SECRET_KEY"] = contents_as_yaml["EC2_SECRET_KEY"] if "EC2_URL" in contents_as_yaml: os.environ["EC2_URL"] = contents_as_yaml["EC2_URL"] if clean: if 'test' not in contents_as_yaml or contents_as_yaml[ 'test'] != True: LocalState.confirm_or_abort( "Clean will delete every data in the deployment.") all_ips = LocalState.get_all_public_ips(keyname) for ip in all_ips: RemoteHelper.ssh(ip, keyname, self.TERMINATE, is_verbose) AppScaleLogger.success( "Successfully cleaned your AppScale deployment.") if terminate: infrastructure = LocalState.get_infrastructure(keyname) if infrastructure != "xen" and not LocalState.are_disks_used( keyname) and 'test' not in contents_as_yaml: LocalState.confirm_or_abort( "Terminate will delete instances and the data on them.") command.append("--terminate") if 'test' in contents_as_yaml and contents_as_yaml['test'] == True: command.append("--test") # Finally, exec the command. Don't worry about validating it - # appscale-terminate-instances will do that for us. options = ParseArgs(command, "appscale-terminate-instances").args AppScaleTools.terminate_instances(options) LocalState.cleanup_appscale_files(keyname, terminate) AppScaleLogger.success( "Successfully shut down your AppScale deployment.")
def validate_node_layout(self): """Checks to see if this NodeLayout represents an acceptable (new) advanced deployment strategy, and if so, constructs self.nodes from it. Returns: True if the deployment strategy is valid. Raises: BadConfigurationException with reason if the deployment strategy is not valid. """ if self.input_yaml and not isinstance(self.input_yaml, list): return self.invalid("Node layout format was not recognized.") if not self.input_yaml and self.infrastructure not in \ InfrastructureAgentFactory.VALID_AGENTS: # When running in a cloud, simple formats don't require an input_yaml return self.invalid("Node layout format was not recognized.") if not self.input_yaml: if self.infrastructure in InfrastructureAgentFactory.VALID_AGENTS: if not self.min_machines: self.invalid(self.NO_YAML_REQUIRES_MIN) if not self.max_machines: self.invalid(self.NO_YAML_REQUIRES_MAX) # No layout was created, so create a generic one and then allow it # to be validated. self.input_yaml = self.generate_cloud_layout() else: self.invalid(self.INPUT_YAML_REQUIRED) # Keep track of whether the deployment is valid while going through. node_hash = {} role_count = { 'compute': 0, 'shadow': 0, 'memcache': 0, 'taskqueue': 0, 'zookeeper': 0, 'login': 0, 'db_master': 0, 'taskqueue_master': 0 } node_count = 0 all_disks = [] login_found = False # Loop through the list of "node sets", which are grouped by role. for node_set in self.input_yaml: # If the key nodes is mapped to an integer it should be a cloud # deployment so we will use node-ids. using_cloud_ids = isinstance(node_set.get('nodes'), int) # In cloud_ids deployments, set the fake public ips to node-#. if using_cloud_ids: ips_list = ["node-{}".format(node_count + i) \ for i in xrange(node_set.get('nodes'))] # Update node_count. node_count += len(ips_list) # Otherwise get the ips and validate them. else: ip_or_ips = node_set.get('nodes') ips_list = ip_or_ips if isinstance(ip_or_ips, list) else [ip_or_ips] # Validate that the ips_list are either node-id or ip addresses. if any([self.is_cloud_ip(ip) for ip in ips_list]): self.invalid("Role(s) {}: using node-id format is not supported" " with the ips_layout format being used. Please " "specify an integer or an ip address."\ .format(node_set.get('roles'))) if len(ips_list) - len(set(ips_list)) > 0: self.invalid(self.DUPLICATE_IPS) # Get the roles. role_or_roles = node_set.get('roles') if len(ips_list) == 0: self.invalid("Node amount cannot be zero for role(s) {}."\ .format(role_or_roles)) roles = role_or_roles if isinstance(role_or_roles, list) else \ [role_or_roles] # Immediately fail if we have more than one node for master. if 'master' in roles and (self.master or len(ips_list) > 1): self.invalid("Only one master is allowed.") # Create or retrieve the nodes from the node_hash. nodes = [node_hash[ip] if ip in node_hash else \ Node(ip, using_cloud_ids) for ip in ips_list] # Validate volume usage, there should be an equal number of volumes to # number of nodes. if node_set.get('disks', None): disk_or_disks = node_set.get('disks') disks = disk_or_disks if isinstance(disk_or_disks, list) else \ [disk_or_disks] all_disks.extend(disks) self.validate_disks(len(nodes), disks) for node, disk in zip(nodes, disks): node.disk = disk instance_type = node_set.get('instance_type', self.default_instance_type) if self.infrastructure: if not instance_type: self.invalid("Must set a default instance type or specify instance " "type per role.") # Check if this is an allowed instance type. if instance_type in ParseArgs.DISALLOWED_INSTANCE_TYPES and \ not (self.force or self.test): reason = "the suggested 4GB of RAM" if 'database' in roles: reason += " to run Cassandra" LocalState.confirm_or_abort("The {0} instance type does not have {1}." "Please consider using a larger instance " "type.".format(instance_type, reason)) # Assign master. if 'master' in roles: self.master = nodes[0] # Add the defined roles to the nodes. for node in nodes: for role in roles: node.add_role(role) if role == 'login': node.public_ip = self.login_host or node.public_ip node.instance_type = instance_type if not node.is_valid(): self.invalid(",".join(node.errors())) # All nodes that have the same roles will be expanded the same way, # so get the updated list of roles from the first node. roles = nodes[0].roles # Check for login after roles have been expanded. if 'login' in roles and login_found: self.invalid("Only one login is allowed.") elif 'login' in roles: login_found = True # Update dictionary containing role counts. role_count.update({role: role_count.get(role, 0) + len(nodes) for role in roles}) # Update the node_hash with the modified nodes. node_hash.update({node.public_ip: node for node in nodes}) # Make sure disks are unique. if all_disks: self.validate_disks(len(all_disks), all_disks) self.validate_database_replication(node_hash.values()) # Distribute unassigned roles and validate that certain roles are filled # and return a list of nodes or raise BadConfigurationException. nodes = self.distribute_unassigned_roles(node_hash.values(), role_count) if self.infrastructure in InfrastructureAgentFactory.VALID_AGENTS: if not self.min_machines: self.min_machines = len(nodes) if not self.max_machines: self.max_machines = len(nodes) self.nodes = nodes return True
def validate_node_layout(self): """Checks to see if this NodeLayout represents an acceptable (new) advanced deployment strategy, and if so, constructs self.nodes from it. Returns: True if the deployment strategy is valid. Raises: BadConfigurationException with reason if the deployment strategy is not valid. """ if self.input_yaml and not isinstance(self.input_yaml, list): return self.invalid("Node layout format was not recognized.") if not self.input_yaml and self.infrastructure not in \ InfrastructureAgentFactory.VALID_AGENTS: # When running in a cloud, simple formats don't require an input_yaml return self.invalid("Node layout format was not recognized.") if not self.input_yaml: if self.infrastructure in InfrastructureAgentFactory.VALID_AGENTS: if not self.min_machines: self.invalid(self.NO_YAML_REQUIRES_MIN) if not self.max_machines: self.invalid(self.NO_YAML_REQUIRES_MAX) # No layout was created, so create a generic one and then allow it # to be validated. self.input_yaml = self.generate_cloud_layout() else: self.invalid(self.INPUT_YAML_REQUIRED) # Keep track of whether the deployment is valid while going through. node_hash = {} role_count = { 'compute': 0, 'shadow': 0, 'memcache': 0, 'taskqueue': 0, 'zookeeper': 0, 'login': 0, 'db_master': 0, 'taskqueue_master': 0 } node_count = 0 all_disks = [] login_found = False # Loop through the list of "node sets", which are grouped by role. for node_set in self.input_yaml: # If the key nodes is mapped to an integer it should be a cloud # deployment so we will use node-ids. using_cloud_ids = isinstance(node_set.get('nodes'), int) # In cloud_ids deployments, set the fake public ips to node-#. if using_cloud_ids: ips_list = ["node-{}".format(node_count + i) \ for i in xrange(node_set.get('nodes'))] # Update node_count. node_count += len(ips_list) # Otherwise get the ips and validate them. else: ip_or_ips = node_set.get('nodes') ips_list = ip_or_ips if isinstance(ip_or_ips, list) else [ip_or_ips] # Validate that the ips_list are either node-id or ip addresses. if any([self.is_cloud_ip(ip) for ip in ips_list]): self.invalid("Role(s) {}: using node-id format is not supported" " with the ips_layout format being used. Please " "specify an integer or an ip address."\ .format(node_set.get('roles'))) if len(ips_list) - len(set(ips_list)) > 0: self.invalid(self.DUPLICATE_IPS) # Get the roles. role_or_roles = node_set.get('roles') if len(ips_list) == 0: self.invalid("Node amount cannot be zero for role(s) {}."\ .format(role_or_roles)) roles = role_or_roles if isinstance(role_or_roles, list) else \ [role_or_roles] # Immediately fail if we have more than one node for master. if 'master' in roles and (self.master or len(ips_list) > 1): self.invalid("Only one master is allowed.") # Create or retrieve the nodes from the node_hash. nodes = [node_hash[ip] if ip in node_hash else \ Node(ip, using_cloud_ids) for ip in ips_list] # Validate volume usage, there should be an equal number of volumes to # number of nodes. if node_set.get('disks', None): disk_or_disks = node_set.get('disks') disks = disk_or_disks if isinstance(disk_or_disks, list) else \ [disk_or_disks] all_disks.extend(disks) self.validate_disks(len(nodes), disks) for node, disk in zip(nodes, disks): node.disk = disk instance_type = node_set.get('instance_type', self.default_instance_type) if self.infrastructure: if not instance_type: self.invalid( "Must set a default instance type or specify instance " "type per role.") # Check if this is an allowed instance type. if instance_type in ParseArgs.DISALLOWED_INSTANCE_TYPES and \ not (self.force or self.test): reason = "the suggested 4GB of RAM" if 'database' in roles: reason += " to run Cassandra" LocalState.confirm_or_abort( "The {0} instance type does not have {1}." "Please consider using a larger instance " "type.".format(instance_type, reason)) # Assign master. if 'master' in roles: self.master = nodes[0] # Add the defined roles to the nodes. for node in nodes: for role in roles: node.add_role(role) if role == 'login': node.public_ip = self.login_host or node.public_ip node.instance_type = instance_type if not node.is_valid(): self.invalid(",".join(node.errors())) # All nodes that have the same roles will be expanded the same way, # so get the updated list of roles from the first node. roles = nodes[0].roles # Check for login after roles have been expanded. if 'login' in roles and login_found: self.invalid("Only one login is allowed.") elif 'login' in roles: login_found = True # Update dictionary containing role counts. role_count.update( {role: role_count.get(role, 0) + len(nodes) for role in roles}) # Update the node_hash with the modified nodes. node_hash.update({node.public_ip: node for node in nodes}) # Make sure disks are unique. if all_disks: self.validate_disks(len(all_disks), all_disks) self.validate_database_replication(node_hash.values()) # Distribute unassigned roles and validate that certain roles are filled # and return a list of nodes or raise BadConfigurationException. nodes = self.distribute_unassigned_roles(node_hash.values(), role_count) if self.infrastructure in InfrastructureAgentFactory.VALID_AGENTS: if not self.min_machines: self.min_machines = len(nodes) if not self.max_machines: self.max_machines = len(nodes) self.nodes = nodes return True
def validate_infrastructure_flags(self): """Validates flags corresponding to cloud infrastructures. Raises: BadConfigurationException: If the value given to us for infrastructure-related flags were invalid. """ if not self.args.infrastructure: # Make sure we didn't get a machine flag, since that's infrastructure-only if self.args.machine: raise BadConfigurationException( "Cannot specify a machine image " + "when infrastructure is not specified." ) # Also make sure they gave us a valid availability zone. if self.args.zone: raise BadConfigurationException( "Cannot specify an availability zone " + "when infrastructure is not specified." ) # Fail if the user is trying to use AWS Spot Instances on a virtualized # cluster. if self.args.use_spot_instances or self.args.max_spot_price: raise BadConfigurationException( "Can't run spot instances when " + "when infrastructure is not specified." ) # Fail if the user is trying to use persistent disks on a virtualized # cluster. if self.args.disks: raise BadConfigurationException( "Can't specify persistent disks " + "when infrastructure is not specified." ) # Fail if the user is trying to use an Elastic IP / Static IP on a # virtualized cluster. if self.args.static_ip: raise BadConfigurationException("Can't specify a static IP " + "when infrastructure is not specified.") return # Make sure the user gave us an ami/emi if running in a cloud. if not self.args.machine: raise BadConfigurationException("Need a machine image (ami) " + "when running in a cloud infrastructure.") # Also make sure they gave us an availability zone if they want to use # persistent disks. if self.args.disks and not self.args.zone: raise BadConfigurationException( "Need an availability zone specified " + "when persistent disks are specified." ) # In Google Compute Engine, we have to specify the availability zone. if self.args.infrastructure == "gce" and not self.args.zone: self.args.zone = GCEAgent.DEFAULT_ZONE # If the user wants to use spot instances in a cloud, make sure that it's # EC2 (since Euca doesn't have spot instances). if self.args.infrastructure != "ec2" and (self.args.use_spot_instances or self.args.max_spot_price): raise BadConfigurationException( "Can't run spot instances unless " + "Amazon EC2 is the infrastructure used." ) # If the user does want to set a max spot price, make sure they told us that # they want to use spot instances in the first place. if self.args.max_spot_price and not self.args.use_spot_instances: raise BadConfigurationException( "Can't have a max spot instance price" + " if --use_spot_instances is not set." ) # If the user does want to use persistent disks, make sure they specified # them in the right format, a dictionary mapping node IDs to disk names. if self.args.disks: self.args.disks = yaml.safe_load(base64.b64decode(self.args.disks)) if not isinstance(self.args.disks, dict): raise BadConfigurationException( "--disks must be a dict, but was a " "{0}".format(type(self.args.disks)) ) if self.args.instance_type in EC2Agent.DISALLOWED_INSTANCE_TYPES and not (self.args.force or self.args.test): LocalState.confirm_or_abort( "The {0} instance type does not have " "enough RAM to run Cassandra in a production setting. Please " "consider using a larger instance type.".format(self.args.instance_type) ) if self.args.gce_instance_type in GCEAgent.DISALLOWED_INSTANCE_TYPES and not ( self.args.force or self.args.test ): LocalState.confirm_or_abort( "The {0} instance type does not have " "enough RAM to run Cassandra in a production setting. Please " "consider using a larger instance type.".format(self.args.gce_instance_type) )
def validate_infrastructure_flags(self): """Validates flags corresponding to cloud infrastructures. Raises: BadConfigurationException: If the value given to us for infrastructure-related flags were invalid. """ if not self.args.infrastructure: # Make sure we didn't get a machine flag, since that's infrastructure-only if self.args.machine: raise BadConfigurationException("Cannot specify a machine image " + \ "when infrastructure is not specified.") # Also make sure they gave us a valid availability zone. if self.args.zone: raise BadConfigurationException("Cannot specify an availability zone " + "when infrastructure is not specified.") # Fail if the user is trying to use AWS Spot Instances on a virtualized # cluster. if self.args.use_spot_instances or self.args.max_spot_price: raise BadConfigurationException("Can't run spot instances when " + \ "when infrastructure is not specified.") # Fail if the user is trying to use persistent disks on a virtualized # cluster. if self.args.disks: raise BadConfigurationException("Can't specify persistent disks " + \ "when infrastructure is not specified.") # Fail if the user is trying to use an Elastic IP / Static IP on a # virtualized cluster. if self.args.static_ip: raise BadConfigurationException("Can't specify a static IP " + \ "when infrastructure is not specified.") return # Make sure the user gave us an ami/emi if running in a cloud. if not self.args.machine: raise BadConfigurationException("Need a machine image (ami) " + "when running in a cloud infrastructure.") # Also make sure they gave us an availability zone if they want to use # persistent disks. if self.args.disks and not self.args.zone: raise BadConfigurationException("Need an availability zone specified " + "when persistent disks are specified.") # In Google Compute Engine, we have to specify the availability zone. if self.args.infrastructure == 'gce' and not self.args.zone: self.args.zone = GCEAgent.DEFAULT_ZONE # If the user wants to use spot instances in a cloud, make sure that it's # EC2 (since Euca doesn't have spot instances). if self.args.infrastructure != 'ec2' and (self.args.use_spot_instances or \ self.args.max_spot_price): raise BadConfigurationException("Can't run spot instances unless " + \ "Amazon EC2 is the infrastructure used.") # If the user does want to set a max spot price, make sure they told us that # they want to use spot instances in the first place. if self.args.max_spot_price and not self.args.use_spot_instances: raise BadConfigurationException("Can't have a max spot instance price" + \ " if --use_spot_instances is not set.") # If the user does want to use persistent disks, make sure they specified # them in the right format, a dictionary mapping node IDs to disk names. if self.args.disks: self.args.disks = yaml.safe_load(base64.b64decode(self.args.disks)) if not isinstance(self.args.disks, dict): raise BadConfigurationException("--disks must be a dict, but was a " \ "{0}".format(type(self.args.disks))) if self.args.instance_type in self.DISALLOWED_INSTANCE_TYPES and \ not (self.args.force or self.args.test): LocalState.confirm_or_abort("The {0} instance type does not have " "the suggested 4GB of RAM. Please consider using a larger instance " "type.".format(self.args.instance_type)) if self.args.infrastructure == 'azure': if not self.args.azure_subscription_id: raise BadConfigurationException("Cannot start an Azure instance without " \ "the Subscription ID.") if not self.args.azure_app_id: raise BadConfigurationException("Cannot authenticate an Azure instance " \ "without the App ID.") if not self.args.azure_app_secret_key: raise BadConfigurationException("Cannot authenticate an Azure instance " \ "without the App Secret Key.") if not self.args.azure_tenant_id: raise BadConfigurationException("Cannot authenticate an Azure instance " \ "without the Tenant ID.") elif self.args.infrastructure in ['euca', 'ec2']: if not (self.args.EC2_ACCESS_KEY and self.args.EC2_SECRET_KEY): raise BadConfigurationException("Both EC2_ACCESS_KEY and " "EC2_SECRET_KEY must be specified.")
def down(self, clean=False, terminate=False): """ 'down' provides a nicer experience for users than the appscale-terminate-instances command, by using the configuration options present in the AppScalefile found in the current working directory. Args: clean: A boolean to indicate if the deployment data and metadata needs to be clean. This will clear the datastore. terminate: A boolean to indicate if instances needs to be terminated (valid only if we spawn instances at start). Raises: AppScalefileException: If there is no AppScalefile in the current working directory. """ contents = self.read_appscalefile() # Construct a terminate-instances command from the file's contents command = [] contents_as_yaml = yaml.safe_load(contents) if 'verbose' in contents_as_yaml and contents_as_yaml['verbose'] == True: is_verbose = contents_as_yaml['verbose'] command.append("--verbose") else: is_verbose = False if 'keyname' in contents_as_yaml: keyname = contents_as_yaml['keyname'] command.append("--keyname") command.append(contents_as_yaml['keyname']) else: keyname = 'appscale' if "EC2_ACCESS_KEY" in contents_as_yaml: os.environ["EC2_ACCESS_KEY"] = contents_as_yaml["EC2_ACCESS_KEY"] if "EC2_SECRET_KEY" in contents_as_yaml: os.environ["EC2_SECRET_KEY"] = contents_as_yaml["EC2_SECRET_KEY"] if "EC2_URL" in contents_as_yaml: os.environ["EC2_URL"] = contents_as_yaml["EC2_URL"] if clean: if 'test' not in contents_as_yaml or contents_as_yaml['test'] != True: LocalState.confirm_or_abort("Clean will delete every data in the deployment.") all_ips = LocalState.get_all_public_ips(keyname) for ip in all_ips: RemoteHelper.ssh(ip, keyname, self.TERMINATE, is_verbose) AppScaleLogger.success("Successfully cleaned your AppScale deployment.") if terminate: infrastructure = LocalState.get_infrastructure(keyname) if infrastructure != "xen" and not LocalState.are_disks_used( keyname) and 'test' not in contents_as_yaml: LocalState.confirm_or_abort("Terminate will delete instances and the data on them.") command.append("--terminate") if 'test' in contents_as_yaml and contents_as_yaml['test'] == True: command.append("--test") # Finally, exec the command. Don't worry about validating it - # appscale-terminate-instances will do that for us. options = ParseArgs(command, "appscale-terminate-instances").args AppScaleTools.terminate_instances(options) LocalState.cleanup_appscale_files(keyname, terminate) AppScaleLogger.success("Successfully shut down your AppScale deployment.")