From 7711eba297955d4cf3852f36fe7c6d118da38171 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 22 Oct 2015 16:02:01 -0700 Subject: More cluster wiring... --- include/ZeroTierOne.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 135c8e11..4de4a765 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -131,12 +131,12 @@ extern "C" { /** * Maximum number of cluster members (and max member ID plus one) */ -#define ZT_CLUSTER_MAX_MEMBERS 256 +#define ZT_CLUSTER_MAX_MEMBERS 128 /** * Maximum allowed cluster message length in bytes */ -#define ZT_CLUSTER_MAX_MESSAGE_LENGTH 65535 +#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1444 * 6) /** * A null/empty sockaddr (all zero) to signify an unspecified socket address -- cgit v1.2.3 From 865acfa40f65626f41bbd718b645f57c7d6db9b2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 12:41:08 -0700 Subject: Cluster status plumbing. --- include/ZeroTierOne.h | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ node/Cluster.cpp | 56 +++++++++++++++++++++++++++++++++++++ node/Cluster.hpp | 7 +++++ node/Topology.cpp | 15 ++++++++++ node/Topology.hpp | 5 ++++ 5 files changed, 160 insertions(+) (limited to 'include') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 4de4a765..86bffcdf 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -133,6 +133,11 @@ extern "C" { */ #define ZT_CLUSTER_MAX_MEMBERS 128 +/** + * Maximum number of physical ZeroTier addresses a cluster member can report + */ +#define ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES 16 + /** * Maximum allowed cluster message length in bytes */ @@ -879,6 +884,78 @@ typedef struct { unsigned int nextHopCount; } ZT_CircuitTestReport; +/** + * A cluster member's status + */ +typedef struct { + /** + * This cluster member's ID (from 0 to 1-ZT_CLUSTER_MAX_MEMBERS) + */ + unsigned int id; + + /** + * Number of milliseconds since last 'alive' heartbeat message received via cluster backplane address + */ + unsigned int msSinceLastHeartbeat; + + /** + * Non-zero if cluster member is alive + */ + int alive; + + /** + * X, Y, and Z coordinates of this member (if specified, otherwise zero) + * + * What these mean depends on the location scheme being used for + * location-aware clustering. At present this is GeoIP and these + * will be the X, Y, and Z coordinates of the location on a spherical + * approximation of Earth where Earth's core is the origin (in km). + * They don't have to be perfect and need only be comparable with others + * to find shortest path via the standard vector distance formula. + */ + int x,y,z; + + /** + * Cluster member's last reported load + */ + uint64_t load; + + /** + * Number of peers this cluster member "has" + */ + uint64_t peers; + + /** + * Physical ZeroTier endpoints for this member (where peers are sent when directed here) + */ + struct sockaddr_storage zeroTierPhysicalEndpoints[ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES]; + + /** + * Number of physical ZeroTier endpoints this member is announcing + */ + unsigned int numZeroTierPhysicalEndpoints; +} ZT_ClusterMemberStatus; + +/** + * ZeroTier cluster status + */ +typedef struct { + /** + * My cluster member ID (a record for 'self' is included in member[]) + */ + unsigned int myId; + + /** + * Number of cluster members + */ + unsigned int clusterSize; + + /** + * Cluster member statuses + */ + ZT_ClusterMemberStatus member[ZT_CLUSTER_MAX_MEMBERS]; +} ZT_ClusterStatus; + /** * An instance of a ZeroTier One node (opaque) */ diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 900804b7..47794214 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -638,6 +638,62 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy } } +void Cluster::status(ZT_ClusterStatus &status) const +{ + const uint64_t now = RR->node->now(); + memset(&status,0,sizeof(ZT_ClusterStatus)); + ZT_ClusterMemberStatus *ms[ZT_CLUSTER_MAX_MEMBERS]; + memset(ms,0,sizeof(ms)); + + status.myId = _id; + + ms[_id] = &(status.member[status.clusterSize++]); + ms[_id]->id = _id; + ms[_id]->alive = 1; + ms[_id]->x = _x; + ms[_id]->y = _y; + ms[_id]->z = _z; + ms[_id]->peers = RR->topology->countAlive(); + for(std::vector::const_iterator ep(_zeroTierPhysicalEndpoints.begin());ep!=_zeroTierPhysicalEndpoints.end();++ep) { + if (ms[_id]->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check + break; + memcpy(&(ms[_id]->zeroTierPhysicalEndpoints[ms[_id]->numZeroTierPhysicalEndpoints++]),&(*ep),sizeof(struct sockaddr_storage)); + } + + { + Mutex::Lock _l1(_memberIds_m); + for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { + if (status.clusterSize >= ZT_CLUSTER_MAX_MEMBERS) // sanity check + break; + ZT_ClusterMemberStatus *s = ms[*mid] = &(status.member[status.clusterSize++]); + _Member &m = _members[*mid]; + Mutex::Lock ml(m.lock); + + s->id = *mid; + s->msSinceLastHeartbeat = (unsigned int)std::min((uint64_t)(~((unsigned int)0)),(now - m.lastReceivedAliveAnnouncement)); + s->alive = (s->msSinceLastHeartbeat < ZT_CLUSTER_TIMEOUT) ? 1 : 0; + s->x = m.x; + s->y = m.y; + s->z = m.z; + s->load = m.load; + for(std::vector::const_iterator ep(m.zeroTierPhysicalEndpoints.begin());ep!=m.zeroTierPhysicalEndpoints.end();++ep) { + if (s->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check + break; + memcpy(&(s->zeroTierPhysicalEndpoints[s->numZeroTierPhysicalEndpoints++]),&(*ep),sizeof(struct sockaddr_storage)); + } + } + } + + { + Mutex::Lock _l2(_peerAffinities_m); + for(std::vector<_PeerAffinity>::const_iterator pi(_peerAffinities.begin());pi!=_peerAffinities.end();++pi) { + unsigned int mid = pi->clusterMemberId(); + if ((ms[mid])&&(mid != _id)&&((now - pi->timestamp) < ZT_PEER_ACTIVITY_TIMEOUT)) + ++ms[mid]->peers; + } + } +} + void Cluster::_send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len) { if ((len + 3) > (ZT_CLUSTER_MAX_MESSAGE_LENGTH - (24 + 2 + 2))) // sanity check diff --git a/node/Cluster.hpp b/node/Cluster.hpp index daa81185..be346659 100644 --- a/node/Cluster.hpp +++ b/node/Cluster.hpp @@ -254,6 +254,13 @@ public: */ bool redirectPeer(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload); + /** + * Fill out ZT_ClusterStatus structure (from core API) + * + * @param status Reference to structure to hold result (anything there is replaced) + */ + void status(ZT_ClusterStatus &status) const; + private: void _send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len); void _flush(uint16_t memberId); diff --git a/node/Topology.cpp b/node/Topology.cpp index 624c4987..e56d1f47 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -332,6 +332,21 @@ void Topology::clean(uint64_t now) } } +unsigned long Topology::countAlive() const +{ + const uint64_t now = RR->node->now(); + unsigned long cnt = 0; + Mutex::Lock _l(_lock); + Hashtable< Address,SharedPtr >::Iterator i(const_cast(this)->_peers); + Address *a = (Address *)0; + SharedPtr *p = (SharedPtr *)0; + while (i.next(a,p)) { + if ((*p)->alive(now)) + ++cnt; + } + return cnt; +} + Identity Topology::_getIdentity(const Address &zta) { char p[128]; diff --git a/node/Topology.hpp b/node/Topology.hpp index 324ec000..ee9827b9 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -192,6 +192,11 @@ public: */ void clean(uint64_t now); + /** + * @return Number of 'alive' peers + */ + unsigned long countAlive() const; + /** * Apply a function or function object to all peers * -- cgit v1.2.3 From 5ff7733f84302bbd0ce0a578b3040122d1140f0f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 12:49:17 -0700 Subject: More plumbing of cluster status. --- include/ZeroTierOne.h | 11 +++++++++++ node/Node.cpp | 48 +++++++++++++++++++----------------------------- node/Node.hpp | 1 + 3 files changed, 31 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 86bffcdf..68c1e4d7 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1516,6 +1516,17 @@ void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId); */ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len); +/** + * Get the current status of the cluster from this node's point of view + * + * Calling this without clusterInit() or without cluster support will just + * zero out the structure and show a cluster size of zero. + * + * @param node Node instance + * @param cs Cluster status structure to fill with data + */ +void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs); + /** * Get ZeroTier One version * diff --git a/node/Node.cpp b/node/Node.cpp index c54d783f..2b298903 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -636,6 +636,18 @@ void Node::clusterHandleIncomingMessage(const void *msg,unsigned int len) #endif } +void Node::clusterStatus(ZT_ClusterStatus *cs) +{ + if (!cs) + return; +#ifdef ZT_ENABLE_CLUSTER + if (RR->cluster) + RR->cluster->status(*cs); + else +#endif + memset(cs,0,sizeof(ZT_ClusterStatus)); +} + /****************************************************************************/ /* Node methods used only within node/ */ /****************************************************************************/ @@ -947,15 +959,6 @@ enum ZT_ResultCode ZT_Node_clusterInit( } } -/** - * Add a member to this cluster - * - * Calling this without having called clusterInit() will do nothing. - * - * @param node Node instance - * @param memberId Member ID (must be less than or equal to ZT_CLUSTER_MAX_MEMBERS) - * @return OK or error if clustering is disabled, ID invalid, etc. - */ enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId) { try { @@ -965,14 +968,6 @@ enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId) } } -/** - * Remove a member from this cluster - * - * Calling this without having called clusterInit() will do nothing. - * - * @param node Node instance - * @param memberId Member ID to remove (nothing happens if not present) - */ void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId) { try { @@ -980,18 +975,6 @@ void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId) } catch ( ... ) {} } -/** - * Handle an incoming cluster state message - * - * The message itself contains cluster member IDs, and invalid or badly - * addressed messages will be silently discarded. - * - * Calling this without having called clusterInit() will do nothing. - * - * @param node Node instance - * @param msg Cluster message - * @param len Length of cluster message - */ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len) { try { @@ -999,6 +982,13 @@ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned } catch ( ... ) {} } +void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs) +{ + try { + reinterpret_cast(node)->clusterStatus(cs); + } catch ( ... ) {} +} + void ZT_version(int *major,int *minor,int *revision,unsigned long *featureFlags) { if (major) *major = ZEROTIER_ONE_VERSION_MAJOR; diff --git a/node/Node.hpp b/node/Node.hpp index b8bd4dc5..4094a79e 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -124,6 +124,7 @@ public: ZT_ResultCode clusterAddMember(unsigned int memberId); void clusterRemoveMember(unsigned int memberId); void clusterHandleIncomingMessage(const void *msg,unsigned int len); + void clusterStatus(ZT_ClusterStatus *cs); // Internal functions ------------------------------------------------------ -- cgit v1.2.3 From debed1ac2dce5822df234ce92bf4692ff1a081db Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 26 Oct 2015 13:06:10 -0700 Subject: Expose cluster status in /status JSON response. --- include/ZeroTierOne.h | 2 +- node/Cluster.cpp | 4 ++-- service/ControlPlane.cpp | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 68c1e4d7..7af4f760 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -953,7 +953,7 @@ typedef struct { /** * Cluster member statuses */ - ZT_ClusterMemberStatus member[ZT_CLUSTER_MAX_MEMBERS]; + ZT_ClusterMemberStatus members[ZT_CLUSTER_MAX_MEMBERS]; } ZT_ClusterStatus; /** diff --git a/node/Cluster.cpp b/node/Cluster.cpp index 47794214..9d25593a 100644 --- a/node/Cluster.cpp +++ b/node/Cluster.cpp @@ -647,7 +647,7 @@ void Cluster::status(ZT_ClusterStatus &status) const status.myId = _id; - ms[_id] = &(status.member[status.clusterSize++]); + ms[_id] = &(status.members[status.clusterSize++]); ms[_id]->id = _id; ms[_id]->alive = 1; ms[_id]->x = _x; @@ -665,7 +665,7 @@ void Cluster::status(ZT_ClusterStatus &status) const for(std::vector::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) { if (status.clusterSize >= ZT_CLUSTER_MAX_MEMBERS) // sanity check break; - ZT_ClusterMemberStatus *s = ms[*mid] = &(status.member[status.clusterSize++]); + ZT_ClusterMemberStatus *s = ms[*mid] = &(status.members[status.clusterSize++]); _Member &m = _members[*mid]; Mutex::Lock ml(m.lock); diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index 7affb08c..9770db90 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -354,8 +354,36 @@ unsigned int ControlPlane::handleRequest( if (ps[0] == "status") { responseContentType = "application/json"; + ZT_NodeStatus status; _node->status(&status); + + std::string clusterJson; +#ifdef ZT_ENABLE_CLUSTER + { + ZT_ClusterStatus cs; + _node->clusterStatus(&cs); + + char t[4096]; + Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members: [\n",cs.myId,cs.clusterSize); + clusterJson.append(t); + for(unsigned int i=0;i 0) ? clusterJson.c_str() : "null")); responseBody = json; scode = 200; } else if (ps[0] == "config") { -- cgit v1.2.3