| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package panos |
| 4 | |
| 5 | import ( |
| 6 | "context" |
| 7 | "fmt" |
| 8 | "strings" |
| 9 | ) |
| 10 | |
| 11 | type systemInfo struct { |
| 12 | Hostname string `xml:"hostname"` |
| 13 | DeviceName string `xml:"devicename"` |
| 14 | Model string `xml:"model"` |
| 15 | Serial string `xml:"serial"` |
| 16 | SWVersion string `xml:"sw-version"` |
| 17 | Uptime string `xml:"uptime"` |
| 18 | CertificateStatus string `xml:"device-certificate-status"` |
| 19 | OperationalMode string `xml:"operational-mode"` |
| 20 | } |
| 21 | |
| 22 | type systemInfoResult struct { |
| 23 | System systemInfo `xml:"system"` |
| 24 | } |
| 25 | |
| 26 | func (c *Collector) collectSystemMetrics(ctx context.Context) (bool, error) { |
| 27 | info, err := c.querySystemInfo(ctx) |
| 28 | if err != nil { |
| 29 | return false, fmt.Errorf("system metricset: %w", err) |
| 30 | } |
| 31 | |
| 32 | uptime, err := parseRequiredPANOSDurationField("system uptime", info.Uptime) |
| 33 | if err != nil { |
| 34 | return false, fmt.Errorf("system metricset: %s response: %w", panosCommandName(systemInfoCommand), err) |
| 35 | } |
| 36 | |
| 37 | labels := systemLabelValues(info) |
| 38 | c.metrics.system.uptime.WithLabelValues(labels...).Observe(float64(uptime)) |
| 39 | |
| 40 | certStatus := strings.TrimSpace(info.CertificateStatus) |
| 41 | if certStatus != "" { |
| 42 | certValid := strings.EqualFold(certStatus, "valid") |
| 43 | observeStateSetVec(c.metrics.system.certStatus, boolState(certValid, "valid", "invalid"), labels...) |
| 44 | } |
| 45 | |
| 46 | operationalMode := strings.TrimSpace(info.OperationalMode) |
| 47 | if operationalMode != "" { |
| 48 | normalMode := strings.EqualFold(operationalMode, "normal") |
| 49 | observeStateSetVec(c.metrics.system.operationalMode, boolState(normalMode, "normal", "other"), labels...) |
| 50 | } |
| 51 | return true, nil |
| 52 | } |
| 53 | |
| 54 | func (c *Collector) querySystemInfo(ctx context.Context) (systemInfo, error) { |
| 55 | body, err := c.apiClient.op(ctx, systemInfoCommand) |
| 56 | if err != nil { |
| 57 | return systemInfo{}, fmt.Errorf("%s API call: %w", panosCommandName(systemInfoCommand), err) |
| 58 | } |
| 59 | |
| 60 | info, err := parseSystemInfo(body) |
| 61 | if err != nil { |
| 62 | return systemInfo{}, fmt.Errorf("%s response: %w", panosCommandName(systemInfoCommand), err) |
| 63 | } |
| 64 | if !info.hasData() { |
| 65 | return systemInfo{}, fmt.Errorf("%s response: %w", panosCommandName(systemInfoCommand), missingPANOSResultError{expected: "<system>"}) |
| 66 | } |
| 67 | return info, nil |
| 68 | } |
| 69 | |
| 70 | func parseSystemInfo(body []byte) (systemInfo, error) { |
| 71 | var result systemInfoResult |
| 72 | if err := decodePANOSResult(body, "PAN-OS system info response", &result); err != nil { |
| 73 | return systemInfo{}, err |
| 74 | } |
| 75 | return result.System, nil |
| 76 | } |
| 77 | |
| 78 | func (i systemInfo) hasData() bool { |
| 79 | return firstNonEmpty(i.Hostname, i.DeviceName, i.Model, i.Serial, i.SWVersion, i.Uptime, i.CertificateStatus, i.OperationalMode) != "" |
| 80 | } |