diff --git a/docs/metrics.md b/docs/metrics.md index daced8eedcb3..071ea33bc7be 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -1,16 +1,4 @@ - - # KubeVirt metrics -This document aims to help users that are not familiar with all metrics exposed by different KubeVirt components. -All metrics documented here are auto-generated by the utility tool `tools/doc-generator` and reflects exactly what is being exposed. - -## KubeVirt Metrics List -### kubevirt_info -Version information. ### kubevirt_allocatable_nodes The number of allocatable nodes in the cluster. Type: Gauge. @@ -24,6 +12,9 @@ Indicates whether the Software Emulation is enabled in the configuration. Type: ### kubevirt_console_active_connections Amount of active Console connections, broken down by namespace and vmi name. Type: Gauge. +### kubevirt_info +Version information. Type: Gauge. + ### kubevirt_nodes_with_kvm The number of nodes in the cluster that have the devices.kubevirt.io/kvm resource available. Type: Gauge. @@ -142,7 +133,7 @@ The total amount of memory written out to swap space of the guest in bytes. Type The amount of memory left completely unused by the system. Memory that is available but used for reclaimable caches should NOT be reported as free. Type: Gauge. ### kubevirt_vmi_memory_usable_bytes -The amount of memory which can be reclaimed by balloon without pushing the guest system to swap, corresponds to 'Available' in /proc/meminfo Type: Gauge. +The amount of memory which can be reclaimed by balloon without pushing the guest system to swap, corresponds to 'Available' in /proc/meminfo. Type: Gauge. ### kubevirt_vmi_memory_used_bytes Amount of `used` memory as seen by the domain. Type: Gauge. @@ -190,7 +181,7 @@ The total number of rx packets dropped on vNIC interfaces. Type: Counter. Total network traffic received packets. Type: Counter. ### kubevirt_vmi_network_traffic_bytes_total -Deprecated. Type: Counter. +[Deprecated] Total number of bytes sent and received. Type: Counter. ### kubevirt_vmi_network_transmit_bytes_total Total network traffic transmitted in bytes. Type: Counter. @@ -280,6 +271,7 @@ Request latency in seconds. Broken down by verb and URL. Type: Histogram. Number of HTTP requests, partitioned by status code, method, and host. Type: Counter. ## Developing new metrics -After developing new metrics or changing old ones, please run `make generate` to regenerate this document. -If you feel that the new metric doesn't follow these rules, please change `doc-generator` with your needs. +All metrics documented here are auto-generated and reflect exactly what is being +exposed. After developing new metrics or changing old ones please regenerate +this document. diff --git a/hack/generate.sh b/hack/generate.sh index 710b0b35fc37..30e822a83e73 100755 --- a/hack/generate.sh +++ b/hack/generate.sh @@ -140,8 +140,7 @@ ${KUBEVIRT_DIR}/tools/openapispec/openapispec --dump-api-spec-path ${KUBEVIRT_DI (cd ${KUBEVIRT_DIR}/tools/doc-generator/ && go_build) ( cd ${KUBEVIRT_DIR}/docs - ${KUBEVIRT_DIR}/tools/doc-generator/doc-generator - mv newmetrics.md metrics.md + ${KUBEVIRT_DIR}/tools/doc-generator/doc-generator >metrics.md ) rm -f ${KUBEVIRT_DIR}/manifests/generated/* diff --git a/tools/doc-generator/BUILD.bazel b/tools/doc-generator/BUILD.bazel index 2bc5961a7da1..7bf72f091258 100644 --- a/tools/doc-generator/BUILD.bazel +++ b/tools/doc-generator/BUILD.bazel @@ -2,27 +2,17 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( name = "go_default_library", - srcs = [ - "doc-generator.go", - "fakeDomainCollector.go", - ], + srcs = ["doc-generator.go"], importpath = "kubevirt.io/kubevirt/tools/doc-generator", visibility = ["//visibility:private"], deps = [ - "//pkg/monitoring/domainstats/prometheus:go_default_library", "//pkg/monitoring/metrics/virt-api:go_default_library", "//pkg/monitoring/metrics/virt-controller:go_default_library", "//pkg/monitoring/metrics/virt-handler:go_default_library", "//pkg/monitoring/metrics/virt-operator:go_default_library", "//pkg/monitoring/rules:go_default_library", - "//pkg/virt-controller/watch:go_default_library", - "//pkg/virt-launcher/virtwrap/stats:go_default_library", - "//pkg/virt-launcher/virtwrap/statsconv:go_default_library", - "//pkg/virt-launcher/virtwrap/statsconv/util:go_default_library", - "//staging/src/kubevirt.io/api/core/v1:go_default_library", + "//vendor/github.com/machadovilaca/operator-observability/pkg/docs:go_default_library", "//vendor/github.com/machadovilaca/operator-observability/pkg/operatormetrics:go_default_library", - "//vendor/github.com/prometheus/client_golang/prometheus:go_default_library", - "//vendor/libvirt.org/go/libvirt:go_default_library", ], ) diff --git a/tools/doc-generator/doc-generator.go b/tools/doc-generator/doc-generator.go index 4b95e6602d1b..ebd50ef6eb4b 100644 --- a/tools/doc-generator/doc-generator.go +++ b/tools/doc-generator/doc-generator.go @@ -1,265 +1,69 @@ package main import ( - "bufio" "fmt" - "io" - "net/http" - "net/http/httptest" - "os" - "sort" - "strings" "github.com/machadovilaca/operator-observability/pkg/operatormetrics" - domainstats "kubevirt.io/kubevirt/pkg/monitoring/domainstats/prometheus" // import for prometheus metrics - virt_api "kubevirt.io/kubevirt/pkg/monitoring/metrics/virt-api" - virt_controller "kubevirt.io/kubevirt/pkg/monitoring/metrics/virt-controller" - virt_handler "kubevirt.io/kubevirt/pkg/monitoring/metrics/virt-handler" - virt_operator "kubevirt.io/kubevirt/pkg/monitoring/metrics/virt-operator" + virtapi "kubevirt.io/kubevirt/pkg/monitoring/metrics/virt-api" + virtcontroller "kubevirt.io/kubevirt/pkg/monitoring/metrics/virt-controller" + virthandler "kubevirt.io/kubevirt/pkg/monitoring/metrics/virt-handler" + virtoperator "kubevirt.io/kubevirt/pkg/monitoring/metrics/virt-operator" "kubevirt.io/kubevirt/pkg/monitoring/rules" - _ "kubevirt.io/kubevirt/pkg/virt-controller/watch" -) - -// constant parts of the file -const ( - genFileComment = `` - title = "# KubeVirt metrics\n" - background = "This document aims to help users that are not familiar with all metrics exposed by different KubeVirt components.\n" + - "All metrics documented here are auto-generated by the utility tool `tools/doc-generator` and reflects exactly what is being exposed.\n\n" - - KVSpecificMetrics = "## KubeVirt Metrics List\n" + - "### kubevirt_info\n" + - "Version information.\n\n" - - opening = genFileComment + "\n\n" + - title + - background + - KVSpecificMetrics - // footer - footerHeading = "## Developing new metrics\n" - footerContent = "After developing new metrics or changing old ones, please run `make generate` to regenerate this document.\n\n" + - "If you feel that the new metric doesn't follow these rules, please change `doc-generator` with your needs.\n" - - footer = footerHeading + footerContent + "github.com/machadovilaca/operator-observability/pkg/docs" ) -func main() { - handler := domainstats.Handler(1) - RegisterFakeDomainCollector() - - req, err := http.NewRequest(http.MethodGet, "/metrics", nil) - checkError(err) - - recorder := httptest.NewRecorder() - - handler.ServeHTTP(recorder, req) - - metrics := getMetricsNotIncludeInEndpointByDefault() - - if status := recorder.Code; status == http.StatusOK { - err := parseVirtMetrics(recorder.Body, &metrics) - checkError(err) - - } else { - panic(fmt.Errorf("got HTTP status code of %d from /metrics", recorder.Code)) - } - writeToFile(metrics) -} - -func writeToFile(metrics metricList) { - newFile, err := os.Create("newmetrics.md") - checkError(err) - defer newFile.Close() +const tpl = `# KubeVirt metrics - fmt.Fprint(newFile, opening) - metrics.writeToFile(newFile) +{{- range . }} - fmt.Fprint(newFile, footer) +{{ $deprecatedVersion := "" -}} +{{- with index .ExtraFields "DeprecatedVersion" -}} + {{- $deprecatedVersion = printf " in %s" . -}} +{{- end -}} -} - -type metric struct { - name string - description string - mType string -} - -func (m metric) writeToFile(newFile io.WriteCloser) { - fmt.Fprintln(newFile, "###", m.name) - fmt.Fprintln(newFile, m.description, "Type:", m.mType+".") - fmt.Fprintln(newFile) -} - -type metricList []metric +{{- $stabilityLevel := "" -}} +{{- if and (.ExtraFields.StabilityLevel) (ne .ExtraFields.StabilityLevel "STABLE") -}} + {{- $stabilityLevel = printf "[%s%s] " .ExtraFields.StabilityLevel $deprecatedVersion -}} +{{- end -}} -// Len implements sort.Interface.Len -func (m metricList) Len() int { - return len(m) -} +### {{ .Name }} +{{ print $stabilityLevel }}{{ .Help }} Type: {{ .Type -}}. -// Less implements sort.Interface.Less -func (m metricList) Less(i, j int) bool { - return m[i].name < m[j].name -} - -// Swap implements sort.Interface.Swap -func (m metricList) Swap(i, j int) { - m[i], m[j] = m[j], m[i] -} - -func (m metricList) writeToFile(newFile io.WriteCloser) { - for _, met := range m { - met.writeToFile(newFile) - } -} - -func getMetricsNotIncludeInEndpointByDefault() metricList { - metrics := metricList{ - { - name: domainstats.MigrateVmiDataProcessedMetricName, - description: "The total Guest OS data processed and migrated to the new VM.", - mType: "Gauge", - }, - { - name: domainstats.MigrateVmiDataRemainingMetricName, - description: "The remaining guest OS data to be migrated to the new VM.", - mType: "Gauge", - }, - { - name: domainstats.MigrateVmiDirtyMemoryRateMetricName, - description: "The rate of memory being dirty in the Guest OS.", - mType: "Gauge", - }, - { - name: domainstats.MigrateVmiMemoryTransferRateMetricName, - description: "The rate at which the memory is being transferred.", - mType: "Gauge", - }, - { - name: "kubevirt_vmi_phase_count", - description: "Sum of VMIs per phase and node. `phase` can be one of the following: [`Pending`, `Scheduling`, `Scheduled`, `Running`, `Succeeded`, `Failed`, `Unknown`].", - mType: "Gauge", - }, - { - name: "kubevirt_vmi_non_evictable", - description: "Indication for a VirtualMachine that its eviction strategy is set to Live Migration but is not migratable.", - mType: "Gauge", - }, - } +{{- end }} - metrics = append(metrics, getVirtControllerMetrics()...) - metrics = append(metrics, getComponentMetrics(virt_api.SetupMetrics, virt_api.ListMetrics)...) - metrics = append(metrics, getComponentMetrics(virt_operator.SetupMetrics, virt_operator.ListMetrics)...) - metrics = append(metrics, getComponentMetrics(virt_handler.SetupMetrics, virt_handler.ListMetrics)...) +## Developing new metrics - err := rules.SetupRules("") - checkError(err) +All metrics documented here are auto-generated and reflect exactly what is being +exposed. After developing new metrics or changing old ones please regenerate +this document. +` - for _, rule := range rules.ListRecordingRules() { - metrics = append(metrics, metric{ - name: rule.GetOpts().Name, - description: rule.GetOpts().Help, - mType: string(rule.GetType()), - }) - } - - return metrics -} - -func getVirtControllerMetrics() metricList { - err := virt_controller.SetupMetrics(nil, nil, nil, nil, nil, nil, nil, nil) - checkError(err) - return listComponentMetrics(virt_controller.ListMetrics) -} - -func getComponentMetrics(setup func() error, list func() []operatormetrics.Metric) metricList { - err := setup() - checkError(err) - return listComponentMetrics(list) -} - -func listComponentMetrics(list func() []operatormetrics.Metric) metricList { - metrics := metricList{} - - for _, m := range list() { - metrics = append(metrics, newMetric(m)) - } - - err := operatormetrics.CleanRegistry() - checkError(err) - - return metrics -} - -func newMetric(om operatormetrics.Metric) metric { - return metric{ - name: om.GetOpts().Name, - description: om.GetOpts().Help, - mType: strings.Replace(string(om.GetType()), "Vec", "", 1), +func main() { + if err := virtcontroller.SetupMetrics(nil, nil, nil, nil, nil, nil, nil, nil); err != nil { + panic(err) } -} - -func parseMetricDesc(line string) (string, string) { - split := strings.Split(line, " ") - name := split[2] - split[3] = strings.Title(split[3]) - description := strings.Join(split[3:], " ") - return name, description -} -func parseMetricType(scan *bufio.Scanner, name string) string { - for scan.Scan() { - typeLine := scan.Text() - if strings.HasPrefix(typeLine, "# TYPE ") { - split := strings.Split(typeLine, " ") - if split[2] == name { - return strings.Title(split[3]) - } - } + if err := virtapi.SetupMetrics(); err != nil { + panic(err) } - return "" -} - -const filter = "kubevirt_" -func parseVirtMetrics(r io.Reader, metrics *metricList) error { - scan := bufio.NewScanner(r) - for scan.Scan() { - helpLine := scan.Text() - if strings.HasPrefix(helpLine, "# HELP ") { - if strings.Contains(helpLine, filter) { - metName, metDesc := parseMetricDesc(helpLine) - metType := parseMetricType(scan, metName) - *metrics = append(*metrics, metric{name: metName, description: metDesc, mType: metType}) - } - } + if err := virtoperator.SetupMetrics(); err != nil { + panic(err) } - if scan.Err() != nil { - return fmt.Errorf("failed to parse metrics from prometheus endpoint, %w", scan.Err()) + if err := virthandler.SetupMetrics("", "", 0, nil); err != nil { + panic(err) } - sort.Sort(metrics) - - // remove duplicates - for i := 0; i < len(*metrics)-1; i++ { - if (*metrics)[i].name == (*metrics)[i+1].name { - *metrics = append((*metrics)[:i], (*metrics)[i+1:]...) - i-- - } + if err := rules.SetupRules(""); err != nil { + panic(err) } - return nil -} + metricsList := operatormetrics.ListMetrics() + rulesList := rules.ListRecordingRules() -func checkError(err error) { - if err != nil { - panic(err) - } + docsString := docs.BuildMetricsDocsWithCustomTemplate(metricsList, rulesList, tpl) + fmt.Print(docsString) } diff --git a/tools/doc-generator/fakeDomainCollector.go b/tools/doc-generator/fakeDomainCollector.go deleted file mode 100644 index 391f13c95ac4..000000000000 --- a/tools/doc-generator/fakeDomainCollector.go +++ /dev/null @@ -1,91 +0,0 @@ -package main - -import ( - "github.com/prometheus/client_golang/prometheus" - "libvirt.org/go/libvirt" - - domainstats "kubevirt.io/kubevirt/pkg/monitoring/domainstats/prometheus" - - k6tv1 "kubevirt.io/api/core/v1" - - "kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/stats" - "kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/statsconv" - "kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/statsconv/util" -) - -type fakeDomainCollector struct { -} - -func (fc fakeDomainCollector) Describe(_ chan<- *prometheus.Desc) { -} - -// Collect needs to report all metrics to see it in docs -func (fc fakeDomainCollector) Collect(ch chan<- prometheus.Metric) { - ps := domainstats.NewPrometheusScraper(ch) - - libstatst, err := util.LoadStats() - if err != nil { - panic(err) - } - - in := &libstatst[0] - inMem := []libvirt.DomainMemoryStat{} - inDomInfo := &libvirt.DomainInfo{} - jobInfo := stats.DomainJobInfo{} - out := stats.DomainStats{} - fs := k6tv1.VirtualMachineInstanceFileSystemList{} - ident := statsconv.DomainIdentifier(&fakeDomainIdentifier{}) - devAliasMap := make(map[string]string) - - if err = statsconv.Convert_libvirt_DomainStats_to_stats_DomainStats(ident, in, inMem, inDomInfo, devAliasMap, &jobInfo, &out); err != nil { - panic(err) - } - - out.Memory.ActualBalloonSet = true - out.Memory.UnusedSet = true - out.Memory.CachedSet = true - out.Memory.AvailableSet = true - out.Memory.RSSSet = true - out.Memory.SwapInSet = true - out.Memory.SwapOutSet = true - out.Memory.UsableSet = true - out.Memory.MinorFaultSet = true - out.Memory.MajorFaultSet = true - out.CPUMapSet = true - out.Cpu.SystemSet = true - out.Cpu.UserSet = true - out.Cpu.TimeSet = true - - fs.Items = []k6tv1.VirtualMachineInstanceFileSystem{ - { - DiskName: "disk1", - MountPoint: "/", - FileSystemType: "EXT4", - TotalBytes: 1000, - UsedBytes: 10, - }, - } - - vmi := k6tv1.VirtualMachineInstance{ - Status: k6tv1.VirtualMachineInstanceStatus{ - Phase: k6tv1.Running, - NodeName: "test", - }, - } - ps.Report("test", &vmi, &domainstats.VirtualMachineInstanceStats{DomainStats: &out, FsStats: fs}) -} - -type fakeDomainIdentifier struct { -} - -func (*fakeDomainIdentifier) GetName() (string, error) { - return "test", nil -} - -func (*fakeDomainIdentifier) GetUUIDString() (string, error) { - return "uuid", nil -} - -func RegisterFakeDomainCollector() { - prometheus.MustRegister(fakeDomainCollector{}) -}