Skip to content

Commit

Permalink
added access to ClientManager, python API for client manager, and cor…
Browse files Browse the repository at this point in the history
…responding tests
  • Loading branch information
mdorier committed Feb 1, 2024
1 parent 81ee8c9 commit 68d0a85
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 31 deletions.
17 changes: 11 additions & 6 deletions include/bedrock/ClientManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ class ClientManager {
*/
operator bool() const;

/**
* @brief Set the DependencyFinder object to use to resolve dependencies.
*
* @param finder DependencyFinder
*/
void setDependencyFinder(const DependencyFinder& finder);

/**
* @brief Look up whether a client with a given name exists.
* This function returns true if a client was found, false
Expand Down Expand Up @@ -140,20 +147,18 @@ class ClientManager {
* }
*
* @param jsonString JSON string.
* @param finder DependencyFinder to resolve the dependencies found.
*/
std::shared_ptr<NamedDependency>
addClientFromJSON(const std::string& jsonString,
const DependencyFinder& finder);
addClientFromJSON(const std::string& jsonString);

/**
* @brief Add a list of providers represented by a JSON string.
* The JSON string must represent an array of entries in the format
* expected by addClientFromJSON.
*
* @param jsonString JSON string.
* @param finder DependencyFinder.
*/
void addClientListFromJSON(const std::string& jsonString,
const DependencyFinder& finder);
void addClientListFromJSON(const std::string& jsonString);

/**
* @brief Return the current JSON configuration.
Expand Down
2 changes: 2 additions & 0 deletions include/bedrock/DependencyFinder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace bedrock {
class Server;
class ServerImpl;
class ProviderManager;
class ClientManager;
class DependencyFinderImpl;

/**
Expand All @@ -29,6 +30,7 @@ class DependencyFinder {

friend class Server;
friend class ProviderManager;
friend class ClientManager;
friend class ServerImpl;

public:
Expand Down
6 changes: 6 additions & 0 deletions include/bedrock/Server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <bedrock/ABTioManager.hpp>
#include <bedrock/SSGManager.hpp>
#include <bedrock/ProviderManager.hpp>
#include <bedrock/ClientManager.hpp>
#include <thallium.hpp>
#include <memory>

Expand Down Expand Up @@ -93,6 +94,11 @@ class Server {
*/
ProviderManager getProviderManager() const;

/**
* @brief Get the underlying ClientManager.
*/
ClientManager getClientManager() const;

/**
* @brief Get the underlying SSG context.
*/
Expand Down
58 changes: 56 additions & 2 deletions python/mochi/bedrock/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import pymargo.core
import pymargo
from typing import Mapping, List
from .spec import ProcSpec, MargoSpec, PoolSpec, XstreamSpec, SSGSpec, AbtIOSpec, ProviderSpec
from .spec import ProcSpec, MargoSpec, PoolSpec, XstreamSpec, SSGSpec, AbtIOSpec, ProviderSpec, ClientSpec
import json


Expand Down Expand Up @@ -50,6 +50,7 @@ def handle(self):
Xstream = NamedDependency
SSGGroup = NamedDependency
AbtIOInstance = NamedDependency
Client = NamedDependency


class ProviderDependency(NamedDependency):
Expand Down Expand Up @@ -216,6 +217,55 @@ def create(self, name: str, pool: str|Pool, config: str|dict = "{}") -> AbtIOIns
return AbtIOInstance(self._internal.add_abtio_instance(name, pool, config))


class ClientManager:

def __init__(self, internal: pybedrock_server.ClientManager, server: 'Server'):
self._internal = internal
self._server = server

@property
def config(self) -> dict:
return json.loads(self._internal.config)

@property
def spec(self) -> list[ClientSpec]:
return [ClientSpec.from_dict(client) for client in self.config]

def __len__(self):
return len(self._internal.clients)

def __getitem__(self, key: int|str) -> Provider:
clients = self._internal.clients
if isinstance(key, int):
key = clients[key].name
return self.lookup(key)

def __delitem__(self, key: int|str) -> None:
if isinstance(key, int):
key = self._internal.clients[key].name
self._internal.destroy_client(key)

def lookup(self, locator: str) -> Client:
return Client(self._internal.lookup_client(locator))

def lookup_or_create_anonymous(self, type: str) -> Client:
return Client(self._internal.lookup_client_or_create(type))

def create(self, name: str, type: str, config: str|dict = "{}",
dependencies: Mapping[str,str] = {},
tags: List[str] = []) -> Client:
if isinstance(config, str):
config = json.loads(config)
info = {
"name": name,
"type": type,
"dependencies": dependencies,
"tags": tags,
"config": config
}
return Client(self._internal.add_client_from_json(json.dumps(info)))


class ProviderManager:

def __init__(self, internal: pybedrock_server.ProviderManager, server: 'Server'):
Expand Down Expand Up @@ -261,7 +311,7 @@ def create(self, name: str, type: str, provider_id: int, pool: str|Pool,
"tags": tags,
"config": config
}
return Provider(self, self._internal.add_providers_from_json(json.dumps(info)))
return Provider(self, self._internal.add_provider_from_json(json.dumps(info)))

def migrate(self, provider: str, dest_addr: str,
dest_provider_id: str, migration_config: str|dict = "{}",
Expand Down Expand Up @@ -333,6 +383,10 @@ def ssg(self) -> SSGManager:
def abtio(self) -> AbtIOManager:
return AbtIOManager(self._internal.abtio_manager, self)

@property
def clients(self) -> ClientManager:
return ClientManager(self._internal.client_manager, self)

@property
def providers(self) -> ProviderManager:
return ProviderManager(self._internal.provider_manager, self)
82 changes: 82 additions & 0 deletions python/mochi/bedrock/test_client_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import unittest
import pymargo.logging
import mochi.bedrock.server as mbs
import mochi.bedrock.spec as spec


class TestClientManager(unittest.TestCase):

def setUp(self):
config = {
"libraries": {
"module_a": "libModuleA.so",
"module_b": "libModuleB.so"
},
"clients": [
{
"name": "my_client_A",
"type": "module_a"
}
]
}
self.server = mbs.Server(address="na+sm", config=config)
self.server.margo.engine.logger.set_log_level(pymargo.logging.level.critical)

def tearDown(self):
self.server.finalize()
del self.server

def test_get_client_manager(self):
clients = self.server.clients
self.assertIsInstance(clients, mbs.ClientManager)
self.assertEqual(len(clients), 1)
client_A = clients[0]
client_B = clients["my_client_A"]
self.assertEqual(client_A.name, client_B.name)
self.assertEqual(client_A.type, client_B.type)
self.assertEqual(client_A.handle, client_B.handle)
with self.assertRaises(IndexError):
c = clients[1]
with self.assertRaises(mbs.BedrockException):
c = clients["bla"]

def test_client_manager_config(self):
config = self.server.clients.config
self.assertIsInstance(config, list)
self.assertEqual(len(config), 1)
client_1 = config[0]
self.assertIsInstance(client_1, dict)
for key in ["name", "config", "dependencies", "tags", "type"]:
self.assertIn(key, client_1)

def test_client_manager_spec(self):
spec_list = self.server.clients.spec
self.assertIsInstance(spec_list, list)
for s in spec_list:
self.assertIsInstance(s, spec.ClientSpec)

def test_add_client(self):
clients = self.server.clients
clients.create(
name="my_client_B",
type="module_b")
self.assertEqual(len(clients), 2)
client_A = clients[1]
client_B = clients["my_client_B"]
self.assertEqual(client_A.name, client_B.name)
self.assertEqual(client_A.type, client_B.type)
self.assertEqual(client_A.handle, client_B.handle)

def test_remove_client(self):
self.test_add_client()
clients = self.server.clients
del clients["my_client_B"]
self.assertEqual(len(clients), 1)
with self.assertRaises(IndexError):
c = clients[1]
with self.assertRaises(mbs.BedrockException):
c = clients["my_provider_B"]


if __name__ == '__main__':
unittest.main()
41 changes: 29 additions & 12 deletions python/src/py-bedrock-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ PYBIND11_MODULE(pybedrock_server, m) {
[](std::shared_ptr<Server> server) {
return server->getProviderManager();
})
.def_property_readonly("client_manager",
[](std::shared_ptr<Server> server) {
return server->getClientManager();
})
.def_property_readonly("ssg_manager",
[](std::shared_ptr<Server> server) {
return server->getSSGManager();
Expand Down Expand Up @@ -200,21 +204,10 @@ PYBIND11_MODULE(pybedrock_server, m) {
.def("lookup_provider", &ProviderManager::lookupProvider,
"spec"_a)
.def_property_readonly("providers", &ProviderManager::listProviders)
/* // need to expose ResolvedDependencyMap
.def("register_provider",
[](ProviderManager& manager,
const ProviderDescriptor& descriptor,
const std::string& pool_name,
const std::string& config,
const ResolvedDependencyMap& dependencies,
const std::vector<std::string>& tags) {
...
})
*/
.def("deregister_provider",
&ProviderManager::deregisterProvider,
"spec"_a)
.def("add_providers_from_json",
.def("add_provider_from_json",
&ProviderManager::addProviderFromJSON,
"json_config"_a)
.def("add_provider_list_from_json",
Expand All @@ -234,4 +227,28 @@ PYBIND11_MODULE(pybedrock_server, m) {
&ProviderManager::restoreProvider,
"provider"_a, "src_path"_a, "restore_config"_a)
;

py11::class_<ClientDescriptor> (m, "ClientDescriptor")
.def(py11::init<const std::string&, const std::string&>())
.def_readonly("name", &ClientDescriptor::name)
.def_readonly("type", &ClientDescriptor::type)
;

py11::class_<ClientManager> (m, "ClientManager")
.def_property_readonly("config", &ClientManager::getCurrentConfig)
.def("lookup_client", &ClientManager::lookupClient,
"name"_a)
.def("lookup_client_or_create", &ClientManager::lookupOrCreateAnonymous,
"type"_a)
.def_property_readonly("clients", &ClientManager::listClients)
.def("destroy_client",
&ClientManager::destroyClient,
"name"_a)
.def("add_client_from_json",
&ClientManager::addClientFromJSON,
"json_config"_a)
.def("add_client_list_from_json",
&ClientManager::addClientListFromJSON,
"json_configs"_a)
;
}
13 changes: 8 additions & 5 deletions src/ClientManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ ClientManager::~ClientManager() = default;

ClientManager::operator bool() const { return static_cast<bool>(self); }

void ClientManager::setDependencyFinder(const DependencyFinder& finder) {
self->m_dependency_finder = finder;
}

std::shared_ptr<NamedDependency> ClientManager::lookupClient(const std::string& name) const {
std::lock_guard<tl::mutex> lock(self->m_clients_mtx);
auto it = self->resolveSpec(name);
Expand Down Expand Up @@ -171,8 +175,8 @@ void ClientManager::destroyClient(const std::string& name) {
}

std::shared_ptr<NamedDependency>
ClientManager::addClientFromJSON(
const std::string& jsonString, const DependencyFinder& dependencyFinder) {
ClientManager::addClientFromJSON(const std::string& jsonString) {
auto dependencyFinder = DependencyFinder(self->m_dependency_finder);
auto config = jsonString.empty() ? json::object() : json::parse(jsonString);
if (!config.is_object()) {
throw DETAILED_EXCEPTION("Client configuration should be an object");
Expand Down Expand Up @@ -278,8 +282,7 @@ ClientManager::addClientFromJSON(
return createClient(descriptor, client_config, resolved_dependency_map, tags);
}

void ClientManager::addClientListFromJSON(
const std::string& jsonString, const DependencyFinder& dependencyFinder) {
void ClientManager::addClientListFromJSON(const std::string& jsonString) {
auto config = json::parse(jsonString);
if (config.is_null()) { return; }
if (!config.is_array()) {
Expand All @@ -288,7 +291,7 @@ void ClientManager::addClientListFromJSON(
"ClientManager::addClientListFromJSON (expected array)");
}
for (const auto& client : config) {
addClientFromJSON(client.dump(), dependencyFinder);
addClientFromJSON(client.dump());
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/ClientManagerImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "bedrock/AbstractServiceFactory.hpp"
#include "bedrock/ClientDescriptor.hpp"
#include "bedrock/ClientManager.hpp"
#include "DependencyFinderImpl.hpp"

#include <thallium.hpp>
#include <nlohmann/json.hpp>
Expand Down Expand Up @@ -78,6 +79,7 @@ class ClientManagerImpl
public std::enable_shared_from_this<ClientManagerImpl> {

public:
std::shared_ptr<DependencyFinderImpl> m_dependency_finder;
std::vector<std::shared_ptr<ClientEntry>> m_clients;
mutable tl::mutex m_clients_mtx;
mutable tl::condition_variable m_clients_cv;
Expand Down
10 changes: 7 additions & 3 deletions src/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,14 @@ Server::Server(const std::string& address, const std::string& configString,

// Creating clients
spdlog::trace("Initializing clients");
clientManager.setDependencyFinder(dependencyFinder);
auto clientManagerConfig = config["clients"].dump();
clientManager.addClientListFromJSON(clientManagerConfig, dependencyFinder);
clientManager.addClientListFromJSON(clientManagerConfig);

// Starting up providers
spdlog::trace("Initializing providers");
auto providerManagerConfig = config["providers"].dump();
ProviderManager(self->m_provider_manager)
.setDependencyFinder(dependencyFinder);
providerManager.setDependencyFinder(dependencyFinder);
providerManager.addProviderListFromJSON(providerManagerConfig);
spdlog::trace("Providers initialized");

Expand All @@ -183,6 +183,10 @@ ProviderManager Server::getProviderManager() const {
return self->m_provider_manager;
}

ClientManager Server::getClientManager() const {
return self->m_client_manager;
}

SSGManager Server::getSSGManager() const { return self->m_ssg_manager; }

void Server::onPreFinalize(void* uargs) {
Expand Down
Loading

0 comments on commit 68d0a85

Please sign in to comment.