diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index b3fa34b8b9b..3da2b9c4fcc 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/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index b1f70eea949..a0e555166cc 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -77,6 +77,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Do not rotate log files on startup when interval is configured and rotateonstartup is disabled. {pull}17613[17613] - Fix goroutine leak and Elasticsearch output file descriptor leak when output reloading is in use. {issue}10491[10491] {pull}17381[17381] - Fix `setup.dashboards.index` setting not working. {pull}17749[17749] +- Fix Elasticsearch license endpoint URL referenced in error message. {issue}17880[17880] {pull}18030[18030] *Auditbeat* @@ -199,6 +200,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Update RPM packages contained in Beat Docker images. {issue}17035[17035] - Update supported versions of `redis` output. {pull}17198[17198] - Update documentation for system.process.memory fields to include clarification on Windows os's. {pull}17268[17268] +- Add `replace` processor for replacing string values of fields. {pull}17342[17342] - Add optional regex based cid extractor to `add_kubernetes_metadata` processor. {pull}17360[17360] - Add `urldecode` processor to for decoding URL-encoded fields. {pull}17505[17505] - Add support for AWS IAM `role_arn` in credentials config. {pull}17658[17658] {issue}12464[12464] diff --git a/Jenkinsfile b/Jenkinsfile index ba73c431091..0ba1f3d8481 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -49,13 +49,13 @@ pipeline { steps { deleteDir() gitCheckout(basedir: "${BASE_DIR}") + stash allowEmpty: true, name: 'source', useDefaultExcludes: false dir("${BASE_DIR}"){ loadConfigEnvVars() } whenTrue(params.debug){ dumpFilteredEnvironment() } - stash allowEmpty: true, name: 'source', useDefaultExcludes: false } } stage('Lint'){ @@ -606,6 +606,9 @@ pipeline { always { runbld() } + cleanup { + notifyBuildResult(prComment: true) + } } } @@ -867,7 +870,6 @@ def isChangedOSSCode(patterns) { "^\\.ci/.*", ] allPatterns.addAll(patterns) - allPatterns.addAll(getVendorPatterns('libbeat')) return isChanged(allPatterns) } @@ -882,7 +884,6 @@ def isChangedXPackCode(patterns) { "^\\.ci/.*", ] allPatterns.addAll(patterns) - allPatterns.addAll(getVendorPatterns('x-pack/libbeat')) return isChanged(allPatterns) } @@ -890,6 +891,10 @@ def loadConfigEnvVars(){ def empty = [] env.GO_VERSION = readFile(".go-version").trim() + withEnv(["HOME=${env.WORKSPACE}"]) { + sh(label: "Install Go ${env.GO_VERSION}", script: ".ci/scripts/install-go.sh") + } + // Libbeat is the core framework of Beats. It has no additional dependencies // on other projects in the Beats repository. env.BUILD_LIBBEAT = isChangedOSSCode(empty) @@ -948,17 +953,25 @@ def loadConfigEnvVars(){ // involved. env.BUILD_KUBERNETES = isChanged(["^deploy/kubernetes/.*"]) - env.BUILD_GENERATOR = isChangedOSSCode(getVendorPatterns('generator')) + def generatorPatterns = ['^generator/.*'] + generatorPatterns.addAll(getVendorPatterns('generator/common/beatgen')) + generatorPatterns.addAll(getVendorPatterns('metricbeat/beater')) + env.BUILD_GENERATOR = isChangedOSSCode(generatorPatterns) } /** This method grab the dependencies of a Go module and transform them on regexp */ def getVendorPatterns(beatName){ + def os = goos() + def goRoot = "${env.WORKSPACE}/.gvm/versions/go${GO_VERSION}.${os}.amd64" def output = "" - docker.image("golang:${GO_VERSION}").inside{ + + withEnv([ + "HOME=${env.WORKSPACE}/${env.BASE_DIR}", + "PATH=${env.WORKSPACE}/bin:${goRoot}/bin:${env.PATH}", + ]) { output = sh(label: 'Get vendor dependency patterns', returnStdout: true, script: """ - export HOME=${WORKSPACE}/${BASE_DIR} go list -mod=vendor -f '{{ .ImportPath }}{{ "\\n" }}{{ join .Deps "\\n" }}' ./${beatName}\ |awk '{print \$1"/.*"}'\ |sed -e "s#github.com/elastic/beats/v7/##g" diff --git a/generator/_templates/metricbeat/{beat}/magefile.go b/generator/_templates/metricbeat/{beat}/magefile.go index b1b78829ee1..934276e633b 100644 --- a/generator/_templates/metricbeat/{beat}/magefile.go +++ b/generator/_templates/metricbeat/{beat}/magefile.go @@ -16,6 +16,9 @@ import ( "github.com/elastic/beats/v7/dev-tools/mage/target/unittest" "github.com/elastic/beats/v7/generator/common/beatgen" metricbeat "github.com/elastic/beats/v7/metricbeat/scripts/mage" + + // mage:import + _ "github.com/elastic/beats/v7/metricbeat/scripts/mage/target/metricset" ) func init() { diff --git a/libbeat/processors/actions/docs/replace.asciidoc b/libbeat/processors/actions/docs/replace.asciidoc new file mode 100644 index 00000000000..3faf3e0bcce --- /dev/null +++ b/libbeat/processors/actions/docs/replace.asciidoc @@ -0,0 +1,49 @@ +[[replace-fields]] +=== Replace fields from events + +++++ +replace +++++ + +The `replace` processor takes a list of fields to replace the field value +matching a pattern with replacement string. Under the `fields` key, each entry +contains a `field: field-name`, `pattern: regex-pattern` and +`replacement: replacement-string`, where: + +* `field` is the original field name +* `pattern` is regex pattern to match field's value +* `replacement` is the replacement string to use for updating the field's value + +The `replace` processor cannot be used to replace value with a completely new value. + +TIP: You can replace field value to truncate part of field value or replace +it with a new string. It can also be used for masking PII information. + +Following example will change path from /usr/bin to /usr/local/bin + +[source,yaml] +------- +processors: +- replace: + fields: + - field: "file.path" + pattern: "/usr/" + replacement: "/usr/local/" + ignore_missing: false + fail_on_error: true +------- + +The `replace` processor has following configuration settings: + +`ignore_missing`:: (Optional) If set to true, no error is logged in case a specifiedfield +is missing. Default is `false`. + +`fail_on_error`:: (Optional) If set to true, in case of an error the replacement of +field values is stopped and the original event is returned. If set to false, replacement +continues even if an error occurs during replacement. Default is `true`. + +See <> for a list of supported conditions. + +You can specify multiple `ignore_missing` processors under the `processors` +section. + diff --git a/libbeat/processors/actions/replace.go b/libbeat/processors/actions/replace.go new file mode 100644 index 00000000000..37245817050 --- /dev/null +++ b/libbeat/processors/actions/replace.go @@ -0,0 +1,118 @@ +// 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 actions + +import ( + "fmt" + "regexp" + + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/processors" + "github.com/elastic/beats/v7/libbeat/processors/checks" + jsprocessor "github.com/elastic/beats/v7/libbeat/processors/script/javascript/module/processor" +) + +type replaceString struct { + config replaceStringConfig +} + +type replaceStringConfig struct { + Fields []replaceConfig `config:"fields"` + IgnoreMissing bool `config:"ignore_missing"` + FailOnError bool `config:"fail_on_error"` +} + +type replaceConfig struct { + Field string `config:"field"` + Pattern *regexp.Regexp `config:"pattern"` + Replacement string `config:"replacement"` +} + +func init() { + processors.RegisterPlugin("replace", + checks.ConfigChecked(NewReplaceString, + checks.RequireFields("fields"))) + + jsprocessor.RegisterPlugin("Replace", NewReplaceString) +} + +// NewReplaceString returns a new replace processor. +func NewReplaceString(c *common.Config) (processors.Processor, error) { + config := replaceStringConfig{ + IgnoreMissing: false, + FailOnError: true, + } + err := c.Unpack(&config) + if err != nil { + return nil, fmt.Errorf("failed to unpack the replace configuration: %s", err) + } + + f := &replaceString{ + config: config, + } + return f, nil +} + +func (f *replaceString) Run(event *beat.Event) (*beat.Event, error) { + var backup common.MapStr + // Creates a copy of the event to revert in case of failure + if f.config.FailOnError { + backup = event.Fields.Clone() + } + + for _, field := range f.config.Fields { + err := f.replaceField(field.Field, field.Pattern, field.Replacement, event.Fields) + if err != nil { + errMsg := fmt.Errorf("Failed to replace fields in processor: %s", err) + logp.Debug("replace", errMsg.Error()) + if f.config.FailOnError { + event.Fields = backup + event.PutValue("error.message", errMsg.Error()) + return event, err + } + } + } + + return event, nil +} + +func (f *replaceString) replaceField(field string, pattern *regexp.Regexp, replacement string, fields common.MapStr) error { + currentValue, err := fields.GetValue(field) + if err != nil { + // Ignore ErrKeyNotFound errors + if f.config.IgnoreMissing && errors.Cause(err) == common.ErrKeyNotFound { + return nil + } + return fmt.Errorf("could not fetch value for key: %s, Error: %s", field, err) + } + + updatedString := pattern.ReplaceAllString(currentValue.(string), replacement) + _, err = fields.Put(field, updatedString) + if err != nil { + return fmt.Errorf("could not put value: %s: %v, %v", replacement, currentValue, err) + } + return nil +} + +func (f *replaceString) String() string { + return "replace=" + fmt.Sprintf("%+v", f.config.Fields) +} diff --git a/libbeat/processors/actions/replace_test.go b/libbeat/processors/actions/replace_test.go new file mode 100644 index 00000000000..e54d16c5012 --- /dev/null +++ b/libbeat/processors/actions/replace_test.go @@ -0,0 +1,248 @@ +// 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 actions + +import ( + "reflect" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" +) + +func TestReplaceRun(t *testing.T) { + var tests = []struct { + description string + Fields []replaceConfig + IgnoreMissing bool + FailOnError bool + Input common.MapStr + Output common.MapStr + error bool + }{ + { + description: "simple field replacing", + Fields: []replaceConfig{ + { + Field: "f", + Pattern: regexp.MustCompile(`a`), + Replacement: "b", + }, + }, + Input: common.MapStr{ + "f": "abc", + }, + Output: common.MapStr{ + "f": "bbc", + }, + error: false, + IgnoreMissing: false, + FailOnError: true, + }, + { + description: "Add one more hierarchy to event", + Fields: []replaceConfig{ + { + Field: "f.b", + Pattern: regexp.MustCompile(`a`), + Replacement: "b", + }, + }, + Input: common.MapStr{ + "f": common.MapStr{ + "b": "abc", + }, + }, + Output: common.MapStr{ + "f": common.MapStr{ + "b": "bbc", + }, + }, + error: false, + IgnoreMissing: false, + FailOnError: true, + }, + { + description: "replace two fields at the same time.", + Fields: []replaceConfig{ + { + Field: "f", + Pattern: regexp.MustCompile(`a.*c`), + Replacement: "cab", + }, + { + Field: "g", + Pattern: regexp.MustCompile(`ef`), + Replacement: "oor", + }, + }, + Input: common.MapStr{ + "f": "abbbc", + "g": "def", + }, + Output: common.MapStr{ + "f": "cab", + "g": "door", + }, + error: false, + IgnoreMissing: false, + FailOnError: true, + }, + { + description: "test missing fields", + Fields: []replaceConfig{ + { + Field: "f", + Pattern: regexp.MustCompile(`abc`), + Replacement: "xyz", + }, + { + Field: "g", + Pattern: regexp.MustCompile(`def`), + Replacement: "", + }, + }, + Input: common.MapStr{ + "m": "abc", + "n": "def", + }, + Output: common.MapStr{ + "m": "abc", + "n": "def", + "error": common.MapStr{ + "message": "Failed to replace fields in processor: could not fetch value for key: f, Error: key not found", + }, + }, + error: true, + IgnoreMissing: false, + FailOnError: true, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + f := &replaceString{ + config: replaceStringConfig{ + Fields: test.Fields, + IgnoreMissing: test.IgnoreMissing, + FailOnError: test.FailOnError, + }, + } + event := &beat.Event{ + Fields: test.Input, + } + + newEvent, err := f.Run(event) + if !test.error { + assert.Nil(t, err) + } else { + assert.NotNil(t, err) + } + + assert.True(t, reflect.DeepEqual(newEvent.Fields, test.Output)) + }) + } +} + +func TestReplaceField(t *testing.T) { + var tests = []struct { + Field string + Pattern *regexp.Regexp + Replacement string + ignoreMissing bool + failOnError bool + Input common.MapStr + Output common.MapStr + error bool + description string + }{ + { + description: "replace part of field value with another string", + Field: "f", + Pattern: regexp.MustCompile(`a`), + Replacement: "b", + Input: common.MapStr{ + "f": "abc", + }, + Output: common.MapStr{ + "f": "bbc", + }, + error: false, + failOnError: true, + ignoreMissing: false, + }, + { + description: "Add hierarchy to event and replace", + Field: "f.b", + Pattern: regexp.MustCompile(`a`), + Replacement: "b", + Input: common.MapStr{ + "f": common.MapStr{ + "b": "abc", + }, + }, + Output: common.MapStr{ + "f": common.MapStr{ + "b": "bbc", + }, + }, + error: false, + ignoreMissing: false, + failOnError: true, + }, + { + description: "try replacing value of missing fields in event", + Field: "f", + Pattern: regexp.MustCompile(`abc`), + Replacement: "xyz", + Input: common.MapStr{ + "m": "abc", + "n": "def", + }, + Output: common.MapStr{ + "m": "abc", + "n": "def", + }, + error: true, + ignoreMissing: false, + failOnError: true, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + f := &replaceString{ + config: replaceStringConfig{ + IgnoreMissing: test.ignoreMissing, + FailOnError: test.failOnError, + }, + } + + err := f.replaceField(test.Field, test.Pattern, test.Replacement, test.Input) + if err != nil { + assert.Equal(t, test.error, true) + } + + assert.True(t, reflect.DeepEqual(test.Input, test.Output)) + }) + } +} diff --git a/metricbeat/Makefile b/metricbeat/Makefile index 9cbf88c3a59..7a05a566775 100644 --- a/metricbeat/Makefile +++ b/metricbeat/Makefile @@ -3,6 +3,6 @@ ES_BEATS ?= .. include $(ES_BEATS)/dev-tools/make/mage.mk # Creates a new metricset. Requires the params MODULE and METRICSET -.PHONY: create-metricset +.PHONY: create-metricset create-metricset: mage createMetricset diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 04c431efd59..0aaafa9488b 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -25865,6 +25865,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 fe03abd62a3..bacbcb00906 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/magefile.go b/metricbeat/magefile.go index 0281c6892ba..b46dc9c7957 100644 --- a/metricbeat/magefile.go +++ b/metricbeat/magefile.go @@ -27,7 +27,6 @@ import ( "time" "github.com/magefile/mage/mg" - "github.com/magefile/mage/sh" devtools "github.com/elastic/beats/v7/dev-tools/mage" metricbeat "github.com/elastic/beats/v7/metricbeat/scripts/mage" @@ -48,6 +47,8 @@ import ( "github.com/elastic/beats/v7/dev-tools/mage/target/unittest" // mage:import _ "github.com/elastic/beats/v7/dev-tools/mage/target/compose" + // mage:import + _ "github.com/elastic/beats/v7/metricbeat/scripts/mage/target/metricset" ) func init() { @@ -197,29 +198,3 @@ func PythonIntegTest(ctx context.Context) error { return devtools.PythonNoseTest(devtools.DefaultPythonTestIntegrationArgs()) }, devtools.ListMatchingEnvVars("NOSE_")...) } - -// CreateMetricset creates a new metricset. -// -// Required ENV variables: -// * MODULE: Name of the module -// * METRICSET: Name of the metricset -func CreateMetricset(ctx context.Context) error { - ve, err := devtools.PythonVirtualenv() - if err != nil { - return err - } - python, err := devtools.LookVirtualenvPath(ve, "python") - if err != nil { - return err - } - path, err := os.Getwd() - if err != nil { - return err - } - - _, err = sh.Exec( - map[string]string{}, os.Stdout, os.Stderr, python, "scripts/create_metricset.py", - "--path", path, "--module", os.Getenv("MODULE"), "--metricset", os.Getenv("METRICSET"), - ) - return err -} diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index 6de1e04436a..bdbb3ae87ba 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 878e279b969..8450e1afc6d 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/metricbeat/scripts/mage/target/metricset/metricset.go b/metricbeat/scripts/mage/target/metricset/metricset.go new file mode 100644 index 00000000000..b1899e9352a --- /dev/null +++ b/metricbeat/scripts/mage/target/metricset/metricset.go @@ -0,0 +1,54 @@ +// 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 metricset + +import ( + "os" + "path/filepath" + + "github.com/magefile/mage/sh" + + devtools "github.com/elastic/beats/v7/dev-tools/mage" +) + +// CreateMetricset creates a new metricset. +// +// Required ENV variables: +// * MODULE: Name of the module +// * METRICSET: Name of the metricset +func CreateMetricset() error { + ve, err := devtools.PythonVirtualenv() + if err != nil { + return err + } + python, err := devtools.LookVirtualenvPath(ve, "python") + if err != nil { + return err + } + beatsDir, err := devtools.ElasticBeatsDir() + if err != nil { + return err + } + scriptPath := filepath.Join(beatsDir, "metricbeat", "scripts", "create_metricset.py") + + _, err = sh.Exec( + map[string]string{}, os.Stdout, os.Stderr, python, scriptPath, + "--path", devtools.CWD(), "--module", os.Getenv("MODULE"), "--metricset", os.Getenv("METRICSET"), + ) + return err +} diff --git a/x-pack/elastic-agent/CHANGELOG.asciidoc b/x-pack/elastic-agent/CHANGELOG.asciidoc index 9cdaa81432c..ed928e1d688 100644 --- a/x-pack/elastic-agent/CHANGELOG.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.asciidoc @@ -38,4 +38,5 @@ - Expose stream.* variables in events {pull}17468[17468] - Monitoring configuration reloadable {pull}17855[17855] - Pack ECS metadata to request payload send to fleet {pull}17894[17894] +- Allow CLI overrides of paths {pull}17781[17781] - Enable Filebeat input: S3, Azureeventhub, cloudfoundry, httpjson, netflow, o365audit. {pull}17909[17909] diff --git a/x-pack/elastic-agent/main_test.go b/x-pack/elastic-agent/main_test.go index cdced7ea677..79e2a9dc719 100644 --- a/x-pack/elastic-agent/main_test.go +++ b/x-pack/elastic-agent/main_test.go @@ -8,17 +8,21 @@ import ( "flag" "testing" - // Just using this a place holder. - "github.com/elastic/beats/v7/x-pack/filebeat/cmd" + "github.com/spf13/cobra" ) var systemTest *bool func init() { testing.Init() + + cmd := &cobra.Command{ + Use: "elastic-agent [subcommand]", + } + systemTest = flag.Bool("systemTest", false, "Set to true when running system tests") - cmd.RootCmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("systemTest")) - cmd.RootCmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("test.coverprofile")) + cmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("systemTest")) + cmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("test.coverprofile")) } // Test started when the test binary is started. Only calls main. diff --git a/x-pack/elastic-agent/pkg/agent/application/global_config.go b/x-pack/elastic-agent/pkg/agent/application/global_config.go index 44e9f2772ff..a08a59b0912 100644 --- a/x-pack/elastic-agent/pkg/agent/application/global_config.go +++ b/x-pack/elastic-agent/pkg/agent/application/global_config.go @@ -8,33 +8,64 @@ import ( "os" "path/filepath" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" ) var ( - homePath string - dataPath string + homePath string + dataPath string + overwrites *common.Config ) func init() { homePath = retrieveExecutablePath() dataPath = retrieveDataPath() + overwrites = common.NewConfig() + common.ConfigOverwriteFlag(nil, overwrites, "path.home", "path.home", "", "Agent root path") + common.ConfigOverwriteFlag(nil, overwrites, "path.data", "path.data", "", "Data path contains Agent managed binaries") +} + +// HomePath returns home path where. +func HomePath() string { + if val, err := overwrites.String("path.home", -1); err == nil { + return val + } + + return homePath +} + +// DataPath returns data path where. +func DataPath() string { + if val, err := overwrites.String("path.data", -1); err == nil { + return val + } + + return dataPath } // InjectAgentConfig injects config to a provided configuration. func InjectAgentConfig(c *config.Config) error { - globalConfig := AgentGlobalConfig() + globalConfig := agentGlobalConfig() if err := c.Merge(globalConfig); err != nil { return errors.New("failed to inject agent global config", err, errors.TypeConfig) } + return injectOverwrites(c) +} + +func injectOverwrites(c *config.Config) error { + if err := c.Merge(overwrites); err != nil { + return errors.New("failed to inject agent overwrites", err, errors.TypeConfig) + } + return nil } -// AgentGlobalConfig gets global config used for resolution of variables inside configuration +// agentGlobalConfig gets global config used for resolution of variables inside configuration // such as ${path.data}. -func AgentGlobalConfig() map[string]interface{} { +func agentGlobalConfig() map[string]interface{} { return map[string]interface{}{ "path": map[string]interface{}{ "data": dataPath, diff --git a/x-pack/elastic-agent/pkg/agent/cmd/common.go b/x-pack/elastic-agent/pkg/agent/cmd/common.go index 0189f8e408d..956b7324d3c 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/common.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/common.go @@ -5,30 +5,30 @@ package cmd import ( + "flag" "fmt" "os" "path/filepath" "github.com/spf13/cobra" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/basecmd" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/cli" ) -var defaultConfig = "elastic-agent.yml" +const defaultConfig = "elastic-agent.yml" type globalFlags struct { - PathConfigFile string PathConfig string - PathData string - PathHome string - PathLogs string + PathConfigFile string FlagStrictPerms bool } +// Config returns path which identifies configuration file. func (f *globalFlags) Config() string { if len(f.PathConfigFile) == 0 { - return filepath.Join(f.PathHome, defaultConfig) + return filepath.Join(application.HomePath(), defaultConfig) } return f.PathConfigFile } @@ -50,11 +50,11 @@ func NewCommandWithArgs(args []string, streams *cli.IOStreams) *cobra.Command { flags := &globalFlags{} + cmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("path.home")) + cmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("path.data")) + cmd.PersistentFlags().StringVarP(&flags.PathConfigFile, "", "c", defaultConfig, fmt.Sprintf(`Configuration file, relative to path.config (default "%s")`, defaultConfig)) - cmd.PersistentFlags().StringVarP(&flags.PathHome, "path.home", "", "", "Home path") cmd.PersistentFlags().StringVarP(&flags.PathConfig, "path.config", "", "${path.home}", "Configuration path") - cmd.PersistentFlags().StringVarP(&flags.PathData, "path.data", "", "${path.home}/data", "Data path") - cmd.PersistentFlags().StringVarP(&flags.PathLogs, "path.logs", "", "${path.home}/logs", "Logs path") cmd.PersistentFlags().BoolVarP(&flags.FlagStrictPerms, "strict.perms", "", true, "Strict permission checking on config files") // Add version. diff --git a/x-pack/elastic-agent/pkg/agent/cmd/enroll.go b/x-pack/elastic-agent/pkg/agent/cmd/enroll.go index a2a7ee48d22..abc5efb3b90 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/enroll.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/enroll.go @@ -46,13 +46,13 @@ func newEnrollCommandWithArgs(flags *globalFlags, _ []string, streams *cli.IOStr func enroll(streams *cli.IOStreams, cmd *cobra.Command, flags *globalFlags, args []string) error { warn.PrintNotGA(streams.Out) - - config, err := config.LoadYAML(flags.PathConfigFile) + pathConfigFile := flags.Config() + config, err := config.LoadYAML(pathConfigFile) if err != nil { return errors.New(err, - fmt.Sprintf("could not read configuration file %s", flags.PathConfigFile), + fmt.Sprintf("could not read configuration file %s", pathConfigFile), errors.TypeFilesystem, - errors.M(errors.MetaKeyPath, flags.PathConfigFile)) + errors.M(errors.MetaKeyPath, pathConfigFile)) } force, _ := cmd.Flags().GetBool("force") @@ -95,7 +95,7 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command, flags *globalFlags, args c, err := application.NewEnrollCmd( logger, &options, - flags.PathConfigFile, + pathConfigFile, ) if err != nil { diff --git a/x-pack/elastic-agent/pkg/agent/cmd/run.go b/x-pack/elastic-agent/pkg/agent/cmd/run.go index f0196ba7875..db199e2b47d 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/run.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/run.go @@ -33,12 +33,13 @@ func newRunCommandWithArgs(flags *globalFlags, _ []string, streams *cli.IOStream } func run(flags *globalFlags, streams *cli.IOStreams) error { - config, err := config.LoadYAML(flags.PathConfigFile) + pathConfigFile := flags.Config() + config, err := config.LoadYAML(pathConfigFile) if err != nil { return errors.New(err, - fmt.Sprintf("could not read configuration file %s", flags.PathConfigFile), + fmt.Sprintf("could not read configuration file %s", pathConfigFile), errors.TypeFilesystem, - errors.M(errors.MetaKeyPath, flags.PathConfigFile)) + errors.M(errors.MetaKeyPath, pathConfigFile)) } logger, err := logger.NewFromConfig(config) @@ -46,7 +47,7 @@ func run(flags *globalFlags, streams *cli.IOStreams) error { return err } - app, err := application.New(logger, flags.PathConfigFile) + app, err := application.New(logger, pathConfigFile) if err != nil { return err } diff --git a/x-pack/libbeat/licenser/elastic_fetcher.go b/x-pack/libbeat/licenser/elastic_fetcher.go index 6ffa5f6fa37..b2c855df6b7 100644 --- a/x-pack/libbeat/licenser/elastic_fetcher.go +++ b/x-pack/libbeat/licenser/elastic_fetcher.go @@ -18,9 +18,9 @@ import ( "github.com/elastic/beats/v7/libbeat/logp" ) -const xPackURL = "/_license" +const licenseURL = "/_license" -// params defaults query parameters to send to the '_xpack' endpoint by default we only need +// params defaults query parameters to send to the '_license' endpoint by default we only need // machine parseable data. var params = map[string]string{ "human": "false", @@ -88,12 +88,12 @@ func NewElasticFetcher(client esclient) *ElasticFetcher { return &ElasticFetcher{client: client, log: logp.NewLogger("elasticfetcher")} } -// Fetch retrieves the license information from an Elasticsearch Client, it will call the `_xpack` -// end point and will return a parsed license. If the `_xpack` endpoint is unreacheable we will +// Fetch retrieves the license information from an Elasticsearch Client, it will call the `_license` +// endpoint and will return a parsed license. If the `_license` endpoint is unreacheable we will // return the OSS License otherwise we return an error. func (f *ElasticFetcher) Fetch() (*License, error) { - status, body, err := f.client.Request("GET", xPackURL, "", params, nil) - // When we are running an OSS release of elasticsearch the _xpack endpoint will return a 405, + status, body, err := f.client.Request("GET", licenseURL, "", params, nil) + // When we are running an OSS release of elasticsearch the _license endpoint will return a 405, // "Method Not Allowed", so we return the default OSS license. if status == http.StatusBadRequest { f.log.Debug("Received 'Bad request' (400) response from server, fallback to OSS license") diff --git a/x-pack/libbeat/licenser/es_callback.go b/x-pack/libbeat/licenser/es_callback.go index 0ca99db8f5f..06cc055083c 100644 --- a/x-pack/libbeat/licenser/es_callback.go +++ b/x-pack/libbeat/licenser/es_callback.go @@ -30,7 +30,7 @@ func Enforce(name string, checks ...CheckFunc) { license, err := fetcher.Fetch() if err != nil { - return errors.Wrapf(err, "cannot retrieve the elasticsearch license from the /_xpack endpoint, "+ + return errors.Wrapf(err, "cannot retrieve the elasticsearch license from the /_license endpoint, "+ "%s requires the default distribution of Elasticsearch. Please make the endpoint accessible "+ "to %s so it can verify the license.", name, name) } diff --git a/x-pack/metricbeat/Makefile b/x-pack/metricbeat/Makefile index 019d3b9309a..c77d4c68517 100644 --- a/x-pack/metricbeat/Makefile +++ b/x-pack/metricbeat/Makefile @@ -1,3 +1,8 @@ ES_BEATS ?= ../.. include $(ES_BEATS)/dev-tools/make/mage.mk + +# Creates a new metricset. Requires the params MODULE and METRICSET +.PHONY: create-metricset +create-metricset: + mage createMetricset diff --git a/x-pack/metricbeat/magefile.go b/x-pack/metricbeat/magefile.go index ffd40949d70..992e4d6d1fa 100644 --- a/x-pack/metricbeat/magefile.go +++ b/x-pack/metricbeat/magefile.go @@ -25,6 +25,8 @@ import ( "github.com/elastic/beats/v7/dev-tools/mage/target/unittest" // mage:import "github.com/elastic/beats/v7/dev-tools/mage/target/test" + // mage:import + _ "github.com/elastic/beats/v7/metricbeat/scripts/mage/target/metricset" ) func init() { diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index 3fba65dbee6..d736b844393 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -843,7 +843,7 @@ metricbeat.modules: #--------------------------------- Kvm Module --------------------------------- - module: kvm - metricsets: ["dommemstat"] + metricsets: ["dommemstat", "status"] enabled: true period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"]