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:
Zexi Li
2019-10-30 21:56:34 +08:00
committed by GitHub
17 changed files with 1058 additions and 57 deletions
+71
View File
@@ -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
})
}
+21 -2
View File
@@ -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
}
+251
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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 (
+7
View File
@@ -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()
+104
View File
@@ -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")
}*/
+1
View File
@@ -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)
}
+71
View File
@@ -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()
}
+40
View File
@@ -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
}
+9 -2
View File
@@ -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)
+17 -6
View File
@@ -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 {
+24
View File
@@ -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
View File
@@ -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
View File
@@ -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")
+63 -3
View File
@@ -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"`