mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-29 03:51:54 +08:00
add doc and copyright
This commit is contained in:
@@ -1 +1,6 @@
|
||||
DESCRIPTION="Baremetal Agent Command Line Utility"
|
||||
DESCRIPTION="Baremetal Agent Command Line Utility"
|
||||
|
||||
REQUIRES=(
|
||||
ipmitool
|
||||
mkisofs
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Vendored
+14
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
@@ -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 (
|
||||
|
||||
@@ -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"
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -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, "", "")
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user