Files
cloudpods/pkg/baremetal/manager.go

2437 lines
64 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 baremetal
import (
"context"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"sync"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/pkg/util/seclib"
"yunion.io/x/pkg/util/sets"
"yunion.io/x/pkg/util/workqueue"
"yunion.io/x/pkg/utils"
api "yunion.io/x/onecloud/pkg/apis/compute"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/baremetal/profiles"
"yunion.io/x/onecloud/pkg/baremetal/pxe"
baremetalstatus "yunion.io/x/onecloud/pkg/baremetal/status"
"yunion.io/x/onecloud/pkg/baremetal/tasks"
baremetaltypes "yunion.io/x/onecloud/pkg/baremetal/types"
"yunion.io/x/onecloud/pkg/baremetal/utils/detect_storages"
"yunion.io/x/onecloud/pkg/baremetal/utils/disktool"
"yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
raiddrivers "yunion.io/x/onecloud/pkg/baremetal/utils/raid/drivers"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/compute/baremetal"
"yunion.io/x/onecloud/pkg/hostman/guestfs"
"yunion.io/x/onecloud/pkg/hostman/guestfs/sshpart"
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/dhcp"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/influxdb"
"yunion.io/x/onecloud/pkg/util/procutils"
"yunion.io/x/onecloud/pkg/util/redfish"
"yunion.io/x/onecloud/pkg/util/redfish/bmconsole"
"yunion.io/x/onecloud/pkg/util/ssh"
"yunion.io/x/onecloud/pkg/util/sysutils"
)
type SBaremetalManager struct {
Agent *SBaremetalAgent
configPath string
baremetals *sBaremetalMap
}
func NewBaremetalManager(agent *SBaremetalAgent) (*SBaremetalManager, error) {
bmPaths := o.Options.BaremetalsPath
err := os.MkdirAll(bmPaths, 0755)
if err != nil {
return nil, err
}
return &SBaremetalManager{
Agent: agent,
configPath: bmPaths,
baremetals: newBaremetalMap(),
}, nil
}
func (m *SBaremetalManager) killAllIPMITool() {
procutils.NewCommand("killall", "-9", "ipmitool").Run()
}
func (m *SBaremetalManager) GetClientSession() *mcclient.ClientSession {
return m.Agent.GetAdminSession()
}
func (m *SBaremetalManager) GetZoneId() string {
return m.Agent.Zone.Id
}
func (m *SBaremetalManager) GetZoneName() string {
return m.Agent.Zone.Name
}
func (m *SBaremetalManager) loadConfigs() ([]os.FileInfo, error) {
m.killAllIPMITool()
files, err := ioutil.ReadDir(m.configPath)
if err != nil {
return nil, err
}
return files, nil
}
func (m *SBaremetalManager) initBaremetals(files []os.FileInfo) error {
bmIds := make([]string, 0)
for _, file := range files {
if file.IsDir() && regutils.MatchUUID(file.Name()) {
bmIds = append(bmIds, file.Name())
}
}
errsChannel := make(chan error, len(bmIds))
initBaremetal := func(i int) {
bmId := bmIds[i]
err := m.InitBaremetal(bmId, true)
if err != nil {
errsChannel <- err
return
}
}
workqueue.Parallelize(4, len(bmIds), initBaremetal)
errs := make([]error, 0)
if len(errsChannel) > 0 {
length := len(errsChannel)
for ; length > 0; length-- {
errs = append(errs, <-errsChannel)
}
}
return errors.NewAggregate(errs)
}
func (m *SBaremetalManager) InitBaremetal(bmId string, update bool) error {
session := m.GetClientSession()
var err error
var desc jsonutils.JSONObject
if update {
desc, err = m.updateBaremetal(session, bmId)
} else {
desc, err = m.fetchBaremetal(session, bmId)
}
if err != nil {
return err
}
isBaremetal, _ := desc.Bool("is_baremetal")
if !isBaremetal {
return errors.Error("not a baremetal???")
}
bmInstance, err := m.AddBaremetal(desc)
if err != nil {
return err
}
if update {
bmObj := bmInstance.(*SBaremetalInstance)
if !sets.NewString(INIT, PREPARE, UNKNOWN).Has(bmObj.GetStatus()) {
bmObj.SyncStatusBackground()
}
}
return nil
}
func (m *SBaremetalManager) CleanBaremetal(bmId string) {
bm := m.baremetals.Pop(bmId)
if bm != nil {
bm.Stop()
}
bm.clearBootIsoImage()
path := bm.GetDir()
procutils.NewCommand("rm", "-fr", path).Run()
}
func (m *SBaremetalManager) updateBaremetal(session *mcclient.ClientSession, bmId string) (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.JSONTrue, "is_baremetal")
obj, err := modules.Hosts.Put(session, bmId, params)
if err != nil {
return nil, err
}
log.Infof("Baremetal %s update success", bmId)
return obj, nil
}
func (m *SBaremetalManager) fetchBaremetal(session *mcclient.ClientSession, bmId string) (jsonutils.JSONObject, error) {
obj, err := modules.Hosts.Get(session, bmId, nil)
if err != nil {
return nil, err
}
log.Infof("Baremetal %s update success", bmId)
return obj, nil
}
func (m *SBaremetalManager) AddBaremetal(desc jsonutils.JSONObject) (pxe.IBaremetalInstance, error) {
id, err := desc.GetString("id")
if err != nil {
return nil, fmt.Errorf("Not found baremetal id in desc %s", desc)
}
if instance, ok := m.baremetals.Get(id); ok {
return instance, instance.SaveDesc(desc)
}
bm, err := newBaremetalInstance(m, desc)
if err != nil {
return nil, err
}
m.baremetals.Add(bm)
return bm, nil
}
func (m *SBaremetalManager) GetBaremetals() []*SBaremetalInstance {
objs := make([]*SBaremetalInstance, 0)
getter := func(key, val interface{}) bool {
objs = append(objs, val.(*SBaremetalInstance))
return true
}
m.baremetals.Range(getter)
return objs
}
func (m *SBaremetalManager) GetBaremetalById(bmId string) *SBaremetalInstance {
obj, _ := m.baremetals.Get(bmId)
return obj
}
func (m *SBaremetalManager) GetBaremetalByMac(mac net.HardwareAddr) pxe.IBaremetalInstance {
var obj pxe.IBaremetalInstance
getter := func(key, val interface{}) bool {
instance := val.(*SBaremetalInstance)
if instance.GetNicByMac(mac) != nil {
obj = instance
// stop the iteration
return false
}
// not found, continue iteration
return true
}
m.baremetals.Range(getter)
return obj
}
type BmRegisterInput struct {
// context with timeout
Ctx context.Context
R *http.Request
W http.ResponseWriter
// For notify web server close this connection
C chan struct{}
// remote server ssh info
SshPort int
SshPasswd string
Hostname string
RemoteIp string
// ipmi info
Username string
Password string
IpAddr string
}
func (i *BmRegisterInput) responseSucc(bmId string) {
fmt.Fprintf(i.W, bmId)
close(i.C)
}
func (i *BmRegisterInput) responseErr(err error) {
httperrors.GeneralServerError(i.W, err)
close(i.C)
}
func (i *BmRegisterInput) isTimeout() bool {
select {
case <-i.Ctx.Done():
return true
default:
return false
}
}
// delay task
func (m *SBaremetalManager) RegisterBaremetal(ctx context.Context, userCred mcclient.TokenCredential, input *BmRegisterInput) {
adminWire, err := m.checkNetworkFromIp(input.RemoteIp)
if input.isTimeout() {
return
} else if err != nil {
input.responseErr(httperrors.NewBadRequestError("Verify network failed: %s", err))
return
}
sshCli, err := m.checkSshInfo(input)
if input.isTimeout() {
return
} else if err != nil {
input.responseErr(httperrors.NewBadRequestError("SSH verify failed: %s", err))
return
}
input.IpAddr, err = m.fetchIpmiIp(sshCli)
if input.isTimeout() {
return
} else if err != nil {
input.responseErr(httperrors.NewBadRequestError("Fetch ipmi address failed: %s", err))
return
}
log.Infof("Find ipmi address %s", input.IpAddr)
ipmiWire, err := m.checkNetworkFromIp(input.IpAddr)
if input.isTimeout() {
return
} else if err != nil {
input.responseErr(httperrors.NewBadRequestError("Verify network failed: %s", err))
return
}
ipmiLanChannel, ipmiMac, err := m.checkIpmiInfo(input.Username, input.Password, input.IpAddr)
if input.isTimeout() {
return
} else if err != nil {
input.responseErr(httperrors.NewBadRequestError("IPMI login info not correct: %s", err))
return
}
err, registered := m.verifyMacAddr(sshCli)
if input.isTimeout() {
return
} else if err != nil {
input.responseErr(httperrors.NewBadRequestError("Verify mac address failed: %s", err))
return
}
registerTask := tasks.NewBaremetalRegisterTask(
userCred, m, sshCli, input.Hostname, input.RemoteIp,
input.Username, input.Password, input.IpAddr,
ipmiMac, ipmiLanChannel, adminWire, ipmiWire,
)
var bmId string
if !registered {
bmId, err = registerTask.CreateBaremetal()
} else {
bmId, err = registerTask.UpdateBaremetal()
}
if err != nil {
input.responseErr(httperrors.NewInternalServerError(err.Error()))
return
}
input.responseSucc(bmId)
registerTask.DoPrepare(ctx, sshCli, registered)
}
func (m *SBaremetalManager) fetchIpmiIp(sshCli *ssh.Client) (string, error) {
res, err := sshCli.RawRun(`/usr/bin/ipmitool lan print | grep "IP Address "`)
if err != nil {
return "", err
}
if len(res) == 1 {
segs := strings.Fields(res[0])
if len(segs) == 4 {
return strings.TrimSpace(segs[3]), nil
}
}
return "", fmt.Errorf("Failed to find ipmi ip address")
}
func (m *SBaremetalManager) checkNetworkFromIp(ip string) (string, error) {
params := jsonutils.NewDict()
params.Set("ip", jsonutils.NewString(ip))
params.Set("scope", jsonutils.NewString("system"))
params.Set("is_on_premise", jsonutils.JSONTrue)
res, err := modules.Networks.List(m.GetClientSession(), params)
if err != nil {
return "", fmt.Errorf("Fetch network by ip %s failed: %s", ip, err)
}
if len(res.Data) != 1 {
return "", fmt.Errorf("Can't find network from ip %s", ip)
}
return res.Data[0].GetString("wire_id")
}
func (m *SBaremetalManager) verifyMacAddr(sshCli *ssh.Client) (error, bool) {
output, err := sshCli.Run("/lib/mos/lsnic")
if err != nil {
return err, false
}
nicinfo := sysutils.ParseNicInfo(output)
if len(nicinfo) == 0 {
return fmt.Errorf("Can't get nic info"), false
}
var registered bool
params := jsonutils.NewDict()
for _, nic := range nicinfo {
if len(nic.Mac) > 0 {
params.Set("any_mac", jsonutils.NewString(nic.Mac.String()))
params.Set("scope", jsonutils.NewString("system"))
res, err := modules.Hosts.List(m.GetClientSession(), params)
if err != nil {
return fmt.Errorf("Get hosts info failed: %s", err), false
}
if len(res.Data) > 0 {
registered = true
}
}
}
return nil, registered
}
func (m *SBaremetalManager) checkSshInfo(input *BmRegisterInput) (*ssh.Client, error) {
sshCLi, err := ssh.NewClient(input.RemoteIp, input.SshPort, "root", input.SshPasswd, "")
if err != nil {
return nil, fmt.Errorf("Ssh connect failed: %s", err)
}
var RETRY, MAX_TRIES = 0, 3
for RETRY = 0; RETRY < MAX_TRIES; RETRY++ {
_, err := sshCLi.Run("/bin/ls")
if err != nil {
log.Errorf("Exec remote command failed: %s", err)
time.Sleep(time.Second * 1)
} else {
break
}
}
if RETRY >= MAX_TRIES {
return nil, fmt.Errorf("SSH login info not correct??")
}
return sshCLi, nil
}
func (m *SBaremetalManager) checkIpmiInfo(username, password, ipAddr string) (int, net.HardwareAddr, error) {
lanPlusTool := ipmitool.NewLanPlusIPMI(ipAddr, username, password)
sysInfo, err := ipmitool.GetSysInfo(lanPlusTool)
if err != nil {
return -1, nil, err
}
for _, lanChannel := range ipmitool.GetLanChannels(sysInfo) {
config, err := ipmitool.GetLanConfig(lanPlusTool, lanChannel)
if err != nil {
log.Errorf("GetLanConfig failed %s", err)
continue
}
if len(config.Mac) == 0 {
continue
}
return lanChannel, config.Mac, nil
}
return -1, nil, fmt.Errorf("Ipmi can't fetch lan config")
}
func (m *SBaremetalManager) Stop() {
for _, bm := range m.GetBaremetals() {
bm.Stop()
}
}
type sBaremetalMap struct {
*sync.Map
}
func newBaremetalMap() *sBaremetalMap {
return &sBaremetalMap{
Map: new(sync.Map),
}
}
func (m *sBaremetalMap) Add(bm *SBaremetalInstance) {
m.Store(bm.GetId(), bm)
}
func (m *sBaremetalMap) Get(id string) (*SBaremetalInstance, bool) {
obj, ok := m.Load(id)
if !ok {
return nil, false
}
return obj.(*SBaremetalInstance), true
}
func (m *sBaremetalMap) Delete(id string) {
m.Map.Delete(id)
}
func (m *sBaremetalMap) Pop(id string) *SBaremetalInstance {
obj, exist := m.Get(id)
if exist {
m.Delete(id)
}
return obj
}
type SBaremetalInstance struct {
manager *SBaremetalManager
desc *jsonutils.JSONDict
descLock *sync.Mutex
taskQueue *tasks.TaskQueue
server baremetaltypes.IBaremetalServer
serverLock *sync.Mutex
cronJobs []IBaremetalCronJob
}
func newBaremetalInstance(man *SBaremetalManager, desc jsonutils.JSONObject) (*SBaremetalInstance, error) {
bm := &SBaremetalInstance{
manager: man,
desc: desc.(*jsonutils.JSONDict),
descLock: new(sync.Mutex),
taskQueue: tasks.NewTaskQueue(),
serverLock: new(sync.Mutex),
}
bm.cronJobs = []IBaremetalCronJob{
NewStatusProbeJob(bm, time.Duration(o.Options.StatusProbeIntervalSeconds)*time.Second),
NewLogFetchJob(bm, time.Duration(o.Options.LogFetchIntervalSeconds)*time.Second),
NewSendMetricsJob(bm, time.Duration(o.Options.SendMetricsIntervalSeconds)*time.Second),
}
err := os.MkdirAll(bm.GetDir(), 0755)
if err != nil {
return nil, err
}
err = bm.SaveDesc(desc)
if err != nil {
return nil, err
}
bm.loadServer()
return bm, nil
}
func (b *SBaremetalInstance) GetClientSession() *mcclient.ClientSession {
return b.manager.GetClientSession()
}
func (b *SBaremetalInstance) Keyword() string {
return "host"
}
func (b *SBaremetalInstance) GetId() string {
id, err := b.desc.GetString("id")
if err != nil {
log.Errorf("Get id from desc %s error: %v", b.desc.String(), err)
}
return id
}
func (b *SBaremetalInstance) GetName() string {
id, err := b.desc.GetString("name")
if err != nil {
log.Fatalf("Get name from desc %s error: %v", b.desc.String(), err)
}
return id
}
func (b *SBaremetalInstance) Stop() {
// TODO:
}
func (b *SBaremetalInstance) GetDir() string {
return filepath.Join(b.manager.configPath, b.GetId())
}
func (b *SBaremetalInstance) GetDescFilePath() string {
return filepath.Join(b.GetDir(), "desc")
}
func (b *SBaremetalInstance) GetServerDescFilePath() string {
return filepath.Join(b.GetDir(), "server")
}
func (b *SBaremetalInstance) GetSSHConfigFilePath() string {
return filepath.Join(b.GetDir(), "ssh")
}
func (b *SBaremetalInstance) GetStatus() string {
status, err := b.desc.GetString("status")
if err != nil {
log.Fatalf("Get status from desc error: %v", err)
}
return status
}
func (b *SBaremetalInstance) AutoSaveDesc() error {
return b.SaveDesc(nil)
}
func (b *SBaremetalInstance) SaveDesc(desc jsonutils.JSONObject) error {
b.descLock.Lock()
defer b.descLock.Unlock()
if desc != nil {
b.desc = desc.(*jsonutils.JSONDict)
}
return ioutil.WriteFile(b.GetDescFilePath(), []byte(b.desc.String()), 0644)
}
func (b *SBaremetalInstance) loadServer() {
b.serverLock.Lock()
defer b.serverLock.Unlock()
if !b.desc.Contains("server_id") {
return
}
descPath := b.GetServerDescFilePath()
desc, err := ioutil.ReadFile(descPath)
if err != nil {
log.Errorf("Failed to read server desc %s: %v", descPath, err)
return
}
descObj, err := jsonutils.Parse(desc)
if err != nil {
log.Errorf("Failed to parse server json string: %v", err)
return
}
srv, err := newBaremetalServer(b, descObj.(*jsonutils.JSONDict))
if err != nil {
log.Errorf("New server error: %v", err)
return
}
if bmSrvId, _ := b.desc.GetString("server_id"); srv.GetId() != bmSrvId {
log.Errorf("Server id %q not equal baremetal %q server id %q", srv.GetId(), b.GetName(), bmSrvId)
return
}
b.server = srv
}
func (b *SBaremetalInstance) SaveSSHConfig(remoteAddr string, key string) error {
var err error
key, err = utils.EncryptAESBase64(b.GetId(), key)
if err != nil {
return err
}
sshConf := types.SSHConfig{
Username: "root",
Password: key,
RemoteIP: remoteAddr,
}
conf := jsonutils.Marshal(sshConf)
err = ioutil.WriteFile(b.GetSSHConfigFilePath(), []byte(conf.String()), 0644)
if err != nil {
return err
}
b.SyncSSHConfig(sshConf)
b.clearBootIso()
return err
}
func (b *SBaremetalInstance) GetSSHConfig() (*types.SSHConfig, error) {
path := b.GetSSHConfigFilePath()
content, err := ioutil.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
conf := types.SSHConfig{}
obj, err := jsonutils.Parse(content)
if err != nil {
return nil, err
}
err = obj.Unmarshal(&conf)
if err != nil {
return nil, err
}
if len(conf.RemoteIP) == 0 {
return nil, nil
}
conf.Password, err = utils.DescryptAESBase64(b.GetId(), conf.Password)
if err != nil {
return nil, err
}
return &conf, nil
}
func (b *SBaremetalInstance) TestSSHConfig() bool {
conf, err := b.GetSSHConfig()
if err != nil {
return false
}
if conf == nil {
return false
}
sshCli, err := ssh.NewClient(conf.RemoteIP, 22, "root", conf.Password, "")
if err != nil {
return false
}
ret, err := sshCli.Run("whoami")
if err != nil {
return false
}
if strings.Contains(strings.Join(ret, ""), "root") {
return true
}
return false
}
func (b *SBaremetalInstance) ClearSSHConfig() {
path := b.GetSSHConfigFilePath()
err := os.Remove(path)
if err != nil {
log.V(2).Warningf("Clear ssh config %s error: %v", path, err)
}
emptyConfig := types.SSHConfig{
Username: "None",
Password: "None",
RemoteIP: "None",
}
err = b.SyncSSHConfig(emptyConfig)
if err != nil {
log.Errorf("Sync emtpy SSH config error: %v", err)
}
}
func (b *SBaremetalInstance) SyncSSHConfig(conf types.SSHConfig) error {
session := b.manager.GetClientSession()
var info *api.HostLoginInfo
if len(conf.RemoteIP) > 0 {
var err error
// encrypt twice
conf.Password, err = utils.EncryptAESBase64(b.GetId(), conf.Password)
if err != nil {
return err
}
info = &api.HostLoginInfo{
Username: conf.Username,
Password: conf.Password,
Ip: conf.RemoteIP,
}
} else {
info = &api.HostLoginInfo{
Username: "None",
Password: "None",
Ip: "None",
}
}
data := info.JSON(info)
_, err := modules.Hosts.SetMetadata(session, b.GetId(), data)
return err
}
func (b *SBaremetalInstance) SyncStatusBackground() {
go func() {
b.AutoSyncAllStatus()
}()
}
func (b *SBaremetalInstance) InitializeServer(s *mcclient.ClientSession, name string) error {
params := jsonutils.NewDict()
params.Set("name", jsonutils.NewString(name))
_, err := modules.Hosts.PerformAction(s, b.GetId(), "initialize", params)
return err
}
func (b *SBaremetalInstance) ServerLoadDesc() error {
res, err := modules.Hosts.Get(b.manager.GetClientSession(), b.GetId(), nil)
if err != nil {
return err
}
b.SaveDesc(res)
sid, err := res.GetString("server_id")
if err == nil {
sDesc := jsonutils.NewDict()
sDesc.Set("uuid", jsonutils.NewString(sid))
b.server, err = newBaremetalServer(b, sDesc)
return err
} else {
return nil
}
}
func PowerStatusToBaremetalStatus(status string) string {
switch status {
case types.POWER_STATUS_ON:
return baremetalstatus.RUNNING
case types.POWER_STATUS_OFF:
return baremetalstatus.READY
}
return baremetalstatus.UNKNOWN
}
func PowerStatusToServerStatus(bm *SBaremetalInstance, status string) string {
switch status {
case types.POWER_STATUS_ON:
if conf, _ := bm.GetSSHConfig(); conf == nil {
return baremetalstatus.SERVER_RUNNING
} else {
return baremetalstatus.SERVER_ADMIN
}
case types.POWER_STATUS_OFF:
return baremetalstatus.SERVER_READY
}
return baremetalstatus.UNKNOWN
}
func (b *SBaremetalInstance) AutoSyncStatus() {
b.SyncStatus("", "")
}
func (b *SBaremetalInstance) SyncStatus(status string, reason string) {
if status == "" {
powerStatus, err := b.GetPowerStatus()
if err != nil {
log.Errorf("Get power status error: %v", err)
}
status = PowerStatusToBaremetalStatus(powerStatus)
}
b.desc.Set("status", jsonutils.NewString(status))
b.AutoSaveDesc()
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(status), "status")
if reason != "" {
params.Add(jsonutils.NewString(reason), "reason")
}
_, err := modules.Hosts.PerformAction(b.GetClientSession(), b.GetId(), "status", params)
if err != nil {
log.Errorf("Update baremetal %s status %s error: %v", b.GetId(), status, err)
return
}
log.Infof("Update baremetal %s to status %s", b.GetId(), status)
}
func (b *SBaremetalInstance) AutoSyncAllStatus() {
b.SyncAllStatus("")
}
func (b *SBaremetalInstance) DelayedSyncStatus(_ jsonutils.JSONObject) (jsonutils.JSONObject, error) {
b.AutoSyncAllStatus()
return nil, nil
}
func (b *SBaremetalInstance) SyncAllStatus(status string) {
var err error
if status == "" {
status, err = b.GetPowerStatus()
if err != nil {
log.Errorf("Get power status error: %v", err)
}
}
b.SyncStatus(PowerStatusToBaremetalStatus(status), "")
b.SyncServerStatus(PowerStatusToServerStatus(b, status))
}
func (b *SBaremetalInstance) SyncServerStatus(status string) {
if b.GetServerId() == "" {
return
}
if status == "" {
powerStatus, err := b.GetPowerStatus()
if err != nil {
log.Errorf("Get power status error: %v", err)
}
status = PowerStatusToServerStatus(b, powerStatus)
}
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(status), "status")
_, err := modules.Servers.PerformAction(b.GetClientSession(), b.GetServerId(), "status", params)
if err != nil {
log.Errorf("Update server %s status %s error: %v", b.GetServerName(), status, err)
return
}
log.Infof("Update server %s to status %s", b.GetServerName(), status)
}
func (b *SBaremetalInstance) GetNics() []types.SNic {
nics := []types.SNic{}
err := b.desc.Unmarshal(&nics, "nic_info")
if err != nil {
log.Errorf("Unmarshal desc to get nics error: %v", err)
return nil
}
return nics
}
func (b *SBaremetalInstance) getNicByType(nicType string) *types.SNic {
nics := b.GetNics()
if len(nics) == 0 {
return nil
}
for i := range nics {
if nics[i].Type == nicType {
return &nics[i]
}
}
return nil
}
func (b *SBaremetalInstance) GetNicByMac(mac net.HardwareAddr) *types.SNic {
nics := b.GetNics()
if len(nics) == 0 {
return nil
}
for _, nic := range nics {
tmp := nic
if tmp.Mac == mac.String() {
return &tmp
}
}
return nil
}
func (b *SBaremetalInstance) GetAdminNic() *types.SNic {
return b.getNicByType(api.NIC_TYPE_ADMIN)
}
func (b *SBaremetalInstance) NeedPXEBoot() bool {
task := b.GetTask()
taskName := "nil"
serverId := b.GetServerId()
if task != nil {
taskName = task.GetName()
}
taskNeedPXEBoot := false
if task != nil && task.NeedPXEBoot() {
taskNeedPXEBoot = true
}
ret := false
if taskNeedPXEBoot || (task == nil && len(serverId) == 0 && b.GetHostType() == "baremetal") {
ret = true
}
log.Infof("Check task %s, server %s NeedPXEBoot: %v", taskName, serverId, ret)
return ret
}
func (b *SBaremetalInstance) GetHostType() string {
hostType, _ := b.desc.GetString("host_type")
return hostType
}
func (b *SBaremetalInstance) GetIPMINic(cliMac net.HardwareAddr) *types.SNic {
nic := b.getNicByType(api.NIC_TYPE_IPMI)
if nic == nil {
return nil
}
if nic.Mac == cliMac.String() {
return nic
}
return nil
}
func (b *SBaremetalInstance) GetIPMINicIPAddr() string {
nic := b.getNicByType(api.NIC_TYPE_IPMI)
if nic == nil {
return ""
}
return nic.IpAddr
}
func (b *SBaremetalInstance) GetDHCPConfig(cliMac net.HardwareAddr) (*dhcp.ResponseConfig, error) {
var nic *types.SNic
var hostname string
if b.GetServer() != nil && (b.GetTask() == nil || !b.GetTask().NeedPXEBoot()) {
nic = b.GetServer().GetNicByMac(cliMac)
hostname = b.GetServer().GetName()
} else {
nic = b.GetNicByMac(cliMac)
}
if nic == nil {
return nil, fmt.Errorf("GetNicDHCPConfig no nic found by mac: %s", cliMac)
}
return b.getDHCPConfig(nic, hostname, false, 0)
}
func (b *SBaremetalInstance) GetPXEDHCPConfig(arch uint16) (*dhcp.ResponseConfig, error) {
return b.getDHCPConfig(b.GetAdminNic(), "", true, arch)
}
func (b *SBaremetalInstance) getDHCPConfig(
nic *types.SNic,
hostName string,
isPxe bool,
arch uint16,
) (*dhcp.ResponseConfig, error) {
if hostName == "" {
hostName = b.GetName()
}
serverIP, err := b.manager.Agent.GetDHCPServerIP()
if err != nil {
return nil, err
}
return GetNicDHCPConfig(nic, serverIP.String(), hostName, isPxe, arch)
}
func (b *SBaremetalInstance) GetNotifyUrl() string {
return fmt.Sprintf("%s/baremetals/%s/notify", b.manager.Agent.GetListenUri(), b.GetId())
}
func (b *SBaremetalInstance) getTftpFileUrl(filename string) string {
serverIP, err := b.manager.Agent.GetDHCPServerIP()
if err != nil {
log.Errorf("Get http file server: %v", err)
return filename
}
return fmt.Sprintf("http://%s:%d/tftp/%s", serverIP, o.Options.Port+1000, filename)
}
func (b *SBaremetalInstance) GetImageCacheUrl() string {
serverIP, err := b.manager.Agent.GetDHCPServerIP()
if err != nil {
log.Errorf("Get http file server: %v", err)
return ""
}
// no /images/, rootcreate.sh will add this
return fmt.Sprintf("http://%s:%d", serverIP, o.Options.Port+1000)
}
func (b *SBaremetalInstance) getBootIsoUrl() string {
serverIP, err := b.manager.Agent.GetDHCPServerIP()
if err != nil {
log.Errorf("Get http file server: %v", err)
return ""
}
// no /images/, rootcreate.sh will add this
return fmt.Sprintf("http://%s:%d/bootiso/%s.iso", serverIP, o.Options.Port+1000, b.GetId())
}
func (b *SBaremetalInstance) GetTFTPResponse() string {
return b.getSyslinuxConf(true)
}
func (b *SBaremetalInstance) getIsolinuxConf() string {
return b.getSyslinuxConf(false)
}
func (b *SBaremetalInstance) getSyslinuxPath(filename string, isTftp bool) string {
if isTftp {
return b.getTftpFileUrl(filename)
} else {
return filename
}
}
func (b *SBaremetalInstance) findAccessNetwork(accessIp string) (*types.SNetworkConfig, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(accessIp), "ip")
params.Add(jsonutils.NewString("system"), "scope")
params.Add(jsonutils.JSONTrue, "is_on_premise")
session := b.manager.GetClientSession()
ret, err := modules.Networks.List(session, params)
if err != nil {
return nil, err
}
if len(ret.Data) == 0 {
return nil, errors.Wrapf(httperrors.ErrNotFound, "accessIp %s", accessIp)
}
network := types.SNetworkConfig{}
err = ret.Data[0].Unmarshal(&network)
return &network, err
}
func (b *SBaremetalInstance) getSyslinuxConf(isTftp bool) string {
resp := `DEFAULT start
serial 1 115200
LABEL start
MENU LABEL ^Start
MENU default
`
if b.NeedPXEBoot() {
kernel := "vmlinuz"
initramfs := "initrd.img"
if isTftp {
kernel = b.getTftpFileUrl("kernel")
initramfs = b.getTftpFileUrl("initramfs")
}
resp += fmt.Sprintf(" kernel %s\n", kernel)
args := []string{
fmt.Sprintf("initrd=%s", initramfs),
fmt.Sprintf("token=%s", auth.GetTokenString()),
fmt.Sprintf("url=%s", b.GetNotifyUrl()),
}
bootmode := api.BOOT_MODE_PXE
if !isTftp {
adminNic := b.GetAdminNic()
var addr string
var mask string
var gateway string
if adminNic != nil {
addr = adminNic.IpAddr
mask = adminNic.GetNetMask()
gateway = adminNic.Gateway
} else {
accessIp := b.GetAccessIp()
accessNet, _ := b.findAccessNetwork(accessIp)
if accessNet != nil {
addr = accessIp
mask = netutils.Masklen2Mask(int8(accessNet.GuestIpMask)).String()
gateway = accessNet.GuestGateway
}
}
serverIP, _ := b.manager.Agent.GetDHCPServerIP()
args = append(args, fmt.Sprintf("dest=%s", serverIP))
args = append(args, fmt.Sprintf("gateway=%s", gateway))
args = append(args, fmt.Sprintf("addr=%s", addr))
args = append(args, fmt.Sprintf("mask=%s", mask))
bootmode = api.BOOT_MODE_ISO
}
args = append(args, fmt.Sprintf("bootmod=%s", bootmode))
resp += fmt.Sprintf(" append %s\n", strings.Join(args, " "))
} else {
resp += fmt.Sprintf(" COM32 %s\n", b.getSyslinuxPath("chain.c32", isTftp))
resp += " APPEND hd0 0\n"
b.ClearSSHConfig()
}
// log.Debugf("[SysLinux config]: \n%s", resp)
return resp
}
func (b *SBaremetalInstance) GetTaskQueue() *tasks.TaskQueue {
return b.taskQueue
}
func (b *SBaremetalInstance) GetTask() tasks.ITask {
return b.taskQueue.GetTask()
}
func (b *SBaremetalInstance) SetTask(task tasks.ITask) {
b.taskQueue.AppendTask(task)
if reflect.DeepEqual(task, b.taskQueue.GetTask()) {
log.Infof("Set task equal, ExecuteTask %s", task.GetName())
tasks.ExecuteTask(task, nil)
}
}
func (b *SBaremetalInstance) InitAdminNetif(
cliMac net.HardwareAddr,
wireId string,
nicType string,
netType string,
isDoImport bool,
importIpAddr string,
) error {
// start prepare task
// sync status to PREPARE
if !isDoImport && nicType == api.NIC_TYPE_ADMIN &&
utils.IsInStringArray(b.GetStatus(),
[]string{baremetalstatus.INIT,
baremetalstatus.PREPARE,
baremetalstatus.PREPARE_FAIL,
baremetalstatus.UNKNOWN}) &&
b.GetTask() == nil && b.GetServer() == nil {
b.SetTask(tasks.NewBaremetalServerPrepareTask(b))
b.SyncStatus(baremetalstatus.PREPARE, "")
}
nic := b.GetNicByMac(cliMac)
if nic == nil || nic.WireId == "" {
_, err := b.attachWire(cliMac, wireId, nicType)
if err != nil {
return err
}
return b.postAttachWire(cliMac, nicType, netType, importIpAddr)
} else if nic.IpAddr == "" {
return b.postAttachWire(cliMac, nicType, netType, importIpAddr)
}
return nil
}
func (b *SBaremetalInstance) RegisterNetif(cliMac net.HardwareAddr, wireId string) error {
var nicType string
nic := b.GetNicByMac(cliMac)
if nic != nil {
nicType = nic.Type
}
if nic == nil || nic.WireId == "" || nic.WireId != wireId {
desc, err := b.attachWire(cliMac, wireId, nicType)
if err != nil {
return err
}
return b.SaveDesc(desc)
}
return nil
}
func (b *SBaremetalInstance) attachWire(mac net.HardwareAddr, wireId string, nicType string) (jsonutils.JSONObject, error) {
session := b.manager.GetClientSession()
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(mac.String()), "mac")
if nicType != "" {
params.Add(jsonutils.NewString(nicType), "nic_type")
}
params.Add(jsonutils.NewString(wireId), "wire")
params.Add(jsonutils.NewInt(-1), "index")
params.Add(jsonutils.JSONTrue, "link_up")
return modules.Hosts.PerformAction(session, b.GetId(), "add-netif", params)
}
func (b *SBaremetalInstance) postAttachWire(mac net.HardwareAddr, nicType string, netType string, ipAddr string) error {
if ipAddr == "" {
switch nicType {
case api.NIC_TYPE_IPMI:
oldIPMIConf := b.GetRawIPMIConfig()
if oldIPMIConf != nil && oldIPMIConf.IpAddr != "" {
ipAddr = oldIPMIConf.IpAddr
}
case api.NIC_TYPE_ADMIN:
accessIp := b.GetAccessIp()
if accessIp != "" {
ipAddr = accessIp
}
}
}
desc, err := b.enableWire(mac, ipAddr, nicType, netType)
if err != nil {
return err
}
return b.SaveDesc(desc)
}
func (b *SBaremetalInstance) enableWire(mac net.HardwareAddr, ipAddr string, nicType string, netType string) (jsonutils.JSONObject, error) {
session := b.manager.GetClientSession()
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(mac.String()), "mac")
if nicType != "" {
params.Add(jsonutils.NewString(nicType), "nic_type")
}
if ipAddr != "" {
params.Add(jsonutils.NewString(ipAddr), "ip_addr")
params.Add(jsonutils.JSONTrue, "reserve")
}
if nicType == api.NIC_TYPE_IPMI {
params.Add(jsonutils.NewString("stepup"), "alloc_dir") // alloc bottom up
}
if len(netType) > 0 {
params.Add(jsonutils.NewString(netType), "net_type")
}
log.Infof("enable net if params: %s", params.String())
return modules.Hosts.PerformAction(session, b.GetId(), "enable-netif", params)
}
func (b *SBaremetalInstance) GetIPMIConfig() *types.SIPMIInfo {
conf := b.GetRawIPMIConfig()
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 == "" {
sysInfo := types.SSystemInfo{}
err := b.desc.Unmarshal(&sysInfo, "sys_info")
if err != nil {
log.Errorf("Unmarshal get sys_info error: %v", err)
}
conf.Username = profiles.GetRootName(&sysInfo)
}
if conf.IpAddr == "" {
nicIPAddr := b.GetIPMINicIPAddr()
if nicIPAddr != "" {
conf.IpAddr = nicIPAddr
}
}
conf.Password = utils.Unquote(conf.Password) // XXX: remove quotes!!!
if conf.IpAddr == "" {
log.Debugf("GetIPMIConfig ipaddr s nil")
return nil
}
return conf
}
func (b *SBaremetalInstance) GetRawIPMIConfig() *types.SIPMIInfo {
ipmiInfo := types.SIPMIInfo{}
err := b.desc.Unmarshal(&ipmiInfo, "ipmi_info")
if err != nil {
log.Errorf("Unmarshal IPMIInfo error: %v", err)
return nil
}
if ipmiInfo.Password != "" {
ipmiInfo.Password, err = utils.DescryptAESBase64(b.GetId(), ipmiInfo.Password)
if err != nil {
log.Errorf("DescryptAESBase64 IPMI password error: %v", err)
return nil
}
}
return &ipmiInfo
}
func (b *SBaremetalInstance) GetAccessIp() string {
accessIp, _ := b.desc.GetString("access_ip")
return accessIp
}
func (b *SBaremetalInstance) GetServer() baremetaltypes.IBaremetalServer {
b.serverLock.Lock()
defer b.serverLock.Unlock()
if !b.desc.Contains("server_id") && b.server != nil {
log.Warningf("baremetal %s server_id not present, remove server %q", b.GetName(), b.server.GetName())
b.RemoveServer()
return nil
}
return b.server
}
func (b *SBaremetalInstance) GetServerId() string {
srv := b.GetServer()
if srv == nil {
return ""
}
return srv.GetId()
}
func (b *SBaremetalInstance) GetServerName() string {
srv := b.GetServer()
if srv == nil {
return ""
}
return srv.GetName()
}
func (b *SBaremetalInstance) RemoveServer() {
b.serverLock.Lock()
defer b.serverLock.Unlock()
b.removeServer()
}
func (b *SBaremetalInstance) removeServer() {
if b.server != nil {
b.server.RemoveDesc()
b.server = nil
}
}
func (b *SBaremetalInstance) SetExistingIPMIIPAddr(ipAddr string) {
info, _ := b.desc.Get("ipmi_info")
if info == nil {
info = jsonutils.NewDict()
}
oIPAddr, _ := info.GetString("ip_addr")
if oIPAddr == "" {
info.(*jsonutils.JSONDict).Add(jsonutils.NewString(ipAddr), "ip_addr")
}
b.desc.Set("ipmi_info", info)
}
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)
}
func (b *SBaremetalInstance) isRedfishCapable() bool {
conf := b.GetIPMIConfig()
if conf == nil {
return false
}
if !conf.Verified {
return false
}
if !conf.RedfishApi {
return false
}
return true
}
func (b *SBaremetalInstance) GetRedfishCli(ctx context.Context) redfish.IRedfishDriver {
if !b.isRedfishCapable() {
return nil
}
conf := b.GetIPMIConfig()
return redfish.NewRedfishDriver(ctx, "https://"+conf.IpAddr,
conf.Username, conf.Password, false)
}
func (b *SBaremetalInstance) GetIPMILanChannel() int {
conf := b.GetIPMIConfig()
if conf == nil {
return 0
}
return conf.LanChannel
}
func (b *SBaremetalInstance) DoPXEBoot() error {
log.Infof("Do PXE Boot ........., wait")
b.ClearSSHConfig()
ipmiCli := b.GetIPMITool()
if ipmiCli != nil {
return ipmitool.DoRebootToPXE(ipmiCli)
}
return fmt.Errorf("Baremetal %s ipmitool is nil", b.GetId())
}
func (b *SBaremetalInstance) DoRedfishPowerOn() error {
log.Infof("Do Redfish PowerOn ........., wait")
ctx := context.Background()
b.ClearSSHConfig()
redfishApi := b.GetRedfishCli(ctx)
if redfishApi != nil {
return redfishApi.Reset(ctx, "On")
}
return fmt.Errorf("Baremetal %s redfishApi is nil", b.GetId())
}
/*
func (b *SBaremetalInstance) DoDiskBoot() error {
log.Infof("Do DISK Boot ........., wait")
b.ClearSSHConfig()
ipmiCli := b.GetIPMITool()
if ipmiCli != nil {
return ipmitool.DoRebootToDisk(ipmiCli)
}
return fmt.Errorf("Baremetal %s ipmitool is nil", b.GetId())
}
*/
func (b *SBaremetalInstance) GetPowerStatus() (string, error) {
ipmiCli := b.GetIPMITool()
if ipmiCli == nil {
return "", fmt.Errorf("Baremetal %s ipmitool is nil", b.GetId())
}
return ipmitool.GetChassisPowerStatus(ipmiCli)
}
func (b *SBaremetalInstance) DoPowerShutdown(soft bool) error {
b.ClearSSHConfig()
ipmiCli := b.GetIPMITool()
if ipmiCli != nil {
if soft {
return ipmitool.DoSoftShutdown(ipmiCli)
}
return ipmitool.DoHardShutdown(ipmiCli)
}
return fmt.Errorf("Baremetal %s ipmitool is nil", b.GetId())
}
func (b *SBaremetalInstance) GetStorageDriver() string {
driver, _ := b.desc.GetString("storage_driver")
return driver
}
func (b *SBaremetalInstance) GetZoneId() string {
return b.manager.GetZoneId()
}
func (b *SBaremetalInstance) GetZoneName() string {
return b.manager.GetZoneName()
}
func (b *SBaremetalInstance) GetStorageCacheId() string {
return b.manager.Agent.CacheManager.GetId()
}
func (b *SBaremetalInstance) GetBootMode() string {
bootMode, _ := b.desc.GetString("boot_mode")
if len(bootMode) == 0 {
bootMode = api.BOOT_MODE_PXE
}
return bootMode
}
func (b *SBaremetalInstance) GetRegion() string {
r, _ := b.desc.GetString("region")
return r
}
func (b *SBaremetalInstance) GetRegionId() string {
r, _ := b.desc.GetString("region_id")
return r
}
func (b *SBaremetalInstance) GetSerialNumber() string {
r, _ := b.desc.GetString("sn")
return r
}
func (b *SBaremetalInstance) GetManufacture() string {
r, _ := b.desc.GetString("sys_info", "manufacture")
return r
}
func (b *SBaremetalInstance) GetModel() string {
r, _ := b.desc.GetString("sys_info", "model")
return r
}
func (b *SBaremetalInstance) GetNodeCount() string {
r, _ := b.desc.GetString("node_count")
return r
}
func (b *SBaremetalInstance) GetMemGb() string {
memMb, _ := b.desc.Int("mem_size")
return strconv.FormatInt(memMb/1024, 10)
}
func (b *SBaremetalInstance) DelayedRemove(_ jsonutils.JSONObject) (jsonutils.JSONObject, error) {
b.remove()
return nil, nil
}
func (b *SBaremetalInstance) remove() {
b.manager.CleanBaremetal(b.GetId())
b.manager = nil
b.desc = nil
}
func (b *SBaremetalInstance) StartNewTask(factory tasks.TaskFactory, userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
go func() {
task := factory(userCred, b, taskId, data)
b.SetTask(task)
}()
}
func (b *SBaremetalInstance) StartBaremetalMaintenanceTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
if jsonutils.QueryBoolean(data, "force_reboot", false) {
b.ClearSSHConfig()
}
if jsonutils.QueryBoolean(data, "guest_running", false) {
data.(*jsonutils.JSONDict).Set("soft_reboot", jsonutils.JSONTrue)
}
b.StartNewTask(tasks.NewBaremetalMaintenanceTask, userCred, taskId, data)
}
func (b *SBaremetalInstance) StartBaremetalUnmaintenanceTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
b.StartNewTask(tasks.NewBaremetalUnmaintenanceTask, userCred, taskId, data)
}
func (b *SBaremetalInstance) StartBaremetalReprepareTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
b.StartNewTask(tasks.NewBaremetalReprepareTask, userCred, taskId, data)
}
func (b *SBaremetalInstance) StartBaremetalResetBMCTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
b.StartNewTask(tasks.NewBaremetalResetBMCTask, userCred, taskId, data)
return nil
}
func (b *SBaremetalInstance) StartBaremetalIpmiProbeTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
b.StartNewTask(tasks.NewBaremetalIpmiProbeTask, userCred, taskId, data)
return nil
}
func (b *SBaremetalInstance) StartBaremetalCdromTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
b.StartNewTask(tasks.NewBaremetalCdromTask, userCred, taskId, data)
return nil
}
func (b *SBaremetalInstance) DelayedServerReset(_ jsonutils.JSONObject) (jsonutils.JSONObject, error) {
err := b.DoPXEBoot()
return nil, err
}
func (b *SBaremetalInstance) StartServerCreateTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
b.serverLock.Lock()
defer b.serverLock.Unlock()
if b.server != nil {
return fmt.Errorf("Baremetal %s already have server %s", b.GetName(), b.server.GetName())
}
descData, err := data.Get("desc")
if err != nil {
return fmt.Errorf("Create data not found server desc: %v", err)
}
server, err := newBaremetalServer(b, descData.(*jsonutils.JSONDict))
if err != nil {
return fmt.Errorf("New server error: %v", err)
}
b.server = server
b.desc.Set("server_id", jsonutils.NewString(b.server.GetId()))
if err := b.AutoSaveDesc(); err != nil {
return err
}
b.StartNewTask(tasks.NewBaremetalServerCreateTask, userCred, taskId, data)
return nil
}
func (b *SBaremetalInstance) StartServerDeployTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
desc, err := data.Get("desc")
if err != nil {
return fmt.Errorf("Not found desc in data")
}
if err := b.GetServer().SaveDesc(desc); err != nil {
return fmt.Errorf("Save server desc: %v", err)
}
b.StartNewTask(tasks.NewBaremetalServerDeployTask, userCred, taskId, data)
return nil
}
func (b *SBaremetalInstance) StartServerRebuildTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
desc, err := data.Get("desc")
if err != nil {
return fmt.Errorf("Not found desc in data")
}
if err := b.GetServer().SaveDesc(desc); err != nil {
return fmt.Errorf("Save server desc: %v", err)
}
b.StartNewTask(tasks.NewBaremetalServerRebuildTask, userCred, taskId, data)
return nil
}
func (b *SBaremetalInstance) StartServerStartTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
b.StartNewTask(tasks.NewBaremetalServerStartTask, userCred, taskId, data)
return nil
}
func (b *SBaremetalInstance) StartServerStopTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
b.StartNewTask(tasks.NewBaremetalServerStopTask, userCred, taskId, data)
return nil
}
func (b *SBaremetalInstance) StartServerDestroyTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) {
b.StartNewTask(tasks.NewBaremetalServerDestroyTask, userCred, taskId, data)
}
func (b *SBaremetalInstance) DelayedSyncIPMIInfo(data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
ipmiCli := b.GetIPMITool()
lanChannel := b.GetIPMILanChannel()
sysInfo, err := ipmitool.GetSysInfo(ipmiCli)
if err != nil {
return nil, err
}
if lanChannel <= 0 {
lanChannel = ipmitool.GetDefaultLanChannel(sysInfo)
}
retObj := make(map[string]string)
if ipAddr, _ := data.GetString("ip_addr"); ipAddr != "" {
err = ipmitool.SetLanStaticIP(ipmiCli, lanChannel, ipAddr)
if err != nil {
return nil, err
}
// TODO: netutils.wait_ip_alive(ipAddr, 120)
retObj["ipmi_ip_addr"] = ipAddr
}
if passwd, _ := data.GetString("password"); passwd != "" {
err = ipmitool.SetLanPasswd(ipmiCli, ipmitool.GetRootId(sysInfo), passwd)
if err != nil {
return nil, err
}
retObj["ipmi_password"] = passwd
}
return jsonutils.Marshal(retObj), nil
}
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
}
func (b *SBaremetalInstance) DelayedServerStatus(data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
ps, err := b.GetPowerStatus()
if err != nil {
return nil, err
}
status := PowerStatusToServerStatus(b, ps)
resp := jsonutils.NewDict()
resp.Add(jsonutils.NewString(status), "status")
return resp, err
}
func (b *SBaremetalInstance) SendNicInfo(nic *types.SNicDevInfo, idx int, nicType string, reset bool, ipAddr string, reserve bool) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(nic.Mac.String()), "mac")
params.Add(jsonutils.NewInt(int64(nic.Speed)), "rate")
if idx >= 0 {
params.Add(jsonutils.NewInt(int64(idx)), "index")
}
if nicType != "" {
params.Add(jsonutils.NewString(nicType), "nic_type")
}
params.Add(jsonutils.NewInt(int64(nic.Mtu)), "mtu")
params.Add(jsonutils.NewBool(nic.Up), "link_up")
if reset {
params.Add(jsonutils.JSONTrue, "reset")
}
if ipAddr != "" {
params.Add(jsonutils.NewString(ipAddr), "ip_addr")
params.Add(jsonutils.JSONTrue, "require_designated_ip")
if reserve {
params.Add(jsonutils.JSONTrue, "reserve")
}
}
resp, err := modules.Hosts.PerformAction(
b.GetClientSession(),
b.GetId(),
"add-netif",
params,
)
if err != nil {
return err
}
return b.SaveDesc(resp)
}
func bindMount(src, dst string) error {
err := procutils.NewCommand("touch", dst).Run()
if err != nil {
return errors.Wrapf(err, "touch %s", dst)
}
err = procutils.NewCommand("mount", "-o", "ro,bind", src, dst).Run()
if err != nil {
return errors.Wrapf(err, "mount %s %s", src, dst)
}
return nil
}
func unbindMount(dst string) error {
err := procutils.NewCommand("umount", dst).Run()
if err != nil {
return errors.Wrapf(err, "umount %s", dst)
}
return nil
}
func (b *SBaremetalInstance) EnablePxeBoot() bool {
return jsonutils.QueryBoolean(b.desc, "enable_pxe_boot", true)
}
func (b *SBaremetalInstance) GenerateBootISO() error {
// precheck
conf := b.GetRawIPMIConfig()
if !conf.Verified {
return errors.Error("GenerateBootISO: IPMI not supported")
}
if !conf.CdromBoot {
return errors.Error("GenerateBootISO: cdrom boot not supported")
}
accessIp := b.GetAccessIp()
if accessIp == "" {
return errors.Error("GenerateBootISO: empty accessIp")
}
adminNic := b.GetAdminNic()
if adminNic == nil {
accessNet, _ := b.findAccessNetwork(accessIp)
if accessNet == nil {
return errors.Error("GenerateBootISO: Nil access Network")
}
}
ctx := context.Background()
redfishApi := b.GetRedfishCli(ctx)
if redfishApi == nil {
return errors.Wrap(httperrors.ErrNotSupported, "no valid redfishApi")
}
// generate ISO
isoDir, err := ioutil.TempDir("", "bmiso")
if err != nil {
return errors.Wrap(err, "ioutil.TempDir")
}
defer os.RemoveAll(isoDir)
isoLinDir := filepath.Join(isoDir, "isolinux")
err = os.Mkdir(isoLinDir, os.FileMode(0766))
if err != nil {
return errors.Wrapf(err, "Mkdir %s", isoLinDir)
}
for _, f := range []string{
"chain.c32", "ldlinux.c32", "libutil.c32", "libcom32.c32",
} {
err = bindMount(filepath.Join(o.Options.TftpRoot, f), filepath.Join(isoLinDir, f))
if err != nil {
return errors.Wrapf(err, "Link %s", f)
}
defer unbindMount(filepath.Join(isoLinDir, f))
}
for src, dst := range map[string]string{
"kernel": "vmlinuz",
"initramfs": "initrd.img",
} {
err = bindMount(filepath.Join(o.Options.TftpRoot, src), filepath.Join(isoLinDir, dst))
if err != nil {
return errors.Wrapf(err, "Link %s %s", src, dst)
}
defer unbindMount(filepath.Join(isoLinDir, dst))
}
for _, f := range []string{
"isolinux.bin",
} {
err = procutils.NewCommand("cp", filepath.Join(o.Options.TftpRoot, f), filepath.Join(isoLinDir, f)).Run()
if err != nil {
return errors.Wrapf(err, "cp %s", f)
}
}
cfgCont := b.getIsolinuxConf()
err = fileutils2.FilePutContents(filepath.Join(isoLinDir, "isolinux.cfg"), cfgCont, false)
if err != nil {
return errors.Wrap(err, "fileutils.FilePutContent")
}
args := []string{
"-quiet",
"-J", "-R",
"-input-charset", "utf-8",
"-b", "isolinux/isolinux.bin",
"-c", "isolinux/boot.cat",
"-no-emul-boot",
"-boot-load-size", "4",
"-boot-info-table",
"-o", b.getBootIsoImagePath(),
isoDir,
}
err = procutils.NewCommand("mkisofs", args...).Run()
if err != nil {
return errors.Wrap(err, "procutils.NewCommand mkisofs")
}
// mount the virtual media
err = redfish.MountVirtualCdrom(ctx, redfishApi, b.getBootIsoUrl(), true)
if err != nil {
return errors.Wrap(err, "redfish.MountVirtualCdrom")
}
return nil
}
func (b *SBaremetalInstance) clearBootIso() error {
ctx := context.Background()
redfishApi := b.GetRedfishCli(ctx)
if redfishApi == nil {
return errors.Wrap(httperrors.ErrNotSupported, "no valid redfishApi")
}
err := redfish.UmountVirtualCdrom(ctx, redfishApi)
if err != nil {
return errors.Wrap(err, "redfish.UmountVirtualCdrom")
}
return b.clearBootIsoImage()
}
func (b *SBaremetalInstance) clearBootIsoImage() error {
path := b.getBootIsoImagePath()
if fileutils2.Exists(path) {
return os.Remove(path)
}
return nil
}
func (b *SBaremetalInstance) getBootIsoImagePath() string {
return filepath.Join(o.Options.BootIsoPath, b.GetId()+".iso")
}
func (b *SBaremetalInstance) DoNTPConfig() error {
var urls []string
for _, ep := range []string{"internal", "public"} {
urls, _ = auth.GetServiceURLs("ntp", o.Options.Region, "", ep)
if len(urls) > 0 {
break
}
}
if len(urls) == 0 {
log.Warningf("NO ntp server specified, skip DoNTPConfig")
return nil
}
for i := range urls {
if strings.HasPrefix(urls[i], "ntp://") {
urls[i] = urls[i][6:]
}
}
log.Infof("Set NTP %s", urls)
ntpConf := redfish.SNTPConf{}
ntpConf.ProtocolEnabled = true
ntpConf.TimeZone = o.Options.TimeZone
ntpConf.NTPServers = urls
ctx := context.Background()
redfishApi := b.GetRedfishCli(ctx)
if redfishApi == nil {
return errors.Wrap(httperrors.ErrNotSupported, "no valid redfishApi")
}
err := redfishApi.SetNTPConf(ctx, ntpConf)
if err != nil {
return errors.Wrap(err, "redfishApi.SetNTPConf")
}
return nil
}
func (b *SBaremetalInstance) fetchLogs(ctx context.Context, logType string, since time.Time) ([]redfish.SEvent, error) {
redfishApi := b.GetRedfishCli(ctx)
if redfishApi == nil {
// errors.Wrap(httperrors.ErrNotSupported, "no valid redfish api")
return nil, nil
}
switch logType {
case redfish.EVENT_TYPE_SYSTEM:
return redfishApi.ReadSystemLogs(ctx, since)
case redfish.EVENT_TYPE_MANAGER:
return redfishApi.ReadManagerLogs(ctx, since)
}
return nil, errors.Wrap(httperrors.ErrNotSupported, logType)
}
func (b *SBaremetalInstance) clearLogs(ctx context.Context, logType string) error {
redfishApi := b.GetRedfishCli(ctx)
if redfishApi == nil {
return errors.Wrap(httperrors.ErrNotSupported, "no valid redfish api")
}
switch logType {
case redfish.EVENT_TYPE_SYSTEM:
return redfishApi.ClearSystemLogs(ctx)
case redfish.EVENT_TYPE_MANAGER:
return redfishApi.ClearManagerLogs(ctx)
}
return errors.Wrap(httperrors.ErrNotSupported, logType)
}
func (b *SBaremetalInstance) doCronJobs(ctx context.Context) {
for _, job := range b.cronJobs {
now := time.Now().UTC()
if job.NeedsToRun(now) {
// log.Debugf("need to run %s", job.Name())
func() {
job.StartRun()
defer job.StopRun()
err := job.Do(ctx, now)
if err != nil {
log.Errorf("Baremetal %s do cronjob %s fail: %s", b.GetName(), job.Name(), err)
}
}()
}
}
}
func (b *SBaremetalInstance) fetchPowerThermalMetrics(ctx context.Context) ([]influxdb.SKeyValue, []influxdb.SKeyValue, error) {
redfishApi := b.GetRedfishCli(ctx)
if redfishApi == nil {
return nil, nil, errors.Wrap(httperrors.ErrNotSupported, "no valid redfish api")
}
powers, err := redfishApi.GetPower(ctx)
if err != nil {
return nil, nil, errors.Wrap(err, "redfishApi.GetPower")
}
thermals, err := redfishApi.GetThermal(ctx)
if err != nil {
return nil, nil, errors.Wrap(err, "redfishApi.Thermal")
}
powerMetrics := powers[0].ToMetrics()
thermalMetrics := make([]influxdb.SKeyValue, 0)
for _, t := range thermals {
thermalMetrics = append(thermalMetrics, t.ToMetric())
}
return powerMetrics, thermalMetrics, nil
}
func (b *SBaremetalInstance) GetConsoleJNLP(ctx context.Context) (string, error) {
cli := b.GetRedfishCli(ctx)
if cli != nil {
return cli.GetConsoleJNLP(ctx)
}
conf := b.GetIPMIConfig()
bmc := bmconsole.NewBMCConsole(conf.IpAddr, conf.Username, conf.Password, false)
manufacture := b.GetManufacture()
switch strings.ToLower(manufacture) {
case "hp", "hpe":
return bmc.GetIloConsoleJNLP(ctx)
case "dell", "dell inc.":
return bmc.GetIdracConsoleJNLP(ctx, "", "")
case "supermicro":
return bmc.GetSupermicroConsoleJNLP(ctx)
case "lenovo":
return bmc.GetLenovoConsoleJNLP(ctx)
}
return "", httperrors.NewNotImplementedError("Unsupported manufacture %s", manufacture)
}
func (b *SBaremetalInstance) getTags() []influxdb.SKeyValue {
tags := []influxdb.SKeyValue{
{
Key: "id",
Value: b.GetId(),
},
{
Key: "name",
Value: b.GetName(),
},
{
Key: "zone_id",
Value: b.GetZoneId(),
},
{
Key: "zone",
Value: b.GetZoneName(),
},
{
Key: "boot_mode",
Value: b.GetBootMode(),
},
{
Key: "host_type",
Value: "baremetal",
},
{
Key: "region",
Value: b.GetRegion(),
},
{
Key: "region_id",
Value: b.GetRegionId(),
},
{
Key: "sn",
Value: b.GetSerialNumber(),
},
{
Key: "manufacture",
Value: b.GetManufacture(),
},
{
Key: "model",
Value: b.GetModel(),
},
{
Key: "status",
Value: b.GetStatus(),
},
{
Key: "ncpu",
Value: b.GetNodeCount(),
},
{
Key: "mem_gb",
Value: b.GetMemGb(),
},
}
srvId := b.GetServerId()
if len(srvId) > 0 {
tags = append(tags, influxdb.SKeyValue{
Key: "server_id",
Value: srvId,
})
tags = append(tags, influxdb.SKeyValue{
Key: "server",
Value: b.GetServerName(),
})
}
return tags
}
type SBaremetalServer struct {
baremetal *SBaremetalInstance
desc *jsonutils.JSONDict
}
func newBaremetalServer(baremetal *SBaremetalInstance, desc *jsonutils.JSONDict) (*SBaremetalServer, error) {
server := &SBaremetalServer{
baremetal: baremetal,
desc: desc,
}
err := server.SaveDesc(desc)
return server, err
}
func (server *SBaremetalServer) GetId() string {
id, err := server.desc.GetString("uuid")
if err != nil {
log.Fatalf("Get id from desc error: %v", err)
}
return id
}
func (server *SBaremetalServer) GetName() string {
id, err := server.desc.GetString("name")
if err != nil {
log.Errorf("Get name from desc %s error: %v", server.desc.String(), err)
}
return id
}
func (server *SBaremetalServer) SaveDesc(desc jsonutils.JSONObject) error {
if desc != nil {
server.desc = desc.(*jsonutils.JSONDict)
}
return ioutil.WriteFile(server.baremetal.GetServerDescFilePath(), []byte(server.desc.String()), 0644)
}
func (s *SBaremetalServer) RemoveDesc() {
os.Remove(s.baremetal.GetServerDescFilePath())
s.desc = nil
s.baremetal = nil
}
func (s *SBaremetalServer) GetRootTemplateId() string {
rootDisk, err := s.desc.GetAt(0, "disks")
if err != nil {
log.Errorf("Can't found root disk")
return ""
}
id, _ := rootDisk.GetString("template_id")
return id
}
func (s *SBaremetalServer) GetDiskConfig() ([]*api.BaremetalDiskConfig, error) {
layouts := make([]baremetal.Layout, 0)
err := s.desc.Unmarshal(&layouts, "disk_config")
if err != nil {
return nil, err
}
return baremetal.GetLayoutRaidConfig(layouts), nil
}
func (s *SBaremetalServer) DoDiskConfig(term *ssh.Client) error {
raid, nonRaid, pcie, err := detect_storages.DetectStorageInfo(term, true)
if err != nil {
return err
}
storages := make([]*baremetal.BaremetalStorage, 0)
storages = append(storages, raid...)
storages = append(storages, nonRaid...)
storages = append(storages, pcie...)
confs, err := s.GetDiskConfig()
if err != nil {
return err
}
layouts, err := baremetal.CalculateLayout(confs, storages)
if err != nil {
return fmt.Errorf("CalculateLayout: %v", err)
}
diskConfs := baremetal.GroupLayoutResultsByDriverAdapter(layouts)
for _, dConf := range diskConfs {
driver := dConf.Driver
raidDrv := raiddrivers.GetDriver(driver, term)
if raidDrv != nil {
if err := raidDrv.ParsePhyDevs(); err != nil {
return fmt.Errorf("RaidDriver %s parse physical devices: %v", raidDrv.GetName(), err)
}
raidDrv.CleanRaid()
}
}
for _, dConf := range diskConfs {
driver := dConf.Driver
adapter := dConf.Adapter
raidDrv := raiddrivers.GetDriver(driver, term)
if raidDrv != nil {
if err := raidDrv.ParsePhyDevs(); err != nil {
return fmt.Errorf("RaidDriver %s parse physical devices: %v", raidDrv.GetName(), err)
}
if err := raiddrivers.BuildRaid(raidDrv, dConf.Configs, adapter); err != nil {
return fmt.Errorf("Build %s raid failed: %v", raidDrv.GetName(), err)
}
time.Sleep(10 * time.Second) // wait 10 seconds for raid status OK
}
}
tool := disktool.NewSSHPartitionTool(term)
tool.FetchDiskConfs(baremetal.GetDiskConfigurations(layouts))
err = tool.RetrieveDiskInfo()
if err != nil {
return err
}
maxTries := 60
for tried := 0; !tool.IsAllDisksReady() && tried < maxTries; tried++ {
time.Sleep(5 * time.Second)
tool.RetrieveDiskInfo()
}
if !tool.IsAllDisksReady() {
return fmt.Errorf("Raid disks are not ready???")
}
return nil
}
func (s *SBaremetalServer) DoDiskUnconfig(term *ssh.Client) error {
// tear down raid
driver := s.baremetal.GetStorageDriver()
raidDrv := raiddrivers.GetDriver(driver, term)
if raidDrv != nil {
if err := raidDrv.ParsePhyDevs(); err != nil {
return err
}
raidDrv.CleanRaid()
}
return nil
}
func (s *SBaremetalServer) DoEraseDisk(term *ssh.Client) error {
cmd := "/lib/mos/partdestroy.sh"
_, err := term.Run(cmd)
return err
}
func replaceHostAddr(urlStr string, addr string) string {
urlComp, _ := url.Parse(urlStr)
commaPos := strings.IndexByte(urlComp.Host, ':')
if commaPos >= 0 {
urlComp.Host = addr + urlComp.Host[commaPos:]
} else {
urlComp.Host = addr
}
return urlComp.String()
}
func (s *SBaremetalServer) doCreateRoot(term *ssh.Client, devName string) error {
session := s.baremetal.GetClientSession()
token := session.GetToken().GetTokenString()
urlStr := s.baremetal.GetImageCacheUrl()
imageId := s.GetRootTemplateId()
cmd := fmt.Sprintf("/lib/mos/rootcreate.sh %s %s %s %s", token, urlStr, imageId, devName)
log.Infof("rootcreate cmd: %q", cmd)
if _, err := term.Run(cmd); err != nil {
return fmt.Errorf("Root create fail: %v", err)
}
return nil
}
func (s *SBaremetalServer) DoPartitionDisk(term *ssh.Client) ([]*disktool.Partition, error) {
raid, nonRaid, pcie, err := detect_storages.DetectStorageInfo(term, false)
if err != nil {
return nil, err
}
storages := make([]*baremetal.BaremetalStorage, 0)
storages = append(storages, raid...)
storages = append(storages, nonRaid...)
storages = append(storages, pcie...)
confs, err := s.GetDiskConfig()
if err != nil {
return nil, errors.Wrapf(err, "do disk config")
}
layouts, err := baremetal.CalculateLayout(confs, storages)
if err != nil {
return nil, errors.Wrapf(err, "CalculateLayout")
}
tool := disktool.NewSSHPartitionTool(term)
tool.FetchDiskConfs(baremetal.GetDiskConfigurations(layouts))
err = tool.RetrieveDiskInfo()
if err != nil {
return nil, errors.Wrapf(err, "RetrieveDiskInfo")
}
disks, _ := s.desc.GetArray("disks")
if len(disks) == 0 {
return nil, errors.Error("Empty disks in desc")
}
rootImageId := s.GetRootTemplateId()
diskOffset := 0
if len(rootImageId) > 0 {
rootDisk := disks[0]
rootSize, _ := rootDisk.Int("size")
err = s.doCreateRoot(term, tool.GetRootDisk().GetDevName())
if err != nil {
return nil, errors.Wrap(err, "Failed to create root")
}
tool.RetrievePartitionInfo()
parts := tool.GetPartitions()
if len(parts) == 0 {
return nil, errors.Error("Root disk create failed, no partitions")
}
log.Infof("Resize root to %d MB", rootSize)
if err := tool.ResizePartition(0, rootSize); err != nil {
return nil, errors.Wrapf(err, "Fail to resize root to %d", rootSize)
}
diskOffset = 1
} else {
tool.RetrievePartitionInfo()
parts := tool.GetPartitions()
if len(parts) > 0 {
return nil, errors.Error("should no partition!!!")
}
}
if len(disks) > diskOffset {
for _, disk := range disks[diskOffset:] {
sz, err := disk.Int("size")
if err != nil {
sz = -1
}
fs, _ := disk.GetString("fs")
uuid, _ := disk.GetString("disk_id")
driver, _ := disk.GetString("driver")
log.Infof("Create partition %d %s", sz, fs)
if err := tool.CreatePartition(-1, sz, fs, true, driver, uuid); err != nil {
return nil, errors.Wrapf(err, "Fail to create disk %s", disk.String())
}
}
}
log.Infof("Finish create partitions")
return tool.GetPartitions(), nil
}
func (s *SBaremetalServer) DoRebuildRootDisk(term *ssh.Client) ([]*disktool.Partition, error) {
raid, nonRaid, pcie, err := detect_storages.DetectStorageInfo(term, false)
if err != nil {
return nil, err
}
storages := make([]*baremetal.BaremetalStorage, 0)
storages = append(storages, raid...)
storages = append(storages, nonRaid...)
storages = append(storages, pcie...)
confs, err := s.GetDiskConfig()
if err != nil {
return nil, err
}
layouts, err := baremetal.CalculateLayout(confs, storages)
if err != nil {
return nil, err
}
tool := disktool.NewSSHPartitionTool(term)
tool.FetchDiskConfs(baremetal.GetDiskConfigurations(layouts))
err = tool.RetrieveDiskInfo()
if err != nil {
return nil, err
}
disks, _ := s.desc.GetArray("disks")
if len(disks) == 0 {
return nil, fmt.Errorf("Empty disks in desc")
}
rootDisk := disks[0]
rootSize, _ := rootDisk.Int("size")
rd := tool.GetRootDisk()
err = s.doCreateRoot(term, rd.GetDevName())
if err != nil {
return nil, fmt.Errorf("Failed to create root: %v", err)
}
tool.RetrievePartitionInfo()
if err := rd.ReInitInfo(); err != nil {
return nil, errors.Wrap(err, "Reinit root disk after create root")
}
log.Infof("Resize root to %d MB", rootSize)
if err := rd.ResizePartition(rootSize); err != nil {
return nil, fmt.Errorf("Fail to resize root to %d, err: %v", rootSize, err)
}
if len(disks) > 1 {
for _, disk := range disks[1:] {
sz, err := disk.Int("size")
if err != nil {
sz = -1
}
fs, _ := disk.GetString("fs")
uuid, _ := disk.GetString("disk_id")
log.Infof("Create partition %d %s", sz, fs)
if err := rd.CreatePartition(sz, fs, false, uuid); err != nil {
log.Errorf("Rebuild root create (%s, %d, %s) partition error: %v", uuid, sz, fs, err)
break
}
}
}
log.Infof("Finish create partitions")
parts := rd.GetPartitions()
restDisks := tool.Disks()
if len(restDisks) > 1 {
restDisks = restDisks[1:]
}
for _, d := range restDisks {
parts = append(parts, d.GetPartitions()...)
}
return parts, nil
}
func (s *SBaremetalServer) SyncPartitionSize(term *ssh.Client, parts []*disktool.Partition) ([]jsonutils.JSONObject, error) {
disks, _ := s.desc.GetArray("disks")
rootPartsCnt := len(parts) - len(disks) + 1
rootParts := parts[0:rootPartsCnt]
dataParts := parts[rootPartsCnt:]
idx := 0
size := (rootParts[len(rootParts)-1].GetEnd() + 1) * 512 / 1024 / 1024
disks[idx].(*jsonutils.JSONDict).Set("size", jsonutils.NewInt(int64(size)))
idx += 1
for _, p := range dataParts {
sizeMB, err := p.GetSizeMB()
if err != nil {
return nil, err
}
disks[idx].(*jsonutils.JSONDict).Set("size", jsonutils.NewInt(int64(sizeMB)))
disks[idx].(*jsonutils.JSONDict).Set("dev", jsonutils.NewString(p.GetDev()))
idx++
}
return disks, nil
}
func (s *SBaremetalServer) DoDeploy(term *ssh.Client, data jsonutils.JSONObject, isInit bool) (jsonutils.JSONObject, error) {
publicKey := deployapi.GetKeys(data)
deploys, _ := data.GetArray("deploys")
password, _ := data.GetString("password")
resetPassword := jsonutils.QueryBoolean(data, "reset_password", false)
if resetPassword && len(password) == 0 {
password = seclib.RandomPassword(12)
}
deployInfo := deployapi.NewDeployInfo(publicKey, deployapi.JsonDeploysToStructs(deploys),
password, isInit, true, o.Options.LinuxDefaultRootUser, o.Options.WindowsDefaultAdminUser, false, "")
return s.deployFs(term, deployInfo)
}
func (s *SBaremetalServer) deployFs(term *ssh.Client, deployInfo *deployapi.DeployInfo) (jsonutils.JSONObject, error) {
raid, nonRaid, pcie, err := detect_storages.DetectStorageInfo(term, false)
if err != nil {
return nil, err
}
storages := make([]*baremetal.BaremetalStorage, 0)
storages = append(storages, raid...)
storages = append(storages, nonRaid...)
storages = append(storages, pcie...)
confs, err := s.GetDiskConfig()
if err != nil {
return nil, err
}
layouts, err := baremetal.CalculateLayout(confs, storages)
if err != nil {
return nil, err
}
rootDev, rootfs, err := sshpart.MountSSHRootfs(term, layouts)
if err != nil {
return nil, fmt.Errorf("Find rootfs error: %s", err)
}
defer func() {
rootDev.Umount()
}()
if strings.ToLower(rootfs.GetOs()) == "windows" {
return nil, fmt.Errorf("Unsupported OS: %s", rootfs.GetOs())
}
return guestfs.DeployGuestFs(rootfs, s.desc, deployInfo)
}
func (s *SBaremetalServer) GetNics() []types.SServerNic {
nics := []types.SServerNic{}
err := s.desc.Unmarshal(&nics, "nics")
if err != nil {
log.Errorf("Unmarshal desc to get server nics error: %v", err)
return nil
}
return nics
}
func (s *SBaremetalServer) GetNicByMac(mac net.HardwareAddr) *types.SNic {
for _, n := range s.GetNics() {
if n.GetMac().String() == mac.String() {
nic := n.ToNic()
return &nic
}
}
return nil
}