fix: compatiblity fixes with lenovo RD620 and huawei 2288

This commit is contained in:
Qiu Jian
2020-09-03 02:43:54 +08:00
parent 7b7b3a9416
commit 6ea402b576
9 changed files with 396 additions and 26 deletions

167
cmd/raidcli/main.go Normal file
View File

@@ -0,0 +1,167 @@
// 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 main
import (
"fmt"
"os"
"yunion.io/x/structarg"
_ "yunion.io/x/onecloud/cmd/raidcli/shell"
"yunion.io/x/onecloud/pkg/baremetal/utils/raid"
"yunion.io/x/onecloud/pkg/baremetal/utils/raid/drivers"
"yunion.io/x/onecloud/pkg/util/shellutils"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type BaseOptions struct {
Debug bool `help:"debug mode"`
Help bool `help:"Show help"`
Host string `help:"SSH Host IP" default:"$RAID_HOST" metavar:"RAID_HOST"`
Username string `help:"Username, usually root" default:"$RAID_USERNAME" metavar:"RAID_USERNAME"`
Password string `help:"Password" default:"$RAID_PASSWORD" metavar:"RAID_PASSWORD"`
Driver string `help:"Password" default:"$RAID_DRIVER" metavar:"RAID_DRIVER" choices:"MegaRaid|HPSARaid|Mpt2SAS|MarvelRaid"`
SUBCOMMAND string `help:"s3cli subcommand" subcommand:"true"`
}
var (
options = &BaseOptions{}
)
func getSubcommandParser() (*structarg.ArgumentParser, error) {
parse, e := structarg.NewArgumentParser(options,
"raidcli",
"Command-line interface to test RAID drivers.",
`See "raidcli help COMMAND" for help on a specific command.`)
if e != nil {
return nil, e
}
subcmd := parse.GetSubcommand()
if subcmd == nil {
return nil, fmt.Errorf("No subcommand argument.")
}
type HelpOptions struct {
SUBCOMMAND string `help:"sub-command name"`
}
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
if e != nil {
return e
} else {
fmt.Print(helpstr)
return nil
}
})
for _, v := range shellutils.CommandTable {
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
if e != nil {
return nil, e
}
}
return parse, nil
}
func showErrorAndExit(e error) {
fmt.Fprintf(os.Stderr, "%s", e)
fmt.Fprintln(os.Stderr)
os.Exit(1)
}
func newClient() (raid.IRaidDriver, error) {
if len(options.Host) == 0 {
return nil, fmt.Errorf("Missing host")
}
if len(options.Username) == 0 {
return nil, fmt.Errorf("Missing username")
}
if len(options.Password) == 0 {
return nil, fmt.Errorf("Missing password")
}
if len(options.Driver) == 0 {
return nil, fmt.Errorf("Missing driver")
}
if options.Debug {
raid.Debug = true
}
sshClient, err := ssh.NewClient(
options.Host,
22,
options.Username,
options.Password,
"",
)
if err != nil {
return nil, fmt.Errorf("ssh client init fail: %s", err)
}
drv := drivers.GetDriver(options.Driver, sshClient)
if drv == nil {
return nil, fmt.Errorf("not supported driver %s", options.Driver)
}
err = drv.ParsePhyDevs()
if err != nil {
return nil, fmt.Errorf("parse phyical devices error %s", err)
}
return drv, nil
}
func main() {
parser, e := getSubcommandParser()
if e != nil {
showErrorAndExit(e)
}
e = parser.ParseArgs(os.Args[1:], false)
// options := parser.Options().(*BaseOptions)
if options.Help {
fmt.Print(parser.HelpString())
} else {
subcmd := parser.GetSubcommand()
subparser := subcmd.GetSubParser()
if e != nil {
if subparser != nil {
fmt.Print(subparser.Usage())
} else {
fmt.Print(parser.Usage())
}
showErrorAndExit(e)
} else {
suboptions := subparser.Options()
if options.SUBCOMMAND == "help" {
e = subcmd.Invoke(suboptions)
} else {
var client raid.IRaidDriver
client, e = newClient()
if e != nil {
showErrorAndExit(e)
}
e = subcmd.Invoke(client, suboptions)
}
if e != nil {
showErrorAndExit(e)
}
}
}
}

37
cmd/raidcli/shell/raid.go Normal file
View File

@@ -0,0 +1,37 @@
// 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 (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/baremetal/utils/raid"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type ShowOptions struct {
}
shellutils.R(&ShowOptions{}, "show", "show raid info", func(drv raid.IRaidDriver, args *ShowOptions) error {
adpts := drv.GetAdapters()
for i := range adpts {
fmt.Println("Index:", i)
fmt.Println(jsonutils.Marshal(adpts[i].GetDevices()).PrettyString())
}
return nil
})
}

View File

@@ -17,12 +17,16 @@ package main
import (
"context"
"fmt"
"net/url"
"os"
"strings"
"yunion.io/x/structarg"
_ "yunion.io/x/onecloud/cmd/redfishcli/shell"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/redfish"
"yunion.io/x/onecloud/pkg/util/redfish/bmconsole"
_ "yunion.io/x/onecloud/pkg/util/redfish/loader"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
@@ -36,8 +40,12 @@ type BaseOptions struct {
SUBCOMMAND string `help:"s3cli subcommand" subcommand:"true"`
}
var (
options = &BaseOptions{}
)
func getSubcommandParser() (*structarg.ArgumentParser, error) {
parse, e := structarg.NewArgumentParser(&BaseOptions{},
parse, e := structarg.NewArgumentParser(options,
"redfishcli",
"Command-line interface to redfish API.",
`See "redfishcli help COMMAND" for help on a specific command.`)
@@ -62,6 +70,7 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
return nil
}
})
bmcJnlp()
for _, v := range shellutils.CommandTable {
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
if e != nil {
@@ -71,13 +80,49 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) {
return parse, nil
}
func bmcJnlp() {
type BmcGetOptions struct {
BRAND string `help:"brand of baremetal" choices:"Lenovo|Huawei|HPE|Dell|Supermicro"`
Save string `help:"save to file"`
Debug bool `help:"turn on debug mode"`
}
shellutils.R(&BmcGetOptions{}, "bmc-jnlp", "Get Java Console JNLP file", func(args *BmcGetOptions) error {
ctx := context.Background()
parts, err := url.Parse(options.Endpoint)
if err != nil {
return err
}
bmc := bmconsole.NewBMCConsole(parts.Hostname(), options.Username, options.Password, args.Debug)
var jnlp string
switch strings.ToLower(args.BRAND) {
case "hp", "hpe":
jnlp, err = bmc.GetIloConsoleJNLP(ctx)
case "dell", "dell inc.":
jnlp, err = bmc.GetIdracConsoleJNLP(ctx, "", "")
case "supermicro":
jnlp, err = bmc.GetSupermicroConsoleJNLP(ctx)
case "lenovo":
jnlp, err = bmc.GetLenovoConsoleJNLP(ctx)
}
if err != nil {
return err
}
if len(args.Save) > 0 {
return fileutils2.FilePutContents(args.Save, jnlp, false)
} else {
fmt.Println(jnlp)
return nil
}
})
}
func showErrorAndExit(e error) {
fmt.Fprintf(os.Stderr, "%s", e)
fmt.Fprintln(os.Stderr)
os.Exit(1)
}
func newClient(options *BaseOptions) (redfish.IRedfishDriver, error) {
func newClient() (redfish.IRedfishDriver, error) {
if len(options.Endpoint) == 0 {
return nil, fmt.Errorf("Missing endpoint")
}
@@ -104,7 +149,7 @@ func main() {
showErrorAndExit(e)
}
e = parser.ParseArgs(os.Args[1:], false)
options := parser.Options().(*BaseOptions)
// options := parser.Options().(*BaseOptions)
if options.Help {
fmt.Print(parser.HelpString())
@@ -122,9 +167,11 @@ func main() {
suboptions := subparser.Options()
if options.SUBCOMMAND == "help" {
e = subcmd.Invoke(suboptions)
} else if options.SUBCOMMAND == "bmc-jnlp" {
e = subcmd.Invoke(suboptions)
} else {
var client redfish.IRedfishDriver
client, e = newClient(options)
client, e = newClient()
if e != nil {
showErrorAndExit(e)
}

View File

@@ -1953,6 +1953,8 @@ func (b *SBaremetalInstance) GetConsoleJNLP(ctx context.Context) (string, error)
return bmc.GetIdracConsoleJNLP(ctx, "", "")
case "supermicro":
return bmc.GetSupermicroConsoleJNLP(ctx)
case "lenovo":
return bmc.GetLenovoConsoleJNLP(ctx)
}
return "", httperrors.NewNotImplementedError("Unsupported manufacture %s", manufacture)
}

View File

@@ -20,9 +20,8 @@ import (
"strconv"
"strings"
"github.com/pkg/errors"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/stringutils"
"yunion.io/x/pkg/utils"
@@ -75,7 +74,7 @@ func (dev *MegaRaidPhyDev) ToBaremetalStorage(index int) *baremetal.BaremetalSto
}
func (dev *MegaRaidPhyDev) GetSize() int64 {
return dev.sector * int64(dev.block) / 1024 / 1024 // MB
return dev.sector * dev.block / 1024 / 1024 // MB
}
func (dev *MegaRaidPhyDev) parseLine(line string) bool {
@@ -124,7 +123,7 @@ func (dev *MegaRaidPhyDev) parseLine(line string) bool {
if err != nil {
log.Errorf("parse logical sector size error: %v", err)
dev.block = 512
} else {
} else if block > 0 {
dev.block = int64(block)
}
default:
@@ -189,6 +188,7 @@ type MegaRaidAdaptor struct {
raid *MegaRaid
devs []*MegaRaidPhyDev
sn string
name string
busNumber string
deviceNumber string
funcNumber string
@@ -209,6 +209,10 @@ func NewMegaRaidAdaptor(index int, raid *MegaRaid) (*MegaRaidAdaptor, error) {
return adapter, nil
}
func (adapter MegaRaidAdaptor) key() string {
return adapter.name + adapter.sn
}
func (adapter *MegaRaidAdaptor) fillInfo() error {
cmd := GetCommand("-AdpAllInfo", fmt.Sprintf("-a%d", adapter.index))
ret, err := adapter.remoteRun(cmd)
@@ -223,10 +227,12 @@ func (adapter *MegaRaidAdaptor) fillInfo() error {
switch key {
case "Serial No":
adapter.sn = val
case "Product Name":
adapter.name = val
}
}
if len(adapter.sn) == 0 {
return errors.New("Not found Serial No")
if len(adapter.key()) == 0 {
return errors.Error("Not found Serial No and Product Name")
}
return adapter.fillPCIInfo()
}
@@ -267,7 +273,7 @@ func (adapter *MegaRaidAdaptor) fillPCIInfo() error {
}
}
if len(adapter.busNumber) == 0 || len(adapter.deviceNumber) == 0 || len(adapter.funcNumber) == 0 {
return errors.New("Not found bus number")
return errors.Error("Not found bus number")
}
pciDir := fmt.Sprintf("/sys/bus/pci/devices/0000:%s:%s.%s/", adapter.busNumber, adapter.deviceNumber, adapter.funcNumber)
cmd = raiddrivers.GetCommand("ls", pciDir, "|", "grep", "host")
@@ -601,18 +607,24 @@ func (adapter *MegaRaidAdaptor) BuildNoneRaid(devs []*baremetal.BaremetalStorage
type StorcliAdaptor struct {
Controller int
SN string
sn string
name string
}
func newStorcliAdaptor() *StorcliAdaptor {
return &StorcliAdaptor{
Controller: -1,
SN: "",
sn: "",
name: "",
}
}
func (a StorcliAdaptor) key() string {
return a.name + a.sn
}
func (a *StorcliAdaptor) isComplete() bool {
return a.Controller >= 0 && a.SN != ""
return a.Controller >= 0 && a.key() != ""
}
func (a *StorcliAdaptor) parseLine(l string) {
@@ -626,13 +638,15 @@ func (a *StorcliAdaptor) parseLine(l string) {
case "Controller":
a.Controller, _ = strconv.Atoi(val)
case "Serial Number":
a.SN = val
a.sn = val
case "Product Name":
a.name = val
}
}
func (raid *MegaRaid) GetStorcliAdaptor() (map[string]*StorcliAdaptor, error) {
ret := make(map[string]*StorcliAdaptor)
cmd := GetCommand2("/call", "show", "|", "grep", "-iE", `'^(Controller|Serial Number)\s='`)
cmd := GetCommand2("/call", "show", "|", "grep", "-iE", `'^(Controller|Product Name|Serial Number)\s='`)
lines, err := raid.term.Run(cmd)
if err != nil {
return nil, errors.Wrap(err, "Get storcli adapter")
@@ -641,7 +655,7 @@ func (raid *MegaRaid) GetStorcliAdaptor() (map[string]*StorcliAdaptor, error) {
for _, l := range lines {
adapter.parseLine(l)
if adapter.isComplete() {
ret[adapter.SN] = adapter
ret[adapter.key()] = adapter
adapter = newStorcliAdaptor()
}
}
@@ -656,9 +670,9 @@ func (adapter *MegaRaidAdaptor) storcliCtrlIndex() (int, error) {
if err != nil {
return -1, errors.Wrap(err, "Get all Storcli adaptor")
}
storAdap, ok := storcliAdaps[adapter.sn]
storAdap, ok := storcliAdaps[adapter.key()]
if !ok {
return -1, errors.Errorf("Not found storcli adaptor by SN %q", adapter.sn)
return -1, errors.Errorf("Not found storcli adaptor by SN %q", adapter.key())
}
return storAdap.Controller, nil
}
@@ -914,6 +928,9 @@ func (raid *MegaRaid) ParsePhyDevs() error {
if err != nil {
return fmt.Errorf("List raid disk error: %v", err)
}
if raiddrivers.Debug {
log.Debugf("-PDList -aALL: %s", ret)
}
err = raid.parsePhyDevs(ret)
if err != nil {
return fmt.Errorf("parse physical disk device error: %v", err)

View File

@@ -28,6 +28,10 @@ import (
"yunion.io/x/onecloud/pkg/util/sysutils"
)
var (
Debug bool
)
const (
MODULE_MEGARAID = "megaraid_sas"
MODULE_HPSA = "hpsa"

View File

@@ -58,7 +58,12 @@ func (self *BaremetalDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneM
}
func (self *BaremetalDeleteTask) OnDeleteBaremetalComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
baremetal.RealDelete(ctx, self.UserCred)
err := baremetal.RealDelete(ctx, self.UserCred)
if err != nil {
log.Errorln("RealDelete fail %s", err)
self.OnFailure(ctx, baremetal, jsonutils.Marshal(err))
return
}
self.SetStageComplete(ctx, nil)
}

View File

@@ -0,0 +1,90 @@
// 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"
"fmt"
"net/http"
"net/url"
"regexp"
"strings"
"time"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/util/httputils"
)
func (r *SBMCConsole) GetLenovoConsoleJNLP(ctx context.Context) (string, error) {
loginData := strings.Join([]string{
"user=" + url.QueryEscape(r.username),
"password=" + url.QueryEscape(r.password),
}, "&")
// cookie:
// _appwebSessionId_=09eb9a178d520d2c9fa1430dd355dc27; path=/; httponly; secure
cookies := make(map[string]string)
cookies["_appwebSessionId_"] = ""
// first do html login
postHdr := http.Header{}
postHdr.Set("Content-Type", "application/x-www-form-urlencoded")
postHdr.Set("Referer", fmt.Sprintf("https://%s/login.html", r.host))
setCookieHeader(postHdr, cookies)
hdr, _, err := r.RawRequest(ctx, httputils.POST, "/data/login", 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{}
getHdr.Set("Referer", fmt.Sprintf("https://%s/bmctree.html", r.host))
setCookieHeader(getHdr, cookies)
_, launchResp, err := r.RawRequest(ctx, httputils.GET, "/vkvmLaunch.html", getHdr, nil)
if err != nil {
return "", errors.Wrap(err, "Get vkvmLauch.html")
}
var token string
st3Pattern := regexp.MustCompile(`<input type="hidden" name="ST3" value="(\w+)">`)
matched := st3Pattern.FindAllStringSubmatch(string(launchResp), -1)
if len(matched) > 0 && len(matched[0]) > 1 {
token = matched[0][1]
}
if len(token) == 0 {
return "", errors.Wrap(httperrors.ErrBadRequest, "no valid ST3 token")
}
path := fmt.Sprintf("viewer.jnlp(%s@0@%d)", r.host, time.Now().UnixNano()/1000000)
body := "ST3=" + url.QueryEscape(token)
getHdr.Set("Referer", fmt.Sprintf("https://%s/vkvmLaunch.html", r.host))
_, rspBody, err := r.RawRequest(ctx, httputils.POST, path, getHdr, []byte(body))
if err != nil {
return "", errors.Wrapf(err, "r.RawGet %s", path)
}
return string(rspBody), nil
}

View File

@@ -16,14 +16,12 @@ package bmconsole
import (
"context"
"fmt"
"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"
)
@@ -44,6 +42,7 @@ func (r *SBMCConsole) GetSupermicroConsoleJNLP(ctx context.Context) (string, err
// first do html login
postHdr := http.Header{}
postHdr.Set("Content-Type", "application/x-www-form-urlencoded")
postHdr.Set("Referer", fmt.Sprintf("http://%s/", r.host))
setCookieHeader(postHdr, cookies)
hdr, _, err := r.RawRequest(ctx, httputils.POST, "/cgi/login.cgi", postHdr, []byte(loginData))
if err != nil {
@@ -60,11 +59,12 @@ func (r *SBMCConsole) GetSupermicroConsoleJNLP(ctx context.Context) (string, err
}
getHdr := http.Header{}
getHdr.Set("Referer", fmt.Sprintf("https://%s/cgi/url_redirect.cgi?url_name=man_ikvm", r.host))
setCookieHeader(getHdr, cookies)
now := time.Now()
// 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{
/*loginData = strings.Join([]string{
"fwtype=255",
"time_stamp=" + url.QueryEscape(timeutils.RFC2882Time(now)),
"_=",
@@ -78,8 +78,9 @@ func (r *SBMCConsole) GetSupermicroConsoleJNLP(ctx context.Context) (string, err
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)
_, 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")
}