diff --git a/docs/metrics/cluster/node-metrics.md b/docs/metrics/cluster/node-metrics.md
index ac1085bf8e..e37d3bbd06 100644
--- a/docs/metrics/cluster/node-metrics.md
+++ b/docs/metrics/cluster/node-metrics.md
@@ -14,3 +14,5 @@
| kube_node_status_condition | Gauge | The condition of a cluster node | | `node`=<node-address>
`condition`=<node-condition>
`status`=<true\|false\|unknown> | STABLE |
| kube_node_created | Gauge | Unix creation timestamp | seconds | `node`=<node-address> | STABLE |
| kube_node_deletion_timestamp | Gauge | Unix deletion timestamp | seconds | `node`=<node-address> | EXPERIMENTAL |
+| kube_node_volumes_in_use_count | Gauge | Number of volumes in use on the node | | `node`=<node-address> | STABLE |
+| kube_node_volumes_attached_count | Gauge | Number of volumes attached to the node | | `node`=<node-address> | STABLE |
diff --git a/internal/store/node.go b/internal/store/node.go
index d059e20df7..01300ca183 100644
--- a/internal/store/node.go
+++ b/internal/store/node.go
@@ -56,6 +56,8 @@ func nodeMetricFamilies(allowAnnotationsList, allowLabelsList []string) []genera
createNodeStatusCapacityFamilyGenerator(),
createNodeStatusConditionFamilyGenerator(),
createNodeStateAddressFamilyGenerator(),
+ createNodeVolumeCountGenerator(),
+ createNodeVolumeInUseGenerator(),
}
}
@@ -506,6 +508,44 @@ func createNodeStatusConditionFamilyGenerator() generator.FamilyGenerator {
)
}
+func createNodeVolumeCountGenerator() generator.FamilyGenerator {
+ return *generator.NewFamilyGeneratorWithStability(
+ "kube_node_volumes_attached_count",
+ "Number of volumes attached to the node",
+ metric.Gauge,
+ basemetrics.STABLE,
+ "",
+ wrapNodeFunc(func(n *v1.Node) *metric.Family {
+ return &metric.Family{
+ Metrics: []*metric.Metric{
+ {
+ Value: float64(len(n.Status.VolumesAttached)),
+ },
+ },
+ }
+ }),
+ )
+}
+
+func createNodeVolumeInUseGenerator() generator.FamilyGenerator {
+ return *generator.NewFamilyGeneratorWithStability(
+ "kube_node_volumes_in_use_count",
+ "Number of volumes in use on the node",
+ metric.Gauge,
+ basemetrics.STABLE,
+ "",
+ wrapNodeFunc(func(n *v1.Node) *metric.Family {
+ return &metric.Family{
+ Metrics: []*metric.Metric{
+ {
+ Value: float64(len(n.Status.VolumesInUse)),
+ },
+ },
+ }
+ }),
+ )
+}
+
func wrapNodeFunc(f func(*v1.Node) *metric.Family) func(interface{}) *metric.Family {
return func(obj interface{}) *metric.Family {
node := obj.(*v1.Node)
diff --git a/internal/store/node_test.go b/internal/store/node_test.go
index 6457aef20e..462fc67b11 100644
--- a/internal/store/node_test.go
+++ b/internal/store/node_test.go
@@ -298,6 +298,43 @@ func TestNodeStore(t *testing.T) {
`,
MetricNames: []string{"kube_node_status_addresses"},
},
+ {
+ Obj: &v1.Node{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "127.0.0.1",
+ },
+ Status: v1.NodeStatus{
+ VolumesAttached: []v1.AttachedVolume{
+ {Name: "volume1", DevicePath: "/dev/sda1"},
+ {Name: "volume2", DevicePath: "/dev/sda2"},
+ },
+ },
+ },
+ Want: `
+ # HELP kube_node_volumes_attached_count [STABLE] Number of volumes attached to the node
+ # TYPE kube_node_volumes_attached_count gauge
+ kube_node_volumes_attached_count{node="127.0.0.1"} 2
+ `,
+ MetricNames: []string{"kube_node_volumes_attached_count"},
+ },
+ {
+ Obj: &v1.Node{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "127.0.0.1",
+ },
+ Status: v1.NodeStatus{
+ VolumesInUse: []v1.UniqueVolumeName{
+ "volume1",
+ },
+ },
+ },
+ Want: `
+ # HELP kube_node_volumes_in_use_count [STABLE] Number of volumes in use on the node
+ # TYPE kube_node_volumes_in_use_count gauge
+ kube_node_volumes_in_use_count{node="127.0.0.1"} 1
+ `,
+ MetricNames: []string{"kube_node_volumes_in_use_count"},
+ },
}
for i, c := range cases {
c.Func = generator.ComposeMetricGenFuncs(nodeMetricFamilies(nil, nil))