From 189ae89e124478a4aaface3c2a593cee2603ebf2 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Wed, 3 Jun 2015 15:58:18 +0200 Subject: [PATCH] Add vsm_get_event_fd and vsm_enter_eventloop [Feature] Provide possibility to implement custom event loop for user of vasum client library [Cause] Integration with other services [Solution] Possibility to fetch event fd and run event loop [Verification] run ClientSuite tests Change-Id: Ic47e5de8bfd09889330d97d35d4c9ef47f238953 --- client/host-ipc-connection.cpp | 15 +- client/host-ipc-connection.hpp | 9 +- client/vasum-client-impl.cpp | 310 +++++++++++++++++++++------------- client/vasum-client-impl.hpp | 48 +++++- client/vasum-client.cpp | 29 +++- client/vasum-client.h | 127 ++++++++++++++ tests/unit_tests/client/ut-client.cpp | 151 +++++++++++++++++ wrapper/wrapper.cpp | 2 +- 8 files changed, 555 insertions(+), 136 deletions(-) diff --git a/client/host-ipc-connection.cpp b/client/host-ipc-connection.cpp index 7444f19..a58e0d7 100644 --- a/client/host-ipc-connection.cpp +++ b/client/host-ipc-connection.cpp @@ -35,22 +35,23 @@ namespace { const int TIMEOUT_INFINITE = -1; } //namespace -void HostIPCConnection::createSystem() +void HostIPCConnection::connect(const std::string& address, ipc::epoll::EventPoll& eventPoll) { - mClient.reset(new ipc::Client(mDispatcher.getPoll(), HOST_IPC_SOCKET)); + mClient.reset(new ipc::Client(eventPoll, address)); mClient->start(); } -ipc::epoll::ThreadDispatcher& HostIPCConnection::getDispatcher() + +void HostIPCConnection::disconnect() { - return mDispatcher; + mClient.reset(); } -void HostIPCConnection::create(const std::string& address) +bool HostIPCConnection::isConnected() { - mClient.reset(new ipc::Client(mDispatcher.getPoll(), address)); - mClient->start(); + return mClient && mClient->isStarted(); } + void HostIPCConnection::callGetZoneIds(api::ZoneIds& argOut) { argOut = *mClient->callSync( diff --git a/client/host-ipc-connection.hpp b/client/host-ipc-connection.hpp index 52b90b7..16a2a9e 100644 --- a/client/host-ipc-connection.hpp +++ b/client/host-ipc-connection.hpp @@ -27,8 +27,8 @@ #define VASUM_CLIENT_HOST_IPC_CONNECTION_HPP #include -#include #include +#include #include @@ -42,9 +42,9 @@ class HostIPCConnection { public: typedef unsigned int SubscriptionId; typedef std::function NotificationCallback; - void createSystem(); - void create(const std::string& address); - ipc::epoll::ThreadDispatcher& getDispatcher(); + void connect(const std::string& address, ipc::epoll::EventPoll& eventPoll); + void disconnect(); + bool isConnected(); void callGetZoneIds(vasum::api::ZoneIds& argOut); void callGetActiveZoneId(vasum::api::ZoneId& argOut); @@ -80,7 +80,6 @@ public: void unsubscribe(const SubscriptionId& id); private: - ipc::epoll::ThreadDispatcher mDispatcher; std::unique_ptr mClient; }; diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index f87e9a0..97cd004 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -23,6 +23,11 @@ * @brief This file contains vasum-server's client implementation */ +//TODO: Make dispatcher related function thread-safe. +//For now vsm_connect, vsm_get_dispatcher_type, vsm_set_dispatcher_type, +//vsm_get_poll_fd, vsm_enter_eventloop can't be used at the same time. +//TODO: Make vsm_get_status_message thread-safe version (vsm_get_status_message_r) + #include #include "vasum-client-impl.hpp" #include "utils.hpp" @@ -34,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -127,6 +131,11 @@ bool readFirstLineOfFile(const string& path, string& ret) } //namespace +#define IS_SET(param) \ + if (!param) { \ + throw InvalidArgumentException(#param " is not set"); \ + } + Client::Status::Status() : mVsmStatus(VSMCLIENT_SUCCESS), mMsg() { @@ -145,20 +154,39 @@ Client::~Client() noexcept { } +bool Client::isInternalDispatcherEnabled() const +{ + return static_cast(mInternalDispatcher); +} + +ipc::epoll::EventPoll& Client::getEventPoll() const +{ + if ((mInternalDispatcher && mEventPoll) || (!mInternalDispatcher && !mEventPoll)) { + throw OperationFailedException("Can't determine dispatcher method"); + } + + if (isInternalDispatcherEnabled()) { + return mInternalDispatcher->getPoll(); + } else { + return *mEventPoll; + } +} + VsmStatus Client::coverException(const function& worker) noexcept { try { worker(); + mStatusMutex.lock(); mStatus = Status(VSMCLIENT_SUCCESS); - } catch (const vasum::IOException& ex) { + } catch (const IOException& ex) { mStatus = Status(VSMCLIENT_IO_ERROR, ex.what()); - } catch (const vasum::OperationFailedException& ex) { + } catch (const OperationFailedException& ex) { mStatus = Status(VSMCLIENT_OPERATION_FAILED, ex.what()); - } catch (const vasum::InvalidArgumentException& ex) { + } catch (const InvalidArgumentException& ex) { mStatus = Status(VSMCLIENT_INVALID_ARGUMENT, ex.what()); - } catch (const vasum::InvalidResponseException& ex) { + } catch (const InvalidResponseException& ex) { mStatus = Status(VSMCLIENT_OTHER_ERROR, ex.what()); - } catch (const vasum::ClientException& ex) { + } catch (const ClientException& ex) { mStatus = Status(VSMCLIENT_CUSTOM_ERROR, ex.what()); } catch (const ipc::IPCUserException& ex) { mStatus = Status(VSMCLIENT_CUSTOM_ERROR, ex.what()); @@ -167,26 +195,86 @@ VsmStatus Client::coverException(const function& worker) noexcept } catch (const exception& ex) { mStatus = Status(VSMCLIENT_CUSTOM_ERROR, ex.what()); } - return mStatus.mVsmStatus; + VsmStatus ret = mStatus.mVsmStatus; + mStatusMutex.unlock(); + return ret; +} + +VsmStatus Client::connectSystem() noexcept +{ + return connect(HOST_IPC_SOCKET); +} + +VsmStatus Client::connect(const std::string& address) noexcept +{ + return coverException([&] { + if (!mInternalDispatcher && !mEventPoll) { + vsm_set_dispatcher_type(VSMDISPATCHER_INTERNAL); + } + mHostClient.connect(address, getEventPoll()); + }); +} + +VsmStatus Client::disconnect() noexcept +{ + return coverException([&] { + mHostClient.disconnect(); + }); } -VsmStatus Client::createSystem() noexcept +VsmStatus Client::vsm_get_poll_fd(int* fd) noexcept { return coverException([&] { - mHostClient.createSystem(); + IS_SET(fd); + if (isInternalDispatcherEnabled()) { + throw OperationFailedException("Can't get event fd from internal dispatcher"); + } + *fd = mEventPoll->getPollFD(); }); } -VsmStatus Client::create(const string& address) noexcept +VsmStatus Client::vsm_enter_eventloop(int /* flags */, int timeout) noexcept { return coverException([&] { - mHostClient.create(address); + if (isInternalDispatcherEnabled()) { + throw OperationFailedException("Can't enter to event loop of internal dispatcher"); + } + mEventPoll->dispatchIteration(timeout); }); } -ipc::epoll::EventPoll& Client::getEventPoll() noexcept +VsmStatus Client::vsm_set_dispatcher_type(VsmDispacherType dispacher) noexcept { - return mHostClient.getDispatcher().getPoll(); + return coverException([&] { + if (mHostClient.isConnected()) { + throw OperationFailedException("Can't change dispacher"); + } + switch (dispacher) { + case VSMDISPATCHER_INTERNAL: + mInternalDispatcher.reset(new ipc::epoll::ThreadDispatcher()); + mEventPoll.reset(); + break; + case VSMDISPATCHER_EXTERNAL: + mEventPoll.reset(new ipc::epoll::EventPoll()); + mInternalDispatcher.reset(); + break; + default: + throw OperationFailedException("Unsupported EventDispacher type"); + } + }); +} + +VsmStatus Client::vsm_get_dispatcher_type(VsmDispacherType* dispacher) noexcept +{ + return coverException([&] { + IS_SET(dispacher); + + if (isInternalDispatcherEnabled()) { + *dispacher = VSMDISPATCHER_INTERNAL; + } else { + *dispacher = VSMDISPATCHER_EXTERNAL; + } + }); } const char* Client::vsm_get_status_message() const noexcept @@ -196,6 +284,7 @@ const char* Client::vsm_get_status_message() const noexcept VsmStatus Client::vsm_get_status() const noexcept { + lock_guard lock(mStatusMutex); return mStatus.mVsmStatus; } @@ -209,9 +298,9 @@ VsmStatus Client::vsm_get_zone_dbuses(VsmArrayString* /*keys*/, VsmArrayString* VsmStatus Client::vsm_get_zone_ids(VsmArrayString* array) noexcept { - assert(array); - return coverException([&] { + IS_SET(array); + api::ZoneIds zoneIds; mHostClient.callGetZoneIds(zoneIds); convert(zoneIds, *array); @@ -220,9 +309,9 @@ VsmStatus Client::vsm_get_zone_ids(VsmArrayString* array) noexcept VsmStatus Client::vsm_get_active_zone_id(VsmString* id) noexcept { - assert(id); - return coverException([&] { + IS_SET(id); + api::ZoneId zoneId; mHostClient.callGetActiveZoneId(zoneId); *id = ::strdup(zoneId.value.c_str()); @@ -231,10 +320,10 @@ VsmStatus Client::vsm_get_active_zone_id(VsmString* id) noexcept VsmStatus Client::vsm_get_zone_rootpath(const char* id, VsmString* rootpath) noexcept { - assert(id); - assert(rootpath); - return coverException([&] { + IS_SET(id); + IS_SET(rootpath); + api::ZoneInfoOut info; mHostClient.callGetZoneInfo({ id }, info); *rootpath = ::strdup(info.rootPath.c_str()); @@ -243,9 +332,9 @@ VsmStatus Client::vsm_get_zone_rootpath(const char* id, VsmString* rootpath) noe VsmStatus Client::vsm_lookup_zone_by_pid(int pid, VsmString* id) noexcept { - assert(id); - return coverException([&] { + IS_SET(id); + const string path = "/proc/" + to_string(pid) + "/cpuset"; string cpuset; @@ -264,10 +353,10 @@ VsmStatus Client::vsm_lookup_zone_by_pid(int pid, VsmString* id) noexcept VsmStatus Client::vsm_lookup_zone_by_id(const char* id, VsmZone* zone) noexcept { - assert(id); - assert(zone); - return coverException([&] { + IS_SET(id); + IS_SET(zone); + api::ZoneInfoOut info; mHostClient.callGetZoneInfo({ id }, info); convert(info, *zone); @@ -284,18 +373,18 @@ VsmStatus Client::vsm_lookup_zone_by_terminal_id(int, VsmString*) noexcept VsmStatus Client::vsm_set_active_zone(const char* id) noexcept { - assert(id); - return coverException([&] { + IS_SET(id); + mHostClient.callSetActiveZone({ id }); }); } VsmStatus Client::vsm_create_zone(const char* id, const char* tname) noexcept { - assert(id); - return coverException([&] { + IS_SET(id); + string template_name = tname ? tname : "default"; mHostClient.callCreateZone({ id, template_name }); }); @@ -303,45 +392,40 @@ VsmStatus Client::vsm_create_zone(const char* id, const char* tname) noexcept VsmStatus Client::vsm_destroy_zone(const char* id) noexcept { - assert(id); - return coverException([&] { + IS_SET(id); mHostClient.callDestroyZone({ id }); }); } VsmStatus Client::vsm_shutdown_zone(const char* id) noexcept { - assert(id); - return coverException([&] { + IS_SET(id); mHostClient.callShutdownZone({ id }); }); } VsmStatus Client::vsm_start_zone(const char* id) noexcept { - assert(id); - return coverException([&] { + IS_SET(id); mHostClient.callStartZone({ id }); }); } VsmStatus Client::vsm_lock_zone(const char* id) noexcept { - assert(id); - return coverException([&] { + IS_SET(id); mHostClient.callLockZone({ id }); }); } VsmStatus Client::vsm_unlock_zone(const char* id) noexcept { - assert(id); - return coverException([&] { + IS_SET(id); mHostClient.callUnlockZone({ id }); }); } @@ -365,30 +449,30 @@ VsmStatus Client::vsm_del_state_callback(VsmSubscriptionId subscriptionId) noexc VsmStatus Client::vsm_grant_device(const char* id, const char* device, uint32_t flags) noexcept { - assert(id); - assert(device); - return coverException([&] { + IS_SET(id); + IS_SET(device); + mHostClient.callGrantDevice({ id, device, flags }); }); } VsmStatus Client::vsm_revoke_device(const char* id, const char* device) noexcept { - assert(id); - assert(device); - return coverException([&] { + IS_SET(id); + IS_SET(device); + mHostClient.callRevokeDevice({ id, device }); }); } VsmStatus Client::vsm_zone_get_netdevs(const char* id, VsmArrayString* netdevIds) noexcept { - assert(id); - assert(netdevIds); - return coverException([&] { + IS_SET(id); + IS_SET(netdevIds); + api::NetDevList netdevs; mHostClient.callGetNetdevList({ id }, netdevs); convert(netdevs, *netdevIds); @@ -402,11 +486,11 @@ VsmStatus Client::vsm_netdev_get_ip_addr(const char* id, { using namespace boost::algorithm; - assert(id); - assert(netdevId); - assert(addr); - return coverException([&] { + IS_SET(id); + IS_SET(netdevId); + IS_SET(addr); + api::GetNetDevAttrs attrs; mHostClient.callGetNetdevAttrs({ id, netdevId }, attrs); @@ -454,11 +538,11 @@ VsmStatus Client::vsm_netdev_set_ipv4_addr(const char* id, struct in_addr* addr, int prefix) noexcept { - assert(id); - assert(netdevId); - assert(addr); - return coverException([&] { + IS_SET(id); + IS_SET(netdevId); + IS_SET(addr); + string value = "ip:" + toString(addr) + ",""prefixlen:" + to_string(prefix); mHostClient.callSetNetdevAttrs({ id, netdevId, { { "ipv4", value } } } ); }); @@ -469,11 +553,11 @@ VsmStatus Client::vsm_netdev_set_ipv6_addr(const char* id, struct in6_addr* addr, int prefix) noexcept { - assert(id); - assert(netdevId); - assert(addr); - return coverException([&] { + IS_SET(id); + IS_SET(netdevId); + IS_SET(addr); + string value = "ip:" + toString(addr) + ",""prefixlen:" + to_string(prefix); mHostClient.callSetNetdevAttrs({ id, netdevId, { { "ipv6", value } } } ); }); @@ -484,11 +568,11 @@ VsmStatus Client::vsm_netdev_del_ipv4_addr(const char* id, struct in_addr* addr, int prefix) noexcept { - assert(id); - assert(netdevId); - assert(addr); - return coverException([&] { + IS_SET(id); + IS_SET(netdevId); + IS_SET(addr); + //CIDR notation string ip = toString(addr) + "/" + to_string(prefix); mHostClient.callDeleteNetdevIpAddress({ id, netdevId, ip }); @@ -500,11 +584,11 @@ VsmStatus Client::vsm_netdev_del_ipv6_addr(const char* id, struct in6_addr* addr, int prefix) noexcept { - assert(id); - assert(netdevId); - assert(addr); - return coverException([&] { + IS_SET(id); + IS_SET(netdevId); + IS_SET(addr); + //CIDR notation string ip = toString(addr) + "/" + to_string(prefix); mHostClient.callDeleteNetdevIpAddress({ id, netdevId, ip }); @@ -514,10 +598,10 @@ VsmStatus Client::vsm_netdev_del_ipv6_addr(const char* id, VsmStatus Client::vsm_netdev_up(const char* id, const char* netdevId) noexcept { - assert(id); - assert(netdevId); - return coverException([&] { + IS_SET(id); + IS_SET(netdevId); + mHostClient.callSetNetdevAttrs({ id, netdevId, { { "flags", to_string(IFF_UP) }, { "change", to_string(IFF_UP) } } } ); }); @@ -525,10 +609,10 @@ VsmStatus Client::vsm_netdev_up(const char* id, const char* netdevId) noexcept VsmStatus Client::vsm_netdev_down(const char* id, const char* netdevId) noexcept { - assert(id); - assert(netdevId); - return coverException([&] { + IS_SET(id); + IS_SET(netdevId); + mHostClient.callSetNetdevAttrs({ id, netdevId, { { "flags", to_string(~IFF_UP) }, { "change", to_string(IFF_UP) } } } ); }); @@ -538,11 +622,11 @@ VsmStatus Client::vsm_create_netdev_veth(const char* id, const char* zoneDev, const char* hostDev) noexcept { - assert(id); - assert(zoneDev); - assert(hostDev); - return coverException([&] { + IS_SET(id); + IS_SET(zoneDev); + IS_SET(hostDev); + mHostClient.callCreateNetdevVeth({ id, zoneDev, hostDev }); }); } @@ -552,21 +636,21 @@ VsmStatus Client::vsm_create_netdev_macvlan(const char* id, const char* hostDev, enum macvlan_mode mode) noexcept { - assert(id); - assert(zoneDev); - assert(hostDev); - return coverException([&] { + IS_SET(id); + IS_SET(zoneDev); + IS_SET(hostDev); + mHostClient.callCreateNetdevMacvlan({ id, zoneDev, hostDev, mode }); }); } VsmStatus Client::vsm_create_netdev_phys(const char* id, const char* devId) noexcept { - assert(id); - assert(devId); - return coverException([&] { + IS_SET(id); + IS_SET(devId); + mHostClient.callCreateNetdevPhys({ id, devId }); }); } @@ -577,11 +661,11 @@ VsmStatus Client::vsm_lookup_netdev_by_name(const char* id, { using namespace boost::algorithm; - assert(id); - assert(netdevId); - assert(netdev); - return coverException([&] { + IS_SET(id); + IS_SET(netdevId); + IS_SET(netdev); + api::GetNetDevAttrs attrs; mHostClient.callGetNetdevAttrs({ id, netdevId }, attrs); auto it = find_if(attrs.values.begin(), @@ -610,10 +694,10 @@ VsmStatus Client::vsm_lookup_netdev_by_name(const char* id, VsmStatus Client::vsm_destroy_netdev(const char* id, const char* devId) noexcept { - assert(id); - assert(devId); - return coverException([&] { + IS_SET(id); + IS_SET(devId); + mHostClient.callDestroyNetdev({ id, devId }); }); } @@ -625,10 +709,10 @@ VsmStatus Client::vsm_declare_file(const char* id, mode_t mode, VsmString* declarationId) noexcept { - assert(id); - assert(path); - return coverException([&] { + IS_SET(id); + IS_SET(path); + api::Declaration declaration; mHostClient.callDeclareFile({ id, type, path, flags, (int)mode }, declaration); if (declarationId != NULL) { @@ -645,15 +729,15 @@ VsmStatus Client::vsm_declare_mount(const char *source, const char *data, VsmString* declarationId) noexcept { - assert(source); - assert(id); - assert(target); - assert(type); - if (!data) { - data = ""; - } - return coverException([&] { + IS_SET(source); + IS_SET(id); + IS_SET(target); + IS_SET(type); + if (!data) { + data = ""; + } + api::Declaration declaration; mHostClient.callDeclareMount({ source, id, target, type, flags, data }, declaration); if (declarationId != NULL) { @@ -667,11 +751,11 @@ VsmStatus Client::vsm_declare_link(const char* source, const char* target, VsmString* declarationId) noexcept { - assert(source); - assert(id); - assert(target); - return coverException([&] { + IS_SET(source); + IS_SET(id); + IS_SET(target); + api::Declaration declaration; mHostClient.callDeclareLink({ source, id, target }, declaration); if (declarationId != NULL) { @@ -682,10 +766,10 @@ VsmStatus Client::vsm_declare_link(const char* source, VsmStatus Client::vsm_list_declarations(const char* id, VsmArrayString* declarations) noexcept { - assert(id); - assert(declarations); - return coverException([&] { + IS_SET(id); + IS_SET(declarations); + api::Declarations declarationsOut; mHostClient.callGetDeclarations({ id }, declarationsOut); convert(declarationsOut, *declarations); @@ -694,10 +778,10 @@ VsmStatus Client::vsm_list_declarations(const char* id, VsmArrayString* declarat VsmStatus Client::vsm_remove_declaration(const char* id, VsmString declaration) noexcept { - assert(id); - assert(declaration); - return coverException([&] { + IS_SET(id); + IS_SET(declaration); + mHostClient.callRemoveDeclaration({ id, declaration }); }); } diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index 5a1d2fd..0ec3634 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -29,6 +29,11 @@ #include "vasum-client.h" #include "host-ipc-connection.hpp" +#include +#include + +#include +#include #include #include @@ -63,21 +68,44 @@ public: ~Client() noexcept; /** - * Create client with system dbus address. + * Connect client with system ipc address. * * @return status of this function call */ - VsmStatus createSystem() noexcept; - - ipc::epoll::EventPoll& getEventPoll() noexcept; + VsmStatus connectSystem() noexcept; /** - * Create client. + * Connect client. * - * @param address Dbus socket address + * @param address ipc socket address * @return status of this function call */ - VsmStatus create(const std::string& address) noexcept; + VsmStatus connect(const std::string& address) noexcept; + + /** + * Disconnect client + */ + VsmStatus disconnect() noexcept; + + /** + * @see ::vsm_get_poll_fd + */ + VsmStatus vsm_get_poll_fd(int* fd) noexcept; + + /** + * @see ::vsm_enter_eventloop + */ + VsmStatus vsm_enter_eventloop(int flags, int timeout) noexcept; + + /** + * @see ::vsm_set_dispatcher_type + */ + VsmStatus vsm_set_dispatcher_type(VsmDispacherType dispacher) noexcept; + + /** + * @see ::vsm_get_dispatcher_type + */ + VsmStatus vsm_get_dispatcher_type(VsmDispacherType* dispacher) noexcept; /** * @see ::vsm_get_status_message @@ -344,11 +372,15 @@ private: VsmStatus mVsmStatus; std::string mMsg; }; - Status mStatus; + mutable std::mutex mStatusMutex; + std::unique_ptr mInternalDispatcher; + std::unique_ptr mEventPoll; HostConnection mHostClient; + bool isInternalDispatcherEnabled() const; + ipc::epoll::EventPoll& getEventPoll() const; VsmStatus coverException(const std::function& worker) noexcept; VsmStatus vsm_netdev_get_ip_addr(const char* zone, const char* netdevId, diff --git a/client/vasum-client.cpp b/client/vasum-client.cpp index 1d5664c..f36c1ed 100644 --- a/client/vasum-client.cpp +++ b/client/vasum-client.cpp @@ -45,6 +45,26 @@ Client& getClient(VsmClient client) } // namespace +API VsmStatus vsm_get_poll_fd(VsmClient client, int* fd) +{ + return getClient(client).vsm_get_poll_fd(fd); +} + +API VsmStatus vsm_enter_eventloop(VsmClient client, int flags, int timeout) +{ + return getClient(client).vsm_enter_eventloop(flags, timeout); +} + +API VsmStatus vsm_set_dispatcher_type(VsmClient client, VsmDispacherType dispacher) +{ + return getClient(client).vsm_set_dispatcher_type(dispacher); +} + +API VsmStatus vsm_get_dispatcher_type(VsmClient client, VsmDispacherType* dispacher) +{ + return getClient(client).vsm_get_dispatcher_type(dispacher); +} + API VsmClient vsm_client_create() { Client* clientPtr = new(nothrow) Client(); @@ -53,12 +73,17 @@ API VsmClient vsm_client_create() API VsmStatus vsm_connect(VsmClient client) { - return getClient(client).createSystem(); + return getClient(client).connectSystem(); } API VsmStatus vsm_connect_custom(VsmClient client, const char* address) { - return getClient(client).create(address); + return getClient(client).connect(address); +} + +API VsmStatus vsm_disconnect(VsmClient client) +{ + return getClient(client).disconnect(); } API void vsm_array_string_free(VsmArrayString astring) diff --git a/client/vasum-client.h b/client/vasum-client.h index 7a98fa7..3832d23 100644 --- a/client/vasum-client.h +++ b/client/vasum-client.h @@ -180,9 +180,128 @@ typedef enum { VSMFILE_REGULAR } VsmFileType; +/** + * Event dispacher types. + * + * @par Example usage: + * @code +#include +#include +#include +#include +#include +#include + +volatile static sig_atomic_t running; +static int LOOP_INTERVAL = 1000; // ms + +void* main_loop(void* client) +{ + int fd = -1; + VsmStatus status = vsm_get_poll_fd(client, &fd); + assert(VSMCLIENT_SUCCESS == status); + + while (running) { + struct epoll_event event; + int num = epoll_wait(fd, &event, 1, LOOP_INTERVAL); + if (num > 0) { + status = vsm_enter_eventloop(client, 0 , 0); + assert(VSMCLIENT_SUCCESS == status); + } + } + return NULL; +} + +int main(int argc, char** argv) +{ + pthread_t loop; + VsmStatus status; + VsmClient client; + int ret = 0; + + client = vsm_client_create(); + assert(client); + + status = vsm_set_dispatcher_type(client, VSMDISPATCHER_EXTERNAL); + assert(VSMCLIENT_SUCCESS == status); + + status = vsm_connect(client); + assert(VSMCLIENT_SUCCESS == status); + + // start event loop + running = 1; + ret = pthread_create(&loop, NULL, main_loop, client); + assert(ret == 0); + + // make vsm_* calls on client + // ... + + status = vsm_disconnect(client); + assert(VSMCLIENT_SUCCESS == status); + + //stop event loop + running = 0; + ret = pthread_join(loop, NULL); + assert(ret == 0); + + vsm_client_free(client); // destroy client handle + return ret; +} + @endcode + */ +typedef enum { + VSMDISPATCHER_EXTERNAL, /**< User must handle dispatching messages */ + VSMDISPATCHER_INTERNAL /**< Library will take care of dispatching messages */ +} VsmDispacherType; + #ifndef __VASUM_WRAPPER_SOURCE__ /** + * Get file descriptor associated with event dispatcher of zone client + * + * The function vsm_get_poll_fd() returns the file descriptor associated with the event dispatcher of vsm client. + * The file descriptor can be bound to another I/O multiplexing facilities like epoll, select, and poll. + * + * @param[in] client vsm client + * @param[out] fd epoll file descriptor + * @return status of this function call +*/ +VsmStatus vsm_get_poll_fd(VsmClient client, int* fd); + +/** + * Wait for an I/O event on a vsm client + * + * vsm_enter_eventloop() waits for event on the vsm client + * + * The call waits for a maximum time of timout milliseconds. Specifying a timeout of -1 makes vsm_enter_eventloop() wait indefinitely, + * while specifying a timeout equal to zero makes vsm_enter_eventloop() to return immediately even if no events are available. + * + * @param[in] client vasum-server's client + * @param[in] flags Reserved + * @param[in] timeout Timeout time (milisecond), -1 is infinite + * @return status of this function call +*/ +VsmStatus vsm_enter_eventloop(VsmClient client, int flags, int timeout); + +/** + * Set dispatching method + * + * @param[in] client vasum-server's client + * @param[in] dispacher dispatching method + * @return status of this function call + */ +VsmStatus vsm_set_dispatcher_type(VsmClient client, VsmDispacherType dispacher); + +/** + * Get dispatching method + * + * @param[in] client vasum-server's client + * @param[out] dispacher dispatching method + * @return status of this function call + */ +VsmStatus vsm_get_dispatcher_type(VsmClient client, VsmDispacherType* dispacher); + +/** * Create a new vasum-server's client. * * @return Created client pointer or NULL on failure. @@ -230,6 +349,14 @@ VsmStatus vsm_connect(VsmClient client); VsmStatus vsm_connect_custom(VsmClient client, const char* address); /** + * Disconnect client from vasum-server. + * + * @param[in] client vasum-server's client + * @return status of this function call + */ +VsmStatus vsm_disconnect(VsmClient client); + +/** * Release VsmArrayString. * * @param[in] astring VsmArrayString diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index ce02a33..65d6e49 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -46,6 +46,8 @@ #include #include +#include + using namespace vasum; using namespace utils; @@ -55,6 +57,7 @@ const std::string TEST_CONFIG_PATH = VSM_TEST_CONFIG_INSTALL_DIR "/test-daemon.conf"; const std::string ZONES_PATH = "/tmp/ut-zones"; // the same as in daemon.conf const std::string TEMPLATE_NAME = "console-ipc"; +const int EVENT_TIMEOUT = 500; // ms struct Fixture { utils::ScopedDir mZonesPathGuard; @@ -83,6 +86,73 @@ struct Fixture { } }; +class SimpleEventLoop { +public: + SimpleEventLoop(VsmClient client) + : mClient(client) + , mIsProcessing(true) + , mThread([this] { loop(); }) + { + } + ~SimpleEventLoop() + { + mIsProcessing = false; + mThread.join(); + } + +private: + VsmClient mClient; + std::atomic_bool mIsProcessing; + std::thread mThread; + void loop() { + while (mIsProcessing) { + vsm_enter_eventloop(mClient, 0, EVENT_TIMEOUT); + } + } +}; + +class AggragatedEventLoop { +public: + AggragatedEventLoop() + : mIsProcessing(true) + , mThread([this] { loop(); }) + { + } + + ~AggragatedEventLoop() + { + mIsProcessing = false; + mThread.join(); + } + + VsmStatus addEventSource(VsmClient client) { + int fd = -1; + VsmStatus ret = vsm_get_poll_fd(client, &fd); + if (VSMCLIENT_SUCCESS != ret) { + return ret; + } + mEventPoll.addFD(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP, [client] (int, ipc::epoll::Events) { + vsm_enter_eventloop(client, 0, 0); + }); + mFds.push_back(fd); + return VSMCLIENT_SUCCESS; + } +private: + std::atomic_bool mIsProcessing; + std::thread mThread; + ipc::epoll::EventPoll mEventPoll; + std::vector mFds; + + void loop() { + while (mIsProcessing) { + mEventPoll.dispatchIteration(EVENT_TIMEOUT); + } + for (int fd : mFds) { + mEventPoll.removeFD(fd); + } + } +}; + const std::set EXPECTED_ZONES = { "zone1", "zone2", "zone3" }; void convertArrayToSet(VsmArrayString values, std::set& ret) @@ -103,6 +173,18 @@ int getArrayStringLength(VsmArrayString astring, int max_len = -1) return i; } +VsmStatus makeSimpleRequest(VsmClient client) +{ + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_connect(client)); + //make a simple call + VsmString zone = NULL; + VsmStatus status = vsm_get_active_zone_id(client, &zone); + vsm_string_free(zone); + // disconnect but not destroy + vsm_disconnect(client); + return status; +} + } // namespace // make nice BOOST_*_EQUAL output @@ -426,6 +508,75 @@ BOOST_AUTO_TEST_CASE(ZoneGetNetdevs) vsm_client_free(client); } +BOOST_AUTO_TEST_CASE(DefaultDispatcher) +{ + VsmClient client = vsm_client_create(); + + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client)); + + vsm_client_free(client); +} + +BOOST_AUTO_TEST_CASE(SetDispatcher) +{ + VsmClient client = vsm_client_create(); + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_INTERNAL)); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client)); + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_EXTERNAL)); + { + SimpleEventLoop loop(client); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client)); + } + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_INTERNAL)); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client)); + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_EXTERNAL)); + { + SimpleEventLoop loop(client); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client)); + } + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client, VSMDISPATCHER_INTERNAL)); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, makeSimpleRequest(client)); + + vsm_client_free(client); +} + +BOOST_AUTO_TEST_CASE(GetPollFd) +{ + VsmClient client1 = vsm_client_create(); + VsmClient client2 = vsm_client_create(); + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client1, VSMDISPATCHER_EXTERNAL)); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_set_dispatcher_type(client2, VSMDISPATCHER_EXTERNAL)); + { + AggragatedEventLoop loop; + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, loop.addEventSource(client1)); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, loop.addEventSource(client2)); + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_connect(client1)); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_connect(client2)); + + VsmString zone; + //make a simple call + zone = NULL; + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_get_active_zone_id(client1, &zone)); + vsm_string_free(zone); + zone = NULL; + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_get_active_zone_id(client2, &zone)); + vsm_string_free(zone); + + // disconnect but not destroy + vsm_disconnect(client1); + vsm_disconnect(client2); + } + vsm_client_free(client1); + vsm_client_free(client2); +} + //TODO: We need createBridge from vasum::netdev //BOOST_AUTO_TEST_CASE(CreateDestroyNetdev) //BOOST_AUTO_TEST_CASE(LookupNetdev) diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index 8b890cf..a975087 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -151,7 +151,7 @@ static int wrap_error(VsmStatus st, const Client *c) static void init_context_wrap(WrappedContext *w) { w->client = new Client(); - VsmStatus st = w->client->createSystem(); + VsmStatus st = w->client->connectSystem(); wrap_error(st, w->client); memset(&w->hq_ctx, 0, sizeof(w->hq_ctx)); -- 2.7.4