mirror of
				https://github.com/ClusterCockpit/cc-metric-collector.git
				synced 2025-11-04 10:45:06 +01:00 
			
		
		
		
	Fix for LustreCollector. Check for root user
This commit is contained in:
		@@ -5,10 +5,12 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"os/user"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	cclog "github.com/ClusterCockpit/cc-metric-collector/internal/ccLogger"
 | 
			
		||||
	lp "github.com/ClusterCockpit/cc-metric-collector/internal/ccMetric"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -19,17 +21,26 @@ const LCTL_OPTION = `get_param`
 | 
			
		||||
type LustreCollectorConfig struct {
 | 
			
		||||
	LCtlCommand    string   `json:"lctl_command"`
 | 
			
		||||
	ExcludeMetrics []string `json:"exclude_metrics"`
 | 
			
		||||
	SendAllMetrics bool     `json:"send_all_metrics"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LustreCollector struct {
 | 
			
		||||
	metricCollector
 | 
			
		||||
	tags    map[string]string
 | 
			
		||||
	matches map[string]map[string]int
 | 
			
		||||
	devices []string
 | 
			
		||||
	stats   map[string]map[string]int64
 | 
			
		||||
	config  LustreCollectorConfig
 | 
			
		||||
	lctl    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *LustreCollector) getDeviceDataCommand(device string) []string {
 | 
			
		||||
	statsfile := fmt.Sprintf("llite.%s.stats", device)
 | 
			
		||||
	command := exec.Command(m.lctl, LCTL_OPTION, statsfile)
 | 
			
		||||
	command.Wait()
 | 
			
		||||
	stdout, _ := command.Output()
 | 
			
		||||
	return strings.Split(string(stdout), "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *LustreCollector) getDevices() []string {
 | 
			
		||||
	devices := make([]string, 0)
 | 
			
		||||
 | 
			
		||||
@@ -44,13 +55,9 @@ func (m *LustreCollector) getDevices() []string {
 | 
			
		||||
	// 	devices = append(devices, pathlist[4])
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
	command := exec.Command(m.lctl, LCTL_OPTION, "llite.*.stats")
 | 
			
		||||
	command.Wait()
 | 
			
		||||
	stdout, err := command.Output()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return devices
 | 
			
		||||
	}
 | 
			
		||||
	for _, line := range strings.Split(string(stdout), "\n") {
 | 
			
		||||
	data := m.getDeviceDataCommand("*")
 | 
			
		||||
 | 
			
		||||
	for _, line := range data {
 | 
			
		||||
		if strings.HasPrefix(line, "llite") {
 | 
			
		||||
			linefields := strings.Split(line, ".")
 | 
			
		||||
			if len(linefields) > 2 {
 | 
			
		||||
@@ -73,14 +80,6 @@ func (m *LustreCollector) getDevices() []string {
 | 
			
		||||
// 	return strings.Split(string(buffer), "\n")
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
func (m *LustreCollector) getDeviceDataCommand(device string) []string {
 | 
			
		||||
	statsfile := fmt.Sprintf("llite.%s.stats", device)
 | 
			
		||||
	command := exec.Command(m.lctl, LCTL_OPTION, statsfile)
 | 
			
		||||
	command.Wait()
 | 
			
		||||
	stdout, _ := command.Output()
 | 
			
		||||
	return strings.Split(string(stdout), "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *LustreCollector) Init(config json.RawMessage) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	m.name = "LustreCollector"
 | 
			
		||||
@@ -93,14 +92,42 @@ func (m *LustreCollector) Init(config json.RawMessage) error {
 | 
			
		||||
	m.setup()
 | 
			
		||||
	m.tags = map[string]string{"type": "node"}
 | 
			
		||||
	m.meta = map[string]string{"source": m.name, "group": "Lustre"}
 | 
			
		||||
	m.matches = map[string]map[string]int{"read_bytes": {"read_bytes": 6, "read_requests": 1},
 | 
			
		||||
		"write_bytes":      {"write_bytes": 6, "write_requests": 1},
 | 
			
		||||
		"open":             {"open": 1},
 | 
			
		||||
		"close":            {"close": 1},
 | 
			
		||||
		"setattr":          {"setattr": 1},
 | 
			
		||||
		"getattr":          {"getattr": 1},
 | 
			
		||||
		"statfs":           {"statfs": 1},
 | 
			
		||||
		"inode_permission": {"inode_permission": 1}}
 | 
			
		||||
	defmatches := map[string]map[string]int{
 | 
			
		||||
		"read_bytes":       {"lustre_read_bytes": 6, "lustre_read_requests": 1},
 | 
			
		||||
		"write_bytes":      {"lustre_write_bytes": 6, "lustre_write_requests": 1},
 | 
			
		||||
		"open":             {"lustre_open": 1},
 | 
			
		||||
		"close":            {"lustre_close": 1},
 | 
			
		||||
		"setattr":          {"lustre_setattr": 1},
 | 
			
		||||
		"getattr":          {"lustre_getattr": 1},
 | 
			
		||||
		"statfs":           {"lustre_statfs": 1},
 | 
			
		||||
		"inode_permission": {"lustre_inode_permission": 1}}
 | 
			
		||||
 | 
			
		||||
	// Lustre file system statistics can only be queried by user root
 | 
			
		||||
	user, err := user.Current()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		cclog.ComponentError(m.name, "Failed to get current user:", err.Error())
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if user.Uid != "0" {
 | 
			
		||||
		cclog.ComponentError(m.name, "Lustre file system statistics can only be queried by user root:", err.Error())
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m.matches = make(map[string]map[string]int)
 | 
			
		||||
	for lineprefix, names := range defmatches {
 | 
			
		||||
		for metricname, offset := range names {
 | 
			
		||||
			_, skip := stringArrayContains(m.config.ExcludeMetrics, metricname)
 | 
			
		||||
			if skip {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if _, prefixExist := m.matches[lineprefix]; !prefixExist {
 | 
			
		||||
				m.matches[lineprefix] = make(map[string]int)
 | 
			
		||||
			}
 | 
			
		||||
			if _, metricExist := m.matches[lineprefix][metricname]; !metricExist {
 | 
			
		||||
				m.matches[lineprefix][metricname] = offset
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	p, err := exec.LookPath(m.config.LCtlCommand)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		p, err = exec.LookPath(LCTL_CMD)
 | 
			
		||||
@@ -110,10 +137,19 @@ func (m *LustreCollector) Init(config json.RawMessage) error {
 | 
			
		||||
	}
 | 
			
		||||
	m.lctl = p
 | 
			
		||||
 | 
			
		||||
	m.devices = m.getDevices()
 | 
			
		||||
	if len(m.devices) == 0 {
 | 
			
		||||
	devices := m.getDevices()
 | 
			
		||||
	if len(devices) == 0 {
 | 
			
		||||
		return errors.New("no metrics to collect")
 | 
			
		||||
	}
 | 
			
		||||
	m.stats = make(map[string]map[string]int64)
 | 
			
		||||
	for _, d := range devices {
 | 
			
		||||
		m.stats[d] = make(map[string]int64)
 | 
			
		||||
		for _, names := range m.matches {
 | 
			
		||||
			for metricname := range names {
 | 
			
		||||
				m.stats[d][metricname] = 0
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	m.init = true
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -122,23 +158,45 @@ func (m *LustreCollector) Read(interval time.Duration, output chan lp.CCMetric)
 | 
			
		||||
	if !m.init {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, device := range m.devices {
 | 
			
		||||
	for device, devData := range m.stats {
 | 
			
		||||
		stats := m.getDeviceDataCommand(device)
 | 
			
		||||
		processed := []string{}
 | 
			
		||||
 | 
			
		||||
		for _, line := range stats {
 | 
			
		||||
			lf := strings.Fields(line)
 | 
			
		||||
			if len(lf) > 1 {
 | 
			
		||||
				for match, fields := range m.matches {
 | 
			
		||||
					if lf[0] == match {
 | 
			
		||||
				if fields, ok := m.matches[lf[0]]; ok {
 | 
			
		||||
					for name, idx := range fields {
 | 
			
		||||
							_, skip := stringArrayContains(m.config.ExcludeMetrics, name)
 | 
			
		||||
							if skip {
 | 
			
		||||
						x, err := strconv.ParseInt(lf[idx], 0, 64)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
							x, err := strconv.ParseInt(lf[idx], 0, 64)
 | 
			
		||||
						value := x - devData[name]
 | 
			
		||||
						devData[name] = x
 | 
			
		||||
						if value < 0 {
 | 
			
		||||
							value = 0
 | 
			
		||||
						}
 | 
			
		||||
						y, err := lp.New(name, m.tags, m.meta, map[string]interface{}{"value": value}, time.Now())
 | 
			
		||||
						if err == nil {
 | 
			
		||||
								y, err := lp.New(name, m.tags, m.meta, map[string]interface{}{"value": x}, time.Now())
 | 
			
		||||
							y.AddTag("device", device)
 | 
			
		||||
							if strings.Contains(name, "byte") {
 | 
			
		||||
								y.AddMeta("unit", "Byte")
 | 
			
		||||
							}
 | 
			
		||||
							output <- y
 | 
			
		||||
							if m.config.SendAllMetrics {
 | 
			
		||||
								processed = append(processed, name)
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if m.config.SendAllMetrics {
 | 
			
		||||
			for name := range devData {
 | 
			
		||||
				if _, done := stringArrayContains(processed, name); !done {
 | 
			
		||||
					y, err := lp.New(name, m.tags, m.meta, map[string]interface{}{"value": 0}, time.Now())
 | 
			
		||||
					if err == nil {
 | 
			
		||||
						y.AddTag("device", device)
 | 
			
		||||
						if strings.Contains(name, "byte") {
 | 
			
		||||
							y.AddMeta("unit", "Byte")
 | 
			
		||||
						}
 | 
			
		||||
@@ -149,9 +207,6 @@ func (m *LustreCollector) Read(interval time.Duration, output chan lp.CCMetric)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *LustreCollector) Close() {
 | 
			
		||||
	m.init = false
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user