add doc and copyright

This commit is contained in:
Qiu Jian
2019-10-06 00:11:41 +08:00
parent 25d0d478e7
commit 7e02839d67
88 changed files with 2777 additions and 502 deletions
+6 -1
View File
@@ -1 +1,6 @@
DESCRIPTION="Baremetal Agent Command Line Utility"
DESCRIPTION="Baremetal Agent Command Line Utility"
REQUIRES=(
ipmitool
mkisofs
)
+9
View File
@@ -47,6 +47,15 @@ func init() {
type BaremetalAgentOpsOperations struct {
ID string `help:"ID or name of agent"`
}
R(&BaremetalAgentOpsOperations{}, "agent-show", "Show details of an agent", func(s *mcclient.ClientSession, args *BaremetalAgentOpsOperations) error {
result, err := modules.Baremetalagents.Get(s, args.ID, nil)
if err != nil {
return err
}
printObject(result)
return nil
})
R(&BaremetalAgentOpsOperations{}, "agent-enable", "Enable agent", func(s *mcclient.ClientSession, args *BaremetalAgentOpsOperations) error {
result, err := modules.Baremetalagents.PerformAction(s, args.ID, "enable", nil)
if err != nil {
+55 -3
View File
@@ -22,6 +22,7 @@ import (
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
"yunion.io/x/onecloud/pkg/util/fileutils2"
)
func init() {
@@ -133,6 +134,12 @@ func init() {
return nil
})
R(&HostOpsOptions{}, "host-ipmi-probe", "Do ipmi probe host", func(s *mcclient.ClientSession, args *HostOpsOptions) error {
results := modules.Hosts.BatchPerformAction(s, args.ID, "ipmi-probe", nil)
printBatchResults(results, modules.Hosts.GetColumns(s))
return nil
})
R(&HostDetailOptions{}, "host-ipmi", "Get IPMI information of a host", func(s *mcclient.ClientSession, args *HostDetailOptions) error {
result, err := modules.Hosts.GetSpecific(s, args.ID, "ipmi", nil)
if err != nil {
@@ -185,7 +192,8 @@ func init() {
MemoryReserved string `help:"Memory reserved"`
CpuReserved int64 `help:"CPU reserved"`
HostType string `help:"Change host type, CAUTION!!!!" choices:"hypervisor|kubelet|esxi|baremetal"`
AccessIp string `help:"Change access ip, CAUTION!!!!"`
// AccessIp string `help:"Change access ip, CAUTION!!!!"`
AccessMac string `help:"Change access MAC, CAUTION!!!!"`
}
R(&HostUpdateOptions{}, "host-update", "Update information of a host", func(s *mcclient.ClientSession, args *HostUpdateOptions) error {
params := jsonutils.NewDict()
@@ -210,8 +218,8 @@ func init() {
if len(args.HostType) > 0 {
params.Add(jsonutils.NewString(args.HostType), "host_type")
}
if len(args.AccessIp) > 0 {
params.Add(jsonutils.NewString(args.AccessIp), "access_ip")
if len(args.AccessMac) > 0 {
params.Add(jsonutils.NewString(args.AccessMac), "access_mac")
}
if params.Size() == 0 {
return fmt.Errorf("Not data to update")
@@ -472,6 +480,14 @@ func init() {
IpmiUser string `help:"IPMI user name"`
IpmiPasswd string `help:"IPMI user password"`
IpmiAddr string `help:"IPMI IP address"`
AccessIp string `help:"Access IP address"`
AccessNet string `help:"Access network"`
AccessWire string `help:"Access wire"`
NoPrepare bool `help:"just initialize, do not reboot baremetal to prepare"`
DisablePxeBoot bool `help:"set enable_pxe_boot to false, which is true by default"`
}
R(&HostCreateOptions{}, "host-create", "Create a baremetal host", func(s *mcclient.ClientSession, args *HostCreateOptions) error {
params := jsonutils.NewDict()
@@ -493,6 +509,21 @@ func init() {
if len(args.IpmiAddr) > 0 {
params.Add(jsonutils.NewString(args.IpmiAddr), "ipmi_ip_addr")
}
if len(args.AccessIp) > 0 {
params.Add(jsonutils.NewString(args.AccessIp), "access_ip")
}
if len(args.AccessNet) > 0 {
params.Add(jsonutils.NewString(args.AccessNet), "access_net")
}
if len(args.AccessWire) > 0 {
params.Add(jsonutils.NewString(args.AccessWire), "access_wire")
}
if args.NoPrepare {
params.Add(jsonutils.JSONTrue, "no_prepare")
}
if args.DisablePxeBoot {
params.Add(jsonutils.JSONFalse, "enable_pxe_boot")
}
result, err := modules.Hosts.Create(s, params)
if err != nil {
return err
@@ -540,4 +571,25 @@ func init() {
printObject(spec)
return nil
})
type HostJnlpOptions struct {
ID string `help:"ID or name of host"`
Save string `help:"save xml into this file"`
}
R(&HostJnlpOptions{}, "host-jnlp", "Get host jnlp file contentn", func(s *mcclient.ClientSession, args *HostJnlpOptions) error {
spec, err := modules.Hosts.GetSpecific(s, args.ID, "jnlp", nil)
if err != nil {
return err
}
jnlp, err := spec.GetString("jnlp")
if err != nil {
return err
}
if len(args.Save) > 0 {
return fileutils2.FilePutContents(args.Save, jnlp, false)
} else {
fmt.Println(jnlp)
}
return nil
})
}
+11 -3
View File
@@ -24,14 +24,18 @@ import (
func init() {
type NetworkReserveIPOptions struct {
NETWORK string `help:"IP or name of network"`
NOTES string `help:"Why reserve this IP"`
IPS []string `help:"IPs to reserve"`
NETWORK string `help:"IP or name of network"`
NOTES string `help:"Why reserve this IP"`
IPS []string `help:"IPs to reserve"`
Duration string `help:"reservation duration, e.g. 1I, 1H, 2M"`
}
R(&NetworkReserveIPOptions{}, "network-reserve-ip", "Reserve an IP address from pool", func(s *mcclient.ClientSession, args *NetworkReserveIPOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewStringArray(args.IPS), "ips")
params.Add(jsonutils.NewString(args.NOTES), "notes")
if len(args.Duration) > 0 {
params.Add(jsonutils.NewString(args.Duration), "duration")
}
net, err := modules.Networks.PerformAction(s, args.NETWORK, "reserve-ip", params)
if err != nil {
return err
@@ -58,6 +62,7 @@ func init() {
type ReservedIPListOptions struct {
options.BaseListOptions
Network string `help:"Network filter"`
All bool `help:"show expired reserved ips"`
}
R(&ReservedIPListOptions{}, "reserved-ip-list", "Show all reserved IPs for any network", func(s *mcclient.ClientSession, args *ReservedIPListOptions) error {
var params *jsonutils.JSONDict
@@ -72,6 +77,9 @@ func init() {
if len(args.Network) > 0 {
params.Add(jsonutils.NewString(args.Network), "network")
}
if args.All {
params.Add(jsonutils.JSONTrue, "all")
}
result, err := modules.ReservedIPs.List(s, params)
if err != nil {
return err
+22
View File
@@ -32,6 +32,7 @@ import (
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
"yunion.io/x/onecloud/pkg/util/fileutils2"
)
func init() {
@@ -1147,4 +1148,25 @@ func init() {
printObject(result)
return nil
})
type ServerJnlpOptions struct {
ID string `help:"ID or name of server"`
Save string `help:"save xml into this file"`
}
R(&ServerJnlpOptions{}, "server-jnlp", "Get baremetal server jnlp file contentn", func(s *mcclient.ClientSession, args *ServerJnlpOptions) error {
spec, err := modules.Servers.GetSpecific(s, args.ID, "jnlp", nil)
if err != nil {
return err
}
jnlp, err := spec.GetString("jnlp")
if err != nil {
return err
}
if len(args.Save) > 0 {
return fileutils2.FilePutContents(args.Save, jnlp, false)
} else {
fmt.Println(jnlp)
}
return nil
})
}
+14
View File
@@ -1,3 +1,17 @@
// 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 (
+29 -23
View File
@@ -1,3 +1,17 @@
// 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 (
@@ -14,6 +28,17 @@ import (
func init() {
type LanConfigGetOptions struct {
}
shellutils.R(&LanConfigGetOptions{}, "lan-get", "Get configuration of BMC lan", func(cli redfish.IRedfishDriver, args *LanConfigGetOptions) error {
confs, err := cli.GetLanConfigs(context.Background())
if err != nil {
return err
}
printutils.PrintInterfaceList(confs, 0, 0, 0, nil)
return nil
})
type VirtualMediaGetOptions struct {
}
shellutils.R(&VirtualMediaGetOptions{}, "cdrom-get", "Get details of manager virtual media", func(cli redfish.IRedfishDriver, args *VirtualMediaGetOptions) error {
@@ -28,21 +53,12 @@ func init() {
})
type VirtualMediaMountOptions struct {
URL string `help:"cdrom http URL"`
URL string `help:"cdrom http URL"`
Boot bool `help:"set boot from virtualmedia on next boot"`
}
shellutils.R(&VirtualMediaMountOptions{}, "cdrom-insert", "Insert iso into virtual CD-ROM", func(cli redfish.IRedfishDriver, args *VirtualMediaMountOptions) error {
ctx := context.Background()
path, cdInfo, err := cli.GetVirtualCdromInfo(ctx)
if err != nil {
return err
}
if len(cdInfo.Image) > 0 {
return fmt.Errorf("image %s in cd-rom", cdInfo.Image)
}
if !cdInfo.SupportAction {
return fmt.Errorf("action not supported")
}
err = cli.MountVirtualCdrom(context.Background(), path, args.URL)
err := redfish.MountVirtualCdrom(ctx, cli, args.URL, args.Boot)
if err != nil {
return err
}
@@ -54,17 +70,7 @@ func init() {
}
shellutils.R(&VirtualMediaUmountOptions{}, "cdrom-eject", "Eject iso from virtual CD-ROM", func(cli redfish.IRedfishDriver, args *VirtualMediaUmountOptions) error {
ctx := context.Background()
path, cdInfo, err := cli.GetVirtualCdromInfo(ctx)
if err != nil {
return err
}
if len(cdInfo.Image) == 0 {
return fmt.Errorf("no image in cd-rom")
}
if !cdInfo.SupportAction {
return fmt.Errorf("action not supported")
}
err = cli.UmountVirtualCdrom(context.Background(), path)
err := redfish.UmountVirtualCdrom(ctx, cli)
if err != nil {
return err
}
+14
View File
@@ -1,3 +1,17 @@
// 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 (
+15 -6
View File
@@ -1,3 +1,17 @@
// 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 (
@@ -37,12 +51,7 @@ func init() {
DEV string `help:"next boot device"`
}
shellutils.R(&SetNextBootOptions{}, "set-next-boot-dev", "Set next boot device", func(cli redfish.IRedfishDriver, args *SetNextBootOptions) error {
var err error
if args.DEV == "vcd" {
err = cli.SetNextBootVirtualCdrom(context.Background())
} else {
err = cli.SetNextBootDev(context.Background(), args.DEV)
}
err := cli.SetNextBootDev(context.Background(), args.DEV)
if err != nil {
return err
}
+8
View File
@@ -63,6 +63,10 @@ const (
BAREMETAL_START_FAIL = "start_fail"
BAREMETAL_STOP_FAIL = "stop_fail"
BAREMETAL_START_PROBE = "start_probe"
BAREMETAL_PROBING = "probing"
BAREMETAL_PROBE_FAIL = "probe_fail"
HOST_STATUS_RUNNING = BAREMETAL_RUNNING
HOST_STATUS_READY = BAREMETAL_READY
HOST_STATUS_UNKNOWN = BAREMETAL_UNKNOWN
@@ -96,3 +100,7 @@ var HOST_TYPES = []string{
}
var NIC_TYPES = []string{NIC_TYPE_IPMI, NIC_TYPE_ADMIN}
const (
ACCESS_MAC_ANY = "00:00:00:00:00:00"
)
+10
View File
@@ -56,3 +56,13 @@ var (
CLOUD_PROVIDER_UCLOUD,
}
)
type IPAllocationDirection string
const (
IPAllocationStepdown IPAllocationDirection = "stepdown"
IPAllocationStepup IPAllocationDirection = "stepup"
IPAllocationRadnom IPAllocationDirection = "random"
IPAllocationNone IPAllocationDirection = "none"
IPAllocationDefault = ""
)
+31 -3
View File
@@ -18,15 +18,18 @@ import (
"context"
"fmt"
"net"
"net/http"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/appsrv"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/baremetal/pxe"
"yunion.io/x/onecloud/pkg/cloudcommon/agent"
"yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/util/httputils"
"yunion.io/x/onecloud/pkg/util/procutils"
)
@@ -52,7 +55,7 @@ type SBaremetalAgent struct {
func newBaremetalAgent() (*SBaremetalAgent, error) {
agent := &SBaremetalAgent{}
err := agent.Init(agent, o.Options.ListenInterface)
err := agent.Init(agent, o.Options.ListenInterface, o.Options.CachePath)
if err != nil {
return nil, err
}
@@ -131,7 +134,9 @@ func (agent *SBaremetalAgent) StartService() error {
}
agent.Manager = manager
agent.startPXEServices(manager)
agent.startFileServer()
agent.DoOnline(agent.GetAdminSession())
return nil
@@ -180,7 +185,25 @@ func (agent *SBaremetalAgent) startPXEServices(manager *SBaremetalManager) {
}()
}
func Start() error {
func (agent *SBaremetalAgent) startFileServer() {
dhcpListenIp, err := agent.GetDHCPServerListenIP()
if err != nil {
log.Fatalf("Get dhcp listen ip address error: %v", err)
}
fs := http.FileServer(httputils.Dir(o.Options.TftpRoot))
http.Handle("/tftp/", http.StripPrefix("/tftp/", fs))
cacheFs := http.FileServer(httputils.Dir(o.Options.CachePath))
http.Handle("/images/", http.StripPrefix("/images/", cacheFs))
isoFs := http.FileServer(httputils.Dir(o.Options.BootIsoPath))
http.Handle("/bootiso/", http.StripPrefix("/bootiso/", isoFs))
go func() {
if err := http.ListenAndServe(fmt.Sprintf("%s:%d", dhcpListenIp, o.Options.Port+1000), nil); err != nil {
panic(fmt.Sprintf("start http file server: %v", err))
}
}()
}
func Start(app *appsrv.Application) error {
var err error
if baremetalAgent != nil {
log.Warningf("Global baremetalAgent already start")
@@ -190,7 +213,12 @@ func Start() error {
if err != nil {
return err
}
return baremetalAgent.Start()
err = baremetalAgent.Start()
if err != nil {
return err
}
baremetalAgent.AddImageCacheHandler("", app)
return nil
}
func Stop() error {
+7 -1
View File
@@ -63,12 +63,13 @@ func initBaremetalsHandler(app *appsrv.Application) {
AddHandler(app, "GET", bmActionPrefix("notify"), bmObjMiddleware(handleBaremetalNotify))
AddHandler(app, "POST", bmActionPrefix("maintenance"), bmObjMiddleware(handleBaremetalMaintenance))
AddHandler(app, "POST", bmActionPrefix("unmaintenance"), bmObjMiddleware(handleBaremetalUnmaintenance))
AddHandler(app, "POST", bmActionPrefix("delete"), bmObjMiddleware(handleBaremetalDelete))
AddHandler(app, "POST", bmActionPrefix("delete"), bmObjMiddlewareWithFetch(handleBaremetalDelete, false))
AddHandler(app, "POST", bmActionPrefix("syncstatus"), bmObjMiddleware(handleBaremetalSyncStatus))
AddHandler(app, "POST", bmActionPrefix("sync-config"), bmObjMiddleware(handleBaremetalSyncConfig))
AddHandler(app, "POST", bmActionPrefix("sync-ipmi"), bmObjMiddleware(handleBaremetalSyncIPMI))
AddHandler(app, "POST", bmActionPrefix("prepare"), bmObjMiddleware(handleBaremetalPrepare))
AddHandler(app, "POST", bmActionPrefix("reset-bmc"), bmObjMiddleware(handleBaremetalResetBMC))
AddHandler(app, "POST", bmActionPrefix("ipmi-probe"), bmObjMiddleware(handleBaremetalIpmiProbe))
// server actions handler
AddHandler(app, "POST", srvActionPrefix("create"), srvClassMiddleware(handleServerCreate))
@@ -148,6 +149,11 @@ func handleBaremetalResetBMC(ctx *Context, bm *baremetal.SBaremetalInstance) {
ctx.ResponseOk()
}
func handleBaremetalIpmiProbe(ctx *Context, bm *baremetal.SBaremetalInstance) {
bm.StartBaremetalIpmiProbeTask(ctx.UserCred(), ctx.TaskId(), ctx.Data())
ctx.ResponseOk()
}
func handleServerCreate(ctx *Context, bm *baremetal.SBaremetalInstance) {
err := bm.StartServerCreateTask(ctx.UserCred(), ctx.TaskId(), ctx.Data())
if err != nil {
+15 -2
View File
@@ -78,13 +78,26 @@ func authMiddleware(h handlerFunc) appsrv.FilterHandler {
type bmObjHandlerFunc func(ctx *Context, bm *baremetal.SBaremetalInstance)
func bmObjMiddleware(h bmObjHandlerFunc) appsrv.FilterHandler {
return bmObjMiddlewareWithFetch(h, true)
}
func bmObjMiddlewareWithFetch(h bmObjHandlerFunc, fetch bool) appsrv.FilterHandler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
newCtx := NewContext(ctx, w, r)
bmId := newCtx.Params()[PARAMS_BMID_KEY]
baremetal := newCtx.GetBaremetalManager().GetBaremetalById(bmId)
if baremetal == nil {
newCtx.ResponseError(httperrors.NewNotFoundError("Not found baremetal by id: %s", bmId))
return
if fetch {
err := newCtx.GetBaremetalManager().InitBaremetal(bmId, false)
if err != nil {
newCtx.ResponseError(err)
return
}
baremetal = newCtx.GetBaremetalManager().GetBaremetalById(bmId)
} else {
newCtx.ResponseError(httperrors.NewNotFoundError("Not found baremetal by id: %s", bmId))
return
}
}
h(newCtx, baremetal)
}
+381 -31
View File
@@ -28,11 +28,10 @@ import (
"sync"
"time"
"github.com/pkg/errors"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
yerrors "yunion.io/x/pkg/util/errors"
"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"
@@ -61,7 +60,9 @@ import (
"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/procutils"
"yunion.io/x/onecloud/pkg/util/redfish"
"yunion.io/x/onecloud/pkg/util/ssh"
"yunion.io/x/onecloud/pkg/util/sysutils"
)
@@ -110,11 +111,10 @@ func (m *SBaremetalManager) loadConfigs() error {
}
}
session := m.GetClientSession()
errsChannel := make(chan error, len(bmIds))
initBaremetal := func(i int) {
bmId := bmIds[i]
err := m.initBaremetal(session, bmId)
err := m.InitBaremetal(bmId, true)
if err != nil {
errsChannel <- err
return
@@ -128,21 +128,34 @@ func (m *SBaremetalManager) loadConfigs() error {
errs = append(errs, <-errsChannel)
}
}
return yerrors.NewAggregate(errs)
return errors.NewAggregate(errs)
}
func (m *SBaremetalManager) initBaremetal(session *mcclient.ClientSession, bmId string) error {
desc, err := m.updateBaremetal(session, bmId)
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
}
bmObj := bmInstance.(*SBaremetalInstance)
if !sets.NewString(INIT, PREPARE, UNKNOWN).Has(bmObj.GetStatus()) {
bmObj.SyncStatusBackground()
if update {
bmObj := bmInstance.(*SBaremetalInstance)
if !sets.NewString(INIT, PREPARE, UNKNOWN).Has(bmObj.GetStatus()) {
bmObj.SyncStatusBackground()
}
}
return nil
}
@@ -152,6 +165,7 @@ func (m *SBaremetalManager) CleanBaremetal(bmId string) {
if bm != nil {
bm.Stop()
}
bm.clearBootIsoImage()
path := bm.GetDir()
procutils.NewCommand("rm", "-fr", path).Run()
}
@@ -167,6 +181,15 @@ func (m *SBaremetalManager) updateBaremetal(session *mcclient.ClientSession, bmI
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 {
@@ -575,6 +598,7 @@ func (b *SBaremetalInstance) SaveSSHConfig(remoteAddr string, key string) error
return err
}
b.SyncSSHConfig(sshConf)
b.clearBootIso()
return err
}
@@ -926,13 +950,63 @@ func (b *SBaremetalInstance) getTftpFileUrl(filename string) string {
log.Errorf("Get http file server: %v", err)
return filename
}
if o.Options.EnableTftpHttpDownload {
return fmt.Sprintf("http://%s:%d/tftp/%s", serverIP, o.Options.Port+1000, 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 ""
}
return filename
// 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.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
@@ -942,15 +1016,45 @@ LABEL start
`
if b.NeedPXEBoot() {
resp += fmt.Sprintf(" kernel %s\n", b.getTftpFileUrl("kernel"))
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", b.getTftpFileUrl("initramfs")),
fmt.Sprintf("initrd=%s", initramfs),
fmt.Sprintf("token=%s", auth.GetTokenString()),
fmt.Sprintf("url=%s", b.GetNotifyUrl()),
}
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))
}
resp += fmt.Sprintf(" append %s\n", strings.Join(args, " "))
} else {
resp += fmt.Sprintf(" COM32 %s\n", b.getTftpFileUrl("chain.c32"))
resp += fmt.Sprintf(" COM32 %s\n", b.getSyslinuxPath("chain.c32", isTftp))
resp += " APPEND hd0 0\n"
b.ClearSSHConfig()
}
@@ -1038,10 +1142,18 @@ func (b *SBaremetalInstance) attachWire(mac net.HardwareAddr, wireId string, nic
}
func (b *SBaremetalInstance) postAttachWire(mac net.HardwareAddr, nicType string, netType string, ipAddr string) error {
if nicType == api.NIC_TYPE_IPMI {
oldIPMIConf := b.GetRawIPMIConfig()
if oldIPMIConf != nil && oldIPMIConf.IpAddr != "" {
ipAddr = oldIPMIConf.IpAddr
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)
@@ -1060,6 +1172,7 @@ func (b *SBaremetalInstance) enableWire(mac net.HardwareAddr, ipAddr string, nic
}
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
@@ -1114,6 +1227,11 @@ func (b *SBaremetalInstance) GetRawIPMIConfig() *types.SIPMIInfo {
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()
@@ -1174,6 +1292,15 @@ func (b *SBaremetalInstance) GetIPMITool() *ipmitool.LanPlusIPMI {
return ipmitool.NewLanPlusIPMI(conf.IpAddr, conf.Username, conf.Password)
}
func (b *SBaremetalInstance) GetRedfishCli(ctx context.Context) redfish.IRedfishDriver {
conf := b.GetIPMIConfig()
if conf == nil {
return nil
}
return redfish.NewRedfishDriver(ctx, "https://"+conf.IpAddr,
conf.Username, conf.Password, false)
}
func (b *SBaremetalInstance) GetIPMILanChannel() int {
conf := b.GetIPMIConfig()
if conf == nil {
@@ -1192,6 +1319,17 @@ func (b *SBaremetalInstance) DoPXEBoot() error {
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")
@@ -1233,6 +1371,10 @@ func (b *SBaremetalInstance) GetZoneId() string {
return b.manager.GetZoneId()
}
func (b *SBaremetalInstance) GetStorageCacheId() string {
return b.manager.Agent.CacheManager.GetId()
}
func (b *SBaremetalInstance) DelayedRemove(_ jsonutils.JSONObject) (jsonutils.JSONObject, error) {
b.remove()
return nil, nil
@@ -1279,6 +1421,11 @@ func (b *SBaremetalInstance) StartBaremetalResetBMCTask(userCred mcclient.TokenC
return nil
}
func (b *SBaremetalInstance) StartBaremetalIpmiProbeTask(userCred mcclient.TokenCredential, taskId string, data jsonutils.JSONObject) error {
b.StartNewTask(tasks.NewBaremetalIpmiProbeTask, taskId, data)
return nil
}
func (b *SBaremetalInstance) DelayedServerReset(_ jsonutils.JSONObject) (jsonutils.JSONObject, error) {
err := b.DoPXEBoot()
return nil, err
@@ -1390,6 +1537,216 @@ func (b *SBaremetalInstance) DelayedServerStatus(data jsonutils.JSONObject) (jso
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
}
type SBaremetalServer struct {
baremetal *SBaremetalInstance
desc *jsonutils.JSONDict
@@ -1548,14 +1905,7 @@ func replaceHostAddr(urlStr string, addr string) string {
func (s *SBaremetalServer) doCreateRoot(term *ssh.Client, devName string) error {
session := s.baremetal.GetClientSession()
token := session.GetToken().GetTokenString()
urlStr, err := session.GetServiceURL("image", "internalURL")
if err != nil {
return err
}
// this is hackish, url should point to an image proxy
// XXX
listenIp, _ := s.baremetal.manager.Agent.GetListenIP()
urlStr = replaceHostAddr(urlStr, listenIp.String())
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)
@@ -1592,7 +1942,7 @@ func (s *SBaremetalServer) DoPartitionDisk(term *ssh.Client) ([]*disktool.Partit
disks, _ := s.desc.GetArray("disks")
if len(disks) == 0 {
return nil, errors.New("Empty disks in desc")
return nil, errors.Error("Empty disks in desc")
}
rootDisk := disks[0]
@@ -1605,7 +1955,7 @@ func (s *SBaremetalServer) DoPartitionDisk(term *ssh.Client) ([]*disktool.Partit
tool.RetrievePartitionInfo()
parts := tool.GetPartitions()
if len(parts) == 0 {
return nil, errors.New("Root disk create failed, no partitions")
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 {
+6 -5
View File
@@ -76,11 +76,12 @@ func GetNicDHCPConfig(
case 6:
conf.BootFile = "bootia32.efi"
default:
bootFile := "pxelinux.0"
if o.Options.EnableTftpHttpDownload {
bootFile = "lpxelinux.0"
}
conf.BootFile = bootFile
//if o.Options.EnableTftpHttpDownload {
// bootFile = "lpxelinux.0"
//}else {
// bootFile := "pxelinux.0"
//}
conf.BootFile = "lpxelinux.0"
}
pxePath := filepath.Join(o.Options.TftpRoot, conf.BootFile)
if f, err := os.Open(pxePath); err != nil {
+5 -1
View File
@@ -41,7 +41,11 @@ type BaremetalOptions struct {
DefaultStrongIpmiPassword string `help:"Default strong IPMI passowrd"`
WindowsDefaultAdminUser bool `default:"true" help:"Default account for Windows system is Administrator"`
EnableTftpHttpDownload bool `default:"true" help:"Pxelinux download file through http"`
// EnableTftpHttpDownload bool `default:"true" help:"Pxelinux download file through http"`
CachePath string `help:"local image cache directory"`
EnablePxeBoot bool `help:"Enable DHCP PXE boot" default:"true"`
BootIsoPath string `help:"iso boot image path"`
}
var (
+8 -5
View File
@@ -15,13 +15,13 @@
package pxe
import (
"errors"
"fmt"
"net"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
o "yunion.io/x/onecloud/pkg/baremetal/options"
@@ -130,10 +130,10 @@ func (h *DHCPHandler) newRequest(pkt dhcp.Packet, man IBaremetalManager) (*dhcpR
// expect to boot.
case 17:
if data[0] != 0 {
err = errors.New("malformed client GUID (option 97), leading byte must be zero")
err = errors.Error("malformed client GUID (option 97), leading byte must be zero")
}
default:
err = errors.New("malformed client GUID (option 97), wrong size")
err = errors.Error("malformed client GUID (option 97), wrong size")
}
cliGuid, err = req.Options.String(optCode)
}
@@ -158,6 +158,9 @@ func (req *dhcpRequest) fetchConfig(session *mcclient.ClientSession) (*dhcp.Resp
// TODO: set cache for netConf
if req.isPXERequest() {
if !o.Options.EnablePxeBoot {
return nil, errors.Error("PXE Boot disabled")
}
// handle PXE DHCP request
log.Infof("DHCP relay from %s(%s) for %s, find matched networks: %#v", req.RelayAddr, req.ClientAddr, req.ClientMac, netConf)
bmDesc, err := req.createOrUpdateBaremetal(session)
@@ -389,10 +392,10 @@ func (s *Server) validateDHCP(pkt dhcp.Packet) (Machine, Firmware, error) {
// well accept these buggy ROMs.
case 17:
if guid[0] != 0 {
return mach, 0, errors.New("malformed client GUID (option 97), leading byte must be zero")
return mach, 0, errors.Error("malformed client GUID (option 97), leading byte must be zero")
}
default:
return mach, 0, errors.New("malformed client GUID (option 97), wrong size")
return mach, 0, errors.Error("malformed client GUID (option 97), wrong size")
}
mach.MAC = pkt.CHAddr()
+24 -19
View File
@@ -15,12 +15,12 @@
package service
import (
"fmt"
"net/http"
"os"
"path/filepath"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/baremetal"
"yunion.io/x/onecloud/pkg/baremetal/handler"
o "yunion.io/x/onecloud/pkg/baremetal/options"
@@ -29,6 +29,8 @@ import (
common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
"yunion.io/x/onecloud/pkg/cloudcommon/service"
"yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
_ "yunion.io/x/onecloud/pkg/util/redfish/loader"
)
type BaremetalService struct {
@@ -41,13 +43,29 @@ func New() *BaremetalService {
func (s *BaremetalService) StartService() {
common_options.ParseOptions(&o.Options, os.Args, "baremetal.conf", "baremetal")
app_common.InitAuth(&o.Options.CommonOptions, s.startAgent)
if len(o.Options.CachePath) == 0 {
o.Options.CachePath = filepath.Join(filepath.Dir(o.Options.BaremetalsPath), "bm_image_cache")
log.Infof("No cachepath, use default %s", o.Options.CachePath)
}
if len(o.Options.BootIsoPath) == 0 {
o.Options.BootIsoPath = filepath.Join(filepath.Dir(o.Options.BaremetalsPath), "bm_boot_iso")
log.Infof("No BootIsoPath, use default %s", o.Options.BootIsoPath)
err := os.MkdirAll(o.Options.BootIsoPath, os.FileMode(0760))
if err != nil {
log.Fatalf("fail to create BootIsoPath %s", o.Options.BootIsoPath)
}
}
app_common.InitAuth(&o.Options.CommonOptions, func() {
log.Infof("auth complete")
})
fsdriver.Init(nil)
app := app_common.InitApp(&o.Options.BaseOptions, false)
handler.InitHandlers(app)
s.startFileServer()
s.startAgent(app)
app_common.ServeForeverWithCleanup(app, &o.Options.BaseOptions, func() {
tasks.OnStop()
@@ -55,21 +73,8 @@ func (s *BaremetalService) StartService() {
})
}
func (s *BaremetalService) startFileServer() {
if !o.Options.EnableTftpHttpDownload {
return
}
fs := http.FileServer(http.Dir(o.Options.TftpRoot))
http.Handle("/tftp/", http.StripPrefix("/tftp/", fs))
go func() {
if err := http.ListenAndServe(fmt.Sprintf("%s:%d", o.Options.Address, o.Options.Port+1000), nil); err != nil {
panic(fmt.Sprintf("start http file server: %v", err))
}
}()
}
func (s *BaremetalService) startAgent() {
err := baremetal.Start()
func (s *BaremetalService) startAgent(app *appsrv.Application) {
err := baremetal.Start(app)
if err != nil {
log.Fatalf("Start agent error: %v", err)
}
+25 -6
View File
@@ -21,11 +21,11 @@ import (
"sync"
"time"
"github.com/pkg/errors"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/ssh"
@@ -181,6 +181,7 @@ type TaskFactory func(bm IBaremetal, taskId string, data jsonutils.JSONObject) (
type SBaremetalTaskBase struct {
Baremetal IBaremetal
PxeBoot bool
userCred mcclient.TokenCredential
stageFunc TaskStageFunc
sshStageFunc SSHTaskStageFunc
@@ -286,14 +287,18 @@ func (self *SBaremetalTaskBase) EnsurePowerShutdown(soft bool) error {
}
func (self *SBaremetalTaskBase) EnsurePowerUp() error {
log.Infof("EnsurePowerUp: bootdev=pxe")
log.Infof("EnsurePowerUp: bootdev=pxe %v", self.PxeBoot)
status, err := self.Baremetal.GetPowerStatus()
if err != nil {
return errors.Wrapf(err, "Get power status")
}
for status == "" || status == types.POWER_STATUS_OFF {
if status == types.POWER_STATUS_OFF {
err = self.Baremetal.DoPXEBoot()
if self.PxeBoot {
err = self.Baremetal.DoPXEBoot()
} else {
err = self.Baremetal.DoRedfishPowerOn()
}
if err != nil {
return errors.Wrapf(err, "Do PXE boot")
}
@@ -353,6 +358,18 @@ func (self *SBaremetalPXEBootTaskBase) InitPXEBootTask(pxeBootTask IPXEBootTask,
pxeBootTask.SetSSHStageParams(pxeBootTask, sshConf.RemoteIP, sshConf.Password)
return self, nil
}
// generate ISO
if err := self.Baremetal.GenerateBootISO(); err != nil {
log.Errorf("GenerateBootISO fail: %s", err)
if !o.Options.EnablePxeBoot || !self.Baremetal.EnablePxeBoot() {
return self, errors.Wrap(err, "self.Baremetal.GenerateBootISO")
}
self.PxeBoot = true
} else {
self.PxeBoot = false
}
// Do soft reboot
if data != nil && jsonutils.QueryBoolean(data, "soft_boot", false) {
self.startTime = time.Now()
@@ -364,12 +381,14 @@ func (self *SBaremetalPXEBootTaskBase) InitPXEBootTask(pxeBootTask IPXEBootTask,
return self, nil
}
// shutdown and power up to PXE mode
if err := self.EnsurePowerShutdown(false); err != nil {
return self, fmt.Errorf("EnsurePowerShutdown: %v", err)
return self, errors.Wrap(err, "EnsurePowerShutdown")
}
if err := self.EnsurePowerUp(); err != nil {
return self, errors.Wrapf(err, "EnsurePowerUp to pxe")
return self, errors.Wrap(err, "EnsurePowerUp to pxe")
}
// this stage will be called by baremetalInstance when pxe start notify
self.SetSSHStage(pxeBootTask.OnPXEBoot)
+86 -67
View File
@@ -21,6 +21,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/seclib"
"yunion.io/x/pkg/utils"
@@ -110,8 +111,13 @@ func (task *sBaremetalPrepareTask) prepareBaremetalInfo(cli *ssh.Client) (*barem
return nil, err
}
ipmiInfo := &types.SIPMIInfo{
Present: ipmiEnable,
ipmiInfo := task.baremetal.GetRawIPMIConfig()
if ipmiInfo == nil {
ipmiInfo = &types.SIPMIInfo{}
}
if !ipmiInfo.Present && ipmiEnable {
ipmiInfo.Present = true
ipmiInfo.Verified = false
}
return &baremetalPrepareInfo{
@@ -131,6 +137,11 @@ func (task *sBaremetalPrepareTask) configIPMISetting(cli *ssh.Client, i *baremet
return nil
}
// if verified, skip ipmi config
if i.ipmiInfo.Verified {
return nil
}
var (
sysInfo = i.sysInfo
ipmiInfo = i.ipmiInfo
@@ -138,8 +149,8 @@ func (task *sBaremetalPrepareTask) configIPMISetting(cli *ssh.Client, i *baremet
sshIPMI := ipmitool.NewSSHIPMI(cli)
// ipmitool.SetSysInfo
ipmiSysInfo := sysInfo.ToIPMISystemInfo()
SetIPMILanPortShared(sshIPMI, ipmiSysInfo)
ipmiUser, ipmiPasswd, ipmiIpAddr := task.getIPMIUserPasswd(ipmiSysInfo)
setIPMILanPortShared(sshIPMI, ipmiSysInfo)
ipmiUser, ipmiPasswd, ipmiIpAddr := task.getIPMIUserPasswd(i.ipmiInfo, ipmiSysInfo)
ipmiInfo.Username = ipmiUser
ipmiInfo.Password = ipmiPasswd
@@ -162,7 +173,8 @@ func (task *sBaremetalPrepareTask) configIPMISetting(cli *ssh.Client, i *baremet
Speed: 100,
Mtu: 1500,
}
if err := task.sendNicInfo(ipmiNic, -1, api.NIC_TYPE_IPMI, true, ""); err != nil {
if err := task.sendNicInfo(ipmiNic, -1, api.NIC_TYPE_IPMI, true, "", false); err != nil {
// ignore the error
log.Errorf("Send IPMI nic %#v info: %v", ipmiNic, err)
}
rootId := ipmitool.GetRootId(ipmiSysInfo)
@@ -206,6 +218,7 @@ func (task *sBaremetalPrepareTask) configIPMISetting(cli *ssh.Client, i *baremet
err = ipmitool.SetLanDHCP(sshIPMI, lanChannel)
if err != nil {
// ignore error
log.Errorf("Set lan channel %d dhcp error: %v", lanChannel, err)
}
time.Sleep(2 * time.Second)
@@ -266,6 +279,7 @@ func (task *sBaremetalPrepareTask) configIPMISetting(cli *ssh.Client, i *baremet
return fmt.Errorf("Fail to get IPMI address from DHCP")
}
ipmiInfo.LanChannel = ipmiLanChannel
ipmiInfo.Verified = true
return nil
}
@@ -284,12 +298,51 @@ func (task *sBaremetalPrepareTask) DoPrepare(cli *ssh.Client) error {
return err
}
// set NTP
if err = task.baremetal.DoNTPConfig(); err != nil {
// ignore error
log.Errorf("SetNTP fail: %s", err)
}
log.Infof("Prepare complete")
return nil
}
func (task *sBaremetalPrepareTask) findAdminNic(cli *ssh.Client, nicsInfo []*types.SNicDevInfo) (int, *types.SNicDevInfo, error) {
for idx := range nicsInfo {
nic := nicsInfo[idx]
output, err := cli.Run("/sbin/ifconfig " + nic.Dev)
if err != nil {
log.Errorf("ifconfig %s fail: %s", nic.Dev, err)
continue
}
isAdmin := false
for _, l := range output {
if strings.Contains(l, task.baremetal.GetAccessIp()) {
isAdmin = true
break
}
}
if isAdmin {
return idx, nic, nil
}
}
return -1, nil, errors.Error("admin nic not found???")
}
func (task *sBaremetalPrepareTask) updateBmInfo(cli *ssh.Client, i *baremetalPrepareInfo) error {
adminNic := task.baremetal.GetAdminNic()
if adminNic == nil {
adminIdx, adminNicDev, err := task.findAdminNic(cli, i.nicsInfo)
if err != nil {
return errors.Wrap(err, "task.findAdminNic")
}
err = task.sendNicInfo(adminNicDev, adminIdx, api.NIC_TYPE_ADMIN, false, task.baremetal.GetAccessIp(), true)
if err != nil {
return errors.Wrap(err, "send Admin Nic Info")
}
adminNic = task.baremetal.GetAdminNic()
}
// collect params
updateInfo := make(map[string]interface{})
oname := fmt.Sprintf("BM%s", strings.Replace(adminNic.Mac, ":", "", -1))
@@ -297,6 +350,7 @@ func (task *sBaremetalPrepareTask) updateBmInfo(cli *ssh.Client, i *baremetalPre
updateInfo["name"] = fmt.Sprintf("BM-%s", strings.Replace(i.ipmiInfo.IpAddr, ".", "-", -1))
}
updateInfo["access_ip"] = adminNic.IpAddr
updateInfo["access_mac"] = adminNic.Mac
updateInfo["cpu_count"] = i.cpuInfo.Count
updateInfo["node_count"] = i.dmiCpuInfo.Nodes
updateInfo["cpu_desc"] = i.cpuInfo.Model
@@ -315,9 +369,11 @@ func (task *sBaremetalPrepareTask) updateBmInfo(cli *ssh.Client, i *baremetalPre
_, err := modules.Hosts.Update(task.getClientSession(), task.baremetal.GetId(), updateData)
if err != nil {
log.Errorf("Update baremetal info error: %v", err)
return errors.Wrap(err, "Hosts.Update")
}
if err := task.sendStorageInfo(size); err != nil {
log.Errorf("sendStorageInfo error: %v", err)
return errors.Wrap(err, "task.sendStorageInfo")
}
// XXX do not change nic order anymore
// for i := range nicsInfo {
@@ -337,19 +393,24 @@ func (task *sBaremetalPrepareTask) updateBmInfo(cli *ssh.Client, i *baremetalPre
err = task.removeNicInfo(removedMacs[idx])
if err != nil {
log.Errorf("Fail to remove Netif %s: %s", removedMacs[idx], err)
return errors.Wrap(err, "task.removeNicInfo")
}
}
for idx := range i.nicsInfo {
err = task.sendNicInfo(i.nicsInfo[idx], idx, "", false, "")
err = task.sendNicInfo(i.nicsInfo[idx], idx, "", false, "", false)
if err != nil {
log.Errorf("Send nicinfo idx: %d, %#v error: %v", idx, i.nicsInfo[idx], err)
return errors.Wrap(err, "task.sendNicInfo")
}
}
for _, nicInfo := range i.nicsInfo {
if nicInfo.Mac.String() != adminNic.GetMac().String() && nicInfo.Up {
err = task.doNicWireProbe(cli, nicInfo)
if err != nil {
log.Errorf("doNicWireProbe nic %#v error: %v", nicInfo, err)
if o.Options.EnablePxeBoot && task.baremetal.EnablePxeBoot() {
for _, nicInfo := range i.nicsInfo {
if nicInfo.Mac.String() != adminNic.GetMac().String() && nicInfo.Up {
err = task.doNicWireProbe(cli, nicInfo)
if err != nil {
// ignore the error
log.Errorf("doNicWireProbe nic %#v error: %v", nicInfo, err)
}
}
}
}
@@ -431,7 +492,7 @@ func (task *sBaremetalPrepareTask) tryLocalIpmiAddr(sshIPMI *ipmitool.SSHIPMI, i
if tried < maxTries {
// make sure the ipaddr is a IPMI address
// enable the netif
err := task.sendNicInfo(ipmiNic, -1, api.NIC_TYPE_IPMI, false, tryAddr)
err := task.sendNicInfo(ipmiNic, -1, api.NIC_TYPE_IPMI, false, tryAddr, true)
if err != nil {
log.Errorf("Fail to set existing BMC IP address to %s", tryAddr)
} else {
@@ -441,7 +502,7 @@ func (task *sBaremetalPrepareTask) tryLocalIpmiAddr(sshIPMI *ipmitool.SSHIPMI, i
return false
}
func (task *sBaremetalPrepareTask) getIPMIUserPasswd(sysInfo *types.SIPMISystemInfo) (string, string, string) {
func (task *sBaremetalPrepareTask) getIPMIUserPasswd(oldIPMIConf *types.SIPMIInfo, sysInfo *types.SIPMISystemInfo) (string, string, string) {
var (
ipmiUser string
ipmiPasswd string
@@ -458,17 +519,14 @@ func (task *sBaremetalPrepareTask) getIPMIUserPasswd(sysInfo *types.SIPMISystemI
} else {
ipmiPasswd = seclib.RandomPassword(20)
}
oldIPMIConf := task.baremetal.GetRawIPMIConfig()
if oldIPMIConf != nil {
if oldIPMIConf.Username != "" {
ipmiUser = oldIPMIConf.Username
}
if oldIPMIConf.Password != "" {
ipmiPasswd = oldIPMIConf.Password
}
if oldIPMIConf.IpAddr != "" {
ipmiIpAddr = oldIPMIConf.IpAddr
}
if oldIPMIConf.Username != "" {
ipmiUser = oldIPMIConf.Username
}
if oldIPMIConf.Password != "" {
ipmiPasswd = oldIPMIConf.Password
}
if oldIPMIConf.IpAddr != "" {
ipmiIpAddr = oldIPMIConf.IpAddr
}
return ipmiUser, ipmiPasswd, ipmiIpAddr
}
@@ -502,19 +560,6 @@ func (task *sBaremetalPrepareTask) getClientSession() *mcclient.ClientSession {
return task.baremetal.GetClientSession()
}
func (task *sBaremetalPrepareTask) removeAllNics() error {
resp, err := modules.Hosts.PerformAction(
task.getClientSession(),
task.baremetal.GetId(),
"remove-all-netifs",
nil,
)
if err != nil {
return nil
}
return task.baremetal.SaveDesc(resp)
}
func getDMISysinfo(cli *ssh.Client) (*types.SDMISystemInfo, error) {
ret, err := cli.Run("/usr/sbin/dmidecode -t 1")
if err != nil {
@@ -578,41 +623,15 @@ func (task *sBaremetalPrepareTask) removeNicInfo(mac string) error {
return task.baremetal.SaveDesc(resp)
}
func (task *sBaremetalPrepareTask) sendNicInfo(nic *types.SNicDevInfo, idx int, nicType string, reset bool, ipAddr string) 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")
}
resp, err := modules.Hosts.PerformAction(
task.getClientSession(),
task.baremetal.GetId(),
"add-netif",
params,
)
if err != nil {
return err
}
return task.baremetal.SaveDesc(resp)
func (task *sBaremetalPrepareTask) sendNicInfo(nic *types.SNicDevInfo, idx int, nicType string, reset bool, ipAddr string, reserve bool) error {
return task.baremetal.SendNicInfo(nic, idx, nicType, reset, ipAddr, reserve)
}
func (task *sBaremetalPrepareTask) sendStorageInfo(size int64) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewInt(size), "capacity")
params.Add(jsonutils.NewString(task.baremetal.GetZoneId()), "zone_id")
params.Add(jsonutils.NewString(task.baremetal.GetStorageCacheId()), "storagecache_id")
_, err := modules.Hosts.PerformAction(task.getClientSession(), task.baremetal.GetId(), "update-storage", params)
return err
}
@@ -656,7 +675,7 @@ func (task *sBaremetalPrepareTask) collectDiskInfo(diskInfo []*baremetal.Baremet
return size, diskType
}
func SetIPMILanPortShared(cli ipmitool.IPMIExecutor, sysInfo *types.SIPMISystemInfo) {
func setIPMILanPortShared(cli ipmitool.IPMIExecutor, sysInfo *types.SIPMISystemInfo) {
if !o.Options.IpmiLanPortShared {
return
}
+2 -2
View File
@@ -171,7 +171,7 @@ func (s *sBaremetalRegisterTask) updateIpmiInfo(cli *ssh.Client) {
} else {
nic.Mac = conf.Mac
}
s.sendNicInfo(nic, -1, api.NIC_TYPE_IPMI, false, "")
s.sendNicInfo(nic, -1, api.NIC_TYPE_IPMI, false, "", false)
}
func (s *sBaremetalRegisterTask) updateBmInfo(cli *ssh.Client, i *baremetalPrepareInfo) error {
@@ -200,7 +200,7 @@ func (s *sBaremetalRegisterTask) updateBmInfo(cli *ssh.Client, i *baremetalPrepa
log.Errorf("sendStorageInfo error: %v", err)
}
for idx := range i.nicsInfo {
err = s.sendNicInfo(i.nicsInfo[idx], idx, "", false, "")
err = s.sendNicInfo(i.nicsInfo[idx], idx, "", false, "", false)
if err != nil {
log.Errorf("Send nicinfo idx: %d, %#v error: %v", idx, i.nicsInfo[idx], err)
}
+8
View File
@@ -28,6 +28,7 @@ import (
type IBaremetal interface {
GetId() string
GetZoneId() string
GetStorageCacheId() string
GetTaskQueue() *TaskQueue
GetSSHConfig() (*types.SSHConfig, error)
TestSSHConfig() bool
@@ -52,6 +53,13 @@ type IBaremetal interface {
DoPXEBoot() error
// DoDiskBoot() error
DoRedfishPowerOn() error
GetAccessIp() string
EnablePxeBoot() bool
GenerateBootISO() error
SendNicInfo(nic *types.SNicDevInfo, idx int, nicType string, reset bool, ipAddr string, reserve bool) error
DoNTPConfig() error
RemoveServer()
InitializeServer(name string) error
SaveSSHConfig(remoteAddr string, key string) error
+227
View File
@@ -0,0 +1,227 @@
// 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 tasks
import (
"context"
"net"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/redfish"
)
type SBaremetalIpmiProbeTask struct {
*SBaremetalTaskBase
}
func NewBaremetalIpmiProbeTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
baseTask := newBaremetalTaskBase(baremetal, taskId, data)
task := new(SBaremetalIpmiProbeTask)
task.SBaremetalTaskBase = baseTask
task.SetStage(task.DoIpmiProbe)
log.Debugf("Start SBaremetalIpmiProbeTask XXXXXX!!!!!")
return task, nil
}
func (self *SBaremetalIpmiProbeTask) GetName() string {
return "BaremetalIpmiProbeTask"
}
func (self *SBaremetalIpmiProbeTask) DoIpmiProbe(ctx context.Context, args interface{}) error {
ipmiInfo := self.Baremetal.GetRawIPMIConfig()
if ipmiInfo == nil {
ipmiInfo = &types.SIPMIInfo{}
}
if ipmiInfo.IpAddr == "" {
return errors.Error("empty IPMI ip_addr")
}
if ipmiInfo.Username == "" {
return errors.Error("empty IPMI username")
}
if ipmiInfo.Password == "" {
return errors.Error("empty IPMI password")
}
redfishCli := redfish.NewRedfishDriver(ctx, "https://"+ipmiInfo.IpAddr, ipmiInfo.Username, ipmiInfo.Password, false)
if redfishCli != nil {
return self.doRedfishIpmiProbe(ctx, redfishCli)
} else {
log.Warningf("BMC not redfish-compatible")
ipmiTool := ipmitool.NewLanPlusIPMI(ipmiInfo.IpAddr, ipmiInfo.Username, ipmiInfo.Password)
return self.doRawIpmiProbe(ctx, ipmiTool)
}
}
func (self *SBaremetalIpmiProbeTask) doRedfishIpmiProbe(ctx context.Context, drv redfish.IRedfishDriver) error {
confs, err := drv.GetLanConfigs(ctx)
if err != nil {
return errors.Wrap(err, "drv.GetLanConfigs")
}
if len(confs) == 0 {
return errors.Wrap(httperrors.ErrNotFound, "no IPMI lan")
}
err = self.sendIpmiNicInfo(&confs[0])
if err != nil {
return errors.Wrap(err, "self.sendIpmiNicInfo")
}
_, sysInfo, err := drv.GetSystemInfo(ctx)
if err != nil {
return errors.Wrap(err, "drv.GetSystemInfo")
}
updateInfo := make(map[string]interface{})
if len(sysInfo.EthernetNICs) > 0 {
updateInfo["access_mac"] = sysInfo.EthernetNICs[0]
}
updateInfo["node_count"] = sysInfo.NodeCount
updateInfo["cpu_count"] = sysInfo.NodeCount
updateInfo["cpu_desc"] = sysInfo.CpuDesc
updateInfo["mem_size"] = sysInfo.MemoryGB * 1024
updateInfo["sn"] = sysInfo.SerialNumber
dmiSysInfo := &types.SDMISystemInfo{
Manufacture: sysInfo.Manufacturer,
Model: sysInfo.Model,
SN: sysInfo.SerialNumber,
}
updateInfo["sys_info"] = dmiSysInfo
updateInfo["is_baremetal"] = true
ipmiInfo := self.Baremetal.GetRawIPMIConfig()
if ipmiInfo == nil {
ipmiInfo = &types.SIPMIInfo{}
}
ipmiInfo.Present = true
ipmiInfo.Verified = true
ipmiInfo.RedfishApi = true
_, cdInfo, _ := drv.GetVirtualCdromInfo(ctx)
ipmiInfo.CdromBoot = cdInfo.SupportAction
ipmiInfo.PxeBoot = o.Options.EnablePxeBoot
updateData := jsonutils.Marshal(updateInfo)
updateData.(*jsonutils.JSONDict).Update(ipmiInfo.ToPrepareParams())
_, err = modules.Hosts.Update(self.Baremetal.GetClientSession(), self.Baremetal.GetId(), updateData)
if err != nil {
log.Errorf("Update baremetal info error: %v", err)
return errors.Wrap(err, "modules.Hosts.Update")
}
for i := range sysInfo.EthernetNICs {
mac, err := net.ParseMAC(sysInfo.EthernetNICs[i])
if err == nil {
err = self.sendNicInfo(i, mac)
if err != nil {
return errors.Wrapf(err, "sendNicInfo %d %s", i, mac)
}
}
}
self.Baremetal.SyncStatus("", "Probe Redfish finished")
SetTaskComplete(self, nil)
return nil
}
func (self *SBaremetalIpmiProbeTask) sendIpmiNicInfo(lanConf *types.SIPMILanConfig) error {
speed := lanConf.SpeedMbps
if speed <= 0 {
speed = 100
}
ipmiNic := &types.SNicDevInfo{
Mac: lanConf.Mac,
Up: true,
Speed: speed,
Mtu: 1500,
}
err := self.Baremetal.SendNicInfo(ipmiNic, -1, api.NIC_TYPE_IPMI, true, lanConf.IPAddr, true)
if err != nil {
return errors.Wrap(err, "SendNicInfo")
}
return nil
}
func (self *SBaremetalIpmiProbeTask) sendNicInfo(index int, mac net.HardwareAddr) error {
nicInfo := &types.SNicDevInfo{
Mac: mac,
}
err := self.Baremetal.SendNicInfo(nicInfo, index, "", true, "", false)
if err != nil {
return errors.Wrap(err, "SendNicInfo")
}
return nil
}
func (self *SBaremetalIpmiProbeTask) doRawIpmiProbe(ctx context.Context, cli ipmitool.IPMIExecutor) error {
sysInfo, err := ipmitool.GetSysInfo(cli)
if err != nil {
// ignore error for qemu
log.Errorf("ipmitool.GetSysInfo error %s", err)
}
var conf *types.SIPMILanConfig
var channel int
for _, lanChannel := range ipmitool.GetLanChannels(sysInfo) {
conf, err = ipmitool.GetLanConfig(cli, lanChannel)
if err != nil {
// ignore error
log.Errorf("ipmitool.GetLanConfig for channel %d fail: %s", lanChannel, err)
} else {
channel = lanChannel
break
}
}
if conf == nil {
return errors.Wrap(httperrors.ErrNotFound, "no IPMI lan")
}
err = self.sendIpmiNicInfo(conf)
if err != nil {
return errors.Wrap(err, "self.sendIpmiNicInfo")
}
updateInfo := make(map[string]interface{})
if len(sysInfo.SN) > 0 {
updateInfo["sn"] = sysInfo.SN
dmiSysInfo := &types.SDMISystemInfo{
Manufacture: sysInfo.Manufacture,
Model: sysInfo.Model,
Version: sysInfo.Version,
SN: sysInfo.SN,
}
updateInfo["sys_info"] = dmiSysInfo
}
updateInfo["is_baremetal"] = true
ipmiInfo := self.Baremetal.GetRawIPMIConfig()
if ipmiInfo == nil {
ipmiInfo = &types.SIPMIInfo{}
}
ipmiInfo.Present = true
ipmiInfo.Verified = true
ipmiInfo.RedfishApi = false
ipmiInfo.CdromBoot = false
ipmiInfo.PxeBoot = o.Options.EnablePxeBoot
ipmiInfo.LanChannel = channel
updateData := jsonutils.Marshal(updateInfo)
updateData.(*jsonutils.JSONDict).Update(ipmiInfo.ToPrepareParams())
_, err = modules.Hosts.Update(self.Baremetal.GetClientSession(), self.Baremetal.GetId(), updateData)
if err != nil {
return errors.Wrap(err, "modules.Hosts.Update")
}
self.Baremetal.SyncStatus("", "Probie IPMI finished")
SetTaskComplete(self, nil)
return nil
}
+6 -3
View File
@@ -34,7 +34,6 @@ import (
"yunion.io/x/onecloud/pkg/util/procutils"
"yunion.io/x/onecloud/pkg/util/ssh"
"yunion.io/x/onecloud/pkg/util/stringutils2"
stage_stringutils "yunion.io/x/onecloud/pkg/util/stringutils2"
"yunion.io/x/onecloud/pkg/util/sysutils"
)
@@ -144,7 +143,8 @@ func GetSysInfo(exector IPMIExecutor) (*types.SIPMISystemInfo, error) {
args := []string{"fru", "print", "0"}
lines, err := exector.ExecuteCommand(args...)
if err != nil {
return nil, err
// ignore error
log.Errorf("fru print 0 error: %s", err)
}
ret := make(map[string]string)
@@ -215,6 +215,9 @@ func GetLanConfig(exector IPMIExecutor, channel int) (*types.SIPMILanConfig, err
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
@@ -352,7 +355,7 @@ func CreateOrSetAdminUser(exector IPMIExecutor, channel int, rootId int, usernam
func SetLanUserAdminPasswd(exector IPMIExecutor, channel int, id int, password string) error {
var err error
password, err = stage_stringutils.EscapeEchoString(password)
password, err = stringutils2.EscapeEchoString(password)
if err != nil {
return fmt.Errorf("EscapeEchoString for password: %s, error: %v", password, err)
}
+86 -11
View File
@@ -21,9 +21,11 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/version"
"yunion.io/x/onecloud/pkg/cloudcommon/object"
"yunion.io/x/onecloud/pkg/hostman/storageman"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
@@ -55,6 +57,9 @@ type SBaseAgent struct {
AgentName string
Zone *SZoneInfo
CachePath string
CacheManager *storageman.SLocalImageCacheManager
stop bool
}
@@ -78,7 +83,7 @@ func (agent *SBaseAgent) IAgent() IAgent {
return agent.GetVirtualObject().(IAgent)
}
func (agent *SBaseAgent) Init(iagent IAgent, ifname string) error {
func (agent *SBaseAgent) Init(iagent IAgent, ifname string, cachePath string) error {
iface, err := net.InterfaceByName(ifname)
if err != nil {
return err
@@ -105,6 +110,7 @@ func (agent *SBaseAgent) Init(iagent IAgent, ifname string) error {
agent.SetVirtualObject(iagent)
agent.ListenInterface = iface
agent.ListenIPs = ips
agent.CachePath = cachePath
return nil
}
@@ -259,7 +265,7 @@ func (agent *SBaseAgent) createOrUpdateBaremetalAgent(session *mcclient.ClientSe
} else {
cloudBmAgent := ret.Data[0]
agentId, _ := cloudBmAgent.GetString("id")
cloudObj, err = agent.updateBaremetalAgent(session, agentId)
cloudObj, err = agent.updateBaremetalAgent(session, agentId, "")
if err != nil {
return err
}
@@ -276,6 +282,25 @@ func (agent *SBaseAgent) createOrUpdateBaremetalAgent(session *mcclient.ClientSe
agent.AgentId = agentId
agent.AgentName = agentName
storageCacheId, _ := cloudObj.GetString("storagecache_id")
if len(storageCacheId) == 0 {
storageCacheId, err = agent.createStorageCache(session)
if err != nil {
return err
}
_, err = agent.updateBaremetalAgent(session, agentId, storageCacheId)
if err != nil {
return err
}
} else {
err = agent.updateStorageCache(session, storageCacheId)
if err != nil {
return err
}
}
agent.CacheManager = storageman.NewLocalImageCacheManager(agent.IAgent(), agent.CachePath, storageCacheId)
return nil
}
@@ -298,13 +323,17 @@ func (agent *SBaseAgent) GetListenUri() string {
}
func (agent *SBaseAgent) getCreateUpdateInfo() (jsonutils.JSONObject, error) {
accessIP, err := agent.IAgent().GetAccessIP()
if err != nil {
return nil, err
}
params := jsonutils.NewDict()
if agent.AgentId == "" {
params.Add(jsonutils.NewString(fmt.Sprintf("%s_%s", agent.IAgent().GetAgentType(), accessIP)), "name")
agentName, err := agent.getName()
if err != nil {
return nil, errors.Wrap(err, "agent.getName")
}
params.Add(jsonutils.NewString(agentName), "name")
}
accessIP, err := agent.IAgent().GetAccessIP()
if err != nil {
return nil, errors.Wrap(err, "agent.IAgent().GetAccessIP()")
}
params.Add(jsonutils.NewString(accessIP.String()), "access_ip")
params.Add(jsonutils.NewString(agent.GetManagerUri()), "manager_uri")
@@ -315,6 +344,45 @@ func (agent *SBaseAgent) getCreateUpdateInfo() (jsonutils.JSONObject, error) {
return params, nil
}
func (agent *SBaseAgent) getName() (string, error) {
accessIP, err := agent.IAgent().GetAccessIP()
if err != nil {
return "", err
}
return fmt.Sprintf("%s-%s", agent.IAgent().GetAgentType(), accessIP), nil
}
func (agent *SBaseAgent) createStorageCache(session *mcclient.ClientSession) (string, error) {
body := jsonutils.NewDict()
agentName, err := agent.getName()
if err != nil {
return "", errors.Wrap(err, "agent.getName")
}
body.Set("name", jsonutils.NewString("imagecache-"+agentName))
body.Set("path", jsonutils.NewString(agent.CachePath))
body.Set("external_id", jsonutils.NewString(agent.AgentId))
sc, err := modules.Storagecaches.Create(session, body)
if err != nil {
return "", errors.Wrap(err, "modules.Storagecaches.Create")
}
storageCacheId, err := sc.GetString("id")
if err != nil {
return "", errors.Wrap(err, "sc.GetString id")
}
return storageCacheId, nil
}
func (agent *SBaseAgent) updateStorageCache(session *mcclient.ClientSession, storageCacheId string) error {
body := jsonutils.NewDict()
body.Set("path", jsonutils.NewString(agent.CachePath))
body.Set("external_id", jsonutils.NewString(agent.AgentId))
_, err := modules.Storagecaches.Update(session, storageCacheId, body)
if err != nil {
return errors.Wrap(err, "modules.Storagecaches.Update")
}
return nil
}
func (agent *SBaseAgent) createBaremetalAgent(session *mcclient.ClientSession) (jsonutils.JSONObject, error) {
params, err := agent.getCreateUpdateInfo()
if err != nil {
@@ -323,10 +391,17 @@ func (agent *SBaseAgent) createBaremetalAgent(session *mcclient.ClientSession) (
return modules.Baremetalagents.Create(session, params)
}
func (agent *SBaseAgent) updateBaremetalAgent(session *mcclient.ClientSession, id string) (jsonutils.JSONObject, error) {
params, err := agent.getCreateUpdateInfo()
if err != nil {
return nil, err
func (agent *SBaseAgent) updateBaremetalAgent(session *mcclient.ClientSession, id string, storageCacheId string) (jsonutils.JSONObject, error) {
var params jsonutils.JSONObject
var err error
if len(storageCacheId) > 0 {
params = jsonutils.NewDict()
params.(*jsonutils.JSONDict).Set("storagecache_id", jsonutils.NewString(storageCacheId))
} else {
params, err = agent.getCreateUpdateInfo()
if err != nil {
return nil, err
}
}
return modules.Baremetalagents.Update(session, id, params)
}
+59
View File
@@ -0,0 +1,59 @@
// 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 agent
import (
"context"
"fmt"
"net/http"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/cloudcommon/workmanager"
"yunion.io/x/onecloud/pkg/hostman/hostutils"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient/auth"
)
func (agent *SBaseAgent) AddImageCacheHandler(prefix string, app *appsrv.Application) {
hostutils.InitWorkerManager()
app.AddHandler("POST",
fmt.Sprintf("%s/disks/image_cache", prefix),
auth.Authenticate(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
performImageCache(ctx, w, r, agent.CacheManager.PrefetchImageCache)
}))
app.AddHandler("DELETE",
fmt.Sprintf("%s/disks/image_cache", prefix),
auth.Authenticate(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
performImageCache(ctx, w, r, agent.CacheManager.DeleteImageCache)
}))
}
func performImageCache(
ctx context.Context,
w http.ResponseWriter,
r *http.Request,
performTask workmanager.DelayTaskFunc,
) {
_, _, body := appsrv.FetchEnv(ctx, w, r)
disk, err := body.Get("disk")
if err != nil {
httperrors.MissingParameterError(w, "disk")
return
}
hostutils.DelayTask(ctx, performTask, disk)
hostutils.ResponseOk(ctx, w)
}
+1 -1
View File
@@ -23,7 +23,7 @@ import (
)
type SExternalizedResourceBase struct {
ExternalId string `width:"256" charset:"utf8" index:"true" list:"user" create:"admin_optional"`
ExternalId string `width:"256" charset:"utf8" index:"true" list:"user" create:"admin_optional" update:"admin"`
}
func (model SExternalizedResourceBase) GetExternalId() string {
+2
View File
@@ -70,6 +70,8 @@ type BaseOptions struct {
NonDefaultDomainProjects bool `help:"allow projects in non-default domains" default:"false"`
TimeZone string `help:"time zone" default:"Asia/Shanghai"`
structarg.BaseOptions
}
+21 -5
View File
@@ -22,11 +22,15 @@ const (
)
type SIPMIInfo struct {
Username string `json:"username"`
Password string `json:"password"`
IpAddr string `json:"ip_addr"`
Present bool `json:"present"`
LanChannel int `json:"lan_channel"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
IpAddr string `json:"ip_addr,omitempty"`
Present bool `json:"present,omitempty"`
LanChannel int `json:"lan_channel,omitzero"`
Verified bool `json:"verified,omitfalse"`
RedfishApi bool `json:"redfish_api,omitfalse"`
CdromBoot bool `json:"cdrom_boot,omitfalse"`
PxeBoot bool `json:"pxe_boot,omitfalse"`
}
func (info SIPMIInfo) ToPrepareParams() jsonutils.JSONObject {
@@ -42,5 +46,17 @@ func (info SIPMIInfo) ToPrepareParams() jsonutils.JSONObject {
}
data.Add(jsonutils.NewBool(info.Present), "ipmi_present")
data.Add(jsonutils.NewInt(int64(info.LanChannel)), "ipmi_lan_channel")
if info.Verified {
data.Add(jsonutils.JSONTrue, "ipmi_verified")
}
if info.RedfishApi {
data.Add(jsonutils.JSONTrue, "ipmi_redfish_api")
}
if info.CdromBoot {
data.Add(jsonutils.JSONTrue, "ipmi_cdrom_boot")
}
if info.PxeBoot {
data.Add(jsonutils.JSONTrue, "ipmi_pxe_boot")
}
return data
}
+3
View File
@@ -93,6 +93,9 @@ type SIPMILanConfig struct {
Netmask string `json:"netmask"`
Mac net.HardwareAddr `json:"mac"`
Gateway string `json:"gateway"`
VlanId int `json:"vlan_id"`
SpeedMbps int `json:"speed_mbps"`
}
type SIPMIBootFlags struct {
+21 -5
View File
@@ -120,7 +120,7 @@ func (self *SBaremetalGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk
return httperrors.NewUnsupportOperationError("Cannot resize disk for baremtal")
}
func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, userCred mcclient.TokenCredential, host *models.SHost, netConfig *api.NetworkConfig) (*models.SNetwork, []models.SNicConfig, models.IPAddlocationDirection) {
func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, userCred mcclient.TokenCredential, host *models.SHost, netConfig *api.NetworkConfig) (*models.SNetwork, []models.SNicConfig, api.IPAllocationDirection) {
netifs, net := host.GetNetinterfacesWithIdAndCredential(netConfig.Network, userCred, netConfig.Reserved)
if netifs != nil {
nicCnt := 1
@@ -142,7 +142,7 @@ func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SG
}
nicConfs = append(nicConfs, nicConf)
}
return net, nicConfs, models.IPAllocationStepup
return net, nicConfs, api.IPAllocationStepup
}
return net, nil, ""
}
@@ -214,7 +214,7 @@ func (self *SBaremetalGuestDriver) Attach2RandomNetwork(guest *models.SGuest, ct
}
nicConfs = append(nicConfs, nicConf)
}
return guest.Attach2Network(ctx, userCred, net, pendingUsage, "", netConfig.Driver, netConfig.BwLimit, netConfig.Vip, false, models.IPAllocationStepup, false, nicConfs)
return guest.Attach2Network(ctx, userCred, net, pendingUsage, "", netConfig.Driver, netConfig.BwLimit, netConfig.Vip, false, api.IPAllocationStepup, false, nicConfs)
}
return nil, fmt.Errorf("No appropriate host virtual network...")
}
@@ -237,8 +237,24 @@ func (self *SBaremetalGuestDriver) ChooseHostStorage(host *models.SHost, backend
}
func (self *SBaremetalGuestDriver) RequestGuestCreateAllDisks(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
task.ScheduleRun(nil) // skip
return nil
diskCat := guest.CategorizeDisks()
var imageId string
if diskCat.Root != nil {
imageId = diskCat.Root.GetTemplateId()
}
if len(imageId) == 0 {
task.ScheduleRun(nil)
return nil
}
storage := diskCat.Root.GetStorage()
if storage == nil {
return fmt.Errorf("no valid storage")
}
storageCache := storage.GetStoragecache()
if storageCache == nil {
return fmt.Errorf("no valid storage cache")
}
return storageCache.StartImageCacheTask(ctx, task.GetUserCred(), imageId, diskCat.Root.DiskFormat, false, task.GetTaskId())
}
func (self *SBaremetalGuestDriver) RequestGuestCreateInsertIso(ctx context.Context, imageId string, guest *models.SGuest, task taskman.ITask) error {
+3 -3
View File
@@ -52,7 +52,7 @@ func (self *SVirtualizedGuestDriver) PrepareDiskRaidConfig(userCred mcclient.Tok
return nil
}
func (self *SVirtualizedGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, userCred mcclient.TokenCredential, host *models.SHost, netConfig *api.NetworkConfig) (*models.SNetwork, []models.SNicConfig, models.IPAddlocationDirection) {
func (self *SVirtualizedGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, userCred mcclient.TokenCredential, host *models.SHost, netConfig *api.NetworkConfig) (*models.SNetwork, []models.SNicConfig, api.IPAllocationDirection) {
net, _ := host.GetNetworkWithIdAndCredential(netConfig.Network, userCred, netConfig.Reserved)
nicConfs := []models.SNicConfig{
{
@@ -68,7 +68,7 @@ func (self *SVirtualizedGuestDriver) GetNamedNetworkConfiguration(guest *models.
Ifname: "",
})
}
return net, nicConfs, models.IPAllocationStepdown
return net, nicConfs, api.IPAllocationStepdown
}
func (self *SVirtualizedGuestDriver) GetRandomNetworkTypes() []string {
@@ -147,7 +147,7 @@ func (self *SVirtualizedGuestDriver) Attach2RandomNetwork(guest *models.SGuest,
}
nicConfs = append(nicConfs, nicConf)
}
gn, err := guest.Attach2Network(ctx, userCred, selNet, pendingUsage, netConfig.Address, netConfig.Driver, netConfig.BwLimit, netConfig.Vip, netConfig.Reserved, models.IPAllocationDefault, netConfig.RequireDesignatedIP, nicConfs)
gn, err := guest.Attach2Network(ctx, userCred, selNet, pendingUsage, netConfig.Address, netConfig.Driver, netConfig.BwLimit, netConfig.Vip, netConfig.Reserved, api.IPAllocationDefault, netConfig.RequireDesignatedIP, nicConfs)
return gn, err
}
+35 -1
View File
@@ -43,7 +43,41 @@ func (self *SBaremetalHostDriver) GetHypervisor() string {
}
func (self *SBaremetalHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
return fmt.Errorf("not supported")
params := task.GetParams()
imageId, err := params.GetString("image_id")
if err != nil {
return err
}
_, err = models.CachedimageManager.FetchById(imageId)
if err != nil {
return err
}
format, _ := params.GetString("format")
isForce := jsonutils.QueryBoolean(params, "is_force", false)
type contentStruct struct {
ImageId string
Format string
IsForce bool
}
content := contentStruct{}
content.ImageId = imageId
content.Format = format
if isForce {
content.IsForce = true
}
url := "/disks/image_cache"
body := jsonutils.NewDict()
body.Add(jsonutils.Marshal(&content), "disk")
header := task.GetTaskRequestHeader()
_, err = host.BaremetalSyncRequest(ctx, "POST", url, header, body)
if err != nil {
return err
}
return nil
}
func (self *SBaremetalHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, content *jsonutils.JSONDict) error {
+43
View File
@@ -44,6 +44,8 @@ type SBaremetalagent struct {
AgentType string `width:"32" charset:"ascii" nullable:"true" default:"baremetal" list:"admin" update:"admin" create:"admin_optional"`
Version string `width:"64" charset:"ascii" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(64, charset='ascii'))
StoragecacheId string `width:"36" charset:"ascii" nullable:"true" list:"admin" get:"admin" update:"admin" create:"admin_optional"`
}
var BaremetalagentManager *SBaremetalagentManager
@@ -82,6 +84,13 @@ func (self *SBaremetalagent) ValidateDeleteCondition(ctx context.Context) error
if self.Status == api.BAREMETAL_AGENT_ENABLED {
return fmt.Errorf("Cannot delete in status %s", self.Status)
}
storageCache, _ := self.getStorageCache()
if storageCache != nil {
err := storageCache.ValidateDeleteCondition(ctx)
if err != nil {
return fmt.Errorf("storagecache cannot be delete: %s", err)
}
}
return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx)
}
@@ -211,3 +220,37 @@ func (manager *SBaremetalagentManager) GetAgent(agentType api.TAgentType, zoneId
}
return &agents[0]
}
func (cache *SBaremetalagent) getStorageCache() (*SStoragecache, error) {
if len(cache.StoragecacheId) > 0 {
cacheObj, err := StoragecacheManager.FetchById(cache.StoragecacheId)
if err != nil {
return nil, err
}
return cacheObj.(*SStoragecache), nil
}
return nil, nil
}
func (agent *SBaremetalagent) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
err := agent.SStandaloneResourceBase.CustomizeDelete(ctx, userCred, query, data)
if err != nil {
return err
}
cache, _ := agent.getStorageCache()
if cache != nil {
err = cache.Delete(ctx, userCred)
if err != nil {
return err
}
}
return nil
}
func (agent *SBaremetalagent) setStoragecacheId(cacheId string) error {
_, err := db.Update(agent, func() error {
agent.StoragecacheId = cacheId
return nil
})
return err
}
+18
View File
@@ -4045,3 +4045,21 @@ func (manager *SGuestManager) CreateGuestFromInstanceSnapshot(
guest := iGuest.(*SGuest)
return guest, guestParams, nil
}
func (self *SGuest) AllowGetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowGetSpec(userCred, self, "jnlp")
}
func (self *SGuest) GetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if self.Hypervisor != api.HYPERVISOR_BAREMETAL {
return nil, httperrors.NewInvalidStatusError("not a baremetal server")
}
host := self.GetHost()
if host == nil {
return nil, httperrors.NewInvalidStatusError("no valid host")
}
if !host.IsBaremetal {
return nil, httperrors.NewInvalidStatusError("host is not a baremetal")
}
return host.GetDetailsJnlp(ctx, userCred, query)
}
+1 -1
View File
@@ -64,7 +64,7 @@ type IGuestDriver interface {
PrepareDiskRaidConfig(userCred mcclient.TokenCredential, host *SHost, params []*api.BaremetalDiskConfig) error
GetNamedNetworkConfiguration(guest *SGuest, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig) (*SNetwork, []SNicConfig, IPAddlocationDirection)
GetNamedNetworkConfiguration(guest *SGuest, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig) (*SNetwork, []SNicConfig, api.IPAllocationDirection)
Attach2RandomNetwork(guest *SGuest, ctx context.Context, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig, pendingUsage quotas.IQuota) ([]SGuestnetwork, error)
GetRandomNetworkTypes() []string
+1 -1
View File
@@ -151,7 +151,7 @@ func (manager *SGuestnetworkManager) GenerateMac(netId string, suggestion string
func (manager *SGuestnetworkManager) newGuestNetwork(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, network *SNetwork,
index int8, address string, mac string, driver string, bwLimit int, virtual bool, reserved bool,
allocDir IPAddlocationDirection, requiredDesignatedIp bool, ifName string, teamWithMac string) (*SGuestnetwork, error) {
allocDir api.IPAllocationDirection, requiredDesignatedIp bool, ifName string, teamWithMac string) (*SGuestnetwork, error) {
gn := SGuestnetwork{}
gn.SetModelManager(GuestnetworkManager, &gn)
+3 -3
View File
@@ -2329,7 +2329,7 @@ func (self *SGuest) Attach2Network(ctx context.Context, userCred mcclient.TokenC
pendingUsage quotas.IQuota,
address string,
driver string, bwLimit int, virtual bool,
reserved bool, allocDir IPAddlocationDirection, requireDesignatedIP bool,
reserved bool, allocDir api.IPAllocationDirection, requireDesignatedIP bool,
nicConfs []SNicConfig) ([]SGuestnetwork, error) {
firstNic, err := self.attach2NetworkOnce(ctx, userCred, network, pendingUsage, address, driver, bwLimit, virtual,
@@ -2359,7 +2359,7 @@ func (self *SGuest) attach2NetworkOnce(ctx context.Context, userCred mcclient.To
pendingUsage quotas.IQuota,
address string,
driver string, bwLimit int, virtual bool,
reserved bool, allocDir IPAddlocationDirection, requireDesignatedIP bool,
reserved bool, allocDir api.IPAllocationDirection, requireDesignatedIP bool,
nicConf SNicConfig, teamWithMac string) (*SGuestnetwork, error) {
/*
allow a guest attach to a network 2 times
@@ -2539,7 +2539,7 @@ func (self *SGuest) SyncVMNics(ctx context.Context, userCred mcclient.TokenCrede
Ifname: "",
}
_, err = self.Attach2Network(ctx, userCred, add.net, nil, ipStr,
add.nic.GetDriver(), 0, false, add.reserve, IPAllocationDefault, true, []SNicConfig{nicConf})
add.nic.GetDriver(), 0, false, add.reserve, api.IPAllocationDefault, true, []SNicConfig{nicConf})
if err != nil {
result.AddError(err)
} else {
+345 -40
View File
@@ -42,6 +42,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/baremetal"
"yunion.io/x/onecloud/pkg/compute/options"
@@ -51,6 +52,7 @@ import (
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/httputils"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/redfish/bmconsole"
"yunion.io/x/onecloud/pkg/util/seclib2"
)
@@ -83,9 +85,11 @@ type SHost struct {
Rack string `width:"16" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
Slots string `width:"16" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
AccessMac string `width:"32" charset:"ascii" nullable:"false" index:"true" list:"admin" update:"admin" create:"admin_required"` // Column(VARCHAR(32, charset='ascii'), nullable=False, index=True)
AccessIp string `width:"16" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
ManagerUri string `width:"256" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(256, charset='ascii'), nullable=True)
AccessMac string `width:"32" charset:"ascii" nullable:"false" index:"true" list:"admin" update:"admin"` // Column(VARCHAR(32, charset='ascii'), nullable=False, index=True)
AccessIp string `width:"16" charset:"ascii" nullable:"true" list:"admin"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
ManagerUri string `width:"256" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(256, charset='ascii'), nullable=True)
SysInfo jsonutils.JSONObject `nullable:"true" search:"admin" list:"admin" update:"admin" create:"admin_optional"` // Column(JSONEncodedDict, nullable=True)
SN string `width:"128" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(128, charset='ascii'), nullable=True)
@@ -108,6 +112,8 @@ type SHost struct {
StorageDriver string `width:"20" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(20, charset='ascii'), nullable=True)
StorageInfo jsonutils.JSONObject `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(JSONEncodedDict, nullable=True)
IpmiIp string `width:"16" charset:"ascii" nullable:"true" list:"admin"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
IpmiInfo jsonutils.JSONObject `nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(JSONEncodedDict, nullable=True)
// Status string = Column(VARCHAR(16, charset='ascii'), nullable=False, default=baremetalstatus.INIT) # status
@@ -130,6 +136,8 @@ type SHost struct {
RealExternalId string `width:"256" charset:"utf8" get:"admin"`
IsImport bool `nullable:"true" default:"false" list:"admin" create:"admin_optional"`
EnablePxeBoot tristate.TriState `nullable:"false" default:"true" list:"admin" create:"admin_optional" update:"admin"`
}
func (manager *SHostManager) GetContextManagers() [][]db.IModelManager {
@@ -652,6 +660,7 @@ func (self *SHost) PerformUpdateStorage(
bs := self.GetBaremetalstorage()
capacity, _ := data.Int("capacity")
zoneId, _ := data.GetString("zone_id")
storageCacheId, _ := data.GetString("storagecache_id")
if bs == nil {
// 1. create storage
storage := SStorage{}
@@ -662,6 +671,7 @@ func (self *SHost) PerformUpdateStorage(
storage.Cmtbound = 1.0
storage.Status = api.STORAGE_ONLINE
storage.ZoneId = zoneId
storage.StoragecacheId = storageCacheId
err := StorageManager.TableSpec().Insert(&storage)
if err != nil {
return nil, fmt.Errorf("Create baremetal storage error: %v", err)
@@ -683,16 +693,17 @@ func (self *SHost) PerformUpdateStorage(
return nil, nil
}
storage := bs.GetStorage()
if capacity != int64(storage.Capacity) {
diff, err := db.Update(storage, func() error {
storage.Capacity = capacity
return nil
})
if err != nil {
return nil, fmt.Errorf("Update baremetal storage error: %v", err)
}
db.OpsLog.LogEvent(storage, db.ACT_UPDATE, diff, userCred)
//if capacity != int64(storage.Capacity) {
diff, err := db.Update(storage, func() error {
storage.Capacity = capacity
storage.StoragecacheId = storageCacheId
return nil
})
if err != nil {
return nil, fmt.Errorf("Update baremetal storage error: %v", err)
}
db.OpsLog.LogEvent(storage, db.ACT_UPDATE, diff, userCred)
//}
return nil, nil
}
@@ -2429,8 +2440,34 @@ func (self *SHost) PostCreate(ctx context.Context, userCred mcclient.TokenCreden
})
if err != nil {
log.Errorln(err.Error())
} else {
ipmiIp, _ := ipmiInfo.GetString("ip_addr")
if len(ipmiIp) > 0 {
self.setIpmiIp(userCred, ipmiIp)
}
}
}
accessIp, _ := data.GetString("access_ip")
if len(accessIp) > 0 {
self.setAccessIp(userCred, accessIp)
}
accessMac, _ := data.GetString("access_mac")
if len(accessMac) > 0 {
self.setAccessMac(userCred, accessMac)
}
if len(self.ZoneId) > 0 && self.HostType == api.HOST_TYPE_BAREMETAL {
self.StartBaremetalCreateTask(ctx, userCred, kwargs, "")
}
}
func (self *SHost) StartBaremetalCreateTask(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, parentTaskId string) error {
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalCreateTask", self, userCred, data, parentTaskId, "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
return nil
}
}
func (manager *SHostManager) ValidateSizeParams(data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
@@ -2470,14 +2507,14 @@ func (manager *SHostManager) ValidateSizeParams(data *jsonutils.JSONDict) (*json
return data, nil
}
func inputUniquenessCheck(data *jsonutils.JSONDict, zoneId string, hostId string) (*jsonutils.JSONDict, error) {
func (manager *SHostManager) inputUniquenessCheck(data *jsonutils.JSONDict, zoneId string, hostId string) (*jsonutils.JSONDict, error) {
for _, key := range []string{
"manager_uri",
"access_ip",
} {
val, _ := data.GetString(key)
if len(val) > 0 {
q := HostManager.Query().Equals(key, val)
q := manager.Query().Equals(key, val)
if len(zoneId) > 0 {
q = q.Equals("zone_id", zoneId)
} else {
@@ -2502,25 +2539,28 @@ func inputUniquenessCheck(data *jsonutils.JSONDict, zoneId string, hostId string
if len(accessMac2) == 0 {
return nil, httperrors.NewInputParameterError("invalid macAddr %s", accessMac)
}
q := HostManager.Query().Equals("access_mac", accessMac2)
if len(hostId) > 0 {
q = q.NotEquals("id", hostId)
if accessMac2 != api.ACCESS_MAC_ANY {
q := manager.Query().Equals("access_mac", accessMac2)
if len(hostId) > 0 {
q = q.NotEquals("id", hostId)
}
cnt, err := q.CountWithError()
if err != nil {
return nil, httperrors.NewInternalServerError("check access_mac duplication fail %s", err)
}
if cnt > 0 {
return nil, httperrors.NewConflictError("duplicate access_mac %s", accessMac)
}
data.Set("access_mac", jsonutils.NewString(accessMac2))
}
cnt, err := q.CountWithError()
if err != nil {
return nil, httperrors.NewInternalServerError("check access_mac duplication fail %s", err)
}
if cnt > 0 {
return nil, httperrors.NewConflictError("duplicate access_mac %s", accessMac)
}
data.Set("access_mac", jsonutils.NewString(accessMac2))
}
return data, nil
}
func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
zoneId := jsonutils.GetAnyString(data, []string{"zone_id", "zone"})
zoneId, zoneKey := jsonutils.GetAnyString2(data, []string{"zone_id", "zone"})
if len(zoneId) > 0 {
data.Remove(zoneKey)
zoneObj, err := ZoneManager.FetchByIdOrName(userCred, zoneId)
if err != nil {
if err == sql.ErrNoRows {
@@ -2533,7 +2573,7 @@ func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mc
data.Set("zone_id", jsonutils.NewString(zoneObj.GetId()))
}
data, err := inputUniquenessCheck(data, zoneId, "")
data, err := manager.inputUniquenessCheck(data, zoneId, "")
if err != nil {
return nil, err
}
@@ -2562,18 +2602,116 @@ func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mc
return nil, httperrors.NewInputParameterError("%s", err)
}
ipmiIpAddr, _ := ipmiInfo.GetString("ip_addr")
if len(ipmiIpAddr) > 0 && !NetworkManager.IsValidOnPremiseNetworkIP(ipmiIpAddr) {
return nil, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr)
if len(ipmiIpAddr) > 0 {
net, _ := NetworkManager.GetOnPremiseNetworkOfIP(ipmiIpAddr, "", tristate.None)
if net == nil {
return nil, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr)
}
// reserve this IP temporarily
err := net.reserveIpWithDuration(ctx, userCred, ipmiIpAddr, "reserve for baremetal ipmi IP", 30*time.Minute)
if err != nil {
return nil, err
}
zoneObj := net.getZone()
if zoneObj == nil {
return nil, httperrors.NewInputParameterError("IPMI network has no zone???")
}
originZoneId, _ := data.GetString("zone_id")
if len(originZoneId) > 0 && originZoneId != zoneObj.GetId() {
return nil, httperrors.NewInputParameterError("IPMI address located in different zone than specified")
}
data.Set("zone_id", jsonutils.NewString(zoneObj.GetId()))
}
var accessNet *SNetwork
accessIpAddr, _ := data.GetString("access_ip")
if len(accessIpAddr) > 0 {
net, _ := NetworkManager.GetOnPremiseNetworkOfIP(accessIpAddr, "", tristate.None)
if net == nil {
return nil, httperrors.NewInputParameterError("%s is out of network IP ranges", accessIpAddr)
}
accessNet = net
} else {
accessNetStr, _ := data.GetString("access_net")
if len(accessNetStr) > 0 {
netObj, err := NetworkManager.FetchByIdOrName(userCred, accessNetStr)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2("network", accessNetStr)
} else {
return nil, httperrors.NewGeneralError(err)
}
}
accessNet = netObj.(*SNetwork)
} else {
accessWireStr, _ := data.GetString("access_wire")
if len(accessWireStr) > 0 {
wireObj, err := WireManager.FetchByIdOrName(userCred, accessWireStr)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2("wire", accessWireStr)
} else {
return nil, httperrors.NewGeneralError(err)
}
}
wire := wireObj.(*SWire)
lockman.LockObject(ctx, wire)
defer lockman.ReleaseObject(ctx, wire)
net, err := wire.GetCandidatePrivateNetwork(userCred, false, []string{api.NETWORK_TYPE_PXE, api.NETWORK_TYPE_BAREMETAL, api.NETWORK_TYPE_GUEST})
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
accessNet = net
}
}
}
if accessNet != nil {
lockman.LockObject(ctx, accessNet)
defer lockman.ReleaseObject(ctx, accessNet)
accessIp, err := accessNet.GetFreeIP(ctx, userCred, nil, nil, accessIpAddr, api.IPAllocationNone, false)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
if len(accessIpAddr) > 0 && accessIpAddr != accessIp {
return nil, httperrors.NewConflictError("Access ip %s has been used", accessIpAddr)
}
zoneObj := accessNet.getZone()
if zoneObj == nil {
return nil, httperrors.NewInputParameterError("Access network has no zone???")
}
originZoneId, _ := data.GetString("zone_id")
if len(originZoneId) > 0 && originZoneId != zoneObj.GetId() {
return nil, httperrors.NewInputParameterError("Access address located in different zone than specified")
}
// reserve this IP temporarily
err = accessNet.reserveIpWithDuration(ctx, userCred, accessIp, "reserve for baremetal access IP", 30*time.Minute)
if err != nil {
return nil, err
}
data.Set("access_ip", jsonutils.NewString(accessIp))
data.Set("zone_id", jsonutils.NewString(zoneObj.GetId()))
}
ipmiPasswd, _ := ipmiInfo.GetString("password")
if len(ipmiPasswd) > 0 && !seclib2.MeetComplxity(ipmiPasswd) {
return nil, httperrors.NewWeakPasswordError()
}
// only baremetal can be created
hostType, _ := data.GetString("host_type")
if len(hostType) == 0 {
hostType = api.HOST_TYPE_BAREMETAL
data.Set("host_type", jsonutils.NewString(hostType))
}
if hostType == api.HOST_TYPE_BAREMETAL {
data.Set("is_baremetal", jsonutils.JSONTrue)
}
return manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, data)
}
func (self *SHost) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
data, err := inputUniquenessCheck(data, self.ZoneId, self.Id)
data, err := HostManager.inputUniquenessCheck(data, self.ZoneId, self.Id)
if err != nil {
return nil, err
}
@@ -2588,8 +2726,18 @@ func (self *SHost) ValidateUpdateData(ctx context.Context, userCred mcclient.Tok
}
if ipmiInfo.Length() > 0 {
ipmiIpAddr, _ := ipmiInfo.GetString("ip_addr")
if len(ipmiIpAddr) > 0 && !NetworkManager.IsValidOnPremiseNetworkIP(ipmiIpAddr) {
return nil, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr)
if len(ipmiIpAddr) > 0 {
net, _ := NetworkManager.GetOnPremiseNetworkOfIP(ipmiIpAddr, "", tristate.None)
if net == nil {
return nil, httperrors.NewInputParameterError("%s is out of network IP ranges", ipmiIpAddr)
}
zoneObj := net.getZone()
if zoneObj == nil {
return nil, httperrors.NewInputParameterError("IPMI network has not zone???")
}
if zoneObj.GetId() != self.ZoneId {
return nil, httperrors.NewInputParameterError("New IPMI address located in another zone!")
}
}
val := jsonutils.NewDict()
val.Update(self.IpmiInfo)
@@ -2986,6 +3134,32 @@ func (self *SHost) StartPrepareTask(ctx context.Context, userCred mcclient.Token
}
}
func (self *SHost) AllowPerformIpmiProbe(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "ipmi-probe")
}
func (self *SHost) PerformIpmiProbe(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if utils.IsInStringArray(self.Status, []string{api.BAREMETAL_INIT, api.BAREMETAL_READY, api.BAREMETAL_RUNNING}) {
return nil, self.StartIpmiProbeTask(ctx, userCred, "")
}
return nil, httperrors.NewInvalidStatusError("Cannot do Ipmi-probe in status %s", self.Status)
}
func (self *SHost) StartIpmiProbeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
data := jsonutils.NewDict()
self.SetStatus(userCred, api.BAREMETAL_START_PROBE, "start ipmi-probe task")
if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalIpmiProbeTask", self, userCred, data, parentTaskId, "", nil); err != nil {
log.Errorln(err)
return err
} else {
task.ScheduleRun(nil)
return nil
}
}
func (self *SHost) AllowPerformInitialize(
ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, data jsonutils.JSONObject,
@@ -3226,6 +3400,12 @@ func (self *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredenti
}
}
}
if netif.NicType == api.NIC_TYPE_ADMIN {
err := self.setAccessMac(userCred, netif.Mac)
if err != nil {
return httperrors.NewBadRequestError(err.Error())
}
}
if len(ipAddr) > 0 {
err = self.EnableNetif(ctx, userCred, netif, "", ipAddr, "", "", reserve, requireDesignatedIp)
if err != nil {
@@ -3322,7 +3502,17 @@ func (self *SHost) EnableNetif(ctx context.Context, userCred mcclient.TokenCrede
} else if net.WireId != wire.Id {
return fmt.Errorf("conflict??? candiate net is not on wire")
}
return self.Attach2Network(ctx, userCred, netif, net, ipAddr, allocDir, reserve, requireDesignatedIp)
err = self.Attach2Network(ctx, userCred, netif, net, ipAddr, allocDir, reserve, requireDesignatedIp)
if err != nil {
return errors.Wrap(err, "self.Attach2Network")
}
switch netif.NicType {
case api.NIC_TYPE_IPMI:
err = self.setIpmiIp(userCred, ipAddr)
case api.NIC_TYPE_ADMIN:
err = self.setAccessIp(userCred, ipAddr)
}
return err
}
func (self *SHost) AllowPerformDisableNetif(ctx context.Context,
@@ -3348,19 +3538,31 @@ func (self *SHost) PerformDisableNetif(ctx context.Context, userCred mcclient.To
func (self *SHost) DisableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error {
bn := netif.GetBaremetalNetwork()
var ipAddr string
if bn != nil {
ipAddr = bn.IpAddr
self.UpdateDnsRecord(netif, false)
self.DeleteBaremetalnetwork(ctx, userCred, bn, reserve)
}
return nil
var err error
switch netif.NicType {
case api.NIC_TYPE_IPMI:
if ipAddr == self.IpmiIp {
err = self.setIpmiIp(userCred, "")
}
case api.NIC_TYPE_ADMIN:
if ipAddr == self.AccessIp {
err = self.setAccessIp(userCred, "")
}
}
return err
}
func (self *SHost) Attach2Network(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, net *SNetwork, ipAddr, allocDir string, reserved, requireDesignatedIp bool) error {
lockman.LockObject(ctx, net)
defer lockman.ReleaseObject(ctx, net)
usedAddr := net.GetUsedAddresses()
freeIp, err := net.GetFreeIP(ctx, userCred, usedAddr, nil, ipAddr, IPAddlocationDirection(allocDir), reserved)
freeIp, err := net.GetFreeIP(ctx, userCred, nil, nil, ipAddr, api.IPAllocationDirection(allocDir), reserved)
if err != nil {
log.Errorf("attach2network: %s", err)
return err
@@ -3407,19 +3609,25 @@ func (self *SHost) PerformRemoveNetif(ctx context.Context, userCred mcclient.Tok
func (self *SHost) RemoveNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error {
wire := netif.GetWire()
self.DisableNetif(ctx, userCred, netif, reserve)
log.Infof("Remove wire")
nicType := netif.NicType
mac := netif.Mac
err := netif.Remove(ctx, userCred)
if err != nil {
return err
return errors.Wrap(err, "netif.Remove")
}
if nicType == api.NIC_TYPE_ADMIN && self.AccessMac == mac {
err := self.setAccessMac(userCred, "")
if err != nil {
return errors.Wrap(err, "self.setAccessMac")
}
}
if wire != nil {
log.Infof("Remove wire")
others := self.GetNetifsOnWire(wire)
if len(others) == 0 {
hw, _ := HostwireManager.FetchByHostIdAndMac(self.Id, netif.Mac)
if hw != nil {
db.OpsLog.LogDetachEvent(ctx, self, wire, userCred, jsonutils.NewString(fmt.Sprintf("disable netif %s", self.AccessMac)))
log.Infof("Detach host wire because of remove netif %s", netif.Mac)
log.Debugf("Detach host wire because of remove netif %s", netif.Mac)
return hw.Delete(ctx, userCred)
}
}
@@ -4167,3 +4375,100 @@ func (host *SHost) InstanceGroups() ([]SGroup, map[string]int, error) {
}
return groups, groupSet, nil
}
func (host *SHost) setIpmiIp(userCred mcclient.TokenCredential, ipAddr string) error {
if host.IpmiIp == ipAddr {
return nil
}
diff, err := db.Update(host, func() error {
host.IpmiIp = ipAddr
return nil
})
if err != nil {
return errors.Wrap(err, "db.Update")
}
db.OpsLog.LogEvent(host, db.ACT_UPDATE, diff, userCred)
return nil
}
func (host *SHost) setAccessIp(userCred mcclient.TokenCredential, ipAddr string) error {
if host.AccessIp == ipAddr {
return nil
}
diff, err := db.Update(host, func() error {
host.AccessIp = ipAddr
return nil
})
if err != nil {
return errors.Wrap(err, "db.Update")
}
db.OpsLog.LogEvent(host, db.ACT_UPDATE, diff, userCred)
return nil
}
func (host *SHost) setAccessMac(userCred mcclient.TokenCredential, mac string) error {
mac = netutils.FormatMacAddr(mac)
if host.AccessMac == mac {
return nil
}
diff, err := db.Update(host, func() error {
host.AccessMac = mac
return nil
})
if err != nil {
return errors.Wrap(err, "db.Update")
}
db.OpsLog.LogEvent(host, db.ACT_UPDATE, diff, userCred)
return nil
}
func (host *SHost) GetIpmiInfo() (types.SIPMIInfo, error) {
info := types.SIPMIInfo{}
if host.IpmiInfo != nil {
err := host.IpmiInfo.Unmarshal(&info)
if err != nil {
return info, errors.Wrap(err, "host.IpmiInfo.Unmarshal")
}
}
return info, nil
}
func (self *SHost) AllowGetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return db.IsAdminAllowGetSpec(userCred, self, "jnlp")
}
func (self *SHost) GetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
ipmi, err := self.GetIpmiInfo()
if err != nil {
return nil, httperrors.NewInvalidStatusError("no valid ipmi_info")
}
if !ipmi.Verified {
return nil, httperrors.NewInvalidStatusError("no veried ipmi_info")
}
if self.SysInfo == nil {
return nil, httperrors.NewInvalidStatusError("no valid sys_info")
}
ipmiPass, err := utils.DescryptAESBase64(self.Id, ipmi.Password)
if err != nil {
return nil, httperrors.NewInternalServerError("decrypt ipmi password fail: %s", err)
}
bmc := bmconsole.NewBMCConsole(ipmi.IpAddr, ipmi.Username, ipmiPass, false)
manufacture, _ := self.SysInfo.GetString("manufacture")
var jnlp string
switch strings.ToLower(manufacture) {
case "hp", "hpe":
jnlp, err = bmc.GetIloConsoleJNLP(ctx)
case "dell":
sku, _ := self.SysInfo.GetString("sku")
model, _ := self.SysInfo.GetString("model")
jnlp, err = bmc.GetIdracConsoleJNLP(ctx, sku, model)
default:
return nil, httperrors.NewNotImplementedError("Unsupported manufacture %s", manufacture)
}
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
ret := jsonutils.NewDict()
ret.Add(jsonutils.NewString(jnlp), "jnlp")
return ret, nil
}
+4 -3
View File
@@ -24,6 +24,7 @@ import (
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/mcclient"
@@ -79,9 +80,9 @@ func (ln *SLoadbalancerNetwork) Network() *SNetwork {
type SLoadbalancerNetworkRequestData struct {
Loadbalancer *SLoadbalancer
NetworkId string
reserved bool // allocate from reserved
Address string // the address user intends to use
strategy IPAddlocationDirection // allocate bottom up, top down, randomly
reserved bool // allocate from reserved
Address string // the address user intends to use
strategy api.IPAllocationDirection // allocate bottom up, top down, randomly
}
type SLoadbalancerNetworkDeleteData struct {
+61 -51
View File
@@ -43,6 +43,7 @@ import (
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/util/billing"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/rand"
"yunion.io/x/onecloud/pkg/util/rbacutils"
@@ -52,16 +53,6 @@ var (
ALL_NETWORK_TYPES = api.ALL_NETWORK_TYPES
)
type IPAddlocationDirection string
const (
IPAllocationStepdown IPAddlocationDirection = "stepdown"
IPAllocationStepup IPAddlocationDirection = "stepup"
IPAllocationRadnom IPAddlocationDirection = "random"
IPAllocationNone IPAddlocationDirection = "none"
IPAllocationDefault = ""
)
type SNetworkManager struct {
db.SSharableVirtualResourceBaseManager
}
@@ -289,7 +280,7 @@ func (self *SNetwork) GetUsedAddresses() map[string]bool {
GuestnetworkManager.Query().SubQuery(),
GroupnetworkManager.Query().SubQuery(),
HostnetworkManager.Query().SubQuery(),
ReservedipManager.Query().SubQuery(),
ReservedipManager.Query().GT("expired_at", time.Now()).SubQuery(),
LoadbalancernetworkManager.Query().SubQuery(),
ElasticipManager.Query().SubQuery(),
NetworkinterfacenetworkManager.Query().SubQuery(),
@@ -339,7 +330,7 @@ func isIpUsed(ipstr string, addrTable map[string]bool, recentUsedAddrTable map[s
}
}
func (self *SNetwork) getFreeIP(addrTable map[string]bool, recentUsedAddrTable map[string]bool, candidate string, allocDir IPAddlocationDirection) (string, error) {
func (self *SNetwork) getFreeIP(addrTable map[string]bool, recentUsedAddrTable map[string]bool, candidate string, allocDir api.IPAllocationDirection) (string, error) {
iprange := self.getIPRange()
// Try candidate first
if len(candidate) > 0 {
@@ -354,10 +345,10 @@ func (self *SNetwork) getFreeIP(addrTable map[string]bool, recentUsedAddrTable m
return candidate, nil
}
}
if len(self.AllocPolicy) > 0 && IPAddlocationDirection(self.AllocPolicy) != IPAllocationNone {
allocDir = IPAddlocationDirection(self.AllocPolicy)
if len(self.AllocPolicy) > 0 && api.IPAllocationDirection(self.AllocPolicy) != api.IPAllocationNone {
allocDir = api.IPAllocationDirection(self.AllocPolicy)
}
if len(allocDir) == 0 || allocDir == IPAllocationStepdown {
if len(allocDir) == 0 || allocDir == api.IPAllocationStepdown {
ip, _ := netutils.NewIPV4Addr(self.GuestIpEnd)
for iprange.Contains(ip) {
if !isIpUsed(ip.String(), addrTable, recentUsedAddrTable) {
@@ -366,7 +357,7 @@ func (self *SNetwork) getFreeIP(addrTable map[string]bool, recentUsedAddrTable m
ip = ip.StepDown()
}
} else {
if allocDir == IPAllocationRadnom {
if allocDir == api.IPAllocationRadnom {
iprange := self.getIPRange()
const MAX_TRIES = 5
for i := 0; i < MAX_TRIES; i += 1 {
@@ -388,21 +379,29 @@ func (self *SNetwork) getFreeIP(addrTable map[string]bool, recentUsedAddrTable m
return "", httperrors.NewInsufficientResourceError("Out of IP address")
}
func (self *SNetwork) GetFreeIP(ctx context.Context, userCred mcclient.TokenCredential, addrTable map[string]bool, recentUsedAddrTable map[string]bool, candidate string, allocDir IPAddlocationDirection, reserved bool) (string, error) {
func (self *SNetwork) GetFreeIP(ctx context.Context, userCred mcclient.TokenCredential, addrTable map[string]bool, recentUsedAddrTable map[string]bool, candidate string, allocDir api.IPAllocationDirection, reserved bool) (string, error) {
// if reserved true, first try find IP in reserved IP pool
if reserved {
rip := ReservedipManager.GetReservedIP(self, candidate)
if rip == nil {
return "", httperrors.NewInsufficientResourceError("Reserved address %s not found", candidate)
if rip != nil {
rip.Release(ctx, userCred, self)
return candidate, nil
}
rip.Release(ctx, userCred, self)
return candidate, nil
} else {
cand, err := self.getFreeIP(addrTable, recentUsedAddrTable, candidate, allocDir)
if err != nil {
return "", err
}
return cand, nil
// return "", httperrors.NewInsufficientResourceError("Reserved address %s not found", candidate)
// if not find, warning, then fallback to normal procedure
log.Warningf("Reserved address %s not found", candidate)
}
if addrTable == nil {
addrTable = self.GetUsedAddresses()
}
if recentUsedAddrTable == nil {
recentUsedAddrTable = GuestnetworkManager.getRecentlyReleasedIPAddresses(self.Id, self.getAllocTimoutDuration())
}
cand, err := self.getFreeIP(addrTable, recentUsedAddrTable, candidate, allocDir)
if err != nil {
return "", err
}
return cand, nil
}
func (self *SNetwork) GetUsedIfnames() map[string]bool {
@@ -1041,23 +1040,20 @@ func (self *SNetwork) PerformReserveIp(ctx context.Context, userCred mcclient.To
if err != nil {
return nil, httperrors.NewMissingParameterError("ips")
}
var duration time.Duration
durationStr, _ := data.GetString("duration")
if len(durationStr) > 0 {
bc, err := billing.ParseBillingCycle(durationStr)
if err != nil {
return nil, httperrors.NewInputParameterError("Duration %s invalid", durationStr)
}
duration = bc.Duration()
}
for _, ip := range ips {
ipstr, _ := ip.GetString()
ipAddr, err := netutils.NewIPV4Addr(ipstr)
if err != nil {
return nil, httperrors.NewInputParameterError("not a valid ip address %s: %s", ipstr, err)
}
if !self.IsAddressInRange(ipAddr) {
return nil, httperrors.NewInputParameterError("Address %s not in network", ipstr)
}
used, err := self.isAddressUsed(ipstr)
if err != nil {
return nil, httperrors.NewInternalServerError("isAddressUsed fail %s", err)
}
if used {
return nil, httperrors.NewConflictError("Address %s has been used", ipstr)
}
err = ReservedipManager.ReserveIP(userCred, self, ipstr, notes)
err := self.reserveIpWithDuration(ctx, userCred, ipstr, notes, duration)
if err != nil {
return nil, err
}
@@ -1065,6 +1061,28 @@ func (self *SNetwork) PerformReserveIp(ctx context.Context, userCred mcclient.To
return nil, nil
}
func (self *SNetwork) reserveIpWithDuration(ctx context.Context, userCred mcclient.TokenCredential, ipstr string, notes string, duration time.Duration) error {
ipAddr, err := netutils.NewIPV4Addr(ipstr)
if err != nil {
return httperrors.NewInputParameterError("not a valid ip address %s: %s", ipstr, err)
}
if !self.IsAddressInRange(ipAddr) {
return httperrors.NewInputParameterError("Address %s not in network", ipstr)
}
used, err := self.isAddressUsed(ipstr)
if err != nil {
return httperrors.NewInternalServerError("isAddressUsed fail %s", err)
}
if used {
return httperrors.NewConflictError("Address %s has been used", ipstr)
}
err = ReservedipManager.ReserveIPWithDuration(userCred, self, ipstr, notes, duration)
if err != nil {
return err
}
return nil
}
func (self *SNetwork) AllowPerformReleaseReservedIp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "release-reserved-ip")
}
@@ -1074,7 +1092,7 @@ func (self *SNetwork) PerformReleaseReservedIp(ctx context.Context, userCred mcc
if len(ipstr) == 0 {
return nil, httperrors.NewInputParameterError("Reserved ip to release must be provided")
}
rip := ReservedipManager.GetReservedIP(self, ipstr)
rip := ReservedipManager.getReservedIP(self, ipstr)
if rip == nil {
return nil, httperrors.NewInvalidStatusError("Address %s not reserved", ipstr)
}
@@ -2164,14 +2182,6 @@ func (network *SNetwork) getAllocTimoutDuration() time.Duration {
return time.Duration(tos) * time.Second
}
func (manager *SNetworkManager) IsValidOnPremiseNetworkIP(ipStr string) bool {
net, _ := manager.GetOnPremiseNetworkOfIP(ipStr, "", tristate.None)
if net != nil {
return true
}
return false
}
func (network *SNetwork) GetSchedtags() []SSchedtag {
return GetSchedtags(NetworkschedtagManager, network.Id)
}
+47 -4
View File
@@ -17,6 +17,7 @@ package models
import (
"context"
"strconv"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
@@ -53,6 +54,8 @@ type SReservedip struct {
IpAddr string `width:"16" charset:"ascii" list:"admin"` // Column(VARCHAR(16, charset='ascii'))
Notes string `width:"512" charset:"utf8" nullable:"true" list:"admin" update:"admin"` // ]Column(VARCHAR(512, charset='utf8'), nullable=True)
ExpiredAt time.Time `nullable:"true" list:"admin"`
}
func (manager *SReservedipManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
@@ -76,7 +79,15 @@ func (self *SReservedip) AllowDeleteItem(ctx context.Context, userCred mcclient.
}
func (manager *SReservedipManager) ReserveIP(userCred mcclient.TokenCredential, network *SNetwork, ip string, notes string) error {
rip := SReservedip{NetworkId: network.Id, IpAddr: ip, Notes: notes}
return manager.ReserveIPWithDuration(userCred, network, ip, notes, 0)
}
func (manager *SReservedipManager) ReserveIPWithDuration(userCred mcclient.TokenCredential, network *SNetwork, ip string, notes string, duration time.Duration) error {
expiredAt := time.Time{}
if duration > 0 {
expiredAt = time.Now().UTC().Add(duration)
}
rip := SReservedip{NetworkId: network.Id, IpAddr: ip, Notes: notes, ExpiredAt: expiredAt}
err := manager.TableSpec().Insert(&rip)
if err != nil {
log.Errorf("ReserveIP fail: %s", err)
@@ -86,11 +97,12 @@ func (manager *SReservedipManager) ReserveIP(userCred mcclient.TokenCredential,
return nil
}
func (manager *SReservedipManager) GetReservedIP(network *SNetwork, ip string) *SReservedip {
func (manager *SReservedipManager) getReservedIP(network *SNetwork, ip string) *SReservedip {
rip := SReservedip{}
rip.SetModelManager(manager, &rip)
err := manager.Query().Equals("network_id", network.Id).Equals("ip_addr", ip).First(&rip)
q := manager.Query().Equals("network_id", network.Id).Equals("ip_addr", ip)
err := q.First(&rip)
if err != nil {
log.Errorf("GetReservedIP fail: %s", err)
return nil
@@ -98,9 +110,21 @@ func (manager *SReservedipManager) GetReservedIP(network *SNetwork, ip string) *
return &rip
}
func (manager *SReservedipManager) GetReservedIP(network *SNetwork, ip string) *SReservedip {
rip := manager.getReservedIP(network, ip)
if rip == nil {
return nil
}
if rip.IsExpired() {
return nil
}
return rip
}
func (manager *SReservedipManager) GetReservedIPs(network *SNetwork) []SReservedip {
rips := make([]SReservedip, 0)
q := manager.Query().Equals("network_id", network.Id)
now := time.Now().UTC()
q := manager.Query().Equals("network_id", network.Id).GT("expired_at", now)
err := db.FetchModelObjects(manager, q, &rips)
if err != nil {
log.Errorf("GetReservedIPs fail: %s", err)
@@ -135,6 +159,11 @@ func (self *SReservedip) GetCustomizeColumns(ctx context.Context, userCred mccli
if net != nil {
extra.Add(jsonutils.NewString(net.Name), "network")
}
if self.IsExpired() {
extra.Add(jsonutils.JSONTrue, "expired")
} else {
extra.Add(jsonutils.JSONFalse, "expired")
}
return extra
}
@@ -144,6 +173,13 @@ func (manager *SReservedipManager) ListItemFilter(ctx context.Context, q *sqlche
log.Errorf("ListItemFilter %s", err)
return nil, err
}
isAll := jsonutils.QueryBoolean(query, "all", false)
if !isAll {
q = q.Filter(sqlchemy.OR(
sqlchemy.IsNullOrEmpty(q.Field("expired_at")),
sqlchemy.GT(q.Field("expired_at"), time.Now().UTC()),
))
}
network, _ := query.GetString("network")
if len(network) > 0 {
netObj, _ := NetworkManager.FetchByIdOrName(userCred, network)
@@ -162,3 +198,10 @@ func (rip *SReservedip) GetId() string {
func (rip *SReservedip) GetName() string {
return rip.GetId()
}
func (rip *SReservedip) IsExpired() bool {
if !rip.ExpiredAt.IsZero() && rip.ExpiredAt.Before(time.Now().UTC()) {
return true
}
return false
}
+28 -3
View File
@@ -156,9 +156,14 @@ func (self *SStoragecache) getHostId() (string, error) {
hosts := make([]SHost, 0)
host := HostManager.Query().SubQuery()
q := host.Query(host.Field("id"))
err := q.Join(hoststorages, sqlchemy.AND(sqlchemy.Equals(hoststorages.Field("host_id"), host.Field("id")),
sqlchemy.Equals(host.Field("host_status"), api.HOST_ONLINE),
sqlchemy.IsTrue(host.Field("enabled")))).
err := q.Join(hoststorages, sqlchemy.AND(
sqlchemy.Equals(hoststorages.Field("host_id"), host.Field("id")),
sqlchemy.OR(
sqlchemy.Equals(host.Field("host_status"), api.HOST_ONLINE),
sqlchemy.Equals(host.Field("host_type"), api.HOST_TYPE_BAREMETAL),
),
sqlchemy.IsTrue(host.Field("enabled")),
)).
Join(storages, sqlchemy.AND(sqlchemy.Equals(storages.Field("storagecache_id"), self.Id),
sqlchemy.In(storages.Field("status"), []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE}),
sqlchemy.IsTrue(storages.Field("enabled")))).
@@ -435,6 +440,10 @@ func (self *SStoragecache) ValidateDeleteCondition(ctx context.Context) error {
if self.getCachedImageCount() > 0 {
return httperrors.NewNotEmptyError("storage cache not empty")
}
storages := self.getStorages()
if len(storages) > 0 {
return httperrors.NewNotEmptyError("referered by storages")
}
return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx)
}
@@ -620,3 +629,19 @@ func (self *SStoragecache) StartRelinquishLeastUsedCachedImageTask(ctx context.C
}
return self.StartImageUncacheTask(ctx, userCred, cachedImages[leastUsedIdx].GetId(), false, parentTaskId)
}
func (cache *SStoragecache) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
err := cache.SStandaloneResourceBase.CustomizeDelete(ctx, userCred, query, data)
if err != nil {
return err
}
if len(cache.ExternalId) > 0 {
agentObj, err := BaremetalagentManager.FetchById(cache.ExternalId)
if err == nil {
agentObj.(*SBaremetalagent).setStoragecacheId("")
} else if err != sql.ErrNoRows {
return err
}
}
return nil
}
@@ -0,0 +1,74 @@
// 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 tasks
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
)
type BaremetalCreateTask struct {
SBaremetalBaseTask
}
func init() {
taskman.RegisterTask(BaremetalCreateTask{})
}
func (self *BaremetalCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
baremetal := obj.(*models.SHost)
self.SetStage("OnIpmiProbeComplete", nil)
baremetal.StartIpmiProbeTask(ctx, self.UserCred, self.GetTaskId())
}
func (self *BaremetalCreateTask) OnIpmiProbeComplete(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
baremetal := obj.(*models.SHost)
ipmiInfo, _ := baremetal.GetIpmiInfo()
if !ipmiInfo.Verified {
self.SetStageComplete(ctx, nil)
return
}
if jsonutils.QueryBoolean(self.Params, "no_prepare", false) {
self.SetStageComplete(ctx, nil)
return
}
if (baremetal.EnablePxeBoot.IsFalse() || !ipmiInfo.PxeBoot) && !ipmiInfo.CdromBoot {
self.SetStageComplete(ctx, nil)
return
}
if baremetal.AccessMac == "" && !ipmiInfo.CdromBoot {
self.SetStageComplete(ctx, nil)
return
}
self.SetStage("OnPrepareComplete", nil)
baremetal.StartPrepareTask(ctx, self.UserCred, "", self.GetTaskId())
}
func (self *BaremetalCreateTask) OnIpmiProbeCompleteFailed(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
self.SetStageFailed(ctx, body.String())
}
func (self *BaremetalCreateTask) OnPrepareComplete(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
self.SetStageComplete(ctx, nil)
}
func (self *BaremetalCreateTask) OnPrepareCompleteFailed(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
self.SetStageFailed(ctx, body.String())
}
@@ -0,0 +1,62 @@
// 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 tasks
import (
"context"
"fmt"
"yunion.io/x/jsonutils"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
)
type BaremetalIpmiProbeTask struct {
SBaremetalBaseTask
}
func init() {
taskman.RegisterTask(BaremetalIpmiProbeTask{})
}
func (self *BaremetalIpmiProbeTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
baremetal := obj.(*models.SHost)
baremetal.SetStatus(self.UserCred, api.BAREMETAL_PROBING, "")
url := fmt.Sprintf("/baremetals/%s/ipmi-probe", baremetal.Id)
headers := self.GetTaskRequestHeader()
self.SetStage("OnSyncConfigComplete", nil)
_, err := baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, self.Params)
if err != nil {
self.OnFailure(ctx, baremetal, err.Error())
}
}
func (self *BaremetalIpmiProbeTask) OnFailure(ctx context.Context, baremetal *models.SHost, reason string) {
baremetal.SetStatus(self.UserCred, api.BAREMETAL_PROBE_FAIL, reason)
self.SetStageFailed(ctx, reason)
}
func (self *BaremetalIpmiProbeTask) OnSyncConfigComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
// baremetal.ClearSchedDescCache()
self.SetStageComplete(ctx, nil)
}
func (self *BaremetalIpmiProbeTask) OnSyncConfigCompleteFailed(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
reason, _ := body.GetString("__reason__")
self.OnFailure(ctx, baremetal, reason)
}
+1 -2
View File
@@ -92,8 +92,7 @@ func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel
lockman.LockObject(ctx, network)
defer lockman.ReleaseObject(ctx, network)
addrTable := network.GetUsedAddresses()
ipAddr, err := network.GetFreeIP(ctx, self.UserCred, addrTable, nil, ip, models.IPAllocationNone, false)
ipAddr, err := network.GetFreeIP(ctx, self.UserCred, nil, nil, ip, api.IPAllocationNone, false)
if err != nil {
self.onFailed(ctx, eip, err.Error())
return
+1 -1
View File
@@ -101,7 +101,7 @@ func (h *SHostInfo) GetHostId() string {
return h.HostId
}
func (h *SHostInfo) GetZone() string {
func (h *SHostInfo) GetZoneName() string {
return h.Zone
}
+10 -2
View File
@@ -36,7 +36,7 @@ import (
)
type IHost interface {
GetZone() string
GetZoneName() string
GetHostId() string
GetMediumType() string
GetMasterIp() string
@@ -186,7 +186,15 @@ func DelayTaskWithWorker(
wm.DelayTaskWithWorker(ctx, task, params, worker)
}
func Init() {
func InitWorkerManager() {
wm = workmanager.NewWorkManger(TaskFailed, TaskComplete, options.HostOptions.DefaultRequestWorkerCount)
}
func InitK8sWorkerManager() {
k8sWm = workmanager.NewWorkManger(K8sTaskFailed, K8sTaskComplete, options.HostOptions.DefaultRequestWorkerCount)
}
func Init() {
InitWorkerManager()
InitK8sWorkerManager()
}
+6 -2
View File
@@ -37,6 +37,10 @@ import (
const MINIMAL_FREE_SPACE = 128
type IStorageManager interface {
GetZoneName() string
}
type SStorageManager struct {
host hostutils.IHost
@@ -103,8 +107,8 @@ func (s *SStorageManager) Remove(storage IStorage) {
}
}
func (s *SStorageManager) GetZone() string {
return s.host.GetZone()
func (s *SStorageManager) GetZoneName() string {
return s.host.GetZoneName()
}
func (s *SStorageManager) GetHostId() string {
+2 -2
View File
@@ -111,8 +111,8 @@ func (d *SBaseDisk) Resize(context.Context, interface{}) (jsonutils.JSONObject,
return nil, fmt.Errorf("Not implemented")
}
func (d *SBaseDisk) GetZone() string {
return d.Storage.GetZone()
func (d *SBaseDisk) GetZoneName() string {
return d.Storage.GetZoneName()
}
func (d *SBaseDisk) DeployGuestFs(diskPath string, guestDesc *jsonutils.JSONDict,
+1 -1
View File
@@ -206,7 +206,7 @@ func (d *SLocalDisk) CreateFromTemplate(ctx context.Context, imageId, format str
func (d *SLocalDisk) createFromTemplate(
ctx context.Context, imageId, format string, imageCacheManager IImageCacheManger,
) (jsonutils.JSONObject, error) {
imageCache := imageCacheManager.AcquireImage(ctx, imageId, d.GetZone(), "", "")
imageCache := imageCacheManager.AcquireImage(ctx, imageId, d.GetZoneName(), "", "")
if imageCache != nil {
defer imageCacheManager.ReleaseImage(imageId)
cacheImagePath := imageCache.GetPath()
+1 -1
View File
@@ -178,7 +178,7 @@ func (d *SRBDDisk) createFromTemplate(ctx context.Context, imageId, format strin
if imageCacheManager == nil {
return nil, fmt.Errorf("failed to find image cache manger for storage %s", d.Storage.GetStorageName())
}
imageCache := imageCacheManager.AcquireImage(ctx, imageId, d.GetZone(), "", "")
imageCache := imageCacheManager.AcquireImage(ctx, imageId, d.GetZoneName(), "", "")
if imageCache == nil {
return nil, fmt.Errorf("failed to qcquire image for storage %s", d.Storage.GetStorageName())
}
@@ -58,7 +58,7 @@ type IImageCacheManger interface {
}
type SBaseImageCacheManager struct {
storagemanager *SStorageManager
storageManager IStorageManager
storagecacaheId string
cachePath string
cachedImages map[string]IImageCache
@@ -76,3 +76,7 @@ func (c *SBaseImageCacheManager) GetId() string {
func (c *SBaseImageCacheManager) SetStoragecacheId(scid string) {
c.storagecacaheId = scid
}
func (c *SBaseImageCacheManager) GetStorageManager() IStorageManager {
return c.storageManager
}
@@ -32,12 +32,12 @@ import (
type SLocalImageCacheManager struct {
SBaseImageCacheManager
// limit int
isTemplate bool
// isTemplate bool
}
func NewLocalImageCacheManager(manager *SStorageManager, cachePath string, storagecacheId string) *SLocalImageCacheManager {
func NewLocalImageCacheManager(manager IStorageManager, cachePath string, storagecacheId string) *SLocalImageCacheManager {
imageCacheManager := new(SLocalImageCacheManager)
imageCacheManager.storagemanager = manager
imageCacheManager.storageManager = manager
imageCacheManager.storagecacaheId = storagecacheId
imageCacheManager.cachePath = cachePath
// imageCacheManager.limit = limit
@@ -130,7 +130,7 @@ func (c *SLocalImageCacheManager) PrefetchImageCache(ctx context.Context, data i
format, _ := body.GetString("format")
srcUrl, _ := body.GetString("src_url")
if imgCache := c.AcquireImage(ctx, imageId, storageManager.GetZone(),
if imgCache := c.AcquireImage(ctx, imageId, c.GetStorageManager().GetZoneName(),
srcUrl, format); imgCache != nil {
defer imgCache.Release()
@@ -35,10 +35,10 @@ type SRbdImageCacheManager struct {
storage IStorage
}
func NewRbdImageCacheManager(manager *SStorageManager, cachePath string, storage IStorage, storagecacheId string) *SRbdImageCacheManager {
func NewRbdImageCacheManager(manager IStorageManager, cachePath string, storage IStorage, storagecacheId string) *SRbdImageCacheManager {
imageCacheManager := new(SRbdImageCacheManager)
imageCacheManager.storagemanager = manager
imageCacheManager.storageManager = manager
imageCacheManager.storagecacaheId = storagecacheId
imageCacheManager.storage = storage
+3 -3
View File
@@ -74,7 +74,7 @@ func NewStorage(manager *SStorageManager, mountPoint, storageType string) IStora
type IStorage interface {
GetId() string
GetStorageName() string
GetZone() string
GetZoneName() string
SetStorageInfo(storageId, storageName string, conf jsonutils.JSONObject)
SyncStorageInfo() (jsonutils.JSONObject, error)
@@ -169,8 +169,8 @@ func (s *SBaseStorage) SetPath(p string) {
s.Path = p
}
func (s *SBaseStorage) GetZone() string {
return s.Manager.GetZone()
func (s *SBaseStorage) GetZoneName() string {
return s.Manager.GetZoneName()
}
func (s *SBaseStorage) GetCapacity() int {
+3 -3
View File
@@ -86,7 +86,7 @@ func (s *SLocalStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
content.Set("capacity", jsonutils.NewInt(int64(s.GetAvailSizeMb())))
content.Set("storage_type", jsonutils.NewString(s.StorageType()))
content.Set("medium_type", jsonutils.NewString(s.GetMediumType()))
content.Set("zone", jsonutils.NewString(s.GetZone()))
content.Set("zone", jsonutils.NewString(s.GetZoneName()))
if len(s.Manager.LocalStorageImagecacheManager.GetId()) > 0 {
content.Set("storagecache_id",
jsonutils.NewString(s.Manager.LocalStorageImagecacheManager.GetId()))
@@ -283,7 +283,7 @@ func (s *SLocalStorage) saveToGlance(ctx context.Context, imageId, imagePath str
}
params.Set("image_id", jsonutils.NewString(imageId))
_, err = modules.Images.Upload(hostutils.GetImageSession(ctx, s.GetZone()),
_, err = modules.Images.Upload(hostutils.GetImageSession(ctx, s.GetZoneName()),
params, f, size)
return err
}
@@ -291,7 +291,7 @@ func (s *SLocalStorage) saveToGlance(ctx context.Context, imageId, imagePath str
func (s *SLocalStorage) onSaveToGlanceFailed(ctx context.Context, imageId string) {
params := jsonutils.NewDict()
params.Set("status", jsonutils.NewString("killed"))
_, err := modules.Images.Update(hostutils.GetImageSession(ctx, s.GetZone()),
_, err := modules.Images.Update(hostutils.GetImageSession(ctx, s.GetZoneName()),
imageId, params)
if err != nil {
log.Errorln(err)
+1 -1
View File
@@ -86,7 +86,7 @@ func (s *SNasStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
content := jsonutils.NewDict()
content.Set("capacity", jsonutils.NewInt(int64(s.GetAvailSizeMb())))
content.Set("storage_type", jsonutils.NewString(s.ins.StorageType()))
content.Set("zone", jsonutils.NewString(s.GetZone()))
content.Set("zone", jsonutils.NewString(s.GetZoneName()))
log.Infof("Sync storage info %s", s.StorageId)
res, err := modules.Storages.Put(
hostutils.GetComputeSession(context.Background()),
+1 -1
View File
@@ -73,7 +73,7 @@ func (s *SNFSStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
content.Set("capacity", jsonutils.NewInt(int64(s.GetAvailSizeMb())))
content.Set("storage_type", jsonutils.NewString(s.StorageType()))
content.Set("status", jsonutils.NewString(api.STORAGE_ONLINE))
content.Set("zone", jsonutils.NewString(s.GetZone()))
content.Set("zone", jsonutils.NewString(s.GetZoneName()))
log.Infof("Sync storage info %s", s.StorageId)
res, err := modules.Storages.Put(
hostutils.GetComputeSession(context.Background()),
+3 -3
View File
@@ -560,7 +560,7 @@ func (s *SRbdStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
"name": s.StorageName,
"capacity": capacity,
"status": api.STORAGE_ONLINE,
"zone": s.GetZone(),
"zone": s.GetZoneName(),
}
return modules.Storages.Put(hostutils.GetComputeSession(context.Background()), s.StorageId, jsonutils.Marshal(content))
}
@@ -639,7 +639,7 @@ func (s *SRbdStorage) SaveToGlance(ctx context.Context, params interface{}) (jso
func (s *SRbdStorage) onSaveToGlanceFailed(ctx context.Context, imageId string) {
params := jsonutils.NewDict()
params.Set("status", jsonutils.NewString("killed"))
_, err := modules.Images.Update(hostutils.GetImageSession(ctx, s.GetZone()),
_, err := modules.Images.Update(hostutils.GetImageSession(ctx, s.GetZoneName()),
imageId, params)
if err != nil {
log.Errorln(err)
@@ -695,7 +695,7 @@ func (s *SRbdStorage) saveToGlance(ctx context.Context, imageId, imagePath strin
}
params.Set("image_id", jsonutils.NewString(imageId))
_, err = modules.Images.Upload(hostutils.GetImageSession(ctx, s.GetZone()),
_, err = modules.Images.Upload(hostutils.GetImageSession(ctx, s.GetZoneName()),
params, f, size)
return err
}
+1 -1
View File
@@ -184,7 +184,7 @@ var (
func init() {
Hosts = HostManager{NewComputeManager("host", "hosts",
[]string{"ID", "Name", "Access_mac", "Access_ip",
[]string{"ID", "Name", "Access_mac", "Access_ip", "Ipmi_Ip",
"Manager_URI",
"Status", "enabled", "host_status",
"Guests", "Running_guests",
+1 -1
View File
@@ -92,7 +92,7 @@ func (this *ReservedIPManager) DoBatchReleaseReservedIPs(s *mcclient.ClientSessi
func init() {
ReservedIPs = ReservedIPManager{NewComputeManager("reservedip", "reservedips",
[]string{},
[]string{"Network_ID", "Network", "IP_addr", "Notes"})}
[]string{"Network_ID", "Network", "IP_addr", "Notes", "Expired_At", "Expired"})}
registerCompute(&ReservedIPs)
}
+14
View File
@@ -1 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cache // import "yunion.io/x/onecloud/pkg/notify/cache"
+14
View File
@@ -1 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package _interface // import "yunion.io/x/onecloud/pkg/notify/interface"
+14
View File
@@ -1 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package apis // import "yunion.io/x/onecloud/pkg/notify/rpc/apis"
+14
View File
@@ -1 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rpc // import "yunion.io/x/onecloud/pkg/notify/rpc"
+24 -18
View File
@@ -27,11 +27,12 @@ import (
type TBillingCycleUnit string
const (
BillingCycleHour = TBillingCycleUnit("H")
BillingCycleDay = TBillingCycleUnit("D")
BillingCycleWeek = TBillingCycleUnit("W")
BillingCycleMonth = TBillingCycleUnit("M")
BillingCycleYear = TBillingCycleUnit("Y")
BillingCycleMinute = TBillingCycleUnit("I")
BillingCycleHour = TBillingCycleUnit("H")
BillingCycleDay = TBillingCycleUnit("D")
BillingCycleWeek = TBillingCycleUnit("W")
BillingCycleMonth = TBillingCycleUnit("M")
BillingCycleYear = TBillingCycleUnit("Y")
)
var (
@@ -49,6 +50,8 @@ func ParseBillingCycle(cycleStr string) (SBillingCycle, error) {
return cycle, ErrInvalidBillingCycle
}
switch cycleStr[len(cycleStr)-1:] {
case string(BillingCycleMinute), strings.ToLower(string(BillingCycleMinute)):
cycle.Unit = BillingCycleMinute
case string(BillingCycleHour), strings.ToLower(string(BillingCycleHour)):
cycle.Unit = BillingCycleHour
case string(BillingCycleDay), strings.ToLower(string(BillingCycleDay)):
@@ -75,11 +78,13 @@ func (cycle *SBillingCycle) String() string {
return fmt.Sprintf("%d%s", cycle.Count, cycle.Unit)
}
func (cycle *SBillingCycle) EndAt(tm time.Time) time.Time {
func (cycle SBillingCycle) EndAt(tm time.Time) time.Time {
if tm.IsZero() {
tm = time.Now().UTC()
}
switch cycle.Unit {
case BillingCycleMinute:
return tm.Add(time.Minute * time.Duration(cycle.Count))
case BillingCycleHour:
return tm.Add(time.Hour * time.Duration(cycle.Count))
case BillingCycleDay:
@@ -95,13 +100,18 @@ func (cycle *SBillingCycle) EndAt(tm time.Time) time.Time {
}
}
func (cycle SBillingCycle) Duration() time.Duration {
now := time.Now().UTC()
endAt := cycle.EndAt(now)
return endAt.Sub(now)
}
func (cycle *SBillingCycle) GetDays() int {
switch cycle.Unit {
case BillingCycleMinute:
return cycle.Count / 24 / 60
case BillingCycleHour:
if cycle.Count%24 == 0 {
return cycle.Count / 24
}
return 0
return cycle.Count / 24
case BillingCycleDay:
return cycle.Count
case BillingCycleWeek:
@@ -113,16 +123,12 @@ func (cycle *SBillingCycle) GetDays() int {
func (cycle *SBillingCycle) GetWeeks() int {
switch cycle.Unit {
case BillingCycleMinute:
return cycle.Count / 7 / 24 / 60
case BillingCycleHour:
if cycle.Count%(7*24) == 0 {
return cycle.Count / (7 * 24)
}
return 0
return cycle.Count / 7 / 24
case BillingCycleDay:
if cycle.Count%7 == 0 {
return cycle.Count / 7
}
return 0
return cycle.Count / 7
case BillingCycleWeek:
return cycle.Count
default:
+38
View File
@@ -0,0 +1,38 @@
package httputils
import (
"net/http"
"strings"
)
// http filesystem that prevent directory listing
// https://gist.github.com/hauxe/f2ea1901216177ccf9550a1b8bd59178#file-http_static_correct-go
// FileSystem custom file system handler
type FileSystem struct {
fs http.FileSystem
}
// Open opens file
func (fs FileSystem) Open(path string) (http.File, error) {
f, err := fs.fs.Open(path)
if err != nil {
return nil, err
}
s, err := f.Stat()
if s.IsDir() {
index := strings.TrimSuffix(path, "/") + "/index.html"
if _, err := fs.fs.Open(index); err != nil {
return nil, err
}
}
return f, nil
}
func Dir(dir string) http.FileSystem {
return FileSystem{
http.Dir(dir),
}
}
+73
View File
@@ -0,0 +1,73 @@
// 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 (
"bytes"
"context"
"fmt"
"net/http"
"strings"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/util/httputils"
)
type SBMCConsole struct {
client *http.Client
username string
password string
host string
isDebug bool
}
func NewBMCConsole(host, username, password string, isDebug bool) *SBMCConsole {
client := httputils.GetDefaultClient()
return &SBMCConsole{
client: client,
host: host,
username: username,
password: password,
isDebug: isDebug,
}
}
func setCookieHeader(hdr http.Header, cookies map[string]string) {
cookieParts := make([]string, 0)
for k, v := range cookies {
cookieParts = append(cookieParts, k+"="+v)
}
if len(cookieParts) > 0 {
hdr.Set("Cookie", strings.Join(cookieParts, "; "))
}
}
func (r *SBMCConsole) RawRequest(ctx context.Context, method httputils.THttpMethod, path string, header http.Header, body []byte) (http.Header, []byte, error) {
urlStr := httputils.JoinPath(fmt.Sprintf("https://%s", r.host), path)
if header == nil {
header = http.Header{}
}
header.Set("Connection", "Close")
header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:69.0) Gecko/20100101 Firefox/69.0")
resp, err := httputils.Request(r.client, ctx, method, urlStr, header, bytes.NewReader(body), r.isDebug)
hdr, rspBody, err := httputils.ParseResponse(resp, err, r.isDebug)
if err != nil {
return nil, nil, errors.Wrap(err, "httputils.Request")
}
return hdr, rspBody, nil
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bmconsole // import "yunion.io/x/onecloud/pkg/util/redfish/bmconsole"
+94
View File
@@ -0,0 +1,94 @@
// 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/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/util/httputils"
)
func (r *SBMCConsole) GetIdracConsoleJNLP(ctx context.Context, sku, model string) (string, error) {
loginData := strings.Join([]string{
"user=" + url.QueryEscape(r.username),
"password=" + url.QueryEscape(r.password),
}, "&")
// cookie:
// -http-session-=::http.session::0103fd02ceac2d642361b6fdcd4a5994;
// sysidledicon=ledIcon%20grayLed;
// tokenvalue=478be97abdaeb4d454c0418fcca9094d
cookies := make(map[string]string)
cookies["-http-session-"] = ""
// first do html login
postHdr := http.Header{}
postHdr.Set("Content-Type", "application/x-www-form-urlencoded")
setCookieHeader(postHdr, cookies)
hdr, loginResp, 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]
}
}
}
forwardUrlPattern := regexp.MustCompile(`<forwardUrl>(.*)</forwardUrl>`)
matched := forwardUrlPattern.FindAllStringSubmatch(string(loginResp), -1)
indexUrlStr := ""
if len(matched) > 0 && len(matched[0]) > 1 {
indexUrlStr = matched[0][1]
}
if len(indexUrlStr) == 0 {
return "", errors.Wrapf(httperrors.ErrBadRequest, "no valid forwardUrl")
}
tokenPattern := regexp.MustCompile(`ST1=(\w+),ST2=`)
matched = tokenPattern.FindAllStringSubmatch(indexUrlStr, -1)
log.Debugf("%s", matched)
token := ""
if len(matched) > 0 && len(matched[0]) > 1 {
token = matched[0][1]
}
cookies["tokenvalue"] = token
getHdr := http.Header{}
setCookieHeader(getHdr, cookies)
sysStr := url.QueryEscape(fmt.Sprintf("idrac-%s, %s, User: %s", sku, model, r.username))
path := fmt.Sprintf("viewer.jnlp(%s@0@%s@%d@ST1=%s)", r.host, sysStr, time.Now().UnixNano()/1000000, token)
_, rspBody, err := r.RawRequest(ctx, httputils.GET, path, getHdr, nil)
if err != nil {
return "", errors.Wrapf(err, "r.RawGet %s", path)
}
return string(rspBody), nil
}
+86
View File
@@ -0,0 +1,86 @@
// 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 (
"bytes"
"context"
"fmt"
"net/http"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/util/httputils"
)
func (r *SBMCConsole) GetIloConsoleJNLP(ctx context.Context) (string, error) {
loginData := jsonutils.NewDict()
loginData.Add(jsonutils.NewString("login"), "method")
loginData.Add(jsonutils.NewString(r.username), "user_login")
loginData.Add(jsonutils.NewString(r.password), "password")
postHdr := http.Header{}
postHdr.Set("Content-Type", "application/json")
_, loginRespBytes, err := r.RawRequest(ctx, httputils.POST, "/json/login_session", postHdr, []byte(loginData.String()))
if err != nil {
return "", errors.Wrap(err, "r.FormPost Login")
}
loginRespJson, err := jsonutils.Parse(loginRespBytes)
if err != nil {
return "", errors.Wrap(err, "jsonutils.Parse loginRespBytes")
}
sessionKey, err := loginRespJson.GetString("session_key")
if err != nil {
return "", errors.Wrap(err, "Get session_key")
}
endpoint := fmt.Sprintf("https://%s/", r.host)
cookies := make(map[string]string)
cookies["sessionKey"] = sessionKey
cookies["sessionLang"] = "en"
cookies["sessionUrl"] = endpoint
getHdr := http.Header{}
setCookieHeader(getHdr, cookies)
_, tempBytes, err := r.RawRequest(ctx, httputils.GET, "/html/jnlp_template.html", getHdr, nil)
if err != nil {
return "", errors.Wrap(err, "request template")
}
startToken := []byte("<![CDATA[\n")
endToken := []byte("]]>")
pos := bytes.Index(tempBytes, startToken)
if pos < 0 {
return "", errors.Wrapf(err, "invalid template content %s: no start token", tempBytes)
}
tempBytes = tempBytes[pos+len(startToken):]
pos = bytes.Index(tempBytes, endToken)
if pos < 0 {
return "", errors.Wrapf(err, "invalid template content %s: no end token", tempBytes)
}
template := string(tempBytes[:pos])
// replace variables
template = strings.ReplaceAll(template, "<%= this.baseUrl %>", endpoint)
template = strings.ReplaceAll(template, "<%= this.sessionKey %>", sessionKey)
template = strings.ReplaceAll(template, "<%= this.langId %>", "en")
return template, nil
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package redfish // import "yunion.io/x/onecloud/pkg/util/redfish"
+23 -2
View File
@@ -1,3 +1,17 @@
// 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 redfish
import (
@@ -5,6 +19,8 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
)
type IRedfishDriverFactory interface {
@@ -32,12 +48,14 @@ type IRedfishDriver interface {
GetResourceCount(ctx context.Context, resname ...string) (int, error)
GetVirtualCdromInfo(ctx context.Context) (string, SCdromInfo, error)
MountVirtualCdrom(ctx context.Context, path string, cdromUrl string) error
MountVirtualCdrom(ctx context.Context, path string, cdromUrl string, boot bool) error
UmountVirtualCdrom(ctx context.Context, path string) error
GetLanConfigs(ctx context.Context) ([]types.SIPMILanConfig, error)
GetSystemInfo(ctx context.Context) (string, SSystemInfo, error)
SetNextBootDev(ctx context.Context, dev string) error
SetNextBootVirtualCdrom(ctx context.Context) error
// SetNextBootVirtualCdrom(ctx context.Context) error
Reset(ctx context.Context, action string) error
@@ -86,6 +104,9 @@ func NewRedfishDriver(ctx context.Context, endpoint string, username, password s
return drv
}
}
if defaultFactory == nil {
return nil
}
drv := defaultFactory.NewApi(endpoint, username, password, debug)
err := drv.Probe(ctx)
if err == nil {
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generic // import "yunion.io/x/onecloud/pkg/util/redfish/generic"
+14
View File
@@ -1,3 +1,17 @@
// 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 generic
import (
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hprest // import "yunion.io/x/onecloud/pkg/util/redfish/hprest"
+21 -3
View File
@@ -1,3 +1,17 @@
// 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 hprest
import (
@@ -73,6 +87,10 @@ func (r *SHpRestApi) MemberKey() string {
return memberKey
}
func (r *SHpRestApi) LogItemsKey() string {
return "Items"
}
func (r *SHpRestApi) Probe(ctx context.Context) error {
err := r.SBaseRedfishClient.Probe(ctx)
if err != nil {
@@ -167,7 +185,7 @@ func (r *SHpRestApi) Reset(ctx context.Context, action string) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(action), "ResetType")
params.Add(jsonutils.NewString("Reset"), "Action")
resp, err := r.Post(ctx, path, params)
_, resp, err := r.Post(ctx, path, params)
if err != nil {
return errors.Wrap(err, "Action.Reset")
}
@@ -184,7 +202,7 @@ func (r *SHpRestApi) BmcReset(ctx context.Context) error {
}
params := jsonutils.NewDict()
params.Add(jsonutils.NewString("Reset"), "Action")
resp, err := r.Post(ctx, path, params)
_, resp, err := r.Post(ctx, path, params)
if err != nil {
return errors.Wrap(err, "Actions/Manager.Reset")
}
@@ -244,7 +262,7 @@ func (r *SHpRestApi) clearLogs(ctx context.Context, subsys string) error {
}
params := jsonutils.NewDict()
params.Add(jsonutils.NewString("ClearLog"), "Action")
resp, err := r.Post(ctx, path, params)
_, resp, err := r.Post(ctx, path, params)
if err != nil {
return errors.Wrap(err, "r.Post")
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package idrac // import "yunion.io/x/onecloud/pkg/util/redfish/idrac"
+24 -68
View File
@@ -1,11 +1,22 @@
// 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 idrac
import (
"context"
"fmt"
"net/http"
"net/url"
"regexp"
"strings"
"time"
@@ -16,6 +27,7 @@ import (
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/util/httputils"
"yunion.io/x/onecloud/pkg/util/redfish"
"yunion.io/x/onecloud/pkg/util/redfish/bmconsole"
"yunion.io/x/onecloud/pkg/util/redfish/generic"
)
@@ -73,7 +85,7 @@ func (r *SIDracRefishApi) GetVirtualCdromInfo(ctx context.Context) (string, redf
return path, cdInfo, nil
}
func (r *SIDracRefishApi) MountVirtualCdrom(ctx context.Context, path string, cdromUrl string) error {
func (r *SIDracRefishApi) MountVirtualCdrom(ctx context.Context, path string, cdromUrl string, boot bool) error {
info := jsonutils.NewDict()
info.Set("Image", jsonutils.NewString(cdromUrl))
@@ -83,6 +95,12 @@ func (r *SIDracRefishApi) MountVirtualCdrom(ctx context.Context, path string, cd
return errors.Wrap(err, "r.Post")
}
// log.Debugf("%s", resp.PrettyString())
if boot {
err = r.SetNextBootVirtualCdrom(ctx)
if err != nil {
return errors.Wrap(err, "r.SetNextBootVirtualCdrom")
}
}
return nil
}
@@ -312,68 +330,6 @@ func (r *SIDracRefishApi) doImportConfig(ctx context.Context, conf iDRACConfig)
}
func (r *SIDracRefishApi) GetConsoleJNLP(ctx context.Context) (string, error) {
loginData := strings.Join([]string{
"user=" + url.QueryEscape(r.GetUsername()),
"password=" + url.QueryEscape(r.GetPassword()),
}, "&")
// cookie:
// -http-session-=::http.session::0103fd02ceac2d642361b6fdcd4a5994;
// sysidledicon=ledIcon%20grayLed;
// tokenvalue=478be97abdaeb4d454c0418fcca9094d
cookies := make(map[string]string)
cookies["-http-session-"] = ""
// first do html login
postHdr := http.Header{}
postHdr.Set("Content-Type", "application/x-www-form-urlencoded")
redfish.SetCookieHeader(postHdr, cookies)
hdr, loginResp, 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]
}
}
}
forwardUrlPattern := regexp.MustCompile(`<forwardUrl>(.*)</forwardUrl>`)
matched := forwardUrlPattern.FindAllStringSubmatch(string(loginResp), -1)
indexUrlStr := ""
if len(matched) > 0 && len(matched[0]) > 1 {
indexUrlStr = matched[0][1]
}
if len(indexUrlStr) == 0 {
return "", errors.Wrapf(httperrors.ErrBadRequest, "no valid forwardUrl")
}
tokenPattern := regexp.MustCompile(`ST1=(\w+),ST2=`)
matched = tokenPattern.FindAllStringSubmatch(indexUrlStr, -1)
log.Debugf("%s", matched)
token := ""
if len(matched) > 0 && len(matched[0]) > 1 {
token = matched[0][1]
}
cookies["tokenvalue"] = token
getHdr := http.Header{}
redfish.SetCookieHeader(getHdr, cookies)
_, sysInfo, err := r.GetSystemInfo(ctx)
if err != nil {
return "", errors.Wrap(err, "r.GetSystemInfo")
}
sysStr := url.QueryEscape(fmt.Sprintf("idrac-%s, %s, User: %s", sysInfo.SKU, sysInfo.Model, r.GetUsername()))
path := fmt.Sprintf("viewer.jnlp(%s@0@%s@%d@ST1=%s)", r.GetHost(), sysStr, time.Now().UnixNano()/1000000, token)
_, rspBody, err := r.RawRequest(ctx, httputils.GET, path, getHdr, nil)
if err != nil {
return "", errors.Wrapf(err, "r.RawGet %s", path)
}
return string(rspBody), nil
bmc := bmconsole.NewBMCConsole(r.GetHost(), r.GetUsername(), r.GetPassword(), r.IsDebug)
return bmc.GetIdracConsoleJNLP(ctx, "", "")
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ilo // import "yunion.io/x/onecloud/pkg/util/redfish/ilo"
+51 -60
View File
@@ -1,9 +1,21 @@
// 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 ilo
import (
"bytes"
"context"
"net/http"
"strings"
"yunion.io/x/jsonutils"
@@ -12,6 +24,7 @@ import (
"yunion.io/x/onecloud/pkg/util/httputils"
"yunion.io/x/onecloud/pkg/util/redfish"
"yunion.io/x/onecloud/pkg/util/redfish/bmconsole"
"yunion.io/x/onecloud/pkg/util/redfish/generic"
)
@@ -179,6 +192,9 @@ func (r *SILORefishApi) SetNTPConf(ctx context.Context, conf redfish.SNTPConf) e
if err != nil {
return errors.Wrap(err, "r.GetResource Managers 0")
}
if len(conf.NTPServers) > 2 {
conf.NTPServers = conf.NTPServers[:2]
}
dateUrl := httputils.JoinPath(path, "DateTime")
params := jsonutils.NewDict()
params.Add(jsonutils.NewStringArray(conf.NTPServers), "StaticNTPServers")
@@ -194,62 +210,37 @@ func (r *SILORefishApi) SetNTPConf(ctx context.Context, conf redfish.SNTPConf) e
}
func (r *SILORefishApi) GetConsoleJNLP(ctx context.Context) (string, error) {
loginData := jsonutils.NewDict()
loginData.Add(jsonutils.NewString("login"), "method")
loginData.Add(jsonutils.NewString(r.GetUsername()), "user_login")
loginData.Add(jsonutils.NewString(r.GetPassword()), "password")
postHdr := http.Header{}
postHdr.Set("Content-Type", "application/json")
_, loginRespBytes, err := r.RawRequest(ctx, httputils.POST, "/json/login_session", postHdr, []byte(loginData.String()))
if err != nil {
return "", errors.Wrap(err, "r.FormPost Login")
}
loginRespJson, err := jsonutils.Parse(loginRespBytes)
if err != nil {
return "", errors.Wrap(err, "jsonutils.Parse loginRespBytes")
}
sessionKey, err := loginRespJson.GetString("session_key")
if err != nil {
return "", errors.Wrap(err, "Get session_key")
}
endpoint := r.GetEndpoint()
if !strings.HasSuffix(endpoint, "/") {
endpoint += "/"
}
cookies := make(map[string]string)
cookies["sessionKey"] = sessionKey
cookies["sessionLang"] = "en"
cookies["sessionUrl"] = endpoint
getHdr := http.Header{}
redfish.SetCookieHeader(getHdr, cookies)
_, tempBytes, err := r.RawRequest(ctx, httputils.GET, "/html/jnlp_template.html", getHdr, nil)
if err != nil {
return "", errors.Wrap(err, "request template")
}
startToken := []byte("<![CDATA[\n")
endToken := []byte("]]>")
pos := bytes.Index(tempBytes, startToken)
if pos < 0 {
return "", errors.Wrapf(err, "invalid template content %s: no start token", tempBytes)
}
tempBytes = tempBytes[pos+len(startToken):]
pos = bytes.Index(tempBytes, endToken)
if pos < 0 {
return "", errors.Wrapf(err, "invalid template content %s: no end token", tempBytes)
}
template := string(tempBytes[:pos])
// replace variables
template = strings.ReplaceAll(template, "<%= this.baseUrl %>", endpoint)
template = strings.ReplaceAll(template, "<%= this.sessionKey %>", sessionKey)
template = strings.ReplaceAll(template, "<%= this.langId %>", "en")
return template, nil
bmc := bmconsole.NewBMCConsole(r.GetHost(), r.GetUsername(), r.GetPassword(), r.IsDebug)
return bmc.GetIloConsoleJNLP(ctx)
}
func (r *SILORefishApi) MountVirtualCdrom(ctx context.Context, path string, cdromUrl string, boot bool) error {
info := jsonutils.NewDict()
info.Set("Image", jsonutils.NewString(cdromUrl))
if boot {
cdInfo, err := r.Get(ctx, path)
if err != nil {
return errors.Wrapf(err, "Get %s", path)
}
var oemKey string
_, err = cdInfo.Bool("Oem", "Hp", "BootOnNextServerReset")
if err != nil {
_, err = cdInfo.Bool("Oem", "Hpe", "BootOnNextServerReset")
if err != nil {
return errors.Wrap(err, "no BootOnNextServerReset found???")
} else {
oemKey = "Hpe"
}
} else {
oemKey = "Hp"
}
info.Add(jsonutils.JSONTrue, "Oem", oemKey, "BootOnNextServerReset")
}
resp, err := r.Patch(ctx, path, info)
if err != nil {
return errors.Wrap(err, "r.Patch")
}
log.Debugf("%s", resp.PrettyString())
return nil
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loader // import "yunion.io/x/onecloud/pkg/util/redfish/loader"
+14
View File
@@ -1,3 +1,17 @@
// 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 loader
import (
+97 -7
View File
@@ -1,9 +1,23 @@
// 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 redfish
import (
"bytes"
"context"
"encoding/base64"
"net"
"net/http"
"net/url"
"strconv"
@@ -16,6 +30,7 @@ import (
"yunion.io/x/pkg/utils"
"yunion.io/x/onecloud/pkg/cloudcommon/object"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/util/httputils"
)
@@ -98,7 +113,7 @@ func (r *SBaseRedfishClient) request(ctx context.Context, method httputils.THttp
return hdr, resp, nil
}
func SetCookieHeader(hdr http.Header, cookies map[string]string) {
/*func SetCookieHeader(hdr http.Header, cookies map[string]string) {
cookieParts := make([]string, 0)
for k, v := range cookies {
cookieParts = append(cookieParts, k+"="+v)
@@ -121,7 +136,7 @@ func (r *SBaseRedfishClient) RawRequest(ctx context.Context, method httputils.TH
return nil, nil, errors.Wrap(err, "httputils.Request")
}
return hdr, rspBody, nil
}
}*/
func (r *SBaseRedfishClient) Get(ctx context.Context, path string) (jsonutils.JSONObject, error) {
_, resp, err := r.request(ctx, httputils.GET, path, nil, nil)
@@ -322,7 +337,7 @@ func (r *SBaseRedfishClient) GetVirtualCdromInfo(ctx context.Context) (string, S
return path, cdInfo, nil
}
func (r *SBaseRedfishClient) MountVirtualCdrom(ctx context.Context, path string, cdromUrl string) error {
func (r *SBaseRedfishClient) MountVirtualCdrom(ctx context.Context, path string, cdromUrl string, boot bool) error {
info := jsonutils.NewDict()
info.Set("Image", jsonutils.NewString(cdromUrl))
@@ -366,6 +381,12 @@ func (r *SBaseRedfishClient) GetSystemInfo(ctx context.Context) (string, SSystem
sysInfo.Model = strings.TrimSpace(sysInfo.Model)
sysInfo.Manufacturer = strings.TrimSpace(sysInfo.Manufacturer)
if strings.EqualFold(sysInfo.PowerState, types.POWER_STATUS_ON) {
sysInfo.PowerState = types.POWER_STATUS_ON
} else {
sysInfo.PowerState = types.POWER_STATUS_OFF
}
memGBStr, _ := resp.GetString("MemorySummary", "TotalSystemMemoryGiB")
memGB, _ := strconv.ParseInt(memGBStr, 10, 64)
if memGB > 0 {
@@ -413,9 +434,16 @@ func (r *SBaseRedfishClient) GetSystemInfo(ctx context.Context) (string, SSystem
if err != nil {
return path, sysInfo, errors.Wrapf(err, "Get EthernetInterface[%d] error", i)
}
macAddr, _ := nicInfo.GetString("MacAddress")
if len(macAddr) == 0 {
macAddr, _ = nicInfo.GetString("PermanentMACAddress")
var macAddr string
for _, key := range []string{
"MacAddress",
"MACAddress",
"PermanentMACAddress",
} {
macAddr, _ = nicInfo.GetString(key)
if len(macAddr) > 0 {
break
}
}
sysInfo.EthernetNICs[i] = netutils.FormatMacAddr(macAddr)
}
@@ -685,3 +713,65 @@ func (r *SBaseRedfishClient) SetNTPConf(ctx context.Context, conf SNTPConf) erro
func (r *SBaseRedfishClient) GetConsoleJNLP(ctx context.Context) (string, error) {
return "", httperrors.ErrNotImplemented
}
func (r *SBaseRedfishClient) GetLanConfigs(ctx context.Context) ([]types.SIPMILanConfig, error) {
_, ethIfsJson, err := r.GetResource(ctx, "Managers", "0", "EthernetInterfaces")
if err != nil {
return nil, errors.Wrap(err, "GetResource Managers 0 EthernetInterfaces")
}
ethIfs, err := ethIfsJson.GetArray(r.IRedfishDriver().MemberKey())
if err != nil {
return nil, errors.Wrap(err, "GetArray")
}
ret := make([]types.SIPMILanConfig, 0)
for i := range ethIfs {
ethLink, _ := ethIfs[i].GetString(r.IRedfishDriver().LinkKey())
if len(ethLink) == 0 {
continue
}
ethJson, err := r.Get(ctx, ethLink)
if err != nil {
continue
}
v4Addrs, err := ethJson.GetArray("IPv4Addresses")
if err != nil {
continue
}
if len(v4Addrs) == 0 {
continue
}
for i := range v4Addrs {
addr, err := v4Addrs[i].GetString("Address")
if err != nil {
continue
}
if len(addr) > 0 && addr != "0.0.0.0" {
// find a config
conf := types.SIPMILanConfig{}
conf.IPAddr = addr
mask, _ := v4Addrs[i].GetString("SubnetMask")
conf.Netmask = mask
gw, _ := v4Addrs[i].GetString("Gateway")
conf.Gateway = gw
src, _ := v4Addrs[i].GetString("AddressOrigin")
if len(src) == 0 || src == "null" {
src = "static"
}
conf.IPSrc = strings.ToLower(src)
mac, _ := ethJson.GetString("MACAddress")
conf.Mac, _ = net.ParseMAC(mac)
var vlanId int64
if ethJson.Contains("VLAN") {
vlanId, _ = ethJson.Int("VLAN", "VLANId")
} else {
vlanId, _ = ethJson.Int("VLANId")
}
speed, _ := ethJson.Int("SpeedMbps")
conf.SpeedMbps = int(speed)
conf.VlanId = int(vlanId)
ret = append(ret, conf)
}
}
}
return ret, nil
}
+14
View File
@@ -1,3 +1,17 @@
// 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 redfish
import "time"
+55
View File
@@ -0,0 +1,55 @@
// 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 redfish
import (
"context"
"yunion.io/x/pkg/errors"
)
func MountVirtualCdrom(ctx context.Context, api IRedfishDriver, cdromUrl string, boot bool) error {
path, cdInfo, err := api.GetVirtualCdromInfo(ctx)
if err != nil {
return errors.Wrap(err, "api.GetVirtualCdromInfo")
}
if cdInfo.Image == cdromUrl {
return nil
}
if !cdInfo.SupportAction {
return errors.Error("action not supported")
}
if cdInfo.Image != "" {
err = api.UmountVirtualCdrom(ctx, path)
if err != nil {
return errors.Wrap(err, "api.UmountVirtualCdrom")
}
}
return api.MountVirtualCdrom(ctx, path, cdromUrl, boot)
}
func UmountVirtualCdrom(ctx context.Context, api IRedfishDriver) error {
path, cdInfo, err := api.GetVirtualCdromInfo(ctx)
if err != nil {
return errors.Wrap(err, "api.GetVirtualCdromInfo")
}
if cdInfo.Image == "" {
return nil
}
if !cdInfo.SupportAction {
return errors.Error("action not supported")
}
return api.UmountVirtualCdrom(ctx, path)
}