Skip to content

Commit 71b1fbe

Browse files
mayastor-borsAbhinandan-Purkait
andcommitted
chore(bors): merge pull request #300
300: Cherry-pick #296 r=Abhinandan-Purkait a=Abhinandan-Purkait Co-authored-by: Abhinandan Purkait <[email protected]>
2 parents c961ee0 + 6fd5020 commit 71b1fbe

File tree

17 files changed

+660
-390
lines changed

17 files changed

+660
-390
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

k8s/plugin/README.md

Lines changed: 10 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,9 @@ kubectl mayastor dump
274274
Usage: kubectl-mayastor dump [OPTIONS] <COMMAND>
275275

276276
Commands:
277-
system Collects entire system information
278-
volumes Collects information about all volumes and its descendants (replicas/pools/nodes)
279-
volume Collects information about particular volume and its descendants matching to given volume ID
280-
pools Collects information about all pools and its descendants (nodes)
281-
pool Collects information about particular pool and its descendants matching to given pool ID
282-
nodes Collects information about all nodes
283-
node Collects information about particular node matching to given node ID
284-
etcd Collects information from etcd
285-
help Print this message or the help of the given subcommand(s)
277+
system Collects entire system information
278+
etcd Collects information from etcd
279+
help Print this message or the help of the given subcommand(s)
286280

287281
Options:
288282
-r, --rest <REST>
@@ -300,7 +294,7 @@ Options:
300294
-d, --output-directory-path <OUTPUT_DIRECTORY_PATH>
301295
Output directory path to store archive file [default: ./]
302296
-n, --namespace <NAMESPACE>
303-
Kubernetes namespace of mayastor service[default: mayastor]
297+
Kubernetes namespace of mayastor service [default: mayastor]
304298
-o, --output <OUTPUT>
305299
The Output, viz yaml, json [default: none]
306300
-j, --jaeger <JAEGER>
@@ -316,58 +310,12 @@ Supportability - collects state & log information of services and dumps it to a
316310
317311
**Examples**:
318312
319-
1. To collect entire mayastor system information into an archive file
320-
```sh
321-
## Command
322-
kubectl mayastor dump system -d <output_directory> -n <mayastor_namespace>
323-
```
324-
325-
- Example command while running inside Kubernetes cluster nodes / system which
326-
has access to cluster node ports
327-
```sh
328-
kubectl mayastor dump system -d /mayastor-dump -n mayastor
329-
```
330-
- Example command while running outside of Kubernetes cluster nodes where
331-
nodes exist in private network (or) node ports are not exposed for outside cluster
332-
```sh
333-
kubectl mayastor dump system -d /mayastor-dump -r http://127.0.0.1:30011 -l http://127.0.0.1:3100 -e http://127.0.0.1:2379 -n mayastor
334-
```
335-
336-
2. To collect information about all mayastor volumes into an archive file
337-
```sh
338-
## Command
339-
kubectl mayastor dump volumes -d <output_directory> -n <mayastor_namespace>
340-
```
341-
342-
- Example command while running inside Kubernetes cluster nodes / system which
343-
has access to cluster node ports
344-
```sh
345-
kubectl mayastor dump volumes -d /mayastor-dump -n mayastor
346-
```
347-
- Example command while running outside of Kubernetes cluster nodes where
348-
nodes exist in private network (or) node ports are not exposed for outside cluster
349-
```sh
350-
kubectl mayastor dump volumes -d /mayastor-dump -r http://127.0.0.1:30011 -l http://127.0.0.1:3100 -e http://127.0.0.1:2379 -n mayastor
351-
```
352-
353-
**Note**: similarly to dump pools/nodes information then replace `volumes` with an associated resource type(`pools/nodes`).
354-
355-
3. To collect information about particular volume into an archive file
356-
```sh
357-
## Command
358-
kubectl mayastor dump volume <volume_name> -d <output_directory> -n <mayastor_namespace>
359-
```
360-
361-
- Example command while running inside Kubernetes cluster nodes / system which
362-
has access to cluster node ports
363-
```sh
364-
kubectl mayastor dump volume volume-1 -d /mayastor-dump -n mayastor
365-
```
366-
- Example command while running outside of Kubernetes cluster nodes where
367-
nodes exist in private network (or) node ports are not exposed for outside cluster
368-
```sh
369-
kubectl mayastor dump volume volume-1 -d /mayastor-dump -r http://127.0.0.1:30011 -l http://127.0.0.1:3100 -e http://127.0.0.1:2379 -n mayastor
370-
```
313+
To collect entire mayastor system information into an archive file
314+
```sh
315+
## Command
316+
kubectl mayastor dump system -d <output_directory> -n <mayastor_namespace>
317+
```
318+
<b>`--disable-log-collection` can be used to disable collection of logs.</b>
371319
372320
</details>
373321
<details>

k8s/supportability/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ clap = { version = "4.1.4", features = ["color", "derive"] }
2424
anyhow = "1.0.69"
2525
humantime = "2.1.0"
2626
async-trait = "0.1.64"
27-
prettytable-rs = "^0.10"
2827
serde = "1.0.152"
2928
serde_json = "1.0.93"
3029
serde_yaml = "0.9.17"

k8s/supportability/src/collect/common.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
use crate::collect::{error::Error, resources::traits::Topologer, rest_wrapper::RestClient};
1+
use crate::collect::{error::Error, rest_wrapper::RestClient};
22
use chrono::Local;
33

4+
#[cfg(debug_assertions)]
5+
use crate::collect::resources::traits::Topologer;
6+
47
/// DumpConfig helps to create new instance of Dumper
58
#[derive(Debug)]
69
pub(crate) struct DumpConfig {
@@ -20,7 +23,8 @@ pub(crate) struct DumpConfig {
2023
pub(crate) kube_config_path: Option<std::path::PathBuf>,
2124
/// Specifies the timeout value to interact with other systems
2225
pub(crate) timeout: humantime::Duration,
23-
/// Topologer implements functionality to build topological infotmation of system
26+
#[cfg(debug_assertions)]
27+
/// Topologer implements functionality to build topological information of system
2428
pub(crate) topologer: Option<Box<dyn Topologer>>,
2529
pub(crate) output_format: OutputFormat,
2630
}

k8s/supportability/src/collect/k8s_resources/client.rs

Lines changed: 164 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
use crate::collect::k8s_resources::common::KUBERNETES_HOST_LABEL_KEY;
2+
use k8s_operators::diskpool::crd::DiskPool;
3+
24
use k8s_openapi::api::{
35
apps::v1::{DaemonSet, Deployment, StatefulSet},
46
core::v1::{Event, Node, Pod},
57
};
6-
use k8s_operators::diskpool::crd::DiskPool;
7-
use kube::{api::ListParams, Api, Client, Resource};
8-
8+
use kube::{
9+
api::{DynamicObject, ListParams},
10+
discovery::{verbs, Scope},
11+
Api, Client, Discovery, Resource,
12+
};
913
use std::{collections::HashMap, convert::TryFrom};
1014

15+
const SNAPSHOT_GROUP: &str = "snapshot.storage.k8s.io";
16+
const SNAPSHOT_VERSION: &str = "v1";
17+
const VOLUME_SNAPSHOT_CLASS: &str = "VolumeSnapshotClass";
18+
const VOLUME_SNAPSHOT_CONTENT: &str = "VolumeSnapshotContent";
19+
const DRIVER: &str = "driver";
20+
const SPEC: &str = "spec";
21+
1122
/// K8sResourceError holds errors that can obtain while fetching
1223
/// information of Kubernetes Objects
1324
#[allow(clippy::enum_variant_names)]
@@ -87,6 +98,43 @@ impl ClientSet {
8798
self.client.clone()
8899
}
89100

101+
/// Get a new api for a `dynamic_object` for the provided GVK.
102+
pub(crate) async fn dynamic_object_api(
103+
&self,
104+
namespace: Option<&str>,
105+
group_name: &str,
106+
version: &str,
107+
kind: &str,
108+
) -> Result<Api<DynamicObject>, K8sResourceError> {
109+
let discovery = Discovery::new(self.kube_client()).run().await?;
110+
for group in discovery.groups() {
111+
if group.name() == group_name {
112+
for (ar, caps) in group.recommended_resources() {
113+
if !caps.supports_operation(verbs::LIST) {
114+
continue;
115+
}
116+
if ar.version == version && ar.kind == kind {
117+
let result = match namespace {
118+
None if caps.scope == Scope::Cluster => {
119+
Ok(Api::all_with(self.kube_client(), &ar))
120+
}
121+
Some(ns) if caps.scope == Scope::Namespaced => {
122+
Ok(Api::namespaced_with(self.kube_client(), ns, &ar))
123+
}
124+
_ => Err(K8sResourceError::CustomError(format!(
125+
"DynamicObject Api not available for {kind} of {group_name}/{version}"
126+
))),
127+
};
128+
return result;
129+
}
130+
}
131+
}
132+
}
133+
Err(K8sResourceError::CustomError(format!(
134+
"DynamicObject Api not available for {kind} of {group_name}/{version}"
135+
)))
136+
}
137+
90138
/// Fetch node objects from API-server then form and return map of node name to node object
91139
pub(crate) async fn get_nodes_map(&self) -> Result<HashMap<String, Node>, K8sResourceError> {
92140
let node_api: Api<Node> = Api::all(self.client.clone());
@@ -164,6 +212,115 @@ impl ClientSet {
164212
Ok(pools.items)
165213
}
166214

215+
/// Fetch list of volume snapshot classes based on the driver if provided.
216+
pub(crate) async fn list_volumesnapshot_classes(
217+
&self,
218+
driver_selector: Option<&str>,
219+
label_selector: Option<&str>,
220+
field_selector: Option<&str>,
221+
) -> Result<Vec<DynamicObject>, K8sResourceError> {
222+
let list_params = ListParams::default()
223+
.labels(label_selector.unwrap_or_default())
224+
.fields(field_selector.unwrap_or_default());
225+
let vsc_api: Api<DynamicObject> = self
226+
.dynamic_object_api(
227+
None,
228+
SNAPSHOT_GROUP,
229+
SNAPSHOT_VERSION,
230+
VOLUME_SNAPSHOT_CLASS,
231+
)
232+
.await?;
233+
let vscs = match vsc_api.list(&list_params).await {
234+
Ok(val) => val,
235+
Err(kube_error) => match kube_error {
236+
kube::Error::Api(e) => {
237+
if e.code == 404 {
238+
return Ok(vec![]);
239+
}
240+
return Err(K8sResourceError::ClientError(kube::Error::Api(e)));
241+
}
242+
_ => return Err(K8sResourceError::ClientError(kube_error)),
243+
},
244+
};
245+
Ok(vscs
246+
.items
247+
.into_iter()
248+
.filter(|item| match driver_selector {
249+
None => true,
250+
Some(driver_selector) => match item.data.get(DRIVER) {
251+
None => false,
252+
Some(value) => match value.as_str() {
253+
None => false,
254+
Some(driver) => driver == driver_selector,
255+
},
256+
},
257+
})
258+
.collect())
259+
}
260+
261+
/// Fetch list of volume snapshot contents based on the driver if provided.
262+
pub(crate) async fn list_volumesnapshotcontents(
263+
&self,
264+
driver_selector: Option<&str>,
265+
label_selector: Option<&str>,
266+
field_selector: Option<&str>,
267+
) -> Result<Vec<DynamicObject>, K8sResourceError> {
268+
let mut list_params = ListParams::default()
269+
.labels(label_selector.unwrap_or_default())
270+
.fields(field_selector.unwrap_or_default())
271+
.limit(2);
272+
let vsc_api: Api<DynamicObject> = self
273+
.dynamic_object_api(
274+
None,
275+
SNAPSHOT_GROUP,
276+
SNAPSHOT_VERSION,
277+
VOLUME_SNAPSHOT_CONTENT,
278+
)
279+
.await?;
280+
281+
let mut vscs_filtered: Vec<DynamicObject> = vec![];
282+
loop {
283+
let vscs = match vsc_api.list(&list_params).await {
284+
Ok(val) => val,
285+
Err(kube_error) => match kube_error {
286+
kube::Error::Api(e) => {
287+
if e.code == 404 {
288+
return Ok(vec![]);
289+
}
290+
return Err(K8sResourceError::ClientError(kube::Error::Api(e)));
291+
}
292+
_ => return Err(K8sResourceError::ClientError(kube_error)),
293+
},
294+
};
295+
vscs_filtered.append(
296+
&mut vscs
297+
.items
298+
.into_iter()
299+
.filter(|item| match driver_selector {
300+
None => true,
301+
Some(driver_selector) => match item.data.get(SPEC) {
302+
None => false,
303+
Some(value) => match value.get(DRIVER) {
304+
None => false,
305+
Some(value) => match value.as_str() {
306+
None => false,
307+
Some(driver) => driver == driver_selector,
308+
},
309+
},
310+
},
311+
})
312+
.collect(),
313+
);
314+
match vscs.metadata.continue_ {
315+
Some(token) if !token.is_empty() => {
316+
list_params = list_params.continue_token(token.as_str())
317+
}
318+
_ => break,
319+
};
320+
}
321+
Ok(vscs_filtered)
322+
}
323+
167324
/// Fetch list of k8s events associated to given label_selector & field_selector
168325
pub(crate) async fn get_events(
169326
&self,
@@ -183,8 +340,10 @@ impl ClientSet {
183340
let mut result = events_api.list(&list_params).await?;
184341
events.append(&mut result.items);
185342
match result.metadata.continue_ {
186-
None => break,
187-
Some(token) => list_params = list_params.continue_token(token.as_str()),
343+
Some(token) if !token.is_empty() => {
344+
list_params = list_params.continue_token(token.as_str())
345+
}
346+
_ => break,
188347
};
189348
}
190349

0 commit comments

Comments
 (0)