def create_network(self, parameters): """ Creates a new network in Google Compute Engine with the specified name. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key indicating the name of the network that we should create in GCE. Returns: The URL corresponding to the name of the network that was created, for use with binding this network to one or more firewalls. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.networks().insert( project=parameters[self.PARAM_PROJECT], body={ "name" : parameters[self.PARAM_GROUP], "description" : "Network used for AppScale instances", "IPv4Range" : "10.240.0.0/16" } ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT]) return response['targetLink']
def create_firewall(self, parameters, network_url): """ Creates a new firewall in Google Compute Engine with the specified name, bound to the specified network. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key indicating the name of the firewall that we should create. network_url: A str containing the URL of the network that this new firewall should be applied to. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.firewalls().insert( project=parameters[self.PARAM_PROJECT], body={ "name" : parameters[self.PARAM_GROUP], "description" : "Firewall used for AppScale instances", "network" : network_url, "sourceRanges" : ["0.0.0.0/0"], "allowed" : [ {"IPProtocol" : "tcp", "ports": ["1-65535"]}, {"IPProtocol" : "udp", "ports": ["1-65535"]}, {"IPProtocol" : "icmp"} ] } ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT])
def terminate_instances(self, parameters): """ Deletes the instances specified in 'parameters' running in Google Compute Engine. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key mapping to a list of instance names that should be deleted. """ instance_ids = parameters[self.PARAM_INSTANCE_IDS] responses = [] for instance_id in instance_ids: gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.instances().delete( project=parameters[self.PARAM_PROJECT], zone=parameters[self.PARAM_ZONE], instance=instance_id ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) responses.append(response) for response in responses: gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT])
def add_access_config(self, parameters, instance_id, static_ip): """ Instructs Google Compute Engine to use the given IP address as the public IP for the named instance. This assumes that there is no existing public IP address for the named instance. If this is not the case, callers should use delete_access_config first to remove it. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key mapping to a list of instance names that should be deleted. instance_id: A str naming the running instance that the new public IP address should be added to. static_ip: A str naming the already allocated static IP address that will be used for the named instance. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.instances().addAccessConfig( project=parameters[self.PARAM_PROJECT], instance=instance_id, networkInterface="nic0", zone=parameters[self.PARAM_ZONE], body={ "kind": "compute#accessConfig", "type" : "ONE_TO_ONE_NAT", "name" : "External NAT", "natIP" : static_ip } ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE])
def does_disk_exist(self, parameters, disk): """ Queries Google Compute Engine to see if the specified persistent disk exists for this user. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. disk: A str containing the name of the disk that we should check for existence. Returns: True if the named persistent disk exists, and False otherwise. """ gce_service, credentials = self.open_connection(parameters) try: http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.disks().get( project=parameters[self.PARAM_PROJECT], disk=disk, zone=parameters[self.PARAM_ZONE]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) return True except errors.HttpError: return False
def does_address_exist(self, parameters): """ Queries Google Compute Engine to see if the specified static IP address exists for this user. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key indicating the name of the static IP address that we should check for existence. Returns: True if the named address exists, and False otherwise. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.addresses().list( project=parameters[self.PARAM_PROJECT], filter="address eq {0}".format(parameters[self.PARAM_STATIC_IP]), region=parameters[self.PARAM_REGION] ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) if 'items' in response: return True else: return False
def create_scratch_disk(self, parameters): """ Creates a disk from a given machine image. GCE does not support scratch disks on API version v1 and higher. We create a persistent disk upon creation to act like one to keep the abstraction used in other infrastructures. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. Returns: A str, the url to the disk to use. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) disk_name = self.generate_disk_name(parameters) project_url = '{0}{1}'.format(self.GCE_URL, parameters[self.PARAM_PROJECT]) source_image_url = '{0}{1}/global/images/{2}'.format( self.GCE_URL, parameters[self.PARAM_PROJECT], parameters[self.PARAM_IMAGE_ID]) request = gce_service.disks().insert( project=parameters[self.PARAM_PROJECT], zone=parameters[self.PARAM_ZONE], body={'name': disk_name}, sourceImage=source_image_url) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT]) disk_url = "{0}/zones/{1}/disks/{2}".format( project_url, parameters[self.PARAM_ZONE], disk_name) return disk_url
def assert_credentials_are_valid(self, parameters): """Contacts GCE to see if the given credentials are valid. Args: parameters: A dict containing the credentials necessary to interact with GCE. Raises: AgentConfigurationException: If an error is encountered during authentication. """ gce_service, credentials = self.open_connection(parameters) try: http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.instances().list( project=parameters[self.PARAM_PROJECT], zone=parameters[self.PARAM_ZONE]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) return True except errors.HttpError as e: error_message = json.loads(e.content)['error']['message'] raise AgentConfigurationException(error_message)
def does_ssh_key_exist(self, parameters): """ Queries Google Compute Engine to see if the specified SSH key exists. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. We don't have an additional key for the name of the SSH key, since we use the one in ~/.ssh. Returns: A tuple of two items. The first item is a bool that is True if our public key's contents are in GCE, and False otherwise, while the second item is the contents of all SSH keys stored in GCE. """ our_public_ssh_key = None public_ssh_key_location = LocalState.LOCAL_APPSCALE_PATH + \ parameters[self.PARAM_KEYNAME] + ".pub" with open(public_ssh_key_location) as file_handle: system_user = os.getenv('LOGNAME', default=pwd.getpwuid(os.getuid())[0]) our_public_ssh_key = system_user + ":" + file_handle.read().rstrip( ) gce_service, credentials = self.open_connection(parameters) try: http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.projects().get( project=parameters[self.PARAM_PROJECT]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) if not 'items' in response['commonInstanceMetadata']: return False, "" metadata = response['commonInstanceMetadata']['items'] if not metadata: return False, "" all_ssh_keys = "" for item in metadata: if item['key'] != 'sshKeys': continue # Now that we know there's one or more SSH keys, just make sure that # ours is in this list. all_ssh_keys = item['value'] if our_public_ssh_key in all_ssh_keys: return True, all_ssh_keys return False, all_ssh_keys except errors.HttpError: return False, ""
def does_ssh_key_exist(self, parameters): """ Queries Google Compute Engine to see if the specified SSH key exists. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. We don't have an additional key for the name of the SSH key, since we use the one in ~/.ssh. Returns: A tuple of two items. The first item is a bool that is True if our public key's contents are in GCE, and False otherwise, while the second item is the contents of all SSH keys stored in GCE. """ our_public_ssh_key = None public_ssh_key_location = LocalState.LOCAL_APPSCALE_PATH + \ parameters[self.PARAM_KEYNAME] + ".pub" with open(public_ssh_key_location) as file_handle: system_user = os.getenv('LOGNAME', default=pwd.getpwuid(os.getuid())[0]) our_public_ssh_key = system_user + ":" + file_handle.read().rstrip() gce_service, credentials = self.open_connection(parameters) try: http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.projects().get( project=parameters[self.PARAM_PROJECT]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) if not 'items' in response['commonInstanceMetadata']: return False, "" metadata = response['commonInstanceMetadata']['items'] if not metadata: return False, "" all_ssh_keys = "" for item in metadata: if item['key'] != 'sshKeys': continue # Now that we know there's one or more SSH keys, just make sure that # ours is in this list. all_ssh_keys = item['value'] if our_public_ssh_key in all_ssh_keys: return True, all_ssh_keys return False, all_ssh_keys except errors.HttpError: return False, ""
def delete_firewall(self, parameters): """ Deletes a firewall in Google Compute Engine with the specified name. Callers should not invoke this method until they are certain that no instances are using the specified firewall, or this method will fail. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key indicating the name of the firewall that we should create. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.firewalls().delete( project=parameters[self.PARAM_PROJECT], firewall=parameters[self.PARAM_GROUP]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT])
def describe_instances(self, parameters, pending=False): """ Queries Google Compute Engine to see which instances are currently running, and retrieve information about their public and private IPs. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. pending: Boolean if we should show pending instances. Returns: A tuple of the form (public_ips, private_ips, instance_ids), where each member is a list. Items correspond to each other across these lists, so a caller is guaranteed that item X in each list belongs to the same virtual machine. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.instances().list( project=parameters[self.PARAM_PROJECT], filter="name eq appscale-{0}-.*".format( parameters[self.PARAM_GROUP]), zone=parameters[self.PARAM_ZONE]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) instance_ids = [] public_ips = [] private_ips = [] if response and 'items' in response: instances = response['items'] for instance in instances: if instance['status'] == "RUNNING": instance_ids.append(instance['name']) network_interface = instance['networkInterfaces'][0] public_ips.append( network_interface['accessConfigs'][0]['natIP']) private_ips.append(network_interface['networkIP']) return public_ips, private_ips, instance_ids
def create_ssh_key(self, parameters, all_ssh_keys): """ Creates a new SSH key in Google Compute Engine with the contents of our newly generated public key. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. all_ssh_keys: A str that contains all of the SSH keys that are currently passed in to GCE instances. """ our_public_ssh_key = None public_ssh_key_location = LocalState.LOCAL_APPSCALE_PATH + \ parameters[self.PARAM_KEYNAME] + ".pub" with open(public_ssh_key_location) as file_handle: system_user = os.getenv('LOGNAME', default=pwd.getpwuid(os.getuid())[0]) our_public_ssh_key = system_user + ":" + file_handle.read().rstrip( ) if all_ssh_keys: new_all_ssh_keys = our_public_ssh_key + "\n" + all_ssh_keys else: new_all_ssh_keys = our_public_ssh_key gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.projects().setCommonInstanceMetadata( project=parameters[self.PARAM_PROJECT], body={ "kind": "compute#metadata", "items": [{ "key": "sshKeys", "value": new_all_ssh_keys }] }) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT])
def delete_firewall(self, parameters): """ Deletes a firewall in Google Compute Engine with the specified name. Callers should not invoke this method until they are certain that no instances are using the specified firewall, or this method will fail. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key indicating the name of the firewall that we should create. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.firewalls().delete( project=parameters[self.PARAM_PROJECT], firewall=parameters[self.PARAM_GROUP] ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT])
def delete_access_config(self, parameters, instance_id): """ Instructs Google Compute Engine to remove the public IP address from the named instance. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key mapping to a list of instance names that should be deleted. instance_id: A str naming the running instance that the new public IP address should be added to. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.instances().deleteAccessConfig( project=parameters[self.PARAM_PROJECT], accessConfig="External NAT", instance=instance_id, networkInterface="nic0", zone=parameters[self.PARAM_ZONE]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE])
def describe_instances(self, parameters, pending=False): """ Queries Google Compute Engine to see which instances are currently running, and retrieve information about their public and private IPs. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. pending: Boolean if we should show pending instances. Returns: A tuple of the form (public_ips, private_ips, instance_ids), where each member is a list. Items correspond to each other across these lists, so a caller is guaranteed that item X in each list belongs to the same virtual machine. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.instances().list( project=parameters[self.PARAM_PROJECT], filter="name eq {group}-.*".format(group=parameters[self.PARAM_GROUP]), zone=parameters[self.PARAM_ZONE] ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) instance_ids = [] public_ips = [] private_ips = [] if response and 'items' in response: instances = response['items'] for instance in instances: if instance['status'] == "RUNNING": instance_ids.append(instance['name']) network_interface = instance['networkInterfaces'][0] public_ips.append(network_interface['accessConfigs'][0]['natIP']) private_ips.append(network_interface['networkIP']) return public_ips, private_ips, instance_ids
def does_image_exist(self, parameters): """ Queries Google Compute Engine to see if the specified image exists for this user. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key indicating the name of the image that we should check for existence. Returns: True if the named image exists, and False otherwise. """ gce_service, credentials = self.open_connection(parameters) try: http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.images().get(project=parameters[self.PARAM_PROJECT], image=parameters[self.PARAM_IMAGE_ID]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) return True except errors.HttpError: return False
def create_ssh_key(self, parameters, all_ssh_keys): """ Creates a new SSH key in Google Compute Engine with the contents of our newly generated public key. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. all_ssh_keys: A str that contains all of the SSH keys that are currently passed in to GCE instances. """ our_public_ssh_key = None public_ssh_key_location = LocalState.LOCAL_APPSCALE_PATH + \ parameters[self.PARAM_KEYNAME] + ".pub" with open(public_ssh_key_location) as file_handle: system_user = os.getenv('LOGNAME', default=pwd.getpwuid(os.getuid())[0]) our_public_ssh_key = system_user + ":" + file_handle.read().rstrip() if all_ssh_keys: new_all_ssh_keys = our_public_ssh_key + "\n" + all_ssh_keys else: new_all_ssh_keys = our_public_ssh_key gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.projects().setCommonInstanceMetadata( project=parameters[self.PARAM_PROJECT], body={ "kind": "compute#metadata", "items": [{ "key": "sshKeys", "value": new_all_ssh_keys }] } ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT])
def does_disk_exist(self, parameters, disk): """ Queries Google Compute Engine to see if the specified persistent disk exists for this user. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. disk: A str containing the name of the disk that we should check for existence. Returns: True if the named persistent disk exists, and False otherwise. """ gce_service, credentials = self.open_connection(parameters) try: http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.disks().get(project=parameters[self.PARAM_PROJECT], disk=disk, zone=parameters[self.PARAM_ZONE]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) return True except errors.HttpError: return False
def assert_credentials_are_valid(self, parameters): """Contacts GCE to see if the given credentials are valid. Args: parameters: A dict containing the credentials necessary to interact with GCE. Raises: AgentConfigurationException: If an error is encountered during authentication. """ gce_service, credentials = self.open_connection(parameters) try: http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.instances().list(project=parameters [self.PARAM_PROJECT], zone=parameters[self.PARAM_ZONE]) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) return True except errors.HttpError as e: error_message = json.loads(e.content)['error']['message'] raise AgentConfigurationException(error_message)
def delete_access_config(self, parameters, instance_id): """ Instructs Google Compute Engine to remove the public IP address from the named instance. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine, and an additional key mapping to a list of instance names that should be deleted. instance_id: A str naming the running instance that the new public IP address should be added to. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.instances().deleteAccessConfig( project=parameters[self.PARAM_PROJECT], accessConfig="External NAT", instance=instance_id, networkInterface="nic0", zone=parameters[self.PARAM_ZONE] ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE])
def create_scratch_disk(self, parameters): """ Creates a disk from a given machine image. GCE does not support scratch disks on API version v1 and higher. We create a persistent disk upon creation to act like one to keep the abstraction used in other infrastructures. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. Returns: A str, the url to the disk to use. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) disk_name = self.generate_disk_name(parameters) project_url = '{0}{1}'.format(self.GCE_URL, parameters[self.PARAM_PROJECT]) source_image_url = '{0}{1}/global/images/{2}'.format(self.GCE_URL, parameters[self.PARAM_PROJECT], parameters[self.PARAM_IMAGE_ID]) request = gce_service.disks().insert( project=parameters[self.PARAM_PROJECT], zone=parameters[self.PARAM_ZONE], body={ 'name':disk_name }, sourceImage=source_image_url ) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT]) disk_url = "{0}/zones/{1}/disks/{2}".format( project_url, parameters[self.PARAM_ZONE], disk_name) return disk_url
def detach_disk(self, parameters, disk_name, instance_id): """ Detaches the persistent disk specified in 'disk_name' from the named instance. Args: parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. disk_name: A str naming the persistent disk to detach. instance_id: A str naming the id of the instance that the disk should be detached from. """ gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) project_id = parameters[self.PARAM_PROJECT] request = gce_service.instances().detachDisk( project=project_id, zone=parameters[self.PARAM_ZONE], instance=instance_id, deviceName='sdb') response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT])
def run_instances(self, count, parameters, security_configured): """ Starts 'count' instances in Google Compute Engine, and returns once they have been started. Callers should create a network and attach a firewall to it before using this method, or the newly created instances will not have a network and firewall to attach to (and thus this method will fail). Args: count: An int that specifies how many virtual machines should be started. parameters: A dict with keys for each parameter needed to connect to Google Compute Engine. security_configured: Unused, as we assume that the network and firewall has already been set up. """ project_id = parameters[self.PARAM_PROJECT] image_id = parameters[self.PARAM_IMAGE_ID] instance_type = parameters[self.PARAM_INSTANCE_TYPE] keyname = parameters[self.PARAM_KEYNAME] group = parameters[self.PARAM_GROUP] zone = parameters[self.PARAM_ZONE] AppScaleLogger.log("Starting {0} machines with machine id {1}, with " \ "instance type {2}, keyname {3}, in security group {4}, in zone {5}" \ .format(count, image_id, instance_type, keyname, group, zone)) # First, see how many instances are running and what their info is. start_time = datetime.datetime.now() active_public_ips, active_private_ips, active_instances = \ self.describe_instances(parameters) # Construct URLs image_url = '{0}{1}/global/images/{2}'.format(self.GCE_URL, project_id, image_id) project_url = '{0}{1}'.format(self.GCE_URL, project_id) machine_type_url = '{0}/zones/{1}/machineTypes/{2}'.format(project_url, zone, instance_type) network_url = '{0}/global/networks/{1}'.format(project_url, group) # Construct the request body for index in range(count): disk_url = self.create_scratch_disk(parameters) instances = { # Truncate the name down to the first 62 characters, since GCE doesn't # let us use arbitrarily long instance names. 'name': '{group}-{uuid}'.format(group=group, uuid=uuid.uuid4())[:62], 'machineType': machine_type_url, 'disks':[{ 'source': disk_url, 'boot': 'true', 'type': 'PERSISTENT' }], 'image': image_url, 'networkInterfaces': [{ 'accessConfigs': [{ 'type': 'ONE_TO_ONE_NAT', 'name': 'External NAT' }], 'network': network_url }], 'serviceAccounts': [{ 'email': self.DEFAULT_SERVICE_EMAIL, 'scopes': [self.GCE_SCOPE] }] } # Create the instance gce_service, credentials = self.open_connection(parameters) http = httplib2.Http() auth_http = credentials.authorize(http) request = gce_service.instances().insert( project=project_id, body=instances, zone=zone) response = request.execute(http=auth_http) AppScaleLogger.verbose(str(response), parameters[self.PARAM_VERBOSE]) self.ensure_operation_succeeds(gce_service, auth_http, response, parameters[self.PARAM_PROJECT]) instance_ids = [] public_ips = [] private_ips = [] end_time = datetime.datetime.now() + datetime.timedelta(0, self.MAX_VM_CREATION_TIME) now = datetime.datetime.now() while now < end_time: AppScaleLogger.log("Waiting for your instances to start...") instance_info = self.describe_instances(parameters) public_ips = instance_info[0] private_ips = instance_info[1] instance_ids = instance_info[2] public_ips = self.diff(public_ips, active_public_ips) private_ips = self.diff(private_ips, active_private_ips) instance_ids = self.diff(instance_ids, active_instances) if count == len(public_ips): break time.sleep(self.SLEEP_TIME) now = datetime.datetime.now() if not public_ips: self.handle_failure('No public IPs were able to be procured ' 'within the time limit') if len(public_ips) != count: for index in range(0, len(public_ips)): if public_ips[index] == '0.0.0.0': instance_to_term = instance_ids[index] AppScaleLogger.log('Instance {0} failed to get a public IP address'\ 'and is being terminated'.format(instance_to_term)) self.terminate_instances([instance_to_term]) end_time = datetime.datetime.now() total_time = end_time - start_time AppScaleLogger.log("Started {0} on-demand instances in {1} seconds" \ .format(count, total_time.seconds)) return instance_ids, public_ips, private_ips