diff --git a/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py b/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py index 84763a7217572..7e711e6393111 100644 --- a/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py +++ b/kubernetes_state/datadog_checks/kubernetes_state/kubernetes_state.py @@ -66,6 +66,18 @@ def __init__(self, name, init_config, agentConfig, instances=None): 'unknown': self.UNKNOWN } + # Parameters for the count_objects_by_tags method + self.object_count_params = { + 'kube_persistentvolume_status_phase': { + 'metric_name': 'persistentvolumes.by_phase', + 'allowed_labels': ['storageclass', 'phase'], + }, + 'kube_service_spec_type': { + 'metric_name': 'service.count', + 'allowed_labels': ['namespace', 'type'], + }, + } + self.METRIC_TRANSFORMERS = { 'kube_pod_status_phase': self.kube_pod_status_phase, 'kube_pod_container_status_waiting_reason': self.kube_pod_container_status_waiting_reason, @@ -84,7 +96,8 @@ def __init__(self, name, init_config, agentConfig, instances=None): 'kube_node_spec_unschedulable': self.kube_node_spec_unschedulable, 'kube_resourcequota': self.kube_resourcequota, 'kube_limitrange': self.kube_limitrange, - 'kube_persistentvolume_status_phase': self.kube_persistentvolume_status_phase + 'kube_persistentvolume_status_phase': self.count_objects_by_tags, + 'kube_service_spec_type': self.count_objects_by_tags, } def check(self, instance): @@ -636,16 +649,17 @@ def kube_limitrange(self, metric, scraper_config): else: self.log.error("Metric type %s unsupported for metric %s" % (metric.type, metric.name)) - def kube_persistentvolume_status_phase(self, metric, scraper_config): - """ The persistent volumes by phase. """ - metric_name = scraper_config['namespace'] + '.persistentvolumes.by_phase' - by_phase_counter = Counter() + def count_objects_by_tags(self, metric, scraper_config): + """ Count objects by whitelisted tags and submit counts as gauges. """ + config = self.object_count_params[metric.name] + metric_name = "{}.{}".format(scraper_config['namespace'], config['metric_name']) + object_counter = Counter() + for sample in metric.samples: tags = [ - self._label_to_tag("storageclass", sample[self.SAMPLE_LABELS], scraper_config), - self._label_to_tag("phase", sample[self.SAMPLE_LABELS], scraper_config) + self._label_to_tag(l, sample[self.SAMPLE_LABELS], scraper_config) for l in config['allowed_labels'] ] + scraper_config['custom_tags'] - by_phase_counter[tuple(sorted(tags))] += sample[self.SAMPLE_VALUE] + object_counter[tuple(sorted(tags))] += sample[self.SAMPLE_VALUE] - for tags, count in by_phase_counter.iteritems(): + for tags, count in object_counter.iteritems(): self.gauge(metric_name, count, tags=list(tags)) diff --git a/kubernetes_state/metadata.csv b/kubernetes_state/metadata.csv index da33804812e50..ac9e6783b5098 100644 --- a/kubernetes_state/metadata.csv +++ b/kubernetes_state/metadata.csv @@ -86,6 +86,7 @@ kubernetes_state.resourcequota.requests.memory.limit,gauge,,byte,,Hard limit on kubernetes_state.resourcequota.requests.storage.limit,gauge,,byte,,Hard limit on the total of storage bytes requested for a resource quota,0,kubernetes,k8s_state.resourcequota.requests.storage.limit kubernetes_state.resourcequota.limits.cpu.limit,gauge,,cpu,,Hard limit on the sum of CPU core limits for a resource quota,0,kubernetes,k8s_state.resourcequota.limits.cpu.limit kubernetes_state.resourcequota.limits.memory.limit,gauge,,byte,,Hard limit on the sum of memory bytes limits for a resource quota,0,kubernetes,k8s_state.resourcequota.limits.mem.limit +kubernetes_state.service.count,gauge,,,,Sum by namespace and type to count active services,0,kubernetes,k8s_state.svc.count kubernetes_state.statefulset.replicas,gauge,,,,The number of replicas per statefulset,0,kubernetes,k8s_state.statefulset.replicas kubernetes_state.statefulset.replicas_desired,gauge,,,,The number of desired replicas per statefulset,0,kubernetes,k8s_state.statefulset.replicas_desired kubernetes_state.statefulset.replicas_current,gauge,,,,The number of current replicas per StatefulSet,0,kubernetes,k8s_state.statefulset.replicas_current diff --git a/kubernetes_state/tests/fixtures/prometheus.txt b/kubernetes_state/tests/fixtures/prometheus.txt index ab0c96566708d..1992d477caf0e 100644 --- a/kubernetes_state/tests/fixtures/prometheus.txt +++ b/kubernetes_state/tests/fixtures/prometheus.txt @@ -812,6 +812,18 @@ kube_service_labels{label_app="helm",label_name="tiller",namespace="kube-system" kube_service_labels{label_addonmanager_kubernetes_io_mode="Reconcile",label_k8s_app="kube-dns",label_kubernetes_io_name="KubeDNS",namespace="kube-system",service="kube-dns"} 1 kube_service_labels{label_addonmanager_kubernetes_io_mode="Reconcile",label_app="kubernetes-dashboard",label_kubernetes_io_minikube_addons="dashboard",label_kubernetes_io_minikube_addons_endpoint="dashboard",namespace="kube-system",service="kubernetes-dashboard"} 1 kube_service_labels{label_app="kube-state-metrics",label_chart="kube-state-metrics-0.3.1",label_heritage="Tiller",label_release="jaundiced-numbat",namespace="default",service="jaundiced-numbat-kube-state-metrics"} 1 +# HELP kube_service_spec_type Type about service. +# TYPE kube_service_spec_type gauge +kube_service_spec_type{namespace="default",service="datadog-cluster-agent",type="ClusterIP"} 1 +kube_service_spec_type{namespace="default",service="ddrc1-kube-state-metrics",type="ClusterIP"} 1 +kube_service_spec_type{namespace="default",service="kubernetes",type="ClusterIP"} 1 +kube_service_spec_type{namespace="default",service="redis-db2",type="LoadBalancer"} 1 +kube_service_spec_type{namespace="default",service="redis-db2-bis",type="LoadBalancer"} 1 +kube_service_spec_type{namespace="kube-system",service="default-http-backend",type="NodePort"} 1 +kube_service_spec_type{namespace="kube-system",service="heapster",type="ClusterIP"} 1 +kube_service_spec_type{namespace="kube-system",service="kube-dns",type="ClusterIP"} 1 +kube_service_spec_type{namespace="kube-system",service="metrics-server",type="ClusterIP"} 1 +kube_service_spec_type{namespace="kube-system",service="tiller-deploy",type="ClusterIP"} 1 # HELP kube_resourcequota Information about resource quota. # TYPE kube_resourcequota gauge kube_resourcequota{namespace="default",resource="cpu",resourcequota="custom-resource-quotas",type="hard"} 65 diff --git a/kubernetes_state/tests/test_kubernetes_state.py b/kubernetes_state/tests/test_kubernetes_state.py index 47433b3f1357b..02a66a87f78e8 100644 --- a/kubernetes_state/tests/test_kubernetes_state.py +++ b/kubernetes_state/tests/test_kubernetes_state.py @@ -91,6 +91,8 @@ NAMESPACE + '.resourcequota.limits.memory.limit', # limitrange NAMESPACE + '.limitrange.cpu.default_request', + # services + NAMESPACE + '.service.count', ] TAGS = { @@ -121,7 +123,14 @@ ], NAMESPACE + '.persistentvolumeclaim.request_storage': [ 'storageclass:manual' - ] + ], + NAMESPACE + '.service.count': [ + 'namespace:kube-system', + 'namespace:default', + 'type:ClusterIP', + 'type:NodePort', + 'type:LoadBalancer', + ], } JOINED_METRICS = {