Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop bmc-log-collector #1432

Merged
merged 1 commit into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
- dir: "./bmc-reverse-proxy"
container-image: "bmc-reverse-proxy"
make-targets: "setup check-generate test"
- dir: "./bmc-log-collector"
container-image: "bmc-log-collector"
make-targets: "setup check-generate test"
- dir: "./bpf-map-pressure-exporter"
container-image: "bpf-map-pressure-exporter"
make-targets: "check-generate test"
Expand Down
1 change: 1 addition & 0 deletions bmc-log-collector/BRANCH
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.9
18 changes: 18 additions & 0 deletions bmc-log-collector/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Stage1: build from source
FROM ghcr.io/cybozu/golang:1.22-jammy AS build

COPY . /work
WORKDIR /work

RUN CGO_ENABLED=0 go install -ldflags="-w -s"

# Stage2: setup runtime container
FROM scratch

LABEL org.opencontainers.image.source="https://github.com/cybozu/neco-containers"

COPY --from=build /go/bin /

USER 10000:10000

ENTRYPOINT ["/log-collector"]
28 changes: 28 additions & 0 deletions bmc-log-collector/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.PHONY: all
all: check-generate test

.PHONY: setup
setup:
go install github.com/cybozu-go/golang-custom-analyzer/cmd/custom-checker@latest
go install honnef.co/go/tools/cmd/staticcheck@latest
go install github.com/onsi/ginkgo/v2/ginkgo

.PHONY: check-generate
check-generate:
go mod tidy
git diff --exit-code --name-only

.PHONY: test
test: clean
test -z "$$(gofmt -s -l . | tee /dev/stderr)"
staticcheck ./...
test -z "$$(custom-checker -restrictpkg.packages=html/template ./... 2>&1 | tee /dev/stderr)"
go vet ./...
ginkgo -v --race -p .

.PHONY: clean
clean:
rm -f testdata/output_main_test/*
rm -fr testdata/pointers_get_machines
rm -fr testdata/pointers_log_collector
rm -fr testdata/pointers_main_test
53 changes: 53 additions & 0 deletions bmc-log-collector/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
bmc-log-collector
============================

`bmc-log-collector` collects hardware logs from Baseboard Management Controller (BMC) and output own stdout.

The following products are assumed as BMC.
- DELL integrated Dell Remote Access Controller (iDRAC)

This program reads the "machineslist.json" and retrieves the System Event Log from each BMC. "bmc-log-collector" adds the serial and the node IP to own STD output.

## Referenced file

#### User and password of BMC

```
{
"USERID-TO-BE-REPLACE": {
"password": {
"raw": "PASSWORD-STRING-TO-BE-REPLACE"
}
},
// Repeat
}
```

#### Target "machineslist.json" of log scraping

```
[
{
serial: "ABC1234", // Uniq serial ID of the server hardware
bmc_ipv4: "192.168.1.1" // BMC IP address
node_ipv4: "192.168.10.1" // Server IP address
},
// Repeat
]
```


## Usage

bmc-log-collector command provides the usage in following.

```
$ bmc-log-collector --help

Usage of ./bmc-log-collector:
--bmc-user-json string User and password of BMC (default "/users/neco/bmc-user.json")
--machine-list-json string Target machines list of log scraping (default "/config/machineslist.json")
--pointer-dir-path string Data directory of pointer management (default "/data/pointers")
--scraping-interval-timer int Timer(sec) of scraping interval time (default 300)
--user-id string User ID of bmc-user-json JSON file (default "support")
```
1 change: 1 addition & 0 deletions bmc-log-collector/TAG
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.9.0
42 changes: 42 additions & 0 deletions bmc-log-collector/bmc-user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"encoding/json"
"os"
)

// BMCPassword represents password for a BMC user.
type BMCPassword struct {
Raw string `json:"raw"`
Hash string `json:"hash"`
Salt string `json:"salt"`
}

// Credentials represents credentials of a BMC user.
type Credentials struct {
Password BMCPassword `json:"password"`
}

// UserConfig represents a set of BMC user credentials in JSON format.
type UserConfig struct {
Root Credentials `json:"root"`
Repair Credentials `json:"repair"`
Power Credentials `json:"power"`
Support Credentials `json:"support"`
}

// LoadConfig loads UserConfig.
func LoadBMCUserConfig(userFile string) (*UserConfig, error) {
fd, err := os.Open(userFile)
if err != nil {
return nil, err
}
defer fd.Close()

bmcUsers := new(UserConfig)
err = json.NewDecoder(fd).Decode(bmcUsers)
if err != nil {
return nil, err
}
return bmcUsers, nil
}
28 changes: 28 additions & 0 deletions bmc-log-collector/bmc-user_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Get User from bmc-user.json", Ordered, func() {
Context("Normal", func() {
It("Read JSON file", func() {
user, err := LoadBMCUserConfig("testdata/etc/bmc-user.json")
Expect(err).NotTo(HaveOccurred())
Expect(user.Support.Password.Raw).To(Equal(basicAuthPassword))
})
})

Context("Abnormal", func() {
It("Read no existing file", func() {
_, err := LoadBMCUserConfig("testdata/etc/no-exist.json")
Expect(err).To(HaveOccurred())
})

It("no support user in json file", func() {
_, err := LoadBMCUserConfig("testdata/etc/bmc-user-err.json")
Expect(err).To(HaveOccurred())
})
})
})
112 changes: 112 additions & 0 deletions bmc-log-collector/docs/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# BMC Log Collector Design

“BMC Log Collector” collects Hareware Error from Baseboard Management Controller (BMC) and output to own stdout.
In case of DELL hardware, “BMC Log Collector” collects System Event Log (SEL) from iDRAC.
The first case of collecting is DELL.

“BMC Log Collector” has the following features
1. Retrieve the IP address and ID of the BMC to be collected from a JSON file
2. Access the IP address of the BMC and retrieve a hardware error logs from the Redfish REST service
3. Output the collected logs that eliminated duplication to STDOUT

Redfish is a standard for server management and provides information as REST API service.
We can get the event of hardware in Server.

## Architecture of BMC Log Collector

The following figure shows an architectural diagram of BMC Log Collector.

```mermaid
flowchart TB
PS[(Pointer File Store)] <---> LC
CF[JSON File]-->LC[Log Collector]
BMC[BMC]-->LC
LC-->LS[Stdout]
```

1. "JSON File" has BMC IP list, server identity string, server's IP address.
2. "Pointer File Store" has latest pointer information for each BMC.
5. "BMC" is Baseboard Management Controller that has IP address and serves Redfish API.

## How “BMC Log Collector” works

1. Get a list of IP addresses of the BMC in a JSON file
2. Access the Redfish path of the BMC's IP address to get the hardware event log
3. Use `/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries` as the path to RedFish.
4. Convert the received JSON data into a Go language structure and inspect for duplicates.
5. Compare the ID of the log received last time with the ID of the log received this time. If the ID of the log received this time is larger, it is considered the latest event.
6. If the ID is smaller than the previous one, the timestamp is compared, and if it is larger than the previous timestamp, it is considered as the latest log.
7. Write ID, type stamp, and identification string in the file. The file name is the identification string, and a separate file is created for each BMC.
8. The latest events in the event log are output in JSON format to standard output.
9. Perform tasks 1 through 8 above, at intervals of a few minutes.
10. Continue this cycle while the “BMC Log Collector” is running.


## Architectural Decisions

### ADR1. Obtaining the BMC machine list

There are two ways to obtain the latest BMC listings.

- Method 1: Get the BMC list from the server's database.
- Method 2: Create a JSON file by adding to the existing functions.

#### Advantages of using Method 1
- Reduced risk of failure due to fewer dependent components

#### Disadvantages of using Method 1
- Complex processes to retrieve data from databases, serfs, etc. must be written.

#### Advantages of adopting Method 2
- Programming and testing can be reduced, and the construction period can be shortened.

#### Disadvantages of adopting Method 2
- If an existing function fails, the main function stops.

### Decision and Reason
Adopt method 2.
As a countermeasure against failure, minimize the impact by using the last updated JSON file when the existing function stops.


### ADR2. Control of processing load

There are two possible methods to obtain event logs from the BMC.

- Method 1: The list is stored in the processing queue and worker tasks retrieve it sequentially.

- Method 2: Go routines are started in a number of BMCs, and they are all retrieved at the same time.


#### Advantages of Method 1
- Workload can be controlled by the number of worker tasks

#### Disadvantages of Method 1
- Management of queues and worker processes becomes complicated

#### Advantages of Method 2
- Simplifies Go programming

#### Disadvantages of Method 2
- Load surges occur at the beginning of each collection cycle when there are many target BMCs,Unable to control the load.

### Decision and Reason
Method 2
With the current number of BMCs, load is not a problem.



## Risks

1. There is a concern that log output may be mixed under multi-threaded execution. There is a possibility that this is not thread-safe.
- Countermeasures
- Put exclusion control before and after the output to make it thread-safe.

2. Concerns that workload surges will adversely affect others
- Countermeasures
- If a problem is discovered, initially limit the number of Go routines by semaphore. If the problem cannot be resolved, consider a queue.



## Usage

please refer [README.md](../README.md)
32 changes: 32 additions & 0 deletions bmc-log-collector/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module log-collector

go 1.22.5

require (
github.com/onsi/ginkgo/v2 v2.19.0
github.com/onsi/gomega v1.33.1
github.com/prometheus/client_golang v1.20.0
github.com/prometheus/common v0.55.0
github.com/spf13/pflag v1.0.5
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.23.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
60 changes: 60 additions & 0 deletions bmc-log-collector/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI=
github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading
Loading