diff options
author | Artjoms Rimdjonoks <artjoms.rimdjonoks@zabbix.com> | 2022-04-06 16:28:07 +0300 |
---|---|---|
committer | Artjoms Rimdjonoks <artjoms.rimdjonoks@zabbix.com> | 2022-04-06 16:28:07 +0300 |
commit | 17d06d7a89b2061af9b197dda90028a1b9a1380d (patch) | |
tree | ef55771b4ee832429fcaab41cf672f7be68103a8 /src | |
parent | 9f13ff30054f586b5f9ed6c856309be3acdc0cc9 (diff) | |
parent | f721d53c0bfc1371fe9e45c97ae6c2c5057d579b (diff) |
.......... [DEV-2133] updated to latest master
Diffstat (limited to 'src')
-rw-r--r-- | src/go/pkg/zbxcmd/zbxcmd_nix.go | 5 | ||||
-rw-r--r-- | src/go/plugins/smart/smart.go | 270 | ||||
-rw-r--r-- | src/go/plugins/smart/smart_nix.go | 2 | ||||
-rw-r--r-- | src/go/plugins/smart/smart_test.go | 422 | ||||
-rw-r--r-- | src/go/plugins/smart/smartfs.go | 125 | ||||
-rw-r--r-- | src/libs/zbxalgo/linked_list.c | 31 | ||||
-rw-r--r-- | src/libs/zbxnix/daemon.c | 28 | ||||
-rw-r--r-- | src/zabbix_server/ha/ha_manager.c | 102 | ||||
-rw-r--r-- | src/zabbix_server/lld/lld_item.c | 10 | ||||
-rw-r--r-- | src/zabbix_server/preprocessor/preproc_manager.c | 104 |
10 files changed, 959 insertions, 140 deletions
diff --git a/src/go/pkg/zbxcmd/zbxcmd_nix.go b/src/go/pkg/zbxcmd/zbxcmd_nix.go index addd629406e..59f83a49303 100644 --- a/src/go/pkg/zbxcmd/zbxcmd_nix.go +++ b/src/go/pkg/zbxcmd/zbxcmd_nix.go @@ -1,5 +1,4 @@ //go:build !windows -// +build !windows /* ** Zabbix @@ -65,7 +64,9 @@ func execute(s string, timeout time.Duration, path string, strict bool) (string, // we need to check error after t.Stop so we can inform the user if timeout was reached and Zabbix agent2 terminated the command if strict && werr != nil && !errors.Is(werr, syscall.ECHILD) { - return "", fmt.Errorf("Command execution failed: %s", werr) + log.Debugf("Command [%s] execution failed: %s\n%s", s, werr, b.String()) + + return "", fmt.Errorf("Command execution failed: %w", werr) } if MaxExecuteOutputLenB <= len(b.String()) { diff --git a/src/go/plugins/smart/smart.go b/src/go/plugins/smart/smart.go index f71407b6b97..36bb16d9b8a 100644 --- a/src/go/plugins/smart/smart.go +++ b/src/go/plugins/smart/smart.go @@ -21,12 +21,27 @@ package smart import ( "encoding/json" + "fmt" + "strings" "zabbix.com/pkg/conf" "zabbix.com/pkg/plugin" "zabbix.com/pkg/zbxerr" ) +const ( + twoParameters = 2 + oneParameter = 1 + all = 0 + + firstParameter = 0 + secondParameter = 1 + + diskGet = "smart.disk.get" + diskDiscovery = "smart.disk.discovery" + attributeDiscovery = "smart.attribute.discovery" +) + // Options - type Options struct { plugin.SystemOptions `conf:"optional,name=System"` @@ -61,7 +76,7 @@ func (p *Plugin) Validate(options interface{}) error { // Export - func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) { - if len(params) > 0 { + if len(params) > 0 && key != diskGet { return nil, zbxerr.ErrorTooManyParameters } @@ -72,84 +87,217 @@ func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) var jsonArray []byte switch key { - case "smart.disk.discovery": - out := []device{} - - r, err := p.execute(false) + case diskDiscovery: + jsonArray, err = p.diskDiscovery() if err != nil { - return nil, err - } - - for _, dev := range r.devices { - out = append(out, device{ - Name: cutPrefix(dev.Info.Name), - DeviceType: getType(dev.Info.DevType, dev.RotationRate, dev.SmartAttributes.Table), - Model: dev.ModelName, SerialNumber: dev.SerialNumber, - }) + return nil, zbxerr.ErrorCannotFetchData.Wrap(err) } - jsonArray, err = json.Marshal(out) + case diskGet: + jsonArray, err = p.diskGet(params) if err != nil { - return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err) + return nil, zbxerr.ErrorCannotFetchData.Wrap(err) } - case "smart.disk.get": - r, err := p.execute(true) + case attributeDiscovery: + jsonArray, err = p.attributeDiscovery() if err != nil { - return nil, err + return nil, zbxerr.ErrorCannotFetchData.Wrap(err) } - fields, err := setDiskFields(r.jsonDevices) - if err != nil { - return nil, err - } + default: + return nil, zbxerr.ErrorUnsupportedMetric + } - if fields == nil { - jsonArray, err = json.Marshal([]string{}) - if err != nil { - return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err) - } + return string(jsonArray), nil +} - break - } +func (p *Plugin) diskDiscovery() (jsonArray []byte, err error) { + out := []device{} + + r, err := p.execute(false) + if err != nil { + return nil, err + } + + for _, dev := range r.devices { + out = append(out, device{ + Name: cutPrefix(dev.Info.Name), + DeviceType: getType(dev.Info.DevType, dev.RotationRate, dev.SmartAttributes.Table), + Model: dev.ModelName, + SerialNumber: dev.SerialNumber, + Path: dev.Info.name, + RaidType: dev.Info.raidType, + Attributes: getAttributes(dev), + }) + } + + jsonArray, err = json.Marshal(out) + if err != nil { + return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err) + } + + return +} + +func (p *Plugin) diskGet(params []string) ([]byte, error) { + switch len(params) { + case twoParameters: + return p.diskGetSingle(params[firstParameter], params[secondParameter]) + case oneParameter: + return p.diskGetSingle(params[firstParameter], "") + case all: + return p.diskGetAll() + default: + return nil, zbxerr.ErrorTooManyParameters + } +} + +func (p *Plugin) diskGetSingle(path, raidType string) ([]byte, error) { + executable := path + + if raidType != "" { + executable = fmt.Sprintf("%s -d %s", executable, raidType) + } + + device, err := p.executeSingle(executable) + if err != nil { + return nil, err + } + + out, err := setSingleDiskFields(device) + if err != nil { + return nil, err + } + + jsonArray, err := json.Marshal(out) + if err != nil { + return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err) + } + + return jsonArray, nil +} + +func (p *Plugin) diskGetAll() (jsonArray []byte, err error) { + r, err := p.execute(true) + if err != nil { + return nil, err + } - jsonArray, err = json.Marshal(fields) + fields, err := setDiskFields(r.jsonDevices) + if err != nil { + return nil, err + } + + if fields == nil { + jsonArray, err = json.Marshal([]string{}) if err != nil { return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err) } - case "smart.attribute.discovery": - out := []attribute{} + return + } - r, err := p.execute(false) - if err != nil { - return nil, err - } + jsonArray, err = json.Marshal(fields) + if err != nil { + return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err) + } - for _, dev := range r.devices { - t := getAttributeType(dev.Info.DevType, dev.RotationRate, dev.SmartAttributes.Table) - for _, attr := range dev.SmartAttributes.Table { - out = append( - out, attribute{ - Name: cutPrefix(dev.Info.Name), - DeviceType: t, - ID: attr.ID, - Attrname: attr.Attrname, - Thresh: attr.Thresh, - }) - } + return +} + +func (p *Plugin) attributeDiscovery() (jsonArray []byte, err error) { + out := []attribute{} + + r, err := p.execute(false) + if err != nil { + return nil, err + } + + for _, dev := range r.devices { + t := getAttributeType(dev.Info.DevType, dev.RotationRate, dev.SmartAttributes.Table) + for _, attr := range dev.SmartAttributes.Table { + out = append( + out, attribute{ + Name: cutPrefix(dev.Info.Name), + DeviceType: t, + ID: attr.ID, + Attrname: attr.Attrname, + Thresh: attr.Thresh, + }) } + } - jsonArray, err = json.Marshal(out) - if err != nil { - return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err) + jsonArray, err = json.Marshal(out) + if err != nil { + return nil, zbxerr.ErrorCannotMarshalJSON.Wrap(err) + } + + return +} + +// setSingleDiskFields goes through provided device json data and sets required output fields. +// It returns an error if there is an issue with unmarshal for the provided input JSON map. +func setSingleDiskFields(dev []byte) (out map[string]interface{}, err error) { + attr := make(map[string]interface{}) + if err = json.Unmarshal(dev, &attr); err != nil { + return out, zbxerr.ErrorCannotUnmarshalJSON.Wrap(err) + } + + var sd singleDevice + if err = json.Unmarshal(dev, &sd); err != nil { + return out, zbxerr.ErrorCannotUnmarshalJSON.Wrap(err) + } + + diskType := getType(getTypeFromJson(attr), getRateFromJson(attr), getTablesFromJson(attr)) + + out = map[string]interface{}{} + out["disk_type"] = diskType + out["firmware_version"] = sd.Firmware + out["model_name"] = sd.ModelName + out["serial_number"] = sd.SerialNumber + out["exit_status"] = sd.Smartctl.ExitStatus + + var errors []string + for _, msg := range sd.Smartctl.Messages { + errors = append(errors, msg.Str) + } + + out["error"] = strings.Join(errors, ", ") + out["self_test_passed"] = setSelfTest(sd) + + if diskType == nvmeType { + out["temperature"] = sd.HealthLog.Temperature + out["power_on_time"] = sd.HealthLog.PowerOnTime + out["critical_warning"] = sd.HealthLog.CriticalWarning + out["media_errors"] = sd.HealthLog.MediaErrors + out["percentage_used"] = sd.HealthLog.Percentage_used + } else { + out["temperature"] = sd.Temperature.Current + out["power_on_time"] = sd.PowerOnTime.Hours + out["critical_warning"] = 0 + out["media_errors"] = 0 + out["percentage_used"] = 0 + } + + for _, a := range sd.SmartAttributes.Table { + if a.Name == unknownAttrName { + continue } - default: - return nil, zbxerr.ErrorUnsupportedMetric + out[strings.ToLower(a.Name)] = singleRequestAttribute{a.Raw.Value, a.Raw.Str} } - return string(jsonArray), nil + return +} + +// setSelfTest determines if device is self test capable and if the test is passed. +func setSelfTest(sd singleDevice) *bool { + if sd.Data.Capabilities.SelfTestsSupported { + return &sd.Data.SelfTest.Status.Passed + } + + return nil } // setDiskFields goes through provided device json map and sets disk_name @@ -242,6 +390,18 @@ func getAttributeType(devType string, rate int, tables []table) string { return getTypeByRateAndAttr(rate, tables) } +func getAttributes(in deviceParser) (out string) { + for _, table := range in.SmartAttributes.Table { + if table.Attrname == unknownAttrName { + continue + } + + out = out + " " + table.Attrname + } + + return strings.TrimSpace(out) +} + func getType(devType string, rate int, tables []table) string { switch devType { case nvmeType: diff --git a/src/go/plugins/smart/smart_nix.go b/src/go/plugins/smart/smart_nix.go index 447406c1d3b..5e87a39de9c 100644 --- a/src/go/plugins/smart/smart_nix.go +++ b/src/go/plugins/smart/smart_nix.go @@ -39,7 +39,7 @@ func (p *Plugin) executeSmartctl(args string, strict bool) ([]byte, error) { var out string var err error - executable := fmt.Sprintf("sudo %s %s", path, args) + executable := fmt.Sprintf("sudo -n %s %s", path, args) p.Tracef("executing smartctl command: %s", executable) diff --git a/src/go/plugins/smart/smart_test.go b/src/go/plugins/smart/smart_test.go index eb73c16c8c6..bce0bc96f34 100644 --- a/src/go/plugins/smart/smart_test.go +++ b/src/go/plugins/smart/smart_test.go @@ -25,20 +25,390 @@ import ( "testing" ) +const ( + nvme = `{ + "smartctl": { + "exit_status": 0 + }, + "device": { + "name": "/dev/nvme0", + "type": "nvme" + }, + "model_name": "INTEL SSDPEKNW512G8H", + "serial_number": "BTNH115603K7512A", + "firmware_version": "HPS1", + "smart_status": { + "passed": true + }, + "nvme_smart_health_information_log": { + "critical_warning": 0, + "temperature": 25, + "percentage_used": 0, + "power_on_hours": 2222, + "media_errors": 0 + } + }` + + hdd = `{ + "json_format_version": [ + 1, + 0 + ], + "smartctl": { + "version": [ + 7, + 2 + ], + "svn_revision": "5155", + "platform_info": "x86_64-linux-5.13.0-30-generic", + "build_info": "(local build)", + "argv": [ + "smartctl", + "-a", + "-j", + "/dev/sda" + ], + "exit_status": 0 + }, + "device": { + "name": "/dev/sda", + "info_name": "/dev/sda [SAT]", + "type": "sat", + "protocol": "ATA" + }, + "model_family": "Seagate Surveillance", + "model_name": "ST1000VX000-1ES162", + "serial_number": "Z4Y7SJBD", + "wwn": { + "naa": 5, + "oui": 3152, + "id": 2071267458 + }, + "firmware_version": "CV26", + "rotation_rate": 7200, + "ata_smart_data": { + "self_test": { + "status": { + "value": 0, + "string": "completed without error", + "passed": true + } + }, + "capabilities": { + "self_tests_supported": true + } + }, + "ata_smart_attributes": { + "table": [ + { + "id": 1, + "name": "Raw_Read_Error_Rate", + "raw": { + "value": 182786912, + "string": "182786912" + } + }, + { + "id": 3, + "name": "Spin_Up_Time", + "raw": { + "value": 0, + "string": "0" + } + } + ] + }, + "power_on_time": { + "hours": 39153 + }, + "temperature": { + "current": 30 + } + }` + + ssd = ` + { + "json_format_version": [ + 1, + 0 + ], + "smartctl": { + "exit_status": 0 + }, + "device": { + "name": "/dev/sda", + "info_name": "/dev/sda", + "type": "ata", + "protocol": "ATA" + }, + "model_name": "TS128GMTS800", + "serial_number": "D486530350", + "firmware_version": "O1225G", + "rotation_rate": 0, + "smart_status": { + "passed": true + }, + "ata_smart_data": { + "self_test": { + "status": { + "passed": true + } + }, + "capabilities": { + "values": [ + 113, + 2 + ], + "self_tests_supported": true + } + }, + "ata_smart_attributes": { + "table": [ + { + "name": "Raw_Read_Error_Rate", + "value": 100, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "name": "Reallocated_Sector_Ct", + "raw": { + "value": 10, + "string": "10" + } + } + ] + }, + "power_on_time": { + "hours": 732 + }, + "temperature": { + "current": 18 + } + }` + + ssdUnknown = ` + { + "json_format_version": [ + 1, + 0 + ], + "smartctl": { + "exit_status": 0 + }, + "device": { + "name": "/dev/sda", + "info_name": "/dev/sda", + "type": "ata", + "protocol": "ATA" + }, + "model_name": "TS128GMTS800", + "serial_number": "D486530350", + "firmware_version": "O1225G", + "rotation_rate": 0, + "smart_status": { + "passed": true + }, + "ata_smart_data": { + "self_test": { + "status": { + "passed": true + } + }, + "capabilities": { + "values": [ + 113, + 2 + ], + "self_tests_supported": true + } + }, + "ata_smart_attributes": { + "table": [ + { + "name": "Raw_Read_Error_Rate", + "value": 100, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "name": "Unknown_Attribute", + "value": 0, + "raw": { + "value": 0, + "string": "0" + } + }, + { + "name": "Reallocated_Sector_Ct", + "raw": { + "value": 10, + "string": "10" + } + } + ] + }, + "power_on_time": { + "hours": 732 + }, + "temperature": { + "current": 18 + } + }` +) + var ( table1 = table{"test1", 1, 11} table2 = table{"test2", 2, 22} table3 = table{"test3", 3, 33} table4 = table{"test4", 4, 44} attrTable = table{"Spin_Up_Time", 5, 55} + unknown = table{"Unknown_Attribute", 0, 0} ) +func Test_setSingleDiskFields(t *testing.T) { + var nilReference *bool + + selftestSuccess := true + + type args struct { + dev []byte + } + tests := []struct { + name string + args args + wantOut map[string]interface{} + wantErr bool + }{ + { + "nvme_device", + args{[]byte(nvme)}, + map[string]interface{}{ + "critical_warning": 0, + "disk_type": "nvme", + "error": "", + "exit_status": 0, + "firmware_version": "HPS1", + "media_errors": 0, + "model_name": "INTEL SSDPEKNW512G8H", + "percentage_used": 0, + "power_on_time": 2222, + "self_test_passed": nilReference, + "serial_number": "BTNH115603K7512A", + "temperature": 25, + }, + false, + }, + { + "hdd_device", + args{[]byte(hdd)}, + map[string]interface{}{ + "critical_warning": 0, + "disk_type": "hdd", + "error": "", + "exit_status": 0, + "firmware_version": "CV26", + "media_errors": 0, + "model_name": "ST1000VX000-1ES162", + "percentage_used": 0, + "power_on_time": 39153, + "self_test_passed": &selftestSuccess, + "serial_number": "Z4Y7SJBD", + "temperature": 30, + "raw_read_error_rate": singleRequestAttribute{ + Value: 182786912, + Raw: "182786912", + }, + "spin_up_time": singleRequestAttribute{ + Value: 0, + Raw: "0", + }, + }, + false, + }, + { + "ssd_device", + args{[]byte(ssd)}, + map[string]interface{}{ + "critical_warning": 0, + "disk_type": "ssd", + "error": "", + "exit_status": 0, + "firmware_version": "O1225G", + "media_errors": 0, + "model_name": "TS128GMTS800", + "percentage_used": 0, + "power_on_time": 732, + "self_test_passed": &selftestSuccess, + "serial_number": "D486530350", + "temperature": 18, + "raw_read_error_rate": singleRequestAttribute{ + Value: 0, + Raw: "0", + }, + "reallocated_sector_ct": singleRequestAttribute{ + Value: 10, + Raw: "10", + }, + }, + false, + }, + { + "ssd_device_with_unknown_attribute", + args{[]byte(ssdUnknown)}, + map[string]interface{}{ + "critical_warning": 0, + "disk_type": "ssd", + "error": "", + "exit_status": 0, + "firmware_version": "O1225G", + "media_errors": 0, + "model_name": "TS128GMTS800", + "percentage_used": 0, + "power_on_time": 732, + "self_test_passed": &selftestSuccess, + "serial_number": "D486530350", + "temperature": 18, + "raw_read_error_rate": singleRequestAttribute{ + Value: 0, + Raw: "0", + }, + "reallocated_sector_ct": singleRequestAttribute{ + Value: 10, + Raw: "10", + }, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotOut, err := setSingleDiskFields(tt.args.dev) + if (err != nil) != tt.wantErr { + t.Errorf("setSingleDiskFields() error = %v, wantErr %v", err, tt.wantErr) + + return + } + + if !reflect.DeepEqual(gotOut, tt.wantOut) { + t.Errorf("setSingleDiskFields() = %v, want %v", gotOut, tt.wantOut) + } + }) + } +} + func Test_setDiskFields(t *testing.T) { - //nolint:lll - jsonSdaStr := `{"device": {"name": "/dev/sda","info_name": "/dev/sda [SAT]","type": "sat","protocol": "ATA"},"rotation_rate": 0}` - //nolint:lll + jsonSdaStr := `{ + "device": {"name": "/dev/sda","info_name": "/dev/sda [SAT]","type": "sat","protocol": "ATA"},"rotation_rate": 0 + }` sdaOutStr := map[string]interface{}{ - "device": map[string]interface{}{"name": "/dev/sda", "info_name": "/dev/sda [SAT]", "type": "sat", "protocol": "ATA"}, + "device": map[string]interface{}{ + "name": "/dev/sda", "info_name": "/dev/sda [SAT]", "type": "sat", "protocol": "ATA", + }, "disk_name": "sda", "disk_type": "ssd", "rotation_rate": 0, } @@ -196,6 +566,50 @@ func Test_getAttributeType(t *testing.T) { } } +func Test_getAttributes(t *testing.T) { + type args struct { + in deviceParser + } + tests := []struct { + name string + args args + want string + }{ + { + "attributes_set", + args{deviceParser{SmartAttributes: smartAttributes{Table: []table{table1, table2}}}}, + "test1 test2", + }, + { + "attributes_table_empty", + args{deviceParser{SmartAttributes: smartAttributes{Table: []table{}}}}, + "", + }, + { + "unknown_attributes_table_empty", + args{deviceParser{SmartAttributes: smartAttributes{Table: []table{table1, unknown, table2}}}}, + "test1 test2", + }, + { + "attributes_missing", + args{deviceParser{}}, + "", + }, + { + "parser_missing", + args{}, + "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getAttributes(tt.args.in); got != tt.want { + t.Errorf("getAttributes() = %v, want %v", got, tt.want) + } + }) + } +} + func Test_getType(t *testing.T) { type args struct { devType string diff --git a/src/go/plugins/smart/smartfs.go b/src/go/plugins/smart/smartfs.go index a01b87c4422..b695e853ba4 100644 --- a/src/go/plugins/smart/smartfs.go +++ b/src/go/plugins/smart/smartfs.go @@ -42,7 +42,8 @@ const ( ssdType = "ssd" hddType = "hdd" - spinUpAttrName = "Spin_Up_Time" + spinUpAttrName = "Spin_Up_Time" + unknownAttrName = "Unknown_Attribute" ataSmartAttrFieldName = "ata_smart_attributes" ataSmartAttrTableFieldName = "table" @@ -68,12 +69,79 @@ type device struct { DeviceType string `json:"{#DISKTYPE}"` Model string `json:"{#MODEL}"` SerialNumber string `json:"{#SN}"` + Path string `json:"{#PATH}"` + RaidType string `json:"{#RAIDTYPE}"` + Attributes string `json:"{#ATTRIBUTES}"` } type jsonDevice struct { serialNumber string jsonData string } +type singleDevice struct { + DiskType string `json:"disk_type"` + Firmware string `json:"firmware_version"` + ModelName string `json:"model_name"` + SerialNumber string `json:"serial_number"` + Smartctl smartctlField `json:"smartctl"` + HealthLog healthLog `json:"nvme_smart_health_information_log"` + SmartAttributes singelRequestTables `json:"ata_smart_attributes"` + Data ataData `json:"ata_smart_data"` + Temperature temperature `json:"temperature"` + PowerOnTime power `json:"power_on_time"` + Err string `json:"-"` + SelfTest bool `json:"-"` +} + +type healthLog struct { + Temperature int `json:"temperature"` + PowerOnTime int `json:"power_on_hours"` + CriticalWarning int `json:"critical_warning"` + MediaErrors int `json:"media_errors"` + Percentage_used int `json:"percentage_used"` +} + +type temperature struct { + Current int `json:"current"` +} + +type power struct { + Hours int `json:"hours"` +} + +type singelRequestTables struct { + Table []singelRequestRaw `json:"table"` +} + +type singelRequestRaw struct { + Name string `json:"name"` + Raw rawField `json:"raw"` +} + +type singleRequestAttribute struct { + Value int `json:"value"` + Raw string `json:"raw"` +} + +type rawField struct { + Value int `json:"value"` + Str string `json:"string"` +} + +type ataData struct { + SelfTest selfTest `json:"self_test"` + Capabilities capabilities `json:"capabilities"` +} +type capabilities struct { + SelfTestsSupported bool `json:"self_tests_supported"` +} +type selfTest struct { + Status status `json:"status"` +} +type status struct { + Passed bool `json:"passed"` +} + type attribute struct { Name string `json:"{#NAME}"` DeviceType string `json:"{#DISKTYPE}"` @@ -96,6 +164,8 @@ type deviceInfo struct { Name string `json:"name"` InfoName string `json:"info_name"` DevType string `json:"type"` + name string `json:"-"` + raidType string `json:"-"` } type smartctl struct { @@ -184,7 +254,17 @@ func (p *Plugin) execute(jsonRunner bool) (*runner, error) { return r, err } -//executeBase executed runners for basic devices retrived from smartctl +// executeSingle returns device data for single device from smartctl based on provided path. +func (p *Plugin) executeSingle(path string) (device []byte, err error) { + device, err = p.executeSmartctl(fmt.Sprintf("-a %s -j", path), false) + if err != nil { + return nil, fmt.Errorf("Failed to execute smartctl: %w.", err) + } + + return +} + +//executeBase executed runners for basic devices retrieved from smartctl. func (r *runner) executeBase(basicDev []deviceInfo, jsonRunner bool) error { r.startBasicRunners(jsonRunner) @@ -197,7 +277,7 @@ func (r *runner) executeBase(basicDev []deviceInfo, jsonRunner bool) error { return r.waitForExecution() } -//executeRaids executes runners for raid devices (except megaraid) retrived from smartctl +//executeRaids executes runners for raid devices (except megaraid) retrieved from smartctl func (r *runner) executeRaids(raids []deviceInfo, jsonRunner bool) { raidTypes := []string{"3ware", "areca", "cciss", "sat"} @@ -216,7 +296,7 @@ func (r *runner) executeRaids(raids []deviceInfo, jsonRunner bool) { r.waitForRaidExecution(r.raidDone) } -//executeMegaRaids executes runners for megaraid devices retrived from smartctl +//executeMegaRaids executes runners for megaraid devices retrieved from smartctl func (r *runner) executeMegaRaids(megaraids []deviceInfo, jsonRunner bool) { r.megaraids = make(chan raidParameters, len(megaraids)) @@ -381,6 +461,8 @@ func (r *runner) getBasicDevices(jsonRunner bool) { return } + dp.Info.name = name + r.mux.Lock() if jsonRunner { @@ -454,10 +536,14 @@ runner: } if dp.SmartStatus != nil { + dp.Info.name = raid.name + if raid.rType == satType { dp.Info.Name = fmt.Sprintf("%s %s", raid.name, raid.rType) + dp.Info.raidType = raid.rType } else { dp.Info.Name = fmt.Sprintf("%s %s,%d", raid.name, raid.rType, i) + dp.Info.raidType = fmt.Sprintf("%s,%d", raid.rType, i) } if r.setRaidDevices(dp, device, raid.rType, jsonRunner) { @@ -523,6 +609,8 @@ func (r *runner) getMegaRaidDevices(jsonRunner bool) { } dp.Info.Name = fmt.Sprintf("%s %s", raid.name, raid.rType) + dp.Info.name = raid.name + dp.Info.raidType = raid.rType r.setRaidDevices(dp, device, raid.rType, jsonRunner) } @@ -619,32 +707,41 @@ func (dp *deviceParser) checkErr() (err error) { func (p *Plugin) getDevices() (basic, raid, megaraid []deviceInfo, err error) { basicTmp, err := p.scanDevices("--scan -j") if err != nil { - return nil, nil, nil, fmt.Errorf("Failed to scan for devices: %s.", err) + return nil, nil, nil, fmt.Errorf("Failed to scan for devices: %w.", err) } raidTmp, err := p.scanDevices("--scan -d sat -j") if err != nil { - return nil, nil, nil, fmt.Errorf("Failed to scan for sat devices: %s.", err) + return nil, nil, nil, fmt.Errorf("Failed to scan for sat devices: %w.", err) } -raid: - for _, tmp := range basicTmp { - for _, r := range raidTmp { + basic, raid, megaraid = formatDeviceOutput(basicTmp, raidTmp) + + return +} + +// formatDeviceOutput removes raid devices from basic device list and separates megaraid devices from the rest of raid +// devices. +func formatDeviceOutput(basic, raid []deviceInfo) (basicDev, raidDev, megaraidDev []deviceInfo) { +loop: + for _, tmp := range basic { + for _, r := range raid { if tmp.Name == r.Name { - continue raid + continue loop } } - basic = append(basic, tmp) + basicDev = append(basicDev, tmp) } - for _, r := range raidTmp { + for _, r := range raid { if strings.Contains(r.DevType, "megaraid") { - megaraid = append(megaraid, r) + megaraidDev = append(megaraidDev, r) + continue } - raid = append(raid, r) + raidDev = append(raidDev, r) } return diff --git a/src/libs/zbxalgo/linked_list.c b/src/libs/zbxalgo/linked_list.c index 8964958edcb..6a281cdd15b 100644 --- a/src/libs/zbxalgo/linked_list.c +++ b/src/libs/zbxalgo/linked_list.c @@ -334,3 +334,34 @@ void zbx_list_iterator_update(zbx_list_iterator_t *iterator) if (NULL != iterator->current) iterator->next = iterator->current->next; } + + +/****************************************************************************** + * * + * Purpose: removes next iterator value from list * + * * + * Parameters: iterator - [IN] list iterator * + * * + * Return value: The data held by the removed item. * + * * + ******************************************************************************/ +void *zbx_list_iterator_remove_next(zbx_list_iterator_t *iterator) +{ + zbx_list_item_t *next; + void *data; + + if (NULL == iterator->current || NULL == iterator->next) + return NULL; + + next = iterator->next; + data = next->data; + + if (iterator->list->tail == next) + iterator->list->tail = iterator->current; + + iterator->current->next = next->next; + iterator->next = next->next; + iterator->list->mem_free_func(next); + + return data; +} diff --git a/src/libs/zbxnix/daemon.c b/src/libs/zbxnix/daemon.c index 07a60eed431..236ee74736e 100644 --- a/src/libs/zbxnix/daemon.c +++ b/src/libs/zbxnix/daemon.c @@ -28,6 +28,11 @@ #include "control.h" #include "pid.h" +#if defined(__linux__) +#define ZBX_PID_FILE_TIMEOUT 20 +#define ZBX_PID_FILE_SLEEP_TIME 100000000 +#endif + char *CONFIG_PID_FILE = NULL; static int parent_pid = -1; @@ -374,8 +379,29 @@ int zbx_daemon_start(int allow_root, const char *user, unsigned int flags) if (0 == (flags & ZBX_TASK_FLAG_FOREGROUND)) { - if (0 != zbx_fork()) + pid_t child_pid; + + if(0 != (child_pid = zbx_fork())) + { +#if defined(__linux__) + if (0 < child_pid) + { + int pid_file_timeout = ZBX_PID_FILE_TIMEOUT; + zbx_stat_t stat_buff; + struct timespec ts = {0, ZBX_PID_FILE_SLEEP_TIME}; + + /* wait for the forked child to create pid file */ + while (0 < pid_file_timeout && 0 != zbx_stat(CONFIG_PID_FILE, &stat_buff)) + { + pid_file_timeout--; + nanosleep(&ts, NULL); + } + } +#else + ZBX_UNUSED(child_pid); +#endif exit(EXIT_SUCCESS); + } setsid(); diff --git a/src/zabbix_server/ha/ha_manager.c b/src/zabbix_server/ha/ha_manager.c index bc4c64cc628..f162a2cdf39 100644 --- a/src/zabbix_server/ha/ha_manager.c +++ b/src/zabbix_server/ha/ha_manager.c @@ -715,6 +715,63 @@ finish: /****************************************************************************** * * + * Purpose: check for active and standby node availability and update * + * unavailable nodes accordingly * + * * + ******************************************************************************/ +static int ha_db_check_unavailable_nodes(zbx_ha_info_t *info, zbx_vector_ha_node_t *nodes, int db_time) +{ + int i, ret = SUCCEED; + + zbx_vector_str_t unavailable_nodes; + + zbx_vector_str_create(&unavailable_nodes); + + for (i = 0; i < nodes->values_num; i++) + { + if (SUCCEED == zbx_cuid_compare(nodes->values[i]->ha_nodeid, info->ha_nodeid)) + continue; + + if (ZBX_NODE_STATUS_STANDBY != nodes->values[i]->status && + ZBX_NODE_STATUS_ACTIVE != nodes->values[i]->status) + { + continue; + } + + + if (db_time >= nodes->values[i]->lastaccess + info->failover_delay) + { + zbx_vector_str_append(&unavailable_nodes, nodes->values[i]->ha_nodeid.str); + + zbx_audit_ha_create_entry(AUDIT_ACTION_UPDATE, nodes->values[i]->ha_nodeid.str, + nodes->values[i]->name); + zbx_audit_ha_update_field_int(nodes->values[i]->ha_nodeid.str, ZBX_AUDIT_HA_STATUS, + nodes->values[i]->status, ZBX_NODE_STATUS_UNAVAILABLE); + } + } + + if (0 != unavailable_nodes.values_num) + { + char *sql = NULL; + size_t sql_alloc = 0, sql_offset = 0; + + zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update ha_node set status=%d where", + ZBX_NODE_STATUS_UNAVAILABLE); + + DBadd_str_condition_alloc(&sql, &sql_alloc, &sql_offset, "ha_nodeid", + (const char **)unavailable_nodes.values, unavailable_nodes.values_num); + + ret = ha_db_execute(info, "%s", sql); + zbx_free(sql); + } + + zbx_vector_str_destroy(&unavailable_nodes); + + return ret; +} + +/****************************************************************************** + * * * Purpose: register server node * * * * Return value: SUCCEED - node was registered or database was offline * @@ -810,6 +867,9 @@ static void ha_db_register_node(zbx_ha_info_t *info) ha_db_execute(info, "delete from ha_node where name<>''"); } + if (ZBX_HA_IS_CLUSTER() && ZBX_NODE_STATUS_ERROR != info->ha_status && ZBX_NODE_STATUS_ACTIVE == ha_status) + ha_db_check_unavailable_nodes(info, &nodes, db_time); + ha_flush_audit(info); zbx_free(sql); @@ -841,49 +901,11 @@ finish: ******************************************************************************/ static int ha_check_standby_nodes(zbx_ha_info_t *info, zbx_vector_ha_node_t *nodes, int db_time) { - int i, ret = SUCCEED; - zbx_vector_str_t unavailable_nodes; + int ret; zbx_audit_init(info->auditlog); - zbx_vector_str_create(&unavailable_nodes); - - for (i = 0; i < nodes->values_num; i++) - { - if (nodes->values[i]->status != ZBX_NODE_STATUS_STANDBY) - continue; - - if (db_time >= nodes->values[i]->lastaccess + info->failover_delay) - { - zbx_vector_str_append(&unavailable_nodes, nodes->values[i]->ha_nodeid.str); - - zbx_audit_ha_create_entry(AUDIT_ACTION_UPDATE, nodes->values[i]->ha_nodeid.str, - nodes->values[i]->name); - zbx_audit_ha_update_field_int(nodes->values[i]->ha_nodeid.str, ZBX_AUDIT_HA_STATUS, - nodes->values[i]->status, ZBX_NODE_STATUS_UNAVAILABLE); - } - } - - if (0 != unavailable_nodes.values_num) - { - char *sql = NULL; - size_t sql_alloc = 0, sql_offset = 0; - - zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update ha_node set status=%d where", - ZBX_NODE_STATUS_UNAVAILABLE); - - DBadd_str_condition_alloc(&sql, &sql_alloc, &sql_offset, "ha_nodeid", - (const char **)unavailable_nodes.values, unavailable_nodes.values_num); - - if (SUCCEED != ha_db_execute(info, "%s", sql)) - ret = FAIL; - - zbx_free(sql); - } - - zbx_vector_str_destroy(&unavailable_nodes); - - if (SUCCEED == ret) + if (SUCCEED == (ret = ha_db_check_unavailable_nodes(info, nodes, db_time))) ha_flush_audit(info); else zbx_audit_clean(); diff --git a/src/zabbix_server/lld/lld_item.c b/src/zabbix_server/lld/lld_item.c index 6215b2489b8..491a027e10c 100644 --- a/src/zabbix_server/lld/lld_item.c +++ b/src/zabbix_server/lld/lld_item.c @@ -1836,6 +1836,16 @@ static void lld_items_validate(zbx_uint64_t hostid, zbx_vector_ptr_t *items, zbx dependent = (zbx_lld_item_t *)item->dependent_items.values[j]; dependent->flags &= ~ZBX_FLAG_LLD_ITEM_DISCOVERED; } + + continue; + } + + if (0 != item->master_itemid && (FAIL != zbx_vector_ptr_bsearch(item_prototypes, &item->master_itemid, + ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC))) + { + item->flags &= ~ZBX_FLAG_LLD_ITEM_DISCOVERED; + *error = zbx_strdcatf(*error, "Cannot %s dependent item: master item is not discovered.\n", + (0 != item->itemid ? "update" : "create")); } } diff --git a/src/zabbix_server/preprocessor/preproc_manager.c b/src/zabbix_server/preprocessor/preproc_manager.c index 99d59c9ca72..1620793ee3f 100644 --- a/src/zabbix_server/preprocessor/preproc_manager.c +++ b/src/zabbix_server/preprocessor/preproc_manager.c @@ -25,6 +25,7 @@ #include "zbxlld.h" #include "preprocessing.h" #include "preproc_history.h" +#include "preproc_manager.h" extern ZBX_THREAD_LOCAL unsigned char process_type; extern unsigned char program_type; @@ -53,13 +54,18 @@ typedef enum } zbx_preprocessing_kind_t; -typedef struct zbx_preprocessing_request_base +typedef struct zbx_preprocessing_request_base zbx_preprocessing_request_base_t; + +ZBX_PTR_VECTOR_DECL(preprocessing_request_base, zbx_preprocessing_request_base_t *) +ZBX_PTR_VECTOR_IMPL(preprocessing_request_base, zbx_preprocessing_request_base_t *) + +struct zbx_preprocessing_request_base { zbx_preprocessing_kind_t kind; zbx_preprocessing_states_t state; - struct zbx_preprocessing_request_base *pending; /* the request waiting on this request to complete */ -} -zbx_preprocessing_request_base_t; + zbx_preprocessing_request_base_t *pending; /* the request waiting on this request to complete */ + zbx_vector_preprocessing_request_base_t flush_queue; /* processed request waiting to be flushed */ +}; /* preprocessing request */ typedef struct preprocessing_request @@ -291,9 +297,11 @@ static zbx_uint32_t preprocessor_create_task(zbx_preprocessing_manager_t *manage static void preprocessor_set_request_state_done(zbx_preprocessing_manager_t *manager, zbx_preprocessing_request_base_t *base, const zbx_list_item_t *queue_item) { - zbx_item_link_t *index, index_local; - zbx_preprocessing_request_t *request; - zbx_preprocessing_dep_request_t *dep_request; + zbx_item_link_t *index, index_local; + zbx_list_iterator_t iterator, next_iterator; + zbx_preprocessing_request_t *request; + zbx_preprocessing_dep_request_t *dep_request; + zbx_preprocessing_request_base_t *prev; base->state = REQUEST_STATE_DONE; @@ -320,6 +328,31 @@ static void preprocessor_set_request_state_done(zbx_preprocessing_manager_t *man { zbx_hashset_remove_direct(&manager->linked_items, index); } + + if (NULL == manager->queue.head) + return; + + zbx_list_iterator_init(&manager->queue, &iterator); + if (iterator.next == queue_item) + return; + + while (SUCCEED == zbx_list_iterator_next(&iterator)) + { + if (iterator.next == queue_item) + break; + } + + prev = (zbx_preprocessing_request_base_t *)iterator.current->data; + zbx_vector_preprocessing_request_base_append(&prev->flush_queue, base); + + next_iterator = iterator; + if (SUCCEED == zbx_list_iterator_next(&next_iterator)) + { + if (SUCCEED == zbx_list_iterator_equal(&next_iterator, &manager->priority_tail)) + manager->priority_tail = iterator; + } + + (void)zbx_list_iterator_remove_next(&iterator); } /****************************************************************************** @@ -623,6 +656,9 @@ static void preprocessor_free_request(zbx_preprocessing_request_base_t *base) zbx_preprocessing_request_t *request; zbx_preprocessing_dep_request_t *dep_request; + zbx_vector_preprocessing_request_base_clear_ext(&base->flush_queue, preprocessor_free_request); + zbx_vector_preprocessing_request_base_destroy(&base->flush_queue); + switch (base->kind) { case ZBX_PREPROC_ITEM: @@ -708,6 +744,40 @@ static void preprocessor_flush_dep_results(zbx_preprocessing_manager_t *manager, /****************************************************************************** * * + * Purpose: recursively flush processed request and the other processed * + * requests that were waiting on this request to be finished * + * * + * Parameters: manager - [IN] preprocessing manager * + * base - [IN] the preprocessing request * + * * + ******************************************************************************/ +static void preprocessing_flush_request(zbx_preprocessing_manager_t *manager, + zbx_preprocessing_request_base_t *base) +{ + zbx_preprocessing_request_t *request; + zbx_preprocessing_dep_request_t *dep_request; + int i; + + switch (base->kind) + { + case ZBX_PREPROC_ITEM: + request = (zbx_preprocessing_request_t *)base; + preprocessor_flush_value(&request->value); + manager->processed_num++; + manager->queued_num--; + break; + case ZBX_PREPROC_DEPS: + dep_request = (zbx_preprocessing_dep_request_t *)base; + preprocessor_flush_dep_results(manager, dep_request); + break; + } + + for (i = 0; i < base->flush_queue.values_num; i++) + preprocessing_flush_request(manager, base->flush_queue.values[i]); +} + +/****************************************************************************** + * * * Purpose: add all sequential processed values from beginning of the queue * * to the local history cache * * * @@ -716,8 +786,6 @@ static void preprocessor_flush_dep_results(zbx_preprocessing_manager_t *manager, ******************************************************************************/ static void preprocessing_flush_queue(zbx_preprocessing_manager_t *manager) { - zbx_preprocessing_request_t *request; - zbx_preprocessing_dep_request_t *dep_request; zbx_preprocessing_request_base_t *base; zbx_list_iterator_t iterator; @@ -729,19 +797,7 @@ static void preprocessing_flush_queue(zbx_preprocessing_manager_t *manager) if (REQUEST_STATE_DONE != base->state) break; - switch (base->kind) - { - case ZBX_PREPROC_ITEM: - request = (zbx_preprocessing_request_t *)base; - preprocessor_flush_value(&request->value); - manager->processed_num++; - manager->queued_num--; - break; - case ZBX_PREPROC_DEPS: - dep_request = (zbx_preprocessing_dep_request_t *)base; - preprocessor_flush_dep_results(manager, dep_request); - break; - } + preprocessing_flush_request(manager, base); if (SUCCEED == zbx_list_iterator_equal(&iterator, &manager->priority_tail)) zbx_list_iterator_clear(&manager->priority_tail); @@ -883,8 +939,9 @@ static void preprocessor_enqueue(zbx_preprocessing_manager_t *manager, zbx_prepr state = REQUEST_STATE_QUEUED; request = (zbx_preprocessing_request_t *)zbx_malloc(NULL, sizeof(zbx_preprocessing_request_t)); - request->base.kind = ZBX_PREPROC_ITEM; memset(request, 0, sizeof(zbx_preprocessing_request_t)); + request->base.kind = ZBX_PREPROC_ITEM; + zbx_vector_preprocessing_request_base_create(&request->base.flush_queue); memcpy(&request->value, value, sizeof(zbx_preproc_item_value_t)); request->base.state = state; @@ -995,6 +1052,7 @@ static void preprocessor_enqueue_dependent(zbx_preprocessing_manager_t *manager, dep_request->base.kind = ZBX_PREPROC_DEPS; dep_request->base.state = REQUEST_STATE_QUEUED; dep_request->base.pending = NULL; + zbx_vector_preprocessing_request_base_create(&dep_request->base.flush_queue); dep_request->hostid = hostid; dep_request->ts = NULL != ts ? *ts : (zbx_timespec_t){0, 0}; |