def post_init(self): """Initialize EC2 cloud framework class.""" self.service_account_file = ( self.custom_args.get('service_account_file') or self.ipa_config['service_account_file'] ) if not self.service_account_file: raise GCECloudException( 'Service account file is required to connect to GCE.' ) else: self.service_account_file = os.path.expanduser( self.service_account_file ) if not self.ssh_private_key_file: raise GCECloudException( 'SSH private key file is required to connect to instance.' ) self.ssh_user = self.ssh_user or GCE_DEFAULT_USER self.ssh_public_key = self._get_ssh_public_key() self.image_project = self.custom_args.get('image_project') self.sev_capable = self.custom_args.get('sev_capable') self.use_gvnic = self.custom_args.get('use_gvnic') self.credentials = self._get_credentials() self.compute_driver = self._get_driver() self._validate_region()
def __init__(self, cleanup=None, config=None, description=None, distro_name=None, early_exit=None, history_log=None, image_id=None, inject=None, instance_type=None, log_level=None, no_default_test_dirs=False, cloud_config=None, region=None, results_dir=None, running_instance_id=None, service_account_file=None, ssh_private_key_file=None, ssh_user=None, subnet_id=None, test_dirs=None, test_files=None, timeout=None, collect_vm_info=None, image_project=None, enable_secure_boot=None, enable_uefi=None, log_callback=None): super(GCECloud, self).__init__('gce', cleanup, config, description, distro_name, early_exit, history_log, image_id, inject, instance_type, log_level, no_default_test_dirs, cloud_config, region, results_dir, running_instance_id, test_dirs, test_files, timeout, collect_vm_info, ssh_private_key_file, ssh_user, subnet_id, enable_secure_boot, enable_uefi, log_callback) self.service_account_file = (service_account_file or self.ipa_config['service_account_file']) if not self.service_account_file: raise GCECloudException( 'Service account file is required to connect to GCE.') else: self.service_account_file = os.path.expanduser( self.service_account_file) if not self.ssh_private_key_file: raise GCECloudException( 'SSH private key file is required to connect to instance.') self.ssh_user = self.ssh_user or GCE_DEFAULT_USER self.ssh_public_key = self._get_ssh_public_key() self.image_project = image_project self.credentials = self._get_credentials() self.compute_driver = self._get_driver() self._validate_region()
def _validate_region(self): """Validate region was passed in and is a valid GCE zone.""" if not self.region: raise GCECloudException( 'Zone is required for GCE cloud framework: ' 'Example: us-west1-a') try: zone = self.compute_driver.zones().get( project=self.service_account_project, zone=self.region).execute() except Exception: zone = None if not zone: raise GCECloudException( '{region} is not a valid GCE zone. ' 'Example: us-west1-a'.format(region=self.region))
def handle_gce_http_errors(type_name, resource_name): """ Context manager to handle GCE HTTP Errors. """ try: yield except Exception as error: message = get_message_from_http_error(error, resource_name) raise GCECloudException( 'Unable to retrieve {type_name}: {error}'.format( type_name=type_name, error=message)) from error
def _get_credentials(self): """Retrieve credentials object using service account file.""" with open(self.service_account_file, 'r') as f: info = json.load(f) self.service_account_email = info.get('client_email') if not self.service_account_email: raise GCECloudException( 'Service account JSON file is invalid for GCE. ' 'client_email key is expected. See getting started ' 'docs for information on GCE configuration.') self.service_account_project = info.get('project_id') if not self.service_account_project: raise GCECloudException( 'Service account JSON file is invalid for GCE. ' 'project_id key is expected. See getting started ' 'docs for information on GCE configuration.') return service_account.Credentials.from_service_account_file( self.service_account_file)
def _set_instance_ip(self): """Retrieve and set the instance ip address.""" instance = self._get_instance() interface = instance['networkInterfaces'][0] try: self.instance_ip = interface['accessConfigs'][0]['natIP'] except (KeyError, IndexError): try: self.instance_ip = interface['networkIP'] except KeyError: raise GCECloudException( 'IP address for instance: %s cannot be found.' % self.running_instance_id)
def _launch_instance(self): """Launch an instance of the given image.""" self.running_instance_id = self._generate_instance_name() self.logger.debug('ID of instance: %s' % self.running_instance_id) machine_type = self._get_instance_type( self.instance_type or GCE_DEFAULT_TYPE )['selfLink'] source_image = self._get_image(self.image_id)['selfLink'] network_interfaces = [ self._get_network_config(self.subnet_id, self.use_gvnic) ] kwargs = { 'instance_name': self.running_instance_id, 'machine_type': machine_type, 'service_account_email': self.service_account_email, 'source_image': source_image, 'ssh_key': self.ssh_public_key, 'network_interfaces': network_interfaces, 'sev_capable': self.sev_capable, 'use_gvnic': self.use_gvnic, } if self.enable_uefi: kwargs['shielded_instance_config'] = \ self.get_shielded_instance_config( enable_secure_boot=self.enable_secure_boot ) try: response = self.compute_driver.instances().insert( project=self.service_account_project, zone=self.region, body=self.get_instance_config(**kwargs) ).execute() except HttpError as error: with suppress(AttributeError): # In python 3.5 content is bytes error.content = error.content.decode() error_obj = json.loads(error.content)['error'] try: message = error_obj['message'] except (AttributeError, KeyError): message = 'Unknown exception.' if error_obj['code'] == 412: # 412 is conditionNotmet error_class = IpaRetryableError else: error_class = GCECloudException raise error_class( 'Failed to launch instance: {message}'.format( message=message ) ) from error except Exception as error: raise GCECloudException( 'Failed to launch instance: {message}'.format( message=str(error) ) ) from error operation = self._wait_on_operation(response['name']) if 'error' in operation and operation['error'].get('errors'): error = operation['error']['errors'][0] if error['code'] in ('QUOTA_EXCEEDED', 'PRECONDITION_FAILED'): error_class = IpaRetryableError else: error_class = GCECloudException raise error_class( 'Failed to launch instance: {message}'.format( message=error['message'] ) ) self._wait_on_instance( 'RUNNING', timeout=self.timeout )