diff --git a/fedn/fedn/common/tracer/mongotracer.py b/fedn/fedn/common/tracer/mongotracer.py index 92af569ea..99725c2d9 100644 --- a/fedn/fedn/common/tracer/mongotracer.py +++ b/fedn/fedn/common/tracer/mongotracer.py @@ -1,4 +1,5 @@ import uuid +from datetime import datetime from google.protobuf.json_format import MessageToDict @@ -18,6 +19,7 @@ def __init__(self, mongo_config, network_id): self.rounds = self.mdb['control.rounds'] self.sessions = self.mdb['control.sessions'] self.validations = self.mdb['control.validations'] + self.clients = self.mdb['network.clients'] except Exception as e: print("FAILED TO CONNECT TO MONGO, {}".format(e), flush=True) self.status = None @@ -82,3 +84,14 @@ def set_round_data(self, round_data): """ self.rounds.update_one({'round_id': str(round_data['round_id'])}, { '$push': {'reducer': round_data}}, True) + + + def update_client_timestamp(self, client_name): + datetime_now = datetime.now() + + filter_query = {"name": client_name} # Replace with the desired name + + # Define the update operation + update_query = {"$set": {"last_seen": datetime_now}} # Replace with the property and value to update + + self.clients.update_one(filter_query, update_query) \ No newline at end of file diff --git a/fedn/fedn/network/api/interface.py b/fedn/fedn/network/api/interface.py index effcaa05d..b4bdabc70 100644 --- a/fedn/fedn/network/api/interface.py +++ b/fedn/fedn/network/api/interface.py @@ -1,19 +1,16 @@ import base64 import copy - import os import threading from io import BytesIO from flask import jsonify, send_from_directory from werkzeug.utils import secure_filename -from fedn.network.dashboard.plots import Plot from fedn.common.config import get_controller_config, get_network_config -from fedn.network.combiner.interfaces import ( - CombinerInterface, - CombinerUnavailableError, -) +from fedn.network.combiner.interfaces import (CombinerInterface, + CombinerUnavailableError) +from fedn.network.dashboard.plots import Plot from fedn.network.state import ReducerState, ReducerStateToString from fedn.utils.checksum import sha @@ -66,27 +63,32 @@ def _allowed_file_extension( and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS ) - def get_all_clients(self): + def get_clients(self, limit=None, skip=None, active_only=False): """Get all clients from the statestore. :return: All clients as a json response. :rtype: :class:`flask.Response` """ # Will return list of ObjectId - clients_objects = self.statestore.list_clients() - payload = {} - for object in clients_objects: - id = object["name"] - info = { - "combiner": object["combiner"], - "combiner_preferred": object["combiner_preferred"], - "ip": object["ip"], - "updated_at": object["updated_at"], - "status": object["status"], + response = self.statestore.list_clients(limit, skip, active_only) + + arr = [] + + for element in response["result"]: + obj = { + "id": element["name"], + "combiner": element["combiner"], + "combiner_preferred": element["combiner_preferred"], + "ip": element["ip"], + "updated_at": element["updated_at"], + "last_seen": element["last_seen"], } - payload[id] = info - return jsonify(payload) + arr.append(obj) + + result = {"result": , "count": response["count"]} + + return jsonify(result) def get_active_clients(self, combiner_id): """Get all active clients, i.e that are assigned to a combiner. diff --git a/fedn/fedn/network/api/server.py b/fedn/fedn/network/api/server.py index 25826bbdd..cea8bedec 100644 --- a/fedn/fedn/network/api/server.py +++ b/fedn/fedn/network/api/server.py @@ -69,7 +69,12 @@ def list_clients(): return: All clients as a json object. rtype: json """ - return api.get_all_clients() + + limit = request.args.get("limit", None) + skip = request.args.get("skip", None) + active_only = request.args.get("active_only", None) + + return api.get_clients(limit, skip, active_only) @app.route("/get_active_clients", methods=["GET"]) diff --git a/fedn/fedn/network/combiner/server.py b/fedn/fedn/network/combiner/server.py index 11d874ea6..71c17b901 100644 --- a/fedn/fedn/network/combiner/server.py +++ b/fedn/fedn/network/combiner/server.py @@ -712,6 +712,8 @@ def ModelUpdateRequestStream(self, response, context): self._send_status(status) + self.tracer.update_client_timestamp(client.name) + while context.is_active(): try: yield q.get(timeout=1.0) diff --git a/fedn/fedn/network/statestore/mongostatestore.py b/fedn/fedn/network/statestore/mongostatestore.py index ae30a64c3..3bf23d8c3 100644 --- a/fedn/fedn/network/statestore/mongostatestore.py +++ b/fedn/fedn/network/statestore/mongostatestore.py @@ -584,17 +584,29 @@ def get_client(self, name): except Exception: return None - def list_clients(self): + def list_clients(self, limit=None, skip=None, active_only=False): """List all clients registered on the network. :return: list of clients. :rtype: list(ObjectId) """ - try: - ret = self.clients.find() - return list(ret) - except Exception: - return None + + result = None + count = None + + if limit is not None and skip is not None: + limit = int(limit) + skip = int(skip) + result = self.clients.find().limit(limit).skip(skip) + else: + result = self.clients.find() + + count = self.clients.count_documents({}) + + return { + "result": result, + "count": count, + } def update_client_status(self, client_data, status, role): """Set or update client status.