diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 907229fb82d..d560186b3c8 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -30,6 +30,7 @@ https://github.com/elastic/beats/compare/v7.5.0...v7.5.1[View commits] - Fix docker network stats when multiple interfaces are configured. {issue}14586[14586] {pull}14825[14825] - Fix ListMetrics pagination in aws module. {issue}14926[14926] {pull}14942[14942] - Fix CPU count in docker/cpu in cases where no `online_cpus` are reported {pull}15070[15070] +- Add domain state to kvm module {pull}17673[17673] [[release-notes-7.5.0]] === Beats version 7.5.0 diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 78d8583a9a9..174c829bc47 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -26936,6 +26936,50 @@ type: long Domain name +type: keyword + +-- + +[float] +=== status + +status + + + +[float] +=== stat + +Memory stat + + + +*`kvm.status.stat.state`*:: ++ +-- +domain state + + +type: keyword + +-- + +*`kvm.status.id`*:: ++ +-- +Domain id + + +type: long + +-- + +*`kvm.status.name`*:: ++ +-- +Domain name + + type: keyword -- diff --git a/metricbeat/docs/modules/kvm.asciidoc b/metricbeat/docs/modules/kvm.asciidoc index f8373184ba1..bc29173f280 100644 --- a/metricbeat/docs/modules/kvm.asciidoc +++ b/metricbeat/docs/modules/kvm.asciidoc @@ -20,7 +20,7 @@ in <>. Here is an example configuration: ---- metricbeat.modules: - module: kvm - metricsets: ["dommemstat"] + metricsets: ["dommemstat", "status"] enabled: true period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] @@ -39,5 +39,9 @@ The following metricsets are available: * <> +* <> + include::kvm/dommemstat.asciidoc[] +include::kvm/status.asciidoc[] + diff --git a/metricbeat/docs/modules/kvm/status.asciidoc b/metricbeat/docs/modules/kvm/status.asciidoc new file mode 100644 index 00000000000..5fea3653349 --- /dev/null +++ b/metricbeat/docs/modules/kvm/status.asciidoc @@ -0,0 +1,24 @@ +//// +This file is generated! See scripts/mage/docs_collector.go +//// + +[[metricbeat-metricset-kvm-status]] +=== kvm status metricset + +beta[] + +include::../../../module/kvm/status/_meta/docs.asciidoc[] + +This is a default metricset. If the host module is unconfigured, this metricset is enabled by default. + +==== Fields + +For a description of each field in the metricset, see the +<> section. + +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../module/kvm/status/_meta/data.json[] +---- diff --git a/metricbeat/docs/modules_list.asciidoc b/metricbeat/docs/modules_list.asciidoc index 8d84ffb3ae8..2215dd901eb 100644 --- a/metricbeat/docs/modules_list.asciidoc +++ b/metricbeat/docs/modules_list.asciidoc @@ -168,7 +168,8 @@ This file is generated! See scripts/mage/docs_collector.go |<> |<> |<> beta[] |image:./images/icon-no.png[No prebuilt dashboards] | -.1+| .1+| |<> beta[] +.2+| .2+| |<> beta[] +|<> beta[] |<> |image:./images/icon-no.png[No prebuilt dashboards] | .2+| .2+| |<> |<> diff --git a/metricbeat/include/list_common.go b/metricbeat/include/list_common.go index f15d6a4be16..ced01fa0d57 100644 --- a/metricbeat/include/list_common.go +++ b/metricbeat/include/list_common.go @@ -92,6 +92,7 @@ import ( _ "github.com/elastic/beats/v7/metricbeat/module/kibana/status" _ "github.com/elastic/beats/v7/metricbeat/module/kvm" _ "github.com/elastic/beats/v7/metricbeat/module/kvm/dommemstat" + _ "github.com/elastic/beats/v7/metricbeat/module/kvm/status" _ "github.com/elastic/beats/v7/metricbeat/module/logstash" _ "github.com/elastic/beats/v7/metricbeat/module/logstash/node" _ "github.com/elastic/beats/v7/metricbeat/module/logstash/node_stats" diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index aa0aac52e97..bf736ac28ac 100644 --- a/metricbeat/metricbeat.reference.yml +++ b/metricbeat/metricbeat.reference.yml @@ -556,7 +556,7 @@ metricbeat.modules: #--------------------------------- Kvm Module --------------------------------- - module: kvm - metricsets: ["dommemstat"] + metricsets: ["dommemstat", "status"] enabled: true period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] diff --git a/metricbeat/module/kvm/_meta/config.reference.yml b/metricbeat/module/kvm/_meta/config.reference.yml index 79f754a52cb..84e584787e1 100644 --- a/metricbeat/module/kvm/_meta/config.reference.yml +++ b/metricbeat/module/kvm/_meta/config.reference.yml @@ -1,5 +1,5 @@ - module: kvm - metricsets: ["dommemstat"] + metricsets: ["dommemstat", "status"] enabled: true period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] diff --git a/metricbeat/module/kvm/_meta/config.yml b/metricbeat/module/kvm/_meta/config.yml index 2df123ca14d..78509f63aa4 100644 --- a/metricbeat/module/kvm/_meta/config.yml +++ b/metricbeat/module/kvm/_meta/config.yml @@ -1,5 +1,6 @@ - module: kvm #metricsets: # - dommemstat + # - status period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] diff --git a/metricbeat/module/kvm/fields.go b/metricbeat/module/kvm/fields.go index 456017804b4..4f509af839a 100644 --- a/metricbeat/module/kvm/fields.go +++ b/metricbeat/module/kvm/fields.go @@ -32,5 +32,5 @@ func init() { // AssetKvm returns asset data. // This is the base64 encoded gzipped contents of module/kvm. func AssetKvm() string { - return "eJyskDFuwzAMRXed4iN7LqChU9ceQq3YQLBoGrLsQrcvnEZFotByh3Lw4G+//8gzBioWw8oGyCFHsjgNK58MkCiSm8ninbIzgKf5I4UpBxktXgyA7T+w+CWSAT4DRT/ba3DG6JgqeJtcJrK4JFmm2xuF9wi5B3lhJp6zy7+Rxtzl3iKN0u5Zp1V5FGogPaFDrZ95I5ZUdLDucu+zPZW4Og1UviR59YtDs8Zur6uqrC4uPZco4+V/RLSmahGel+3UH1a/CrswPlO79+/f/q+dV/R3AAAA///hct15" + return "eJzckzFuxCAQRXtO8bX9XoAiVdocgoTJCpkxFmBHvn3k7BLZeBZHStLsFC485v0HZs7oaNboJlZAdtmTxqmb+KSASJ5MIo1XykYBltJbdEN2odd4UgCWdeBgR08KeHfkbdJfjTN6w1TAS+V5II1LDONweyPwtpA1yAZm4pRN/m5JzLvcW0ui1PssVatshSpIS+hQ61ovxCHOMlh2WfssT6FdnDqaP0K04heHZpXdvayiMhk/tlx86C9/IyIlFQu332wj/jD6ObBx/Z7aPP/22f80c4Ne378x/WYYdoTHGIRl2b9Ogr3+FSnnQe/eZwAAAP//6Q5sWg==" } diff --git a/metricbeat/module/kvm/status/_meta/data.json b/metricbeat/module/kvm/status/_meta/data.json new file mode 100644 index 00000000000..fb7fd3c3595 --- /dev/null +++ b/metricbeat/module/kvm/status/_meta/data.json @@ -0,0 +1,28 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "agent": { + "hostname": "host.example.com", + "name": "host.example.com" + }, + "event":{ + "dataset":"kvm.status", + "module":"kvm", + "duration":4012216 + }, + "metricset":{ + "name":"status" + }, + "service":{ + "address":"unix:///var/run/libvirt/libvirt-sock", + "type":"kvm" + }, + "kvm":{ + "status":{ + "stat":{ + "state":"running" + }, + "id":1, + "name":"generic-2" + } + } +} diff --git a/metricbeat/module/kvm/status/_meta/docs.asciidoc b/metricbeat/module/kvm/status/_meta/docs.asciidoc new file mode 100644 index 00000000000..b94f0f0f147 --- /dev/null +++ b/metricbeat/module/kvm/status/_meta/docs.asciidoc @@ -0,0 +1 @@ +This is the status metricset of the module kvm. diff --git a/metricbeat/module/kvm/status/_meta/fields.yml b/metricbeat/module/kvm/status/_meta/fields.yml new file mode 100644 index 00000000000..9f75085f2cf --- /dev/null +++ b/metricbeat/module/kvm/status/_meta/fields.yml @@ -0,0 +1,23 @@ +- name: status + type: group + description: > + status + release: beta + fields: + - name: stat + type: group + description: > + Memory stat + fields: + - name: state + type: keyword + description: > + domain state + - name: id + type: long + description: > + Domain id + - name: name + type: keyword + description: > + Domain name diff --git a/metricbeat/module/kvm/status/status.go b/metricbeat/module/kvm/status/status.go new file mode 100644 index 00000000000..eab1b947d7f --- /dev/null +++ b/metricbeat/module/kvm/status/status.go @@ -0,0 +1,161 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package status + +import ( + "net" + "net/url" + "time" + + "github.com/pkg/errors" + + "github.com/digitalocean/go-libvirt" + "github.com/digitalocean/go-libvirt/libvirttest" + + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/cfgwarn" + "github.com/elastic/beats/v7/metricbeat/mb" +) + +// init registers the MetricSet with the central registry as soon as the program +// starts. The New function will be called later to instantiate an instance of +// the MetricSet for each host defined in the module's configuration. After the +// MetricSet has been created then Fetch will begin to be called periodically. +func init() { + mb.Registry.MustAddMetricSet("kvm", "status", New, + mb.DefaultMetricSet(), + ) +} + +// MetricSet holds any configuration or state information. It must implement +// the mb.MetricSet interface. And this is best achieved by embedding +// mb.BaseMetricSet because it implements all of the required mb.MetricSet +// interface methods except for Fetch. +type MetricSet struct { + mb.BaseMetricSet + Timeout time.Duration + HostURL *url.URL +} + +// New creates a new instance of the MetricSet. New is responsible for unpacking +// any MetricSet specific configuration options if there are any. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + cfgwarn.Beta("The kvm status metricset is beta.") + u, err := url.Parse(base.HostData().URI) + if err != nil { + return nil, err + } + + return &MetricSet{ + BaseMetricSet: base, + Timeout: base.Module().Config().Timeout, + HostURL: u, + }, nil +} + +// Fetch methods implements the data gathering and data conversion to the right +// format. It publishes the event which is then forwarded to the output. In case +// of an error set the Error field of mb.Event or simply call report.Error(). +func (m *MetricSet) Fetch(report mb.ReporterV2) error { + var ( + c net.Conn + err error + ) + + u := m.HostURL + + if u.Scheme == "test" { + // when running tests, a mock Libvirt server is used + c = libvirttest.New() + } else { + address := u.Host + if u.Host == "" { + address = u.Path + } + + c, err = net.DialTimeout(u.Scheme, address, m.Timeout) + if err != nil { + return errors.Wrapf(err, "cannot connect to %v", u) + } + } + + defer c.Close() + + l := libvirt.New(c) + if err = l.Connect(); err != nil { + return errors.Wrap(err, "error connecting to libvirtd") + } + defer func() { + if err = l.Disconnect(); err != nil { + msg := errors.Wrap(err, "failed to disconnect") + report.Error(msg) + m.Logger().Error(msg) + } + }() + + domains, err := l.Domains() + if err != nil { + return errors.Wrap(err, "error listing domains") + } + + for _, d := range domains { + state, err := l.DomainState(d.Name) + if err != nil { + continue + } + reported := report.Event(mb.Event{ + ModuleFields: common.MapStr{ + "id": d.ID, + "name": d.Name, + }, + MetricSetFields: common.MapStr{ + "stat": common.MapStr{ + "state": getDomainStateName(state), + }, + }, + }) + if !reported { + return nil + } + } + + return nil +} + +func getDomainStateName(tag libvirt.DomainState) string { + switch tag { + case 0: + return "no state" + case 1: + return "running" + case 2: + return "blocked" + case 3: + return "paused" + case 4: + return "shutdown" + case 5: + return "shutoff" + case 6: + return "crashed" + case 7: + return "suspended" + default: + return "unidentified" + } +} diff --git a/metricbeat/module/kvm/status/status_test.go b/metricbeat/module/kvm/status/status_test.go new file mode 100644 index 00000000000..e484cd775be --- /dev/null +++ b/metricbeat/module/kvm/status/status_test.go @@ -0,0 +1,69 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package status + +import ( + "testing" + + "github.com/digitalocean/go-libvirt/libvirttest" + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" +) + +func TestFetchEventContents(t *testing.T) { + conn := libvirttest.New() + + f := mbtest.NewReportingMetricSetV2Error(t, getConfig(conn)) + + events, errs := mbtest.ReportingFetchV2Error(f) + if len(errs) > 0 { + t.Fatal(errs) + } + if len(events) == 0 { + t.Fatal("no events received") + } + + for _, e := range events { + if e.Error != nil { + t.Fatalf("received error: %+v", e.Error) + } + } + if len(events) == 0 { + t.Fatal("received no events") + } + + e := events[0] + + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), e) + + statName, err := e.MetricSetFields.GetValue("stat.state") + if err == nil { + assert.EqualValues(t, statName.(string), "running") + } else { + t.Errorf("error while getting value from event: %v", err) + } +} + +func getConfig(conn *libvirttest.MockLibvirt) map[string]interface{} { + return map[string]interface{}{ + "module": "kvm", + "metricsets": []string{"status"}, + "hosts": []string{"test://" + conn.RemoteAddr().String() + ":123"}, + } +} diff --git a/metricbeat/modules.d/kvm.yml.disabled b/metricbeat/modules.d/kvm.yml.disabled index a2c0ee07d13..86e8baab700 100644 --- a/metricbeat/modules.d/kvm.yml.disabled +++ b/metricbeat/modules.d/kvm.yml.disabled @@ -4,5 +4,6 @@ - module: kvm #metricsets: # - dommemstat + # - status period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index 4cfc5e8b750..d9401526356 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -844,7 +844,7 @@ metricbeat.modules: #--------------------------------- Kvm Module --------------------------------- - module: kvm - metricsets: ["dommemstat"] + metricsets: ["dommemstat", "status"] enabled: true period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"]