def get_wind_speed(ctx, city_name): ''' Gets wind speed from an external service based on city :param str city_name: Name of a city to check wind speed for. Format is "<City>, <State Abbr.>". :rtype: int :returns: Wind speed in MPH ''' ctx.logger.debug('Executing get_wind_speed(%s)' % city_name) url = 'https://query.yahooapis.com/v1/public/yql?q=' \ 'select wind from weather.forecast where woeid in ' \ '(select woeid from geo.places(1) where text="%s")' \ '&format=json&env=store://datatables.org/alltableswithkeys' \ % city_name ctx.logger.debug('Querying URL "%s"' % url) res = requests.get(url) try: data = res.json() ctx.logger.debug('Response received: %s' % json.dumps(data, indent=2)) speed = data.get('query', {}).get('results', {}).get('channel', {}).get('wind', {}).get('speed') if not speed: raise RecoverableError( 'Missing wind speed data from weather service') return int(speed) except ValueError: raise RecoverableError('Unexpected response from weather service')
def operation_complete(self, op_info): ''' Checks the status of an asynchronous operation :param dict op_info: Long-running operation headers :raises: :exc:`cloudify.exceptions.RecoverableError`, :exc:`cloudify.exceptions.NonRecoverableError`, :exc:`requests.RequestException` ''' # Get the operation ID op_id = op_info.get('x-ms-request-id', 'not-reported') # Make the request self.log.info('Checking status of operation "{0}"'.format(op_id)) op_url = op_info.get('location') or \ op_info.get('azure-asyncoperation') res = self.client.request(method='get', url=op_url) # Convert headers from CaseInsensitiveDict to Dict headers = dict(res.headers) self.log.debug('headers: {0}'.format( utils.secure_logging_content(headers))) # HTTP 200 (OK) - Operation is successful and complete if res.status_code == httplib.OK: if self.validate_res_json(res) == 'InProgress': return ctx.operation.retry( 'Operation: "{0}" still pending'.format( self.get_operation_id(headers)), retry_after=self.get_retry_after(headers)) self.ctx.instance.runtime_properties['async_op'] = None return # Clear the async state self.ctx.instance.runtime_properties['async_op'] = None # HTTP 202 (ACCEPTED) - Operation is still pending if res.status_code == httplib.ACCEPTED: if not headers.get('location'): raise RecoverableError( 'HTTP 202 ACCEPTED but no Location header present') self.ctx.instance.runtime_properties['async_op'] = headers return ctx.operation.retry( 'Operation: "{0}" still pending'.format( self.get_operation_id(headers)), retry_after=self.get_retry_after(headers)) # HTTP 409 (CONFLICT) - Operation failed elif res.status_code == httplib.CONFLICT: if "AnotherOperationInProgress" in res.text: self.log.warn( "Another Operation In Progress, let's wait: {0}".format( headers)) raise RecoverableError('Another Operation In Progress') raise NonRecoverableError( 'Operation "{0}" failed. (code={1}, data={2})'.format( op_id, res.status_code, res.text)) # HTTP 500 (INTERNAL_SERVER_ERROR) - Operation time out elif res.status_code == httplib.INTERNAL_SERVER_ERROR: # Preserve op_info in case it's really just a time-out self.ctx.instance.runtime_properties['async_op'] = op_info return ctx.operation.retry( 'Operation: "{0}" still pending'.format( self.get_operation_id(headers)), retry_after=self.get_retry_after(headers)) res.raise_for_status()
def check_or_create_resource(headers, resource_name, resource_params, check_resource_url, create_resource_url, resource_type, save_response=False): if constants.REQUEST_ACCEPTED in ctx.instance.runtime_properties: if resource_was_created(headers, resource_name, check_resource_url, save_response): ctx.logger.info( "check_or_create_resource resource {0} ({1}) is ready ".format( resource_name, resource_type)) return constants.CREATED_STATUS_CODE else: raise RecoverableError( "check_or_create_resource: resource {0} ({1}) is not ready yet" .format(resource_name, resource_type)) elif create_resource(headers, resource_name, resource_params, create_resource_url, resource_type): if resource_was_created(headers, resource_name, check_resource_url, save_response): ctx.logger.info( "_create_resource resource {0} ({1}) is ready ".format( resource_name, resource_type)) return constants.CREATED_STATUS_CODE else: raise RecoverableError( "check_or_create_resource: resource {0} ({1}) is not ready yet" .format(resource_name, resource_type))
def _make_call(ctx, request_method, url, data, operation_inputs): with _get_cert(ctx, operation_inputs) as cert: result = request_method( url, auth=_get_credentials(ctx, operation_inputs), json=data, verify=cert, ) if result.status_code >= 500: raise RecoverableError('Server is currently unavailable. ' 'Call was to {url}, and ' 'response was {code}: {details}'.format( url=url, code=result.status_code, details=result.text, )) elif result.status_code >= 400: raise RecoverableError('Parameters passed to server were incorrect, ' 'or Nagios is still starting. ' 'Call was to {url}, and ' 'response was {code}: {details}'.format( url=url, code=result.status_code, details=result.text, )) else: return result
def validate_agent_amqp(current_amqp=True, manager_ip=None, manager_certificate=None, **_): """ Validate connectivity between a cloudify agent and an AMQP server :param current_amqp: If set to True, validation is done against the current manager's AMQP. If set to False, validation is done against the old manager's AMQP to which the agent is currently connected. Note: in case of an in-place upgrade, both AMQP servers should be identical :param manager_ip: the IP of the current leader (master) Manager, relevant only in HA cluster. This IP is used to validate that an agent is connected to the Manager's RabbitMQ. :param manager_certificate: the SSL certificate of the current leader (master) Manager. """ if 'cloudify_agent' not in ctx.instance.runtime_properties: raise NonRecoverableError( 'cloudify_agent key not available in runtime_properties') agent = ctx.instance.runtime_properties['cloudify_agent'] _update_broker_config(agent, manager_ip, manager_certificate) validator = _validate_current_amqp if current_amqp else _validate_old_amqp result = validator(agent) result['timestamp'] = time.time() ctx.instance.runtime_properties['agent_status'] = result if current_amqp and not result['agent_alive']: raise RecoverableError(result['agent_alive_error']) if not current_amqp and not result['agent_alive_crossbroker']: raise RecoverableError(result['agent_alive_crossbroker_error'])
def delete(self, name): ''' Deletes an existing resource :param string name: Name of the existing resource :raises: :exc:`cloudify.exceptions.RecoverableError`, :exc:`cloudify.exceptions.NonRecoverableError`, :exc:`requests.RequestException` ''' self.log.info('Deleting {0} "{1}"'.format(self.name, name)) if self.ctx.instance._modifiable: self.ctx.instance.runtime_properties['async_op'] = None # Make the request res = self.client.request(method='delete', url='{0}/{1}'.format(self.endpoint, name)) # Convert headers from CaseInsensitiveDict to Dict headers = dict(res.headers) self.log.debug('headers: {0}'.format( utils.secure_logging_content(headers))) # HTTP 200 (OK) - The operation is successful and complete if res.status_code == httplib.OK: return # HTTP 204 (NO_CONTENT) - The resource doesn't exist elif res.status_code == httplib.NO_CONTENT: return # HTTP 202 (ACCEPTED) - The operation has started but is asynchronous elif res.status_code == httplib.ACCEPTED: if not headers.get('location'): raise RecoverableError( 'HTTP 202 ACCEPTED but no Location header present') if not self.ctx.instance._modifiable: self.log.warn('Not retrying async operation, ' 'because NodeInstanceContext is not modifiable. ' 'headers: {0}'.format(headers)) return self.ctx.instance.runtime_properties['async_op'] = headers return ctx.operation.retry( 'Operation: "{0}" started'.format( self.get_operation_id(headers)), retry_after=self.get_retry_after(headers)) # HTTP 400 (BAD_REQUEST) - We're sending bad data elif res.status_code == httplib.BAD_REQUEST: self.log.info('BAD REQUEST: response: {}'.format( utils.secure_logging_content(res.content))) raise UnexpectedResponse('Recieved HTTP 400 BAD REQUEST', res.json()) # HTTP 409 (CONFLICT) - Operation failed elif res.status_code == httplib.CONFLICT: raise NonRecoverableError( 'Operation failed. (code={0}, data={1})'.format( res.status_code, res.text)) # All other errors will be treated as recoverable elif res.status_code != httplib.CREATED: raise RecoverableError( 'Expected HTTP status code {0}, recieved {1}'.format( httplib.CREATED, res.status_code))
def get(self, name=None): ''' Gets details about an existing resource :param string name: Name of the existing resource :returns: Response data from the Azure API call :rtype: dict :raises: :exc:`cloudify.exceptions.RecoverableError`, :exc:`cloudify.exceptions.NonRecoverableError`, :exc:`requests.RequestException` ''' self.log.info('Retrieving {0} "{1}"'.format(self.name, name)) # Make the request if name: url = '{0}/{1}'.format(self.endpoint, name) else: url = self.endpoint res = self.client.request( method='get', url=url) self.log.debug('headers: {0}'.format( utils.secure_logging_content(dict(res.headers)))) headers = self.lowercase_headers(res.headers) # Check the response # HTTP 202 (ACCEPTED) - The operation has started but is asynchronous if res.status_code == httplib.ACCEPTED: if not headers.get('location'): raise RecoverableError( 'HTTP 202 ACCEPTED but no Location header present') self.ctx.instance.runtime_properties['async_op'] = headers return ctx.operation.retry( 'Operation: "{0}" started' .format(self.get_operation_id(headers)), retry_after=self.get_retry_after(headers)) # HTTP 200 (OK) - The resource already exists elif res.status_code == httplib.OK: return res.json() # If Azure sent a 400, we're sending bad data if res.status_code == httplib.BAD_REQUEST: self.log.info('BAD REQUEST: response: {}'.format(res.content)) raise UnexpectedResponse( 'Recieved HTTP 400 BAD REQUEST', res.json()) # If Azure sent a 404, the resource doesn't exist (yet?) if res.status_code == httplib.NOT_FOUND: raise RecoverableError( '{0} "{1}" doesn\'t exist (yet?)' .format(self.name, name)) # All other errors will be treated as recoverable if res.status_code != httplib.CREATED: raise RecoverableError( 'Expected HTTP status code {0}, recieved {1}' .format(httplib.CREATED, res.status_code)) return res.json()
def create(): ctx.logger.info('Verifying that Salt is installed.') # Check if Salt is running salt_master_service = sudo('systemctl status salt-master.service') if salt_master_service.return_code == 0: ctx.logger.debug('Salt installed and running.') elif salt_master_service.return_code == 3: raise RecoverableError('Salt is installed, but is not running.') else: raise RecoverableError( 'Salt is not installed and not running: {0}'.format(salt_master_service))
def disassociate(self, args=None, **_): """ Disassocates an ENI created by Cloudify from an EC2 Instance that was also created by Cloudify. """ attachment_id = \ ctx.source.instance.runtime_properties.pop('attachment_id') detach_args = dict(attachment_id=attachment_id) detach_args = utils.update_args(detach_args, args) try: output = self.execute(self.client.detach_network_interface, detach_args, raise_on_falsy=True) except (exception.EC2ResponseError, exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) if not output: ctx.source.instance.runtime_properties['attachment_id'] = \ attachment_id raise RecoverableError('Failed to detach network interface {0} ' 'from instance {1}'.format( self.source_resource_id, self.target_resource_id)) return output
def create_agent_amqp(install_agent_timeout=300, manager_ip=None, manager_certificate=None, stop_old_agent=False, **_): """ Installs a new agent on a host machine. :param install_agent_timeout: operation's timeout. :param manager_ip: the private IP of the current leader (master) Manager. This IP is used to connect to the Manager's RabbitMQ. (relevant only in HA cluster) :param manager_certificate: the SSL certificate of the current leader (master) Manager. (relevant only in HA cluster) :param stop_old_agent: if set, stop the old agent after successfully installing the new one """ old_agent = _validate_agent() _update_broker_config(old_agent, manager_ip, manager_certificate) agents = _run_install_script(old_agent, install_agent_timeout) new_agent = agents['new'] ctx.logger.info('Installed agent {0}'.format(new_agent['name'])) result = _validate_current_amqp(new_agent) if not result['agent_alive']: raise RecoverableError('New agent did not start and connect') if stop_old_agent: _stop_old_diamond(old_agent, install_agent_timeout) _stop_old_agent(new_agent, old_agent, install_agent_timeout) # Setting old_cloudify_agent in order to uninstall it later. ctx.instance.runtime_properties['old_cloudify_agent'] = agents['old'] update_agent_runtime_properties(new_agent)
def wrapper(**kwargs): configuration_property = _retrieve_property( ctx.instance, NODE_PROPERTY_CONFIGURATION ) authentication_property = _retrieve_property( ctx.instance, NODE_PROPERTY_AUTHENTICATION ) try: kwargs['client'] = CloudifyKubernetesClient( ctx.logger, KubernetesApiConfigurationVariants( ctx.logger, configuration_property, download_resource=ctx.download_resource ), KubernetesApiAuthenticationVariants( ctx.logger, authentication_property ) ) function(**kwargs) except KuberentesApiInitializationFailedError as e: _, exc_value, exc_traceback = sys.exc_info() raise RecoverableError( '{0}'.format(str(e)), causes=[exception_to_error_cause(exc_value, exc_traceback)] )
def _create_elb(self, args): create_args = self._create_elb_params() create_args = utils.update_args(create_args, args) try: lb = self.execute(self.client.create_load_balancer, create_args, raise_on_falsy=True) except (exception.EC2ResponseError, exception.BotoServerError, exception.BotoClientError) as e: raise RecoverableError('Load Balancer not created ' '{0}'.format(str(e))) if not lb: raise NonRecoverableError( 'Load Balancer not created. While the create ' 'request was completed' ' successfully, verifying the load balancer ' 'afterwards has failed') ctx.instance.runtime_properties['elb_name'] = create_args['name'] self.resource_id = create_args['name'] return lb
def raiseRecoverableError(msg): """ Print a warning message and raise a RecoverableError exception. This is a handy endpoint to add other extended debugging calls. """ warn(msg) raise RecoverableError(msg)
def _execute_initial_state(minion_id): mgr = _instantiate_manager() ctx.logger.info('Connecting to Salt API...') resp, result = mgr.log_in() if not resp.ok: ctx.logger.error('Got response {0}'.format(resp)) raise NonRecoverableError('Unable to connect with Salt API.') ctx.logger.info('Connected to Salt API.') ctx.logger.info('Pinging minion {0}...'.format(minion_id)) for i in xrange(15): time.sleep(2) resp, result = mgr.ping(minion_id) if resp.ok and minion_id in result: break else: raise RecoverableError('{0} does not respond.'.format(minion_id)) ctx.logger.info('Executing highstate on minion {0}...'.format(minion_id)) resp, result = mgr.highstate(minion_id) if not resp.ok: ctx.logger.error('Got response {0}'.format(resp)) raise NonRecoverableError( 'Unable to execute highstate on minion {0}.'.format(minion_id)) ctx.logger.info('Executed highstate on minion {0}.'.format(minion_id)) resp, result = mgr.log_out() if resp.ok: ctx.logger.debug('Token has been cleared') else: ctx.logger.warn('Unable to clear token.')
def test_image_delete(self, glance_m): glance_instance = glance_m.return_value server_ctx = self._simplectx() # still alive glance_instance.images.list = mock.Mock(return_value=[ { 'name': 'vm-server_id-snapshot_name-increment', 'image_type': 'snapshot', 'id': 'abc', 'status': 'active' } ]) server_ctx.operation.retry = mock.Mock( side_effect=RecoverableError()) with self.assertRaises(RecoverableError): nova_plugin.server._image_delete( glance_instance, snapshot_name='vm-server_id-snapshot_name-increment', snapshot_incremental=True) server_ctx.operation.retry.assert_called_with( message='abc is still alive', retry_after=30) # removed glance_instance.images.list = mock.Mock(return_value=[]) nova_plugin.server._image_delete( glance_instance, snapshot_name='snapshot_name', snapshot_incremental=True)
def delete(self, prev_kv=False, return_response=False): key = self.config.get('key') self.logger.debug('Attempting to delete the key: {}'.format(key)) fail_on_overwrite = self.config.get('fail_on_overwrite') if fail_on_overwrite: predelete_value, existing_kvmetadata = self.connection.get(key) if self.config.get('value') != str(predelete_value): return response = self.connection.delete(key, prev_kv=True, return_response=True) if not response.deleted: raise RecoverableError( 'Key: {0} delete unsuccessful'.format(key)) deleted_value = response.prev_kvs[-1].value if deleted_value != str(predelete_value): raise CommandExecutionError( 'etcd3 delete key', 'Deleted another value ({0}) than expected ({1}) for' ' key: {2}'.format(deleted_value, predelete_value, key)) else: response = self.connection.delete(key, prev_kv=prev_kv, return_response=return_response) # TODO: add some check if the deletion was successful self.logger.debug('Deleted key-value pair "{}" with result: {}'.format( key, response)) return response
def custom_resource_delete(client, api_mapping, resource_definition, **kwargs): try: read_response = _do_resource_read(client, api_mapping, _retrieve_id(ctx.instance), **kwargs) ctx.instance.runtime_properties[INSTANCE_RUNTIME_PROPERTY_KUBERNETES] \ = read_response except KuberentesApiOperationError as e: if '"code":404' in str(e): ctx.logger.debug( 'Ignoring error: {0}'.format(str(e))) else: raise RecoverableError( 'Raising error: {0}'.format(str(e))) else: delete_response = _do_resource_delete( client, api_mapping, resource_definition, _retrieve_id(ctx.instance), **kwargs ) raise OperationRetry( 'Delete response: {0}'.format(delete_response))
def get_config_content(filename): with open(filename, 'r') as outfile: try: return yaml.load(outfile) except yaml.YAMLError as e: raise RecoverableError('Unable to read file: {0}: {1}'.format( filename, str(e)))
def stop(retry_interval, params, daemon_client=None, **_): """ cloudify.docker.container type stop lifecycle operation. Stops a container. Similar to the docker stop command. Any properties and runtime_properties set in the create and start lifecycle operations also available in stop. :param daemon_client: optional configuration for client creation :param timeout: Timeout in seconds to wait for the container to stop before sending a SIGKILL. """ daemon_client = daemon_client or {} client = docker_client.get_client(daemon_client) container_id = ctx.instance.runtime_properties['container_id'] ctx.logger.info('Stopping container: {}'.format(container_id)) container_id = ctx.instance.runtime_properties['container_id'] arguments = {'container': container_id} arguments.update(params) ctx.logger.info('Stop arguments: {0}'.format(arguments)) try: client.stop(**arguments) except APIError as e: raise NonRecoverableError( 'Failed to start container: {0}.'.format(str(e))) if 'Exited' not in utils.check_container_status(client): raise RecoverableError('Container still running. Retyring.', retry_after=retry_interval) ctx.logger.info('Stopped container: {}'.format(container_id))
def list_keys(self, name=None): ''' Calls /listKeys :param string name: Name of the existing resource :returns: Response data from the Azure API call :rtype: dict :raises: :exc:`cloudify.exceptions.RecoverableError` ''' # Get the resource name name = name or utils.get_resource_name(self.ctx) url = '{0}/{1}/listKeys'.format(self.endpoint, name) res = self.client.request( method='post', url=url) if res.status_code != httplib.OK: raise RecoverableError( 'Unexpected status returned after calling ' '/listKeys. Status={0}'.format(res.status_code)) keys = res.json() ret = list() for key in keys: ret.append({ 'name': key, 'key': keys[key] }) return ret
def delete(ctx, **_): '''Deletes an AWS API Gateway REST API Deployment''' props = ctx.node.properties # Get a connection to the service client = APIGatewayConnection(ctx.node).client() # Get the parent API api_resource_id = props.get( 'api_id', utils.get_ancestor_resource_id(ctx.instance, NODE_TYPE_API)) # Get the resource ID (must exist at this point) resource_id = utils.get_resource_id() if not resource_id: ctx.logger.warn('Missing resource ID. Skipping workflow...') return # Delete the resource (if needed) if props['use_external_resource']: return if ctx.operation.retry_number == 0: stages = client.get_stages(restApiId=api_resource_id, deploymentId=resource_id) # There are stages left that must be removed if stages and 'item' in stages and len(stages['item']): for item in stages['item']: ctx.logger.debug('Removing deployment stage "%s"' % item['stageName']) client.delete_stage(restApiId=api_resource_id, stageName=item['stageName']) ctx.logger.info('Deleting Deployment "%s"' % resource_id) try: client.delete_deployment(restApiId=api_resource_id, deploymentId=resource_id) ctx.logger.debug('Deployment "%s" deleted' % resource_id) except ClientError as exc: raise RecoverableError('Error deleting Deployment: %s' % str(exc))
def wrapper(**kwargs): try: kwargs['resource_definition'] = \ retrieve_resource_definition(**kwargs) kwargs['api_mapping'] = retrieve_mapping(**kwargs) task(**kwargs) except (KuberentesMappingNotFoundError, KuberentesInvalidPayloadClassError, KuberentesInvalidApiClassError, KuberentesInvalidApiMethodError) as e: raise NonRecoverableError(str(e)) except OperationRetry as e: _, exc_value, exc_traceback = sys.exc_info() raise OperationRetry( '{0}'.format(str(e)), retry_after=15, causes=[exception_to_error_cause(exc_value, exc_traceback)] ) except NonRecoverableError as e: _, exc_value, exc_traceback = sys.exc_info() raise NonRecoverableError( '{0}'.format(str(e)), causes=[exception_to_error_cause(exc_value, exc_traceback)] ) except Exception as e: _, exc_value, exc_traceback = sys.exc_info() raise RecoverableError( '{0}'.format(str(e)), causes=[exception_to_error_cause(exc_value, exc_traceback)] )
def wrapper(**kwargs): configuration_property = _retrieve_property( ctx.instance, NODE_PROPERTY_CONFIGURATION ) authentication_property = _retrieve_property( ctx.instance, NODE_PROPERTY_AUTHENTICATION ) try: kwargs['client'] = CloudifyKubernetesClient( ctx.logger, KubernetesApiConfigurationVariants( ctx.logger, configuration_property, download_resource=ctx.download_resource ), KubernetesApiAuthenticationVariants( ctx.logger, authentication_property ) ) function(**kwargs) except KuberentesApiInitializationFailedError as e: raise RecoverableError(e)
def wrapper(**kwargs): node_property_definition = \ ctx.node.properties[NODE_PROPERTY_DEFINITION] file_resource = \ node_property_definition.pop('file', {}) if file_resource: resource_definition = \ _yaml_from_file(**file_resource) else: resource_definition = node_property_definition if 'kind' not in resource_definition.keys(): node_type = \ ctx.node.type if \ isinstance(ctx.node.type, basestring) else '' resource_definition['kind'] = node_type.split('.')[-1] kwargs['resource_definition'] = \ KubernetesResourceDefinition( **resource_definition) kwargs['mapping'] = KubernetesApiMapping( **ctx.node.properties[NODE_PROPERTY_API_MAPPING] ) try: task(**kwargs) except (KuberentesInvalidPayloadClassError, KuberentesInvalidApiClassError, KuberentesInvalidApiMethodError) as e: raise NonRecoverableError(str(e)) except Exception as e: raise RecoverableError(str(e))
def associate(self, args=None, **_): """ Associates an ENI created by Cloudify with an EC2 Instance that was also created by Cloudify. """ network_interface_id = self.source_resource_id instance_id = self.target_resource_id attachment_args = dict(network_interface_id=network_interface_id, instance_id=instance_id, device_index=1) attachment_args = utils.update_args(attachment_args, args) try: output = self.execute(self.client.attach_network_interface, attachment_args, raise_on_falsy=True) except (exception.EC2ResponseError, exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) if not output: raise RecoverableError( 'Failed to attach eni {0} to instance {1}'.format( self.source_resource_id, self.target_resource_id)) network_interface = self.get_source_resource() ctx.source.instance.runtime_properties['attachment_id'] = \ network_interface.attachment.id return output
def remove_instance_from_elb(**_): elb_name = \ utils.get_external_resource_id_or_raise( 'elb_name', ctx.target.instance) instance_id = \ utils.get_external_resource_id_or_raise( 'instance_id', ctx.source.instance) instance_list = [instance_id] lb = _get_existing_elb(elb_name) ctx.logger.info('Attemping to remove instance: {0} from elb {1}'.format( instance_id, elb_name)) try: lb.deregister_instances(instance_list) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError, boto.exception.BotoClientError) as e: if instance_id in _get_instance_list(): raise RecoverableError('Instance not removed from Load Balancer ' '{0}'.format(str(e))) ctx.logger.info('Instance {0} removed from Load Balancer {1}.'.format( instance_id, elb_name)) _remove_instance_from_elb_list_in_properties(instance_id)
def _create_elb(): ctx.logger.info('Attempting to Create Load Balancer.') params_dict = _create_elb_params() elb_client = connection.ELBConnectionClient().client() try: lb = elb_client.create_load_balancer(**params_dict) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError, boto.exception.BotoClientError) as e: raise RecoverableError('Load Balancer not created ' '{0}'.format(str(e))) if not lb: raise NonRecoverableError( 'Load Balancer not created. While the create request was completed' ' successfully, verifying the load balancer afterwards has failed') ctx.logger.info('Load Balancer Created.') utils.set_external_resource_id(params_dict['name'], ctx.instance, external=False) ctx.instance.runtime_properties['elb_name'] = params_dict['name'] return lb
def attach_volume(nova_client, cinder_client, status_attempts, status_timeout, **kwargs): server_id = get_openstack_id(ctx.target) volume_id = get_openstack_id(ctx.source) if is_external_relationship_not_conditionally_created(ctx): ctx.logger.info('Validating external volume and server ' 'are connected') attachment = volume.get_attachment(cinder_client=cinder_client, volume_id=volume_id, server_id=server_id) if attachment: return else: raise NonRecoverableError( 'Expected external resources server {0} and volume {1} to be ' 'connected'.format(server_id, volume_id)) # Note: The 'device_name' property should actually be a property of the # relationship between a server and a volume; It'll move to that # relationship type once relationship properties are better supported. device = ctx.source.node.properties[volume.DEVICE_NAME_PROPERTY] nova_client.volumes.create_server_volume( server_id, volume_id, device if device != 'auto' else None) try: vol, wait_succeeded = volume.wait_until_status( cinder_client=cinder_client, volume_id=volume_id, status=volume.VOLUME_STATUS_IN_USE, num_tries=status_attempts, timeout=status_timeout ) if not wait_succeeded: raise RecoverableError( 'Waiting for volume status {0} failed - detaching volume and ' 'retrying..'.format(volume.VOLUME_STATUS_IN_USE)) if device == 'auto': # The device name was assigned automatically so we # query the actual device name attachment = volume.get_attachment( cinder_client=cinder_client, volume_id=volume_id, server_id=server_id ) device_name = attachment['device'] ctx.logger.info('Detected device name for attachment of volume ' '{0} to server {1}: {2}' .format(volume_id, server_id, device_name)) ctx.source.instance.runtime_properties[ volume.DEVICE_NAME_PROPERTY] = device_name except Exception, e: if not isinstance(e, NonRecoverableError): _prepare_attach_volume_to_be_repeated( nova_client, cinder_client, server_id, volume_id, status_attempts, status_timeout) raise
def wrapper(*args, **kwargs): _put_api_in_kwargs('fco_api', kwargs) try: return f(*args, **kwargs) except fco_exceptions.NonRecoverableError as e: raise NonRecoverableError(str(e)) except fco_exceptions.RecoverableError as e: raise RecoverableError(str(e), retry_after=e.retry_after)
def _re_raise(e, recoverable, retry_after=None): exc_type, exc, traceback = sys.exc_info() if recoverable: if retry_after == 0: retry_after = None raise RecoverableError(message=e.message, retry_after=retry_after), None, traceback else: raise NonRecoverableError(e.message), None, traceback