diff --git a/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go b/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go index 65dd2aea..f7f07437 100644 --- a/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go +++ b/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go @@ -88,6 +88,10 @@ type Certificate struct { KeyPath string `json:"keyPath" yaml:"keyPath"` } +type HostServices struct { + HostCollectorMeta `json:",inline" yaml:",inline"` +} + type HostCollect struct { CPU *CPU `json:"cpu,omitempty" yaml:"cpu,omitempty"` Memory *Memory `json:"memory,omitempty" yaml:"memory,omitempty"` @@ -103,6 +107,7 @@ type HostCollect struct { TCPConnect *TCPConnect `json:"tcpConnect,omitempty" yaml:"tcpConnect,omitempty"` FilesystemPerformance *FilesystemPerformance `json:"filesystemPerformance,omitempty" yaml:"filesystemPerformance,omitempty"` Certificate *Certificate `json:"certificate,omitempty" yaml:"certificate,omitempty"` + HostServices *HostServices `json:"hostServices,omitempty" yaml:"hostServices,omitempty"` } func (c *HostCollect) GetName() string { diff --git a/pkg/collect/host_collector.go b/pkg/collect/host_collector.go index 9a44f2d4..a864cc9b 100644 --- a/pkg/collect/host_collector.go +++ b/pkg/collect/host_collector.go @@ -38,6 +38,8 @@ func GetHostCollector(collector *troubleshootv1beta2.HostCollect) (HostCollector return &CollectHostFilesystemPerformance{collector.FilesystemPerformance}, true case collector.Certificate != nil: return &CollectHostCertificate{collector.Certificate}, true + case collector.HostServices != nil: + return &CollectHostServices{collector.HostServices}, true default: return nil, false } diff --git a/pkg/collect/host_services.go b/pkg/collect/host_services.go new file mode 100644 index 00000000..484bf061 --- /dev/null +++ b/pkg/collect/host_services.go @@ -0,0 +1,68 @@ +package collect + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "os/exec" + + "github.com/pkg/errors" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" +) + +type ServiceInfo struct { + Unit string `json:"Unit"` + Load string `json:"Load"` + Active string `json:"Active"` + Sub string `json:"Sub"` +} + +const systemctlFormat = `%s %s %s %s` // this leaves off the description + +type CollectHostServices struct { + hostCollector *troubleshootv1beta2.HostServices +} + +func (c *CollectHostServices) Title() string { + return hostCollectorTitleOrDefault(c.hostCollector.HostCollectorMeta, "Block Devices") +} + +func (c *CollectHostServices) IsExcluded() (bool, error) { + return isExcluded(c.hostCollector.Exclude) +} + +func (c *CollectHostServices) Collect(progressChan chan<- interface{}) (map[string][]byte, error) { + var devices []ServiceInfo + + cmd := exec.Command("systemctl", "list-units", "--type=service", "--no-legend", "--all") + stdout, err := cmd.Output() + if err != nil { + return nil, errors.Wrapf(err, "failed to execute systemctl") + } + buf := bytes.NewBuffer(stdout) + scanner := bufio.NewScanner(buf) + + for scanner.Scan() { + bdi := ServiceInfo{} + fmt.Sscanf( + scanner.Text(), + systemctlFormat, + &bdi.Unit, + &bdi.Load, + &bdi.Active, + &bdi.Sub, + ) + + devices = append(devices, bdi) + } + + b, err := json.Marshal(devices) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal systemctl service info") + } + + return map[string][]byte{ + "system/systemctl_services.json": b, + }, nil +}