Source code for cloudify_rest_client.nodes

import warnings

from cloudify_rest_client import utils
from cloudify_rest_client.exceptions import CloudifyClientError
from cloudify_rest_client.responses import ListResponse


[docs] class Node(dict): """ Cloudify node. """ @property def id(self): """ :return: The identifier of the node. """ return self.get('id') @property def deployment_id(self): """ :return: The deployment id the node belongs to. """ return self.get('deployment_id') @property def created_by(self): """ :return: The name of the node creator. """ return self.get('created_by') @property def properties(self): """ :return: The static properties of the node. """ return self.get('properties') @property def operations(self): """ :return: The node operations mapped to plugins. :rtype: dict """ return self.get('operations') @property def relationships(self): """ :return: The node relationships with other nodes. :rtype: list """ return self.get('relationships') @property def blueprint_id(self): """ :return: The id of the blueprint this node belongs to. :rtype: str """ return self.get('blueprint_id') @property def plugins(self): """ :return: The plugins this node has operations mapped to. :rtype: dict """ return self.get('plugins') @property def number_of_instances(self): """ :return: The number of instances this node has. :rtype: int """ return int(self.get( 'number_of_instances')) if 'number_of_instances' in self else None @property def planned_number_of_instances(self): """ :return: The planned number of instances this node has. :rtype: int """ return int(self.get( 'planned_number_of_instances')) if 'planned_number_of_instances' \ in self else None @property def deploy_number_of_instances(self): """ :return: The number of instances this set for this node when the deployment was created. :rtype: int """ return int(self.get( 'deploy_number_of_instances')) if 'deploy_number_of_instances' \ in self else None @property def unavailable_instances(self): """Amount of instances that failed their status check.""" return self.get('unavailable_instances') @property def drifted_instances(self): """Amount of instances that have configuration drift.""" return self.get('drifted_instances') @property def host_id(self): """ :return: The id of the node instance which hosts this node. :rtype: str """ return self.get('host_id') @property def type_hierarchy(self): """ :return: The type hierarchy of this node. :rtype: list """ return self['type_hierarchy'] @property def type(self): """ :return: The type of this node. :rtype: str """ return self['type']
[docs] class NodeTypes(dict): def __init__(self, node_type): super(NodeTypes, self).__init__() self.update(node_type) @property def id(self): """ID of the node.""" return self['id'] @property def deployment_id(self): """ID of the deployment the node belong to.""" return self['deployment_id'] @property def type(self): """Node's type.""" return self['type']
[docs] class NodesClient(object): def __init__(self, api): self.api = api self._wrapper_cls = Node self._uri_prefix = 'nodes' self.types = NodeTypesClient(api) def _create_filters( self, deployment_id=None, node_id=None, sort=None, is_descending=False, evaluate_functions=False, **kwargs ): params = {'_evaluate_functions': evaluate_functions} if deployment_id: params['deployment_id'] = deployment_id if node_id: warnings.warn("'node_id' filtering capability is deprecated, use" " 'id' instead", DeprecationWarning) params['id'] = node_id params.update(kwargs) if sort: params['_sort'] = '-' + sort if is_descending else sort return params
[docs] def list(self, _include=None, filter_rules=None, constraints=None, **kwargs): """ Returns a list of nodes which belong to the deployment identified by the provided deployment id. :param deployment_id: The deployment's id to list nodes for. :param node_id: If provided, returns only the requested node. This parameter is deprecated, use 'id' instead. :param _include: List of fields to include in response. :param sort: Key for sorting the list. :param is_descending: True for descending order, False for ascending. :param constraints: A list of DSL constraints for node_template data type to filter the nodes by. :param kwargs: Optional filter fields. for a list of available fields see the REST service's models.DeploymentNode.fields :param evaluate_functions: Evaluate intrinsic functions :return: Nodes. :rtype: list """ if constraints and filter_rules: raise ValueError( 'provide either filter_rules or DSL constraints, not both') params = self._create_filters(**kwargs) if filter_rules is not None: if _include: params['_include'] = ','.join(_include) response = self.api.post( '/searches/{self._uri_prefix}'.format(self=self), params=params, data={'filter_rules': filter_rules} ) elif constraints is not None: if _include: params['_include'] = ','.join(_include) response = self.api.post( '/searches/{self._uri_prefix}'.format(self=self), params=params, data={'constraints': constraints} ) else: response = self.api.get( '/{self._uri_prefix}'.format(self=self), params=params, _include=_include ) return ListResponse( [self._wrapper_cls(item) for item in response['items']], response['metadata'] )
[docs] def get( self, deployment_id, node_id, _include=None, evaluate_functions=False, instance_context=None, ): """ Returns the node which belongs to the deployment identified by the provided deployment id . :param deployment_id: The deployment's id of the node. :param node_id: The node id. :param _include: List of fields to include in response. :param evaluate_functions: Evaluate intrinsic functions :param instance_context: When evaluating functions, do it in context of this node instance id, i.e. get_attribute calls in node properties can be treated as if they were on the node instance with the given id. :return: Nodes. :rtype: Node """ assert deployment_id assert node_id params = { 'deployment_id': deployment_id, 'id': node_id, '_evaluate_functions': evaluate_functions, '_instance_context': instance_context, } if _include: params['_include'] = ','.join(_include) response = self.api.get( '/{self._uri_prefix}'.format(self=self), params=params, _include=_include, ) if not response.get('items'): return None return self._wrapper_cls(response['items'][0])
[docs] def create_many(self, deployment_id, nodes): """Create multiple nodes. :param deployment_id: the new nodes belong to this deployment :param nodes: list of dicts representing the nodes to be created. Each node dict must contain at least the keys: id, type. :return: None """ self.api.post( '/{self._uri_prefix}'.format(self=self), data={ 'deployment_id': deployment_id, 'nodes': nodes }, expected_status_code=(201, 204), )
[docs] def update(self, deployment_id, node_id, **kwargs): """Update a node with new attributes. This is only useful from the deployment-update workflow: updating node attributes will do nothing else by itself (it won't re-install the existing instances, or re-establish relationships, etc.) :param deployment_id: The deployment the node belongs to :param node_id: The node id within the given deployment :param kwargs: The new node attributes """ self.api.patch( '/{self._uri_prefix}/{deployment_id}/{node_id}' .format(self=self, deployment_id=deployment_id, node_id=node_id), data=kwargs, expected_status_code=204, )
[docs] def delete(self, deployment_id, node_id): """Delete a node :param deployment_id: The deployment the node belongs to :param node_id: The node id within the given deployment """ self.api.delete( '/{self._uri_prefix}/{deployment_id}/{node_id}' .format(self=self, deployment_id=deployment_id, node_id=node_id), expected_status_code=204, )
[docs] def dump(self, deployment_ids=None, node_ids=None): """Generate nodes' attributes for a snapshot. :param deployment_ids: A list of deployments' identifiers used to select nodes to be dumped, should not be empty. :param node_ids: A list of nodes' identifiers, if not empty, used to select specific nodes to be dumped. :returns: A generator of dictionaries, which describe nodes' attributes. """ if not deployment_ids: return for deployment_id in deployment_ids: for entity in utils.get_all( self.api.get, '/nodes', params={'deployment_id': deployment_id}, _include=['id', 'host_id', 'plugins', 'plugins_to_install', 'properties', 'max_number_of_instances', 'min_number_of_instances', 'planned_number_of_instances', 'deploy_number_of_instances', 'relationships', 'operations', 'type', 'type_hierarchy', 'visibility', 'created_by', 'number_of_instances'], ): if not node_ids or entity['id'] in node_ids: yield {'__entity': entity, '__source_id': deployment_id}
[docs] def restore(self, entities, logger, deployment_id): """Restore nodes from a snapshot. :param entities: An iterable (e.g. a list) of dictionaries describing nodes to be restored. :param logger: A logger instance. :param deployment_id: A deployment identifier for the entities. """ for entity in entities: entity['creator'] = entity.pop('created_by') try: self.create_many(deployment_id, entities) except CloudifyClientError as exc: logger.error(f"Error restoring nodes: {exc}")
[docs] class NodeTypesClient(object): def __init__(self, api): self.api = api
[docs] def list(self, node_type=None, constraints=None, **kwargs): """ Returns a list of node's types matching constraints. :param deployment_id: An identifier of a deployment which nodes are going to be searched. If omitted, 'deployment_id' key should be present in the `constraints` dict, otherwise the request will fail. :param node_type: If provided, returns only the requested type. :param constraints: A list of DSL constraints for node_type data type to filter the types by. :param kwargs: Optional filter fields. for a list of available fields see the REST service's models.Node.fields :return: NodeTypes list. """ params = kwargs if node_type: params['_search'] = node_type if constraints is None: constraints = dict() response = self.api.post('/searches/node-types', params=params, data={'constraints': constraints}) return ListResponse( items=[NodeTypes(item) for item in response['items']], metadata=response['metadata'] )