def check_wazuh_status(self): """ There are some services that are required for wazuh to correctly process API requests. If any of those services is not running, the API must raise an exception indicating that: * It's not ready yet to process requests if services are restarting * There's an error in any of those services that must be addressed before using the API if any service is in failed status. * Wazuh must be started before using the API is the services are stopped. The basic services wazuh needs to be running are: wazuh-modulesd, wazuh-remoted, wazuh-analysisd, wazuh-execd and wazuh-db """ if self.f == wazuh.core.manager.status: return status = wazuh.core.manager.status() not_ready_daemons = { k: status[k] for k in self.basic_services if status[k] in ('failed', 'restarting', 'stopped') } if not_ready_daemons: extra_info = { 'node_name': self.node_info.get('node', 'UNKNOWN NODE'), 'not_ready_daemons': ', '.join([ f'{key}->{value}' for key, value in not_ready_daemons.items() ]) } raise exception.WazuhError(1017, extra_message=extra_info)
def get_connected_nodes(self, filter_node: str = None, offset: int = 0, limit: int = common.database_limit, sort: Dict = None, search: Dict = None, select: Dict = None, filter_type: str = 'all') -> Dict: """ Return all connected nodes, including the master node :return: A dictionary containing data from each node """ def return_node(node_info: Dict) -> bool: """ Returns whether the node must be added to the result or not :param node_info: Node information :return: A boolean """ return (filter_node is None or node_info['name'] in filter_node) and ( filter_type == 'all' or node_info['type'] == filter_type) default_fields = self.to_dict()['info'].keys() if select is None: select = default_fields else: if not set(select).issubset(default_fields): raise exception.WazuhError(code=1724, extra_message=', '.join(set(select) - default_fields), extra_remediation=', '.join(default_fields)) if filter_type != 'all' and filter_type not in {'worker', 'master'}: raise exception.WazuhError(1728) if filter_node is not None: filter_node = set(filter_node) if isinstance(filter_node, list) else {filter_node} if not filter_node.issubset(set(itertools.chain(self.clients.keys(), [self.configuration['node_name']]))): raise exception.WazuhResourceNotFound(1730) res = [val.to_dict()['info'] for val in itertools.chain([self], self.clients.values()) if return_node(val.to_dict()['info'])] return utils.process_array([{k: v[k] for k in select} for v in res], search_text=search['value'] if search is not None else None, complementary_search=search['negation'] if search is not None else False, sort_by=sort['fields'] if sort is not None else None, sort_ascending=False if sort is not None and sort['order'] == 'desc' else True, allowed_sort_fields=default_fields, offset=offset, limit=limit)
async def distribute_function(self) -> [Dict, exception.WazuhException]: """ Distribute an API call. Returns ------- dict or WazuhException Dictionary with API response or WazuhException in case of error. """ try: self.logger.debug("Receiving parameters {}".format(self.f_kwargs)) is_dapi_enabled = self.cluster_items['distributed_api']['enabled'] is_cluster_disabled = self.node == local_client and wazuh.core.cluster.cluster.check_cluster_status( ) # If it is a cluster API request and the cluster is not enabled, raise an exception if is_cluster_disabled and self.cluster_required: raise exception.WazuhError(3013) # First case: execute the request locally. # If the distributed api is not enabled # If the cluster is disabled or the request type is local_any # if the request was made in the master node and the request type is local_master # if the request came forwarded from the master node and its type is distributed_master if not is_dapi_enabled or is_cluster_disabled or self.request_type == 'local_any' or \ (self.request_type == 'local_master' and self.node_info['type'] == 'master') or \ (self.request_type == 'distributed_master' and self.from_cluster): response = await self.execute_local_request() # Second case: forward the request # Only the master node will forward a request, and it will only be forwarded if its type is distributed_ # master elif self.request_type == 'distributed_master' and self.node_info[ 'type'] == 'master': response = await self.forward_request() # Last case: execute the request remotely. # A request will only be executed remotely if it was made in a worker node and its type isn't local_any else: response = await self.execute_remote_request() try: response = json.loads(response, object_hook=c_common.as_wazuh_object) \ if isinstance(response, str) else response except json.decoder.JSONDecodeError: response = {'message': response} return response if isinstance(response, (wresults.AbstractWazuhResult, exception.WazuhException)) \ else wresults.WazuhResult(response) except exception.WazuhError as e: e.dapi_errors = self.get_error_info(e) return e except exception.WazuhInternalError as e: e.dapi_errors = self.get_error_info(e) if self.debug: raise self.logger.error(f'{e.message}', exc_info=True) return e except Exception as e: if self.debug: raise self.logger.error(f'Unhandled exception: {str(e)}', exc_info=True) return exception.WazuhInternalError( 1000, dapi_errors=self.get_error_info(e))
def get_connected_nodes(self, filter_node: str = None, offset: int = 0, limit: int = common.database_limit, sort: Dict = None, search: Dict = None, select: Dict = None, filter_type: str = 'all') -> Dict: """Get all connected nodes, including the master node. Parameters ---------- filter_node : str, list Node to return. offset : int First element to return. limit : int Maximum number of elements to return. sort : dict Sorts the collection by a field or fields. search : dict Looks for elements with the specified string. select : dict Select which fields to return (separated by comma). filter_type : str Type of node (worker/master). Returns ------- dict Data from each node. """ def return_node(node_info: Dict) -> bool: """Return whether the node must be added to the result or not. Parameters ---------- node_info : dict Node information. Returns ------- bool Whether the node must be added to the result or not. """ return (filter_node is None or node_info['name'] in filter_node) and (filter_type == 'all' or node_info['type'] == filter_type) default_fields = self.to_dict()['info'].keys() if select is None: select = default_fields else: if not set(select).issubset(default_fields): raise exception.WazuhError( code=1724, extra_message=', '.join(set(select) - default_fields), extra_remediation=', '.join(default_fields)) if filter_type != 'all' and filter_type not in {'worker', 'master'}: raise exception.WazuhError(1728) if filter_node is not None: filter_node = set(filter_node) if isinstance( filter_node, list) else {filter_node} if not filter_node.issubset( set( itertools.chain(self.clients.keys(), [self.configuration['node_name']]))): raise exception.WazuhResourceNotFound(1730) res = [ val.to_dict()['info'] for val in itertools.chain([self], self.clients.values()) if return_node(val.to_dict()['info']) ] return utils.process_array( [{k: v[k] for k in select} for v in res], search_text=search['value'] if search is not None else None, complementary_search=search['negation'] if search is not None else False, sort_by=sort['fields'] if sort is not None else None, sort_ascending=False if sort is not None and sort['order'] == 'desc' else True, allowed_sort_fields=default_fields, offset=offset, limit=limit)