Merge branch 'release/2.6.0' of ssh://git.yunion.io/~qiujian/onecloud into feature/qj-image-p2p-distribution

Conflicts:
	pkg/appsrv/appsrv.go
	pkg/cloudcommon/policy/defaults.go
	pkg/util/sysutils/sysutils.go
This commit is contained in:
Qiu Jian
2019-02-15 22:46:01 +08:00
864 changed files with 86781 additions and 3791 deletions
Generated
+60 -8
View File
@@ -94,6 +94,14 @@
revision = "879f631812a30a580659e8035e7cda9994bb99ac"
version = "v1.20.0"
[[projects]]
digest = "1:e92f5581902c345eb4ceffdcd4a854fb8f73cf436d47d837d1ec98ef1fe0a214"
name = "github.com/StackExchange/wmi"
packages = ["."]
pruneopts = "UT"
revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338"
version = "1.0.0"
[[projects]]
digest = "1:8c3176921eb3a8c3b9cadd2bbb2e8e39f62a99ee2e0965074a857868cc18a10b"
name = "github.com/aliyun/alibaba-cloud-sdk-go"
@@ -533,6 +541,17 @@
revision = "07c9b44f60d7ffdfb7d8efe1ad539965737836dc"
version = "v0.4.0"
[[projects]]
digest = "1:c96d16a4451e48e2c44b2c3531fd8ec9248d822637f1911a88959ca0bcae4a64"
name = "github.com/go-ole/go-ole"
packages = [
".",
"oleutil",
]
pruneopts = "UT"
revision = "39dc8486bd0952279431257138bc428275b86797"
version = "v1.2.2"
[[projects]]
digest = "1:ec6f9bf5e274c833c911923c9193867f3f18788c461f76f05f62bb1510e0ae65"
name = "github.com/go-sql-driver/mysql"
@@ -1117,6 +1136,29 @@
pruneopts = "UT"
revision = "49a4782e9908fe098c907022a1bd7519c79803d6"
[[projects]]
digest = "1:946db283b5540b64c60e7517b5afa54dc148b4672d69b38423a1c8d7fe24f3d1"
name = "github.com/shirou/gopsutil"
packages = [
"cpu",
"host",
"internal/common",
"mem",
"net",
"process",
]
pruneopts = "UT"
revision = "3ec50d2876a36047b2ca39f955ba88fb7a455e92"
version = "v2.18.10"
[[projects]]
branch = "master"
digest = "1:99c6a6dab47067c9b898e8c8b13d130c6ab4ffbcc4b7cc6236c2cd0b1e344f5b"
name = "github.com/shirou/w32"
packages = ["."]
pruneopts = "UT"
revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b"
[[projects]]
digest = "1:69b1cc331fca23d702bd72f860c6a647afd0aa9fcbc1d0659b1365e26546dd70"
name = "github.com/sirupsen/logrus"
@@ -1615,11 +1657,11 @@
[[projects]]
branch = "master"
digest = "1:9e1972be6a76a8ef37aebcf94dedbe0508274464b5091baff4a8780583affebb"
digest = "1:9a92b34083e218c123d21a4b0d74228df6b186de9dcd6489e5b4cf68f4e90104"
name = "yunion.io/x/jsonutils"
packages = ["."]
pruneopts = "UT"
revision = "c191216583f759c3d54a1d1d84c9832fdee68596"
revision = "c40afc81cccc4618168f722c64deba7cfbb1f8bd"
[[projects]]
branch = "master"
@@ -1634,7 +1676,7 @@
[[projects]]
branch = "master"
digest = "1:c6edf85623ec3a8c5e688dafbdc5bbde1d6057b9d0d9b78e6cb6dd84a52b23c0"
digest = "1:6277045f9c9a63d84e1aa48c7ad3c1f42ea6523bc54ea92e772d541c78e37404"
name = "yunion.io/x/pkg"
packages = [
"gotypes",
@@ -1657,6 +1699,7 @@
"util/seclib",
"util/secrules",
"util/sets",
"util/signalutils",
"util/stringutils",
"util/timeutils",
"util/trace",
@@ -1667,23 +1710,23 @@
"utils",
]
pruneopts = "UT"
revision = "9075712a1c3ff8c0bad1c413e18f0b7f2c18f29c"
revision = "e2e42205d868c26829b9322b40cdc48f24fed379"
[[projects]]
branch = "master"
digest = "1:82629c6aeecc1e6e3ea7108385aaac2bdcceeba8f8dc3175daca256602e3be71"
digest = "1:0e3c8da76b7b7ba0f67ef0737d361e117b65bd328bfbcb28fb030268df587ca9"
name = "yunion.io/x/sqlchemy"
packages = ["."]
pruneopts = "UT"
revision = "c2445071ddf7b3f2a22ccdd3be684b67ac2f9008"
revision = "0b1ca973f3e5140dee7e773fc51cfc8c07ce0f69"
[[projects]]
branch = "master"
digest = "1:6ea9ac8f317e7dd79309bb1633642bc9d1822011c5175bd241552534d216ac6d"
digest = "1:cec4892868ae1c9fec0d2cace79d4c43e620345cc3c070d004dec09caf197208"
name = "yunion.io/x/structarg"
packages = ["."]
pruneopts = "UT"
revision = "e4f0f5201b4acad185ebfb76e65905fe5ae7c1b1"
revision = "f39549642b91f03f0b46403f9856d4750e3e59eb"
[solve-meta]
analyzer-name = "dep"
@@ -1759,6 +1802,11 @@
"github.com/nelsonken/cos-go-sdk-v5/cos",
"github.com/pierrec/lz4",
"github.com/serialx/hashring",
"github.com/shirou/gopsutil/cpu",
"github.com/shirou/gopsutil/host",
"github.com/shirou/gopsutil/mem",
"github.com/shirou/gopsutil/net",
"github.com/shirou/gopsutil/process",
"github.com/stretchr/testify/assert",
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common",
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http",
@@ -1774,6 +1822,9 @@
"github.com/vmware/govmomi/vim25/types",
"go.etcd.io/etcd/clientv3",
"golang.org/x/crypto/ssh",
"golang.org/x/net/bpf",
"golang.org/x/net/ipv4",
"golang.org/x/sys/unix",
"gopkg.in/gin-gonic/gin.v1",
"k8s.io/api/core/v1",
"k8s.io/apimachinery/pkg/api/errors",
@@ -1804,6 +1855,7 @@
"yunion.io/x/pkg/util/seclib",
"yunion.io/x/pkg/util/secrules",
"yunion.io/x/pkg/util/sets",
"yunion.io/x/pkg/util/signalutils",
"yunion.io/x/pkg/util/stringutils",
"yunion.io/x/pkg/util/timeutils",
"yunion.io/x/pkg/util/trace",
+4
View File
@@ -195,3 +195,7 @@
[prune]
go-tests = true
unused-packages = true
[[constraint]]
name = "github.com/shirou/gopsutil"
version = "2.18.10"
+3 -1
View File
@@ -128,7 +128,9 @@ getent passwd %{owner} >/dev/null || /usr/sbin/useradd -r -s /sbin/nologin -d %{
$(for b in $EXTRA_BINS; do echo /opt/yunion/bin/$b; done)
" > $SPEC_FILE
find $ROOT/root/ -type f | sed -e "s:$ROOT/root::g" >> $SPEC_FILE
if [ -d $ROOT/root/ ]; then
find $ROOT/root/ -type f | sed -e "s:$ROOT/root::g" >> $SPEC_FILE
fi
rpmbuild --define "_topdir $BUILDROOT" -bb $SPEC_FILE
@@ -0,0 +1,9 @@
region = 'LocalTest'
port = 9999
auth_uri = 'http://10.16.22.51:35357/v2.0'
admin_user = 'logadmin'
admin_password = 'password0'
admin_tenant_name = 'system'
sql_connection = 'mysql+pymysql://yunionlogger:password0@10.16.22.51:3306/yunionlogger?charset=utf8'
auto_sync_table = True
@@ -0,0 +1,17 @@
[Unit]
Description=Yunion Cloud Log Service
Documentation=http://doc.yunionyun.com
After=network.target httpd.service
[Service]
Type=simple
User=yunion
Group=yunion
ExecStart=/opt/yunion/bin/logger --config /etc/yunion/log.conf
WorkingDirectory=/opt/yunion
KillMode=process
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
+1
View File
@@ -0,0 +1 @@
DESCRIPTION="Yunion Cloud Log Service"
@@ -0,0 +1 @@
您的云主机{{ .name }}的套餐类型已经更改为 CPU:{{ index .flavor_info "cpu" }}核,内存:{{ index .flavor_info "memory" }}M,数据盘:{{ index .flavor_info "datadisk" }}G,带宽: {{ index .flavor_info "ebw" }}M。
@@ -0,0 +1 @@
您的云主机{{ .name }}已经创建成功,服务器IP地址为{{ .ips }}{{ if .account }}初始帐号为{{ .account }}{{ end }}{{ if .keypair }}访问ssh密钥为{{ .keypair }}{{ end }}{{ if .password }}初始密码为{{ .password }}{{ end }}请使用{{ if .windows }}远程桌面连接器(RDC){{ else }}SSH{{ end }}或控制面板控制台访问云主机。
@@ -0,0 +1 @@
用户{{ .tenant }}的云主机{{ .name }}已经创建成功。
@@ -0,0 +1 @@
您的云主机{{ .name }}已经删除。
@@ -0,0 +1 @@
用户{{ .tenant }}的云主机{{ .name }}已经删除。
@@ -0,0 +1 @@
您的云主机{{ .name }}的系统盘已经重置成功,{{ if .account }}初始帐号为{{ .account }}{{ end }}{{ if .keypair }}访问密钥为{{ .keypair }}{{ end }}{{ if .password }}初始密码为{{ .password }}{{ end }}请使用{{ if .windows }}远程桌面连接器(RDC){{ else }}SSH{{ end }}或控制面板控制台访问云主机。
@@ -0,0 +1 @@
系统错误消息:{{ .msg }}({{ .created }})
@@ -0,0 +1 @@
系统警告消息:{{ .msg }}({{ .created }})
@@ -0,0 +1 @@
您的云主机{{ .name }}的套餐类型已经更改为 CPU:{{ index .flavor_info "cpu" }}核,内存:{{ index .flavor_info "memory" }}M,数据盘:{{ index .flavor_info "datadisk" }}G,带宽: {{ index .flavor_info "ebw" }}M。
@@ -0,0 +1 @@
云主机{{ .Name }}创建成功
@@ -0,0 +1 @@
用户{{ .tenant }}的云主机{{ .name }}已经创建成功
@@ -0,0 +1 @@
云主机{{ .name }}删除通知
@@ -0,0 +1 @@
用户{{ .tenant }}的云主机{{ .name }}已经删除
@@ -0,0 +1 @@
云主机{{ .name }}的系统盘重置成功
@@ -0,0 +1 @@
系统错误消息:{{ .msg }}({{ .created }})
@@ -0,0 +1 @@
系统警告消息:{{ .msg }}({{ .created }})
+2 -1
View File
@@ -13,6 +13,7 @@ import (
)
type BaseOptions struct {
Debug bool `help:"debug mode"`
Help bool `help:"Show help"`
AccessKey string `help:"Access key" default:"$ALIYUN_ACCESS_KEY"`
Secret string `help:"Secret" default:"$ALIYUN_SECRET"`
@@ -69,7 +70,7 @@ func newClient(options *BaseOptions) (*aliyun.SRegion, error) {
return nil, fmt.Errorf("Missing secret")
}
cli, err := aliyun.NewAliyunClient("", "", options.AccessKey, options.Secret)
cli, err := aliyun.NewAliyunClient("", "", options.AccessKey, options.Secret, options.Debug)
if err != nil {
return nil, err
}
+9
View File
@@ -0,0 +1,9 @@
package main
import (
"yunion.io/x/onecloud/pkg/baremetal/service"
)
func main() {
service.New().StartService()
}
+4
View File
@@ -16,6 +16,7 @@ type BaseActionListOptions struct {
Descending bool `help:"Descending order"`
Action []string `help:"Log action"`
Search string `help:"Filter action logs by obj_name, using 'like' syntax."`
Admin bool `help:"admin mode"`
}
type ActionListOptions struct {
@@ -60,6 +61,9 @@ func doActionList(s *mcclient.ClientSession, args *ActionListOptions) error {
if len(args.Action) > 0 {
params.Add(jsonutils.NewStringArray(args.Action), "action")
}
if args.Admin {
params.Add(jsonutils.JSONTrue, "admin")
}
logs, err := modules.Actions.List(s, params)
if err != nil {
return err
+1 -1
View File
@@ -346,7 +346,7 @@ func init() {
params := jsonutils.NewDict()
params.Add(jsonutils.NewInt(args.ENABLED), "enabled")
_, err := modules.AlarmTemplateAlarms.Update(s, args.ALARMTEMPLATE_ID, args.ALARM_ID, params)
_, err := modules.AlarmTemplateAlarms.Update(s, args.ALARMTEMPLATE_ID, args.ALARM_ID, nil, params)
if err != nil {
return err
}
+13 -15
View File
@@ -1,7 +1,6 @@
package shell
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
@@ -10,16 +9,15 @@ import (
func init() {
type CachedImageListOptions struct {
options.BaseListOptions
ImageType string `help:"image type" choices:"system|customized|shared|market"`
Region string `help:"show images cached at cloud region"`
Zone string `help:"show images cached at zone"`
}
R(&CachedImageListOptions{}, "cached-image-list", "List cached images", func(s *mcclient.ClientSession, args *CachedImageListOptions) error {
var params *jsonutils.JSONDict
{
var err error
params, err = args.BaseListOptions.Params()
if err != nil {
return err
}
params, err := options.ListStructToParams(args)
if err != nil {
return err
}
result, err := modules.Cachedimages.List(s, params)
if err != nil {
@@ -50,12 +48,12 @@ func init() {
return nil
})
R(&CachedImageShowOptions{}, "cached-image-delete", "Remove cached image information", func(s *mcclient.ClientSession, args *CachedImageShowOptions) error {
result, err := modules.Cachedimages.Delete(s, args.ID, nil)
if err != nil {
return err
}
printObject(result)
type CachedImageDeleteOptions struct {
ID []string `help:"ID or Name of the cached image to show"`
}
R(&CachedImageDeleteOptions{}, "cached-image-delete", "Remove cached image information", func(s *mcclient.ClientSession, args *CachedImageDeleteOptions) error {
results := modules.Cachedimages.BatchDelete(s, args.ID, nil)
printBatchResults(results, modules.Cachedimages.GetColumns(s))
return nil
})
}
+57 -19
View File
@@ -1,6 +1,8 @@
package shell
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
@@ -31,13 +33,25 @@ func init() {
})
type CloudaccountCreateOptions struct {
NAME string `help:"Name of cloud account"`
ACCOUNT string `help:"Account to access the cloud account"`
SECRET string `help:"Secret to access the cloud account, clientId/clientScret for Azure"`
PROVIDER string `help:"Driver for cloud account" choices:"VMware|Aliyun|Azure|Qcloud|OpenStack|Huawei"`
AccessURL string `helo:"hello" metavar:"Azure choices: <AzureGermanCloud、AzureChinaCloud、AzureUSGovernmentCloud、AzurePublicCloud>"`
Desc string `help:"Description"`
Enabled bool `help:"Enabled the account automatically"`
NAME string `help:"Name of cloud account"`
AccessKeyID string `help:"Aiyun|HuaWei|Aws access_key_id"`
AccessKeySecret string `help:"Aiyun|HuaWei|Aws access_key_secret"`
AppID string `help:"Qcloud appid"`
SecretID string `help:"Qcloud secret_id"`
SecretKey string `help:"Qcloud secret_key"`
ProjectName string `help:"OpenStack project_name"`
Username string `help:"OpenStack|VMware username"`
Password string `help:"OpenStack|VMware password"`
AuthURL string `help:"OpenStack auth_url"`
Host string `help:"VMware host"`
Port string `help:"VMware host port" default:"443"`
DirectoryID string `help:"Azure directory_id"`
ClientID string `help:"Azure client_id"`
ClientSecret string `help:"Azure clinet_secret"`
Environment string `help:"Azure|Huawei|Aws environment" choices:"AzureGermanCloud|AzureChinaCloud|AzureUSGovernmentCloud|AzurePublicCloud|InternationalCloud|ChinaCloud|"`
PROVIDER string `help:"Driver for cloud account" choices:"VMware|Aliyun|Azure|Qcloud|OpenStack|Huawei|Aws"`
Desc string `help:"Description"`
Enabled bool `help:"Enabled the account automatically"`
Import bool `help:"Import all sub account automatically"`
AutoSync bool `help:"Enabled the account automatically"`
@@ -46,9 +60,31 @@ func init() {
R(&CloudaccountCreateOptions{}, "cloud-account-create", "Create a cloud account", func(s *mcclient.ClientSession, args *CloudaccountCreateOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.NAME), "name")
params.Add(jsonutils.NewString(args.ACCOUNT), "account")
params.Add(jsonutils.NewString(args.SECRET), "secret")
params.Add(jsonutils.NewString(args.PROVIDER), "provider")
data := jsonutils.Marshal(args)
requireParamsMap := map[string][]string{
"VMware": {"username", "password", "host", "port"},
"Aliyun": {"access_key_id", "access_key_secret"},
"Azure": {"directory_id", "client_id", "client_secret", "environment"},
"Qcloud": {"app_id", "secret_id", "secret_key"},
"OpenStack": {"project_name", "username", "password", "auth_url"},
"Huawei": {"access_key_id", "access_key_secret", "environment"},
"Aws": {"access_key_id", "access_key_secret", "environment"},
}
requireParams, ok := requireParamsMap[args.PROVIDER]
if !ok {
return fmt.Errorf("Unsupport provider %s", args.PROVIDER)
}
for _, key := range requireParams {
v, _ := data.GetString(key)
if len(v) == 0 {
return fmt.Errorf("Missing %s", key)
}
params.Add(jsonutils.NewString(v), key)
}
if args.Enabled {
params.Add(jsonutils.JSONTrue, "enabled")
@@ -64,9 +100,6 @@ func init() {
}
}
if len(args.AccessURL) > 0 {
params.Add(jsonutils.NewString(args.AccessURL), "access_url")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
}
@@ -182,15 +215,20 @@ func init() {
})
type CloudaccountUpdateCredentialOptions struct {
ID string `help:"ID or Name of cloud account"`
ACCOUNT string `help:"new account"`
SECRET string `help:"new secret"`
ID string `help:"ID or Name of cloud account"`
AccessKeyID string `help:"Aiyun|HuaWei|Aws access_key_id"`
AccessKeySecret string `help:"Aiyun|HuaWei|Aws access_key_secret"`
AppID string `help:"Qcloud appid"`
SecretID string `help:"Qcloud secret_id"`
SecretKey string `help:"Qcloud secret_key"`
ProjectName string `help:"OpenStack project_name"`
Username string `help:"OpenStack|VMware username"`
Password string `help:"OpenStack|VMware password"`
ClientID string `help:"Azure tenant_id"`
ClientSecret string `help:"Azure clinet_secret"`
}
R(&CloudaccountUpdateCredentialOptions{}, "cloud-account-update-credential", "Update credential of a cloud account", func(s *mcclient.ClientSession, args *CloudaccountUpdateCredentialOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.ACCOUNT), "account")
params.Add(jsonutils.NewString(args.SECRET), "secret")
params := jsonutils.Marshal(args)
result, err := modules.Cloudaccounts.PerformAction(s, args.ID, "update-credential", params)
if err != nil {
return err
+69 -20
View File
@@ -15,6 +15,8 @@ func init() {
Public *bool `help:"show public cloud regions only" json:"is_public"`
Usable *bool `help:"List regions where networks are usable"`
UsableVpc *bool `help:"List regions where VPC are usable"`
City string `help:"List regions in the specified city"`
}
R(&CloudregionListOptions{}, "cloud-region-list", "List cloud regions", func(s *mcclient.ClientSession, opts *CloudregionListOptions) error {
params, err := options.ListStructToParams(opts)
@@ -29,20 +31,66 @@ func init() {
return nil
})
type CloudregionCityListOptions struct {
Manager string `help:"List objects belonging to the cloud provider"`
Account string `help:"List objects belonging to the cloud account"`
Provider string `help:"List objects from the provider" choices:"VMware|Aliyun|Qcloud|Azure|Aws|Huawei|Openstack"`
Private *bool `help:"show private cloud regions only" json:"is_private"`
Public *bool `help:"show public cloud regions only" json:"is_public"`
Usable *bool `help:"List regions where networks are usable"`
UsableVpc *bool `help:"List regions where VPC are usable"`
}
R(&CloudregionCityListOptions{}, "cloud-region-cities", "List cities where cloud region resides", func(s *mcclient.ClientSession, args *CloudregionCityListOptions) error {
params, err := options.StructToParams(args)
if err != nil {
return err
}
results, err := modules.Cloudregions.GetRegionCities(s, params)
if err != nil {
return err
}
listResult := modules.ListResult{}
listResult.Data, err = results.GetArray()
if err != nil {
return err
}
printList(&listResult, nil)
return nil
})
R(&CloudregionCityListOptions{}, "cloud-region-providers", "List cities where cloud region resides", func(s *mcclient.ClientSession, args *CloudregionCityListOptions) error {
params, err := options.StructToParams(args)
if err != nil {
return err
}
results, err := modules.Cloudregions.GetRegionProviders(s, params)
if err != nil {
return err
}
listResult := modules.ListResult{}
listResult.Data, err = results.GetArray()
if err != nil {
return err
}
printList(&listResult, nil)
return nil
})
type CloudregionCreateOptions struct {
Id string `help:"ID of the region"`
NAME string `help:"Name of the region"`
Provider string `help:"Cloud provider"`
Desc string `help:"Description"`
Id string `help:"ID of the region"`
NAME string `help:"Name of the region"`
Provider string `help:"Cloud provider"`
Desc string `help:"Description" json:"description" token:"desc"`
Latitude float32 `help:"region geographical location - latitude"`
Longitude float32 `help:"region geographical location - longitude"`
City string `help:"region geograpical location - city, e.g. Beijing, Frankfurt"`
CountryCode string `help:"region geographical location - ISO country code, e.g. CN"`
}
R(&CloudregionCreateOptions{}, "cloud-region-create", "Create a cloud region", func(s *mcclient.ClientSession, args *CloudregionCreateOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.NAME), "name")
if len(args.Id) > 0 {
params.Add(jsonutils.NewString(args.Id), "id")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
params, err := options.StructToParams(args)
if err != nil {
return err
}
results, err := modules.Cloudregions.Create(s, params)
if err != nil {
@@ -74,17 +122,18 @@ func init() {
})
type CloudregionUpdateOptions struct {
ID string `help:"ID or name of the region"`
Name string `help:"New name of the region"`
Desc string `help:"Description of the region"`
ID string `help:"ID or name of the region to update" json:"-"`
Name string `help:"New name of the region"`
Desc string `help:"Description of the region" json:"description" token:"desc"`
Latitude float32 `help:"region geographical location - latitude"`
Longitude float32 `help:"region geographical location - longitude"`
City string `help:"region geograpical location - city, e.g. Beijing, Frankfurt"`
CountryCode string `help:"region geographical location - ISO country code, e.g. CN"`
}
R(&CloudregionUpdateOptions{}, "cloud-region-update", "Update a cloud region", func(s *mcclient.ClientSession, args *CloudregionUpdateOptions) error {
params := jsonutils.NewDict()
if len(args.Name) > 0 {
params.Add(jsonutils.NewString(args.Name), "name")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
params, err := options.StructToParams(args)
if err != nil {
return err
}
results, err := modules.Cloudregions.Update(s, args.ID, params)
if err != nil {
+52 -7
View File
@@ -11,13 +11,13 @@ import (
func init() {
type DiskListOptions struct {
options.BaseListOptions
Unused *bool `help:"Show unused disks"`
Share *bool `help:"Show Share storage disks"`
Local *bool `help:"Show Local storage disks"`
Guest string `help:"Guest ID or name"`
Storage string `help:"Storage ID or name"`
Type string `help:"Disk type" choices:"sys|data|swap|volume"`
Unused *bool `help:"Show unused disks"`
Share *bool `help:"Show Share storage disks"`
Local *bool `help:"Show Local storage disks"`
Guest string `help:"Guest ID or name"`
Storage string `help:"Storage ID or name"`
Type string `help:"Disk type" choices:"sys|data|swap|volume"`
CloudType string `help:"Public cloud or private cloud" choices:"Public|Private"`
BillingType string `help:"billing type" choices:"postpaid|prepaid"`
}
@@ -26,6 +26,14 @@ func init() {
if err != nil {
return err
}
if len(opts.CloudType) > 0 {
if opts.CloudType == "Public" {
params.Add(jsonutils.JSONTrue, "public_cloud")
} else if opts.CloudType == "Private" {
params.Add(jsonutils.JSONTrue, "private_cloud")
}
}
result, err := modules.Disks.List(s, params)
if err != nil {
return err
@@ -230,4 +238,41 @@ func init() {
printObject(disk)
return nil
})
type DiskSaveOptions struct {
ID string `help:"ID or name of the disk" json:"-"`
NAME string `help:"Image name"`
OSTYPE string `help:"Os type" choices:"Linux|Windows|VMware" json:"-"`
Public *bool `help:"Make the image public available" json:"is_public"`
Format string `help:"image format" choices:"vmdk|qcow2"`
Notes string `help:"Notes about the image"`
}
R(&DiskSaveOptions{}, "disk-save", "Disk save image", func(s *mcclient.ClientSession, args *DiskSaveOptions) error {
params, err := options.StructToParams(args)
if err != nil {
return err
}
params.Add(jsonutils.NewString(args.OSTYPE), "properties", "os_type")
disk, err := modules.Disks.PerformAction(s, args.ID, "save", params)
if err != nil {
return err
}
printObject(disk)
return nil
})
type DiskUpdateStatusOptions struct {
ID string `help:"ID or name of disk"`
STATUS string `help:"Disk status" choices:"ready"`
}
R(&DiskUpdateStatusOptions{}, "disk-update-status", "Set disk status", func(s *mcclient.ClientSession, args *DiskUpdateStatusOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.STATUS), "status")
disk, err := modules.Disks.PerformAction(s, args.ID, "status", params)
if err != nil {
return err
}
printObject(disk)
return nil
})
}
+20 -4
View File
@@ -71,6 +71,9 @@ func init() {
ID string `help:"ID or name of EIP"`
Name string `help:"New name of EIP"`
Desc string `help:"New description of EIP"`
EnableAutoDellocate bool `help:"enable automatically dellocate when dissociate from instance"`
DisableAutoDellocate bool `help:"disable automatically dellocate when dissociate from instance"`
}
R(&EipUpdateOptions{}, "eip-update", "Update EIP properties", func(s *mcclient.ClientSession, args *EipUpdateOptions) error {
params := jsonutils.NewDict()
@@ -80,6 +83,11 @@ func init() {
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
}
if args.EnableAutoDellocate {
params.Add(jsonutils.JSONTrue, "auto_dellocate")
} else if args.DisableAutoDellocate {
params.Add(jsonutils.JSONFalse, "auto_dellocate")
}
result, err := modules.Elasticips.Update(s, args.ID, params)
if err != nil {
return err
@@ -105,11 +113,16 @@ func init() {
return nil
})
type EipSingleOptions struct {
ID string `help:"ID or name of EIP"`
type EipDissociateOptions struct {
ID string `help:"ID or name of EIP"`
AutoDelete bool `help:"automatically delete the dissociate EIP" json:"auto_delete,omitfalse"`
}
R(&EipSingleOptions{}, "eip-dissociate", "Dissociate an EIP from an instance", func(s *mcclient.ClientSession, args *EipSingleOptions) error {
result, err := modules.Elasticips.PerformAction(s, args.ID, "dissociate", nil)
R(&EipDissociateOptions{}, "eip-dissociate", "Dissociate an EIP from an instance", func(s *mcclient.ClientSession, args *EipDissociateOptions) error {
params, err := options.StructToParams(args)
if err != nil {
return err
}
result, err := modules.Elasticips.PerformAction(s, args.ID, "dissociate", params)
if err != nil {
return err
}
@@ -117,6 +130,9 @@ func init() {
return nil
})
type EipSingleOptions struct {
ID string `help:"ID or name of EIP"`
}
R(&EipSingleOptions{}, "eip-sync", "Synchronize status of an EIP", func(s *mcclient.ClientSession, args *EipSingleOptions) error {
result, err := modules.Elasticips.PerformAction(s, args.ID, "sync", nil)
if err != nil {
+1 -1
View File
@@ -50,7 +50,7 @@ func init() {
if params.Size() == 0 {
return InvalidUpdateError()
}
result, err := modules.Hostcachedimages.Update(s, args.HOST, args.IMAGE, params)
result, err := modules.Hostcachedimages.Update(s, args.HOST, args.IMAGE, nil, params)
if err != nil {
return err
}
+1 -1
View File
@@ -56,7 +56,7 @@ func init() {
})
R(&HostStorageDetailOptions{}, "host-storage-detach", "Detach a storage from a host", func(s *mcclient.ClientSession, args *HostStorageDetailOptions) error {
result, err := modules.Hoststorages.Detach(s, args.HOST, args.STORAGE)
result, err := modules.Hoststorages.Detach(s, args.HOST, args.STORAGE, nil)
if err != nil {
return err
}
+2 -2
View File
@@ -78,7 +78,7 @@ func init() {
params.Add(jsonutils.JSONFalse, "is_master")
}
}
result, err := modules.Hostwires.Update(s, args.HOST, args.WIRE, params)
result, err := modules.Hostwires.Update(s, args.HOST, args.WIRE, nil, params)
if err != nil {
return err
}
@@ -91,7 +91,7 @@ func init() {
WIRE string `help:"ID or Name of Wire"`
}
R(&HostWireDetachOptions{}, "host-wire-detach", "Detach host from wire", func(s *mcclient.ClientSession, args *HostWireDetachOptions) error {
result, err := modules.Hostwires.Detach(s, args.HOST, args.WIRE)
result, err := modules.Hostwires.Detach(s, args.HOST, args.WIRE, nil)
if err != nil {
return err
}
+8 -7
View File
@@ -251,16 +251,17 @@ func init() {
ID string `help:"Image ID or name"`
}
R(&ImageDetailOptions{}, "image-delete", "Delete a image", func(s *mcclient.ClientSession, args *ImageDetailOptions) error {
imgID, err := modules.Images.GetId(s, args.ID, nil)
type ImageDeleteOptions struct {
ID []string `help:"Image ID or name"`
OverridePendingDelete *bool `help:"Delete image directly instead of pending delete"`
}
R(&ImageDeleteOptions{}, "image-delete", "Delete a image", func(s *mcclient.ClientSession, args *ImageDeleteOptions) error {
params, err := options.StructToParams(args)
if err != nil {
return err
}
if result, err := modules.Images.Delete(s, imgID, nil); err != nil {
return err
} else {
printObject(result)
}
ret := modules.Images.BatchDeleteWithParam(s, args.ID, params, nil)
printBatchResults(ret, modules.Images.GetColumns(s))
return nil
})
+8
View File
@@ -93,6 +93,14 @@ func init() {
printLbAcl(lbacl)
return nil
})
R(&options.LoadbalancerAclDeleteOptions{}, "lbacl-purge", "Purge lbacl", func(s *mcclient.ClientSession, opts *options.LoadbalancerAclDeleteOptions) error {
lbacl, err := modules.LoadbalancerAcls.PerformAction(s, opts.ID, "purge", nil)
if err != nil {
return err
}
printLbAcl(lbacl)
return nil
})
R(&options.LoadbalancerAclDeleteOptions{}, "lbacl-delete", "Show lbacl", func(s *mcclient.ClientSession, opts *options.LoadbalancerAclDeleteOptions) error {
lbacl, err := modules.LoadbalancerAcls.Delete(s, opts.ID, nil)
if err != nil {
+10 -2
View File
@@ -8,7 +8,7 @@ import (
func init() {
R(&options.LoadbalancerBackendGroupCreateOptions{}, "lbbackendgroup-create", "Create lbbackendgroup", func(s *mcclient.ClientSession, opts *options.LoadbalancerBackendGroupCreateOptions) error {
params, err := options.StructToParams(opts)
params, err := opts.Params()
if err != nil {
return err
}
@@ -51,7 +51,7 @@ func init() {
printObject(lbbackendgroup)
return nil
})
R(&options.LoadbalancerBackendGroupDeleteOptions{}, "lbbackendgroup-delete", "Show lbbackendgroup", func(s *mcclient.ClientSession, opts *options.LoadbalancerBackendGroupDeleteOptions) error {
R(&options.LoadbalancerBackendGroupDeleteOptions{}, "lbbackendgroup-delete", "Delete lbbackendgroup", func(s *mcclient.ClientSession, opts *options.LoadbalancerBackendGroupDeleteOptions) error {
lbbackendgroup, err := modules.LoadbalancerBackendGroups.Delete(s, opts.ID, nil)
if err != nil {
return err
@@ -59,4 +59,12 @@ func init() {
printObject(lbbackendgroup)
return nil
})
R(&options.LoadbalancerBackendGroupDeleteOptions{}, "lbbackendgroup-purge", "Purge lbbackendgroup", func(s *mcclient.ClientSession, opts *options.LoadbalancerBackendGroupDeleteOptions) error {
lbbackendgroup, err := modules.LoadbalancerBackendGroups.PerformAction(s, opts.ID, "purge", nil)
if err != nil {
return err
}
printObject(lbbackendgroup)
return nil
})
}
+9 -1
View File
@@ -48,7 +48,7 @@ func init() {
printObject(lbbackend)
return nil
})
R(&options.LoadbalancerBackendDeleteOptions{}, "lbbackend-delete", "Show lbbackend", func(s *mcclient.ClientSession, opts *options.LoadbalancerBackendDeleteOptions) error {
R(&options.LoadbalancerBackendDeleteOptions{}, "lbbackend-delete", "Delete lbbackend", func(s *mcclient.ClientSession, opts *options.LoadbalancerBackendDeleteOptions) error {
lbbackend, err := modules.LoadbalancerBackends.Delete(s, opts.ID, nil)
if err != nil {
return err
@@ -56,4 +56,12 @@ func init() {
printObject(lbbackend)
return nil
})
R(&options.LoadbalancerBackendDeleteOptions{}, "lbbackend-purge", "Purge lbbackend", func(s *mcclient.ClientSession, opts *options.LoadbalancerBackendDeleteOptions) error {
lbbackend, err := modules.LoadbalancerBackends.PerformAction(s, opts.ID, "purge", nil)
if err != nil {
return err
}
printObject(lbbackend)
return nil
})
}
+9 -1
View File
@@ -51,7 +51,7 @@ func init() {
printObject(lbcert)
return nil
})
R(&options.LoadbalancerCertificateDeleteOptions{}, "lbcert-delete", "Show lbcert", func(s *mcclient.ClientSession, opts *options.LoadbalancerCertificateDeleteOptions) error {
R(&options.LoadbalancerCertificateDeleteOptions{}, "lbcert-delete", "Delete lbcert", func(s *mcclient.ClientSession, opts *options.LoadbalancerCertificateDeleteOptions) error {
lbcert, err := modules.LoadbalancerCertificates.Delete(s, opts.ID, nil)
if err != nil {
return err
@@ -59,4 +59,12 @@ func init() {
printObject(lbcert)
return nil
})
R(&options.LoadbalancerCertificateDeleteOptions{}, "lbcert-purge", "Purge lbcert", func(s *mcclient.ClientSession, opts *options.LoadbalancerCertificateDeleteOptions) error {
lbcert, err := modules.LoadbalancerCertificates.PerformAction(s, opts.ID, "purge", nil)
if err != nil {
return err
}
printObject(lbcert)
return nil
})
}
+9 -1
View File
@@ -46,7 +46,7 @@ func init() {
printObject(lblistenerrule)
return nil
})
R(&options.LoadbalancerListenerRuleDeleteOptions{}, "lblistenerrule-delete", "Show lblistenerrule", func(s *mcclient.ClientSession, opts *options.LoadbalancerListenerRuleDeleteOptions) error {
R(&options.LoadbalancerListenerRuleDeleteOptions{}, "lblistenerrule-delete", "Delete lblistenerrule", func(s *mcclient.ClientSession, opts *options.LoadbalancerListenerRuleDeleteOptions) error {
lblistenerrule, err := modules.LoadbalancerListenerRules.Delete(s, opts.ID, nil)
if err != nil {
return err
@@ -54,6 +54,14 @@ func init() {
printObject(lblistenerrule)
return nil
})
R(&options.LoadbalancerListenerRuleDeleteOptions{}, "lblistenerrule-purge", "Purge lblistenerrule", func(s *mcclient.ClientSession, opts *options.LoadbalancerListenerRuleDeleteOptions) error {
lblistenerrule, err := modules.LoadbalancerListenerRules.PerformAction(s, opts.ID, "purge", nil)
if err != nil {
return err
}
printObject(lblistenerrule)
return nil
})
R(&options.LoadbalancerListenerRuleActionStatusOptions{}, "lblistenerrule-status", "Change lblistenerrule status", func(s *mcclient.ClientSession, opts *options.LoadbalancerListenerRuleActionStatusOptions) error {
params, err := options.StructToParams(opts)
if err != nil {
+17 -1
View File
@@ -48,7 +48,7 @@ func init() {
printObject(lblistener)
return nil
})
R(&options.LoadbalancerListenerDeleteOptions{}, "lblistener-delete", "Show lblistener", func(s *mcclient.ClientSession, opts *options.LoadbalancerListenerDeleteOptions) error {
R(&options.LoadbalancerListenerDeleteOptions{}, "lblistener-delete", "Delete lblistener", func(s *mcclient.ClientSession, opts *options.LoadbalancerListenerDeleteOptions) error {
lblistener, err := modules.LoadbalancerListeners.Delete(s, opts.ID, nil)
if err != nil {
return err
@@ -56,6 +56,14 @@ func init() {
printObject(lblistener)
return nil
})
R(&options.LoadbalancerListenerDeleteOptions{}, "lblistener-purge", "Purge lblistener", func(s *mcclient.ClientSession, opts *options.LoadbalancerListenerDeleteOptions) error {
lblistener, err := modules.LoadbalancerListeners.PerformAction(s, opts.ID, "purge", nil)
if err != nil {
return err
}
printObject(lblistener)
return nil
})
R(&options.LoadbalancerListenerActionStatusOptions{}, "lblistener-status", "Change lblistener status", func(s *mcclient.ClientSession, opts *options.LoadbalancerListenerActionStatusOptions) error {
params, err := options.StructToParams(opts)
if err != nil {
@@ -68,4 +76,12 @@ func init() {
printObject(lblistener)
return nil
})
R(&options.LoadbalancerListenerActionSyncStatusOptions{}, "lblistener-syncstatus", "Sync lblistener status", func(s *mcclient.ClientSession, opts *options.LoadbalancerListenerActionSyncStatusOptions) error {
lblistener, err := modules.LoadbalancerListeners.PerformAction(s, opts.ID, "syncstatus", nil)
if err != nil {
return err
}
printObject(lblistener)
return nil
})
}
+46
View File
@@ -0,0 +1,46 @@
package shell
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
func init() {
type LoadbalancerNetworkListOptions struct {
options.BaseListOptions
Loadbalancer string `help:"ID or Name of Loadbalancer"`
Network string `help:"ID or Name of network"`
Ip string `help:"search the IP address"`
}
R(&LoadbalancerNetworkListOptions{}, "loadbalancer-network-list", "List loadbalancer network pairs", func(s *mcclient.ClientSession, args *LoadbalancerNetworkListOptions) error {
var params *jsonutils.JSONDict
{
var err error
params, err = args.BaseListOptions.Params()
if err != nil {
return err
}
}
if len(args.Ip) > 0 {
params.Add(jsonutils.NewString(args.Ip), "ip_addr")
}
var result *modules.ListResult
var err error
if len(args.Loadbalancer) > 0 {
result, err = modules.Loadbalancernetworks.ListDescendent(s, args.Loadbalancer, params)
} else if len(args.Network) > 0 {
result, err = modules.Loadbalancernetworks.ListDescendent2(s, args.Network, params)
} else {
result, err = modules.Loadbalancernetworks.List(s, params)
}
if err != nil {
return err
}
printList(result, modules.Loadbalancernetworks.GetColumns(s))
return nil
})
}
+17 -1
View File
@@ -48,7 +48,7 @@ func init() {
printObject(lb)
return nil
})
R(&options.LoadbalancerDeleteOptions{}, "lb-delete", "Show lb", func(s *mcclient.ClientSession, opts *options.LoadbalancerDeleteOptions) error {
R(&options.LoadbalancerDeleteOptions{}, "lb-delete", "Delete lb", func(s *mcclient.ClientSession, opts *options.LoadbalancerDeleteOptions) error {
lb, err := modules.Loadbalancers.Delete(s, opts.ID, nil)
if err != nil {
return err
@@ -56,6 +56,14 @@ func init() {
printObject(lb)
return nil
})
R(&options.LoadbalancerPurgeOptions{}, "lb-purge", "Purge lb", func(s *mcclient.ClientSession, opts *options.LoadbalancerPurgeOptions) error {
lb, err := modules.Loadbalancers.PerformAction(s, opts.ID, "purge", nil)
if err != nil {
return err
}
printObject(lb)
return nil
})
R(&options.LoadbalancerActionStatusOptions{}, "lb-status", "Change lb status", func(s *mcclient.ClientSession, opts *options.LoadbalancerActionStatusOptions) error {
params, err := options.StructToParams(opts)
if err != nil {
@@ -68,4 +76,12 @@ func init() {
printObject(lb)
return nil
})
R(&options.LoadbalancerActionSyncStatusOptions{}, "lb-syncstatus", "Sync lb status", func(s *mcclient.ClientSession, opts *options.LoadbalancerActionSyncStatusOptions) error {
lb, err := modules.Loadbalancers.PerformAction(s, opts.ID, "syncstatus", nil)
if err != nil {
return err
}
printObject(lb)
return nil
})
}
+103
View File
@@ -0,0 +1,103 @@
package shell
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
func init() {
/**
* 创建一条报警规则
*/
type MeterAlertCreateOptions struct {
TYPE string `help:"Alert rule type" choices:"balance|resFee|monthFee"`
PROVIDER string `help:"Name of the cloud platform"`
ACCOUNT_ID string `help:"ID of the cloud platform"`
THRESHOLD float64 `help:"Threshold value of the metric"`
COMPARATOR string `help:"Comparison operator for join expressions" choices:">|<|>=|<=|=|!="`
RECIPIENTS string `help:"Comma separated recipient ID"`
LEVEL string `help:"Alert level" choices:"normal|important|fatal"`
CHANNEL string `help:"Ways to send an alarm" choices:"email|mobile"`
}
R(&MeterAlertCreateOptions{}, "meteralert-create", "Create a meter alert rule", func(s *mcclient.ClientSession, args *MeterAlertCreateOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.TYPE), "type")
params.Add(jsonutils.NewString(args.PROVIDER), "provider")
params.Add(jsonutils.NewString(args.ACCOUNT_ID), "account_id")
params.Add(jsonutils.NewFloat(args.THRESHOLD), "threshold")
params.Add(jsonutils.NewString(args.COMPARATOR), "comparator")
params.Add(jsonutils.NewString(args.RECIPIENTS), "recipients")
params.Add(jsonutils.NewString(args.LEVEL), "level")
params.Add(jsonutils.NewString(args.CHANNEL), "channel")
rst, err := modules.MeterAlert.Create(s, params)
if err != nil {
return err
}
printObject(rst)
return nil
})
/**
* 删除指定ID的报警规则
*/
type MeterAlertDeleteOptions struct {
ID string `help:"ID of alarm"`
}
R(&MeterAlertDeleteOptions{}, "meteralert-delete", "Delete a meter alert", func(s *mcclient.ClientSession, args *MeterAlertDeleteOptions) error {
alarm, e := modules.MeterAlert.Delete(s, args.ID, nil)
if e != nil {
return e
}
printObject(alarm)
return nil
})
/**
* 修改指定ID的报警规则状态
*/
type MeterAlertUpdateOptions struct {
ID string `help:"ID of the meter alert"`
STATUS string `help:"Name of the new alarm" choices:"Enabled|Disabled"`
}
R(&MeterAlertUpdateOptions{}, "meteralert-change-status", "Change status of a meter alert", func(s *mcclient.ClientSession, args *MeterAlertUpdateOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.STATUS), "status")
alarm, err := modules.MeterAlert.Patch(s, args.ID, params)
if err != nil {
return err
}
printObject(alarm)
return nil
})
/**
* 列出报警规则
*/
type MeterAlertListOptions struct {
options.BaseListOptions
}
R(&MeterAlertListOptions{}, "meteralert-list", "List meter alert", func(s *mcclient.ClientSession, args *MeterAlertListOptions) error {
var params *jsonutils.JSONDict
{
var err error
params, err = args.BaseListOptions.Params()
if err != nil {
return err
}
}
result, err := modules.MeterAlert.List(s, params)
if err != nil {
return err
}
printList(result, modules.MeterAlert.GetColumns(s))
return nil
})
}
+1 -1
View File
@@ -163,7 +163,7 @@ func init() {
params.Add(jsonutils.NewString(args.MonitorConfig), "monitor_conf_value")
}
monitor_template_inputs, err := modules.MonitorTemplateInputs.Update(s, args.MONITOR_TEMPLATE_ID, args.MONITOR_INPUT_NAME, params)
monitor_template_inputs, err := modules.MonitorTemplateInputs.Update(s, args.MONITOR_TEMPLATE_ID, args.MONITOR_INPUT_NAME, nil, params)
if err != nil {
return err
+5 -3
View File
@@ -17,7 +17,9 @@ func init() {
Wire string `help:"search networks belongs to a wire" json:"-"`
Vpc string `help:"search networks belongs to a VPC"`
Region string `help:"search networks belongs to a CloudRegion" json:"cloudregion"`
ServerType string `help:"search networks belongs to a ServerType"`
ServerType string `help:"search networks belongs to a ServerType" choices:"guest|baremetal|container|pxe|ipmi"`
Private *bool `help:"show private cloud networks only" json:"is_private"`
Public *bool `help:"show public cloud networks only" json:"is_public"`
}
R(&NetworkListOptions{}, "network-list", "List networks", func(s *mcclient.ClientSession, opts *NetworkListOptions) error {
params, err := options.ListStructToParams(opts)
@@ -41,7 +43,7 @@ func init() {
ID string `help:"ID or Name of zone to update"`
Name string `help:"Name of zone"`
Desc string `metavar:"<DESCRIPTION>" help:"Description"`
ServerType string `help:"server type," choices:"baremetal|guest|container"`
ServerType string `help:"server type," choices:"baremetal|guest|container|pxe|ipmi"`
StartIp string `help:"Start ip"`
EndIp string `help:"end ip"`
NetMask int64 `help:"Netmask"`
@@ -183,7 +185,7 @@ func init() {
Gateway string `help:"Default gateway"`
VlanId int64 `help:"Vlan ID" default:"1"`
AllocPolicy string `help:"Address allocation policy" choices:"none|stepdown|stepup|random"`
ServerType string `help:"Server type" choices:"baremetal|guest|container"`
ServerType string `help:"Server type" choices:"baremetal|guest|container|pxe|ipmi"`
Desc string `help:"Description" metavar:"DESCRIPTION"`
}
R(&NetworkCreateOptions{}, "network-create", "Create a virtual network", func(s *mcclient.ClientSession, args *NetworkCreateOptions) error {
+21 -18
View File
@@ -1,10 +1,10 @@
package shell
import (
//"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
@@ -13,35 +13,38 @@ func init() {
/**
* 新建一个通知发送任务
*/
type NotificationCreateOptions struct {
UID string `help:"The user you wanna sent to (Keystone User ID)"`
CONTACTTYPE string `help:"User's contacts type, maybe email|mobile|dingtalk" choices:"email|mobile|dingtalk"`
CONTACTTYPE string `help:"User's contacts type, cloud be email|mobile|dingtalk|webconsole" choices:"email|mobile|dingtalk|webconsole"`
TOPIC string `help:"Title or topic of the notification"`
PRIORITY string `help:"Priority of the notification maybe normal|important|fatal" choices:"normal|important|fatal"`
MSG string `help:"The content of the notification"`
Remark string `help:"Remark or description of the notification"`
Group bool `help:"Send to group"`
Channel string `help:"User's contacts type, cloud be email|mobile|dingtalk|webconsole" choices:"email|mobile|dingtalk|webconsole"`
}
R(&NotificationCreateOptions{}, "notify", "Send a notification to sb", func(s *mcclient.ClientSession, args *NotificationCreateOptions) error {
params := jsonutils.NewDict()
msg := notify.SNotifyMessage{}
if args.Group {
params.Add(jsonutils.NewString(args.UID), "gid")
msg.Gid = args.UID
} else {
params.Add(jsonutils.NewString(args.UID), "uid")
}
params.Add(jsonutils.NewString(args.CONTACTTYPE), "contact_type")
params.Add(jsonutils.NewString(args.TOPIC), "topic")
params.Add(jsonutils.NewString(args.PRIORITY), "priority")
params.Add(jsonutils.NewString(args.MSG), "msg")
if len(args.Remark) > 0 {
params.Add(jsonutils.NewString(args.Remark), "remark")
msg.Uid = args.UID
}
notification, err := modules.Notifications.Create(s, params)
msg.ContactType = []notify.TNotifyChannel{notify.TNotifyChannel(args.CONTACTTYPE)}
for _, c := range args.Channel {
msg.ContactType = append(msg.ContactType, notify.TNotifyChannel(c))
}
msg.Topic = args.TOPIC
msg.Priority = notify.TNotifyPriority(args.PRIORITY)
msg.Msg = args.MSG
msg.Remark = args.Remark
err := notify.Notifications.Send(s, msg)
if err != nil {
return err
}
printObject(notification)
return nil
})
@@ -60,7 +63,7 @@ func init() {
params.Add(jsonutils.NewString(args.Remark), "remark")
}
notification, err := modules.Notifications.Put(s, args.ID, params)
notification, err := notify.Notifications.Put(s, args.ID, params)
if err != nil {
return err
}
@@ -75,12 +78,12 @@ func init() {
options.BaseListOptions
}
R(&NotificationListOptions{}, "notify-list", "List notification history", func(s *mcclient.ClientSession, args *NotificationListOptions) error {
result, err := modules.Notifications.List(s, nil)
result, err := notify.Notifications.List(s, nil)
if err != nil {
return err
}
printList(result, modules.Notifications.GetColumns(s))
printList(result, notify.Notifications.GetColumns(s))
return nil
})
+4 -4
View File
@@ -9,13 +9,13 @@ import (
func init() {
type NetworkReserveIPOptions struct {
NETWORK string `help:"IP or name of network"`
IP string `help:"IP to reserve"`
NOTES string `help:"Why reserve this IP"`
NETWORK string `help:"IP or name of network"`
NOTES string `help:"Why reserve this IP"`
IPS []string `help:"IPs to reserve"`
}
R(&NetworkReserveIPOptions{}, "network-reserve-ip", "Reserve an IP address from pool", func(s *mcclient.ClientSession, args *NetworkReserveIPOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.IP), "ip")
params.Add(jsonutils.NewStringArray(args.IPS), "ips")
params.Add(jsonutils.NewString(args.NOTES), "notes")
net, err := modules.Networks.PerformAction(s, args.NETWORK, "reserve-ip", params)
if err != nil {
+1 -1
View File
@@ -53,7 +53,7 @@ func init() {
})
R(&SchedtagHostPair{}, "schedtag-host-remove", "Remove a schedtag from a host", func(s *mcclient.ClientSession, args *SchedtagHostPair) error {
schedtag, err := modules.Schedtaghosts.Detach(s, args.SCHEDTAG, args.HOST)
schedtag, err := modules.Schedtaghosts.Detach(s, args.SCHEDTAG, args.HOST, nil)
if err != nil {
return err
}
+1 -1
View File
@@ -82,7 +82,7 @@ func init() {
if params.Size() == 0 {
return InvalidUpdateError()
}
srv, err := modules.Serverdisks.Update(s, args.SERVER, args.DISK, params)
srv, err := modules.Serverdisks.Update(s, args.SERVER, args.DISK, nil, params)
if err != nil {
return err
}
+83 -11
View File
@@ -1,7 +1,12 @@
package shell
import (
"fmt"
"strconv"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
@@ -31,9 +36,6 @@ func init() {
if len(args.Ip) > 0 {
params.Add(jsonutils.NewString(args.Ip), "ip_addr")
}
if len(args.Network) > 0 {
params.Add(jsonutils.NewString(args.Network), "network_id")
}
var result *modules.ListResult
var err error
if len(args.Server) > 0 {
@@ -53,9 +55,14 @@ func init() {
type ServerNetworkDetailOptions struct {
SERVER string `help:"ID or Name of Server"`
NETWORK string `help:"ID or Name of Network"`
Mac string `help:"Mac of the guest NIC"`
}
R(&ServerNetworkDetailOptions{}, "server-network-show", "Show server network details", func(s *mcclient.ClientSession, args *ServerNetworkDetailOptions) error {
result, err := modules.Servernetworks.Get(s, args.SERVER, args.NETWORK, nil)
query := jsonutils.NewDict()
if len(args.Mac) > 0 {
query.Add(jsonutils.NewString(args.Mac), "mac")
}
result, err := modules.Servernetworks.Get(s, args.SERVER, args.NETWORK, query)
if err != nil {
return err
}
@@ -66,6 +73,7 @@ func init() {
type ServerNetworkUpdateOptions struct {
SERVER string `help:"ID or Name of Server"`
NETWORK string `help:"ID or Name of Wire"`
Mac string `help:"Mac of NIC"`
Driver string `help:"Driver model of vNIC" choices:"virtio|e1000|vmxnet3|rtl8139"`
Index int64 `help:"Index of NIC" default:"-1"`
Ifname string `help:"Interface name of vNIC on host"`
@@ -84,7 +92,11 @@ func init() {
if params.Size() == 0 {
return InvalidUpdateError()
}
result, err := modules.Servernetworks.Update(s, args.SERVER, args.NETWORK, params)
query := jsonutils.NewDict()
if len(args.Mac) > 0 {
query.Add(jsonutils.NewString(args.Mac), "mac")
}
result, err := modules.Servernetworks.Update(s, args.SERVER, args.NETWORK, query, params)
if err != nil {
return err
}
@@ -93,13 +105,25 @@ func init() {
})
type ServerNetworkBWOptions struct {
SERVER string `help:"ID or Name of server"`
INDEX int64 `help:"Index of NIC"`
BW int64 `help:"Bandwidth in Mbps"`
SERVER string `help:"ID or Name of server"`
MACORIP string `help:"IP, Mac, or Index of NIC"`
BW int64 `help:"Bandwidth in Mbps"`
}
R(&ServerNetworkBWOptions{}, "server-change-bandwidth", "Change server network bandwidth in Mbps", func(s *mcclient.ClientSession, args *ServerNetworkBWOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewInt(args.INDEX), "index")
if regutils.MatchMacAddr(args.MACORIP) {
params.Add(jsonutils.NewString(args.MACORIP), "mac")
} else if regutils.MatchIP4Addr(args.MACORIP) {
params.Add(jsonutils.NewString(args.MACORIP), "ip_addr")
} else if regutils.MatchInteger(args.MACORIP) {
index, err := strconv.ParseInt(args.MACORIP, 10, 64)
if err != nil {
return err
}
params.Add(jsonutils.NewInt(index), "index")
} else {
return fmt.Errorf("Please specify Ip or Mac")
}
params.Add(jsonutils.NewInt(args.BW), "bandwidth")
server, err := modules.Servers.PerformAction(s, args.SERVER, "change-bandwidth", params)
if err != nil {
@@ -126,15 +150,30 @@ func init() {
type ServerDetachNetworkOptions struct {
SERVER string `help:"ID or Name of server"`
NETWORK string `help:"ID or Name of network to detach"`
MACORIP string `help:"Mac Or IP of NIC"`
Reserve bool `help:"Put the release IP address into reserved address pool"`
}
R(&ServerDetachNetworkOptions{}, "server-detach-network", "Detach the virtual network fron a virtual server", func(s *mcclient.ClientSession, args *ServerDetachNetworkOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.NETWORK), "net_id")
// params.Add(jsonutils.NewString(args.NETWORK), "net_id")
if args.Reserve {
params.Add(jsonutils.JSONTrue, "reserve")
}
if regutils.MatchMacAddr(args.MACORIP) {
params.Add(jsonutils.NewString(args.MACORIP), "mac")
} else if regutils.MatchIP4Addr(args.MACORIP) {
params.Add(jsonutils.NewString(args.MACORIP), "ip_addr")
} else if regutils.MatchInteger(args.MACORIP) {
index, err := strconv.ParseInt(args.MACORIP, 10, 64)
if err != nil {
return err
}
params.Add(jsonutils.NewInt(index), "index")
} else if len(args.MACORIP) > 0 {
params.Add(jsonutils.NewString(args.MACORIP), "net_id")
} else {
return fmt.Errorf("Please specify Ip or Mac")
}
srv, err := modules.Servers.PerformAction(s, args.SERVER, "detachnetwork", params)
if err != nil {
return err
@@ -143,4 +182,37 @@ func init() {
return nil
})
type ServerChangeIPAddressOptions struct {
SERVER string `help:"ID or Name of server"`
MACORIP string `help:"Mac Or IP of NIC"`
NETDESC string `help:"Network description"`
Reserve bool `help:"Put the release IP address into reserved address pool"`
}
R(&ServerChangeIPAddressOptions{}, "server-change-ipaddr", "Change ipaddr of a virtual server", func(s *mcclient.ClientSession, args *ServerChangeIPAddressOptions) error {
params := jsonutils.NewDict()
if args.Reserve {
params.Add(jsonutils.JSONTrue, "reserve")
}
if regutils.MatchMacAddr(args.MACORIP) {
params.Add(jsonutils.NewString(args.MACORIP), "mac")
} else if regutils.MatchIP4Addr(args.MACORIP) {
params.Add(jsonutils.NewString(args.MACORIP), "ip_addr")
} else if regutils.MatchInteger(args.MACORIP) {
index, err := strconv.ParseInt(args.MACORIP, 10, 64)
if err != nil {
return err
}
params.Add(jsonutils.NewInt(index), "index")
} else {
return fmt.Errorf("Please specify Ip or Mac")
}
params.Add(jsonutils.NewString(args.NETDESC), "net_desc")
srv, err := modules.Servers.PerformAction(s, args.SERVER, "change-ipaddr", params)
if err != nil {
return err
}
printObject(srv)
return nil
})
}
+16 -2
View File
@@ -166,6 +166,15 @@ func init() {
return nil
})
R(&options.ServerDeleteBackupOptions{}, "server-delete-backup", "Guest delete backup", func(s *mcclient.ClientSession, opts *options.ServerDeleteBackupOptions) error {
ret, err := modules.Servers.PerformAction(s, opts.ID, "delete-backup", nil)
if err != nil {
return err
}
printObject(ret)
return nil
})
R(&options.ServerStopOptions{}, "server-stop", "Stop servers", func(s *mcclient.ClientSession, opts *options.ServerStopOptions) error {
params, err := options.StructToParams(opts)
if err != nil {
@@ -560,10 +569,15 @@ func init() {
})
type ServerDissociateEipOptions struct {
ID string `help:"ID or name of server"`
ID string `help:"ID or name of server" json:"-"`
AutoDelete bool `help:"automatically delete the dissociate EIP" json:"auto_delete,omitfalse"`
}
R(&ServerDissociateEipOptions{}, "server-dissociate-eip", "Dissociate an eip from a server", func(s *mcclient.ClientSession, args *ServerDissociateEipOptions) error {
result, err := modules.Servers.PerformAction(s, args.ID, "dissociate-eip", nil)
params, err := options.StructToParams(args)
if err != nil {
return err
}
result, err := modules.Servers.PerformAction(s, args.ID, "dissociate-eip", params)
if err != nil {
return nil
}
+1 -1
View File
@@ -52,7 +52,7 @@ func init() {
if params.Size() == 0 {
return InvalidUpdateError()
}
result, err := modules.Storagecachedimages.Update(s, args.STORAGECACHE, args.IMAGE, params)
result, err := modules.Storagecachedimages.Update(s, args.STORAGECACHE, args.IMAGE, nil, params)
if err != nil {
return err
}
+3 -1
View File
@@ -10,6 +10,8 @@ import (
func init() {
type StoragecacheListOptions struct {
options.BaseListOptions
CloudregionId string `help:"cloudregion id"`
}
R(&StoragecacheListOptions{}, "storage-cache-list", "List storage caches", func(s *mcclient.ClientSession, opts *StoragecacheListOptions) error {
params, err := options.ListStructToParams(opts)
@@ -71,7 +73,7 @@ func init() {
type StorageUncacheImageActionOptions struct {
ID string `help:"ID or name of storage"`
IMAGE string `help:"ID or name of image"`
Force bool `help:"Force uncache, even if the image exists in cache"`
Force bool `help:"Force uncache, even if the image exists in cache is invalid"`
}
R(&StorageUncacheImageActionOptions{}, "storagecache-uncache-image", "Ask a storage cache to remove image from its cache", func(s *mcclient.ClientSession, args *StorageUncacheImageActionOptions) error {
params := jsonutils.NewDict()
+8
View File
@@ -0,0 +1,8 @@
package main
import "yunion.io/x/onecloud/pkg/hostman"
func main() {
var srv = hostman.SHostService{}
srv.StartService()
}
+132
View File
@@ -0,0 +1,132 @@
package main
import (
"fmt"
"os"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/util/huawei"
"yunion.io/x/onecloud/pkg/util/shellutils"
"yunion.io/x/structarg"
_ "yunion.io/x/onecloud/pkg/util/huawei/shell"
)
type BaseOptions struct {
Help bool `help:"Show help" default:"false"`
Debug bool `help:"Show debug" default:"false"`
CloudEnv string `help:"Cloud environment" default:"$HUAWEI_CLOUD_ENV" choices:"ChinaCloud|InternationalCloud"`
AccessKey string `help:"Access key" default:"$HUAWEI_ACCESS_KEY"`
Secret string `help:"Secret" default:"$HUAWEI_SECRET"`
RegionId string `help:"RegionId" default:"$HUAWEI_REGION"`
ProjectId string `help:"RegionId" default:"$HUAWEI_PROJECT"`
SUBCOMMAND string `help:"huaweicli subcommand" subcommand:"true"`
}
func getSubcommandParser() (*structarg.ArgumentParser, error) {
parse, e := structarg.NewArgumentParser(&BaseOptions{},
"huaweicli",
"Command-line interface to huawei API.",
`See "huaweicli help COMMAND" for help on a specific command.`)
if e != nil {
return nil, e
}
subcmd := parse.GetSubcommand()
if subcmd == nil {
return nil, fmt.Errorf("No subcommand argument.")
}
type HelpOptions struct {
SUBCOMMAND string `help:"sub-command name"`
}
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
if e != nil {
return e
} else {
fmt.Print(helpstr)
return nil
}
})
for _, v := range shellutils.CommandTable {
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
if e != nil {
return nil, e
}
}
return parse, nil
}
func showErrorAndExit(e error) {
log.Errorf("%s", e)
os.Exit(1)
}
func newClient(options *BaseOptions) (*huawei.SRegion, error) {
if len(options.AccessKey) == 0 {
return nil, fmt.Errorf("Missing accessKey")
}
if len(options.Secret) == 0 {
return nil, fmt.Errorf("Missing secret")
}
account := ""
if len(options.ProjectId) > 0 {
account = options.AccessKey + "/" + options.ProjectId
} else {
account = options.AccessKey
}
cli, err := huawei.NewHuaweiClient("", "", options.CloudEnv, account, options.Secret, options.Debug)
if err != nil {
return nil, err
}
region := cli.GetRegion(options.RegionId)
if region == nil {
return nil, fmt.Errorf("No such region %s", options.RegionId)
}
return region, nil
}
func main() {
parser, e := getSubcommandParser()
if e != nil {
showErrorAndExit(e)
}
e = parser.ParseArgs(os.Args[1:], false)
options := parser.Options().(*BaseOptions)
if options.Help {
fmt.Print(parser.HelpString())
} else {
subcmd := parser.GetSubcommand()
subparser := subcmd.GetSubParser()
if e != nil {
if subparser != nil {
fmt.Print(subparser.Usage())
} else {
fmt.Print(parser.Usage())
}
showErrorAndExit(e)
} else {
suboptions := subparser.Options()
if options.SUBCOMMAND == "help" {
e = subcmd.Invoke(suboptions)
} else {
var region *huawei.SRegion
region, e = newClient(options)
if e != nil {
showErrorAndExit(e)
}
e = subcmd.Invoke(region, suboptions)
}
if e != nil {
showErrorAndExit(e)
}
}
}
}
+130
View File
@@ -0,0 +1,130 @@
package main
import (
"fmt"
"os"
"yunion.io/x/log"
"yunion.io/x/structarg"
"yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
"yunion.io/x/onecloud/pkg/util/shellutils"
"yunion.io/x/onecloud/pkg/util/ssh"
_ "yunion.io/x/onecloud/pkg/util/ipmitool/shell"
)
type BaseOptions struct {
Help bool `help:"Show help" short-token:"h"`
MODE string `help:"Execute command mode" choices:"ssh|rmcp"`
HOST string `help:"IP address of remote host"`
PASSWD string `help:"Password"`
User string `help:"Username" short-token:"u" default:"root"`
Port int `help:"Remote service port"`
SUBCOMMAND string `help:"ipmicli subcommand" subcommand:"true"`
}
func showErrorAndExit(err error) {
log.Errorf("%s", err)
os.Exit(1)
}
func getSubcommandParser() (*structarg.ArgumentParser, error) {
parser, err := structarg.NewArgumentParser(
&BaseOptions{},
"ipmicli",
"Command-line interface to ipmitool",
`See "ipmicli help COMMAND" for help on a specific command.`,
)
if err != nil {
return nil, err
}
subcmd := parser.GetSubcommand()
if subcmd == nil {
return nil, fmt.Errorf("No subcommand argument.")
}
type HelpOptions struct {
SUBCOMMAND string `help:"sub-command name"`
}
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
if e != nil {
return e
} else {
fmt.Print(helpstr)
return nil
}
})
for _, v := range shellutils.CommandTable {
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
if e != nil {
return nil, e
}
}
return parser, nil
}
func newExecutor(options *BaseOptions) (ipmitool.IPMIExecutor, error) {
if options.MODE == "ssh" {
port := 22
if options.Port > 0 {
port = options.Port
}
sshCli, err := ssh.NewClient(options.HOST, port, options.User, options.PASSWD, "")
if err != nil {
return nil, err
}
return ipmitool.NewSSHIPMI(sshCli), nil
}
if options.MODE == "rmcp" {
port := 623
if options.Port > 0 {
port = options.Port
}
return ipmitool.NewLanPlusIPMIWithPort(options.HOST, options.User, options.PASSWD, port), nil
}
return nil, fmt.Errorf("Unsupported mode: %s", options.MODE)
}
func main() {
parser, err := getSubcommandParser()
if err != nil {
showErrorAndExit(err)
}
err = parser.ParseArgs(os.Args[1:], false)
options := parser.Options().(*BaseOptions)
if options.Help {
fmt.Print(parser.HelpString())
return
}
subcmd := parser.GetSubcommand()
subparser := subcmd.GetSubParser()
if err != nil {
if subparser != nil {
fmt.Print(subparser.Usage())
} else {
fmt.Print(parser.Usage())
}
showErrorAndExit(err)
return
}
suboptions := subparser.Options()
var args []interface{}
if options.SUBCOMMAND == "help" {
args = append(args, suboptions)
} else {
executor, err := newExecutor(options)
if err != nil {
showErrorAndExit(err)
}
args = append(args, executor, suboptions)
}
err = subcmd.Invoke(args...)
if err != nil {
showErrorAndExit(err)
}
}
+9
View File
@@ -0,0 +1,9 @@
package main
import (
"yunion.io/x/onecloud/pkg/logger/service"
)
func main() {
service.StartService()
}
+50 -20
View File
@@ -6,7 +6,6 @@ import (
"math/rand"
"net/http"
"os"
"os/signal"
"strings"
"sync"
"syscall"
@@ -15,6 +14,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/trace"
"yunion.io/x/pkg/util/signalutils"
"yunion.io/x/pkg/utils"
"yunion.io/x/onecloud/pkg/appctx"
@@ -40,6 +40,7 @@ type Application struct {
cors *Cors
middlewares []MiddlewareFunc
isExiting bool
idleConnsClosed chan struct{}
}
@@ -332,44 +333,73 @@ func (app *Application) initServer(addr string) *http.Server {
func (app *Application) registerCleanShutdown(s *http.Server, onStop func()) {
app.idleConnsClosed = make(chan struct{})
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
log.Infof("Close signal received: %+v", <-c)
// dump goroutine stack
signalutils.RegisterSignal(func() {
utils.DumpAllGoroutineStack(log.Logger().Out)
}, syscall.SIGUSR1)
quitSignals := []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM}
signalutils.RegisterSignal(func() {
if app.isExiting {
log.Infof("Quit signal received!!! clean up in progress, be patient...")
return
}
app.isExiting = true
log.Infof("Quit signal received!!! do cleanup...")
if err := s.Shutdown(context.Background()); err != nil {
// Error from closing listeners, or context timeout:
log.Errorf("HTTP server Shutdown: %v", err)
}
onStop()
if onStop != nil {
func() {
defer func() {
if r := recover(); r != nil {
log.Errorf("app exiting error: %s", r)
}
}()
onStop()
}()
}
close(app.idleConnsClosed)
}()
}, quitSignals...)
signalutils.StartTrap()
}
func (app *Application) waitCleanShutdown() {
<-app.idleConnsClosed
log.Infof("Service stopped.")
}
func (app *Application) ListenAndServe(addr string, onStop func()) {
func (app *Application) ListenAndServe(addr string) {
app.ListenAndServeWithCleanup(addr, nil)
}
func (app *Application) ListenAndServeTLS(addr string, certFile, keyFile string) {
app.ListenAndServeTLSWithCleanup(addr, certFile, keyFile, nil)
}
func (app *Application) ListenAndServeWithCleanup(addr string, onStop func()) {
app.ListenAndServeTLSWithCleanup(addr, "", "", onStop)
}
func (app *Application) ListenAndServeTLSWithCleanup(addr string, certFile, keyFile string, onStop func()) {
s := app.initServer(addr)
app.registerCleanShutdown(s, onStop)
err := s.ListenAndServe()
var err error
if len(certFile) == 0 && len(keyFile) == 0 {
err = s.ListenAndServe()
} else {
err = s.ListenAndServeTLS(certFile, keyFile)
}
if err != nil && err != http.ErrServerClosed {
log.Fatalf("ListAndServer fail: %s", err)
}
app.waitCleanShutdown()
}
func (app *Application) ListenAndServeTLS(addr string, certFile, keyFile string, onStop func()) {
s := app.initServer(addr)
app.registerCleanShutdown(s, onStop)
err := s.ListenAndServeTLS(certFile, keyFile)
if err != nil && err != http.ErrServerClosed {
log.Fatalf("ListAndServerTLS fail: %s", err)
}
app.waitCleanShutdown()
}
func isJsonContentType(r *http.Request) bool {
contType := strings.ToLower(r.Header.Get("Content-Type"))
if strings.HasPrefix(contType, "application/json") {
+399
View File
@@ -0,0 +1,399 @@
package baremetal
import (
"context"
"fmt"
"net"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/baremetal/pxe"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/procutils"
)
var (
baremetalAgent *SBaremetalAgent
)
type SZone struct {
Name string `json:"name"`
Id string `json:"id"`
}
type SBaremetalAgent struct {
PXEServer *pxe.Server
ListenInterface *net.Interface
AgentId string
AgentName string
Zone *SZone
Manager *SBaremetalManager
}
func newBaremetalAgent() (*SBaremetalAgent, error) {
iface, err := net.InterfaceByName(o.Options.ListenInterface)
if err != nil {
return nil, err
}
ips, err := getIfaceIPs(iface)
if err != nil {
return nil, err
}
if len(ips) == 0 {
return nil, fmt.Errorf("Interface %s ip address not found", o.Options.ListenInterface)
}
log.Debugf("Interface %s ip address: %v", iface.Name, ips)
agent := &SBaremetalAgent{
ListenInterface: iface,
}
return agent, nil
}
func GetAdminSession() *mcclient.ClientSession {
return auth.GetAdminSession(context.TODO(), o.Options.Region, "v2")
}
func (agent *SBaremetalAgent) GetListenIPs() ([]net.IP, error) {
ips, err := getIfaceIPs(agent.ListenInterface)
if err != nil {
return nil, err
}
if len(ips) == 0 {
return nil, fmt.Errorf("Interface %s ip address not found", agent.ListenInterface.Name)
}
return ips, nil
}
func (agent *SBaremetalAgent) GetListenIP() (net.IP, error) {
ips, err := agent.GetListenIPs()
if err != nil {
return nil, err
}
if o.Options.ListenAddress == "" {
return ips[0], nil
}
if o.Options.ListenAddress == "0.0.0.0" {
return net.ParseIP(o.Options.ListenAddress), nil
}
for _, ip := range ips {
if ip.String() == o.Options.ListenAddress {
return ip, nil
}
}
return nil, fmt.Errorf("Not found ListenAddress %s on %s", o.Options.ListenAddress, o.Options.ListenInterface)
}
func (agent *SBaremetalAgent) GetAccessIP() (net.IP, error) {
ips, err := agent.GetListenIPs()
if err != nil {
return nil, err
}
if o.Options.AccessAddress == "" {
return ips[0], nil
}
for _, ip := range ips {
if ip.String() == o.Options.AccessAddress {
return ip, nil
}
}
return nil, fmt.Errorf("Not found AccessAddress %s on %s", o.Options.AccessAddress, o.Options.ListenInterface)
}
func (agent *SBaremetalAgent) GetDHCPServerIP() (net.IP, error) {
listenIP := o.Options.ListenAddress
if len(listenIP) == 0 || listenIP == "0.0.0.0" {
return agent.GetAccessIP()
}
return agent.GetListenIP()
}
func getIfaceIPs(iface *net.Interface) ([]net.IP, error) {
addrs, err := iface.Addrs()
if err != nil {
return nil, err
}
ips := make([]net.IP, 0)
for _, a := range addrs {
if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
ips = append(ips, ipnet.IP)
}
}
}
return ips, nil
}
func (agent *SBaremetalAgent) startRegister() {
if agent.AgentId != "" {
return
}
var delayRetryTime time.Duration = 30 * time.Second
for {
err := agent.register()
if err != nil {
log.Errorf("Register error: %v, retry after %s...", err, delayRetryTime)
time.Sleep(delayRetryTime)
continue
}
break
}
return
}
func (agent *SBaremetalAgent) register() error {
session := GetAdminSession()
var err error
err = agent.fetchZone(session)
if err != nil {
return err
}
err = agent.createOrUpdateBaremetalAgent(session)
if err != nil {
return err
}
log.Infof("Baremetal %s:%s register success, do offline", agent.AgentName, agent.AgentId)
err = agent.doOffline(session)
if err != nil {
return err
}
agent.tuneSystem()
manager, err := NewBaremetalManager(agent)
if err != nil {
return fmt.Errorf("New baremetal manager error: %v", err)
}
err = manager.loadConfigs()
if err != nil {
return fmt.Errorf("Baremetal manager load config error: %v", err)
}
agent.Manager = manager
agent.startPXEServices(manager)
return nil
}
func (agent *SBaremetalAgent) GetManager() *SBaremetalManager {
return agent.Manager
}
func (agent *SBaremetalAgent) getZoneByIP(session *mcclient.ClientSession) (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
listenIP, err := agent.GetListenIP()
if err != nil {
return nil, err
}
params.Add(jsonutils.NewString(listenIP.String()), "ip")
networks, err := modules.Networks.List(session, params)
if err != nil {
return nil, err
}
if len(networks.Data) == 0 {
return nil, fmt.Errorf("Not found networks by agent listen ip: %s", listenIP)
}
wireId, err := networks.Data[0].GetString("wire_id")
if err != nil {
return nil, err
}
wire, err := modules.Wires.Get(session, wireId, nil)
if err != nil {
return nil, err
}
zoneId, err := wire.GetString("zone_id")
if err != nil {
return nil, err
}
zone, err := modules.Zones.Get(session, zoneId, nil)
if err != nil {
return nil, err
}
return zone, nil
}
func (agent *SBaremetalAgent) fetchZone(session *mcclient.ClientSession) error {
zoneName := o.Options.Zone
var zoneInfoObj jsonutils.JSONObject
var err error
if zoneName != "" {
zoneInfoObj, err = modules.Zones.Get(session, zoneName, nil)
} else {
zoneInfoObj, err = agent.getZoneByIP(session)
}
if err != nil {
return err
}
zone := SZone{}
err = zoneInfoObj.Unmarshal(&zone)
if err != nil {
return err
}
agent.Zone = &zone
return nil
}
func (agent *SBaremetalAgent) createOrUpdateBaremetalAgent(session *mcclient.ClientSession) error {
params := jsonutils.NewDict()
naccessIP, err := agent.GetAccessIP()
if err != nil {
return err
}
params.Add(jsonutils.NewString(naccessIP.String()), "access_ip")
ret, err := modules.Baremetalagents.List(session, params)
if err != nil {
return err
}
var (
cloudObj jsonutils.JSONObject
agentId string
agentName string
)
// create or update BaremetalAgent
if len(ret.Data) == 0 {
cloudObj, err = agent.createBaremetalAgent(session)
if err != nil {
return err
}
} else {
cloudBmAgent := ret.Data[0]
accessIP, _ := cloudBmAgent.GetString("access_ip")
managerUri, _ := cloudBmAgent.GetString("manager_uri")
zoneId, _ := cloudBmAgent.GetString("zone_id")
agentId, _ := cloudBmAgent.GetString("id")
if naccessIP.String() != accessIP ||
agent.GetManagerUri() != managerUri ||
zoneId != agent.Zone.Id {
cloudObj, err = agent.updateBaremetalAgent(session, agentId)
if err != nil {
return err
}
} else {
cloudObj = cloudBmAgent
}
}
agentId, err = cloudObj.GetString("id")
if err != nil {
return err
}
agentName, err = cloudObj.GetString("name")
if err != nil {
return err
}
agent.AgentId = agentId
agent.AgentName = agentName
return nil
}
func (agent *SBaremetalAgent) GetManagerUri() string {
accessIP, _ := agent.GetAccessIP()
proto := "http"
if o.Options.EnableSsl {
proto = "https"
}
return fmt.Sprintf("%s://%s:%d", proto, accessIP, o.Options.Port)
}
func (agent *SBaremetalAgent) getCreateUpdateInfo() (jsonutils.JSONObject, error) {
accessIP, err := agent.GetAccessIP()
if err != nil {
return nil, err
}
params := jsonutils.NewDict()
if agent.AgentId == "" {
params.Add(jsonutils.NewString(fmt.Sprintf("baremetal_%s", accessIP)), "name")
}
params.Add(jsonutils.NewString(accessIP.String()), "access_ip")
params.Add(jsonutils.NewString(agent.GetManagerUri()), "manager_uri")
params.Add(jsonutils.NewString(agent.Zone.Id), "zone_id")
return params, nil
}
func (agent *SBaremetalAgent) createBaremetalAgent(session *mcclient.ClientSession) (jsonutils.JSONObject, error) {
params, err := agent.getCreateUpdateInfo()
if err != nil {
return nil, err
}
return modules.Baremetalagents.Create(session, params)
}
func (agent *SBaremetalAgent) updateBaremetalAgent(session *mcclient.ClientSession, id string) (jsonutils.JSONObject, error) {
params, err := agent.getCreateUpdateInfo()
if err != nil {
return nil, err
}
return modules.Baremetalagents.Update(session, id, params)
}
func (agent *SBaremetalAgent) doOffline(session *mcclient.ClientSession) error {
_, err := modules.Baremetalagents.PerformAction(session, agent.AgentId, "offline", nil)
return err
}
func (agent *SBaremetalAgent) doOnline(session *mcclient.ClientSession) error {
_, err := modules.Baremetalagents.PerformAction(session, agent.AgentId, "online", nil)
return err
}
func (agent *SBaremetalAgent) tuneSystem() {
agent.disableUDPOffloading()
}
func (agent *SBaremetalAgent) disableUDPOffloading() {
log.Infof("Disable UDP offloading")
offTx := procutils.NewCommand("ethtool", "--offload", o.Options.ListenInterface, "tx", "off")
offTx.Run()
offGso := procutils.NewCommand("ethtool", "-K", o.Options.ListenInterface, "gso", "off")
offGso.Run()
}
func (agent *SBaremetalAgent) startPXEServices(manager *SBaremetalManager) {
listenIP, err := agent.GetListenIP()
if err != nil {
log.Fatalf("Get listen ip address error: %v", err)
}
agent.PXEServer = &pxe.Server{
TFTPRootDir: o.Options.TftpRoot,
Address: listenIP.String(),
BaremetalManager: manager,
}
go func() {
err := agent.PXEServer.Serve()
if err != nil {
log.Fatalf("Start PXE server error: %v", err)
}
}()
}
func Start() error {
var err error
if baremetalAgent != nil {
log.Warningf("Global baremetalAgent already start")
return nil
}
baremetalAgent, err = newBaremetalAgent()
if err != nil {
return err
}
baremetalAgent.startRegister()
return nil
}
func GetBaremetalAgent() *SBaremetalAgent {
return baremetalAgent
}
func GetBaremetalManager() *SBaremetalManager {
return GetBaremetalAgent().GetManager()
}
+1
View File
@@ -0,0 +1 @@
package baremetal // import "yunion.io/x/onecloud/pkg/baremetal"
+48
View File
@@ -0,0 +1,48 @@
package handler
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
var delayTaskWorkerMan *appsrv.SWorkerManager
func init() {
delayTaskWorkerMan = appsrv.NewWorkerManager("DelayTaskWorkerManager", 8, 1024, false)
}
type ProcessFunc func(data jsonutils.JSONObject) (jsonutils.JSONObject, error)
type delayTask struct {
process ProcessFunc
taskId string
session *mcclient.ClientSession
data jsonutils.JSONObject
}
func newDelayTask(process ProcessFunc, session *mcclient.ClientSession, taskId string, data jsonutils.JSONObject) *delayTask {
return &delayTask{
process: process,
taskId: taskId,
session: session,
data: data,
}
}
func DelayProcess(process ProcessFunc, session *mcclient.ClientSession, taskId string, data jsonutils.JSONObject) {
delayTaskWorkerMan.Run(func() {
executeDelayProcess(newDelayTask(process, session, taskId, data))
}, nil, nil)
}
func executeDelayProcess(task *delayTask) {
ret, err := task.process(task.data)
if err != nil {
modules.ComputeTasks.TaskFailed(task.session, task.taskId, err)
return
}
modules.ComputeTasks.TaskComplete(task.session, task.taskId, ret)
}
+1
View File
@@ -0,0 +1 @@
package handler // import "yunion.io/x/onecloud/pkg/baremetal/handler"
+153
View File
@@ -0,0 +1,153 @@
package handler
import (
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/baremetal"
baremetaltypes "yunion.io/x/onecloud/pkg/baremetal/types"
"yunion.io/x/onecloud/pkg/httperrors"
)
func InitHandlers(app *appsrv.Application) {
initBaremetalsHandler(app)
}
func initBaremetalsHandler(app *appsrv.Application) {
// baremetal actions handler
app.AddHandler("GET", bmActionPrefix("notify"), bmObjMiddleware(handleBaremetalNotify))
app.AddHandler("POST", bmActionPrefix("maintenance"), bmObjMiddleware(handleBaremetalMaintenance))
app.AddHandler("POST", bmActionPrefix("unmaintenance"), bmObjMiddleware(handleBaremetalUnmaintenance))
app.AddHandler("POST", bmActionPrefix("delete"), bmObjMiddleware(handleBaremetalDelete))
app.AddHandler("POST", bmActionPrefix("syncstatus"), bmObjMiddleware(handleBaremetalSyncStatus))
app.AddHandler("POST", bmActionPrefix("sync-config"), bmObjMiddleware(handleBaremetalSyncConfig))
app.AddHandler("POST", bmActionPrefix("sync-ipmi"), bmObjMiddleware(handleBaremetalSyncIPMI))
app.AddHandler("POST", bmActionPrefix("prepare"), bmObjMiddleware(handleBaremetalPrepare))
app.AddHandler("POST", bmActionPrefix("reset-bmc"), bmObjMiddleware(handleBaremetalResetBMC))
// server actions handler
app.AddHandler("POST", srvActionPrefix("create"), srvClassMiddleware(handleServerCreate))
app.AddHandler("POST", srvActionPrefix("deploy"), srvObjMiddleware(handleServerDeploy))
app.AddHandler("POST", srvActionPrefix("rebuild"), srvObjMiddleware(handleServerRebuild))
app.AddHandler("POST", srvActionPrefix("start"), srvObjMiddleware(handleServerStart))
app.AddHandler("POST", srvActionPrefix("stop"), srvObjMiddleware(handleServerStop))
app.AddHandler("POST", srvActionPrefix("reset"), srvObjMiddleware(handleServerReset))
app.AddHandler("POST", srvActionPrefix("status"), srvObjMiddleware(handleServerStatus))
app.AddHandler("DELETE", srvIdPrefix(), srvObjMiddleware(handleServerDelete))
}
func handleBaremetalNotify(ctx *Context, bm *baremetal.SBaremetalInstance) {
key, err := ctx.Query().GetString("key")
if err != nil {
ctx.ResponseError(httperrors.NewInputParameterError("Not found key in query"))
return
}
remoteAddr := ctx.RequestRemoteIP()
err = bm.SaveSSHConfig(remoteAddr, key)
if err != nil {
log.Errorf("Save baremetal %s ssh config: %v", bm.GetId(), err)
}
// execute BaremetalServerPrepareTask
task := bm.GetTask()
if task != nil {
task.SSHExecute(task, remoteAddr, key, nil)
}
ctx.ResponseOk()
}
func handleBaremetalMaintenance(ctx *Context, bm *baremetal.SBaremetalInstance) {
bm.StartBaremetalMaintenanceTask(ctx.UserCred(), ctx.TaskId(), ctx.Data())
ctx.ResponseOk()
}
func handleBaremetalUnmaintenance(ctx *Context, bm *baremetal.SBaremetalInstance) {
bm.StartBaremetalUnmaintenanceTask(ctx.UserCred(), ctx.TaskId(), ctx.Data())
ctx.ResponseOk()
}
func handleBaremetalDelete(ctx *Context, bm *baremetal.SBaremetalInstance) {
ctx.DelayProcess(bm.DelayedRemove, nil)
ctx.ResponseOk()
}
func handleBaremetalSyncStatus(ctx *Context, bm *baremetal.SBaremetalInstance) {
ctx.DelayProcess(bm.DelayedSyncStatus, nil)
ctx.ResponseOk()
}
func handleBaremetalSyncConfig(ctx *Context, bm *baremetal.SBaremetalInstance) {
ctx.DelayProcess(bm.DelayedSyncDesc, nil)
ctx.ResponseOk()
}
func handleBaremetalSyncIPMI(ctx *Context, bm *baremetal.SBaremetalInstance) {
ctx.DelayProcess(bm.DelayedSyncIPMIInfo, nil)
ctx.ResponseOk()
}
func handleBaremetalPrepare(ctx *Context, bm *baremetal.SBaremetalInstance) {
bm.StartBaremetalReprepareTask(ctx.UserCred(), ctx.TaskId(), ctx.Data())
ctx.ResponseOk()
}
func handleBaremetalResetBMC(ctx *Context, bm *baremetal.SBaremetalInstance) {
bm.StartBaremetalResetBMCTask(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 {
ctx.ResponseError(httperrors.NewGeneralError(err))
return
}
ctx.ResponseOk()
}
func handleServerDelete(ctx *Context, bm *baremetal.SBaremetalInstance, _ baremetaltypes.IBaremetalServer) {
bm.StartServerDestroyTask(ctx.UserCred(), ctx.TaskId(), nil)
ctx.ResponseOk()
}
func handleServerDeploy(ctx *Context, bm *baremetal.SBaremetalInstance, _ baremetaltypes.IBaremetalServer) {
if err := bm.StartServerDeployTask(ctx.UserCred(), ctx.TaskId(), ctx.Data()); err != nil {
ctx.ResponseError(httperrors.NewGeneralError(err))
return
}
ctx.ResponseOk()
}
func handleServerRebuild(ctx *Context, bm *baremetal.SBaremetalInstance, _ baremetaltypes.IBaremetalServer) {
if err := bm.StartServerRebuildTask(ctx.UserCred(), ctx.TaskId(), ctx.Data()); err != nil {
ctx.ResponseError(httperrors.NewGeneralError(err))
return
}
ctx.ResponseOk()
}
func handleServerStart(ctx *Context, bm *baremetal.SBaremetalInstance, _ baremetaltypes.IBaremetalServer) {
if err := bm.StartServerStartTask(ctx.UserCred(), ctx.TaskId(), ctx.Data()); err != nil {
ctx.ResponseError(httperrors.NewGeneralError(err))
return
}
ctx.ResponseOk()
}
func handleServerStop(ctx *Context, bm *baremetal.SBaremetalInstance, _ baremetaltypes.IBaremetalServer) {
if err := bm.StartServerStopTask(ctx.UserCred(), ctx.TaskId(), ctx.Data()); err != nil {
ctx.ResponseError(httperrors.NewGeneralError(err))
return
}
ctx.ResponseOk()
}
func handleServerReset(ctx *Context, bm *baremetal.SBaremetalInstance, _ baremetaltypes.IBaremetalServer) {
ctx.DelayProcess(bm.DelayedServerReset, nil)
ctx.ResponseOk()
}
func handleServerStatus(ctx *Context, bm *baremetal.SBaremetalInstance, _ baremetaltypes.IBaremetalServer) {
ctx.DelayProcess(bm.DelayedServerStatus, nil)
ctx.ResponseOk()
}
+186
View File
@@ -0,0 +1,186 @@
package handler
import (
"context"
"fmt"
"net/http"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/baremetal"
baremetaltypes "yunion.io/x/onecloud/pkg/baremetal/types"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
)
const (
BM_PREFIX = "baremetals"
SERVER_PREFIX = "servers"
PARAMS_BMID_KEY = "<bm_id>"
PARAMS_SRVID_KEY = "<srv_id>"
)
func bmIdPrefix() string {
// baremetals/<bm_id>
return fmt.Sprintf("%s/%s", BM_PREFIX, PARAMS_BMID_KEY)
}
func bmActionPrefix(action string) string {
// baremetals/<bm_id>/action
return fmt.Sprintf("%s/%s", bmIdPrefix(), action)
}
func srvIdPrefix() string {
// baremetals/<bm_id>/servers/<srv_id>
return fmt.Sprintf("%s/%s/%s", bmIdPrefix(), SERVER_PREFIX, PARAMS_SRVID_KEY)
}
func srvActionPrefix(action string) string {
// baremetals/<bm_id>/servers/<srv_id>/action
return fmt.Sprintf("%s/%s", srvIdPrefix(), action)
}
type handlerFunc func(ctx *Context)
func authMiddleware(h handlerFunc) appsrv.FilterHandler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
newCtx := NewContext(ctx, w, r)
h(newCtx)
}
}
type bmObjHandlerFunc func(ctx *Context, bm *baremetal.SBaremetalInstance)
func bmObjMiddleware(h bmObjHandlerFunc) 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
}
h(newCtx, baremetal)
}
}
type srvObjHandlerFunc func(ctx *Context, bm *baremetal.SBaremetalInstance, srv baremetaltypes.IBaremetalServer)
func srvClassMiddleware(h bmObjHandlerFunc) appsrv.FilterHandler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
newCtx := NewContext(ctx, w, r)
bmId := newCtx.Params()[PARAMS_BMID_KEY]
//srvId := newCtx.Params()[PARAMS_SRVID_KEY]
baremetal := newCtx.GetBaremetalManager().GetBaremetalById(bmId)
if baremetal == nil {
newCtx.ResponseError(httperrors.NewNotFoundError("Not found baremetal by id: %s", bmId))
return
}
if baremetal.GetServerId() != "" {
newCtx.ResponseError(httperrors.NewNotAcceptableError("Baremetal %s occupied", bmId))
return
}
h(newCtx, baremetal)
}
}
func srvObjMiddleware(h srvObjHandlerFunc) appsrv.FilterHandler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
newCtx := NewContext(ctx, w, r)
bmId := newCtx.Params()[PARAMS_BMID_KEY]
srvId := newCtx.Params()[PARAMS_SRVID_KEY]
baremetal := newCtx.GetBaremetalManager().GetBaremetalById(bmId)
if baremetal == nil {
newCtx.ResponseError(httperrors.NewNotFoundError("Not found baremetal by id: %s", bmId))
return
}
if baremetal.GetServerId() != srvId {
newCtx.ResponseError(httperrors.NewNotFoundError("Not found server by id: %s", srvId))
return
}
srv := baremetal.GetServer()
h(newCtx, baremetal, srv)
}
}
type Context struct {
context.Context
userCred mcclient.TokenCredential
params map[string]string
query jsonutils.JSONObject
data jsonutils.JSONObject
request *http.Request
writer http.ResponseWriter
}
func NewContext(ctx context.Context, w http.ResponseWriter, r *http.Request) *Context {
params, query, body := appsrv.FetchEnv(ctx, w, r)
return &Context{
Context: ctx,
userCred: auth.FetchUserCredential(ctx, nil),
params: params,
query: query,
data: body,
request: r,
writer: w,
}
}
func (ctx *Context) Params() map[string]string {
return ctx.params
}
func (ctx *Context) Data() jsonutils.JSONObject {
return ctx.data
}
func (ctx *Context) Query() jsonutils.JSONObject {
return ctx.query
}
func (ctx *Context) UserCred() mcclient.TokenCredential {
return ctx.userCred
}
func (ctx *Context) TaskId() string {
return ctx.Request().Header.Get(mcclient.TASK_ID)
}
func (ctx *Context) ResponseStruct(obj interface{}) {
appsrv.SendStruct(ctx.writer, obj)
}
func (ctx *Context) ResponseJson(obj jsonutils.JSONObject) {
appsrv.SendJSON(ctx.writer, obj)
}
func (ctx *Context) ResponseError(err error) {
httperrors.GeneralServerError(ctx.writer, err)
}
func (ctx *Context) Request() *http.Request {
return ctx.request
}
func (ctx *Context) RequestRemoteIP() string {
remoteAddr := ctx.Request().RemoteAddr
return strings.Split(remoteAddr, ":")[0]
}
func (ctx *Context) ResponseOk() {
obj := jsonutils.NewDict()
obj.Add(jsonutils.NewString("ok"), "result")
appsrv.SendJSON(ctx.writer, obj)
}
func (ctx *Context) GetBaremetalManager() *baremetal.SBaremetalManager {
return baremetal.GetBaremetalManager()
}
func (ctx *Context) DelayProcess(process ProcessFunc, data jsonutils.JSONObject) {
DelayProcess(process, ctx.GetBaremetalManager().GetClientSession(), ctx.TaskId(), data)
}
File diff suppressed because it is too large Load Diff
+84
View File
@@ -0,0 +1,84 @@
package baremetal
import (
"fmt"
"net"
"os"
"path/filepath"
"time"
"yunion.io/x/pkg/util/netutils"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/util/dhcp"
)
func GetNicDHCPConfig(
n *types.SNic,
serverIP string,
hostName string,
isPxe bool,
arch uint16,
) (*dhcp.ResponseConfig, error) {
if n == nil {
return nil, fmt.Errorf("Nic is nil")
}
if n.IpAddr == "" {
return nil, fmt.Errorf("Nic no ip address")
}
ipAddr, err := netutils.NewIPV4Addr(n.IpAddr)
if err != nil {
return nil, fmt.Errorf("Parse IP address error: %q", n.IpAddr)
}
subnetMask := net.ParseIP(netutils.Masklen2Mask(n.MaskLen).String())
routes := make([][]string, 0)
for _, route := range n.Routes {
routes = append(routes, []string{route[0], route[1]})
}
conf := &dhcp.ResponseConfig{
ServerIP: net.ParseIP(serverIP),
ClientIP: net.ParseIP(ipAddr.String()),
Gateway: net.ParseIP(n.Gateway),
SubnetMask: subnetMask,
BroadcastAddr: net.ParseIP(ipAddr.BroadcastAddr(n.MaskLen).String()),
DNSServer: net.ParseIP(n.Dns),
Domain: n.Domain,
OsName: "Linux",
Hostname: hostName,
Routes: routes,
LeaseTime: time.Duration(o.Options.DhcpLeaseTime) * time.Second,
RenewalTime: time.Duration(o.Options.DhcpRenewalTime) * time.Second,
}
if isPxe {
conf.BootServer = serverIP
switch arch {
case 7, 9:
conf.BootFile = "bootx64.efi"
case 6:
conf.BootFile = "bootia32.efi"
default:
conf.BootFile = "pxelinux.0"
}
pxePath := filepath.Join(o.Options.TftpRoot, conf.BootFile)
if f, err := os.Open(pxePath); err != nil {
return nil, err
} else {
if info, err := f.Stat(); err != nil {
return nil, err
} else {
pxeSize := info.Size()
pxeBlk := pxeSize / 512
if pxeSize > pxeBlk*512 {
pxeBlk += 1
}
conf.BootBlock = uint16(pxeBlk)
}
}
}
return conf, nil
}
+1
View File
@@ -0,0 +1 @@
package options // import "yunion.io/x/onecloud/pkg/baremetal/options"
+35
View File
@@ -0,0 +1,35 @@
package options
import (
"yunion.io/x/onecloud/pkg/cloudcommon"
)
type BaremetalOptions struct {
cloudcommon.CommonOptions
ListenInterface string `help:"Master net interface of baremetal server" default:"br0"`
AccessAddress string `help:"Management IP address of baremetal server, only need to use when multiple address bind to ListenInterface"`
ListenAddress string `help:"PXE serve IP address to select when multiple address bind to ListenInterface" default:"0.0.0.0"`
TftpRoot string `help:"tftp root directory"`
AutoRegisterBaremetal bool `default:"true" help:"Automatically create a baremetal instance"`
BaremetalsPath string `default:"/opt/cloud/workspace/baremetals" help:"Path for baremetals configuration files"`
LinuxDefaultRootUser bool `default:"false" help:"Default account for linux system is root"`
IpmiLanPortShared bool `default:"false" help:"IPMI Lan port shared or dedicated"`
Zone string `help:"Zone where the agent locates"`
DhcpLeaseTime int `default:"100663296" help:"DHCP lease time in seconds"` // 0x6000000
DhcpRenewalTime int `default:"67108864" help:"DHCP renewal time in seconds"` // 0x4000000
EnableGeneralGuestDhcp bool `default:"false" help:"Enable DHCP service for general guest, e.g. those on VMware ESXi or Xen"`
ForceDhcpProbeIpmi bool `default:"false" help:"Force DHCP probe IPMI interface network connection"`
TftpMaxTimeoutRetries int `default:"20" help:"Maximal tftp timeout retries, default is 20"`
LengthyWorkerCount int `default:"8" help:"Parallel worker count for lengthy tasks"`
ShortWorkerCount int `default:"8" help:"Parallel worker count for short-lived tasks"`
DefaultIpmiPassword string `help:"Default IPMI passowrd"`
DefaultStrongIpmiPassword string `help:"Default strong IPMI passowrd"`
WindowsDefaultAdminUser bool `default:"true" help:"Default account for Windows system is Administrator"`
}
var (
Options BaremetalOptions
)
+1
View File
@@ -0,0 +1 @@
package profiles // import "yunion.io/x/onecloud/pkg/baremetal/profiles"
+108
View File
@@ -0,0 +1,108 @@
package profiles
import (
"strings"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
)
type IPMIProfile struct {
LanChannel []int
RootName string
RootId int
StrongPass bool
}
func DefaultProfile() IPMIProfile {
return IPMIProfile{
LanChannel: []int{1},
RootName: "root",
RootId: 2,
}
}
func InspurProfile() IPMIProfile {
return IPMIProfile{
LanChannel: []int{8, 1},
RootName: "admin",
RootId: 2,
}
}
func LenovoProfile() IPMIProfile {
return IPMIProfile{
LanChannel: []int{8},
RootName: "root",
RootId: 2,
}
}
func HpProfile() IPMIProfile {
return IPMIProfile{
LanChannel: []int{2},
RootName: "root",
RootId: 1,
}
}
func HuaweiProfile() IPMIProfile {
return IPMIProfile{
LanChannel: []int{1},
RootName: "root",
RootId: 2,
StrongPass: true,
}
}
func FoxconnProfile() IPMIProfile {
return IPMIProfile{
LanChannel: []int{1},
RootName: "root",
RootId: 2,
StrongPass: true,
}
}
func QemuProfile() IPMIProfile {
return IPMIProfile{
LanChannel: []int{8, 1},
RootName: "root",
RootId: 2,
StrongPass: true,
}
}
var (
PROFILES map[string]IPMIProfile = map[string]IPMIProfile{
"inspur": InspurProfile(),
"lenovo": LenovoProfile(),
"hp": HpProfile(),
"huawei": HuaweiProfile(),
"foxconn": FoxconnProfile(),
"qemu": QemuProfile(),
}
)
func GetProfile(sysinfo *types.SIPMISystemInfo) IPMIProfile {
profile, ok := PROFILES[strings.ToLower(sysinfo.Manufacture)]
if ok {
return profile
}
return DefaultProfile()
}
func GetLanChannel(sysinfo *types.SIPMISystemInfo) []int {
return GetProfile(sysinfo).LanChannel
}
func GetRootId(sysinfo *types.SIPMISystemInfo) int {
return GetProfile(sysinfo).RootId
}
func GetRootName(sysinfo *types.SIPMISystemInfo) string {
return GetProfile(sysinfo).RootName
}
func IsStrongPass(sysinfo *types.SIPMISystemInfo) bool {
return GetProfile(sysinfo).StrongPass
}
+361
View File
@@ -0,0 +1,361 @@
package pxe
import (
"errors"
"fmt"
"net"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/dhcp"
)
func (s *Server) serveDHCP(srv *dhcp.DHCPServer, handler dhcp.DHCPHandler) error {
return srv.ListenAndServe(handler)
}
type NetworkInterfaceIdent struct {
Type uint16
Major uint16
Minior uint16
}
type DHCPHandler struct {
ClientMac net.HardwareAddr // client nic mac
ClientAddr net.IP // IP address from DHCP client
RelayAddr net.IP // IP address of DHCP relay agent
Options dhcp.Options // dhcp packet options
VendorClassId string
ClientArch uint16
NetworkInterfaceIdent NetworkInterfaceIdent
ClientGuid string
packet dhcp.Packet
// baremetal manager
baremetalManager IBaremetalManager
// baremetal instance
baremetalInstance IBaremetalInstance
// cloud network config
netConfig *types.SNetworkConfig
}
func (h *DHCPHandler) ServeDHCP(pkt dhcp.Packet, _ *net.UDPAddr, _ *net.Interface) (dhcp.Packet, error) {
//log.V(4).Debugf("[DHCP] request: %s", pkt.DebugString())
err := h.parsePacket(pkt)
if err != nil {
log.Errorf("[DHCP] parse packet error: %v", err)
}
log.V(4).Debugf("[DHCP] parse packet end: %#v", h)
if h.RelayAddr.String() == "0.0.0.0" {
return nil, fmt.Errorf("Request not from a DHCP relay, ignore mac: %s", h.ClientMac)
}
conf, err := h.fetchConfig()
if err != nil {
return nil, err
}
if conf == nil {
return nil, fmt.Errorf("Empty packet config")
}
return dhcp.MakeReplyPacket(pkt, conf)
}
func (h *DHCPHandler) parsePacket(pkt dhcp.Packet) error {
h.packet = pkt
h.ClientAddr = pkt.CIAddr()
h.ClientMac = pkt.CHAddr()
h.RelayAddr = pkt.RelayAddr()
h.Options = pkt.ParseOptions()
var (
vendorClsId string
cliArch uint16
err error
netIfIdent NetworkInterfaceIdent
cliGuid string
)
for optCode, data := range h.Options {
switch optCode {
case dhcp.OptionVendorClassIdentifier:
vendorClsId, err = h.Options.String(optCode)
case dhcp.OptionClientArchitecture:
cliArch, err = h.Options.Uint16(optCode)
case dhcp.OptionClientNetworkInterfaceIdentifier:
netIfIdentBs, err := h.Options.Bytes(optCode)
if err != nil {
break
}
netIfIdent = NetworkInterfaceIdent{
Type: uint16(netIfIdentBs[0]),
Major: uint16(netIfIdentBs[1]),
Minior: uint16(netIfIdentBs[2]),
}
log.Debugf("[DHCP] get network iface identifier: %#v", netIfIdent)
case dhcp.OptionClientMachineIdentifier:
switch len(data) {
case 0:
// A missing GUID is invalid according to the spec, however
// there are PXE ROMs in the wild that omit the GUID and still
// expect to boot.
case 17:
if data[0] != 0 {
err = errors.New("malformed client GUID (option 97), leading byte must be zero")
}
default:
err = errors.New("malformed client GUID (option 97), wrong size")
}
cliGuid, err = h.Options.String(optCode)
}
if err != nil {
log.Errorf("[DHCP] parse vendor option %d error: %v", optCode, err)
}
}
h.VendorClassId = vendorClsId
h.ClientArch = cliArch
h.NetworkInterfaceIdent = netIfIdent
h.ClientGuid = cliGuid
return err
}
func (h *DHCPHandler) fetchConfig() (*dhcp.ResponseConfig, error) {
// 1. find_network_conf
netConf, err := h.findNetworkConf(false)
if err != nil {
return nil, err
}
h.netConfig = netConf
// TODO: set cache for netConf
//
if h.isPXERequest() {
// handle PXE DHCP request
log.Infof("DHCP relay from %s(%s) for %s, find matched networks: %#v", h.RelayAddr, h.ClientAddr, h.ClientMac, netConf)
bmDesc, err := h.createOrUpdateBaremetal()
if err != nil {
return nil, err
}
err = h.doInitBaremetalAdminNetif(bmDesc)
if err != nil {
return nil, err
}
if h.baremetalInstance.NeedPXEBoot() {
return h.baremetalInstance.GetPXEDHCPConfig(h.ClientArch)
}
// ignore
log.Warningf("No need to pxeboot, ignore the request ...(mac:%s guid:%s)", h.ClientMac, h.ClientGuid)
return nil, nil
} else {
// handle normal DHCP request
bmInstance := h.baremetalManager.GetBaremetalByMac(h.ClientMac)
if bmInstance == nil {
// options.EnableGeneralGuestDhcp
// cloud be an instance not served by a host-server
// from guestdhcp import GuestDHCPHelperTask
// task = GuestDHCPHelperTask(self)
// task.start()
return nil, nil
}
h.baremetalInstance = bmInstance
ipmiNic := h.baremetalInstance.GetIPMINic(h.ClientMac)
if ipmiNic != nil && ipmiNic.Mac == h.ClientMac.String() {
err = h.baremetalInstance.InitAdminNetif(h.ClientMac, h.netConfig, types.NIC_TYPE_IPMI, models.NETWORK_TYPE_IPMI)
if err != nil {
return nil, err
}
} else {
err = h.baremetalInstance.RegisterNetif(h.ClientMac, h.netConfig)
if err != nil {
log.Errorf("RegisterNetif error: %v", err)
return nil, err
}
}
return h.baremetalInstance.GetDHCPConfig(h.ClientMac)
}
}
func (h *DHCPHandler) findNetworkConf(filterUseIp bool) (*types.SNetworkConfig, error) {
params := jsonutils.NewDict()
if filterUseIp {
params.Add(jsonutils.NewString(h.RelayAddr.String()), "ip")
} else {
params.Add(jsonutils.NewString(
fmt.Sprintf("guest_gateway.equals(%s)", h.RelayAddr)),
"filter.0")
params.Add(jsonutils.NewString(
fmt.Sprintf("guest_dhcp.equals(%s)", h.RelayAddr)),
"filter.1")
params.Add(jsonutils.JSONTrue, "filter_any")
}
params.Add(jsonutils.JSONTrue, "is_private")
session := h.baremetalManager.GetClientSession()
ret, err := modules.Networks.List(session, params)
if err != nil {
return nil, err
}
if len(ret.Data) == 0 {
if !filterUseIp {
// use ip filter try again
return h.findNetworkConf(true)
}
return nil, fmt.Errorf("DHCP relay from %s(%s) for %s, find no match network", h.RelayAddr, h.ClientAddr, h.ClientMac)
}
network := types.SNetworkConfig{}
err = ret.Data[0].Unmarshal(&network)
return &network, err
}
// createOrUpdateBaremetal create or update baremetal by client MAC
func (h *DHCPHandler) createOrUpdateBaremetal() (jsonutils.JSONObject, error) {
session := h.baremetalManager.GetClientSession()
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(h.ClientMac.String()), "any_mac")
ret, err := modules.Hosts.List(session, params)
if err != nil {
return nil, err
}
switch len(ret.Data) {
case 0:
// found new baremetal, create it if auto register
if o.Options.AutoRegisterBaremetal {
return h.createBaremetal()
}
case 1:
// already exists, do update
bmId, err := ret.Data[0].GetString("id")
if err != nil {
return nil, err
}
return h.updateBaremetal(bmId)
}
return nil, fmt.Errorf("Found %d records match %s", len(ret.Data), h.ClientMac)
}
func (h *DHCPHandler) createBaremetal() (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
mac := h.ClientMac.String()
zoneId := h.baremetalManager.GetZoneId()
name := fmt.Sprintf("BM%s", strings.Replace(mac, ":", "", -1))
params.Add(jsonutils.NewString(name), "name")
params.Add(jsonutils.NewString(mac), "access_mac")
params.Add(jsonutils.NewString("baremetal"), "host_type")
params.Add(jsonutils.JSONTrue, "is_baremetal")
params.Add(jsonutils.NewString(zoneId), "zone_id")
session := h.baremetalManager.GetClientSession()
desc, err := modules.Hosts.Create(session, params)
if err != nil {
return nil, err
}
return desc, nil
}
func (h *DHCPHandler) updateBaremetal(id string) (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(h.ClientMac.String()), "access_mac")
params.Add(jsonutils.NewString(h.baremetalManager.GetZoneId()), "zone_id")
params.Add(jsonutils.NewString("baremetal"), "host_type")
params.Add(jsonutils.JSONTrue, "is_baremetal")
session := h.baremetalManager.GetClientSession()
desc, err := modules.Hosts.Update(session, id, params)
if err != nil {
return nil, err
}
return desc, nil
}
func (h *DHCPHandler) doInitBaremetalAdminNetif(desc jsonutils.JSONObject) error {
var err error
h.baremetalInstance, err = h.baremetalManager.AddBaremetal(desc)
if err != nil {
return err
}
err = h.baremetalInstance.InitAdminNetif(h.ClientMac, h.netConfig, types.NIC_TYPE_ADMIN, models.NETWORK_TYPE_PXE)
return err
}
func (h *DHCPHandler) isPXERequest() bool {
pkt := h.packet
return dhcp.IsPXERequest(pkt)
}
func (s *Server) validateDHCP(pkt dhcp.Packet) (Machine, Firmware, error) {
var mach Machine
var fwtype Firmware
fwt, err := pkt.ParseOptions().Uint16(dhcp.OptionClientArchitecture)
if err != nil {
return mach, fwtype, fmt.Errorf("malformed DHCP option 93 (required for PXE): %s", err)
}
// Basic architecture and firmware identification, based purely on
// the PXE architecture option.
switch fwt {
// TODO: complete case 1, 2, 3, 4, 5, 8
case 0:
// Intel x86PC
mach.Arch = ArchIA32
fwtype = FirmwareX86PC
case 1:
// NEC/PC98
mach.Arch = ArchUnknown
fwtype = FirmwareUnknown
case 2:
// EFI Itanium
mach.Arch = ArchUnknown
fwtype = FirmwareUnknown
case 3:
// DEC Alpha
mach.Arch = ArchUnknown
fwtype = FirmwareUnknown
case 4:
// Arc x86
mach.Arch = ArchUnknown
fwtype = FirmwareUnknown
case 5:
// Intel Lean Client
mach.Arch = ArchUnknown
fwtype = FirmwareUnknown
case 6:
// EFI IA32
mach.Arch = ArchIA32
fwtype = FirmwareEFI32
case 7:
// EFI BC
mach.Arch = ArchX64
fwtype = FirmwareEFI64
case 8:
// EFI Xscale
mach.Arch = ArchUnknown
fwtype = FirmwareUnknown
case 9:
// EFI x86-64
mach.Arch = ArchX64
fwtype = FirmwareEFIBC
default:
return mach, 0, fmt.Errorf("unsupported client firmware type '%d'", fwtype)
}
guid, _ := pkt.ParseOptions().Bytes(dhcp.OptionClientMachineIdentifier)
switch len(guid) {
case 0:
// A missing GUID is invalid according to the spec, however
// there are PXE ROMs in the wild that omit the GUID and still
// expect to boot. The only thing we do with the GUID is
// mirror it back to the client if it's there, so we might as
// 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")
}
default:
return mach, 0, errors.New("malformed client GUID (option 97), wrong size")
}
mach.MAC = pkt.CHAddr()
return mach, fwtype, nil
}
+1
View File
@@ -0,0 +1 @@
package pxe // import "yunion.io/x/onecloud/pkg/baremetal/pxe"
+134
View File
@@ -0,0 +1,134 @@
package pxe
import (
"fmt"
"net"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/dhcp"
)
const (
portDHCP = 67
portTFTP = 69
)
// Architecture describes a kind of CPU architecture
type Architecture int
// Architecture types that pxe knows how to boot
// These architectures are self-reported by the booting machine. The
// machine may support additional execution mode. For example, legacy
// PC BIOS reports itself as an ArchIA32, but may also support ArchX64
// execution
const (
// ArchIA32 is a 32-bit x86 machine. It may also support X64
// execution, but pxe has no way of kowning.
ArchIA32 Architecture = iota
// ArchX64 is a 64-bit x86 machine (aka amd64 aka x64)
ArchX64
ArchUnknown
)
func (a Architecture) String() string {
switch a {
case ArchIA32:
return "IA32"
case ArchX64:
return "X64"
default:
return "Unknown architecture"
}
}
// A Machine describes a machine that is attempting to boot
type Machine struct {
MAC net.HardwareAddr
Arch Architecture
}
// Firmware describes a kind of firmware attempting to boot.
// This should only be used for selecting the right bootloader within
// pxe, kernel selection should key off the more generic
// Architecture
type Firmware int
// The bootloaders that pxe knows how to handle
const (
FirmwareX86PC Firmware = iota // "Classic" x86 BIOS with PXE/UNDI support
FirmwareEFI32 // 32-bit x86 processor running EFI
FirmwareEFI64 // 64-bit x86 processor running EFI
FirmwareEFIBC // 64-bit x86 processor running EFI
FirmwareX86Ipxe // "Classic" x86 BIOS running iPXE (no UNDI support)
FirmwareUnknown
)
type IBaremetalManager interface {
GetZoneId() string
GetBaremetalByMac(mac net.HardwareAddr) IBaremetalInstance
AddBaremetal(desc jsonutils.JSONObject) (IBaremetalInstance, error)
GetClientSession() *mcclient.ClientSession
}
type IBaremetalInstance interface {
NeedPXEBoot() bool
GetIPMINic(cliMac net.HardwareAddr) *types.SNic
GetPXEDHCPConfig(arch uint16) (*dhcp.ResponseConfig, error)
GetDHCPConfig(cliMac net.HardwareAddr) (*dhcp.ResponseConfig, error)
InitAdminNetif(cliMac net.HardwareAddr, netConf *types.SNetworkConfig, nicType string, netType string) error
RegisterNetif(cliMac net.HardwareAddr, netConf *types.SNetworkConfig) error
GetTFTPResponse() string
}
type Server struct {
// Address to listen on, or empty for all interfaces
Address string
DHCPPort int
TFTPPort int
TFTPRootDir string
errs chan error
BaremetalManager IBaremetalManager
}
func (s *Server) Serve() error {
if s.Address == "" {
s.Address = "0.0.0.0"
}
if s.DHCPPort == 0 {
s.DHCPPort = portDHCP
}
if s.TFTPPort == 0 {
s.TFTPPort = portTFTP
}
tftpConn, err := net.ListenPacket("udp", fmt.Sprintf("%s:%d", s.Address, s.TFTPPort))
if err != nil {
return err
}
tftpHandler, err := NewTFTPHandler(s.TFTPRootDir, s.BaremetalManager)
if err != nil {
return err
}
//tftpSrv := tftp.NewServer(tftpHandler.ReadHandler, nil)
//tftpSrv.SetTimeout(5 * time.Second)
log.Infof("DHCPServer Bind %s %d", s.Address, s.DHCPPort)
dhcpSrv, _, err := dhcp.NewDHCPServer2(s.Address, s.DHCPPort)
if err != nil {
return err
}
s.errs = make(chan error)
dhcpHandler := &DHCPHandler{baremetalManager: s.BaremetalManager}
go func() { s.errs <- s.serveDHCP(dhcpSrv, dhcpHandler) }()
go func() { s.errs <- s.serveTFTP(tftpConn, tftpHandler) }()
err = <-s.errs
return err
}
+119
View File
@@ -0,0 +1,119 @@
package pxe
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"path/filepath"
"regexp"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/util/tftp"
)
var (
PxeLinuxCfgPattern = `^pxelinux.cfg/01-(?P<mac>([0-9a-f]{2}-){5}[0-9a-f]{2})$`
)
type TFTPHandler struct {
RootDir string
BaremetalManager IBaremetalManager
}
func NewTFTPHandler(rootDir string, baremetalManager IBaremetalManager) (*TFTPHandler, error) {
if _, err := os.Stat(rootDir); err != nil {
return nil, fmt.Errorf("TFTP root dir %q stat error: %v", rootDir, err)
}
return &TFTPHandler{
RootDir: rootDir,
BaremetalManager: baremetalManager,
}, nil
}
// Handle is called when client starts file download from server
func (h *TFTPHandler) Handle(filename string, clientAddr net.Addr) (io.ReadCloser, int64, error) {
regEx := regexp.MustCompile(PxeLinuxCfgPattern)
matches := regEx.FindStringSubmatch(filename)
if len(matches) != 0 {
paramsMap := make(map[string]string)
// pxelinux config matched
for i, name := range regEx.SubexpNames() {
if i > 0 && i <= len(matches) {
paramsMap[name] = matches[i]
}
}
mac, ok := paramsMap["mac"]
if !ok {
return nil, 0, fmt.Errorf("request filename %q not found mac pattern", filename)
}
macAddr, err := net.ParseMAC(mac)
if err != nil {
return nil, 0, fmt.Errorf("Parse mac string %q error: %v", mac, err)
}
return h.sendPxeLinuxCfgResponse(macAddr, clientAddr)
}
return h.sendFile(filename, clientAddr)
}
func (h *TFTPHandler) sendPxeLinuxCfgResponse(mac net.HardwareAddr, _ net.Addr) (io.ReadCloser, int64, error) {
log.Debugf("[TFTP] client mac: %s", mac)
bmInstance := h.BaremetalManager.GetBaremetalByMac(mac)
if bmInstance == nil {
err := fmt.Errorf("Not found baremetal instance by mac: %s", mac)
log.Errorf("Get baremetal error: %v", err)
return nil, 0, err
}
respStr := bmInstance.GetTFTPResponse()
log.Debugf("[TFTP] get tftp response config: %s", respStr)
bs := []byte(respStr)
size := int64(len(bs))
buffer := bytes.NewBufferString(respStr)
return ioutil.NopCloser(buffer), size, nil
}
func (h *TFTPHandler) sendFile(filename string, _ net.Addr) (io.ReadCloser, int64, error) {
filename = h.getFilePath(filename)
st, err := os.Stat(filename)
if err != nil {
log.Errorf("TFTP stat file %q error: %v", filename, err)
return nil, 0, err
}
if !st.Mode().IsRegular() {
return nil, 0, fmt.Errorf("requested path %q is not a file", filename)
}
file, err := os.Open(filename)
if err != nil {
log.Errorf("TFTP open file %q error: %v", filename, err)
return nil, 0, err
}
return file, st.Size(), err
}
func (h *TFTPHandler) getFilePath(fileName string) string {
return filepath.Join(h.RootDir, fileName)
}
func (h *TFTPHandler) transferLog(clientAddr net.Addr, path string, err error) {
log.Debugf("TFTP transfer log clientAddr: %s, path: %s, error: %v", clientAddr, path, err)
}
func (s *Server) serveTFTP(l net.PacketConn, handler *TFTPHandler) error {
ts := tftp.Server{
Handler: handler.Handle,
InfoLog: func(msg string) { log.Debugf("TFTP msg: %s", msg) },
TransferLog: handler.transferLog,
}
err := ts.Serve(l)
if err != nil {
return fmt.Errorf("TFTP server shut down: %v", err)
}
return nil
}
+1
View File
@@ -0,0 +1 @@
package service // import "yunion.io/x/onecloud/pkg/baremetal/service"
+41
View File
@@ -0,0 +1,41 @@
package service
import (
"os"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/baremetal"
"yunion.io/x/onecloud/pkg/baremetal/handler"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/baremetal/tasks"
"yunion.io/x/onecloud/pkg/cloudcommon"
"yunion.io/x/onecloud/pkg/cloudcommon/service"
)
type BaremetalService struct {
service.SServiceBase
}
func New() *BaremetalService {
return &BaremetalService{}
}
func (s *BaremetalService) StartService() {
cloudcommon.ParseOptions(&o.Options, os.Args, "baremetal.conf", "baremetal")
cloudcommon.InitAuth(&o.Options.CommonOptions, s.startAgent)
app := cloudcommon.InitApp(&o.Options.CommonOptions, false)
handler.InitHandlers(app)
cloudcommon.ServeForeverWithCleanup(app, &o.Options.CommonOptions, func() {
tasks.OnStop()
})
}
func (s *BaremetalService) startAgent() {
err := baremetal.Start()
if err != nil {
log.Fatalf("Start agent error: %v", err)
}
}
+29
View File
@@ -0,0 +1,29 @@
package baremetal
const (
INIT = "init"
PREPARE = "prepare"
PREPARE_FAIL = "prepare_fail"
READY = "ready"
RUNNING = "running"
MAINTAINING = "maintaining"
START_MAINTAIN = "start_maintain"
DELETING = "deleting"
DELETE = "delete"
DELETE_FAIL = "delete_fail"
UNKNOWN = "unknown"
SYNCING_STATUS = "syncing_status"
SYNC = "sync"
SYNC_FAIL = "sync_fail"
START_CONVERT = "start_convert"
CONVERTING = "converting"
START_FAIL = "start_fail"
STOP_FAIL = "stop_fail"
)
const (
NIC_TYPE_IPMI = "ipmi"
NIC_TYPE_ADMIN = "admin"
)
var NIC_TYPES = []string{NIC_TYPE_ADMIN, NIC_TYPE_IPMI}
+29
View File
@@ -0,0 +1,29 @@
package status
const (
INIT = "init"
PREPARE = "prepare"
PREPARE_FAIL = "prepare_fail"
READY = "ready"
RUNNING = "running"
MAINTAINING = "maintaining"
START_MAINTAIN = "start_maintain"
DELETING = "deleting"
DELETE = "delete"
DELETE_FAIL = "delete_fail"
UNKNOWN = "unknown"
SYNCING_STATUS = "syncing_status"
SYNC = "sync"
SYNC_FAIL = "sync_fail"
START_CONVERT = "start_convert"
CONVERTING = "converting"
START_FAIL = "start_fail"
STOP_FAIL = "stop_fail"
)
const (
SERVER_RUNNING = "running"
SERVER_ADMIN = "admin"
SERVER_READY = "ready"
SERVER_UNKNOWN = "unknown"
)
+1
View File
@@ -0,0 +1 @@
package status // import "yunion.io/x/onecloud/pkg/baremetal/status"
+405
View File
@@ -0,0 +1,405 @@
package tasks
import (
"container/list"
"context"
"fmt"
"sync"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type Queue struct {
objList *list.List
objListLock *sync.Mutex
}
func NewQueue() *Queue {
return &Queue{
objList: list.New(),
objListLock: new(sync.Mutex),
}
}
func (q *Queue) Append(obj interface{}) *Queue {
q.objListLock.Lock()
defer q.objListLock.Unlock()
q.objList.PushBack(obj)
return q
}
func (q *Queue) First() interface{} {
q.objListLock.Lock()
defer q.objListLock.Unlock()
if q.objList.Len() == 0 {
return nil
}
return q.objList.Front().Value
}
func (q *Queue) IsEmpty() bool {
return q.First() == nil
}
func (q *Queue) Pop() interface{} {
q.objListLock.Lock()
defer q.objListLock.Unlock()
if q.objList.Len() == 0 {
return nil
}
first := q.objList.Front()
q.objList.Remove(first)
return first.Value
}
func (q *Queue) String() string {
itemStrings := debugString(q.objList.Front())
return fmt.Sprintf("%v", itemStrings)
}
func debugString(elem *list.Element) []string {
if elem == nil {
return nil
}
strings := []string{fmt.Sprintf("%v", elem.Value)}
rest := debugString(elem.Next())
if rest != nil {
strings = append(strings, rest...)
}
return strings
}
type TaskQueue struct {
*Queue
}
type TaskStageFunc func(ctx context.Context, args interface{}) error
type SSHTaskStageFunc func(ctx context.Context, cli *ssh.Client, args interface{}) error
type sshStageWrapper struct {
sshStage SSHTaskStageFunc
remoteIP string
password string
}
func sshStageW(
stage SSHTaskStageFunc,
remoteIP string,
password string,
) *sshStageWrapper {
return &sshStageWrapper{
sshStage: stage,
remoteIP: remoteIP,
password: password,
}
}
func (sw *sshStageWrapper) Do(ctx context.Context, args interface{}) error {
cli, err := ssh.NewClient(sw.remoteIP, 22, "root", sw.password, "")
if err != nil {
return err
}
return sw.sshStage(ctx, cli, args)
}
type ITask interface {
// GetStage return current task stage func
GetStage() TaskStageFunc
// SetStage set task next execute stage func
SetStage(stage TaskStageFunc)
// GetSSHStage return current task ssh stage func
GetSSHStage() SSHTaskStageFunc
// SetSSHStage set task next execute ssh stage func
SetSSHStage(stage SSHTaskStageFunc)
// GetTaskId return remote service task id
GetTaskId() string
GetClientSession() *mcclient.ClientSession
GetTaskQueue() *TaskQueue
// GetData return TaskData from region
GetData() jsonutils.JSONObject
GetName() string
Execute(ITask ITask, args interface{})
SetSSHStageParams(task ITask, remoteIP string, passwd string)
SSHExecute(task ITask, remoteIP string, passwd string, args interface{})
NeedPXEBoot() bool
}
func NewTaskQueue() *TaskQueue {
return &TaskQueue{
Queue: NewQueue(),
}
}
func (q *TaskQueue) GetTask() ITask {
if q.IsEmpty() {
return nil
}
return q.First().(ITask)
}
func (q *TaskQueue) PopTask() ITask {
if q.IsEmpty() {
return nil
}
return q.Pop().(ITask)
}
func (q *TaskQueue) AppendTask(task ITask) *TaskQueue {
log.Infof("Append task %s", task.GetName())
q.Append(task)
return q
}
type TaskFactory func(bm IBaremetal, taskId string, data jsonutils.JSONObject) (ITask, error)
type SBaremetalTaskBase struct {
Baremetal IBaremetal
userCred mcclient.TokenCredential
stageFunc TaskStageFunc
sshStageFunc SSHTaskStageFunc
taskId string
data jsonutils.JSONObject
}
func newBaremetalTaskBase(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) *SBaremetalTaskBase {
task := &SBaremetalTaskBase{
Baremetal: baremetal,
taskId: taskId,
data: data,
}
return task
}
func (task *SBaremetalTaskBase) GetTaskQueue() *TaskQueue {
return task.Baremetal.GetTaskQueue()
}
func (task *SBaremetalTaskBase) GetTaskId() string {
return task.taskId
}
func (task *SBaremetalTaskBase) GetData() jsonutils.JSONObject {
return task.data
}
func (task *SBaremetalTaskBase) GetStage() TaskStageFunc {
return task.stageFunc
}
func (task *SBaremetalTaskBase) GetSSHStage() SSHTaskStageFunc {
return task.sshStageFunc
}
func (task *SBaremetalTaskBase) SetStage(stage TaskStageFunc) {
task.stageFunc = stage
}
func (task *SBaremetalTaskBase) SetSSHStage(stage SSHTaskStageFunc) {
task.sshStageFunc = stage
}
func (task *SBaremetalTaskBase) Execute(iTask ITask, args interface{}) {
ExecuteTask(iTask, args)
}
func (task *SBaremetalTaskBase) SetSSHStageParams(iTask ITask, remoteIP string, password string) {
iTask.SetStage(sshStageW(iTask.GetSSHStage(), remoteIP, password).Do)
}
func (task *SBaremetalTaskBase) SSHExecute(
iTask ITask,
remoteIP string,
password string,
args interface{},
) {
//iTask.SetStage(sshStageW(iTask.GetSSHStage(), remoteIP, password).Do)
task.SetSSHStageParams(iTask, remoteIP, password)
ExecuteTask(iTask, args)
}
//func (task *SBaremetalTaskBase) CallNextStage(iTask ITask, stage TaskStageFunc, args interface{}) {
//iTask.SetStage(stage)
//ExecuteTask(iTask, args)
//}
func (task *SBaremetalTaskBase) GetClientSession() *mcclient.ClientSession {
return task.Baremetal.GetClientSession()
}
func (self *SBaremetalTaskBase) EnsurePowerShutdown(soft bool) error {
log.Infof("EnsurePowerShutdown: soft=%v", soft)
status, err := self.Baremetal.GetPowerStatus()
if err != nil {
return err
}
startTime := time.Now()
maxWait := 60 * time.Second
for status == "" || status == types.POWER_STATUS_ON {
if time.Since(startTime).Seconds() >= maxWait.Seconds() && soft {
soft = false
}
err = self.Baremetal.DoPowerShutdown(soft)
if err != nil {
log.Errorf("DoPowerShutdown: %v", err)
}
time.Sleep(20 * time.Second)
status, err = self.Baremetal.GetPowerStatus()
if err != nil {
log.Errorf("GetPowerStatus: %v", err)
}
}
if status != types.POWER_STATUS_OFF {
return fmt.Errorf("Baremetal invalid status %s for shutdown", status)
}
return nil
}
func (self *SBaremetalTaskBase) EnsurePowerUp(bootdev string) error {
log.Infof("EnsurePowerUp: bootdev=%s", bootdev)
var bootFunc func() error = nil
switch bootdev {
case "pxe":
bootFunc = self.Baremetal.DoPXEBoot
case "disk":
bootFunc = self.Baremetal.DoDiskBoot
}
if bootFunc == nil {
return fmt.Errorf("No boot func %s found", bootdev)
}
status, err := self.Baremetal.GetPowerStatus()
if err != nil {
return err
}
for status == "" || status == types.POWER_STATUS_OFF {
if status == types.POWER_STATUS_OFF {
err = bootFunc()
if err != nil {
return err
}
}
status, err = self.Baremetal.GetPowerStatus()
if err != nil {
return err
}
if status == "" || status == types.POWER_STATUS_OFF {
time.Sleep(40 * time.Second)
status, err = self.Baremetal.GetPowerStatus()
if err != nil {
return err
}
}
}
if status != types.POWER_STATUS_ON {
return fmt.Errorf("Baremetal invalid restart status: %s", status)
}
return nil
}
func (self *SBaremetalTaskBase) NeedPXEBoot() bool {
return false
}
type IPXEBootTask interface {
ITask
OnPXEBoot(ctx context.Context, cli *ssh.Client, args interface{}) error
}
type SBaremetalPXEBootTaskBase struct {
*SBaremetalTaskBase
pxeBootTask IPXEBootTask
startTime time.Time
}
func newBaremetalPXEBootTaskBase(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) *SBaremetalPXEBootTaskBase {
baseTask := newBaremetalTaskBase(baremetal, taskId, data)
self := &SBaremetalPXEBootTaskBase{
SBaremetalTaskBase: baseTask,
}
return self
}
func (self *SBaremetalPXEBootTaskBase) InitPXEBootTask(pxeBootTask IPXEBootTask, data jsonutils.JSONObject) (*SBaremetalPXEBootTaskBase, error) {
self.pxeBootTask = pxeBootTask
//OnInitStage(pxeBootTask)
sshConf, _ := self.Baremetal.GetSSHConfig()
if sshConf != nil && self.Baremetal.TestSSHConfig() {
pxeBootTask.SetSSHStage(pxeBootTask.OnPXEBoot)
pxeBootTask.SetSSHStageParams(pxeBootTask, sshConf.RemoteIP, sshConf.Password)
return self, nil
}
// Do soft reboot
if data != nil && jsonutils.QueryBoolean(data, "soft_boot", false) {
self.startTime = time.Now()
if err := self.Baremetal.DoPowerShutdown(true); err != nil {
log.Errorf("DoPowerShutdown error: %v", err)
}
//self.CallNextStage(self, self.WaitForShutdown, nil)
self.SetStage(self.WaitForShutdown)
return self, nil
}
// shutdown and power up to PXE mode
if err := self.EnsurePowerShutdown(false); err != nil {
return self, fmt.Errorf("EnsurePowerShutdown: %v", err)
}
if err := self.EnsurePowerUp("pxe"); err != nil {
return self, fmt.Errorf("EnsurePowerUp to pxe: %v", err)
}
// this stage will be called by baremetalInstance when pxe start notify
self.SetSSHStage(pxeBootTask.OnPXEBoot)
return self, nil
}
func (self *SBaremetalPXEBootTaskBase) NeedPXEBoot() bool {
return true
}
func (self *SBaremetalPXEBootTaskBase) WaitForShutdown(ctx context.Context, args interface{}) error {
self.SetStage(self.OnStopComplete)
status, err := self.Baremetal.GetPowerStatus()
if err != nil {
return err
}
if status == types.POWER_STATUS_OFF {
self.Execute(self, nil)
} else if time.Since(self.startTime) >= 90*time.Second {
err = self.Baremetal.DoPowerShutdown(false)
if err != nil {
return err
}
}
return nil
}
func (self *SBaremetalPXEBootTaskBase) OnStopComplete(ctx context.Context, args interface{}) error {
err := self.EnsurePowerUp("pxe")
if err != nil {
return err
}
self.SetSSHStage(self.pxeBootTask.OnPXEBoot)
return nil
}
func (self *SBaremetalPXEBootTaskBase) GetName() string {
return "BaremetalPXEBootTaskBase"
}
+44
View File
@@ -0,0 +1,44 @@
package tasks
import (
"testing"
)
func TestQueue(t *testing.T) {
type test struct {
queue *Queue
expected string
}
q123 := NewQueue().Append("1").Append("2").Append("3")
q123Pop := NewQueue().Append("1").Append("2").Append("3")
q123Pop.Pop()
qEmptyPop := NewQueue().Append("1").Append("2")
qEmptyPop.Pop()
qEmptyPop.Pop()
qEmptyPop.Pop()
tests := map[string]test{
"Empty queue": {
queue: NewQueue(),
expected: "[]",
},
"Queue append": {
queue: q123,
expected: "[1 2 3]",
},
"Queue pop": {
queue: q123Pop,
expected: "[2 3]",
},
"Queue pop to empty": {
queue: qEmptyPop,
expected: "[]",
},
}
for name, testCase := range tests {
output := testCase.queue.String()
expected := testCase.expected
if output != expected {
t.Errorf("TestCase %q failed, output: %v, expected: %v", name, output, expected)
}
}
}
+78
View File
@@ -0,0 +1,78 @@
package tasks
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/utils"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type IServerBaseDeployTask interface {
IPXEBootTask
DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error)
}
type SBaremetalServerBaseDeployTask struct {
*SBaremetalPXEBootTaskBase
serverDeployTask IServerBaseDeployTask
}
func newBaremetalServerBaseDeployTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
deployTask IServerBaseDeployTask,
) (*SBaremetalServerBaseDeployTask, error) {
task := new(SBaremetalServerBaseDeployTask)
baseTask := newBaremetalPXEBootTaskBase(baremetal, taskId, data)
task.SBaremetalPXEBootTaskBase = baseTask
_, err := baseTask.InitPXEBootTask(task, data)
task.serverDeployTask = deployTask
return task, err
}
func (self *SBaremetalServerBaseDeployTask) GetName() string {
return "BaremetalServerBaseDeployTask"
}
func (self *SBaremetalServerBaseDeployTask) GetFinishAction() string {
if self.data != nil {
action, _ := self.data.GetString("on_finish")
return action
}
return ""
}
func (self *SBaremetalServerBaseDeployTask) OnPXEBoot(ctx context.Context, term *ssh.Client, args interface{}) error {
log.Infof("%s called on stage pxeboot, args: %v", self.GetName(), args)
result, err := self.serverDeployTask.DoDeploys(term)
if err != nil {
return err
}
_, err = term.Run(
"/bin/sync",
"/sbin/sysctl -w vm.drop_caches=3",
)
if err != nil {
return err
}
onFinishAction := self.GetFinishAction()
if utils.IsInStringArray(onFinishAction, []string{"restart", "shutdown"}) {
err = self.EnsurePowerShutdown(false)
if err != nil {
return err
}
if onFinishAction == "restart" {
err = self.EnsurePowerUp("disk")
if err != nil {
return err
}
}
}
self.Baremetal.AutoSyncAllStatus()
SetTaskComplete(self, result)
return nil
}
+529
View File
@@ -0,0 +1,529 @@
package tasks
import (
"fmt"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/seclib"
o "yunion.io/x/onecloud/pkg/baremetal/options"
"yunion.io/x/onecloud/pkg/baremetal/profiles"
"yunion.io/x/onecloud/pkg/baremetal/utils/detect_storages"
"yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/compute/baremetal"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/ssh"
"yunion.io/x/onecloud/pkg/util/sysutils"
)
type sBaremetalPrepareTask struct {
baremetal IBaremetal
}
func newBaremetalPrepareTask(baremetal IBaremetal) *sBaremetalPrepareTask {
return &sBaremetalPrepareTask{
baremetal: baremetal,
}
}
func (task *sBaremetalPrepareTask) DoPrepare(cli *ssh.Client) error {
_, err := cli.Run("/lib/mos/sysinit.sh")
if err != nil {
return err
}
sysInfo, err := getDMISysinfo(cli)
if err != nil {
return err
}
cpuInfo, err := getCPUInfo(cli)
if err != nil {
return err
}
dmiCPUInfo, err := getDMICPUInfo(cli)
if err != nil {
return err
}
memInfo, err := getDMIMemInfo(cli)
if err != nil {
return err
}
nicsInfo, err := getNicsInfo(cli)
if err != nil {
return err
}
raidDiskInfo, nonRaidDiskInfo, pcieDiskInfo, err := detect_storages.DetectStorageInfo(cli, true)
if err != nil {
return err
}
diskInfo := make([]*baremetal.BaremetalStorage, 0)
diskInfo = append(diskInfo, raidDiskInfo...)
diskInfo = append(diskInfo, nonRaidDiskInfo...)
diskInfo = append(diskInfo, pcieDiskInfo...)
var storageDriver string
if len(raidDiskInfo) > 0 {
raidDrivers := []string{}
for _, drv := range raidDiskInfo {
raidDrivers = append(raidDrivers, drv.Driver)
}
storageDriver = strings.Join(raidDrivers, ",")
} else {
storageDriver = baremetal.DISK_DRIVER_LINUX
}
ipmiEnable, err := isIPMIEnable(cli)
if err != nil {
return err
}
ipmiInfo := &types.SIPMIInfo{
Present: ipmiEnable,
}
// set ipmi nic DHCP
if ipmiEnable {
sshIPMI := ipmitool.NewSSHIPMI(cli)
// ipmitool.SetSysInfo
ipmiSysInfo := sysInfo.ToIPMISystemInfo()
SetIPMILanPortShared(sshIPMI, ipmiSysInfo)
ipmiUser, ipmiPasswd, ipmiIpAddr := task.getIPMIUserPasswd(ipmiSysInfo)
ipmiInfo.Username = ipmiUser
ipmiInfo.Password = ipmiPasswd
var ipmiLanChannel int = -1
for _, lanChannel := range ipmitool.GetLanChannels(ipmiSysInfo) {
log.Infof("Try lan channel %d ...", lanChannel)
conf, err := ipmitool.GetLanConfig(sshIPMI, lanChannel)
if err != nil {
log.Errorf("Get lan channel %d config error: %v", lanChannel, err)
continue
}
if conf.Mac == nil {
log.Errorf("Lan channel %d MAC address is empty", lanChannel)
continue
}
ipmiNic := &types.SNicDevInfo{
Mac: conf.Mac,
Up: false,
Speed: 100,
Mtu: 1500,
}
task.sendNicInfo(ipmiNic, -1, types.NIC_TYPE_IPMI, true, "")
rootId := ipmitool.GetRootId(ipmiSysInfo)
err = ipmitool.SetLanUserPasswd(sshIPMI, lanChannel, rootId, ipmiUser, ipmiPasswd)
if err != nil {
log.Errorf("Lan channel %d set user password error: %v", lanChannel, err)
}
err = ipmitool.EnableLanAccess(sshIPMI, lanChannel)
if err != nil {
log.Errorf("Lan channel %d enable lan access error: %v", lanChannel, err)
}
tryAddrs := make([]string, 0)
if ipmiIpAddr != "" {
tryAddrs = append(tryAddrs, ipmiIpAddr)
}
if conf.IPAddr != "" && conf.IPAddr != ipmiIpAddr {
tryAddrs = append(tryAddrs, conf.IPAddr)
}
if len(tryAddrs) > 0 && !o.Options.ForceDhcpProbeIpmi {
for _, tryAddr := range tryAddrs {
log.Infof("IP addr found in IPMI config, try use %s as IPMI address", tryAddr)
ipConf, err := task.getIPMIIPConfig(tryAddr)
if err != nil {
log.Errorf("Failed to get IPMI ipconfig for %s", tryAddr)
continue
}
err = ipmitool.SetLanStatic(sshIPMI, lanChannel, ipConf.IPAddr, ipConf.Netmask, ipConf.Gateway)
if err != nil {
log.Errorf("Failed to set IPMI static net config %#v for %s", *ipConf, tryAddr)
continue
}
time.Sleep(1 * time.Second)
conf, err = ipmitool.GetLanConfig(sshIPMI, lanChannel)
if err != nil {
log.Errorf("Failed to get lan config after set static network: %v", err)
continue
}
log.Infof("Get lan config %#v", *conf)
if conf.IPAddr == "" || conf.IPAddr != tryAddr {
log.Errorf("Failed to set ipmi lan channel %d static ipaddr", lanChannel)
continue
}
rmcpIPMI := ipmitool.NewLanPlusIPMI(tryAddr, ipmiUser, ipmiPasswd)
conf2, err := ipmitool.GetLanConfig(rmcpIPMI, lanChannel)
if err != nil {
log.Errorf("Failed to get lan channel %d config use RMCP mode: %v", lanChannel, err)
continue
}
if len(conf2.Mac) != 0 &&
conf2.Mac.String() == conf.Mac.String() &&
conf2.IPAddr != "" && conf2.IPAddr == tryAddr {
// make sure the ipaddr is a IPMI address
// enable the netif
if err := task.sendNicInfo(ipmiNic, -1, types.NIC_TYPE_IPMI, false, tryAddr); err != nil {
log.Errorf("Fail to set existing BMC IP address to %s", tryAddr)
} else {
ipmiInfo.IpAddr = tryAddr
ipmiLanChannel = lanChannel
break
}
} else {
log.Errorf("Use RMCP mode get invalid lan config: %#v", conf2)
}
if ipmiLanChannel >= 0 {
// found and set config on lanChannel
break
}
}
}
if len(tryAddrs) > 0 {
task.baremetal.SetExistingIPMIIPAddr(tryAddrs[0])
}
err = ipmitool.SetLanDHCP(sshIPMI, lanChannel)
if err != nil {
log.Errorf("Set lan channel %d dhcp error: %v", lanChannel, err)
}
time.Sleep(1 * time.Second)
nic := task.baremetal.GetIPMINic(conf.Mac)
maxTries := 180 // wait 3 minutes
for tried := 0; nic != nil && nic.IpAddr == "" && tried < maxTries; tried++ {
nic = task.baremetal.GetIPMINic(conf.Mac)
}
if len(nic.IpAddr) == 0 {
err = ipmitool.DoBMCReset(sshIPMI) // do BMC reset to force DHCP request
if err != nil {
log.Errorf("Do BMC reset error: %v", err)
}
}
for tried := 0; nic != nil && nic.IpAddr == "" && tried < maxTries; tried++ {
nic = task.baremetal.GetIPMINic(conf.Mac)
}
if nic != nil && len(nic.IpAddr) == 0 {
log.Errorf("DHCP wait IPMI address fail, retry ...")
continue
}
log.Infof("DHCP get IPMI address succ, wait 2 seconds ...")
var tried int = 0
for tried < maxTries {
time.Sleep(2 * time.Second)
lanConf, err := ipmitool.GetLanConfig(sshIPMI, lanChannel)
if err != nil {
log.Errorf("Get lan config at channel %d error: %v", lanChannel, err)
tried += 2
continue
}
if lanConf.IPAddr == nic.IpAddr {
break
}
log.Infof("waiting IPMI DHCP address %s %s", lanConf.IPAddr, nic.IpAddr)
tried += 2
}
if tried >= maxTries {
continue
}
err = ipmitool.SetLanStatic(
sshIPMI,
lanChannel,
nic.IpAddr,
nic.GetNetMask(),
nic.Gateway,
)
if err != nil {
log.Errorf("Set lanChannel %d static net %#v error: %v", lanChannel, nic, err)
continue
}
ipmiInfo.IpAddr = nic.IpAddr
ipmiLanChannel = lanChannel
}
if ipmiLanChannel == -1 {
return fmt.Errorf("Fail to get IPMI address from DHCP")
}
ipmiInfo.LanChannel = ipmiLanChannel
}
adminNic := task.baremetal.GetAdminNic()
// collect params
updateInfo := make(map[string]interface{})
oname := fmt.Sprintf("BM%s", strings.Replace(adminNic.Mac, ":", "", -1))
if task.baremetal.GetName() == oname {
updateInfo["name"] = fmt.Sprintf("BM-%s", strings.Replace(ipmiInfo.IpAddr, ".", "-", -1))
}
updateInfo["access_ip"] = adminNic.IpAddr
updateInfo["cpu_count"] = cpuInfo.Count
updateInfo["node_count"] = dmiCPUInfo.Nodes
updateInfo["cpu_desc"] = cpuInfo.Model
updateInfo["cpu_mhz"] = cpuInfo.Freq
updateInfo["cpu_cache"] = cpuInfo.Cache
updateInfo["mem_size"] = memInfo.Total
updateInfo["storage_driver"] = storageDriver
updateInfo["storage_info"] = diskInfo
updateInfo["sys_info"] = sysInfo
updateInfo["sn"] = sysInfo.SN
size, diskType := task.collectDiskInfo(diskInfo)
updateInfo["storage_size"] = size
updateInfo["storage_type"] = diskType
updateData := jsonutils.Marshal(updateInfo)
updateData.(*jsonutils.JSONDict).Update(ipmiInfo.ToPrepareParams())
_, err = modules.Hosts.Update(task.getClientSession(), task.baremetal.GetId(), updateData)
if err != nil {
log.Errorf("Update baremetal info error: %v", err)
}
if err := task.sendStorageInfo(size); err != nil {
log.Errorf("sendStorageInfo error: %v", err)
}
for i := range nicsInfo {
if nicsInfo[i].Mac.String() == adminNic.GetMac().String() {
if i != 0 {
nicsInfo = append(nicsInfo[i:], nicsInfo[0:i]...)
}
break
}
}
err = task.removeAllNics()
if err != nil {
return err
}
for i := range nicsInfo {
err = task.sendNicInfo(nicsInfo[i], i, "", false, "")
if err != nil {
log.Errorf("Send nicinfo idx: %d, %#v error: %v", i, nicsInfo[i], err)
}
}
for _, nicInfo := range 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)
}
}
}
log.Infof("Prepare complete")
return nil
}
func (task *sBaremetalPrepareTask) getIPMIUserPasswd(sysInfo *types.SIPMISystemInfo) (string, string, string) {
var (
ipmiUser string
ipmiPasswd string
ipmiIpAddr string
)
ipmiUser = profiles.GetRootName(sysInfo)
isStrongPass := profiles.IsStrongPass(sysInfo)
if !isStrongPass && o.Options.DefaultIpmiPassword != "" {
ipmiPasswd = o.Options.DefaultIpmiPassword
} else if isStrongPass && o.Options.DefaultStrongIpmiPassword != "" {
ipmiPasswd = o.Options.DefaultStrongIpmiPassword
} else if isStrongPass && o.Options.DefaultIpmiPassword != "" {
ipmiPasswd = o.Options.DefaultIpmiPassword
} 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
}
}
return ipmiUser, ipmiPasswd, ipmiIpAddr
}
type ipmiIPConfig struct {
IPAddr string
Netmask string
Gateway string
}
func (task *sBaremetalPrepareTask) getIPMIIPConfig(ipAddr string) (*ipmiIPConfig, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(ipAddr), "ip")
listRet, err := modules.Networks.List(task.getClientSession(), params)
if err != nil {
return nil, err
}
if len(listRet.Data) != 1 {
return nil, fmt.Errorf("Invalid network list count: %d", len(listRet.Data))
}
netObj := listRet.Data[0]
config := &ipmiIPConfig{}
config.IPAddr = ipAddr
maskLen, _ := netObj.Int("guest_ip_mask")
config.Netmask = netutils.Masklen2Mask(int8(maskLen)).String()
config.Gateway, _ = netObj.GetString("guest_gateway")
return config, nil
}
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 {
return nil, err
}
return sysutils.ParseDMISysinfo(ret)
}
func getCPUInfo(cli *ssh.Client) (*types.SCPUInfo, error) {
ret, err := cli.Run("cat /proc/cpuinfo")
if err != nil {
return nil, err
}
return sysutils.ParseCPUInfo(ret)
}
func getDMICPUInfo(cli *ssh.Client) (*types.SDMICPUInfo, error) {
ret, err := cli.Run("/usr/sbin/dmidecode -t 4")
if err != nil {
return nil, err
}
return sysutils.ParseDMICPUInfo(ret), nil
}
func getDMIMemInfo(cli *ssh.Client) (*types.SDMIMemInfo, error) {
ret, err := cli.Run("/usr/sbin/dmidecode -t 17")
if err != nil {
return nil, err
}
return sysutils.ParseDMIMemInfo(ret), nil
}
func getNicsInfo(cli *ssh.Client) ([]*types.SNicDevInfo, error) {
ret, err := cli.Run("/lib/mos/lsnic")
if err != nil {
return nil, fmt.Errorf("Failed to retrieve NIC info: %v", err)
}
return sysutils.ParseNicInfo(ret), nil
}
func isIPMIEnable(cli *ssh.Client) (bool, error) {
ret, err := cli.Run("/usr/sbin/dmidecode -t 38")
if err != nil {
return false, fmt.Errorf("Failed to retrieve IPMI info: %v", err)
}
return sysutils.ParseDMIIPMIInfo(ret), nil
}
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
}
task.baremetal.SaveDesc(resp)
return nil
}
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")
_, err := modules.Hosts.PerformAction(task.getClientSession(), task.baremetal.GetId(), "update-storage", params)
return err
}
func (task *sBaremetalPrepareTask) doNicWireProbe(cli *ssh.Client, nic *types.SNicDevInfo) error {
maxTries := 6
for tried := 0; tried < maxTries; tried++ {
log.Infof("doNicWireProbe %v", nic)
_, err := cli.Run(fmt.Sprintf("/sbin/udhcpc -t 1 -T 3 -n -i %s", nic.Dev))
if err != nil {
log.Errorf("/sbin/udhcpc error: %v", err)
}
nicInfo := task.baremetal.GetNicByMac(nic.Mac)
if nicInfo != nil && nicInfo.WireId != "" {
log.Infof("doNicWireProbe success, get result %#v", nicInfo)
break
}
}
return nil
}
func (task *sBaremetalPrepareTask) collectDiskInfo(diskInfo []*baremetal.BaremetalStorage) (int64, string) {
cnt := 0
rotateCnt := 0
var size int64 = 0
var diskType string
for _, d := range diskInfo {
if d.Rotate {
rotateCnt += 1
}
size += d.Size
cnt += 1
}
if rotateCnt == cnt {
diskType = "rotate"
} else if rotateCnt == 0 {
diskType = "ssd"
} else {
diskType = "hybrid"
}
return size, diskType
}
func SetIPMILanPortShared(cli ipmitool.IPMIExecutor, sysInfo *types.SIPMISystemInfo) {
if !o.Options.IpmiLanPortShared {
return
}
oemName := strings.ToLower(sysInfo.Manufacture)
var err error
if strings.Contains(oemName, "huawei") {
err = ipmitool.SetHuaweiIPMILanPortShared(cli)
} else if strings.Contains(oemName, "dell") {
err = ipmitool.SetDellIPMILanPortShared(cli)
}
if err != nil {
log.Errorf("Set %s ipmi lan port shared failed: %v", oemName, err)
}
}
+68
View File
@@ -0,0 +1,68 @@
package tasks
import (
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type SBaremetalServerCreateTask struct {
*SBaremetalServerBaseDeployTask
}
func NewBaremetalServerCreateTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
task := new(SBaremetalServerCreateTask)
baseTask, err := newBaremetalServerBaseDeployTask(baremetal, taskId, data, task)
task.SBaremetalServerBaseDeployTask = baseTask
return task, err
}
func (self *SBaremetalServerCreateTask) GetName() string {
return "BaremetalServerCreateTask"
}
func (self *SBaremetalServerCreateTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
// Build raid
err := self.Baremetal.GetServer().DoDiskConfig(term)
if err != nil {
return nil, self.onError(term, err)
}
time.Sleep(2 * time.Second)
if err := self.Baremetal.GetServer().DoEraseDisk(term); err != nil {
return nil, self.onError(term, err)
}
time.Sleep(2 * time.Second)
parts, err := self.Baremetal.GetServer().DoPartitionDisk(term)
if err != nil {
return nil, self.onError(term, err)
}
data := jsonutils.NewDict()
disks, err := self.Baremetal.GetServer().SyncPartitionSize(term, parts)
if err != nil {
return nil, self.onError(term, err)
}
data.Add(jsonutils.Marshal(disks), "disks")
deployInfo, err := self.Baremetal.GetServer().DoDeploy(term, self.data, true)
if err != nil {
return nil, self.onError(term, err)
}
if deployInfo != nil {
data.Update(deployInfo)
}
return data, nil
}
func (self *SBaremetalServerCreateTask) onError(term *ssh.Client, err error) error {
if err1 := self.Baremetal.GetServer().DoEraseDisk(term); err1 != nil {
log.Warningf("EraseDisk error: %v", err1)
}
self.Baremetal.AutoSyncStatus()
return err
}
+30
View File
@@ -0,0 +1,30 @@
package tasks
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type SBaremetalServerDeployTask struct {
*SBaremetalServerBaseDeployTask
}
func NewBaremetalServerDeployTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
task := new(SBaremetalServerDeployTask)
baseTask, err := newBaremetalServerBaseDeployTask(baremetal, taskId, data, task)
task.SBaremetalServerBaseDeployTask = baseTask
return task, err
}
func (self *SBaremetalServerDeployTask) GetName() string {
return "BaremetalServerDeployTask"
}
func (self *SBaremetalServerDeployTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
return self.Baremetal.GetServer().DoDeploy(term, self.data, false)
}
+38
View File
@@ -0,0 +1,38 @@
package tasks
import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type SBaremetalServerDestroyTask struct {
*SBaremetalServerBaseDeployTask
}
func NewBaremetalServerDestroyTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
task := new(SBaremetalServerDestroyTask)
baseTask, err := newBaremetalServerBaseDeployTask(baremetal, taskId, data, task)
task.SBaremetalServerBaseDeployTask = baseTask
return task, err
}
func (self *SBaremetalServerDestroyTask) GetName() string {
return "BaremetalServerDestroyTask"
}
func (self *SBaremetalServerDestroyTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
if err := self.Baremetal.GetServer().DoEraseDisk(term); err != nil {
log.Errorf("Delete server do erase disk: %v", err)
}
if err := self.Baremetal.GetServer().DoDiskUnconfig(term); err != nil {
log.Errorf("Baremetal do disk unconfig: %v", err)
}
self.Baremetal.RemoveServer()
return nil, nil
}
+1
View File
@@ -0,0 +1 @@
package tasks // import "yunion.io/x/onecloud/pkg/baremetal/tasks"
+40
View File
@@ -0,0 +1,40 @@
package tasks
import (
"net"
"yunion.io/x/jsonutils"
baremetaltypes "yunion.io/x/onecloud/pkg/baremetal/types"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/mcclient"
)
type IBaremetal interface {
GetId() string
GetZoneId() string
GetTaskQueue() *TaskQueue
GetSSHConfig() (*types.SSHConfig, error)
TestSSHConfig() bool
GetAdminNic() *types.SNic
GetName() string
GetClientSession() *mcclient.ClientSession
SaveDesc(desc jsonutils.JSONObject) error
GetNicByMac(net.HardwareAddr) *types.SNic
GetRawIPMIConfig() *types.SIPMIInfo
GetIPMINic(mac net.HardwareAddr) *types.SNic
SetExistingIPMIIPAddr(ipAddr string)
GetServer() baremetaltypes.IBaremetalServer
SyncStatus(status, reason string)
AutoSyncStatus()
SyncAllStatus(status string)
AutoSyncAllStatus()
GetPowerStatus() (string, error)
DoPowerShutdown(soft bool) error
DoPXEBoot() error
DoDiskBoot() error
RemoveServer()
}
+40
View File
@@ -0,0 +1,40 @@
package tasks
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type SBaremetalMaintenanceTask struct {
*SBaremetalPXEBootTaskBase
}
func NewBaremetalMaintenanceTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
task := new(SBaremetalMaintenanceTask)
baseTask := newBaremetalPXEBootTaskBase(baremetal, taskId, data)
task.SBaremetalPXEBootTaskBase = baseTask
_, err := baseTask.InitPXEBootTask(task, data)
return task, err
}
func (self *SBaremetalMaintenanceTask) OnPXEBoot(ctx context.Context, term *ssh.Client, args interface{}) error {
sshConfig := term.GetConfig()
dataObj := map[string]interface{}{
"username": sshConfig.Username,
"password": sshConfig.Password,
"ip": sshConfig.Host,
}
if jsonutils.QueryBoolean(self.data, "guest_running", false) {
dataObj["guest_running"] = true
}
self.Baremetal.AutoSyncStatus()
SetTaskComplete(self, jsonutils.Marshal(dataObj))
return nil
}
+46
View File
@@ -0,0 +1,46 @@
package tasks
import (
"context"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/baremetal/status"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type SBaremetalServerPrepareTask struct {
*SBaremetalTaskBase
}
func NewBaremetalServerPrepareTask(
baremetal IBaremetal,
) *SBaremetalServerPrepareTask {
baseTask := newBaremetalTaskBase(baremetal, "", nil)
task := &SBaremetalServerPrepareTask{
SBaremetalTaskBase: baseTask,
}
task.SetSSHStage(task.OnPXEBootRequest)
return task
}
func (self *SBaremetalServerPrepareTask) NeedPXEBoot() bool {
return true
}
func (self *SBaremetalServerPrepareTask) GetName() string {
return "BaremetalServerPrepareTask"
}
// OnPXEBootRequest called by notify api handler
func (self *SBaremetalServerPrepareTask) OnPXEBootRequest(ctx context.Context, cli *ssh.Client, args interface{}) error {
err := newBaremetalPrepareTask(self.Baremetal).DoPrepare(cli)
if err != nil {
log.Errorf("Prepare failed: %v", err)
self.Baremetal.SyncStatus(status.PREPARE_FAIL, err.Error())
return err
}
self.Baremetal.AutoSyncStatus()
SetTaskComplete(self, nil)
return nil
}
+47
View File
@@ -0,0 +1,47 @@
package tasks
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type SBaremetalServerRebuildTask struct {
*SBaremetalServerBaseDeployTask
}
func NewBaremetalServerRebuildTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
task := new(SBaremetalServerRebuildTask)
baseTask, err := newBaremetalServerBaseDeployTask(baremetal, taskId, data, task)
task.SBaremetalServerBaseDeployTask = baseTask
return task, err
}
func (self *SBaremetalServerRebuildTask) GetName() string {
return "BaremetalServerRebuildTask"
}
func (self *SBaremetalServerRebuildTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
parts, err := self.Baremetal.GetServer().DoRebuildRootDisk(term)
if err != nil {
return nil, fmt.Errorf("Rebuild root disk: %v", err)
}
disks, err := self.Baremetal.GetServer().SyncPartitionSize(term, parts)
if err != nil {
return nil, fmt.Errorf("SyncPartitionSize: %v", err)
}
data := jsonutils.NewDict()
data.Add(jsonutils.NewArray(disks...), "disks")
deployInfo, err := self.Baremetal.GetServer().DoDeploy(term, data, false)
if err != nil {
return nil, fmt.Errorf("DoDeploy: %v", err)
}
data.Update(deployInfo)
return data, nil
}
+32
View File
@@ -0,0 +1,32 @@
package tasks
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type SBaremetalReprepareTask struct {
*SBaremetalServerBaseDeployTask
}
func NewBaremetalReprepareTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
task := new(SBaremetalReprepareTask)
baseTask, err := newBaremetalServerBaseDeployTask(baremetal, taskId, data, task)
task.SBaremetalServerBaseDeployTask = baseTask
return task, err
}
func (self *SBaremetalReprepareTask) GetName() string {
return "BaremetalReprepareTask"
}
func (self *SBaremetalReprepareTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
task := newBaremetalPrepareTask(self.Baremetal)
err := task.DoPrepare(term)
return nil, err
}
+67
View File
@@ -0,0 +1,67 @@
package tasks
import (
"context"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type SBaremetalResetBMCTask struct {
*SBaremetalPXEBootTaskBase
term *ssh.Client
}
func NewBaremetalResetBMCTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
task := new(SBaremetalResetBMCTask)
baseTask := newBaremetalPXEBootTaskBase(baremetal, taskId, data)
task.SBaremetalPXEBootTaskBase = baseTask
_, err := baseTask.InitPXEBootTask(task, data)
return task, err
}
func (self *SBaremetalResetBMCTask) GetName() string {
return "BaremetalResetBMCTask"
}
func (self *SBaremetalResetBMCTask) GetIPMITool() *ipmitool.SSHIPMI {
return ipmitool.NewSSHIPMI(self.term)
}
func (self *SBaremetalResetBMCTask) OnPXEBoot(ctx context.Context, term *ssh.Client, args interface{}) error {
self.term = term
self.SetStage(self.WaitForBMCReady)
err := ipmitool.DoBMCReset(self.GetIPMITool())
if err != nil {
return err
}
time.Sleep(10 * time.Second)
ExecuteTask(self, nil)
return nil
}
func (self *SBaremetalResetBMCTask) WaitForBMCReady(ctx context.Context, args interface{}) error {
self.SetStage(self.OnBMCReady)
status, err := ipmitool.GetChassisPowerStatus(self.GetIPMITool())
if err != nil {
return err
}
if status != "" && status == types.POWER_STATUS_ON {
ExecuteTask(self, nil)
}
return nil
}
func (self *SBaremetalResetBMCTask) OnBMCReady(ctx context.Context, args interface{}) error {
time.Sleep(20 * time.Second)
SetTaskComplete(self, nil)
return nil
}
+55
View File
@@ -0,0 +1,55 @@
package tasks
import (
"context"
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
)
type SBaremetalServerStartTask struct {
*SBaremetalTaskBase
}
func NewBaremetalServerStartTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
baseTask := newBaremetalTaskBase(baremetal, taskId, data)
self := &SBaremetalServerStartTask{
SBaremetalTaskBase: baseTask,
}
if err := self.Baremetal.DoDiskBoot(); err != nil {
return nil, fmt.Errorf("DoDiskBoot: %v", err)
}
self.SetStage(self.WaitForStart)
ExecuteTask(self, nil)
return self, nil
}
func (self *SBaremetalServerStartTask) GetName() string {
return "BaremetalServerStartTask"
}
func (self *SBaremetalServerStartTask) WaitForStart(ctx context.Context, args interface{}) error {
self.SetStage(self.OnStartComplete)
status, err := self.Baremetal.GetPowerStatus()
if err != nil {
return fmt.Errorf("GetPowerStatus: %v", err)
}
log.Infof("%s WaitForStart status=%s", self.GetName(), status)
if status == types.POWER_STATUS_ON {
ExecuteTask(self, nil)
}
return nil
}
func (self *SBaremetalServerStartTask) OnStartComplete(ctx context.Context, args interface{}) error {
self.Baremetal.SyncAllStatus(types.POWER_STATUS_ON)
SetTaskComplete(self, nil)
return nil
}
+59
View File
@@ -0,0 +1,59 @@
package tasks
import (
"context"
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
)
type SBaremetalServerStopTask struct {
*SBaremetalTaskBase
startTime time.Time
}
func NewBaremetalServerStopTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
baseTask := newBaremetalTaskBase(baremetal, taskId, data)
self := &SBaremetalServerStopTask{
SBaremetalTaskBase: baseTask,
}
if err := self.Baremetal.DoPowerShutdown(true); err != nil {
return nil, fmt.Errorf("Do power shutdown error: %v", err)
}
self.startTime = time.Now()
self.SetStage(self.WaitForStop)
ExecuteTask(self, nil)
return self, nil
}
func (self *SBaremetalServerStopTask) GetName() string {
return "BaremetalServerStopTask"
}
func (self *SBaremetalServerStopTask) WaitForStop(ctx context.Context, args interface{}) error {
self.SetStage(self.OnStopComplete)
status, err := self.Baremetal.GetPowerStatus()
if err != nil {
return fmt.Errorf("GetPowerStatus: %v", err)
}
if status == types.POWER_STATUS_OFF {
ExecuteTask(self, nil)
} else if time.Since(self.startTime) >= 90*time.Second {
if err := self.Baremetal.DoPowerShutdown(false); err != nil {
return err
}
}
return nil
}
func (self *SBaremetalServerStopTask) OnStopComplete(ctx context.Context, args interface{}) error {
SetTaskComplete(self, nil)
return nil
}
+68
View File
@@ -0,0 +1,68 @@
package tasks
import (
"context"
"fmt"
"yunion.io/x/jsonutils"
baremetalstatus "yunion.io/x/onecloud/pkg/baremetal/status"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
)
type SBaremetalUnmaintenanceTask struct {
*SBaremetalTaskBase
}
func NewBaremetalUnmaintenanceTask(
baremetal IBaremetal,
taskId string,
data jsonutils.JSONObject,
) (ITask, error) {
task := new(SBaremetalUnmaintenanceTask)
baseTask := newBaremetalTaskBase(baremetal, taskId, data)
task.SBaremetalTaskBase = baseTask
var err error
if jsonutils.QueryBoolean(task.data, "guest_running", false) {
err = task.EnsurePowerShutdown(false)
if err != nil {
return task, fmt.Errorf("EnsurePowerShutdown hard: %v", err)
}
err = task.EnsurePowerUp("disk")
if err != nil {
return task, fmt.Errorf("EnsurePowerUp disk: %v", err)
}
task.Baremetal.SyncStatus(baremetalstatus.RUNNING, "")
SetTaskComplete(task, nil)
return task, nil
}
task.SetStage(task.WaitForStop)
err = task.EnsurePowerShutdown(true)
if err != nil {
return task, fmt.Errorf("EnsurePowerShutdown soft: %v", err)
}
ExecuteTask(task, nil)
return task, nil
}
func (self *SBaremetalUnmaintenanceTask) WaitForStop(ctx context.Context, args interface{}) error {
status, err := self.Baremetal.GetPowerStatus()
if err != nil {
return err
}
self.SetStage(self.OnStopComplete)
if status == types.POWER_STATUS_OFF {
ExecuteTask(self, nil)
}
return nil
}
func (self *SBaremetalUnmaintenanceTask) OnStopComplete(ctx context.Context, args interface{}) error {
self.Baremetal.SyncStatus(baremetalstatus.READY, "")
SetTaskComplete(self, nil)
return nil
}
func (self *SBaremetalUnmaintenanceTask) GetName() string {
return "BaremetalUnmaintenanceTask"
}
+87
View File
@@ -0,0 +1,87 @@
package tasks
import (
"context"
"fmt"
"runtime/debug"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
var baremetalTaskWorkerMan *appsrv.SWorkerManager
func init() {
baremetalTaskWorkerMan = appsrv.NewWorkerManager("BaremetalTaskWorkerManager", 8, 1024, false)
}
func GetWorkManager() *appsrv.SWorkerManager {
return baremetalTaskWorkerMan
}
func OnStop() {
for GetWorkManager().ActiveWorkerCount() > 0 {
log.Warningf("Busy workers count %d, waiting them finish", GetWorkManager().ActiveWorkerCount())
time.Sleep(5 * time.Second)
}
}
func ExecuteTask(task ITask, args interface{}) {
baremetalTaskWorkerMan.Run(func() {
executeTask(task, args)
}, nil, nil)
}
func executeTask(task ITask, args interface{}) {
if task == nil {
return
}
curStage := task.GetStage()
if curStage == nil {
return
}
defer func() {
if err := recover(); err != nil {
log.Errorf("Execute task panic: %v", err)
debug.PrintStack()
SetTaskFail(task, fmt.Errorf("%v", err))
}
}()
err := curStage(context.Background(), args)
if err != nil {
log.Errorf("Execute task %s error: %v", task.GetName(), err)
SetTaskFail(task, err)
}
}
func SetTaskComplete(task ITask, data jsonutils.JSONObject) {
taskId := task.GetTaskId()
if taskId != "" {
session := task.GetClientSession()
modules.ComputeTasks.TaskComplete(session, taskId, data)
}
onTaskEnd(task)
}
func SetTaskFail(task ITask, err error) {
taskId := task.GetTaskId()
if taskId != "" {
session := task.GetClientSession()
modules.ComputeTasks.TaskFailed(session, taskId, err)
}
onTaskEnd(task)
}
func onTaskEnd(task ITask) {
task.SetStage(nil)
ExecuteTask(task.GetTaskQueue().PopTask(), nil)
}
func OnInitStage(task ITask) error {
log.Infof("Start task %s", task.GetName())
return nil
}
+1
View File
@@ -0,0 +1 @@
package types // import "yunion.io/x/onecloud/pkg/baremetal/types"
+26
View File
@@ -0,0 +1,26 @@
package types
import (
"net"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/baremetal/utils/disktool"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/util/ssh"
)
type IBaremetalServer interface {
GetName() string
GetId() string
RemoveDesc()
DoDiskUnconfig(term *ssh.Client) error
DoDiskConfig(term *ssh.Client) error
DoEraseDisk(term *ssh.Client) error
DoPartitionDisk(term *ssh.Client) ([]*disktool.Partition, error)
DoRebuildRootDisk(term *ssh.Client) ([]*disktool.Partition, error)
SyncPartitionSize(term *ssh.Client, parts []*disktool.Partition) ([]jsonutils.JSONObject, error)
DoDeploy(term *ssh.Client, data jsonutils.JSONObject, isInit bool) (jsonutils.JSONObject, error)
SaveDesc(desc jsonutils.JSONObject) error
GetNicByMac(mac net.HardwareAddr) *types.SNic
}

Some files were not shown because too many files have changed in this diff Show More