Skip to content

Commit

Permalink
chore: add snasphot and rebuild history to bundle, disable resouce level
Browse files Browse the repository at this point in the history
dump

Signed-off-by: Abhinandan Purkait <[email protected]>
  • Loading branch information
Abhinandan-Purkait committed Jul 17, 2023
1 parent 6c7ad4c commit 2dfa7aa
Show file tree
Hide file tree
Showing 15 changed files with 446 additions and 281 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 16 additions & 57 deletions k8s/plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,15 +274,9 @@ kubectl mayastor dump
Usage: kubectl-mayastor dump [OPTIONS] <COMMAND>

Commands:
system Collects entire system information
volumes Collects information about all volumes and its descendants (replicas/pools/nodes)
volume Collects information about particular volume and its descendants matching to given volume ID
pools Collects information about all pools and its descendants (nodes)
pool Collects information about particular pool and its descendants matching to given pool ID
nodes Collects information about all nodes
node Collects information about particular node matching to given node ID
etcd Collects information from etcd
help Print this message or the help of the given subcommand(s)
system Collects entire system information
etcd Collects information from etcd
help Print this message or the help of the given subcommand(s)

Options:
-r, --rest <REST>
Expand All @@ -300,7 +294,7 @@ Options:
-d, --output-directory-path <OUTPUT_DIRECTORY_PATH>
Output directory path to store archive file [default: ./]
-n, --namespace <NAMESPACE>
Kubernetes namespace of mayastor service[default: mayastor]
Kubernetes namespace of mayastor service [default: mayastor]
-o, --output <OUTPUT>
The Output, viz yaml, json [default: none]
-j, --jaeger <JAEGER>
Expand All @@ -316,58 +310,23 @@ Supportability - collects state & log information of services and dumps it to a
**Examples**:
1. To collect entire mayastor system information into an archive file
```sh
## Command
kubectl mayastor dump system -d <output_directory> -n <mayastor_namespace>
```
To collect entire mayastor system information into an archive file
```sh
## Command
kubectl mayastor dump system -d <output_directory> -n <mayastor_namespace>
```
- Example command while running inside Kubernetes cluster nodes / system which
has access to cluster node ports
```sh
kubectl mayastor dump system -d /mayastor-dump -n mayastor
```
- Example command while running outside of Kubernetes cluster nodes where
nodes exist in private network (or) node ports are not exposed for outside cluster
```sh
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
```
2. To collect information about all mayastor volumes into an archive file
- Example command while running inside Kubernetes cluster nodes / system which
has access to cluster node ports
```sh
## Command
kubectl mayastor dump volumes -d <output_directory> -n <mayastor_namespace>
kubectl mayastor dump system -d /mayastor-dump -n mayastor
```
- Example command while running inside Kubernetes cluster nodes / system which
has access to cluster node ports
```sh
kubectl mayastor dump volumes -d /mayastor-dump -n mayastor
```
- Example command while running outside of Kubernetes cluster nodes where
nodes exist in private network (or) node ports are not exposed for outside cluster
```sh
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
```
**Note**: similarly to dump pools/nodes information then replace `volumes` with an associated resource type(`pools/nodes`).
3. To collect information about particular volume into an archive file
- Example command while running outside of Kubernetes cluster nodes where
nodes exist in private network (or) node ports are not exposed for outside cluster
```sh
## Command
kubectl mayastor dump volume <volume_name> -d <output_directory> -n <mayastor_namespace>
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
```
- Example command while running inside Kubernetes cluster nodes / system which
has access to cluster node ports
```sh
kubectl mayastor dump volume volume-1 -d /mayastor-dump -n mayastor
```
- Example command while running outside of Kubernetes cluster nodes where
nodes exist in private network (or) node ports are not exposed for outside cluster
```sh
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
```
<b>`--disable-log-collection` can be used to disable collection of logs.</b>
</details>
<details>
Expand Down
1 change: 0 additions & 1 deletion k8s/supportability/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ clap = { version = "4.1.4", features = ["color", "derive"] }
anyhow = "1.0.69"
humantime = "2.1.0"
async-trait = "0.1.64"
prettytable-rs = "^0.10"
serde = "1.0.152"
serde_json = "1.0.93"
serde_yaml = "0.9.17"
Expand Down
169 changes: 164 additions & 5 deletions k8s/supportability/src/collect/k8s_resources/client.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
use crate::collect::k8s_resources::common::KUBERNETES_HOST_LABEL_KEY;
use k8s_operators::diskpool::crd::DiskPool;

use k8s_openapi::api::{
apps::v1::{DaemonSet, Deployment, StatefulSet},
core::v1::{Event, Node, Pod},
};
use k8s_operators::diskpool::crd::DiskPool;
use kube::{api::ListParams, Api, Client, Resource};

use kube::{
api::{DynamicObject, ListParams},
discovery::{verbs, Scope},
Api, Client, Discovery, Resource,
};
use std::{collections::HashMap, convert::TryFrom};

const SNAPSHOT_GROUP: &str = "snapshot.storage.k8s.io";
const SNAPSHOT_VERSION: &str = "v1";
const VOLUME_SNAPSHOT_CLASS: &str = "VolumeSnapshotClass";
const VOLUME_SNAPSHOT_CONTENT: &str = "VolumeSnapshotContent";
const DRIVER: &str = "driver";
const SPEC: &str = "spec";

/// K8sResourceError holds errors that can obtain while fetching
/// information of Kubernetes Objects
#[allow(clippy::enum_variant_names)]
Expand Down Expand Up @@ -87,6 +98,43 @@ impl ClientSet {
self.client.clone()
}

/// Get a new api for a `dynamic_object` for the provided GVK.
pub(crate) async fn dynamic_object_api(
&self,
namespace: Option<&str>,
group_name: &str,
version: &str,
kind: &str,
) -> Result<Api<DynamicObject>, K8sResourceError> {
let discovery = Discovery::new(self.kube_client()).run().await?;
for group in discovery.groups() {
if group.name() == group_name {
for (ar, caps) in group.recommended_resources() {
if !caps.supports_operation(verbs::LIST) {
continue;
}
if ar.version == version && ar.kind == kind {
let result = match namespace {
None if caps.scope == Scope::Cluster => {
Ok(Api::all_with(self.kube_client(), &ar))
}
Some(ns) if caps.scope == Scope::Namespaced => {
Ok(Api::namespaced_with(self.kube_client(), ns, &ar))
}
_ => Err(K8sResourceError::CustomError(format!(
"DynamicObject Api not available for {kind} of {group_name}/{version}"
))),
};
return result;
}
}
}
}
Err(K8sResourceError::CustomError(format!(
"DynamicObject Api not available for {kind} of {group_name}/{version}"
)))
}

/// Fetch node objects from API-server then form and return map of node name to node object
pub(crate) async fn get_nodes_map(&self) -> Result<HashMap<String, Node>, K8sResourceError> {
let node_api: Api<Node> = Api::all(self.client.clone());
Expand Down Expand Up @@ -164,6 +212,115 @@ impl ClientSet {
Ok(pools.items)
}

/// Fetch list of volume snapshot classes based on the driver if provided.
pub(crate) async fn list_volumesnapshot_classes(
&self,
driver_selector: Option<&str>,
label_selector: Option<&str>,
field_selector: Option<&str>,
) -> Result<Vec<DynamicObject>, K8sResourceError> {
let list_params = ListParams::default()
.labels(label_selector.unwrap_or_default())
.fields(field_selector.unwrap_or_default());
let vsc_api: Api<DynamicObject> = self
.dynamic_object_api(
None,
SNAPSHOT_GROUP,
SNAPSHOT_VERSION,
VOLUME_SNAPSHOT_CLASS,
)
.await?;
let vscs = match vsc_api.list(&list_params).await {
Ok(val) => val,
Err(kube_error) => match kube_error {
kube::Error::Api(e) => {
if e.code == 404 {
return Ok(vec![]);
}
return Err(K8sResourceError::ClientError(kube::Error::Api(e)));
}
_ => return Err(K8sResourceError::ClientError(kube_error)),
},
};
Ok(vscs
.items
.into_iter()
.filter(|item| match driver_selector {
None => true,
Some(driver_selector) => match item.data.get(DRIVER) {
None => false,
Some(value) => match value.as_str() {
None => false,
Some(driver) => driver == driver_selector,
},
},
})
.collect())
}

/// Fetch list of volume snapshot contents based on the driver if provided.
pub(crate) async fn list_volumesnapshotcontents(
&self,
driver_selector: Option<&str>,
label_selector: Option<&str>,
field_selector: Option<&str>,
) -> Result<Vec<DynamicObject>, K8sResourceError> {
let mut list_params = ListParams::default()
.labels(label_selector.unwrap_or_default())
.fields(field_selector.unwrap_or_default())
.limit(2);
let vsc_api: Api<DynamicObject> = self
.dynamic_object_api(
None,
SNAPSHOT_GROUP,
SNAPSHOT_VERSION,
VOLUME_SNAPSHOT_CONTENT,
)
.await?;

let mut vscs_filtered: Vec<DynamicObject> = vec![];
loop {
let vscs = match vsc_api.list(&list_params).await {
Ok(val) => val,
Err(kube_error) => match kube_error {
kube::Error::Api(e) => {
if e.code == 404 {
return Ok(vec![]);
}
return Err(K8sResourceError::ClientError(kube::Error::Api(e)));
}
_ => return Err(K8sResourceError::ClientError(kube_error)),
},
};
vscs_filtered.append(
&mut vscs
.items
.into_iter()
.filter(|item| match driver_selector {
None => true,
Some(driver_selector) => match item.data.get(SPEC) {
None => false,
Some(value) => match value.get(DRIVER) {
None => false,
Some(value) => match value.as_str() {
None => false,
Some(driver) => driver == driver_selector,
},
},
},
})
.collect(),
);
match vscs.metadata.continue_ {
Some(token) if !token.is_empty() => {
list_params = list_params.continue_token(token.as_str())
}
_ => break,
};
}
Ok(vscs_filtered)
}

/// Fetch list of k8s events associated to given label_selector & field_selector
pub(crate) async fn get_events(
&self,
Expand All @@ -183,8 +340,10 @@ impl ClientSet {
let mut result = events_api.list(&list_params).await?;
events.append(&mut result.items);
match result.metadata.continue_ {
None => break,
Some(token) => list_params = list_params.continue_token(token.as_str()),
Some(token) if !token.is_empty() => {
list_params = list_params.continue_token(token.as_str())
}
_ => break,
};
}

Expand Down
42 changes: 42 additions & 0 deletions k8s/supportability/src/collect/k8s_resources/k8s_resource_dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use kube::Resource;
use serde::Serialize;
use std::{collections::HashSet, fs::File, io::Write, iter::FromIterator, path::PathBuf};

const MAYASTOR_CSI_DRIVER: &str = "io.openebs.csi-mayastor";

/// K8s resource dumper client
#[derive(Clone)]
pub(crate) struct K8sResourceDumperClient {
Expand Down Expand Up @@ -212,6 +214,46 @@ impl K8sResourceDumperClient {
}
}

log("\t Collecting Kubernetes VolumeSnapshotClass resources".to_string());
match self
.k8s_client
.list_volumesnapshot_classes(Some(MAYASTOR_CSI_DRIVER), None, None)
.await
{
Ok(vscs) => {
// NOTE: Unmarshalling object recevied from K8s API-server will not fail
let _ = create_file_and_write(
root_dir.clone(),
"volume_snapshot_classes.yaml".to_string(),
serde_yaml::to_string(&vscs)?,
)
.map_err(|e| errors.push(K8sResourceDumperError::IOError(e)));
}
Err(e) => {
errors.push(K8sResourceDumperError::K8sResourceError(e));
}
}

log("\t Collecting Kubernetes VolumeSnapshotContents resources".to_string());
match self
.k8s_client
.list_volumesnapshotcontents(Some(MAYASTOR_CSI_DRIVER), None, None)
.await
{
Ok(vscs) => {
// NOTE: Unmarshalling object recevied from K8s API-server will not fail
let _ = create_file_and_write(
root_dir.clone(),
"volume_snapshot_contents.yaml".to_string(),
serde_yaml::to_string(&vscs)?,
)
.map_err(|e| errors.push(K8sResourceDumperError::IOError(e)));
}
Err(e) => {
errors.push(K8sResourceDumperError::K8sResourceError(e));
}
}

// Fetch all Pods in provided NAMESPACE
log("\t Collecting Kuberbetes pod resources".to_string());
match self.k8s_client.get_pods("", "").await {
Expand Down
1 change: 1 addition & 0 deletions k8s/supportability/src/collect/resources/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod error;
pub mod node;
pub mod pool;
pub mod replica;
pub mod snapshot;
pub mod traits;
pub mod utils;
pub mod volume;
Loading

0 comments on commit 2dfa7aa

Please sign in to comment.