From 0659981e240fde15c03733f93debc12f2501d43e Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 9 Apr 2020 12:44:53 +0800 Subject: [PATCH] feature: redfish support IDRAC9 baremetal --- pkg/baremetal/cronjobs.go | 9 +-- pkg/baremetal/manager.go | 13 +++- pkg/baremetal/tasks/baseprepare.go | 4 +- pkg/compute/models/hosts.go | 2 +- pkg/util/redfish/bmconsole/idrac9.go | 70 ++++++++++++++++++ pkg/util/redfish/idrac/idrac.go | 34 +++++++-- pkg/util/redfish/idrac9/doc.go | 15 ++++ pkg/util/redfish/idrac9/idrac.go | 104 +++++++++++++++++++++++++++ pkg/util/redfish/loader/loader.go | 1 + pkg/util/redfish/redfish.go | 10 +-- 10 files changed, 247 insertions(+), 15 deletions(-) create mode 100644 pkg/util/redfish/bmconsole/idrac9.go create mode 100644 pkg/util/redfish/idrac9/doc.go create mode 100644 pkg/util/redfish/idrac9/idrac.go diff --git a/pkg/baremetal/cronjobs.go b/pkg/baremetal/cronjobs.go index d9b651f6d0..5caaba7636 100644 --- a/pkg/baremetal/cronjobs.go +++ b/pkg/baremetal/cronjobs.go @@ -189,10 +189,11 @@ func (job *SLogFetchJob) Do(ctx context.Context, now time.Time) error { 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") - } + // no longer fetch management logs + // 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 } diff --git a/pkg/baremetal/manager.go b/pkg/baremetal/manager.go index 14d3625a66..978f75d552 100644 --- a/pkg/baremetal/manager.go +++ b/pkg/baremetal/manager.go @@ -1235,7 +1235,12 @@ func (b *SBaremetalInstance) enableWire(mac net.HardwareAddr, ipAddr string, nic func (b *SBaremetalInstance) GetIPMIConfig() *types.SIPMIInfo { conf := b.GetRawIPMIConfig() - if conf == nil || conf.Password == "" { + if conf == nil { + log.Debugf("GetIPMIConfig conf is nil") + return nil + } + if conf.Password == "" { + log.Debugf("GetIPMIConfig password is nil") return nil } if conf.Username == "" { @@ -1254,6 +1259,7 @@ func (b *SBaremetalInstance) GetIPMIConfig() *types.SIPMIInfo { } conf.Password = utils.Unquote(conf.Password) // XXX: remove quotes!!! if conf.IpAddr == "" { + log.Debugf("GetIPMIConfig ipaddr s nil") return nil } return conf @@ -1336,6 +1342,7 @@ func (b *SBaremetalInstance) SetExistingIPMIIPAddr(ipAddr string) { func (b *SBaremetalInstance) GetIPMITool() *ipmitool.LanPlusIPMI { conf := b.GetIPMIConfig() if conf == nil { + log.Debugf("GetIPMIConfig is nil") return nil } return ipmitool.NewLanPlusIPMI(conf.IpAddr, conf.Username, conf.Password) @@ -1632,6 +1639,10 @@ func (b *SBaremetalInstance) DelayedSyncIPMIInfo(data jsonutils.JSONObject) (jso } func (b *SBaremetalInstance) DelayedSyncDesc(data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if data == nil { + session := b.manager.GetClientSession() + data, _ = b.manager.fetchBaremetal(session, b.GetId()) + } err := b.SaveDesc(data) return nil, err } diff --git a/pkg/baremetal/tasks/baseprepare.go b/pkg/baremetal/tasks/baseprepare.go index 2d3bd4d9d2..8a60fe34cb 100644 --- a/pkg/baremetal/tasks/baseprepare.go +++ b/pkg/baremetal/tasks/baseprepare.go @@ -108,7 +108,9 @@ func (task *sBaremetalPrepareTask) prepareBaremetalInfo(cli *ssh.Client) (*barem if len(raidDiskInfo) > 0 { raidDrivers := []string{} for _, drv := range raidDiskInfo { - raidDrivers = append(raidDrivers, drv.Driver) + if !utils.IsInStringArray(drv.Driver, raidDrivers) { + raidDrivers = append(raidDrivers, drv.Driver) + } } storageDriver = strings.Join(raidDrivers, ",") } else { diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 5377cf04a2..b4e313adba 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -99,7 +99,7 @@ type SHost struct { // SN信息 SN string `width:"128" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` - // CPU大小 + // CPU核数 CpuCount int `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // 物理CPU颗数 NodeCount int8 `nullable:"true" list:"admin" update:"admin" create:"admin_optional"` diff --git a/pkg/util/redfish/bmconsole/idrac9.go b/pkg/util/redfish/bmconsole/idrac9.go new file mode 100644 index 0000000000..cd90b88154 --- /dev/null +++ b/pkg/util/redfish/bmconsole/idrac9.go @@ -0,0 +1,70 @@ +// 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 bmconsole + +import ( + "context" + "net/http" + "strings" + + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/util/httputils" +) + +const ( + IDRAC9_LOGIN_URL = "/sysmgmt/2015/bmc/session" + IDRAC9_VCONSOLE_JAVA = "/sysmgmt/2015/server/vconsole?type=Java" +) + +func (r *SBMCConsole) GetIdrac9ConsoleJNLP(ctx context.Context) (string, error) { + cookies := make(map[string]string) + cookies["-http-session-"] = "" + + // first do html login + postHdr := http.Header{} + postHdr.Set("password", r.password) + postHdr.Set("user", r.username) + postHdr.Set("Content-Length", "0") + setCookieHeader(postHdr, cookies) + hdr, _, err := r.RawRequest(ctx, httputils.POST, IDRAC9_LOGIN_URL, postHdr, nil) + if err != nil { + return "", errors.Wrap(err, "r.FormPost Login") + } + for _, cookieHdr := range hdr["Set-Cookie"] { + parts := strings.Split(cookieHdr, ";") + if len(parts) > 0 { + pparts := strings.Split(parts[0], "=") + if len(pparts) > 1 { + cookies[pparts[0]] = pparts[1] + } + } + } + // XSRF-TOKEN: a636651fcc8841a3c146cea89730389c + xsrfToken := hdr.Get("Xsrf-Token") + + getHdr := http.Header{} + setCookieHeader(getHdr, cookies) + getHdr.Set("Xsrf-Token", xsrfToken) + getHdr.Set("Sec-Fetch-Dest", "empty") + getHdr.Set("Sec-Fetch-Mode", "cors") + getHdr.Set("Sec-Fetch-Site", "same-origin") + _, rspBody, err := r.RawRequest(ctx, httputils.GET, IDRAC9_VCONSOLE_JAVA, getHdr, nil) + if err != nil { + return "", errors.Wrap(err, "r.RawGet") + } + + return string(rspBody), nil +} diff --git a/pkg/util/redfish/idrac/idrac.go b/pkg/util/redfish/idrac/idrac.go index ac4399db2a..dfc9ac4954 100644 --- a/pkg/util/redfish/idrac/idrac.go +++ b/pkg/util/redfish/idrac/idrac.go @@ -253,9 +253,22 @@ func (r *SIDracRefishApi) fetchExportConfig(ctx context.Context, target string) if err != nil { return nil, errors.Wrap(err, "GetResource") } - urlPath, err := manager.GetString("Actions", "Oem", "OemManager.v1_1_0#OemManager.ExportSystemConfiguration", "target") + oemJson, err := manager.GetMap("Actions", "Oem") if err != nil { - return nil, errors.Wrap(err, "OemManager.v1_1_0#OemManager.ExportSystemConfiguration") + return nil, errors.Wrap(err, "GetMap Actions Oem") + } + var urlPath string + for k, conf := range oemJson { + if strings.HasSuffix(k, "OemManager.ExportSystemConfiguration") { + urlPath, err = conf.GetString("target") + if err != nil { + return nil, errors.Wrap(err, "find OemManager.ExportSystemConfiguration target") + } + break + } + } + if len(urlPath) == 0 { + return nil, errors.Wrap(httperrors.ErrNotFound, "Key OemManager.ExportSystemConfiguration not found") } params := jsonutils.NewDict() params.Add(jsonutils.NewString("JSON"), "ExportFormat") @@ -299,9 +312,22 @@ func (r *SIDracRefishApi) doImportConfig(ctx context.Context, conf iDRACConfig) if err != nil { return errors.Wrap(err, "GetResource") } - urlPath, err := manager.GetString("Actions", "Oem", "OemManager.v1_1_0#OemManager.ImportSystemConfiguration", "target") + oemJson, err := manager.GetMap("Actions", "Oem") if err != nil { - return errors.Wrap(err, "OemManager.v1_1_0#OemManager.ImportSystemConfiguration") + return errors.Wrap(err, "GetMap Actions Oem") + } + var urlPath string + for k, conf := range oemJson { + if strings.HasSuffix(k, "OemManager.ImportSystemConfiguration") { + urlPath, err = conf.GetString("target") + if err != nil { + return errors.Wrap(err, "OemManager.ImportSystemConfiguration target") + } + break + } + } + if len(urlPath) == 0 { + return errors.Wrap(httperrors.ErrNotFound, "Key OemManager.ImportSystemConfiguration not found") } payload := jsonutils.NewDict() payload.Add(jsonutils.NewString(conf.toXml()), "ImportBuffer") diff --git a/pkg/util/redfish/idrac9/doc.go b/pkg/util/redfish/idrac9/doc.go new file mode 100644 index 0000000000..5dd30b53c7 --- /dev/null +++ b/pkg/util/redfish/idrac9/doc.go @@ -0,0 +1,15 @@ +// 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 idrac9 // import "yunion.io/x/onecloud/pkg/util/redfish/idrac9" diff --git a/pkg/util/redfish/idrac9/idrac.go b/pkg/util/redfish/idrac9/idrac.go new file mode 100644 index 0000000000..0f673409cc --- /dev/null +++ b/pkg/util/redfish/idrac9/idrac.go @@ -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 idrac9 + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/util/redfish" + "yunion.io/x/onecloud/pkg/util/redfish/bmconsole" + "yunion.io/x/onecloud/pkg/util/redfish/generic" + "yunion.io/x/onecloud/pkg/util/redfish/idrac" +) + +type SIDrac9RedfishApiFactory struct { +} + +func (f *SIDrac9RedfishApiFactory) Name() string { + return "iDRAC9" +} + +func (f *SIDrac9RedfishApiFactory) NewApi(endpoint, username, password string, debug bool) redfish.IRedfishDriver { + return NewIDrac9RedfishApi(endpoint, username, password, debug) +} + +func init() { + redfish.RegisterApiFactory(&SIDrac9RedfishApiFactory{}) +} + +type SIDrac9RefishApi struct { + idrac.SIDracRefishApi +} + +func NewIDrac9RedfishApi(endpoint, username, password string, debug bool) redfish.IRedfishDriver { + api := &SIDrac9RefishApi{ + SIDracRefishApi: idrac.SIDracRefishApi{ + SGenericRefishApi: generic.SGenericRefishApi{ + SBaseRedfishClient: redfish.NewBaseRedfishClient(endpoint, username, password, debug), + }, + }, + } + api.SetVirtualObject(api) + return api +} + +func (r *SIDrac9RefishApi) ParseRoot(root jsonutils.JSONObject) error { + // log.Debugf("ParseRoot %s", root.PrettyString()) + oem, _ := root.Get("Oem", "Dell") + if oem != nil { + return nil + } + return errors.Error("not iDrac") +} + +func (r *SIDrac9RefishApi) GetNTPConf(ctx context.Context) (redfish.SNTPConf, error) { + return r.SGenericRefishApi.GetNTPConf(ctx) +} + +func (r *SIDrac9RefishApi) SetNTPConf(ctx context.Context, conf redfish.SNTPConf) error { + return r.SGenericRefishApi.SetNTPConf(ctx, conf) +} + +func (r *SIDrac9RefishApi) GetConsoleJNLP(ctx context.Context) (string, error) { + bmc := bmconsole.NewBMCConsole(r.GetHost(), r.GetUsername(), r.GetPassword(), r.IsDebug) + return bmc.GetIdrac9ConsoleJNLP(ctx) +} + +func (r *SIDrac9RefishApi) GetSystemLogsPath() string { + return "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Entries" +} + +func (r *SIDrac9RefishApi) GetManagerLogsPath() string { + return "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Lclog/Entries" +} + +func (r *SIDrac9RefishApi) GetClearSystemLogsPath() string { + return "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Actions/LogService.ClearLog" +} + +func (r *SIDrac9RefishApi) GetClearManagerLogsPath() string { + return "/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Lclog/Actions/LogService.ClearLog" +} + +func (r *SIDrac9RefishApi) GetPowerPath() string { + return "/redfish/v1/Chassis/System.Embedded.1/Power" +} + +func (r *SIDrac9RefishApi) GetThermalPath() string { + return "/redfish/v1/Chassis/System.Embedded.1/Thermal" +} diff --git a/pkg/util/redfish/loader/loader.go b/pkg/util/redfish/loader/loader.go index 74815c8b92..775cdc11fd 100644 --- a/pkg/util/redfish/loader/loader.go +++ b/pkg/util/redfish/loader/loader.go @@ -19,6 +19,7 @@ import ( _ "yunion.io/x/onecloud/pkg/util/redfish/generic" _ "yunion.io/x/onecloud/pkg/util/redfish/idrac" + _ "yunion.io/x/onecloud/pkg/util/redfish/idrac9" _ "yunion.io/x/onecloud/pkg/util/redfish/ilo" ) diff --git a/pkg/util/redfish/redfish.go b/pkg/util/redfish/redfish.go index 47e569b057..77f2150e96 100644 --- a/pkg/util/redfish/redfish.go +++ b/pkg/util/redfish/redfish.go @@ -535,10 +535,12 @@ func (r *SBaseRedfishClient) readLogs(ctx context.Context, path string, subsys s for { resp, err := r.Get(ctx, path) if err != nil { - if httputils.ErrorCode(err) == 404 { - break - } - return nil, errors.Wrap(err, path) + log.Errorf("Get %s fail %s", path, err) + break + // if httputils.ErrorCode(err) == 404 { + // break + // } + // return nil, errors.Wrap(err, path) } tmpEvents := make([]SEvent, 0) err = resp.Unmarshal(&tmpEvents, r.IRedfishDriver().LogItemsKey())