def _start_existing_instance(self, status): """ Start a existing instance. Args: status (str): Status of the instance. """ # Waiting for the instance stop if currently stopping if status == self.STATUS_STOPPING: with _utl.Timeout(self.TIMEOUT, sleep=self._TIMEOUT_SLEEP) as timeout: while True: # Get instance status status = self._status() if status != self.STATUS_STOPPING: break elif timeout.reached(): raise _exc.HostRuntimeException( gen_msg=('timeout_status', 'stop', status)) # If instance stopped, starts it if status == self.STATUS_STOPPED: with _exception_handler(): self._instance.start() # If another status, raises error elif status != self.STATUS_RUNNING: raise _exc.HostRuntimeException(gen_msg=('unable_to_status', 'start', status))
def _instance_request(self, action_name, status_desc='', **parameters): """ Changes instance status. Does it in a retry loop to be sure to have the correct status when performing action. Getting status information seem to not always return an up to date information and is not reliable to perform requests without error. see "AlibabaCSP._request" for more information on requests and parameters. Args: action_name (str): Action name. status_desc (str): Status name for description (starting, stopping, ...) parameters: Request extra parameters. Returns: dict: Request response. """ instance_id = parameters.pop('InstanceId', self._instance_id) with _utl.Timeout(self.TIMEOUT, sleep=self._TIMEOUT_SLEEP) as timeout: while True: # Tries to execute requests try: return self._request( action_name, error_code_filter='IncorrectInstanceStatus', InstanceId=instance_id, **parameters) # If incorrect instance status, waits and retries except _acs_exceptions.ServerException: if timeout.reached(): raise _exc.HostRuntimeException(gen_msg=( 'timeout', status_desc))
def _attach_instance_profile_role(self): """ Attach IAM role to IAM instance profile. """ # Attach role to instance profile with _exception_handler(filter_error_codes='LimitExceeded'): with _utl.Timeout(self.TIMEOUT, sleep=self._TIMEOUT_SLEEP) as timeout: while True: try: self._iam_client.add_role_to_instance_profile( InstanceProfileName=self._instance_profile_name, RoleName=self._role) break # Some time, instance_profile is not ready immediately except _ClientError as exception: if (exception.response['Error']['Code'] == 'NoSuchEntityException'): if timeout.reached(): raise _exc.HostRuntimeException(gen_msg=( 'timeout', 'IAM instance_profile and role attachment' )) continue raise _get_logger().debug( _utl.gen_msg('attached_to', 'IAM role', self._role, 'IAM instance profile', self._instance_profile_name))
def _get_private_ip(self): """ Read current instance private IP from CSP instance. Returns: str: IP address """ try: return self._get_instance()['PrivateIpAddress']['IpAddress'][0] except (KeyError, IndexError): raise _exc.HostRuntimeException(gen_msg='no_instance_ip')
def _get_status(self): """ Returns current status of current instance. Returns: str: Status """ try: return self._get_instance()['Status'] except KeyError: raise _exc.HostRuntimeException( gen_msg=('no_instance_id', self._instance_id))
def _get_instance(self): """ Returns current instance. Returns: dict: Instance description """ response = self._request( 'DescribeInstances', InstanceIds=[self._instance_id]) try: return response['Instances']['Instance'][0] except (KeyError, IndexError): raise _exc.HostRuntimeException( gen_msg=('no_instance_id', self._instance_id))
def _status(self): """ Returns current status of current instance. Returns: str: Status Raises: apyfal.exceptions.HostRuntimeException: No instance from which get status. """ if self._instance_id is None: raise _exc.HostRuntimeException(gen_msg='no_instance') # Update instance self._instance = self._get_instance() if self._instance is None: raise _exc.HostRuntimeException(gen_msg=('no_instance_id', self._instance_id)) # Read instance status return self._get_status()
def private_ip(self): """ Private IP of the current instance. Returns: str: IP address Raises: apyfal.exceptions.HostRuntimeException: No instance from which get IP. """ if self._instance is None: raise _exc.HostRuntimeException(gen_msg='no_instance') return self._get_private_ip()
def _get_public_ip(self): """ Read current instance public IP from CSP instance. Returns: str: IP address """ with _utl.Timeout(self.TIMEOUT, sleep=self._TIMEOUT_SLEEP) as timeout: while True: for address in list( self._get_instance().addresses.values())[0]: if address['version'] == 4: return address['addr'] if timeout.reached(): raise _exc.HostRuntimeException(gen_msg='no_instance_ip')
def _wait_instance_ready(self): """ Waits until instance is ready. """ warned = False # Waiting for the instance provisioning with _utl.Timeout(self.TIMEOUT, sleep=self._TIMEOUT_SLEEP) as timeout: while True: # Get instance status status = self._status() if status == self.STATUS_RUNNING: return elif status == self.STATUS_ERROR: raise _exc.HostRuntimeException( gen_msg=('unable_to_status', "provision", status)) elif timeout.reached(): raise _exc.HostRuntimeException(gen_msg=('timeout_status', "provisioning", status)) elif not warned: # Avoid to show message if already booted warned = True _get_logger().info("Waiting instance provisioning...")
def _wait_instance_ready(self): """ Waits until instance is ready. """ try: _CSPHost._wait_instance_ready(self) except _exc.HostException as exception: # Get extra information about error if possible try: raise _exc.HostRuntimeException( exception.args[0], exc=self._get_instance().fault['message']) # If not extra information, re raise previous error except (AttributeError, _nova_exc.ClientException): raise exception
def _exception_handler(to_catch=_ClientError, to_raise=None, filter_error_codes=None, exception_msg=None, **exc_kwargs): """ Context manager that catch AWS EC2 exceptions and raises Apyfal exceptions. Args: to_catch (Exception sub class or tuple of Exception): Exception to catch. ClientError if not specified. to_raise (apyfal.exception.AcceleratorException subclass): Exception to raise. apyfal.exceptions.HostRuntimeException if not specified. filter_error_codes (str or tuple of str): Don't raise exception if error code in this argument. exception_msg (str): Exception message. exc_kwargs: Exception to raise arguments. """ # Performs operation try: yield # Catch specified exceptions except to_catch as exception: # Try to get error code and message try: error_dict = exception.response['Error'] error_code = error_dict['Code'] except (AttributeError, KeyError): raise _exc.HostRuntimeException(exception_msg, exc=exception) # Converts single str to tuple if filter_error_codes is None: filter_error_codes = () elif isinstance(filter_error_codes, str): filter_error_codes = (filter_error_codes, ) # Raises if not in filter if error_code not in filter_error_codes: exception = to_raise or _exc.HostRuntimeException raise exception(exception_msg, exc=error_dict['Message'], **exc_kwargs)
def _wait_instance_boot(self): """ Waits until instance has booted and webservice is OK Raises: apyfal.exceptions.HostRuntimeException: Timeout while booting. """ if not self.ALLOW_PORTS or _utl.check_port(self.host_ip, 80): # Avoid to show message if already booted or not return _get_logger().info("Waiting instance boot...") _sleep(self._TIMEOUT_SLEEP) if not _utl.check_port( self.host_ip, 80, timeout=self.TIMEOUT, sleep=self._TIMEOUT_SLEEP): raise _exc.HostRuntimeException(gen_msg=('timeout', "boot"))
def _get_status(self): """ Returns current status of current instance. Returns: str: Status """ with _utl.Timeout(1, sleep=0.01) as timeout: while True: # Check Timeout if timeout.reached(): raise _exc.HostRuntimeException( gen_msg=('no_instance_id', self._instance_id)) # Get status with _exception_handler( filter_error_codes='InvalidInstanceID.NotFound'): return self._instance.state["Name"]
def start(self, accelerator=None, accel_parameters=None, stop_mode=None, image_id=None, instance_type=None): """ Start instance if not already started. Create instance if necessary. Needs "accel_client" or "accel_parameters". Args: accelerator (str): Name of the accelerator. accel_parameters (dict): Can override parameters from accelerator client. image_id (str): Force the use of specified image ID. instance_type (str): Force the use of specified instance type. stop_mode (str or int): See "stop_mode" property for more information. """ # Updates stop mode self.stop_mode = stop_mode # Starts instance only if not already started if self._host_ip is None: # Get parameters from accelerator self._set_accelerator_requirements( accelerator=accelerator, accel_parameters=accel_parameters, image_id=image_id, instance_type=instance_type) # Checks CSP credential self._check_credential() # Creates and starts instance if not exists if self.instance_id is None: _get_logger().info("Configuring host on %s instance...", self._host_type) with self._stop_silently_on_exception(): self._create_instance() with self._stop_silently_on_exception(): self._instance, self._instance_id = \ self._start_new_instance() _get_logger().debug( _utl.gen_msg('created_named', 'instance', self._instance_id)) # If exists, starts it directly else: self._start_existing_instance(self._status()) # Waiting for instance provisioning with self._stop_silently_on_exception(): self._wait_instance_ready() # Update instance URL self._host_ip = self.host_ip self._url = _utl.format_url(self._host_ip, force_secure=bool(self._ssl_cert_crt)) # Waiting for the instance to boot self._wait_instance_boot() _get_logger().info("Host ready") # If Host IP exists exists, checks if reachable elif self.ALLOW_PORTS and not _utl.check_port(self.host_ip, 80): raise _exc.HostRuntimeException(gen_msg=('unable_reach_port', self.host_ip, 80))