Files
cloudpods/pkg/util/redfish/ilo/ilo.go
2022-12-27 01:21:26 +08:00

310 lines
8.8 KiB
Go

// 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 ilo
import (
"context"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/httputils"
"yunion.io/x/onecloud/pkg/util/redfish"
"yunion.io/x/onecloud/pkg/util/redfish/bmconsole"
"yunion.io/x/onecloud/pkg/util/redfish/generic"
)
type SILORedfishApiFactory struct {
}
func (f *SILORedfishApiFactory) Name() string {
return "iLO"
}
func (f *SILORedfishApiFactory) NewApi(endpoint, username, password string, debug bool) redfish.IRedfishDriver {
return NewILORedfishApi(endpoint, username, password, debug)
}
func init() {
redfish.RegisterApiFactory(&SILORedfishApiFactory{})
}
type SILORefishApi struct {
generic.SGenericRefishApi
}
func NewILORedfishApi(endpoint, username, password string, debug bool) redfish.IRedfishDriver {
api := &SILORefishApi{
SGenericRefishApi: generic.SGenericRefishApi{
SBaseRedfishClient: redfish.NewBaseRedfishClient(endpoint, username, password, debug),
},
}
api.SetVirtualObject(api)
return api
}
func (r *SILORefishApi) ParseRoot(root jsonutils.JSONObject) error {
oemHp, _ := root.Get("Oem", "Hp")
if oemHp != nil {
return nil
}
return errors.Error("not iLO")
}
func (r *SILORefishApi) Probe(ctx context.Context) error {
err := r.SGenericRefishApi.Probe(ctx)
if err != nil {
return errors.Wrap(err, "r.SGenericRefishApi.Probe")
}
if r.IsDebug {
resp, err := r.Get(ctx, "/redfish/v1/ResourceDirectory/")
if err != nil {
return errors.Wrap(err, "r.Get /redfish/v1/ResourceDirectory/")
}
log.Debugf("%s", resp.PrettyString())
}
return nil
}
func (r *SILORefishApi) GetVirtualCdromInfo(ctx context.Context) (string, redfish.SCdromInfo, error) {
path, cdInfo, err := r.SGenericRefishApi.GetVirtualCdromInfo(ctx)
if err == nil {
cdInfo.SupportAction = true
}
return path, cdInfo, err
}
func (r *SILORefishApi) SetNextBootVirtualCdrom(ctx context.Context) error {
path, cdInfo, err := r.GetVirtualCdromJSON(ctx)
if err != nil {
return errors.Wrap(err, "GetVirtualCdromJSON")
}
var oemKey string
nextBoot, err := cdInfo.Bool("Oem", "Hp", "BootOnNextServerReset")
if err != nil {
nextBoot, err = cdInfo.Bool("Oem", "Hpe", "BootOnNextServerReset")
if err != nil {
return errors.Wrap(err, "no BootOnNextServerReset found???")
}
oemKey = "Hpe"
} else {
oemKey = "Hp"
}
if !nextBoot {
params := jsonutils.NewDict()
params.Add(jsonutils.JSONTrue, "Oem", oemKey, "BootOnNextServerReset")
resp, err := r.Patch(ctx, path, params)
if err != nil {
return errors.Wrap(err, "r.Patch")
}
if r.IsDebug {
log.Debugf("%s", resp.PrettyString())
}
}
return nil
}
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 {
return nil, errors.Wrap(err, path)
}
paths, err := resp.GetArray(r.IRedfishDriver().MemberKey())
if err != nil {
return nil, errors.Wrap(err, "GetArray")
}
events := make([]redfish.SEvent, 0)
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")
}
eventResp, err := r.Get(ctx, eventPath)
if err != nil {
log.Errorf("GetEvent fail %s", err)
continue
}
tmpEvent := redfish.SEvent{}
err = eventResp.Unmarshal(&tmpEvent)
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, 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, 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, r.IRedfishDriver().GetClearManagerLogsPath(), "Systems", 0)
}
func (r *SILORefishApi) ClearManagerLogs(ctx context.Context) error {
return r.ClearLogs(ctx, r.IRedfishDriver().GetClearManagerLogsPath(), "Managers", 0)
}
func (r *SILORefishApi) LogItemsKey() string {
return "Members"
}
func (r *SILORefishApi) GetIndicatorLED(ctx context.Context) (bool, error) {
_, val, err := r.GetIndicatorLEDInternal(ctx, "Systems")
if err != nil {
return false, errors.Wrap(err, "r.GetIndicatorLEDInternal")
}
if strings.EqualFold(val, "Off") {
return false, nil
} else {
return true, nil
}
}
func (r *SILORefishApi) SetIndicatorLED(ctx context.Context, on bool) error {
valStr := "Off"
if on {
valStr = "Lit"
}
return r.SetIndicatorLEDInternal(ctx, "Systems", valStr)
}
func (r *SILORefishApi) GetNTPConf(ctx context.Context) (redfish.SNTPConf, error) {
ntpConf := redfish.SNTPConf{}
path, _, err := r.GetResource(ctx, "Managers", "0")
if err != nil {
return ntpConf, errors.Wrap(err, "r.GetResource Managers 0")
}
dateUrl := httputils.JoinPath(path, "DateTime")
resp, err := r.Get(ctx, dateUrl)
if err != nil {
return ntpConf, errors.Wrapf(err, "r.Get %s", dateUrl)
}
if r.IsDebug {
log.Debugf("%s", resp.PrettyString())
}
ntpSrvs, _ := jsonutils.GetStringArray(resp, "StaticNTPServers")
tz, _ := resp.GetString("TimeZone", "Name")
ntpConf.NTPServers = make([]string, 0)
for _, ntpsrv := range ntpSrvs {
if len(ntpsrv) > 0 {
ntpConf.NTPServers = append(ntpConf.NTPServers, ntpsrv)
}
}
if len(ntpConf.NTPServers) > 0 {
ntpConf.ProtocolEnabled = true
}
ntpConf.TimeZone = tz
return ntpConf, nil
}
func (r *SILORefishApi) SetNTPConf(ctx context.Context, conf redfish.SNTPConf) error {
path, _, err := r.GetResource(ctx, "Managers", "0")
if err != nil {
return errors.Wrap(err, "r.GetResource Managers 0")
}
if len(conf.NTPServers) > 2 {
conf.NTPServers = conf.NTPServers[:2]
}
dateUrl := httputils.JoinPath(path, "DateTime")
params := jsonutils.NewDict()
params.Add(jsonutils.NewStringArray(conf.NTPServers), "StaticNTPServers")
params.Add(jsonutils.NewString(conf.TimeZone), "TimeZone", "Name")
resp, err := r.Patch(ctx, dateUrl, params)
if err != nil {
return errors.Wrapf(err, "r.Patch %s", dateUrl)
}
if r.IsDebug {
log.Debugf("%s", resp.PrettyString())
}
return nil
}
func (r *SILORefishApi) GetConsoleJNLP(ctx context.Context) (string, error) {
bmc := bmconsole.NewBMCConsole(r.GetHost(), r.GetUsername(), r.GetPassword(), r.IsDebug)
return bmc.GetIloConsoleJNLP(ctx)
}
func (r *SILORefishApi) MountVirtualCdrom(ctx context.Context, path string, cdromUrl string, boot bool) error {
info := jsonutils.NewDict()
info.Set("Image", jsonutils.NewString(cdromUrl))
if boot {
cdInfo, err := r.Get(ctx, path)
if err != nil {
return errors.Wrapf(err, "Get %s", path)
}
var oemKey string
_, err = cdInfo.Bool("Oem", "Hp", "BootOnNextServerReset")
if err != nil {
_, err = cdInfo.Bool("Oem", "Hpe", "BootOnNextServerReset")
if err != nil {
return errors.Wrap(err, "no BootOnNextServerReset found???")
} else {
oemKey = "Hpe"
}
} else {
oemKey = "Hp"
}
info.Add(jsonutils.JSONTrue, "Oem", oemKey, "BootOnNextServerReset")
}
resp, err := r.Patch(ctx, path, info)
if err != nil {
return errors.Wrap(err, "r.Patch")
}
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/"
}