mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #3407 from swordqiu/feature/qj-baremetal-cronjobs
feature: baremetal cronjobs: status probe, fetch bmc logs and report power/themal metrics
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type BaremetalEventListOptions struct {
|
||||
HostId string `help:"filter by hostId"`
|
||||
HostName string `help:"filter by hostname"`
|
||||
IpmiIp string `help:"filter by ipmi_ip"`
|
||||
Type string `help:"filter by type" choices:"system|manager"`
|
||||
PagingMarker string `help:"marker for pagination"`
|
||||
Limit int `help:"page limit, default 20" default:"20"`
|
||||
Severity string `help:"filter by severity"`
|
||||
}
|
||||
R(&BaremetalEventListOptions{}, "baremetal-event-list", "List baremetal events", func(s *mcclient.ClientSession, args *BaremetalEventListOptions) error {
|
||||
params := jsonutils.Marshal(args)
|
||||
result, err := modules.BaremetalEvents.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result, modules.BaremetalEvents.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
|
||||
type BaremetalEventCreateOptions struct {
|
||||
HOST string `json:"-" help:"Host Id"`
|
||||
Type string `json:"type" help:"event type" choices:"system|manager"`
|
||||
EventId string `json:"event_id" help:"original event Id"`
|
||||
MESSAGE string `json:"message" help:"content of event"`
|
||||
Severity string `json:"severity" help:"Severity of event"`
|
||||
CREATED string `json:"created" help:"when event was created"`
|
||||
}
|
||||
R(&BaremetalEventCreateOptions{}, "baremetal-event-create", "Create baremetal event", func(s *mcclient.ClientSession, args *BaremetalEventCreateOptions) error {
|
||||
host, err := modules.Hosts.Get(s, args.HOST, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostId, _ := host.Get("id")
|
||||
hostName, _ := host.Get("name")
|
||||
ipmiIp, _ := host.Get("ipmi_ip")
|
||||
params := jsonutils.Marshal(args).(*jsonutils.JSONDict)
|
||||
params.Add(hostId, "host_id")
|
||||
params.Add(hostName, "host_name")
|
||||
params.Add(ipmiIp, "ipmi_ip")
|
||||
result, err := modules.BaremetalEvents.Create(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -17,8 +17,10 @@ package shell
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/util/timeutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/printutils"
|
||||
@@ -79,9 +81,18 @@ func init() {
|
||||
})
|
||||
|
||||
type ReadLogsOptions struct {
|
||||
Since string `help:"read logs after, format: YYYY-MM-DD"`
|
||||
}
|
||||
shellutils.R(&ReadLogsOptions{}, "system-logs", "Read system logs", func(cli redfish.IRedfishDriver, args *ReadLogsOptions) error {
|
||||
events, err := cli.ReadSystemLogs(context.Background())
|
||||
var sinceTm time.Time
|
||||
var err error
|
||||
if len(args.Since) > 0 {
|
||||
sinceTm, err = timeutils.ParseTimeStr(args.Since)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
events, err := cli.ReadSystemLogs(context.Background(), sinceTm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -89,7 +100,15 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
shellutils.R(&ReadLogsOptions{}, "manager-logs", "Read manager logs", func(cli redfish.IRedfishDriver, args *ReadLogsOptions) error {
|
||||
events, err := cli.ReadManagerLogs(context.Background())
|
||||
var sinceTm time.Time
|
||||
var err error
|
||||
if len(args.Since) > 0 {
|
||||
sinceTm, err = timeutils.ParseTimeStr(args.Since)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
events, err := cli.ReadManagerLogs(context.Background(), sinceTm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package baremetal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/timeutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
o "yunion.io/x/onecloud/pkg/baremetal/options"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/util/influxdb"
|
||||
"yunion.io/x/onecloud/pkg/util/redfish"
|
||||
)
|
||||
|
||||
type IBaremetalCronJob interface {
|
||||
Name() string
|
||||
NeedsToRun(now time.Time) bool
|
||||
StartRun()
|
||||
Do(ctx context.Context, now time.Time) error
|
||||
StopRun()
|
||||
}
|
||||
|
||||
func DoCronJobs(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
|
||||
if isStart {
|
||||
return
|
||||
}
|
||||
for _, bm := range GetBaremetalManager().GetBaremetals() {
|
||||
if bm.GetTask() == nil {
|
||||
bm.doCronJobs(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SBaseBaremetalCronJob struct {
|
||||
lastTime time.Time
|
||||
running bool
|
||||
interval time.Duration
|
||||
baremetal *SBaremetalInstance
|
||||
}
|
||||
|
||||
func (job *SBaseBaremetalCronJob) NeedsToRun(now time.Time) bool {
|
||||
if (job.lastTime.IsZero() || now.Sub(job.lastTime) > job.interval) && !job.running {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (job *SBaseBaremetalCronJob) StartRun() {
|
||||
job.running = true
|
||||
}
|
||||
|
||||
func (job *SBaseBaremetalCronJob) StopRun() {
|
||||
job.running = false
|
||||
}
|
||||
|
||||
type SStatusProbeJob struct {
|
||||
SBaseBaremetalCronJob
|
||||
}
|
||||
|
||||
func NewStatusProbeJob(baremetal *SBaremetalInstance, interval time.Duration) IBaremetalCronJob {
|
||||
return &SStatusProbeJob{
|
||||
SBaseBaremetalCronJob: SBaseBaremetalCronJob{
|
||||
baremetal: baremetal,
|
||||
interval: interval,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (job *SStatusProbeJob) Name() string {
|
||||
return "StatusProbeJob"
|
||||
}
|
||||
|
||||
func (job *SStatusProbeJob) Do(ctx context.Context, now time.Time) error {
|
||||
bStatus := job.baremetal.GetStatus()
|
||||
if bStatus == api.BAREMETAL_READY || bStatus == api.BAREMETAL_RUNNING {
|
||||
ps, err := job.baremetal.GetPowerStatus()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetPowerStatus")
|
||||
}
|
||||
job.lastTime = now
|
||||
pps := PowerStatusToBaremetalStatus(ps)
|
||||
if pps != bStatus {
|
||||
log.Debugf("Detected baremetal status change!")
|
||||
job.baremetal.SyncAllStatus(ps)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SLogFetchJob struct {
|
||||
SBaseBaremetalCronJob
|
||||
}
|
||||
|
||||
func NewLogFetchJob(baremetal *SBaremetalInstance, interval time.Duration) IBaremetalCronJob {
|
||||
return &SLogFetchJob{
|
||||
SBaseBaremetalCronJob: SBaseBaremetalCronJob{
|
||||
baremetal: baremetal,
|
||||
interval: interval,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (job *SLogFetchJob) Name() string {
|
||||
return "LogFetchJob"
|
||||
}
|
||||
|
||||
func eventToJson(e redfish.SEvent) *jsonutils.JSONDict {
|
||||
json := jsonutils.NewDict()
|
||||
json.Add(jsonutils.NewString(timeutils.IsoTime(e.Created)), "created")
|
||||
json.Add(jsonutils.NewString(e.EventId), "event_id")
|
||||
json.Add(jsonutils.NewString(e.Message), "message")
|
||||
json.Add(jsonutils.NewString(e.Severity), "severity")
|
||||
json.Add(jsonutils.NewString(e.Type), "type")
|
||||
return json
|
||||
}
|
||||
|
||||
func fetchLogs(baremetal *SBaremetalInstance, ctx context.Context, logType string) error {
|
||||
params := jsonutils.NewDict()
|
||||
s := auth.GetAdminSession(ctx, consts.GetRegion(), "")
|
||||
params.Add(jsonutils.NewInt(1), "limit")
|
||||
params.Add(jsonutils.NewString(baremetal.GetId()), "host_id")
|
||||
params.Add(jsonutils.NewString(logType), "type")
|
||||
result, err := modules.BaremetalEvents.List(s, params)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "modules.BaremetalEvents.List")
|
||||
}
|
||||
log.Debugf("%s", result.Data)
|
||||
var eventId string
|
||||
var since time.Time
|
||||
if len(result.Data) > 0 {
|
||||
since, _ = result.Data[0].GetTime("created")
|
||||
eventId, _ = result.Data[0].GetString("event_id")
|
||||
}
|
||||
offset := 0
|
||||
logs, err := baremetal.fetchLogs(ctx, logType, since)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "job.baremetal.FetchLogs")
|
||||
}
|
||||
if len(eventId) > 0 {
|
||||
// latset eventId
|
||||
for i := range logs {
|
||||
if logs[i].EventId == eventId {
|
||||
offset = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := offset; i < len(logs); i += 1 {
|
||||
eventData := eventToJson(logs[i])
|
||||
eventData.Add(jsonutils.NewString(baremetal.GetId()), "host_id")
|
||||
eventData.Add(jsonutils.NewString(baremetal.GetName()), "host_name")
|
||||
eventData.Add(jsonutils.NewString(baremetal.GetIPMINicIPAddr()), "ipmi_ip")
|
||||
log.Debugf("%s", eventData)
|
||||
_, err := modules.BaremetalEvents.Create(s, eventData)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "modules.BaremetalEvents.Create")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (job *SLogFetchJob) Do(ctx context.Context, now time.Time) error {
|
||||
if !job.baremetal.isRedfishCapable() {
|
||||
return nil
|
||||
}
|
||||
err := fetchLogs(job.baremetal, ctx, redfish.EVENT_TYPE_SYSTEM)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetchLogs api.EVENT_TYPE_SYSTEM")
|
||||
}
|
||||
err = fetchLogs(job.baremetal, ctx, redfish.EVENT_TYPE_MANAGER)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetchLogs api.EVENT_TYPE_MANAGER")
|
||||
}
|
||||
job.lastTime = now
|
||||
return nil
|
||||
}
|
||||
|
||||
type SSendMetricsJob struct {
|
||||
SBaseBaremetalCronJob
|
||||
}
|
||||
|
||||
func NewSendMetricsJob(baremetal *SBaremetalInstance, interval time.Duration) IBaremetalCronJob {
|
||||
return &SSendMetricsJob{
|
||||
SBaseBaremetalCronJob: SBaseBaremetalCronJob{
|
||||
baremetal: baremetal,
|
||||
interval: interval,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (job *SSendMetricsJob) Name() string {
|
||||
return "SendMetricsJob"
|
||||
}
|
||||
|
||||
func (job *SSendMetricsJob) Do(ctx context.Context, now time.Time) error {
|
||||
s := auth.GetAdminSession(ctx, consts.GetRegion(), "")
|
||||
urls, err := s.GetServiceURLs("influxdb", o.Options.SessionEndpointType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "s.GetServiceURLs")
|
||||
}
|
||||
if len(urls) == 0 {
|
||||
return nil
|
||||
}
|
||||
if !job.baremetal.isRedfishCapable() {
|
||||
return nil
|
||||
}
|
||||
powerMetrics, thermalMetrics, err := job.baremetal.fetchPowerThermalMetrics(ctx)
|
||||
tags := job.baremetal.getTags()
|
||||
metrics := []influxdb.SMetricData{
|
||||
{
|
||||
Name: "power",
|
||||
Tags: tags,
|
||||
Metrics: powerMetrics,
|
||||
Timestamp: now,
|
||||
},
|
||||
{
|
||||
Name: "thermal",
|
||||
Tags: tags,
|
||||
Metrics: thermalMetrics,
|
||||
Timestamp: now,
|
||||
},
|
||||
}
|
||||
err = influxdb.SendMetrics(urls, "telegraf", metrics, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "influxdb.SendMetrics")
|
||||
}
|
||||
job.lastTime = now
|
||||
return nil
|
||||
}
|
||||
+216
-2
@@ -24,6 +24,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -61,6 +62,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/util/dhcp"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/influxdb"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/redfish"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
@@ -98,6 +100,10 @@ func (m *SBaremetalManager) GetZoneId() string {
|
||||
return m.Agent.Zone.Id
|
||||
}
|
||||
|
||||
func (m *SBaremetalManager) GetZoneName() string {
|
||||
return m.Agent.Zone.Name
|
||||
}
|
||||
|
||||
func (m *SBaremetalManager) loadConfigs() error {
|
||||
m.killAllIPMITool()
|
||||
files, err := ioutil.ReadDir(m.configPath)
|
||||
@@ -469,6 +475,8 @@ type SBaremetalInstance struct {
|
||||
taskQueue *tasks.TaskQueue
|
||||
server baremetaltypes.IBaremetalServer
|
||||
serverLock *sync.Mutex
|
||||
|
||||
cronJobs []IBaremetalCronJob
|
||||
}
|
||||
|
||||
func newBaremetalInstance(man *SBaremetalManager, desc jsonutils.JSONObject) (*SBaremetalInstance, error) {
|
||||
@@ -479,6 +487,11 @@ func newBaremetalInstance(man *SBaremetalManager, desc jsonutils.JSONObject) (*S
|
||||
taskQueue: tasks.NewTaskQueue(),
|
||||
serverLock: new(sync.Mutex),
|
||||
}
|
||||
bm.cronJobs = []IBaremetalCronJob{
|
||||
NewStatusProbeJob(bm, time.Duration(o.Options.StatusProbeIntervalSeconds)*time.Second),
|
||||
NewLogFetchJob(bm, time.Duration(o.Options.LogFetchIntervalSeconds)*time.Second),
|
||||
NewSendMetricsJob(bm, time.Duration(o.Options.SendMetricsIntervalSeconds)*time.Second),
|
||||
}
|
||||
err := os.MkdirAll(bm.GetDir(), 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1061,7 +1074,7 @@ LABEL start
|
||||
resp += " APPEND hd0 0\n"
|
||||
b.ClearSSHConfig()
|
||||
}
|
||||
log.Infof("[TFTP response]: \n%s", resp)
|
||||
// log.Debugf("[SysLinux config]: \n%s", resp)
|
||||
return resp
|
||||
}
|
||||
|
||||
@@ -1295,11 +1308,25 @@ func (b *SBaremetalInstance) GetIPMITool() *ipmitool.LanPlusIPMI {
|
||||
return ipmitool.NewLanPlusIPMI(conf.IpAddr, conf.Username, conf.Password)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetRedfishCli(ctx context.Context) redfish.IRedfishDriver {
|
||||
func (b *SBaremetalInstance) isRedfishCapable() bool {
|
||||
conf := b.GetIPMIConfig()
|
||||
if conf == nil {
|
||||
return false
|
||||
}
|
||||
if !conf.Verified {
|
||||
return false
|
||||
}
|
||||
if !conf.RedfishApi {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetRedfishCli(ctx context.Context) redfish.IRedfishDriver {
|
||||
if !b.isRedfishCapable() {
|
||||
return nil
|
||||
}
|
||||
conf := b.GetIPMIConfig()
|
||||
return redfish.NewRedfishDriver(ctx, "https://"+conf.IpAddr,
|
||||
conf.Username, conf.Password, false)
|
||||
}
|
||||
@@ -1374,10 +1401,57 @@ func (b *SBaremetalInstance) GetZoneId() string {
|
||||
return b.manager.GetZoneId()
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetZoneName() string {
|
||||
return b.manager.GetZoneName()
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetStorageCacheId() string {
|
||||
return b.manager.Agent.CacheManager.GetId()
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetBootMode() string {
|
||||
bootMode, _ := b.desc.GetString("boot_mode")
|
||||
if len(bootMode) == 0 {
|
||||
bootMode = api.BOOT_MODE_PXE
|
||||
}
|
||||
return bootMode
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetRegion() string {
|
||||
r, _ := b.desc.GetString("region")
|
||||
return r
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetRegionId() string {
|
||||
r, _ := b.desc.GetString("region_id")
|
||||
return r
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetSerialNumber() string {
|
||||
r, _ := b.desc.GetString("sn")
|
||||
return r
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetManufacture() string {
|
||||
r, _ := b.desc.GetString("sys_info", "manufacture")
|
||||
return r
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetModel() string {
|
||||
r, _ := b.desc.GetString("sys_info", "model")
|
||||
return r
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetNodeCount() string {
|
||||
r, _ := b.desc.GetString("node_count")
|
||||
return r
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetMemGb() string {
|
||||
memMb, _ := b.desc.Int("mem_size")
|
||||
return strconv.FormatInt(memMb/1024, 10)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) DelayedRemove(_ jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
b.remove()
|
||||
return nil, nil
|
||||
@@ -1750,6 +1824,146 @@ func (b *SBaremetalInstance) DoNTPConfig() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) fetchLogs(ctx context.Context, logType string, since time.Time) ([]redfish.SEvent, error) {
|
||||
redfishApi := b.GetRedfishCli(ctx)
|
||||
if redfishApi == nil {
|
||||
// errors.Wrap(httperrors.ErrNotSupported, "no valid redfish api")
|
||||
return nil, nil
|
||||
}
|
||||
switch logType {
|
||||
case redfish.EVENT_TYPE_SYSTEM:
|
||||
return redfishApi.ReadSystemLogs(ctx, since)
|
||||
case redfish.EVENT_TYPE_MANAGER:
|
||||
return redfishApi.ReadManagerLogs(ctx, since)
|
||||
}
|
||||
return nil, errors.Wrap(httperrors.ErrNotSupported, logType)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) clearLogs(ctx context.Context, logType string) error {
|
||||
redfishApi := b.GetRedfishCli(ctx)
|
||||
if redfishApi == nil {
|
||||
return errors.Wrap(httperrors.ErrNotSupported, "no valid redfish api")
|
||||
}
|
||||
switch logType {
|
||||
case redfish.EVENT_TYPE_SYSTEM:
|
||||
return redfishApi.ClearSystemLogs(ctx)
|
||||
case redfish.EVENT_TYPE_MANAGER:
|
||||
return redfishApi.ClearManagerLogs(ctx)
|
||||
}
|
||||
return errors.Wrap(httperrors.ErrNotSupported, logType)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) doCronJobs(ctx context.Context) {
|
||||
for _, job := range b.cronJobs {
|
||||
now := time.Now().UTC()
|
||||
if job.NeedsToRun(now) {
|
||||
log.Debugf("need to run %s", job.Name())
|
||||
func() {
|
||||
job.StartRun()
|
||||
defer job.StopRun()
|
||||
err := job.Do(ctx, now)
|
||||
if err != nil {
|
||||
log.Errorf("Baremetal %s do cronjob %s fail: %s", b.GetName(), job.Name(), err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) fetchPowerThermalMetrics(ctx context.Context) ([]influxdb.SKeyValue, []influxdb.SKeyValue, error) {
|
||||
redfishApi := b.GetRedfishCli(ctx)
|
||||
if redfishApi == nil {
|
||||
return nil, nil, errors.Wrap(httperrors.ErrNotSupported, "no valid redfish api")
|
||||
}
|
||||
powers, err := redfishApi.GetPower(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "redfishApi.GetPower")
|
||||
}
|
||||
thermals, err := redfishApi.GetThermal(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "redfishApi.Thermal")
|
||||
}
|
||||
powerMetrics := powers[0].ToMetrics()
|
||||
thermalMetrics := make([]influxdb.SKeyValue, 0)
|
||||
for _, t := range thermals {
|
||||
thermalMetrics = append(thermalMetrics, t.ToMetric())
|
||||
}
|
||||
return powerMetrics, thermalMetrics, nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) getTags() []influxdb.SKeyValue {
|
||||
tags := []influxdb.SKeyValue{
|
||||
{
|
||||
Key: "id",
|
||||
Value: b.GetId(),
|
||||
},
|
||||
{
|
||||
Key: "name",
|
||||
Value: b.GetName(),
|
||||
},
|
||||
{
|
||||
Key: "zone_id",
|
||||
Value: b.GetZoneId(),
|
||||
},
|
||||
{
|
||||
Key: "zone",
|
||||
Value: b.GetZoneName(),
|
||||
},
|
||||
{
|
||||
Key: "boot_mode",
|
||||
Value: b.GetBootMode(),
|
||||
},
|
||||
{
|
||||
Key: "host_type",
|
||||
Value: "baremetal",
|
||||
},
|
||||
{
|
||||
Key: "region",
|
||||
Value: b.GetRegion(),
|
||||
},
|
||||
{
|
||||
Key: "region_id",
|
||||
Value: b.GetRegionId(),
|
||||
},
|
||||
{
|
||||
Key: "sn",
|
||||
Value: b.GetSerialNumber(),
|
||||
},
|
||||
{
|
||||
Key: "manufacture",
|
||||
Value: b.GetManufacture(),
|
||||
},
|
||||
{
|
||||
Key: "model",
|
||||
Value: b.GetModel(),
|
||||
},
|
||||
{
|
||||
Key: "status",
|
||||
Value: b.GetStatus(),
|
||||
},
|
||||
{
|
||||
Key: "ncpu",
|
||||
Value: b.GetNodeCount(),
|
||||
},
|
||||
{
|
||||
Key: "mem_gb",
|
||||
Value: b.GetMemGb(),
|
||||
},
|
||||
}
|
||||
srvId := b.GetServerId()
|
||||
if len(srvId) > 0 {
|
||||
tags = append(tags, influxdb.SKeyValue{
|
||||
Key: "server_id",
|
||||
Value: srvId,
|
||||
})
|
||||
tags = append(tags, influxdb.SKeyValue{
|
||||
Key: "server",
|
||||
Value: b.GetServerName(),
|
||||
})
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
type SBaremetalServer struct {
|
||||
baremetal *SBaremetalInstance
|
||||
desc *jsonutils.JSONDict
|
||||
|
||||
@@ -46,6 +46,10 @@ type BaremetalOptions struct {
|
||||
CachePath string `help:"local image cache directory"`
|
||||
EnablePxeBoot bool `help:"Enable DHCP PXE boot" default:"true"`
|
||||
BootIsoPath string `help:"iso boot image path"`
|
||||
|
||||
StatusProbeIntervalSeconds int `help:"interval to probe baremetal status, default is 60 seconds" default:"60"`
|
||||
LogFetchIntervalSeconds int `help:"interval to fetch baremetal log, default is 900 seconds" default:"900"`
|
||||
SendMetricsIntervalSeconds int `help:"interval to send baremetal metrics, default is 300 seconds" default:"300"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -17,6 +17,7 @@ package service
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
@@ -26,6 +27,7 @@ import (
|
||||
o "yunion.io/x/onecloud/pkg/baremetal/options"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/tasks"
|
||||
app_common "yunion.io/x/onecloud/pkg/cloudcommon/app"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/cronman"
|
||||
common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/service"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
|
||||
@@ -66,6 +68,11 @@ func (s *BaremetalService) StartService() {
|
||||
|
||||
s.startAgent(app)
|
||||
|
||||
cron := cronman.InitCronJobManager(false, o.Options.CronJobWorkerCount)
|
||||
cron.AddJobAtIntervals("BaremetalCronJobs", 10*time.Second, baremetal.DoCronJobs)
|
||||
cron.Start()
|
||||
defer cron.Stop()
|
||||
|
||||
app_common.ServeForeverWithCleanup(app, &o.Options.BaseOptions, func() {
|
||||
tasks.OnStop()
|
||||
baremetal.Stop()
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
type SBaremetalEventManager struct {
|
||||
db.SModelBaseManager
|
||||
}
|
||||
|
||||
type SBaremetalEvent struct {
|
||||
db.SModelBase
|
||||
|
||||
Id int64 `primary:"true" auto_increment:"true" list:"user"`
|
||||
HostId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" index:"true"`
|
||||
HostName string `width:"64" charset:"utf8" nullable:"false" list:"user" create:"required"`
|
||||
IpmiIp string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional"`
|
||||
Created time.Time `nullable:"false" create:"required" list:"user"`
|
||||
EventId string `width:"32" nullable:"true" create:"optional" list:"user"`
|
||||
Type string `width:"10" nullable:"true" create:"optional" list:"user"`
|
||||
|
||||
Message string `nullable:"false" create:"required" list:"user"`
|
||||
Severity string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional"`
|
||||
}
|
||||
|
||||
var BaremetalEventManager *SBaremetalEventManager
|
||||
|
||||
func init() {
|
||||
BaremetalEventManager = &SBaremetalEventManager{
|
||||
SModelBaseManager: db.NewModelBaseManager(
|
||||
SBaremetalEvent{},
|
||||
"baremetal_event_tbl",
|
||||
"baremetalevent",
|
||||
"baremetalevents",
|
||||
),
|
||||
}
|
||||
BaremetalEventManager.SetVirtualObject(BaremetalEventManager)
|
||||
}
|
||||
|
||||
func (event *SBaremetalEvent) GetId() string {
|
||||
return fmt.Sprintf("%d", event.Id)
|
||||
}
|
||||
|
||||
func (event *SBaremetalEvent) GetName() string {
|
||||
return event.HostName + event.EventId
|
||||
}
|
||||
|
||||
func (event *SBaremetalEvent) GetModelManager() db.IModelManager {
|
||||
return BaremetalEventManager
|
||||
}
|
||||
|
||||
func (manager *SBaremetalEventManager) ResourceScope() rbacutils.TRbacScope {
|
||||
return rbacutils.ScopeSystem
|
||||
}
|
||||
|
||||
func (manager *SBaremetalEventManager) GetPagingConfig() *db.SPagingConfig {
|
||||
return &db.SPagingConfig{
|
||||
Order: sqlchemy.SQL_ORDER_DESC,
|
||||
MarkerField: "id",
|
||||
DefaultLimit: 20,
|
||||
}
|
||||
}
|
||||
|
||||
/*func (manager *SBaremetalEventManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
hostId, _ := data.GetString("host_id")
|
||||
eventId, _ := data.GetString("event_id")
|
||||
q := manager.Query().Equals("host_id", hostId).Equals("event_id", eventId)
|
||||
cnt, err := q.CountWithError()
|
||||
if err != nil {
|
||||
return nil, httperrors.NewGeneralError(err)
|
||||
}
|
||||
if cnt > 0 {
|
||||
return nil, httperrors.NewConflictError("duplicate log")
|
||||
}
|
||||
return manager.SModelBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, data)
|
||||
}*/
|
||||
|
||||
/*func (event *SBaremetalEvent) ValidateDeleteCondition(ctx context.Context) error {
|
||||
return httperrors.NewForbiddenError("read only event")
|
||||
}*/
|
||||
|
||||
/*func (event *SBaremetalEvent) ValidateUpdateCondition(ctx context.Context) error {
|
||||
return httperrors.NewForbiddenError("read only event")
|
||||
}*/
|
||||
@@ -33,6 +33,7 @@ func initHandlers(app *appsrv.Application) {
|
||||
|
||||
for _, manager := range []db.IModelManager{
|
||||
models.ActionLog,
|
||||
models.BaremetalEventManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
handler := db.NewModelHandler(manager)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package modules
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
|
||||
var (
|
||||
BaremetalEvents modulebase.ResourceManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
BaremetalEvents = NewActionManager("baremetalevent", "baremetalevents",
|
||||
[]string{"id", "host_id", "host_name",
|
||||
"ipmi_ip", "created", "event_id",
|
||||
"message", "severity", "type",
|
||||
},
|
||||
[]string{})
|
||||
register(&BaremetalEvents)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package influxdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SKeyValue struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (kv SKeyValue) String() string {
|
||||
return fmt.Sprintf("%s=%s", kv.Key, strings.ReplaceAll(kv.Value, " ", "+"))
|
||||
}
|
||||
|
||||
type TKeyValuePairs []SKeyValue
|
||||
|
||||
func (a TKeyValuePairs) Len() int { return len(a) }
|
||||
func (a TKeyValuePairs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a TKeyValuePairs) Less(i, j int) bool { return a[i].Key < a[j].Key }
|
||||
|
||||
type SMetricData struct {
|
||||
Name string
|
||||
Tags []SKeyValue
|
||||
Metrics []SKeyValue
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
func (m *SMetricData) Line() string {
|
||||
sort.Sort(TKeyValuePairs(m.Tags))
|
||||
sort.Sort(TKeyValuePairs(m.Metrics))
|
||||
|
||||
line := strings.Builder{}
|
||||
line.WriteString(m.Name)
|
||||
for i := range m.Tags {
|
||||
line.WriteByte(',')
|
||||
line.WriteString(m.Tags[i].String())
|
||||
}
|
||||
line.WriteByte(' ')
|
||||
for i := range m.Metrics {
|
||||
if i > 0 {
|
||||
line.WriteByte(',')
|
||||
}
|
||||
line.WriteString(m.Metrics[i].String())
|
||||
}
|
||||
line.WriteByte(' ')
|
||||
if m.Timestamp.IsZero() {
|
||||
m.Timestamp = time.Now()
|
||||
}
|
||||
line.WriteString(strconv.FormatInt(m.Timestamp.UnixNano()/1000000, 10))
|
||||
|
||||
return line.String()
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package influxdb
|
||||
|
||||
import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
func SendMetrics(urls []string, dbName string, metrics []SMetricData, debug bool) error {
|
||||
lines := make([]string, len(metrics))
|
||||
for i := range metrics {
|
||||
lines[i] = metrics[i].Line()
|
||||
}
|
||||
for _, url := range urls {
|
||||
db := NewInfluxdbWithDebug(url, debug)
|
||||
err := db.SetDatabase(dbName)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SetDatabase")
|
||||
}
|
||||
for _, line := range lines {
|
||||
err = db.Write(line, "ms")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "db.Write")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -16,6 +16,7 @@ package redfish
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -59,8 +60,12 @@ type IRedfishDriver interface {
|
||||
|
||||
Reset(ctx context.Context, action string) error
|
||||
|
||||
ReadSystemLogs(ctx context.Context) ([]SEvent, error)
|
||||
ReadManagerLogs(ctx context.Context) ([]SEvent, error)
|
||||
GetSystemLogsPath() string
|
||||
GetManagerLogsPath() string
|
||||
GetClearSystemLogsPath() string
|
||||
GetClearManagerLogsPath() string
|
||||
ReadSystemLogs(ctx context.Context, since time.Time) ([]SEvent, error)
|
||||
ReadManagerLogs(ctx context.Context, since time.Time) ([]SEvent, error)
|
||||
ClearSystemLogs(ctx context.Context) error
|
||||
ClearManagerLogs(ctx context.Context) error
|
||||
|
||||
@@ -71,6 +76,8 @@ type IRedfishDriver interface {
|
||||
GetIndicatorLED(ctx context.Context) (bool, error)
|
||||
SetIndicatorLED(ctx context.Context, on bool) error
|
||||
|
||||
GetPowerPath() string
|
||||
GetThermalPath() string
|
||||
GetPower(ctx context.Context) ([]SPower, error)
|
||||
GetThermal(ctx context.Context) ([]STemperature, error)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -210,7 +211,7 @@ func (r *SHpRestApi) BmcReset(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SHpRestApi) readLogs(ctx context.Context, subsys string) ([]redfish.SEvent, error) {
|
||||
func (r *SHpRestApi) readLogs(ctx context.Context, subsys string, since time.Time) ([]redfish.SEvent, error) {
|
||||
_, manager, err := r.GetResource(ctx, subsys, "0", "Logs", "0")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource Managers 0")
|
||||
@@ -236,7 +237,17 @@ func (r *SHpRestApi) readLogs(ctx context.Context, subsys string) ([]redfish.SEv
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "resp.Unmarshal")
|
||||
}
|
||||
events = append(events, tmpEvents...)
|
||||
timeBreak := false
|
||||
for i := range tmpEvents {
|
||||
if !since.IsZero() && tmpEvents[i].Created.Before(since) {
|
||||
timeBreak = true
|
||||
break
|
||||
}
|
||||
events = append(events, tmpEvents[i])
|
||||
}
|
||||
if timeBreak {
|
||||
break
|
||||
}
|
||||
nextPage, _ := resp.Int("links", "NextPage", "page")
|
||||
if nextPage > 0 {
|
||||
nextPath = fmt.Sprintf("%s?page=%d", path, nextPage)
|
||||
@@ -247,12 +258,12 @@ func (r *SHpRestApi) readLogs(ctx context.Context, subsys string) ([]redfish.SEv
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func (r *SHpRestApi) ReadSystemLogs(ctx context.Context) ([]redfish.SEvent, error) {
|
||||
return r.readLogs(ctx, "Systems")
|
||||
func (r *SHpRestApi) ReadSystemLogs(ctx context.Context, since time.Time) ([]redfish.SEvent, error) {
|
||||
return r.readLogs(ctx, "Systems", since)
|
||||
}
|
||||
|
||||
func (r *SHpRestApi) ReadManagerLogs(ctx context.Context) ([]redfish.SEvent, error) {
|
||||
return r.readLogs(ctx, "Managers")
|
||||
func (r *SHpRestApi) ReadManagerLogs(ctx context.Context, since time.Time) ([]redfish.SEvent, error) {
|
||||
return r.readLogs(ctx, "Managers", since)
|
||||
}
|
||||
|
||||
func (r *SHpRestApi) clearLogs(ctx context.Context, subsys string) error {
|
||||
|
||||
@@ -333,3 +333,27 @@ func (r *SIDracRefishApi) GetConsoleJNLP(ctx context.Context) (string, error) {
|
||||
bmc := bmconsole.NewBMCConsole(r.GetHost(), r.GetUsername(), r.GetPassword(), r.IsDebug)
|
||||
return bmc.GetIdracConsoleJNLP(ctx, "", "")
|
||||
}
|
||||
|
||||
func (r *SIDracRefishApi) GetSystemLogsPath() string {
|
||||
return "/redfish/v1/Managers/iDRAC.Embedded.1/Logs/Sel"
|
||||
}
|
||||
|
||||
func (r *SIDracRefishApi) GetManagerLogsPath() string {
|
||||
return "/redfish/v1/Managers/iDRAC.Embedded.1/Logs/Lclog"
|
||||
}
|
||||
|
||||
func (r *SIDracRefishApi) GetClearSystemLogsPath() string {
|
||||
return "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Actions/LogService.ClearLog"
|
||||
}
|
||||
|
||||
func (r *SIDracRefishApi) GetClearManagerLogsPath() string {
|
||||
return "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Lclog/Actions/LogService.ClearLog"
|
||||
}
|
||||
|
||||
func (r *SIDracRefishApi) GetPowerPath() string {
|
||||
return "/redfish/v1/Chassis/System.Embedded.1/Power"
|
||||
}
|
||||
|
||||
func (r *SIDracRefishApi) GetThermalPath() string {
|
||||
return "/redfish/v1/Chassis/System.Embedded.1/Thermal"
|
||||
}
|
||||
|
||||
+36
-11
@@ -17,6 +17,7 @@ package ilo
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -118,10 +119,13 @@ func (r *SILORefishApi) SetNextBootVirtualCdrom(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) readLogs(ctx context.Context, subsys string) ([]redfish.SEvent, error) {
|
||||
path, _, err := r.GetResource(ctx, subsys, "0", "LogServices", "0", "Entries")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource")
|
||||
func (r *SILORefishApi) readLogs(ctx context.Context, path string, subsys string, typeStr string, since time.Time) ([]redfish.SEvent, error) {
|
||||
if len(path) == 0 {
|
||||
var err error
|
||||
path, _, err = r.GetResource(ctx, subsys, "0", "LogServices", "0", "Entries")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource")
|
||||
}
|
||||
}
|
||||
resp, err := r.Get(ctx, path)
|
||||
if err != nil {
|
||||
@@ -132,7 +136,7 @@ func (r *SILORefishApi) readLogs(ctx context.Context, subsys string) ([]redfish.
|
||||
return nil, errors.Wrap(err, "GetArray")
|
||||
}
|
||||
events := make([]redfish.SEvent, 0)
|
||||
for idx := range paths {
|
||||
for idx := len(paths) - 1; idx >= 0; idx -= 1 {
|
||||
eventPath, err := paths[idx].GetString(r.IRedfishDriver().LinkKey())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetString")
|
||||
@@ -147,29 +151,42 @@ func (r *SILORefishApi) readLogs(ctx context.Context, subsys string) ([]redfish.
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "eventResp.Unmarshal")
|
||||
}
|
||||
if !since.IsZero() && tmpEvent.Created.Before(since) {
|
||||
break
|
||||
}
|
||||
tmpEvent.EventId, _ = eventResp.GetString("Oem", "Hp", "EventNumber")
|
||||
if len(tmpEvent.EventId) == 0 {
|
||||
tmpEvent.EventId, _ = eventResp.GetString("Oem", "Hpe", "EventNumber")
|
||||
}
|
||||
tmpEvent.Type = typeStr
|
||||
events = append(events, tmpEvent)
|
||||
}
|
||||
redfish.SortEvents(events)
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) ReadSystemLogs(ctx context.Context) ([]redfish.SEvent, error) {
|
||||
return r.readLogs(ctx, "Systems")
|
||||
func (r *SILORefishApi) ReadSystemLogs(ctx context.Context, since time.Time) ([]redfish.SEvent, error) {
|
||||
return r.readLogs(ctx, "/redfish/v1/Systems/1/LogServices/IML/Entries/", "Systems", redfish.EVENT_TYPE_SYSTEM, since)
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) ReadManagerLogs(ctx context.Context) ([]redfish.SEvent, error) {
|
||||
return r.readLogs(ctx, "Managers")
|
||||
func (r *SILORefishApi) ReadManagerLogs(ctx context.Context, since time.Time) ([]redfish.SEvent, error) {
|
||||
return r.readLogs(ctx, "/redfish/v1/Managers/1/LogServices/IEL/Entries/", "Managers", redfish.EVENT_TYPE_MANAGER, since)
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) GetClearSystemLogsPath() string {
|
||||
return "/redfish/v1/Systems/1/LogServices/IML/Actions/LogService.ClearLog/"
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) GetClearManagerLogsPath() string {
|
||||
return "/redfish/v1/Managers/1/LogServices/IEL/Actions/LogService.ClearLog/"
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) ClearSystemLogs(ctx context.Context) error {
|
||||
return r.ClearLogs(ctx, "Systems", 0)
|
||||
return r.ClearLogs(ctx, r.IRedfishDriver().GetClearManagerLogsPath(), "Systems", 0)
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) ClearManagerLogs(ctx context.Context) error {
|
||||
return r.ClearLogs(ctx, "Managers", 0)
|
||||
return r.ClearLogs(ctx, r.IRedfishDriver().GetClearManagerLogsPath(), "Managers", 0)
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) LogItemsKey() string {
|
||||
@@ -282,3 +299,11 @@ func (r *SILORefishApi) MountVirtualCdrom(ctx context.Context, path string, cdro
|
||||
log.Debugf("%s", resp.PrettyString())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) GetPowerPath() string {
|
||||
return "/redfish/v1/Chassis/1/Power/"
|
||||
}
|
||||
|
||||
func (r *SILORefishApi) GetThermalPath() string {
|
||||
return "/redfish/v1/Chassis/1/Thermal/"
|
||||
}
|
||||
|
||||
+92
-31
@@ -22,6 +22,7 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -190,7 +191,7 @@ func (r *SBaseRedfishClient) Probe(ctx context.Context) error {
|
||||
urlPath, _ := respMap[k].GetString(r.IRedfishDriver().LinkKey())
|
||||
if len(urlPath) > 0 {
|
||||
r.Registries[k] = urlPath
|
||||
log.Debugf("%s %s", k, urlPath)
|
||||
// log.Debugf("%s %s", k, urlPath)
|
||||
}
|
||||
}
|
||||
if len(r.Registries) == 0 {
|
||||
@@ -522,20 +523,21 @@ func Int2str(i int) string {
|
||||
return strconv.FormatInt(int64(i), 10)
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) ReadLogs(ctx context.Context, subsys string, index int) ([]SEvent, error) {
|
||||
_, manager, err := r.GetResource(ctx, subsys, "0", "LogServices", Int2str(index))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource Managers 0")
|
||||
}
|
||||
manager = r.IRedfishDriver().GetParent(manager)
|
||||
path, _ := manager.GetString("Entries", r.IRedfishDriver().LinkKey())
|
||||
func (r *SBaseRedfishClient) readLogs(ctx context.Context, path string, subsys string, index int, typeStr string, since time.Time) ([]SEvent, error) {
|
||||
if len(path) == 0 {
|
||||
return nil, errors.Wrap(err, "no entry???")
|
||||
var err error
|
||||
path, _, err = r.GetResource(ctx, subsys, "0", "LogServices", Int2str(index), "Entries")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource Managers 0")
|
||||
}
|
||||
}
|
||||
events := make([]SEvent, 0)
|
||||
for {
|
||||
resp, err := r.Get(ctx, path)
|
||||
if err != nil {
|
||||
if httputils.ErrorCode(err) == 404 {
|
||||
break
|
||||
}
|
||||
return nil, errors.Wrap(err, path)
|
||||
}
|
||||
tmpEvents := make([]SEvent, 0)
|
||||
@@ -543,47 +545,84 @@ func (r *SBaseRedfishClient) ReadLogs(ctx context.Context, subsys string, index
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "resp.Unmarshal")
|
||||
}
|
||||
events = append(events, tmpEvents...)
|
||||
for i := range tmpEvents {
|
||||
tmpEvents[i].Type = typeStr
|
||||
}
|
||||
timeBreak := false
|
||||
for i := range tmpEvents {
|
||||
if !since.IsZero() && tmpEvents[i].Created.Before(since) {
|
||||
timeBreak = true
|
||||
break
|
||||
}
|
||||
if !strings.Contains(tmpEvents[i].Message, "Log cleared.") {
|
||||
events = append(events, tmpEvents[i])
|
||||
}
|
||||
}
|
||||
if timeBreak {
|
||||
break
|
||||
}
|
||||
nextPage, _ := resp.GetString("@odata.nextLink")
|
||||
if len(nextPage) == 0 {
|
||||
nextPage, _ = resp.GetString("Members@odata.nextLink")
|
||||
}
|
||||
if len(nextPage) > 0 {
|
||||
path = nextPage
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
SortEvents(events)
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) ReadSystemLogs(ctx context.Context) ([]SEvent, error) {
|
||||
return r.ReadLogs(ctx, "Managers", 0)
|
||||
func (r *SBaseRedfishClient) GetSystemLogsPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) ReadManagerLogs(ctx context.Context) ([]SEvent, error) {
|
||||
return r.ReadLogs(ctx, "Managers", 1)
|
||||
func (r *SBaseRedfishClient) GetManagerLogsPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) ClearLogs(ctx context.Context, subsys string, index int) error {
|
||||
_, logInfo, err := r.GetResource(ctx, subsys, "0", "LogServices", Int2str(index))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetResource Managers 0")
|
||||
func (r *SBaseRedfishClient) ReadSystemLogs(ctx context.Context, since time.Time) ([]SEvent, error) {
|
||||
return r.readLogs(ctx, r.IRedfishDriver().GetSystemLogsPath(), "Managers", 0, EVENT_TYPE_SYSTEM, since)
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) ReadManagerLogs(ctx context.Context, since time.Time) ([]SEvent, error) {
|
||||
return r.readLogs(ctx, r.IRedfishDriver().GetManagerLogsPath(), "Managers", 1, EVENT_TYPE_MANAGER, since)
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) ClearLogs(ctx context.Context, urlPath string, subsys string, index int) error {
|
||||
if len(urlPath) == 0 {
|
||||
_, logInfo, err := r.GetResource(ctx, subsys, "0", "LogServices", Int2str(index))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetResource Managers 0")
|
||||
}
|
||||
urlPath, err = logInfo.GetString("Actions", "#LogService.ClearLog", "target")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Actions.#LogService.ClearLog.target")
|
||||
}
|
||||
}
|
||||
urlPath, err := logInfo.GetString("Actions", "#LogService.ClearLog", "target")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Actions.#LogService.ClearLog.target")
|
||||
}
|
||||
_, _, err = r.Post(ctx, urlPath, jsonutils.NewDict())
|
||||
_, _, err := r.Post(ctx, urlPath, jsonutils.NewDict())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "r.Post")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) GetClearSystemLogsPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) GetClearManagerLogsPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) ClearSystemLogs(ctx context.Context) error {
|
||||
return r.ClearLogs(ctx, "Managers", 0)
|
||||
return r.ClearLogs(ctx, r.IRedfishDriver().GetClearSystemLogsPath(), "Managers", 0)
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) ClearManagerLogs(ctx context.Context) error {
|
||||
return r.ClearLogs(ctx, "Managers", 1)
|
||||
return r.ClearLogs(ctx, r.IRedfishDriver().GetClearManagerLogsPath(), "Managers", 1)
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) GetBiosInfo(ctx context.Context) (SBiosInfo, error) {
|
||||
@@ -654,10 +693,25 @@ func (r *SBaseRedfishClient) SetIndicatorLED(ctx context.Context, on bool) error
|
||||
return r.SetIndicatorLEDInternal(ctx, "Chassis", valStr)
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) GetPowerPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) GetThermalPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) GetPower(ctx context.Context) ([]SPower, error) {
|
||||
_, resp, err := r.GetResource(ctx, "Chassis", "0", "Power")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource")
|
||||
path := r.IRedfishDriver().GetPowerPath()
|
||||
var resp jsonutils.JSONObject
|
||||
var err error
|
||||
if len(path) > 0 {
|
||||
resp, err = r.Get(ctx, path)
|
||||
} else {
|
||||
_, resp, err = r.GetResource(ctx, "Chassis", "0", "Power")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource")
|
||||
}
|
||||
}
|
||||
powers := make([]SPower, 0)
|
||||
err = resp.Unmarshal(&powers, "PowerControl")
|
||||
@@ -668,9 +722,16 @@ func (r *SBaseRedfishClient) GetPower(ctx context.Context) ([]SPower, error) {
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) GetThermal(ctx context.Context) ([]STemperature, error) {
|
||||
_, resp, err := r.GetResource(ctx, "Chassis", "0", "Thermal")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource")
|
||||
path := r.IRedfishDriver().GetThermalPath()
|
||||
var resp jsonutils.JSONObject
|
||||
var err error
|
||||
if len(path) > 0 {
|
||||
resp, err = r.Get(ctx, path)
|
||||
} else {
|
||||
_, resp, err = r.GetResource(ctx, "Chassis", "0", "Thermal")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource")
|
||||
}
|
||||
}
|
||||
temps := make([]STemperature, 0)
|
||||
err = resp.Unmarshal(&temps, "Temperatures")
|
||||
|
||||
@@ -14,7 +14,15 @@
|
||||
|
||||
package redfish
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/influxdb"
|
||||
)
|
||||
|
||||
type SCdromInfo struct {
|
||||
Image string `json:"Image"`
|
||||
@@ -38,11 +46,27 @@ type SSystemInfo struct {
|
||||
ResetTypeSupported []string `json:"ResetTypeSupported"`
|
||||
}
|
||||
|
||||
const (
|
||||
EVENT_TYPE_SYSTEM = "system"
|
||||
EVENT_TYPE_MANAGER = "manager"
|
||||
)
|
||||
|
||||
type SEvent struct {
|
||||
Created time.Time `json:"Created"`
|
||||
EventId string `json:"MessageID"`
|
||||
EventId string `json:"Id"`
|
||||
Message string `json:"Message"`
|
||||
Severity string `json:"Severity"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type SEventList []SEvent
|
||||
|
||||
func (el SEventList) Len() int { return len(el) }
|
||||
func (el SEventList) Swap(i, j int) { el[i], el[j] = el[j], el[i] }
|
||||
func (el SEventList) Less(i, j int) bool { return el[i].Created.Before(el[j].Created) }
|
||||
|
||||
func SortEvents(el []SEvent) {
|
||||
sort.Sort(SEventList(el))
|
||||
}
|
||||
|
||||
type SBiosInfo struct {
|
||||
@@ -59,12 +83,48 @@ type SPower struct {
|
||||
} `json:"PowerMetrics"`
|
||||
}
|
||||
|
||||
func (p SPower) ToMetrics() []influxdb.SKeyValue {
|
||||
return []influxdb.SKeyValue{
|
||||
{
|
||||
Key: "PowerCapacityWatts",
|
||||
Value: strconv.FormatInt(int64(p.PowerCapacityWatts), 10),
|
||||
},
|
||||
{
|
||||
Key: "PowerConsumedWatts",
|
||||
Value: strconv.FormatInt(int64(p.PowerConsumedWatts), 10),
|
||||
},
|
||||
{
|
||||
Key: "AverageConsumedWatts",
|
||||
Value: strconv.FormatInt(int64(p.PowerMetrics.AverageConsumedWatts), 10),
|
||||
},
|
||||
{
|
||||
Key: "IntervalInMin",
|
||||
Value: strconv.FormatInt(int64(p.PowerMetrics.IntervalInMin), 10),
|
||||
},
|
||||
{
|
||||
Key: "MaxConsumedWatts",
|
||||
Value: strconv.FormatInt(int64(p.PowerMetrics.MaxConsumedWatts), 10),
|
||||
},
|
||||
{
|
||||
Key: "MinConsumedWatts",
|
||||
Value: strconv.FormatInt(int64(p.PowerMetrics.MinConsumedWatts), 10),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type STemperature struct {
|
||||
Name string `help:"Name"`
|
||||
Name string `json:"Name"`
|
||||
PhysicalContext string `json:"PhysicalContext"`
|
||||
ReadingCelsius int `json:"ReadingCelsius"`
|
||||
}
|
||||
|
||||
func (t STemperature) ToMetric() influxdb.SKeyValue {
|
||||
return influxdb.SKeyValue{
|
||||
Key: fmt.Sprintf("%s/%s", t.PhysicalContext, strings.ReplaceAll(t.Name, " ", "")),
|
||||
Value: strconv.FormatInt(int64(t.ReadingCelsius), 10),
|
||||
}
|
||||
}
|
||||
|
||||
type SNTPConf struct {
|
||||
NTPServers []string `json:"NTPServers,allowempty"`
|
||||
ProtocolEnabled bool `json:"ProtocolEnabled,allowfalse"`
|
||||
|
||||
Reference in New Issue
Block a user