1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//! etcd's statistics API.

use std::collections::HashMap;

use futures::{Future, IntoFuture, Stream};
use futures::stream::futures_unordered;
use hyper::Uri;
use hyper::client::Connect;

use client::{Client, Response};
use error::Error;

/// Statistics about an etcd cluster leader.
#[derive(Clone, Debug, Deserialize)]
pub struct LeaderStats {
    /// A unique identifier of a leader member.
    pub leader: String,
    /// Statistics for each peer in the cluster keyed by each peer's unique identifier.
    pub followers: HashMap<String, FollowerStats>,
}

/// Statistics about the health of a single etcd follower node.
#[derive(Clone, Debug, Deserialize)]
pub struct FollowerStats {
    /// Counts of Raft RPC request successes and failures to this follower.
    pub counts: CountStats,
    /// Latency statistics for this follower.
    pub latency: LatencyStats,
}

/// Statistics about the number of successful and failed Raft RPC requests to an etcd node.
#[derive(Clone, Debug, Deserialize)]
pub struct CountStats {
    /// The number of times an RPC request to the node failed.
    pub fail: u64,
    /// The number of times an RPC request to the node succeeded.
    pub success: u64,
}

/// Statistics about the network latency to an etcd node.
#[derive(Clone, Debug, Deserialize)]
pub struct LatencyStats {
    /// The average observed latency to the node, in seconds.
    pub average: f64,
    /// The current observed latency to the node, in seconds.
    pub current: f64,
    /// The maximum observed latency to the node, in seconds.
    pub maximum: f64,
    /// The minimum observed latency to the node, in seconds.
    pub minimum: f64,
    /// The standard deviation of latency to the node.
    #[serde(rename = "standardDeviation")]
    pub standard_deviation: f64,
}

/// Statistics about an etcd cluster member.
#[derive(Clone, Debug, Deserialize)]
pub struct SelfStats {
    /// The unique Raft ID of the member.
    pub id: String,
    /// The member's name.
    pub name: String,
    /// A small amount of information about the leader of the cluster.
    #[serde(rename = "leaderInfo")]
    pub leader_info: LeaderInfo,
    /// The number of received requests.
    #[serde(rename = "recvAppendRequestCnt")]
    pub received_append_request_count: u64,
    /// The bandwidth rate of received requests.
    #[serde(rename = "recvBandwidthRate")]
    pub received_bandwidth_rate: Option<f64>,
    #[serde(rename = "recvPkgRate")]
    /// The package rate of received requests.
    pub received_package_rate: Option<f64>,
    /// The number of sent requests.
    #[serde(rename = "sendAppendRequestCnt")]
    pub sent_append_request_count: u64,
    /// The bandwidth rate of sent requests.
    #[serde(rename = "sendBandwidthRate")]
    pub sent_bandwidth_rate: Option<f64>,
    /// The package rate of sent requests.
    #[serde(rename = "sendPkgRate")]
    pub sent_package_rate: Option<f64>,
    /// The time the member started.
    #[serde(rename = "startTime")]
    pub start_time: String,
    /// The Raft state of the member.
    pub state: String,
}

/// A small amount of information about the leader of the cluster.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct LeaderInfo {
    /// The unique Raft ID of the leader.
    #[serde(rename = "leader")]
    pub id: String,
    /// The time the leader started.
    #[serde(rename = "startTime")]
    pub start_time: String,
    /// The amount of time the leader has been up.
    pub uptime: String,
}

/// Statistics about the operations handled by an etcd member.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct StoreStats {
    /// The number of failed compare and delete operations.
    #[serde(rename = "compareAndDeleteFail")]
    pub compare_and_delete_fail: u64,
    /// The number of successful compare and delete operations.
    #[serde(rename = "compareAndDeleteSuccess")]
    pub compare_and_delete_success: u64,
    /// The number of failed compare and swap operations.
    #[serde(rename = "compareAndSwapFail")]
    pub compare_and_swap_fail: u64,
    /// The number of successful compare and swap operations.
    #[serde(rename = "compareAndSwapSuccess")]
    pub compare_and_swap_success: u64,
    /// The number of failed create operations.
    #[serde(rename = "createFail")]
    pub create_fail: u64,
    /// The number of successful create operations.
    #[serde(rename = "createSuccess")]
    pub create_success: u64,
    /// The number of failed delete operations.
    #[serde(rename = "deleteFail")]
    pub delete_fail: u64,
    /// The number of successful delete operations.
    #[serde(rename = "deleteSuccess")]
    pub delete_success: u64,
    /// The number of expire operations.
    #[serde(rename = "expireCount")]
    pub expire_count: u64,
    /// The number of failed get operations.
    #[serde(rename = "getsFail")]
    pub get_fail: u64,
    /// The number of successful get operations.
    #[serde(rename = "getsSuccess")]
    pub get_success: u64,
    /// The number of failed set operations.
    #[serde(rename = "setsFail")]
    pub set_fail: u64,
    /// The number of successful set operations.
    #[serde(rename = "setsSuccess")]
    pub set_success: u64,
    /// The number of failed update operations.
    #[serde(rename = "updateFail")]
    pub update_fail: u64,
    /// The number of successful update operations.
    #[serde(rename = "updateSuccess")]
    pub update_success: u64,
    /// The number of watchers.
    pub watchers: u64,
}

/// Returns statistics about the leader member of a cluster.
///
/// Fails if JSON decoding fails, which suggests a bug in our schema.
pub fn leader_stats<C>(
    client: &Client<C>,
) -> Box<Future<Item = Response<LeaderStats>, Error = Error>>
where
    C: Clone + Connect,
{
    let url = build_url(&client.endpoints()[0], "v2/stats/leader");
    let uri = url.parse().map_err(Error::from).into_future();

    client.request(uri)
}

/// Returns statistics about each cluster member the client was initialized with.
///
/// Fails if JSON decoding fails, which suggests a bug in our schema.
pub fn self_stats<C>(client: &Client<C>) -> Box<Stream<Item = Response<SelfStats>, Error = Error>>
where
    C: Clone + Connect,
{
    let futures = client.endpoints().iter().map(|endpoint| {
        let url = build_url(&endpoint, "v2/stats/self");
        let uri = url.parse().map_err(Error::from).into_future();

        client.request(uri)
    });

    Box::new(futures_unordered(futures))
}

/// Returns statistics about operations handled by each etcd member the client was initialized
/// with.
///
/// Fails if JSON decoding fails, which suggests a bug in our schema.
pub fn store_stats<C>(client: &Client<C>) -> Box<Stream<Item = Response<StoreStats>, Error = Error>>
where
    C: Clone + Connect,
{
    let futures = client.endpoints().iter().map(|endpoint| {
        let url = build_url(&endpoint, "v2/stats/store");
        let uri = url.parse().map_err(Error::from).into_future();

        client.request(uri)
    });

    Box::new(futures_unordered(futures))
}

/// Constructs the full URL for an API call.
fn build_url(endpoint: &Uri, path: &str) -> String {
    let maybe_slash = if endpoint.as_ref().ends_with("/") {
        ""
    } else {
        "/"
    };

    format!("{}{}{}", endpoint, maybe_slash, path)
}