mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 10:46:58 +08:00
Merge pull request #5970 from swordqiu/automated-cherry-pick-of-#5966-upstream-release-3.2
Automated cherry pick of #5966: supermicro redfish support
This commit is contained in:
@@ -1949,6 +1949,8 @@ func (b *SBaremetalInstance) GetConsoleJNLP(ctx context.Context) (string, error)
|
||||
return bmc.GetIloConsoleJNLP(ctx)
|
||||
case "dell", "dell inc.":
|
||||
return bmc.GetIdracConsoleJNLP(ctx, "", "")
|
||||
case "supermicro":
|
||||
return bmc.GetSupermicroConsoleJNLP(ctx)
|
||||
}
|
||||
return "", httperrors.NewNotImplementedError("Unsupported manufacture %s", manufacture)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/timeutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
)
|
||||
|
||||
func (r *SBMCConsole) GetSupermicroConsoleJNLP(ctx context.Context) (string, error) {
|
||||
loginData := strings.Join([]string{
|
||||
"name=" + url.QueryEscape(r.username),
|
||||
"pwd=" + url.QueryEscape(r.password),
|
||||
}, "&")
|
||||
|
||||
// cookie:
|
||||
// -http-session-=::http.session::0103fd02ceac2d642361b6fdcd4a5994;
|
||||
// sysidledicon=ledIcon%20grayLed;
|
||||
// tokenvalue=478be97abdaeb4d454c0418fcca9094d
|
||||
|
||||
cookies := make(map[string]string)
|
||||
|
||||
// first do html login
|
||||
postHdr := http.Header{}
|
||||
postHdr.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
setCookieHeader(postHdr, cookies)
|
||||
hdr, _, err := r.RawRequest(ctx, httputils.POST, "/cgi/login.cgi", postHdr, []byte(loginData))
|
||||
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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getHdr := http.Header{}
|
||||
setCookieHeader(getHdr, cookies)
|
||||
|
||||
now := time.Now()
|
||||
// fwtype=255&time_stamp=Thu%20Apr%2023%202020%2002%3A25%3A13%20GMT%2B0800%20(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)&_=
|
||||
loginData = strings.Join([]string{
|
||||
"fwtype=255",
|
||||
"time_stamp=" + url.QueryEscape(timeutils.RFC2882Time(now)),
|
||||
"_=",
|
||||
}, "&")
|
||||
|
||||
_, rspBody, err := r.RawRequest(ctx, httputils.POST, "/cgi/upgrade_process.cgi", getHdr, []byte(loginData))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "r.RawPost %s", loginData)
|
||||
}
|
||||
|
||||
if r.isDebug {
|
||||
log.Debugf("upgrade_process.cgi %s", rspBody)
|
||||
}
|
||||
|
||||
_, rspBody, err = r.RawRequest(ctx, httputils.GET, "/cgi/url_redirect.cgi?url_name=ikvm&url_type=jwsk", getHdr, nil)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "r.RawGet")
|
||||
}
|
||||
|
||||
return string(rspBody), nil
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
_ "yunion.io/x/onecloud/pkg/util/redfish/idrac"
|
||||
_ "yunion.io/x/onecloud/pkg/util/redfish/idrac9"
|
||||
_ "yunion.io/x/onecloud/pkg/util/redfish/ilo"
|
||||
_ "yunion.io/x/onecloud/pkg/util/redfish/supermicro"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
+35
-24
@@ -78,7 +78,11 @@ func (r *SBaseRedfishClient) GetHost() string {
|
||||
log.Errorf("urlParse %s fail %s", r.endpoint, err)
|
||||
return ""
|
||||
}
|
||||
return parts.Hostname()
|
||||
ports := parts.Port()
|
||||
if len(ports) == 0 {
|
||||
return parts.Hostname()
|
||||
}
|
||||
return parts.Hostname() + ":" + parts.Port()
|
||||
}
|
||||
|
||||
func (r *SBaseRedfishClient) GetUsername() string {
|
||||
@@ -421,32 +425,36 @@ func (r *SBaseRedfishClient) GetSystemInfo(ctx context.Context) (string, SSystem
|
||||
sysInfo.ResetTypeSupported[i] = resetTypeStr
|
||||
}
|
||||
|
||||
nicListPath, _ := resp.GetString("EthernetInterfaces", "@odata.id")
|
||||
nicListInfo, err := r.Get(ctx, nicListPath)
|
||||
nicListPath, err := resp.GetString("EthernetInterfaces", "@odata.id")
|
||||
if err != nil {
|
||||
return path, sysInfo, errors.Wrap(err, "Get EthernetInterfaces fail")
|
||||
}
|
||||
nicList, _ := nicListInfo.GetArray("Members")
|
||||
if len(nicList) > 0 {
|
||||
sysInfo.EthernetNICs = make([]string, len(nicList))
|
||||
for i := range nicList {
|
||||
nicPath, _ := nicList[i].GetString("@odata.id")
|
||||
nicInfo, err := r.Get(ctx, nicPath)
|
||||
if err != nil {
|
||||
return path, sysInfo, errors.Wrapf(err, "Get EthernetInterface[%d] error", i)
|
||||
}
|
||||
var macAddr string
|
||||
for _, key := range []string{
|
||||
"MacAddress",
|
||||
"MACAddress",
|
||||
"PermanentMACAddress",
|
||||
} {
|
||||
macAddr, _ = nicInfo.GetString(key)
|
||||
if len(macAddr) > 0 {
|
||||
break
|
||||
log.Warningf("no EthernetInterfaces info")
|
||||
} else {
|
||||
nicListInfo, err := r.Get(ctx, nicListPath)
|
||||
if err != nil {
|
||||
return path, sysInfo, errors.Wrap(err, "Get EthernetInterfaces fail")
|
||||
}
|
||||
nicList, _ := nicListInfo.GetArray("Members")
|
||||
if len(nicList) > 0 {
|
||||
sysInfo.EthernetNICs = make([]string, len(nicList))
|
||||
for i := range nicList {
|
||||
nicPath, _ := nicList[i].GetString("@odata.id")
|
||||
nicInfo, err := r.Get(ctx, nicPath)
|
||||
if err != nil {
|
||||
return path, sysInfo, errors.Wrapf(err, "Get EthernetInterface[%d] error", i)
|
||||
}
|
||||
var macAddr string
|
||||
for _, key := range []string{
|
||||
"MacAddress",
|
||||
"MACAddress",
|
||||
"PermanentMACAddress",
|
||||
} {
|
||||
macAddr, _ = nicInfo.GetString(key)
|
||||
if len(macAddr) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
sysInfo.EthernetNICs[i] = netutils.FormatMacAddr(macAddr)
|
||||
}
|
||||
sysInfo.EthernetNICs[i] = netutils.FormatMacAddr(macAddr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,6 +550,9 @@ func (r *SBaseRedfishClient) readLogs(ctx context.Context, path string, subsys s
|
||||
// }
|
||||
// return nil, errors.Wrap(err, path)
|
||||
}
|
||||
if r.IsDebug {
|
||||
log.Debugf("readLogs %s", resp.PrettyString())
|
||||
}
|
||||
tmpEvents := make([]SEvent, 0)
|
||||
err = resp.Unmarshal(&tmpEvents, r.IRedfishDriver().LogItemsKey())
|
||||
if err != nil {
|
||||
|
||||
@@ -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 supermicro // import "yunion.io/x/onecloud/pkg/util/redfish/supermicro"
|
||||
@@ -0,0 +1,252 @@
|
||||
// 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 supermicro
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/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 SSupermicroRedfishApiFactory struct {
|
||||
}
|
||||
|
||||
func (f *SSupermicroRedfishApiFactory) Name() string {
|
||||
return "Supermicro"
|
||||
}
|
||||
|
||||
func (f *SSupermicroRedfishApiFactory) NewApi(endpoint, username, password string, debug bool) redfish.IRedfishDriver {
|
||||
return NewSupermicroRedfishApi(endpoint, username, password, debug)
|
||||
}
|
||||
|
||||
func init() {
|
||||
redfish.RegisterApiFactory(&SSupermicroRedfishApiFactory{})
|
||||
}
|
||||
|
||||
type SSupermicroRefishApi struct {
|
||||
generic.SGenericRefishApi
|
||||
}
|
||||
|
||||
func NewSupermicroRedfishApi(endpoint, username, password string, debug bool) redfish.IRedfishDriver {
|
||||
api := &SSupermicroRefishApi{
|
||||
SGenericRefishApi: generic.SGenericRefishApi{
|
||||
SBaseRedfishClient: redfish.NewBaseRedfishClient(endpoint, username, password, debug),
|
||||
},
|
||||
}
|
||||
api.SetVirtualObject(api)
|
||||
return api
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) ParseRoot(root jsonutils.JSONObject) error {
|
||||
if root.Contains("UUID") && root.Contains("UpdateService") && root.Contains("Oem") {
|
||||
return nil
|
||||
}
|
||||
return errors.Error("not iDrac")
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) GetVirtualCdromJSON(ctx context.Context) (string, jsonutils.JSONObject, error) {
|
||||
_, resp, err := r.GetResource(ctx, "Managers", "0", "VirtualMedia")
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(err, "r.GetResource")
|
||||
}
|
||||
log.Debugf("resp %s", resp)
|
||||
path, err := resp.GetString("Oem", "Supermicro", "VirtualMediaConfig", "@odata.id")
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(err, "GetString Oem Supermicro VirtualMediaConfig @odata.id")
|
||||
}
|
||||
cdResp, err := r.Get(ctx, path)
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(err, "Get Cdrom info fail")
|
||||
}
|
||||
if cdResp != nil {
|
||||
return path, cdResp, nil
|
||||
}
|
||||
return "", nil, httperrors.ErrNotFound
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) GetVirtualCdromInfo(ctx context.Context) (string, redfish.SCdromInfo, error) {
|
||||
cdInfo := redfish.SCdromInfo{}
|
||||
path, jsonResp, err := r.GetVirtualCdromJSON(ctx)
|
||||
if err != nil {
|
||||
return "", cdInfo, errors.Wrap(err, "r.GetVirtualCdromJSON")
|
||||
}
|
||||
imgPath, _ := jsonResp.GetString("Image")
|
||||
if imgPath == "null" {
|
||||
imgPath = ""
|
||||
}
|
||||
cdInfo.Image = imgPath
|
||||
if jsonResp.Contains("Actions") {
|
||||
cdInfo.SupportAction = true
|
||||
}
|
||||
return path, cdInfo, nil
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) MountVirtualCdrom(ctx context.Context, path string, cdromUrl string, boot bool) error {
|
||||
info := jsonutils.NewDict()
|
||||
info.Set("Image", jsonutils.NewString(cdromUrl))
|
||||
|
||||
path = httputils.JoinPath(path, "Actions/VirtualMedia.InsertMedia")
|
||||
_, _, err := r.Post(ctx, path, info)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "r.Post")
|
||||
}
|
||||
// log.Debugf("%s", resp.PrettyString())
|
||||
if boot {
|
||||
err = r.SetNextBootVirtualCdrom(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "r.SetNextBootVirtualCdrom")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) UmountVirtualCdrom(ctx context.Context, path string) error {
|
||||
info := jsonutils.NewDict()
|
||||
|
||||
path = httputils.JoinPath(path, "Actions/VirtualMedia.EjectMedia")
|
||||
_, _, err := r.Post(ctx, path, info)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "r.Post")
|
||||
}
|
||||
// log.Debugf("%s", resp.PrettyString())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) GetConsoleJNLP(ctx context.Context) (string, error) {
|
||||
bmc := bmconsole.NewBMCConsole(r.GetHost(), r.GetUsername(), r.GetPassword(), r.IsDebug)
|
||||
return bmc.GetSupermicroConsoleJNLP(ctx)
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) ReadSystemLogs(ctx context.Context, since time.Time) ([]redfish.SEvent, error) {
|
||||
_, resp, err := r.GetResource(ctx, "Managers", "0", "LogServices", "0", "Entries")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetResource Managers 0 LogServices 0 Entries")
|
||||
}
|
||||
members, err := resp.GetArray("Members")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "find Members")
|
||||
}
|
||||
events := make([]redfish.SEvent, 0)
|
||||
for i := range members {
|
||||
path, _ := members[i].GetString("@odata.id")
|
||||
resp, err := r.Get(ctx, path)
|
||||
if err != nil {
|
||||
log.Errorf("Get url for event %s error %s", path, err)
|
||||
continue
|
||||
}
|
||||
event := redfish.SEvent{}
|
||||
err = resp.Unmarshal(&event)
|
||||
if err == nil {
|
||||
event.Type, _ = resp.GetString("EntryType")
|
||||
event.Severity, _ = resp.GetString("EntryCode")
|
||||
events = append(events, event)
|
||||
} else {
|
||||
log.Errorf("unmarshal event fail %s %s", resp, err)
|
||||
}
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) ReadManagerLogs(ctx context.Context, since time.Time) ([]redfish.SEvent, error) {
|
||||
return nil, httperrors.ErrNotSupported
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) GetClearSystemLogsPath() string {
|
||||
return "/redfish/v1/Managers/1/LogServices/Log1/Actions/LogService.Reset"
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) GetClearManagerLogsPath() string {
|
||||
return "/redfish/v1/Managers/1/LogServices/Log1/Actions/LogService.Reset"
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) GetPowerPath() string {
|
||||
return "/redfish/v1/Chassis/1/Power"
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) GetThermalPath() string {
|
||||
return "/redfish/v1/Chassis/1/Thermal"
|
||||
}
|
||||
|
||||
type sNtpConf struct {
|
||||
NTPEnable bool `json:"NTPEnable"`
|
||||
PrimaryNTPServer string `json:"PrimaryNTPServer"`
|
||||
SecondaryNTPServer string `json:"SecondaryNTPServer"`
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) GetNTPConf(ctx context.Context) (redfish.SNTPConf, error) {
|
||||
ntpConf := redfish.SNTPConf{}
|
||||
_, resp, err := r.GetResource(ctx, "Managers", "0")
|
||||
if err != nil {
|
||||
return ntpConf, errors.Wrap(err, "GetResource")
|
||||
}
|
||||
path, err := resp.GetString("Oem", "Supermicro", "NTP", "@odata.id")
|
||||
if err != nil {
|
||||
return ntpConf, errors.Wrap(err, "GetString \"Oem\", \"Supermicro\", \"NTP\", \"@odata.id\"")
|
||||
}
|
||||
resp, err = r.Get(ctx, path)
|
||||
if err != nil {
|
||||
return ntpConf, errors.Wrapf(err, "Get %s fail %s", path, err)
|
||||
}
|
||||
conf := sNtpConf{}
|
||||
err = resp.Unmarshal(&conf)
|
||||
if err != nil {
|
||||
return ntpConf, errors.Wrap(err, "resp.Unmarshal NTP")
|
||||
}
|
||||
ntpConf.ProtocolEnabled = conf.NTPEnable
|
||||
if len(conf.PrimaryNTPServer) > 0 {
|
||||
ntpConf.NTPServers = append(ntpConf.NTPServers, conf.PrimaryNTPServer)
|
||||
}
|
||||
if len(conf.SecondaryNTPServer) > 0 {
|
||||
ntpConf.NTPServers = append(ntpConf.NTPServers, conf.SecondaryNTPServer)
|
||||
}
|
||||
return ntpConf, nil
|
||||
}
|
||||
|
||||
func (r *SSupermicroRefishApi) SetNTPConf(ctx context.Context, conf redfish.SNTPConf) error {
|
||||
_, resp, err := r.GetResource(ctx, "Managers", "0")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetResource")
|
||||
}
|
||||
path, err := resp.GetString("Oem", "Supermicro", "NTP", "@odata.id")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetString \"Oem\", \"Supermicro\", \"NTP\", \"@odata.id\"")
|
||||
}
|
||||
nconf := sNtpConf{
|
||||
NTPEnable: conf.ProtocolEnabled,
|
||||
}
|
||||
if len(conf.NTPServers) > 0 {
|
||||
nconf.PrimaryNTPServer = conf.NTPServers[0]
|
||||
}
|
||||
if len(conf.NTPServers) > 1 {
|
||||
nconf.SecondaryNTPServer = conf.NTPServers[1]
|
||||
}
|
||||
resp, err = r.Patch(ctx, path, jsonutils.Marshal(nconf))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "r.Patch")
|
||||
}
|
||||
if r.IsDebug {
|
||||
log.Debugf("%s", resp.PrettyString())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user