| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 |
- // 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 ipmitool
- import (
- "context"
- "fmt"
- "net"
- "strconv"
- "strings"
- "time"
- "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"
- "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/cloudcommon/types"
- "yunion.io/x/onecloud/pkg/util/procutils"
- "yunion.io/x/onecloud/pkg/util/ssh"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
- "yunion.io/x/onecloud/pkg/util/sysutils"
- )
- type IPMIParser struct{}
- func (parser *IPMIParser) GetDefaultTimeout() time.Duration {
- return 20 * time.Second
- }
- var (
- BOOTDEVS = []string{"pxe", "disk", "safe", "diag", "cdrom", "bios"}
- SOLOPTS = []string{"default", "skip", "enable"}
- )
- type Args []string
- func newArgs(args ...interface{}) Args {
- ret := make([]string, len(args))
- for i, arg := range args {
- ret[i] = fmt.Sprintf("%v", arg)
- }
- return ret
- }
- type IPMIExecutor interface {
- GetMode() string
- ExecuteCommand(args ...string) ([]string, error)
- }
- type SSHIPMI struct {
- IPMIParser
- sshClient *ssh.Client
- }
- func NewSSHIPMI(cli *ssh.Client) *SSHIPMI {
- return &SSHIPMI{
- sshClient: cli,
- }
- }
- func (ipmi *SSHIPMI) GetMode() string {
- return "ssh"
- }
- func (ipmi *SSHIPMI) GetCommand(args ...string) *procutils.Command {
- nArgs := []string{"-I", "open"}
- nArgs = append(nArgs, args...)
- return procutils.NewCommand("/usr/bin/ipmitool", nArgs...)
- }
- func (ipmi *SSHIPMI) ExecuteCommand(args ...string) ([]string, error) {
- cmd := ipmi.GetCommand(args...)
- log.Debugf("[SSHIPMI] execute command: %s", cmd)
- return ipmi.sshClient.Run(cmd.String())
- }
- type LanPlusIPMI struct {
- IPMIParser
- host string
- user string
- password string
- port int
- }
- func NewLanPlusIPMI(host, user, password string) *LanPlusIPMI {
- return NewLanPlusIPMIWithPort(host, user, password, 623)
- }
- func NewLanPlusIPMIWithPort(host, user, password string, port int) *LanPlusIPMI {
- return &LanPlusIPMI{
- host: host,
- user: user,
- password: password,
- port: port,
- }
- }
- func (ipmi *LanPlusIPMI) GetMode() string {
- return "rmcp"
- }
- func (ipmi *LanPlusIPMI) GetCommand(args ...string) (*procutils.Command, context.CancelFunc) {
- nArgs := []string{
- "-I", "lanplus", "-H", ipmi.host,
- "-p", fmt.Sprintf("%d", ipmi.port),
- "-U", ipmi.user,
- "-P", ipmi.password,
- }
- nArgs = append(nArgs, args...)
- ctx, cancel := context.WithTimeout(context.Background(), ipmi.GetDefaultTimeout())
- return procutils.NewCommandContext(ctx, "ipmitool", nArgs...), cancel
- }
- func (ipmi *LanPlusIPMI) ExecuteCommand(args ...string) ([]string, error) {
- cmd, cancel := ipmi.GetCommand(args...)
- defer cancel()
- log.Debugf("[LanPlusIPMI] execute command: %s", cmd.String())
- out, err := cmd.Output()
- if err != nil {
- return nil, err
- }
- return ssh.ParseOutput(out), nil
- }
- func GetSysGuid(exector IPMIExecutor) string {
- args := []string{"mc", "guid"}
- // args := []string{"raw", "0x06", "0x37"}
- lines, err := exector.ExecuteCommand(args...)
- if err != nil {
- // ignore error
- log.Errorf("mc guid error: %s", err)
- }
- for _, line := range lines {
- key, val := stringutils.SplitKeyValue(line)
- if key == "System GUID" {
- return sysutils.NormalizeUuid(val)
- }
- }
- return ""
- }
- func GetSysInfo(exector IPMIExecutor) (*types.SSystemInfo, error) {
- // TODO: do cache
- args := []string{"fru", "print", "0"}
- lines, err := exector.ExecuteCommand(args...)
- if err != nil {
- // ignore error
- log.Errorf("fru print 0 error: %s", err)
- }
- ret := make(map[string]string)
- keys := map[string]string{
- "manufacture": "Product Manufacturer",
- "model": "Product Name",
- "bmodel": "Board Product",
- "version": "Product Version",
- "sn": "Product Serial",
- "bsn": "Board Serial",
- }
- for _, line := range lines {
- key, val := stringutils.SplitKeyValue(line)
- if key != "" {
- for n, v := range keys {
- if _, ok := ret[n]; v == key && !ok {
- ret[n] = val
- }
- }
- }
- }
- _, snOk := ret["sn"]
- bsn, bsnOk := ret["bsn"]
- if !snOk && bsnOk {
- // no product serial
- ret["sn"] = bsn
- }
- info := types.SSystemInfo{}
- err = sysutils.DumpMapToObject(ret, &info)
- info.OemName = types.ManufactureOemName(info.Manufacture)
- return &info, err
- }
- /*func GetLanChannels(sysinfo *types.SSystemInfo) []int {
- return profiles.GetLanChannel(sysinfo)
- }
- func GetDefaultLanChannel(sysinfo *types.SSystemInfo) int {
- return GetLanChannels(sysinfo)[0]
- }
- func GetRootId(sysinfo *types.SSystemInfo) int {
- return profiles.GetRootId(sysinfo)
- }*/
- func GetLanConfig(exector IPMIExecutor, channel uint8) (*types.SIPMILanConfig, error) {
- args := newArgs("lan", "print", channel)
- lines, err := ExecuteCommands(exector, args)
- if err != nil {
- return nil, err
- }
- ret := new(types.SIPMILanConfig)
- for _, line := range lines {
- key, val := stringutils.SplitKeyValue(line)
- if key == "" {
- continue
- }
- switch key {
- case "IP Address Source":
- if val == "Static Address" {
- ret.IPSrc = "static"
- }
- case "IP Address":
- ret.IPAddr = val
- case "Subnet Mask":
- ret.Netmask = val
- case "MAC Address":
- ret.Mac, _ = net.ParseMAC(val)
- case "Default Gateway IP":
- ret.Gateway = val
- case "802.1q VLAN ID":
- vlanId, _ := strconv.ParseInt(val, 10, 64)
- ret.VlanId = int(vlanId)
- }
- }
- return ret, nil
- }
- func tryExecuteCommand(exector IPMIExecutor, args ...string) ([]string, error) {
- var err error
- var ret []string
- maxTries := 3
- for tried := 0; tried < maxTries; tried++ {
- ret, err = exector.ExecuteCommand(args...)
- if err == nil {
- return ret, nil
- }
- sleepTime := time.Second * (1 << uint(tried))
- log.Errorf("Execute args %v error: %v, sleep %s then try again", args, err, sleepTime)
- time.Sleep(sleepTime)
- }
- return ret, err
- }
- func ExecuteCommands(exector IPMIExecutor, args ...Args) ([]string, error) {
- results := make([]string, 0)
- for _, arg := range args {
- ret, err := tryExecuteCommand(exector, arg...)
- if err != nil {
- return nil, err
- }
- results = append(results, ret...)
- }
- return results, nil
- }
- func doActions(exector IPMIExecutor, actionName string, args ...Args) error {
- _, err := ExecuteCommands(exector, args...)
- if err != nil {
- return fmt.Errorf("Do %s action error: %v", actionName, err)
- }
- return nil
- }
- func SetLanDHCP(exector IPMIExecutor, lanChannel uint8) error {
- args := newArgs("lan", "set", lanChannel, "ipsrc", "dhcp")
- return doActions(exector, "set_lan_dhcp", args)
- }
- func SetLanStatic(
- exector IPMIExecutor,
- channel uint8,
- ip string,
- mask string,
- gateway string,
- ) error {
- // config, err := GetLanConfig(exector, channel)
- // if err != nil {
- // return err
- // }
- // var argss []Args
- // if config.IPAddr == ip && config.Netmask == mask && config.Gateway == gateway {
- argss := []Args{
- newArgs("lan", "set", channel, "ipsrc", "static"),
- newArgs("lan", "set", channel, "ipaddr", ip),
- newArgs("lan", "set", channel, "netmask", mask),
- newArgs("lan", "set", channel, "defgw", "ipaddr", gateway),
- }
- // } else {
- // argss = []Args{
- // newArgs("lan", "set", channel, "ipsrc", "static"),
- // newArgs("lan", "set", channel, "ipaddr", ip),
- // newArgs("lan", "set", channel, "netmask", mask),
- // newArgs("lan", "set", channel, "defgw", "ipaddr", gateway),
- // }
- // }
- return doActions(exector, "set_lan_static", argss...)
- }
- func SetLanStaticIP(exector IPMIExecutor, channel uint8, ip string) error {
- args := newArgs("lan", "set", channel, "ipaddr", ip)
- return doActions(exector, "set_lan_static_ip", args)
- }
- func setLanAccess(exector IPMIExecutor, channel uint8, access string) error {
- args := []Args{
- newArgs("lan", "set", channel, "access", access),
- // newArgs("lan", "set", channel, "auth", "ADMIN", "MD5"),
- }
- return doActions(exector, "set_lan_access", args...)
- }
- func EnableLanAccess(exector IPMIExecutor, channel uint8) error {
- return setLanAccess(exector, channel, "on")
- }
- func ListLanUsers(exector IPMIExecutor, channel uint8) ([]compute.IPMIUser, error) {
- args := newArgs("user", "list", channel)
- ret, err := ExecuteCommands(exector, args)
- if err != nil {
- return nil, errors.Wrapf(err, "list user at channel %d", channel)
- }
- return sysutils.ParseIPMIUser(ret), nil
- }
- func CreateOrSetAdminUser(exector IPMIExecutor, channel uint8, rootId int, username string, password string) error {
- users, err := ListLanUsers(exector, channel)
- if err != nil {
- return errors.Wrap(err, "List users")
- }
- if len(users) == 0 {
- return errors.Errorf("Empty users at channel %d", channel)
- }
- var foundUser *compute.IPMIUser = nil
- for _, user := range users {
- tmp := user
- if user.Name == username {
- foundUser = &tmp
- break
- }
- }
- if foundUser == nil {
- minEmptyUserId := -1
- for _, user := range users {
- if user.Name == "" {
- minEmptyUserId = user.Id
- break
- }
- }
- if minEmptyUserId == -1 {
- log.Warningf("Not found min empty user id, use root id %d to set", rootId)
- minEmptyUserId = rootId
- }
- return SetIdUserPasswd(exector, channel, minEmptyUserId, username, password)
- }
- return SetLanUserAdminPasswd(exector, channel, foundUser.Id, password)
- }
- func SetLanUserAdminPasswd(exector IPMIExecutor, channel uint8, id int, password string) error {
- var err error
- password, err = stringutils2.EscapeEchoString(password)
- if err != nil {
- return fmt.Errorf("EscapeEchoString for password: %s, error: %v", password, err)
- }
- args := []Args{
- newArgs("user", "enable", id),
- newArgs("user", "set", "password", id, fmt.Sprintf("\"%s\"", password)),
- newArgs("user", "priv", id, 4, channel),
- }
- err = doActions(exector, "set_lan_user_password", args...)
- if err != nil {
- return err
- }
- args = []Args{newArgs(
- "raw", "0x06", "0x43",
- fmt.Sprintf("0x%02x", 0xb0+channel),
- fmt.Sprintf("0x%02x", id), "0x04", "0x00")}
- err = doActions(exector, "set_lan_user_password2", args...)
- if err == nil {
- return nil
- }
- args = []Args{newArgs(
- "channel", "setaccess", channel,
- id, "link=on", "ipmi=on",
- "callin=on", "privilege=4",
- )}
- return doActions(exector, "set_lan_user_password3", args...)
- }
- func SetIdUserPasswd(exector IPMIExecutor, channel uint8, id int, user string, password string) error {
- args := newArgs("user", "set", "name", id, user)
- if err := doActions(exector, fmt.Sprintf("set_id%d_name", id), args); err != nil {
- return errors.Wrapf(err, "change root id %d to name %s", id, user)
- }
- return SetLanUserAdminPasswd(exector, channel, id, password)
- }
- func SetLanPasswd(exector IPMIExecutor, rootId int, password string) error {
- password, err := stringutils2.EscapeEchoString(password)
- if err != nil {
- return fmt.Errorf("EscapeEchoString for password: %v", err)
- }
- args := newArgs("user", "set", "password", rootId, fmt.Sprintf("\"%s\"", password))
- return doActions(exector, "set_lan_passwd", args)
- }
- func GetChassisPowerStatus(exector IPMIExecutor) (string, error) {
- args := newArgs("chassis", "power", "status")
- ret, err := ExecuteCommands(exector, args)
- if err != nil {
- return "", err
- }
- for _, line := range ret {
- if strings.Contains(line, "Chassis Power is") {
- data := strings.Split(line, " ")
- status := strings.ToLower(strings.TrimSpace(data[len(data)-1]))
- return status, nil
- }
- }
- return "", fmt.Errorf("Unknown chassis status")
- }
- func GetBootFlags(exector IPMIExecutor) (*types.SIPMIBootFlags, error) {
- args := newArgs("raw", "0x00", "0x09", "0x05", "0x00", "0x00")
- ret, err := ExecuteCommands(exector, args)
- if err != nil {
- return nil, err
- }
- bytes, err := HexStr2Bytes(ret[0])
- if err != nil {
- return nil, err
- }
- bootdevIdx := ((bytes[3] >> 2) & 0x0f) - 1
- bootdev := ""
- if bootdevIdx >= 0 && int(bootdevIdx) < len(BOOTDEVS) {
- bootdev = BOOTDEVS[bootdevIdx]
- }
- flags := &types.SIPMIBootFlags{
- Dev: bootdev,
- }
- solIdx := (bytes[4] & 0x03)
- if solIdx == 1 {
- sol := false
- flags.Sol = &sol
- } else if solIdx == 2 {
- sol := true
- flags.Sol = &sol
- }
- return flags, nil
- }
- func HexStr2Bytes(hs string) ([]int64, error) {
- b := []int64{}
- for _, x := range strings.Split(hs, " ") {
- intV, err := strconv.ParseInt(x, 16, 64)
- if err != nil {
- return nil, err
- }
- b = append(b, intV)
- }
- return b, nil
- }
- func GetACPIPowerStatus(exector IPMIExecutor) ([]int64, error) {
- args := newArgs("raw", "0x06", "0x07")
- ret, err := ExecuteCommands(exector, args)
- if err != nil {
- return nil, err
- }
- return HexStr2Bytes(ret[0])
- }
- func DoSoftShutdown(exector IPMIExecutor) error {
- args := newArgs("chassis", "power", "soft")
- return doActions(exector, "do_soft_shutdown", args)
- }
- func DoHardShutdown(exector IPMIExecutor) error {
- args := newArgs("chassis", "power", "off")
- return doActions(exector, "do_hard_shutdown", args)
- }
- func DoPowerOn(exector IPMIExecutor) error {
- args := newArgs("chassis", "power", "on")
- return doActions(exector, "do_power_on", args)
- }
- func DoPowerReset(exector IPMIExecutor) error {
- args := newArgs("chassis", "power", "reset")
- return doActions(exector, "do_power_reset", args)
- }
- func DoPowerCycle(exector IPMIExecutor) error {
- args := newArgs("chassis", "power", "cycle")
- return doActions(exector, "do_power_cycle", args)
- }
- func DoReboot(exector IPMIExecutor) error {
- maxTries := 10
- var status string
- var err error
- status, err = GetChassisPowerStatus(exector)
- if err != nil {
- log.Errorf("DoReboot get power status 1st: %v", err)
- }
- isValidStatus := func(s string) bool {
- return utils.IsInStringArray(s, []string{string(types.POWER_STATUS_ON), string(types.POWER_STATUS_OFF)})
- }
- for tried := 0; !isValidStatus(status) && tried <= maxTries; tried++ {
- time.Sleep(1 * time.Second)
- status, err = GetChassisPowerStatus(exector)
- if err != nil {
- log.Errorf("DoReboot %d tries to get power status: %v", tried, err)
- }
- }
- if !isValidStatus(status) {
- return fmt.Errorf("Unexpected power status: %q", status)
- }
- // do shutdown
- if status == string(types.POWER_STATUS_ON) {
- if err := DoHardShutdown(exector); err != nil {
- log.Errorf("DoHardShutdown: %v", err)
- }
- time.Sleep(1 * time.Second)
- for tried := 0; tried < maxTries; tried++ {
- status, err = GetChassisPowerStatus(exector)
- if err != nil {
- log.Errorf("DoReboot %d tries to get power status: %v", tried, err)
- }
- if status == string(types.POWER_STATUS_OFF) {
- break
- }
- time.Sleep(10 * time.Second)
- }
- }
- // do power on
- status, _ = GetChassisPowerStatus(exector)
- for tried := 0; status != string(types.POWER_STATUS_ON) && tried < maxTries; tried++ {
- if err := DoPowerOn(exector); err != nil {
- log.Errorf("DoReboot %d tries to power on: %v", tried, err)
- }
- time.Sleep(10 * time.Second)
- status, err = GetChassisPowerStatus(exector)
- if err != nil {
- log.Errorf("DoReboot %d tries to get power status: %v", tried, err)
- }
- }
- status, err = GetChassisPowerStatus(exector)
- if err != nil {
- return errors.Wrap(err, "Get power status after power on")
- }
- if status != string(types.POWER_STATUS_ON) {
- return errors.Errorf("do reboot fail to poweron, current status: %s", status)
- }
- return nil
- }
- func doRebootToFlag(exector IPMIExecutor, setFunc func(IPMIExecutor) error) error {
- err := setFunc(exector)
- if err != nil {
- return err
- }
- return DoReboot(exector)
- }
- func SetRebootToDisk(exector IPMIExecutor) error {
- return SetBootFlags(exector, "disk", tristate.True, true)
- }
- func DoRebootToDisk(exector IPMIExecutor) error {
- return doRebootToFlag(exector, SetRebootToDisk)
- }
- func SetRebootToPXE(exector IPMIExecutor) error {
- return SetBootFlagPXE(exector)
- }
- func DoRebootToPXE(exector IPMIExecutor) error {
- return doRebootToFlag(exector, SetRebootToPXE)
- }
- func SetRebootToBIOS(exector IPMIExecutor) error {
- return SetBootFlags(exector, "bios", tristate.True, false)
- }
- func DoRebootToBIOS(exector IPMIExecutor) error {
- return doRebootToFlag(exector, SetRebootToBIOS)
- }
- func SetBootFlagPXE(exector IPMIExecutor) error {
- return setBootFlagsV2(exector, "pxe")
- }
- func SetBootFlags(
- exector IPMIExecutor,
- bootdev string,
- sol tristate.TriState,
- enablePersistent bool,
- ) error {
- err := setBootFlagsV1(exector, bootdev, sol, enablePersistent)
- if err == nil {
- return nil
- }
- return setBootFlagsV2(exector, bootdev)
- }
- func setBootFlagsV1(
- exector IPMIExecutor,
- bootdev string,
- sol tristate.TriState,
- enablePersistent bool,
- ) error {
- cmd := []interface{}{"raw", "0x00", "0x08", "0x05"}
- bootdevIdx := 0
- if ok, idx := utils.InStringArray(bootdev, BOOTDEVS); ok {
- bootdevIdx = idx + 1
- } else {
- return fmt.Errorf("Illegal bootdev %s", bootdev)
- }
- valid := 0x80
- if enablePersistent {
- valid = valid + 0x40
- }
- solIdx := 0
- if !sol.IsNone() {
- if sol.IsTrue() {
- solIdx = 2
- } else {
- solIdx = 1
- }
- }
- for _, x := range []int{valid, bootdevIdx << 2, solIdx, 0, 0} {
- cmd = append(cmd, fmt.Sprintf("0x%02x", x))
- }
- return doActions(exector, "set_boot_flags_v1", newArgs(cmd...))
- }
- func setBootFlagsV2(exector IPMIExecutor, bootdev string) error {
- return doActions(
- exector,
- fmt.Sprintf("set_boot_flag_%s", bootdev),
- newArgs("chassis", "bootdev", bootdev),
- )
- }
- func GetIPMILanPort(exector IPMIExecutor) (string, error) {
- ret, err := ExecuteCommands(exector, newArgs("delloem", "lan", "get"))
- if err != nil {
- return "", err
- }
- return ret[1], nil
- }
- func SetDellIPMILanPortShared(exector IPMIExecutor) error {
- args1 := newArgs("delloem", "lan", "set", "shared")
- args2 := newArgs("delloem", "lan", "set", "shared", "with", "lom1")
- err2 := doActions(exector, "_dell_set_ipmi_lan_port_shared_02", args2)
- if err2 != nil {
- return doActions(exector, "_dell_set_ipmi_lan_port_shared_01", args1)
- }
- return nil
- }
- func SetHuaweiIPMILanPortShared(exector IPMIExecutor) error {
- args := []Args{
- newArgs(
- "raw", "0xc", "0x1", "0x1", "0xd7", "0xdb",
- "0x07", "0x00", "0x2",
- ),
- newArgs(
- "raw", "0x30", "0x93", "0xdb", "0x07", "0x00",
- "0x05", "0x0d", "0x0", "0x0", "0x1", "0x0",
- ),
- }
- return doActions(exector, "_huawei_set_ipmi_lan_port_shared", args...)
- }
- func SetIPMILanPortDedicated(exector IPMIExecutor) error {
- return doActions(
- exector,
- "set_ipmi_lan_port_dedicated",
- newArgs("delloem", "lan", "set", "dedicated"),
- )
- }
- func DoBMCReset(exector IPMIExecutor) error {
- return doActions(exector, "do_bmc_reset", newArgs("mc", "reset", "cold"))
- }
|