package collectors import ( "io/ioutil" "net/http" "net/http/httptest" "regexp" "testing" "github.com/prometheus/client_golang/prometheus" ) func TestOSDCollector(t *testing.T) { for _, tt := range []struct { cmdOut map[string]string regexes []*regexp.Regexp }{ { cmdOut: map[string]string{ "ceph osd df": ` { "nodes": [ { "id": 0, "name": "osd.0", "type": "osd", "type_id": 0, "crush_weight": 0.010391, "depth": 2, "reweight": 1.000000, "kb": 11150316, "kb_used": 40772, "kb_avail": 11109544, "utilization": 0.365658, "var": 1.053676, "pgs": 283 }, { "id": 2, "name": "osd.2", "type": "osd", "type_id": 0, "crush_weight": 0.010391, "depth": 2, "reweight": 1.000000, "kb": 11150316, "kb_used": 36712, "kb_avail": 11113604, "utilization": 0.329246, "var": 0.948753, "pgs": 162 }, { "id": 1, "name": "osd.1", "type": "osd", "type_id": 0, "crush_weight": 0.010391, "depth": 2, "reweight": 1.000000, "kb": 11150316, "kb_used": 40512, "kb_avail": 11109804, "utilization": 0.363326, "var": 1.046957, "pgs": 279 }, { "id": 3, "name": "osd.3", "type": "osd", "type_id": 0, "crush_weight": 0.010391, "depth": 2, "reweight": 1.000000, "kb": 11150316, "kb_used": 36784, "kb_avail": 11113532, "utilization": 0.329892, "var": 0.950614, "pgs": 164 }, { "id": 4, "name": "osd.4", "type": "osd", "type_id": 0, "crush_weight": 0.010391, "depth": 2, "reweight": 0, "kb": 0, "kb_used": 0, "kb_avail": 0, "utilization": -nan, "var": -nan, "pgs": 0 } ], "stray": [], "summary": { "total_kb": 44601264, "total_kb_used": 154780, "total_kb_avail": 44446484, "average_utilization": 0.347031, "min_var": 0.948753, "max_var": 1.053676, "dev": 0.017482 } }`, }, regexes: []*regexp.Regexp{ regexp.MustCompile(`ceph_osd_crush_weight{cluster="ceph",osd="osd.0"} 0.010391`), regexp.MustCompile(`ceph_osd_crush_weight{cluster="ceph",osd="osd.1"} 0.010391`), regexp.MustCompile(`ceph_osd_crush_weight{cluster="ceph",osd="osd.2"} 0.010391`), regexp.MustCompile(`ceph_osd_crush_weight{cluster="ceph",osd="osd.3"} 0.010391`), regexp.MustCompile(`ceph_osd_crush_weight{cluster="ceph",osd="osd.4"} 0.010391`), regexp.MustCompile(`ceph_osd_depth{cluster="ceph",osd="osd.0"} 2`), regexp.MustCompile(`ceph_osd_depth{cluster="ceph",osd="osd.1"} 2`), regexp.MustCompile(`ceph_osd_depth{cluster="ceph",osd="osd.2"} 2`), regexp.MustCompile(`ceph_osd_depth{cluster="ceph",osd="osd.3"} 2`), regexp.MustCompile(`ceph_osd_depth{cluster="ceph",osd="osd.4"} 2`), regexp.MustCompile(`ceph_osd_reweight{cluster="ceph",osd="osd.0"} 1`), regexp.MustCompile(`ceph_osd_reweight{cluster="ceph",osd="osd.1"} 1`), regexp.MustCompile(`ceph_osd_reweight{cluster="ceph",osd="osd.2"} 1`), regexp.MustCompile(`ceph_osd_reweight{cluster="ceph",osd="osd.3"} 1`), regexp.MustCompile(`ceph_osd_reweight{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_bytes{cluster="ceph",osd="osd.0"} 1.1150316e`), regexp.MustCompile(`ceph_osd_bytes{cluster="ceph",osd="osd.1"} 1.1150316e`), regexp.MustCompile(`ceph_osd_bytes{cluster="ceph",osd="osd.2"} 1.1150316e`), regexp.MustCompile(`ceph_osd_bytes{cluster="ceph",osd="osd.3"} 1.1150316e`), regexp.MustCompile(`ceph_osd_bytes{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_used_bytes{cluster="ceph",osd="osd.0"} 4.0772e`), regexp.MustCompile(`ceph_osd_used_bytes{cluster="ceph",osd="osd.1"} 4.0512e`), regexp.MustCompile(`ceph_osd_used_bytes{cluster="ceph",osd="osd.2"} 3.6712e`), regexp.MustCompile(`ceph_osd_used_bytes{cluster="ceph",osd="osd.3"} 3.6784e`), regexp.MustCompile(`ceph_osd_used_bytes{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_avail_bytes{cluster="ceph",osd="osd.0"} 1.1109544e`), regexp.MustCompile(`ceph_osd_avail_bytes{cluster="ceph",osd="osd.1"} 1.1109804e`), regexp.MustCompile(`ceph_osd_avail_bytes{cluster="ceph",osd="osd.2"} 1.1113604e`), regexp.MustCompile(`ceph_osd_avail_bytes{cluster="ceph",osd="osd.3"} 1.1113532e`), regexp.MustCompile(`ceph_osd_avail_bytes{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_utilization{cluster="ceph",osd="osd.0"} 0.365658`), regexp.MustCompile(`ceph_osd_utilization{cluster="ceph",osd="osd.1"} 0.363326`), regexp.MustCompile(`ceph_osd_utilization{cluster="ceph",osd="osd.2"} 0.329246`), regexp.MustCompile(`ceph_osd_utilization{cluster="ceph",osd="osd.3"} 0.329892`), regexp.MustCompile(`ceph_osd_utilization{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_variance{cluster="ceph",osd="osd.0"} 1.053676`), regexp.MustCompile(`ceph_osd_variance{cluster="ceph",osd="osd.1"} 1.046957`), regexp.MustCompile(`ceph_osd_variance{cluster="ceph",osd="osd.2"} 0.948753`), regexp.MustCompile(`ceph_osd_variance{cluster="ceph",osd="osd.3"} 0.950614`), regexp.MustCompile(`ceph_osd_variance{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_pgs{cluster="ceph",osd="osd.0"} 283`), regexp.MustCompile(`ceph_osd_pgs{cluster="ceph",osd="osd.1"} 279`), regexp.MustCompile(`ceph_osd_pgs{cluster="ceph",osd="osd.2"} 162`), regexp.MustCompile(`ceph_osd_pgs{cluster="ceph",osd="osd.3"} 164`), regexp.MustCompile(`ceph_osd_pgs{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_total_bytes{cluster="ceph"} 4.4601264e`), regexp.MustCompile(`ceph_osd_total_used_bytes{cluster="ceph"} 1.5478e`), regexp.MustCompile(`ceph_osd_total_avail_bytes{cluster="ceph"} 4.4446484e`), regexp.MustCompile(`ceph_osd_average_utilization{cluster="ceph"} 0.347031`), }, }, { cmdOut: map[string]string{ "ceph osd perf": ` { "osd_perf_infos": [ { "id": 4, "perf_stats": { "commit_latency_ms": 0, "apply_latency_ms": 0 } }, { "id": 3, "perf_stats": { "commit_latency_ms": 1, "apply_latency_ms": 64 } }, { "id": 2, "perf_stats": { "commit_latency_ms": 2, "apply_latency_ms": 79 } }, { "id": 1, "perf_stats": { "commit_latency_ms": 2, "apply_latency_ms": 39 } }, { "id": 0, "perf_stats": { "commit_latency_ms": 2, "apply_latency_ms": 31 } } ] }`, }, regexes: []*regexp.Regexp{ regexp.MustCompile(`ceph_osd_perf_commit_latency_seconds{cluster="ceph",osd="osd.0"} 0.002`), regexp.MustCompile(`ceph_osd_perf_commit_latency_seconds{cluster="ceph",osd="osd.1"} 0.002`), regexp.MustCompile(`ceph_osd_perf_commit_latency_seconds{cluster="ceph",osd="osd.2"} 0.002`), regexp.MustCompile(`ceph_osd_perf_commit_latency_seconds{cluster="ceph",osd="osd.3"} 0.001`), regexp.MustCompile(`ceph_osd_perf_commit_latency_seconds{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_perf_apply_latency_seconds{cluster="ceph",osd="osd.0"} 0.031`), regexp.MustCompile(`ceph_osd_perf_apply_latency_seconds{cluster="ceph",osd="osd.1"} 0.039`), regexp.MustCompile(`ceph_osd_perf_apply_latency_seconds{cluster="ceph",osd="osd.2"} 0.079`), regexp.MustCompile(`ceph_osd_perf_apply_latency_seconds{cluster="ceph",osd="osd.3"} 0.064`), regexp.MustCompile(`ceph_osd_perf_apply_latency_seconds{cluster="ceph",osd="osd.4"} 0`), }, }, { cmdOut: map[string]string{ "ceph osd dump": ` { "osds": [ { "osd": 0, "uuid": "135b53c3", "up": 1, "in": 1 }, { "osd": 1, "uuid": "370a33f2", "up": 1, "in": 1 }, { "osd": 2, "uuid": "ca9ab3de", "up": 1, "in": 1, "state": [ "nearfull", "exists", "up" ] }, { "osd": 3, "uuid": "bef98b10", "up": 1, "in": 1, "state": [ "full", "backfillfull", "exists", "up" ] }, { "osd": 4, "uuid": "5936c9e8", "up": 0, "in": 0, "state": [ "backfillfull", "exists", "up" ] } ] }`, }, regexes: []*regexp.Regexp{ regexp.MustCompile(`ceph_osd_in{cluster="ceph",osd="osd.0"} 1`), regexp.MustCompile(`ceph_osd_in{cluster="ceph",osd="osd.1"} 1`), regexp.MustCompile(`ceph_osd_in{cluster="ceph",osd="osd.2"} 1`), regexp.MustCompile(`ceph_osd_in{cluster="ceph",osd="osd.3"} 1`), regexp.MustCompile(`ceph_osd_in{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_up{cluster="ceph",osd="osd.0"} 1`), regexp.MustCompile(`ceph_osd_up{cluster="ceph",osd="osd.1"} 1`), regexp.MustCompile(`ceph_osd_up{cluster="ceph",osd="osd.2"} 1`), regexp.MustCompile(`ceph_osd_up{cluster="ceph",osd="osd.3"} 1`), regexp.MustCompile(`ceph_osd_up{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_full{cluster="ceph",osd="osd.0"} 0`), regexp.MustCompile(`ceph_osd_full{cluster="ceph",osd="osd.1"} 0`), regexp.MustCompile(`ceph_osd_full{cluster="ceph",osd="osd.2"} 0`), regexp.MustCompile(`ceph_osd_full{cluster="ceph",osd="osd.3"} 1`), regexp.MustCompile(`ceph_osd_full{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_near_full{cluster="ceph",osd="osd.0"} 0`), regexp.MustCompile(`ceph_osd_near_full{cluster="ceph",osd="osd.1"} 0`), regexp.MustCompile(`ceph_osd_near_full{cluster="ceph",osd="osd.2"} 1`), regexp.MustCompile(`ceph_osd_near_full{cluster="ceph",osd="osd.3"} 0`), regexp.MustCompile(`ceph_osd_near_full{cluster="ceph",osd="osd.4"} 0`), regexp.MustCompile(`ceph_osd_backfill_full{cluster="ceph",osd="osd.0"} 0`), regexp.MustCompile(`ceph_osd_backfill_full{cluster="ceph",osd="osd.1"} 0`), regexp.MustCompile(`ceph_osd_backfill_full{cluster="ceph",osd="osd.2"} 0`), regexp.MustCompile(`ceph_osd_backfill_full{cluster="ceph",osd="osd.3"} 1`), regexp.MustCompile(`ceph_osd_backfill_full{cluster="ceph",osd="osd.4"} 1`), }, }, { cmdOut: map[string]string{ "ceph pg dump pgs_brief": ` [ { "acting": [ 1, 2, 3, 4 ], "acting_primary": 1, "pgid": "81.1fff", "state": "active+clean" }, { "acting": [ 10, 11, 12, 13 ], "acting_primary": 10, "pgid": "82.1fff", "state": "active+clean+scrubbing" }, { "acting": [ 20, 21, 22, 23 ], "acting_primary": 20, "pgid": "83.1fff", "state": "active+clean+scrubbing+deep" } ]`, }, regexes: []*regexp.Regexp{ regexp.MustCompile(`ceph_osd_scrub_state{cluster="ceph",osd="osd.10"} 1`), regexp.MustCompile(`ceph_osd_scrub_state{cluster="ceph",osd="osd.11"} 1`), regexp.MustCompile(`ceph_osd_scrub_state{cluster="ceph",osd="osd.12"} 1`), regexp.MustCompile(`ceph_osd_scrub_state{cluster="ceph",osd="osd.13"} 1`), regexp.MustCompile(`ceph_osd_scrub_state{cluster="ceph",osd="osd.20"} 2`), regexp.MustCompile(`ceph_osd_scrub_state{cluster="ceph",osd="osd.21"} 2`), regexp.MustCompile(`ceph_osd_scrub_state{cluster="ceph",osd="osd.22"} 2`), regexp.MustCompile(`ceph_osd_scrub_state{cluster="ceph",osd="osd.23"} 2`), }, }, { cmdOut: map[string]string{ "ceph osd tree down": ` { "nodes": [], "stray": [ { "id": 524, "name": "osd.524", "type": "osd", "type_id": 0, "crush_weight": 0.000000, "depth": 0, "exists": 1, "status": "destroyed", "reweight": 0.000000, "primary_affinity": 1.000000 } ] }`, }, regexes: []*regexp.Regexp{ regexp.MustCompile(`ceph_osd_down{cluster="ceph",osd="osd.524",status="destroyed"} 1`), }, }, { cmdOut: map[string]string{ "ceph osd tree down": ` { "nodes": [], "stray": [ { "id": 524, "name": "osd.524", "type": "osd", "type_id": 0, "crush_weight": 0.000000, "depth": 0, "exists": 1, "status": "down", "reweight": 0.000000, "primary_affinity": 1.000000 } ] }`, }, regexes: []*regexp.Regexp{ regexp.MustCompile(`ceph_osd_down{cluster="ceph",osd="osd.524",status="down"} 1`), }, }, { cmdOut: map[string]string{ "ceph osd tree down": ` { "nodes": [ { "id": -18, "name": "data", "type": "root", "type_id": 10, "children": [ -20 ] }, { "id": -20, "name": "R1-data", "type": "rack", "type_id": 3, "pool_weights": {}, "children": [ -8 ] }, { "id": -8, "name": "test-data03-object01", "type": "host", "type_id": 1, "pool_weights": {}, "children": [ 97 ] }, { "id": 524, "device_class": "hdd", "name": "osd.524", "type": "osd", "type_id": 0, "crush_weight": 7.265991, "depth": 3, "pool_weights": {}, "exists": 1, "status": "destroyed", "reweight": 0.000000, "primary_affinity": 1.000000 } ], "stray": [] }`, }, regexes: []*regexp.Regexp{ regexp.MustCompile(`ceph_osd_down{cluster="ceph",osd="osd.524",status="destroyed"} 1`), }, }, { cmdOut: map[string]string{ "ceph osd tree down": ` { "nodes": [ { "id": -18, "name": "data", "type": "root", "type_id": 10, "children": [ -20 ] }, { "id": -20, "name": "R1-data", "type": "rack", "type_id": 3, "pool_weights": {}, "children": [ -8 ] }, { "id": -8, "name": "test-data03-object01", "type": "host", "type_id": 1, "pool_weights": {}, "children": [ 97 ] }, { "id": 524, "device_class": "hdd", "name": "osd.524", "type": "osd", "type_id": 0, "crush_weight": 7.265991, "depth": 3, "pool_weights": {}, "exists": 1, "status": "destroyed", "reweight": 0.000000, "primary_affinity": 1.000000 } ], "stray": [ { "id": 525, "name": "osd.525", "type": "osd", "type_id": 0, "crush_weight": 0.000000, "depth": 0, "exists": 1, "status": "down", "reweight": 0.000000, "primary_affinity": 1.000000 } ] }`, }, regexes: []*regexp.Regexp{ regexp.MustCompile(`ceph_osd_down{cluster="ceph",osd="osd.524",status="destroyed"} 1`), regexp.MustCompile(`ceph_osd_down{cluster="ceph",osd="osd.525",status="down"} 1`), }, }, { cmdOut: map[string]string{ "ceph osd tree down": ` { "nodes": []}} }`, }, regexes: []*regexp.Regexp{}, }, { cmdOut: map[string]string{ "ceph pg dump pgs_brief": ` [ { "acting": [ 1, 2, 3, 4 ], "acting_primary": 1, "pgid": "81.1fff", "state": "active+clean" }, { "acting": [ 10, 11, 12, 13 ], "acting_primary": 10, "pgid": "82.1fff", "state": "active+clean+scrubbing" }, { "acting": [ 20, 21, 22, 23 ], "acting_primary": 20, "pgid": "83.1fff", "state": "active+clean+scrubbing+deep" }, { "acting": [ 30, 31, 32, 33 ], "acting_primary": 30, "pgid": "84.1fff", "state": "active+recovering+degraded" } ]`, "ceph tell 84.1fff query": ` { "state": "active+recovering+degraded", "info": { "stats": { "stat_sum": { "num_objects_recovered": 123 } } } }`, }, regexes: []*regexp.Regexp{ regexp.MustCompile(`ceph_pg_objects_recovered_total{cluster="ceph",pgid="84.1fff"} 123`), }, }, } { func() { collector := NewOSDCollector(NewNoopConnWithCmdOut(tt.cmdOut), "ceph") if err := prometheus.Register(collector); err != nil { t.Fatalf("collector failed to register: %s", err) } defer prometheus.Unregister(collector) server := httptest.NewServer(prometheus.Handler()) defer server.Close() resp, err := http.Get(server.URL) if err != nil { t.Fatalf("unexpected failed response from prometheus: %s", err) } defer resp.Body.Close() buf, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("failed reading server response: %s", err) } for _, re := range tt.regexes { if !re.Match(buf) { t.Errorf("failed matching: %q", re) } } }() } }