mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
feature: google云操作支持
This commit is contained in:
@@ -661,6 +661,23 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&options.SGoogleCloudAccountUpdateCredentialOptions{}, "cloud-account-update-credential-google", "Update credential of a Google cloud account", func(s *mcclient.ClientSession, args *options.SGoogleCloudAccountUpdateCredentialOptions) error {
|
||||
data, err := ioutil.ReadFile(args.GoogleJsonFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params, err := jsonutils.Parse(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.Cloudaccounts.PerformAction(s, args.ID, "update-credential", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&options.SS3CloudAccountUpdateCredentialOptions{}, "cloud-account-update-credential-s3", "Update credential of a generic S3 cloud account", func(s *mcclient.ClientSession, args *options.SS3CloudAccountUpdateCredentialOptions) error {
|
||||
params := jsonutils.Marshal(args)
|
||||
result, err := modules.Cloudaccounts.PerformAction(s, args.ID, "update-credential", params)
|
||||
|
||||
@@ -38,7 +38,7 @@ func init() {
|
||||
Occupied bool `help:"show occupid host" json:"-"`
|
||||
Enabled bool `help:"Show enabled host only" json:"-"`
|
||||
Disabled bool `help:"Show disabled host only" json:"-"`
|
||||
HostType string `help:"Host type filter" choices:"baremetal|hypervisor|esxi|kubelet|hyperv|aliyun|azure|qcloud|aws|huawei|ucloud"`
|
||||
HostType string `help:"Host type filter" choices:"baremetal|hypervisor|esxi|kubelet|hyperv|aliyun|azure|qcloud|aws|huawei|ucloud|google"`
|
||||
AnyMac string `help:"Mac matches one of the host's interface"`
|
||||
|
||||
IsBaremetal *bool `help:"filter host list by is_baremetal=true|false"`
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
)
|
||||
|
||||
type GeneralUsageOptions struct {
|
||||
HostType []string `help:"Host types" choices:"hypervisor|baremetal|esxi|xen|kubelet|hyperv|aliyun|azure|aws|huawei|qcloud|openstack|ucloud|zstack"`
|
||||
Provider []string `help:"Provider" choices:"OneCloud|VMware|Aliyun|Azure|Aws|Qcloud|Huawei|OpenStack|Ucloud|ZStack"`
|
||||
Brand []string `help:"Brands" choices:"OneCloud|VMware|Aliyun|Azure|Aws|Qcloud|Huawei|OpenStack|Ucloud|ZStack|DStack"`
|
||||
HostType []string `help:"Host types" choices:"hypervisor|baremetal|esxi|xen|kubelet|hyperv|aliyun|azure|aws|huawei|qcloud|openstack|ucloud|zstack|google"`
|
||||
Provider []string `help:"Provider" choices:"OneCloud|VMware|Aliyun|Azure|Aws|Qcloud|Huawei|OpenStack|Ucloud|ZStack|Google"`
|
||||
Brand []string `help:"Brands" choices:"OneCloud|VMware|Aliyun|Azure|Aws|Qcloud|Huawei|OpenStack|Ucloud|ZStack|DStack|Google"`
|
||||
Project string `help:"show usage of specified project"`
|
||||
|
||||
ProjectDomain string `help:"show usage of specified domain"`
|
||||
|
||||
@@ -110,6 +110,7 @@ var HOST_TYPES = []string{
|
||||
HOST_TYPE_UCLOUD,
|
||||
HOST_TYPE_ZSTACK,
|
||||
HOST_TYPE_CTYUN,
|
||||
HOST_TYPE_GOOGLE,
|
||||
}
|
||||
|
||||
var NIC_TYPES = []string{NIC_TYPE_IPMI, NIC_TYPE_ADMIN}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
package cloudprovider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/util/osprofile"
|
||||
|
||||
@@ -34,8 +32,9 @@ type SDiskInfo struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CLOUD_SHELL = "cloud-shell"
|
||||
CLOUD_CONFIG = "cloud-config"
|
||||
CLOUD_SHELL = "cloud-shell"
|
||||
CLOUD_SHELL_WITHOUT_ENCRYPT = "cloud-shell-without-encrypt"
|
||||
CLOUD_CONFIG = "cloud-config"
|
||||
)
|
||||
|
||||
type SManagedVMCreateConfig struct {
|
||||
@@ -69,6 +68,15 @@ type SManagedVMChangeConfig struct {
|
||||
InstanceType string
|
||||
}
|
||||
|
||||
type SManagedVMRebuildRootConfig struct {
|
||||
Account string
|
||||
Password string
|
||||
ImageId string
|
||||
PublicKey string
|
||||
SysSizeGB int
|
||||
OsType string
|
||||
}
|
||||
|
||||
func (vmConfig *SManagedVMCreateConfig) GetConfig(config *jsonutils.JSONDict) error {
|
||||
if err := config.Unmarshal(vmConfig, "desc"); err != nil {
|
||||
return err
|
||||
@@ -120,9 +128,6 @@ func generateUserData(adminPublicKey, projectPublicKey, oUserData string) string
|
||||
}
|
||||
|
||||
func (vmConfig *SManagedVMCreateConfig) InjectPasswordByCloudInit() error {
|
||||
if vmConfig.OsType != osprofile.OS_TYPE_LINUX {
|
||||
return fmt.Errorf("Only support inject Linux password, current osType is %s", vmConfig.OsType)
|
||||
}
|
||||
loginUser := cloudinit.NewUser(vmConfig.Account)
|
||||
loginUser.SudoPolicy(cloudinit.USER_SUDO_NOPASSWD)
|
||||
if len(vmConfig.PublicKey) > 0 {
|
||||
|
||||
@@ -258,6 +258,8 @@ type ICloudVM interface {
|
||||
// GetStatus() string
|
||||
// GetRemoteStatus() string
|
||||
|
||||
GetSerialOutput(port int) (string, error) // 目前仅谷歌云windows机器会使用到此接口
|
||||
|
||||
GetVcpuCount() int
|
||||
GetVmemSizeMB() int //MB
|
||||
GetBootOrder() string
|
||||
@@ -285,7 +287,7 @@ type ICloudVM interface {
|
||||
|
||||
UpdateUserData(userData string) error
|
||||
|
||||
RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error)
|
||||
RebuildRoot(ctx context.Context, config *SManagedVMRebuildRootConfig) (string, error)
|
||||
|
||||
DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error
|
||||
|
||||
|
||||
@@ -274,6 +274,10 @@ func (self *SBaseGuestDriver) RemoteDeployGuestForCreate(ctx context.Context, us
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SBaseGuestDriver) RemoteActionAfterGuestCreated(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, ivm cloudprovider.ICloudVM, desc *cloudprovider.SManagedVMCreateConfig) {
|
||||
return
|
||||
}
|
||||
|
||||
func (self *SBaseGuestDriver) RemoteDeployGuestForDeploy(ctx context.Context, guest *models.SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -15,10 +15,26 @@
|
||||
package guestdrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/osprofile"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/google"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
@@ -56,3 +72,189 @@ func (self *SGoogleGuestDriver) GetDefaultSysDiskBackend() string {
|
||||
func (self *SGoogleGuestDriver) GetMinimalSysDiskSizeGb() int {
|
||||
return 10
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) GetStorageTypes() []string {
|
||||
return []string{
|
||||
api.STORAGE_GOOGLE_PD_SSD,
|
||||
api.STORAGE_GOOGLE_PD_STANDARD,
|
||||
api.STORAGE_GOOGLE_LOCAL_SSD,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
return self.chooseHostStorage(self, host, backend, storageIds)
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) GetGuestInitialStateAfterCreate() string {
|
||||
return api.VM_RUNNING
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) GetAttachDiskStatus() ([]string, error) {
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) GetRebuildRootStatus() ([]string, error) {
|
||||
return []string{api.VM_READY}, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) GetChangeConfigStatus() ([]string, error) {
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) GetDeployStatus() ([]string, error) {
|
||||
return []string{api.VM_READY}, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error {
|
||||
if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING}) {
|
||||
return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status)
|
||||
}
|
||||
if !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD}) {
|
||||
return fmt.Errorf("Cannot resize %s disk", storage.StorageType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) {
|
||||
input, err := self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(input.Networks) > 2 {
|
||||
return nil, httperrors.NewInputParameterError("cannot support more than 1 nic")
|
||||
}
|
||||
localDisk := 0
|
||||
for i, disk := range input.Disks {
|
||||
minGB := -1
|
||||
maxGB := -1
|
||||
switch disk.Backend {
|
||||
case api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD:
|
||||
minGB = 10
|
||||
maxGB = 65536
|
||||
case api.STORAGE_GOOGLE_LOCAL_SSD:
|
||||
minGB = 375
|
||||
maxGB = 375
|
||||
localDisk++
|
||||
}
|
||||
if i == 0 && disk.Backend == api.STORAGE_GOOGLE_LOCAL_SSD {
|
||||
return nil, httperrors.NewInputParameterError("System disk does not support %s disk", disk.Backend)
|
||||
}
|
||||
if disk.SizeMb < minGB*1024 || disk.SizeMb > maxGB*1024 {
|
||||
return nil, httperrors.NewInputParameterError("The %s disk size must be in the range of %dGB ~ %dGB", disk.Backend, minGB, maxGB)
|
||||
}
|
||||
}
|
||||
if localDisk > 8 {
|
||||
return nil, httperrors.NewInputParameterError("%s disk cannot exceed 8", api.STORAGE_GOOGLE_LOCAL_SSD)
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) GetGuestInitialStateAfterRebuild() string {
|
||||
return api.VM_READY
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// 谷歌云的用户自定义脚本不支持base64加密
|
||||
func (self *SGoogleGuestDriver) GetUserDataType() string {
|
||||
return cloudprovider.CLOUD_SHELL_WITHOUT_ENCRYPT
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) RequestStartOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) (jsonutils.JSONObject, error) {
|
||||
ihost, err := host.GetIHost()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "host.GetIHost")
|
||||
}
|
||||
|
||||
ivm, err := ihost.GetIVMById(guest.GetExternalId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ihost.GetIVMById")
|
||||
}
|
||||
|
||||
result := jsonutils.NewDict()
|
||||
if ivm.GetStatus() != api.VM_RUNNING {
|
||||
err := ivm.StartVM(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ivm.StartVM")
|
||||
}
|
||||
vm := ivm.(*google.SInstance)
|
||||
updateUserdata := false
|
||||
for _, item := range vm.Metadata.Items {
|
||||
if item.Key == google.METADATA_STARTUP_SCRIPT || item.Key == google.METADATA_STARTUP_SCRIPT_POWER_SHELL {
|
||||
updateUserdata = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if updateUserdata {
|
||||
keyword := "Finished running startup scripts"
|
||||
err = cloudprovider.Wait(time.Second*5, time.Minute*6, func() (bool, error) {
|
||||
output, err := ivm.GetSerialOutput(1)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "iVM.GetSerialOutput")
|
||||
}
|
||||
log.Debugf("wait for google startup scripts finish")
|
||||
if strings.Contains(output, keyword) {
|
||||
log.Debugf(keyword)
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed wait google cloud startup scripts finish err: %v", err)
|
||||
}
|
||||
log.Debugf("clean google instance %s(%s) startup-script", guest.Name, guest.Id)
|
||||
err := ivm.UpdateUserData("")
|
||||
if err != nil {
|
||||
log.Errorf("failed to update google userdata")
|
||||
}
|
||||
}
|
||||
task.ScheduleRun(result)
|
||||
} else {
|
||||
result.Add(jsonutils.NewBool(true), "is_running")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) RemoteActionAfterGuestCreated(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, iVM cloudprovider.ICloudVM, desc *cloudprovider.SManagedVMCreateConfig) {
|
||||
keywords := map[string]string{
|
||||
strings.ToLower(osprofile.OS_TYPE_WINDOWS): "Finished with sysprep specialize phase",
|
||||
strings.ToLower(osprofile.OS_TYPE_LINUX): "Finished running startup scripts",
|
||||
}
|
||||
if keyword, ok := keywords[strings.ToLower(desc.OsType)]; ok {
|
||||
err := cloudprovider.Wait(time.Second*5, time.Minute*6, func() (bool, error) {
|
||||
output, err := iVM.GetSerialOutput(1)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "iVM.GetSerialOutput")
|
||||
}
|
||||
log.Debugf("wait for google sysprep finish")
|
||||
if strings.Contains(output, keyword) {
|
||||
log.Debugf(keyword)
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed wait google %s error: %v", keyword, err)
|
||||
}
|
||||
}
|
||||
log.Debugf("clean google instance %s(%s) startup-script", guest.Name, guest.Id)
|
||||
err := iVM.UpdateUserData("")
|
||||
if err != nil {
|
||||
log.Errorf("failed to update google userdata")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) AllowReconfigGuest() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/osprofile"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
@@ -153,7 +155,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestDetachDisk(ctx context.Contex
|
||||
iVM, err := guest.GetIVM()
|
||||
if err != nil {
|
||||
//若guest被删除,忽略错误,否则会无限删除guest失败(有挂载的云盘)
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrapf(err, "guest.GetIVM")
|
||||
@@ -338,9 +340,14 @@ func (self *SManagedVirtualizedGuestDriver) RequestDeployGuestOnHost(ctx context
|
||||
switch guest.GetDriver().GetUserDataType() {
|
||||
case cloudprovider.CLOUD_SHELL:
|
||||
desc.UserData = oUserData.UserDataScriptBase64()
|
||||
case cloudprovider.CLOUD_SHELL_WITHOUT_ENCRYPT:
|
||||
desc.UserData = oUserData.UserDataScript()
|
||||
default:
|
||||
desc.UserData = oUserData.UserDataBase64()
|
||||
}
|
||||
if strings.ToLower(desc.OsType) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) {
|
||||
desc.UserData = oUserData.UserDataPowerShell()
|
||||
}
|
||||
}
|
||||
|
||||
action, err := config.GetString("action")
|
||||
@@ -396,7 +403,11 @@ func (self *SManagedVirtualizedGuestDriver) GetGuestInitialStateAfterRebuild() s
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizedGuestDriver) GetLinuxDefaultAccount(desc cloudprovider.SManagedVMCreateConfig) string {
|
||||
return "root"
|
||||
userName := "root"
|
||||
if strings.ToLower(desc.OsType) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) {
|
||||
userName = "Administrator"
|
||||
}
|
||||
return userName
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error) {
|
||||
@@ -467,6 +478,8 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx conte
|
||||
return nil, err
|
||||
}
|
||||
|
||||
guest.GetDriver().RemoteActionAfterGuestCreated(ctx, userCred, guest, host, iVM, &desc)
|
||||
|
||||
data := fetchIVMinfo(desc, iVM, guest.Id, desc.Account, desc.Password, desc.PublicKey, "create")
|
||||
return data, nil
|
||||
}
|
||||
@@ -541,7 +554,15 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForRebuildRoot(ctx
|
||||
lockman.LockObject(ctx, guest)
|
||||
defer lockman.ReleaseObject(ctx, guest)
|
||||
|
||||
return iVM.RebuildRoot(ctx, desc.ExternalImageId, desc.Password, desc.PublicKey, desc.SysDisk.SizeGB)
|
||||
conf := cloudprovider.SManagedVMRebuildRootConfig{
|
||||
Account: desc.Account,
|
||||
ImageId: desc.ExternalImageId,
|
||||
Password: desc.Password,
|
||||
PublicKey: desc.PublicKey,
|
||||
SysSizeGB: desc.SysDisk.SizeGB,
|
||||
OsType: desc.OsType,
|
||||
}
|
||||
return iVM.RebuildRoot(ctx, &conf)
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -610,7 +631,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestUndeployGuestOnHost(ctx conte
|
||||
|
||||
ivm, err := ihost.GetIVMById(guest.ExternalId)
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
log.Errorf("ihost.GetIVMById fail %s", err)
|
||||
@@ -626,7 +647,7 @@ func (self *SManagedVirtualizedGuestDriver) RequestUndeployGuestOnHost(ctx conte
|
||||
if disk := guestdisk.GetDisk(); disk != nil && disk.AutoDelete {
|
||||
idisk, err := disk.GetIDisk()
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
continue
|
||||
}
|
||||
log.Errorf("disk.GetIDisk fail %s", err)
|
||||
|
||||
@@ -199,7 +199,15 @@ func (self *SOpenStackGuestDriver) RemoteDeployGuestForRebuildRoot(ctx context.C
|
||||
}
|
||||
storage := sysDisk.GetStorage()
|
||||
if storage.StorageType == api.STORAGE_OPENSTACK_NOVA { //不通过镜像创建磁盘的机器
|
||||
return iVM.RebuildRoot(ctx, desc.ExternalImageId, desc.Password, desc.PublicKey, desc.SysDisk.SizeGB)
|
||||
conf := cloudprovider.SManagedVMRebuildRootConfig{
|
||||
Account: desc.Account,
|
||||
ImageId: desc.ExternalImageId,
|
||||
Password: desc.Password,
|
||||
PublicKey: desc.PublicKey,
|
||||
SysSizeGB: desc.SysDisk.SizeGB,
|
||||
OsType: desc.OsType,
|
||||
}
|
||||
return iVM.RebuildRoot(ctx, &conf)
|
||||
}
|
||||
|
||||
iDisks, err := iVM.GetIDisks()
|
||||
|
||||
@@ -72,7 +72,7 @@ func (self *SAliyunHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb
|
||||
func (self *SAliyunHostDriver) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, guests []models.SGuest, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
for _, guest := range guests {
|
||||
if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY}) {
|
||||
return nil, httperrors.NewBadGatewayError("Aliyun reset disk required guest status is running or read")
|
||||
return nil, httperrors.NewBadGatewayError("Aliyun reset disk required guest status is running or ready")
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
|
||||
@@ -15,8 +15,16 @@
|
||||
package hostdrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
type SGoogleHostDriver struct {
|
||||
@@ -35,3 +43,27 @@ func (self *SGoogleHostDriver) GetHostType() string {
|
||||
func (self *SGoogleHostDriver) GetHypervisor() string {
|
||||
return api.HYPERVISOR_GOOGLE
|
||||
}
|
||||
|
||||
func (self *SGoogleHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error {
|
||||
minGB := 10
|
||||
maxGB := -1
|
||||
switch storage.StorageType {
|
||||
case api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD:
|
||||
maxGB = 65536
|
||||
default:
|
||||
return fmt.Errorf("Not support resize %s disk", storage.StorageType)
|
||||
}
|
||||
if sizeGb < minGB || sizeGb > maxGB {
|
||||
return fmt.Errorf("The %s disk size must be in the range of %dG ~ %dGB", storage.StorageType, minGB, maxGB)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleHostDriver) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, guests []models.SGuest, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
for _, guest := range guests {
|
||||
if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY}) {
|
||||
return nil, httperrors.NewBadGatewayError("%s reset disk required guest status is running or ready", self.GetHostType())
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ func (self *SManagedVirtualizationHostDriver) RequestDeallocateDiskOnHost(ctx co
|
||||
|
||||
iDisk, err := iCloudStorage.GetIDiskById(disk.GetExternalId())
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
task.ScheduleRun(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -726,6 +726,12 @@ func (self *SCloudregion) ValidateUpdateCondition(ctx context.Context) error {
|
||||
return self.SEnabledStatusStandaloneResourceBase.ValidateUpdateCondition(ctx)
|
||||
}
|
||||
|
||||
func (self *SCloudregion) SyncVpcs(ctx context.Context, userCred mcclient.TokenCredential, iregion cloudprovider.ICloudRegion, provider *SCloudprovider) error {
|
||||
syncResults, syncRange := SSyncResultSet{}, &SSyncRange{}
|
||||
syncRegionVPCs(ctx, userCred, syncResults, provider, self, iregion, syncRange)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SCloudregion) AllowGetDetailsCapability(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ type IGuestDriver interface {
|
||||
|
||||
RequestDeployGuestOnHost(ctx context.Context, guest *SGuest, host *SHost, task taskman.ITask) error
|
||||
RemoteDeployGuestForCreate(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, host *SHost, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error)
|
||||
RemoteActionAfterGuestCreated(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, host *SHost, iVM cloudprovider.ICloudVM, desc *cloudprovider.SManagedVMCreateConfig)
|
||||
RemoteDeployGuestForDeploy(ctx context.Context, guest *SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error)
|
||||
RemoteDeployGuestForRebuildRoot(ctx context.Context, guest *SGuest, ihost cloudprovider.ICloudHost, task taskman.ITask, desc cloudprovider.SManagedVMCreateConfig) (jsonutils.JSONObject, error)
|
||||
GetGuestInitialStateAfterCreate() string
|
||||
|
||||
@@ -87,6 +87,8 @@ type IRegionDriver interface {
|
||||
|
||||
ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error)
|
||||
ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, input *api.SElasticipCreateInput) error
|
||||
RequestCreateVpc(ctx context.Context, userCred mcclient.TokenCredential, region *SCloudregion, vpc *SVpc, task taskman.ITask) error
|
||||
RequestDeleteVpc(ctx context.Context, userCred mcclient.TokenCredential, region *SCloudregion, vpc *SVpc, task taskman.ITask) error
|
||||
|
||||
// Region Driver Snapshot Policy Apis
|
||||
//ValidateCreateSnapshotPolicyData(context.Context, mcclient.TokenCredential, *compute.SSnapshotPolicyCreateInput, mcclient.IIdentityProvider, *jsonutils.JSONDict) error
|
||||
|
||||
@@ -132,8 +132,20 @@ func (self *SSecurityGroupCache) GetCustomizeColumns(ctx context.Context, userCr
|
||||
func (manager *SSecurityGroupCacheManager) GetSecgroupCache(ctx context.Context, userCred mcclient.TokenCredential, secgroupId, vpcId string, regionId string, providerId string) (*SSecurityGroupCache, error) {
|
||||
secgroupCache := SSecurityGroupCache{}
|
||||
query := manager.Query()
|
||||
cond := sqlchemy.AND(sqlchemy.Equals(query.Field("secgroup_id"), secgroupId), sqlchemy.Equals(query.Field("vpc_id"), vpcId), sqlchemy.Equals(query.Field("cloudregion_id"), regionId), sqlchemy.Equals(query.Field("manager_id"), providerId))
|
||||
query = query.Filter(cond)
|
||||
conds := []sqlchemy.ICondition{
|
||||
sqlchemy.Equals(query.Field("secgroup_id"), secgroupId),
|
||||
sqlchemy.Equals(query.Field("vpc_id"), vpcId),
|
||||
sqlchemy.Equals(query.Field("manager_id"), providerId),
|
||||
}
|
||||
_region, err := CloudregionManager.FetchById(regionId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "CloudregionManager.FetchById(%s)", regionId)
|
||||
}
|
||||
region := _region.(*SCloudregion)
|
||||
if !region.GetDriver().IsSecurityGroupBelongGlobalVpc() {
|
||||
conds = append(conds, sqlchemy.Equals(query.Field("cloudregion_id"), regionId))
|
||||
}
|
||||
query = query.Filter(sqlchemy.AND(conds...))
|
||||
|
||||
count, err := query.CountWithError()
|
||||
if err != nil {
|
||||
|
||||
@@ -203,6 +203,14 @@ func (self *SBaseRegionDriver) RequestBingToNatgateway(ctx context.Context, task
|
||||
return fmt.Errorf("Not implement RequestBindIPToNatgateway")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) RequestCreateVpc(ctx context.Context, userCred mcclient.TokenCredential, region *models.SCloudregion, vpc *models.SVpc, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not implement RequestCreateVpc")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) RequestDeleteVpc(ctx context.Context, userCred mcclient.TokenCredential, region *models.SCloudregion, vpc *models.SVpc, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not implement RequestDeleteVpc")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) RequestCacheSecurityGroup(ctx context.Context, userCred mcclient.TokenCredential, region *models.SCloudregion, vpc *models.SVpc, secgroup *models.SSecurityGroup, classic bool, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not Implemented RequestCacheSecurityGroup")
|
||||
}
|
||||
|
||||
@@ -15,8 +15,18 @@
|
||||
package regiondrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
type SGoogleRegionDriver struct {
|
||||
@@ -39,3 +49,106 @@ func (self *SGoogleRegionDriver) IsSecurityGroupBelongGlobalVpc() bool {
|
||||
func (self *SGoogleRegionDriver) IsVpcBelongGlobalVpc() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SGoogleRegionDriver) RequestCreateVpc(ctx context.Context, userCred mcclient.TokenCredential, region *models.SCloudregion, vpc *models.SVpc, task taskman.ITask) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
provider := vpc.GetCloudprovider()
|
||||
if provider == nil {
|
||||
return nil, fmt.Errorf("failed to found vpc %s(%s) cloudprovider", vpc.Name, vpc.Id)
|
||||
}
|
||||
providerDriver, err := provider.GetProvider()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "provider.GetProvider")
|
||||
}
|
||||
iregion, err := providerDriver.GetIRegionById(region.ExternalId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.GetIRegion")
|
||||
}
|
||||
ivpc, err := iregion.CreateIVpc(vpc.Name, vpc.Description, vpc.CidrBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "iregion.CreateIVpc")
|
||||
}
|
||||
db.SetExternalId(vpc, userCred, ivpc.GetGlobalId())
|
||||
|
||||
regions, err := models.CloudregionManager.GetRegionByExternalIdPrefix(self.GetProvider())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetRegionByExternalIdPrefix")
|
||||
}
|
||||
for _, region := range regions {
|
||||
iregion, err := providerDriver.GetIRegionById(region.ExternalId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "providerDrivder.GetIRegionById")
|
||||
}
|
||||
region.SyncVpcs(ctx, userCred, iregion, provider)
|
||||
}
|
||||
|
||||
err = vpc.SyncWithCloudVpc(ctx, userCred, ivpc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.SyncWithCloudVpc")
|
||||
}
|
||||
|
||||
err = vpc.SyncRemoteWires(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.SyncRemoteWires")
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleRegionDriver) RequestDeleteVpc(ctx context.Context, userCred mcclient.TokenCredential, region *models.SCloudregion, vpc *models.SVpc, task taskman.ITask) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
region, err := vpc.GetIRegion()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.GetIRegion")
|
||||
}
|
||||
ivpc, err := region.GetIVpcById(vpc.GetExternalId())
|
||||
if err != nil {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
err = vpc.Purge(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.Purge")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrap(err, "region.GetIVpcById")
|
||||
}
|
||||
|
||||
globalVpc, err := vpc.GetGlobalVpc()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.GetGlobalVpc")
|
||||
}
|
||||
|
||||
vpcs, err := globalVpc.GetVpcs()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "globalVpc.GetVpcs")
|
||||
}
|
||||
|
||||
for i := range vpcs {
|
||||
if vpcs[i].Status == api.VPC_STATUS_AVAILABLE && vpcs[i].ManagerId == vpc.ManagerId {
|
||||
err = vpc.ValidateDeleteCondition(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "vpc %s(%s) not empty", vpc.Name, vpc.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = ivpc.Delete()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ivpc.Delete")
|
||||
}
|
||||
|
||||
for i := range vpcs {
|
||||
if vpcs[i].ManagerId == vpc.ManagerId && vpcs[i].Id != vpc.Id {
|
||||
err = vpcs[i].Purge(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "vpc.Purge %s(%s)", vpc.Name, vpc.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
@@ -1101,6 +1101,64 @@ func (self *SManagedVirtualizationRegionDriver) ValidateCreateEipData(ctx contex
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestCreateVpc(ctx context.Context, userCred mcclient.TokenCredential, region *models.SCloudregion, vpc *models.SVpc, task taskman.ITask) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
iregion, err := vpc.GetIRegion()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.GetIRegion")
|
||||
}
|
||||
ivpc, err := iregion.CreateIVpc(vpc.Name, vpc.Description, vpc.CidrBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "iregion.CreateIVpc")
|
||||
}
|
||||
db.SetExternalId(vpc, userCred, ivpc.GetGlobalId())
|
||||
|
||||
err = cloudprovider.WaitStatus(ivpc, api.VPC_STATUS_AVAILABLE, 10*time.Second, 300*time.Second)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cloudprovider.WaitStatus")
|
||||
}
|
||||
|
||||
err = vpc.SyncWithCloudVpc(ctx, userCred, ivpc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.SyncWithCloudVpc")
|
||||
}
|
||||
|
||||
err = vpc.SyncRemoteWires(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.SyncRemoteWires")
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestDeleteVpc(ctx context.Context, userCred mcclient.TokenCredential, region *models.SCloudregion, vpc *models.SVpc, task taskman.ITask) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
region, err := vpc.GetIRegion()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "vpc.GetIRegion")
|
||||
}
|
||||
ivpc, err := region.GetIVpcById(vpc.GetExternalId())
|
||||
if err != nil {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
// already deleted, do nothing
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrap(err, "region.GetIVpcById")
|
||||
}
|
||||
err = ivpc.Delete()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ivpc.Delete(")
|
||||
}
|
||||
err = cloudprovider.WaitDeleted(ivpc, 10*time.Second, 300*time.Second)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cloudprovider.WaitDeleted")
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestUpdateSnapshotPolicy(ctx context.Context, userCred mcclient.
|
||||
TokenCredential, sp *models.SSnapshotPolicy, input cloudprovider.SnapshotPolicyInput, task taskman.ITask) error {
|
||||
// it's too cumbersome to pass parameters in taskman, so change a simple way for the moment
|
||||
|
||||
@@ -67,7 +67,7 @@ func (self *SecurityGroupCacheDeleteTask) OnInit(ctx context.Context, obj db.ISt
|
||||
|
||||
iRegion, err := cache.GetIRegion()
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
self.taskComplete(ctx, cache)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -16,15 +16,15 @@ package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -49,36 +49,24 @@ func (self *VpcCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel,
|
||||
vpc := obj.(*models.SVpc)
|
||||
vpc.SetStatus(self.UserCred, api.VPC_STATUS_PENDING, "")
|
||||
|
||||
iregion, err := vpc.GetIRegion()
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
ivpc, err := iregion.CreateIVpc(vpc.Name, vpc.Description, vpc.CidrBlock)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
db.SetExternalId(vpc, self.UserCred, ivpc.GetGlobalId())
|
||||
|
||||
err = cloudprovider.WaitStatus(ivpc, api.VPC_STATUS_AVAILABLE, 10*time.Second, 300*time.Second)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = vpc.SyncWithCloudVpc(ctx, self.UserCred, ivpc)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = vpc.SyncRemoteWires(ctx, self.UserCred)
|
||||
region, err := vpc.GetRegion()
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, vpc, errors.Wrap(err, "vpc.GetRegion"))
|
||||
return
|
||||
}
|
||||
self.SetStage("OnCreateVpcComplete", nil)
|
||||
err = region.GetDriver().RequestCreateVpc(ctx, self.UserCred, region, vpc, self)
|
||||
if err != nil {
|
||||
self.TaskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (self *VpcCreateTask) OnCreateVpcComplete(ctx context.Context, vpc *models.SVpc, data jsonutils.JSONObject) {
|
||||
logclient.AddActionLogWithStartable(self, vpc, logclient.ACT_ALLOCATE, nil, self.UserCred, true)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (self *VpcCreateTask) OnCreateVpcCompleteFailed(ctx context.Context, vpc *models.SVpc, data jsonutils.JSONObject) {
|
||||
self.TaskFailed(ctx, vpc, fmt.Errorf("%s", data.String()))
|
||||
}
|
||||
|
||||
@@ -16,15 +16,15 @@ package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
@@ -47,40 +47,32 @@ func (self *VpcDeleteTask) taskFailed(ctx context.Context, vpc *models.SVpc, err
|
||||
|
||||
func (self *VpcDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
vpc := obj.(*models.SVpc)
|
||||
|
||||
vpc.SetStatus(self.UserCred, api.VPC_STATUS_DELETING, "")
|
||||
region, err := vpc.GetRegion()
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, vpc, errors.Wrap(err, "vpc.GetRegion"))
|
||||
return
|
||||
}
|
||||
|
||||
self.SetStage("OnDeleteVpcComplete", nil)
|
||||
err = region.GetDriver().RequestDeleteVpc(ctx, self.UserCred, region, vpc, self)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, vpc, errors.Wrap(err, "RequestDeleteVpc"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (self *VpcDeleteTask) OnDeleteVpcComplete(ctx context.Context, vpc *models.SVpc, body jsonutils.JSONObject) {
|
||||
err := vpc.Purge(ctx, self.UserCred)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, vpc, errors.Wrap(err, "vpc.Purge"))
|
||||
return
|
||||
}
|
||||
db.OpsLog.LogEvent(vpc, db.ACT_DELOCATING, vpc.GetShortDesc(ctx), self.UserCred)
|
||||
|
||||
region, err := vpc.GetIRegion()
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
ivpc, err := region.GetIVpcById(vpc.GetExternalId())
|
||||
if ivpc != nil {
|
||||
err = ivpc.Delete()
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
err = cloudprovider.WaitDeleted(ivpc, 10*time.Second, 300*time.Second)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
} else if err == cloudprovider.ErrNotFound {
|
||||
// already deleted, do nothing
|
||||
} else {
|
||||
self.taskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = vpc.Purge(ctx, self.UserCred)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, vpc, err)
|
||||
return
|
||||
}
|
||||
|
||||
logclient.AddActionLogWithStartable(self, vpc, logclient.ACT_DELETE, nil, self.UserCred, true)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (self *VpcDeleteTask) OnDeleteVpcCompleteFailed(ctx context.Context, vpc *models.SVpc, reason jsonutils.JSONObject) {
|
||||
self.taskFailed(ctx, vpc, fmt.Errorf("%s", reason))
|
||||
}
|
||||
|
||||
@@ -215,6 +215,11 @@ type SCtyunCloudAccountUpdateCredentialOptions struct {
|
||||
SAccessKeyCredential
|
||||
}
|
||||
|
||||
type SGoogleCloudAccountUpdateCredentialOptions struct {
|
||||
SCloudAccountUpdateCredentialBaseOptions
|
||||
GoogleJsonFile string `help:"Google auth json file" positional:"true"`
|
||||
}
|
||||
|
||||
// update
|
||||
|
||||
type SCloudAccountUpdateBaseOptions struct {
|
||||
|
||||
@@ -129,7 +129,7 @@ type ServerConfigs struct {
|
||||
Host string `help:"Preferred host where virtual server should be created" json:"prefer_host"`
|
||||
BackupHost string `help:"Perfered host where virtual backup server should be created"`
|
||||
|
||||
Hypervisor string `help:"Hypervisor type" choices:"kvm|esxi|baremetal|container|aliyun|azure|qcloud|aws|huawei|openstack|ucloud|zstack"`
|
||||
Hypervisor string `help:"Hypervisor type" choices:"kvm|esxi|baremetal|container|aliyun|azure|qcloud|aws|huawei|openstack|ucloud|zstack|google"`
|
||||
ResourceType string `help:"Resource type" choices:"shared|prepaid|dedicated"`
|
||||
Backup bool `help:"Create server with backup server"`
|
||||
|
||||
|
||||
@@ -493,16 +493,16 @@ func (self *SInstance) DeployVM(ctx context.Context, name string, username strin
|
||||
return self.host.zone.region.DeployVM(self.InstanceId, name, password, keypairName, deleteKeypair, description)
|
||||
}
|
||||
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
keypair := ""
|
||||
if len(publicKey) > 0 {
|
||||
if len(desc.PublicKey) > 0 {
|
||||
var err error
|
||||
keypair, err = self.host.zone.region.syncKeypair(publicKey)
|
||||
keypair, err = self.host.zone.region.syncKeypair(desc.PublicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
diskId, err := self.host.zone.region.ReplaceSystemDisk(self.InstanceId, imageId, passwd, keypair, sysSizeGB)
|
||||
diskId, err := self.host.zone.region.ReplaceSystemDisk(self.InstanceId, desc.ImageId, desc.Password, keypair, desc.SysSizeGB)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ func (self *SInstance) UpdateVM(ctx context.Context, name string) error {
|
||||
return self.host.zone.region.UpdateVM(self.InstanceId, name)
|
||||
}
|
||||
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
udata, err := self.GetUserData()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -424,30 +424,30 @@ func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd s
|
||||
keypairName := self.KeyPairName
|
||||
loginUser := cloudinit.NewUser(api.VM_AWS_DEFAULT_LOGIN_USER)
|
||||
loginUser.SudoPolicy(cloudinit.USER_SUDO_NOPASSWD)
|
||||
if len(publicKey) > 0 {
|
||||
loginUser.SshKey(publicKey)
|
||||
if len(desc.PublicKey) > 0 {
|
||||
loginUser.SshKey(desc.PublicKey)
|
||||
cloudconfig.MergeUser(loginUser)
|
||||
keypairName, err = self.host.zone.region.syncKeypair(publicKey)
|
||||
keypairName, err = self.host.zone.region.syncKeypair(desc.PublicKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("RebuildRoot.syncKeypair %s", err)
|
||||
}
|
||||
} else if len(passwd) > 0 {
|
||||
loginUser.Password(passwd)
|
||||
} else if len(desc.Password) > 0 {
|
||||
loginUser.Password(desc.Password)
|
||||
cloudconfig.MergeUser(loginUser)
|
||||
}
|
||||
|
||||
// compare sysSizeGB
|
||||
image, err := self.host.zone.region.GetImage(imageId)
|
||||
image, err := self.host.zone.region.GetImage(desc.ImageId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
minSizeGB := image.GetMinOsDiskSizeGb()
|
||||
if minSizeGB > sysSizeGB {
|
||||
sysSizeGB = minSizeGB
|
||||
if minSizeGB > desc.SysSizeGB {
|
||||
desc.SysSizeGB = minSizeGB
|
||||
}
|
||||
}
|
||||
|
||||
diskId, err := self.host.zone.region.ReplaceSystemDisk(ctx, self.InstanceId, imageId, sysSizeGB, keypairName, cloudconfig.UserDataBase64())
|
||||
diskId, err := self.host.zone.region.ReplaceSystemDisk(ctx, self.InstanceId, desc.ImageId, desc.SysSizeGB, keypairName, cloudconfig.UserDataBase64())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ func (self *SClassicInstance) DeployVM(ctx context.Context, name string, usernam
|
||||
//return self.host.zone.region.DeployVM(self.ID, name, password, publicKey, deleteKeypair, description)
|
||||
}
|
||||
|
||||
func (self *SClassicInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
func (self *SClassicInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
//return self.host.zone.region.ReplaceSystemDisk(self.ID, imageId, passwd, publicKey, int32(sysSizeGB))
|
||||
}
|
||||
|
||||
@@ -775,11 +775,11 @@ func (region *SRegion) DeployVM(ctx context.Context, instanceId, name, password,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
cpu := self.GetVcpuCount()
|
||||
memoryMb := self.GetVmemSizeMB()
|
||||
self.StopVM(ctx, true)
|
||||
return self.host.zone.region.ReplaceSystemDisk(self, cpu, memoryMb, imageId, passwd, publicKey, sysSizeGB)
|
||||
return self.host.zone.region.ReplaceSystemDisk(self, cpu, memoryMb, desc.ImageId, desc.Password, desc.PublicKey, desc.SysSizeGB)
|
||||
}
|
||||
|
||||
func (region *SRegion) ReplaceSystemDisk(instance *SInstance, cpu int, memoryMb int, imageId, passwd, publicKey string, sysSizeGB int) (string, error) {
|
||||
|
||||
@@ -429,7 +429,7 @@ func (self *SInstance) UpdateUserData(userData string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ func (self *SVirtualMachine) DeployVM(ctx context.Context, name string, username
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SVirtualMachine) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
func (self *SVirtualMachine) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -1078,3 +1078,7 @@ func (self *SVirtualMachine) ExportTemplate(ctx context.Context, idx int, diskPa
|
||||
log.Debugf("download to %s finish", diskPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SVirtualMachine) GetSerialOutput(port int) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type SLifecycleRuleAction struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
type SLifecycleRuleCondition struct {
|
||||
Age int
|
||||
}
|
||||
|
||||
type SLifecycleRule struct {
|
||||
Action SLifecycleRuleAction
|
||||
Condition SLifecycleRuleCondition
|
||||
}
|
||||
|
||||
type SBucketPolicyOnly struct {
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
type SUniformBucketLevelAccess struct {
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
type SIamConfiguration struct {
|
||||
BucketPolicyOnly SBucketPolicyOnly
|
||||
UniformBucketLevelAccess SUniformBucketLevelAccess
|
||||
}
|
||||
|
||||
type SLifecycle struct {
|
||||
Rule []SLifecycleRule
|
||||
}
|
||||
|
||||
type SBucket struct {
|
||||
Kind string
|
||||
SelfLink string
|
||||
Id string
|
||||
Name string
|
||||
ProjectNumber string
|
||||
Metageneration string
|
||||
Location string
|
||||
StorageClass string
|
||||
Etag string
|
||||
TimeCreated time.Time
|
||||
Updated time.Time
|
||||
Lifecycle SLifecycle
|
||||
IamConfiguration SIamConfiguration
|
||||
LocationType string
|
||||
}
|
||||
|
||||
func (region *SRegion) GetBucket(name string) (*SBucket, error) {
|
||||
resource := "b/" + name
|
||||
bucket := &SBucket{}
|
||||
err := region.StorageGet(resource, bucket)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBucket")
|
||||
}
|
||||
return bucket, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetBuckets(maxResults int, pageToken string) ([]SBucket, error) {
|
||||
buckets := []SBucket{}
|
||||
params := map[string]string{
|
||||
"project": region.GetProjectId(),
|
||||
}
|
||||
err := region.StorageList("b", params, maxResults, pageToken, &buckets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buckets, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateBucket(name string, storageClass string) (*SBucket, error) {
|
||||
body := map[string]interface{}{
|
||||
"name": name,
|
||||
"location": region.Name,
|
||||
}
|
||||
if len(storageClass) > 0 {
|
||||
body["storageClass"] = storageClass
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("project", region.GetProjectId())
|
||||
bucket := &SBucket{}
|
||||
err := region.StorageInsert(fmt.Sprintf("b?%s", params.Encode()), jsonutils.Marshal(body), bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bucket, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) UploadObject(bucket string, params url.Values, header http.Header, input io.Reader) error {
|
||||
resource := fmt.Sprintf("b/%s/o", bucket)
|
||||
if len(params) > 0 {
|
||||
resource = fmt.Sprintf("%s?%s", resource, params.Encode())
|
||||
}
|
||||
return region.client.storageUpload(resource, header, input)
|
||||
}
|
||||
|
||||
func (region *SRegion) PutObject(bucket string, name string, input io.Reader, contType string, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType) error {
|
||||
params := url.Values{}
|
||||
params.Set("name", name)
|
||||
params.Set("uploadType", "media")
|
||||
switch cannedAcl {
|
||||
case cloudprovider.ACLPrivate:
|
||||
params.Set("predefinedAcl", "private")
|
||||
case cloudprovider.ACLAuthRead:
|
||||
params.Set("predefinedAcl", "authenticatedRead")
|
||||
case cloudprovider.ACLPublicRead:
|
||||
params.Set("predefinedAcl", "publicRead")
|
||||
case cloudprovider.ACLPublicReadWrite:
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
header := http.Header{}
|
||||
header.Set("Content-Length", fmt.Sprintf("%v", sizeBytes))
|
||||
header.Set("Content-Type", "application/octet-stream")
|
||||
|
||||
return region.UploadObject(bucket, params, header, input)
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteBucket(name string) error {
|
||||
return region.StorageDelete("b/" + name)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google
|
||||
|
||||
import "yunion.io/x/pkg/errors"
|
||||
|
||||
type SCloudbuildBuild struct {
|
||||
Id string
|
||||
Status string
|
||||
LogUrl string
|
||||
}
|
||||
|
||||
type SCloudbuildMetadata struct {
|
||||
Build SCloudbuildBuild
|
||||
}
|
||||
|
||||
type SCloudbuildOperation struct {
|
||||
Name string
|
||||
Metadata SCloudbuildMetadata
|
||||
}
|
||||
|
||||
func (region *SRegion) GetCloudbuildOperation(name string) (*SCloudbuildOperation, error) {
|
||||
operation := SCloudbuildOperation{}
|
||||
err := region.cloudbuildGet(name, &operation)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "region.cloudbuildGet")
|
||||
}
|
||||
return &operation, nil
|
||||
}
|
||||
@@ -43,6 +43,7 @@ type SDisk struct {
|
||||
LabelFingerprint string
|
||||
PhysicalBlockSizeBytes string
|
||||
ResourcePolicies []string
|
||||
Users []string
|
||||
Kind string
|
||||
autoDelete bool
|
||||
boot bool
|
||||
@@ -116,6 +117,9 @@ func (disk *SDisk) GetDiskSizeMB() int {
|
||||
}
|
||||
|
||||
func (disk *SDisk) GetIsAutoDelete() bool {
|
||||
if len(disk.Users) == 0 {
|
||||
return false
|
||||
}
|
||||
return disk.autoDelete
|
||||
}
|
||||
|
||||
@@ -124,7 +128,7 @@ func (disk *SDisk) GetTemplateId() string {
|
||||
}
|
||||
|
||||
func (disk *SDisk) GetDiskType() string {
|
||||
if disk.index == 0 || disk.boot {
|
||||
if disk.boot && len(disk.Users) > 0 {
|
||||
return api.DISK_TYPE_SYS
|
||||
}
|
||||
return api.DISK_TYPE_DATA
|
||||
@@ -155,11 +159,16 @@ func (disk *SDisk) GetAccessPath() string {
|
||||
}
|
||||
|
||||
func (disk *SDisk) Delete(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return disk.storage.zone.region.Delete(disk.SelfLink)
|
||||
}
|
||||
|
||||
func (disk *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
snapshot, err := disk.storage.zone.region.CreateSnapshot(disk.SelfLink, name, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshot.region = disk.storage.zone.region
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func (disk *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
@@ -189,15 +198,15 @@ func (disk *SDisk) GetExtSnapshotPolicyIds() ([]string, error) {
|
||||
}
|
||||
|
||||
func (disk *SDisk) Resize(ctx context.Context, newSizeMB int64) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return disk.storage.zone.region.ResizeDisk(disk.SelfLink, int(newSizeMB>>10))
|
||||
}
|
||||
|
||||
func (disk *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (disk *SDisk) Rebuild(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (disk *SDisk) GetBillingType() string {
|
||||
@@ -215,3 +224,47 @@ func (disk *SDisk) GetExpiredAt() time.Time {
|
||||
func (disk *SDisk) GetProjectId() string {
|
||||
return disk.storage.zone.region.GetProjectId()
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateDisk(name string, sizeGb int, zone string, storageType string, image string, desc string) (*SDisk, error) {
|
||||
if !strings.HasPrefix(storageType, GOOGLE_COMPUTE_DOMAIN) {
|
||||
storageType = fmt.Sprintf("projects/%s/zones/%s/diskTypes/%s", region.GetProjectId(), zone, storageType)
|
||||
}
|
||||
body := map[string]interface{}{
|
||||
"name": name,
|
||||
"description": desc,
|
||||
// https://www.googleapis.com/compute/v1/projects/my-project-15390453537169/zones/us-west2-c/diskTypes/pd-standard
|
||||
// projects/my-project-15390453537169/zones/us-west2-c/diskTypes/pd-standard
|
||||
"type": storageType,
|
||||
}
|
||||
if len(image) > 0 {
|
||||
body["sourceImage"] = image
|
||||
} else {
|
||||
body["sizeGb"] = sizeGb
|
||||
}
|
||||
disk := &SDisk{}
|
||||
resource := fmt.Sprintf("zones/%s/disks", zone)
|
||||
err := region.Insert(resource, jsonutils.Marshal(body), disk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return disk, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) ResizeDisk(id string, sizeGb int) error {
|
||||
body := map[string]int{
|
||||
"sizeGb": sizeGb,
|
||||
}
|
||||
return region.Do(id, "resize", nil, jsonutils.Marshal(body))
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateSnapshot(diskId string, name string, desc string) (*SSnapshot, error) {
|
||||
body := map[string]string{
|
||||
"name": name,
|
||||
"description": desc,
|
||||
}
|
||||
err := region.Do(diskId, "createSnapshot", nil, jsonutils.Marshal(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return region.GetSnapshot(fmt.Sprintf("projects/%s/global/snapshots/%s", region.GetProjectId(), name))
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
billing "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
@@ -145,17 +146,71 @@ func (addr *SAddress) GetInternetChargeType() string {
|
||||
}
|
||||
|
||||
func (addr *SAddress) Delete() error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return addr.region.Delete(addr.SelfLink)
|
||||
}
|
||||
|
||||
func (addr *SAddress) Associate(instanceId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return addr.region.AssociateInstanceEip(instanceId, addr.Address)
|
||||
}
|
||||
|
||||
func (addr *SAddress) Dissociate() error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
if len(addr.Users) > 0 {
|
||||
return addr.region.DissociateInstanceEip(addr.Users[0], addr.Address)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (addr *SAddress) ChangeBandwidth(bw int) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateEip(name string, desc string) (*SAddress, error) {
|
||||
body := map[string]string{
|
||||
"name": name,
|
||||
"description": desc,
|
||||
}
|
||||
resource := fmt.Sprintf("regions/%s/addresses", region.Name)
|
||||
addr := &SAddress{region: region}
|
||||
err := region.Insert(resource, jsonutils.Marshal(body), addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) AssociateInstanceEip(instanceId string, eip string) error {
|
||||
instance, err := region.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "region.GetInstance")
|
||||
}
|
||||
for _, networkInterface := range instance.NetworkInterfaces {
|
||||
body := map[string]interface{}{
|
||||
"type": "ONE_TO_ONE_NAT",
|
||||
"name": "External NAT",
|
||||
"natIP": eip,
|
||||
}
|
||||
params := map[string]string{"networkInterface": networkInterface.Name}
|
||||
return region.Do(instance.SelfLink, "addAccessConfig", params, jsonutils.Marshal(body))
|
||||
}
|
||||
return fmt.Errorf("no valid networkinterface to associate")
|
||||
}
|
||||
|
||||
func (region *SRegion) DissociateInstanceEip(instanceId string, eip string) error {
|
||||
instance, err := region.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "region.GetInstance")
|
||||
}
|
||||
for _, networkInterface := range instance.NetworkInterfaces {
|
||||
for _, accessConfig := range networkInterface.AccessConfigs {
|
||||
if accessConfig.NatIP == eip {
|
||||
body := map[string]string{}
|
||||
params := map[string]string{
|
||||
"networkInterface": networkInterface.Name,
|
||||
"accessConfig": accessConfig.Name,
|
||||
}
|
||||
return region.Do(instance.SelfLink, "deleteAccessConfig", params, jsonutils.Marshal(body))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package google
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -60,3 +61,16 @@ func (cli *SGoogleClient) GetGlobalNetworks(maxResults int, pageToken string) ([
|
||||
}
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateGlobalNetwork(name string, desc string) (*SGlobalNetwork, error) {
|
||||
body := map[string]string{
|
||||
"name": name,
|
||||
"description": desc,
|
||||
}
|
||||
globalnetwork := &SGlobalNetwork{}
|
||||
err := region.Insert("global/networks", jsonutils.Marshal(body), globalnetwork)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "region.Insert")
|
||||
}
|
||||
return globalnetwork, nil
|
||||
}
|
||||
|
||||
@@ -17,15 +17,18 @@ package google
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/oauth2/jwt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
@@ -39,11 +42,21 @@ const (
|
||||
|
||||
GOOGLE_DEFAULT_REGION = "asia-east1"
|
||||
|
||||
GOOGLE_COMPUTE_DOMAIN = "https://www.googleapis.com/compute"
|
||||
GOOGLE_MANAGER_DOMAIN = "https://cloudresourcemanager.googleapis.com"
|
||||
|
||||
GOOGLE_API_VERSION = "v1"
|
||||
GOOGLE_MANAGER_API_VERSION = "v1"
|
||||
|
||||
GOOGLE_STORAGE_API_VERSION = "v1"
|
||||
GOOGLE_CLOUDBUILD_API_VERSION = "v1"
|
||||
GOOGLE_BILLING_API_VERSION = "v1"
|
||||
|
||||
GOOGLE_MANAGER_DOMAIN = "https://cloudresourcemanager.googleapis.com"
|
||||
GOOGLE_COMPUTE_DOMAIN = "https://www.googleapis.com/compute"
|
||||
GOOGLE_STORAGE_DOMAIN = "https://storage.googleapis.com/storage"
|
||||
GOOGLE_CLOUDBUILD_DOMAIN = "https://cloudbuild.googleapis.com"
|
||||
GOOGLE_STORAGE_UPLOAD_DOMAIN = "https://www.googleapis.com/upload/storage"
|
||||
GOOGLE_BILLING_DOMAIN = "https://cloudbilling.googleapis.com"
|
||||
|
||||
MAX_RETRY = 3
|
||||
)
|
||||
|
||||
type SGoogleClient struct {
|
||||
@@ -85,6 +98,9 @@ func NewGoogleClient(providerId string, providerName string, projectId, clientEm
|
||||
"https://www.googleapis.com/auth/cloud-platform.read-only",
|
||||
"https://www.googleapis.com/auth/cloudplatformprojects",
|
||||
"https://www.googleapis.com/auth/cloudplatformprojects.readonly",
|
||||
|
||||
"https://www.googleapis.com/auth/devstorage.full_control",
|
||||
"https://www.googleapis.com/auth/devstorage.read_write",
|
||||
},
|
||||
TokenURL: google.JWTTokenURL,
|
||||
}
|
||||
@@ -113,6 +129,9 @@ func (self *SGoogleClient) fetchRegions() error {
|
||||
|
||||
func jsonRequest(client *http.Client, method httputils.THttpMethod, domain, apiVersion, resource string, params map[string]string, body jsonutils.JSONObject, debug bool) (jsonutils.JSONObject, error) {
|
||||
resource = strings.TrimPrefix(resource, fmt.Sprintf("%s/%s/", domain, apiVersion))
|
||||
if len(resource) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
_url := fmt.Sprintf("%s/%s/%s", domain, apiVersion, resource)
|
||||
values := url.Values{}
|
||||
for k, v := range params {
|
||||
@@ -127,7 +146,7 @@ func jsonRequest(client *http.Client, method httputils.THttpMethod, domain, apiV
|
||||
func (self *SGoogleClient) ecsGet(resource string, retval interface{}) error {
|
||||
resp, err := jsonRequest(self.client, "GET", GOOGLE_COMPUTE_DOMAIN, GOOGLE_API_VERSION, resource, nil, nil, self.Debug)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "jsonRequest")
|
||||
return err
|
||||
}
|
||||
if retval != nil {
|
||||
err = resp.Unmarshal(retval)
|
||||
@@ -179,10 +198,247 @@ func (self *SGoogleClient) ecsListAll(resource string, params map[string]string,
|
||||
return items.Unmarshal(retval)
|
||||
}
|
||||
|
||||
func _jsonRequest(client *http.Client, method httputils.THttpMethod, url string, body jsonutils.JSONObject, debug bool) (jsonutils.JSONObject, error) {
|
||||
_, data, err := httputils.JSONRequest(client, context.Background(), method, url, nil, body, debug)
|
||||
func (self *SGoogleClient) ecsDelete(id string, retval interface{}) error {
|
||||
resp, err := jsonRequest(self.client, "DELETE", GOOGLE_COMPUTE_DOMAIN, GOOGLE_API_VERSION, id, nil, nil, self.Debug)
|
||||
if err != nil {
|
||||
if strings.Index(err.Error(), "not found") > 0 {
|
||||
return err
|
||||
}
|
||||
if retval != nil {
|
||||
return resp.Unmarshal(retval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) ecsPatch(resource string, action string, params map[string]string, body jsonutils.JSONObject) (string, error) {
|
||||
if len(action) > 0 {
|
||||
resource = fmt.Sprintf("%s/%s", resource, action)
|
||||
}
|
||||
resp, err := jsonRequest(self.client, "PATCH", GOOGLE_COMPUTE_DOMAIN, GOOGLE_API_VERSION, resource, params, body, self.Debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
selfLink, _ := resp.GetString("selfLink")
|
||||
return selfLink, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) ecsDo(resource string, action string, params map[string]string, body jsonutils.JSONObject) (string, error) {
|
||||
resource = fmt.Sprintf("%s/%s", resource, action)
|
||||
resp, err := jsonRequest(self.client, "POST", GOOGLE_COMPUTE_DOMAIN, GOOGLE_API_VERSION, resource, params, body, self.Debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
selfLink, _ := resp.GetString("selfLink")
|
||||
return selfLink, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) ecsInsert(resource string, body jsonutils.JSONObject, retval interface{}) error {
|
||||
resource = fmt.Sprintf("projects/%s/%s", self.projectId, resource)
|
||||
if name, _ := body.GetString("name"); len(name) > 0 {
|
||||
generateName := ""
|
||||
for _, s := range name {
|
||||
if unicode.IsLetter(s) || unicode.IsDigit(s) {
|
||||
generateName = fmt.Sprintf("%s%c", generateName, s)
|
||||
} else {
|
||||
generateName = fmt.Sprintf("%s-", generateName)
|
||||
}
|
||||
}
|
||||
if name != generateName {
|
||||
err := jsonutils.Update(body, map[string]string{"name": generateName})
|
||||
if err != nil {
|
||||
log.Errorf("faild to generate google name from %s -> %s", name, generateName)
|
||||
}
|
||||
}
|
||||
}
|
||||
resp, err := jsonRequest(self.client, "POST", GOOGLE_COMPUTE_DOMAIN, GOOGLE_API_VERSION, resource, nil, body, self.Debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if retval != nil {
|
||||
return resp.Unmarshal(retval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) storageInsert(resource string, body jsonutils.JSONObject, retval interface{}) error {
|
||||
resp, err := jsonRequest(self.client, "POST", GOOGLE_STORAGE_DOMAIN, GOOGLE_STORAGE_API_VERSION, resource, nil, body, self.Debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if retval != nil {
|
||||
return resp.Unmarshal(retval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) storageUpload(resource string, header http.Header, body io.Reader) error {
|
||||
return rawRequest(self.client, "POST", GOOGLE_STORAGE_UPLOAD_DOMAIN, GOOGLE_STORAGE_API_VERSION, resource, header, body, self.Debug)
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) storageList(resource string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
return jsonRequest(self.client, "GET", GOOGLE_STORAGE_DOMAIN, GOOGLE_STORAGE_API_VERSION, resource, params, nil, self.Debug)
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) storageListAll(resource string, params map[string]string, retval interface{}) error {
|
||||
if params == nil {
|
||||
params = map[string]string{}
|
||||
}
|
||||
items := jsonutils.NewArray()
|
||||
nextPageToken := ""
|
||||
params["maxResults"] = "500"
|
||||
for {
|
||||
params["pageToken"] = nextPageToken
|
||||
resp, err := self.storageList(resource, params)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "storageList")
|
||||
}
|
||||
if resp.Contains("items") {
|
||||
_items, err := resp.GetArray("items")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "resp.GetArray")
|
||||
}
|
||||
items.Add(_items...)
|
||||
}
|
||||
nextPageToken, _ = resp.GetString("nextPageToken")
|
||||
if len(nextPageToken) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return items.Unmarshal(retval)
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) storageGet(resource string, retval interface{}) error {
|
||||
resp, err := jsonRequest(self.client, "GET", GOOGLE_STORAGE_DOMAIN, GOOGLE_STORAGE_API_VERSION, resource, nil, nil, self.Debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if retval != nil {
|
||||
err = resp.Unmarshal(retval)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "resp.Unmarshal")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) storageDelete(id string, retval interface{}) error {
|
||||
resp, err := jsonRequest(self.client, "DELETE", GOOGLE_STORAGE_DOMAIN, GOOGLE_STORAGE_API_VERSION, id, nil, nil, self.Debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if retval != nil {
|
||||
return resp.Unmarshal(retval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) storageDo(resource string, action string, params map[string]string, body jsonutils.JSONObject) (string, error) {
|
||||
resource = fmt.Sprintf("%s/%s", resource, action)
|
||||
resp, err := jsonRequest(self.client, "POST", GOOGLE_STORAGE_DOMAIN, GOOGLE_STORAGE_API_VERSION, resource, params, body, self.Debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
selfLink, _ := resp.GetString("selfLink")
|
||||
return selfLink, nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) cloudbuildGet(resource string, retval interface{}) error {
|
||||
resp, err := jsonRequest(self.client, "GET", GOOGLE_CLOUDBUILD_DOMAIN, GOOGLE_CLOUDBUILD_API_VERSION, resource, nil, nil, self.Debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if retval != nil {
|
||||
err = resp.Unmarshal(retval)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "resp.Unmarshal")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) cloudbuildInsert(resource string, body jsonutils.JSONObject, retval interface{}) error {
|
||||
resp, err := jsonRequest(self.client, "POST", GOOGLE_CLOUDBUILD_DOMAIN, GOOGLE_CLOUDBUILD_API_VERSION, resource, nil, body, self.Debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if retval != nil {
|
||||
return resp.Unmarshal(retval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) billingList(resource string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
return jsonRequest(self.client, "GET", GOOGLE_BILLING_DOMAIN, GOOGLE_BILLING_API_VERSION, resource, params, nil, self.Debug)
|
||||
}
|
||||
|
||||
func (self *SGoogleClient) billingListAll(resource string, params map[string]string, retval interface{}) error {
|
||||
if params == nil {
|
||||
params = map[string]string{}
|
||||
}
|
||||
items := jsonutils.NewArray()
|
||||
nextPageToken := ""
|
||||
params["pageSize"] = "5000"
|
||||
for {
|
||||
params["pageToken"] = nextPageToken
|
||||
resp, err := self.billingList(resource, params)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "billingList")
|
||||
}
|
||||
if resp.Contains("skus") {
|
||||
_items, err := resp.GetArray("skus")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "resp.GetArray")
|
||||
}
|
||||
items.Add(_items...)
|
||||
}
|
||||
nextPageToken, _ = resp.GetString("nextPageToken")
|
||||
if len(nextPageToken) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return items.Unmarshal(retval)
|
||||
}
|
||||
|
||||
func rawRequest(client *http.Client, method httputils.THttpMethod, domain, apiVersion string, resource string, header http.Header, body io.Reader, debug bool) error {
|
||||
resource = strings.TrimPrefix(resource, fmt.Sprintf("%s/%s/", domain, apiVersion))
|
||||
resource = fmt.Sprintf("%s/%s/%s", domain, apiVersion, resource)
|
||||
_, err := httputils.Request(client, context.Background(), method, resource, header, body, debug)
|
||||
return err
|
||||
}
|
||||
|
||||
func _jsonRequest(client *http.Client, method httputils.THttpMethod, url string, body jsonutils.JSONObject, debug bool) (jsonutils.JSONObject, error) {
|
||||
var (
|
||||
retry bool = false
|
||||
err error = nil
|
||||
data jsonutils.JSONObject = nil
|
||||
)
|
||||
for i := 0; i < MAX_RETRY; i++ {
|
||||
_, data, err = httputils.JSONRequest(client, context.Background(), method, url, nil, body, debug)
|
||||
if err != nil {
|
||||
if body != nil {
|
||||
log.Errorf("%s %s params: %s error: %v", method, url, body.PrettyString(), err)
|
||||
} else {
|
||||
log.Errorf("%s %s error: %v", method, url, err)
|
||||
}
|
||||
for _, msg := range []string{
|
||||
"EOF",
|
||||
"i/o timeout",
|
||||
"TLS handshake timeout",
|
||||
} {
|
||||
if strings.Index(err.Error(), msg) >= 0 {
|
||||
retry = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !retry {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !retry {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if strings.Index(strings.ToLower(err.Error()), "not found") > 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return nil, errors.Wrap(err, "JSONRequest")
|
||||
|
||||
@@ -167,11 +167,17 @@ func (host *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
|
||||
if instance.Zone != host.zone.SelfLink {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
instance.host = host
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func (host *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
instance, err := host.zone.region._createVM(host.zone.Name, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instance.host = host
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
|
||||
|
||||
@@ -16,6 +16,7 @@ package google
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -187,7 +188,7 @@ func (image *SImage) GetCreatedAt() time.Time {
|
||||
}
|
||||
|
||||
func (image *SImage) GetImageFormat() string {
|
||||
return "vhd"
|
||||
return "raw"
|
||||
}
|
||||
|
||||
func (image *SImage) IsEmulated() bool {
|
||||
@@ -213,3 +214,32 @@ func (region *SRegion) fetchImages() ([]SImage, error) {
|
||||
region.client.images = images
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateImage(name string, desc string, bucketName string, sourceFile string) (*SImage, error) {
|
||||
body := map[string]interface{}{
|
||||
"timeout": "7200s",
|
||||
"steps": []struct {
|
||||
Args []string
|
||||
Name string
|
||||
}{
|
||||
{
|
||||
Args: []string{
|
||||
fmt.Sprintf("-source_file=gs://%s/%s", bucketName, sourceFile),
|
||||
"-data_disk",
|
||||
"-timeout=7056s",
|
||||
"-image_name=" + name,
|
||||
"-no_guest_environment",
|
||||
"-client_id=onecloud",
|
||||
"-description=" + desc,
|
||||
},
|
||||
Name: "gcr.io/compute-image-tools/gce_vm_image_import:release",
|
||||
},
|
||||
},
|
||||
"tags": []string{"gce-daisy", "gce-daisy-image-import"},
|
||||
}
|
||||
err := region.CloudbuildInsert(jsonutils.Marshal(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return region.GetImage(fmt.Sprintf("projects/%s/global/images/%s", region.GetProjectId(), name))
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/fileutils"
|
||||
"yunion.io/x/pkg/util/osprofile"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
@@ -30,9 +32,17 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/onecloud/pkg/util/cloudinit"
|
||||
"yunion.io/x/onecloud/pkg/util/imagetools"
|
||||
)
|
||||
|
||||
const (
|
||||
METADATA_SSH_KEYS = "ssh-keys"
|
||||
METADATA_STARTUP_SCRIPT = "startup-script"
|
||||
METADATA_POWER_SHELL = "sysprep-specialize-script-ps1"
|
||||
METADATA_STARTUP_SCRIPT_POWER_SHELL = "windows-startup-script-ps1"
|
||||
)
|
||||
|
||||
type AccessConfig struct {
|
||||
Type string
|
||||
Name string
|
||||
@@ -65,6 +75,16 @@ type SInstanceTag struct {
|
||||
Fingerprint string
|
||||
}
|
||||
|
||||
type SMetadataItem struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type SMetadata struct {
|
||||
Fingerprint string
|
||||
Items []SMetadataItem
|
||||
}
|
||||
|
||||
type SInstance struct {
|
||||
multicloud.SInstanceBase
|
||||
host *SHost
|
||||
@@ -80,7 +100,7 @@ type SInstance struct {
|
||||
CanIpForward bool
|
||||
NetworkInterfaces []SNetworkInterface
|
||||
Disks []InstanceDisk
|
||||
Metadata map[string]string
|
||||
Metadata SMetadata
|
||||
ServiceAccounts []ServiceAccount
|
||||
Scheduling map[string]interface{}
|
||||
CpuPlatform string
|
||||
@@ -153,7 +173,7 @@ func (instance *SInstance) GetStatus() string {
|
||||
case "SUSPENDED":
|
||||
return api.VM_SUSPEND
|
||||
case "TERMINATED":
|
||||
return api.VM_DELETING
|
||||
return api.VM_READY
|
||||
default:
|
||||
return api.VM_UNKNOWN
|
||||
}
|
||||
@@ -186,17 +206,20 @@ func (instance *SInstance) GetIHostId() string {
|
||||
func (instance *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
idisks := []cloudprovider.ICloudDisk{}
|
||||
for _, disk := range instance.Disks {
|
||||
disk, err := instance.host.zone.region.GetDisk(disk.Source)
|
||||
_disk, err := instance.host.zone.region.GetDisk(disk.Source)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetDisk")
|
||||
}
|
||||
storage, err := instance.host.zone.region.GetStorage(disk.Type)
|
||||
storage, err := instance.host.zone.region.GetStorage(_disk.Type)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetStorage")
|
||||
}
|
||||
storage.zone = instance.host.zone
|
||||
disk.storage = storage
|
||||
idisks = append(idisks, disk)
|
||||
_disk.storage = storage
|
||||
_disk.autoDelete = disk.AutoDelete
|
||||
_disk.boot = disk.Boot
|
||||
_disk.index = disk.Index
|
||||
idisks = append(idisks, _disk)
|
||||
}
|
||||
return idisks, nil
|
||||
}
|
||||
@@ -219,6 +242,7 @@ func (instance *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
|
||||
return nil, errors.Wrapf(err, "region.GetEip(%s)", conf.NatIP)
|
||||
}
|
||||
if len(eips) == 1 {
|
||||
eips[0].region = instance.host.zone.region
|
||||
return &eips[0], nil
|
||||
}
|
||||
eip := &SAddress{
|
||||
@@ -297,7 +321,29 @@ func (instance *SInstance) GetInstanceType() string {
|
||||
}
|
||||
|
||||
func (instance *SInstance) AssignSecurityGroup(id string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
for _, secgrpType := range []string{SECGROUP_TYPE_TAG, SECGROUP_TYPE_SERVICE_ACCOUNT} {
|
||||
if strings.Contains(id, fmt.Sprintf("/%s/", secgrpType)) {
|
||||
idx := strings.LastIndex(id, "/") + 1
|
||||
if idx <= 0 {
|
||||
return fmt.Errorf("invalid secgroup %s with id %s", secgrpType, id)
|
||||
}
|
||||
secgroup := id[idx:]
|
||||
switch secgrpType {
|
||||
case SECGROUP_TYPE_TAG:
|
||||
tag := strings.ToLower(secgroup)
|
||||
if !utils.IsInStringArray(tag, instance.Tags.Items) {
|
||||
instance.Tags.Items = append(instance.Tags.Items, tag)
|
||||
return instance.host.zone.region.SetTags(instance.SelfLink, instance.Tags)
|
||||
}
|
||||
case SECGROUP_TYPE_SERVICE_ACCOUNT:
|
||||
if len(instance.ServiceAccounts) > 0 {
|
||||
return fmt.Errorf("instance %s has already set serviceAccount %s", instance.Name, instance.ServiceAccounts[0].Email)
|
||||
}
|
||||
return instance.host.zone.region.SetServiceAccount(instance.SelfLink, secgroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("unknown secgroup type %s", id)
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetSecurityGroupIds() ([]string, error) {
|
||||
@@ -318,14 +364,14 @@ func (instance *SInstance) GetSecurityGroupIds() ([]string, error) {
|
||||
if len(instance.ServiceAccounts) > 0 && isecgroup.GetName() == instance.ServiceAccounts[0].Email {
|
||||
secgroupIds = append(secgroupIds, isecgroup.GetGlobalId())
|
||||
}
|
||||
if isecgroup.GetName() == globalnetwork.Name {
|
||||
if isecgroup.GetName() == globalnetwork.Name && !strings.Contains(isecgroup.GetGlobalId(), fmt.Sprintf("/%s/", SECGROUP_TYPE_TAG)) {
|
||||
secgroupIds = append(secgroupIds, isecgroup.GetGlobalId())
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(instance.NetworkInterfaces) == 1 {
|
||||
for _, secgroup := range isecgroups {
|
||||
if utils.IsInStringArray(secgroup.GetName(), instance.Tags.Items) {
|
||||
if utils.IsInStringArray(secgroup.GetName(), instance.Tags.Items) && strings.Contains(secgroup.GetGlobalId(), fmt.Sprintf("/%s/", SECGROUP_TYPE_TAG)) {
|
||||
secgroupIds = append(secgroupIds, secgroup.GetGlobalId())
|
||||
}
|
||||
}
|
||||
@@ -334,7 +380,46 @@ func (instance *SInstance) GetSecurityGroupIds() ([]string, error) {
|
||||
}
|
||||
|
||||
func (instance *SInstance) SetSecurityGroups(ids []string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
secgroups := map[string][]string{}
|
||||
for _, id := range ids {
|
||||
for _, secgrpType := range []string{SECGROUP_TYPE_TAG, SECGROUP_TYPE_SERVICE_ACCOUNT} {
|
||||
if strings.Contains(id, fmt.Sprintf("/%s/", secgrpType)) {
|
||||
idx := strings.LastIndex(id, "/") + 1
|
||||
if idx <= 0 {
|
||||
return fmt.Errorf("invalid secgroup %s with id %s", secgrpType, id)
|
||||
}
|
||||
secgroup := id[idx:]
|
||||
if len(secgroup) == 0 {
|
||||
return fmt.Errorf("invalid secgroup %s with id %s", secgrpType, id)
|
||||
}
|
||||
if _, ok := secgroups[secgrpType]; !ok {
|
||||
secgroups[secgrpType] = []string{}
|
||||
}
|
||||
if !utils.IsInStringArray(secgroup, secgroups[secgrpType]) {
|
||||
secgroups[secgrpType] = append(secgroups[secgrpType], secgroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if tags, ok := secgroups[SECGROUP_TYPE_TAG]; ok && len(tags) > 0 {
|
||||
for _, tag := range tags {
|
||||
tag = strings.ToLower(tag)
|
||||
if !utils.IsInStringArray(tag, instance.Tags.Items) {
|
||||
instance.Tags.Items = append(instance.Tags.Items, tag)
|
||||
}
|
||||
}
|
||||
err := instance.host.zone.region.SetTags(instance.SelfLink, instance.Tags)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SetTags")
|
||||
}
|
||||
}
|
||||
if serviceAccounts, ok := secgroups[SECGROUP_TYPE_SERVICE_ACCOUNT]; ok && len(serviceAccounts) > 0 {
|
||||
if len(serviceAccounts) > 1 {
|
||||
return fmt.Errorf("can not set multi service account for google instance")
|
||||
}
|
||||
return instance.host.zone.region.SetServiceAccount(instance.SelfLink, serviceAccounts[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetHypervisor() string {
|
||||
@@ -342,46 +427,90 @@ func (instance *SInstance) GetHypervisor() string {
|
||||
}
|
||||
|
||||
func (instance *SInstance) StartVM(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return instance.host.zone.region.StartInstance(instance.SelfLink)
|
||||
}
|
||||
|
||||
func (instance *SInstance) StopVM(ctx context.Context, isForce bool) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return instance.host.zone.region.StopInstance(instance.SelfLink)
|
||||
}
|
||||
|
||||
func (instance *SInstance) DeleteVM(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return instance.host.zone.region.Delete(instance.SelfLink)
|
||||
}
|
||||
|
||||
func (instance *SInstance) UpdateVM(ctx context.Context, name string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (instance *SInstance) UpdateUserData(userData string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
items := []SMetadataItem{}
|
||||
for _, item := range instance.Metadata.Items {
|
||||
if item.Key != METADATA_STARTUP_SCRIPT && item.Key != METADATA_POWER_SHELL && item.Key != METADATA_STARTUP_SCRIPT_POWER_SHELL {
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
if len(userData) > 0 {
|
||||
items = append(items, SMetadataItem{Key: METADATA_STARTUP_SCRIPT, Value: userData})
|
||||
items = append(items, SMetadataItem{Key: METADATA_STARTUP_SCRIPT_POWER_SHELL, Value: userData})
|
||||
items = append(items, SMetadataItem{Key: METADATA_POWER_SHELL, Value: userData})
|
||||
}
|
||||
instance.Metadata.Items = items
|
||||
return instance.host.zone.region.SetMetadata(instance.SelfLink, instance.Metadata)
|
||||
}
|
||||
|
||||
func (instance *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
func (instance *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
diskId, err := instance.host.zone.region.RebuildRoot(instance.SelfLink, desc.ImageId, desc.SysSizeGB)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "region.RebuildRoot")
|
||||
}
|
||||
return diskId, instance.DeployVM(ctx, "", desc.Account, desc.Password, desc.PublicKey, false, "")
|
||||
}
|
||||
|
||||
func (instance *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
conf := cloudinit.SCloudConfig{}
|
||||
user := cloudinit.NewUser(username)
|
||||
if len(password) > 0 {
|
||||
user.Password(password)
|
||||
}
|
||||
if len(publicKey) > 0 {
|
||||
user.SshKey(publicKey)
|
||||
}
|
||||
if len(password) > 0 || len(publicKey) > 0 {
|
||||
conf.MergeUser(user)
|
||||
items := []SMetadataItem{}
|
||||
instance.Refresh()
|
||||
for _, item := range instance.Metadata.Items {
|
||||
if item.Key != METADATA_STARTUP_SCRIPT_POWER_SHELL && item.Key != METADATA_STARTUP_SCRIPT {
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
items = append(items, SMetadataItem{Key: METADATA_STARTUP_SCRIPT_POWER_SHELL, Value: conf.UserDataPowerShell()})
|
||||
items = append(items, SMetadataItem{Key: METADATA_STARTUP_SCRIPT, Value: conf.UserDataScript()})
|
||||
instance.Metadata.Items = items
|
||||
return instance.host.zone.region.SetMetadata(instance.SelfLink, instance.Metadata)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return instance.host.zone.region.ChangeInstanceConfig(instance.SelfLink, instance.host.zone.Name, config.InstanceType, config.Cpu, config.MemoryMB)
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (instance *SInstance) AttachDisk(ctx context.Context, diskId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return instance.host.zone.region.AttachDisk(instance.SelfLink, diskId, false)
|
||||
}
|
||||
|
||||
func (instance *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
for _, disk := range instance.Disks {
|
||||
if strings.HasSuffix(disk.Source, diskId) {
|
||||
return instance.host.zone.region.DetachDisk(instance.SelfLink, disk.DeviceName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error {
|
||||
@@ -389,9 +518,363 @@ func (instance *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid stri
|
||||
}
|
||||
|
||||
func (instance *SInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDiskInfo(disk string) (cloudprovider.SDiskInfo, error) {
|
||||
result := cloudprovider.SDiskInfo{}
|
||||
diskInfo := strings.Split(disk, ":")
|
||||
for _, d := range diskInfo {
|
||||
if utils.IsInStringArray(d, []string{api.STORAGE_GOOGLE_PD_STANDARD, api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_LOCAL_SSD}) {
|
||||
result.StorageType = d
|
||||
} else if memSize, err := fileutils.GetSizeMb(d, 'M', 1024); err == nil {
|
||||
result.SizeGB = memSize >> 10
|
||||
} else {
|
||||
result.Name = d
|
||||
}
|
||||
}
|
||||
if len(result.StorageType) == 0 {
|
||||
result.StorageType = api.STORAGE_GOOGLE_PD_STANDARD
|
||||
}
|
||||
|
||||
if result.SizeGB == 0 {
|
||||
return result, fmt.Errorf("Missing disk size")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateInstance(zone, name, desc, instanceType string, cpu, memoryMb int, networkId string, ipAddr, imageId string, disks []string) (*SInstance, error) {
|
||||
if len(instanceType) == 0 && (cpu == 0 || memoryMb == 0) {
|
||||
return nil, fmt.Errorf("Missing instanceType or cpu &memory info")
|
||||
}
|
||||
if len(disks) == 0 {
|
||||
return nil, fmt.Errorf("Missing disk info")
|
||||
}
|
||||
sysDisk, err := getDiskInfo(disks[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getDiskInfo.sys")
|
||||
}
|
||||
dataDisks := []cloudprovider.SDiskInfo{}
|
||||
for _, d := range disks[1:] {
|
||||
dataDisk, err := getDiskInfo(d)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "getDiskInfo(%s)", d)
|
||||
}
|
||||
dataDisks = append(dataDisks, dataDisk)
|
||||
}
|
||||
conf := &cloudprovider.SManagedVMCreateConfig{
|
||||
Name: name,
|
||||
Description: desc,
|
||||
ExternalImageId: imageId,
|
||||
Cpu: cpu,
|
||||
MemoryMB: memoryMb,
|
||||
ExternalNetworkId: networkId,
|
||||
IpAddr: ipAddr,
|
||||
SysDisk: sysDisk,
|
||||
DataDisks: dataDisks,
|
||||
}
|
||||
return region._createVM(zone, conf)
|
||||
}
|
||||
|
||||
func (region *SRegion) getSecgroupByIds(ids []string) (map[string][]string, error) {
|
||||
secgroups := map[string][]string{}
|
||||
for _, id := range ids {
|
||||
for _, secgrpType := range []string{SECGROUP_TYPE_TAG, SECGROUP_TYPE_SERVICE_ACCOUNT} {
|
||||
if strings.Contains(id, fmt.Sprintf("/%s/", secgrpType)) {
|
||||
idx := strings.LastIndex(id, "/") + 1
|
||||
if idx <= 0 {
|
||||
return nil, fmt.Errorf("invalid secgroup %s for %s", id, secgrpType)
|
||||
}
|
||||
secgroup := id[idx:]
|
||||
if len(secgroup) == 0 {
|
||||
return nil, fmt.Errorf("invalid secgroup %s for %s", id, secgrpType)
|
||||
}
|
||||
if _, ok := secgroups[secgrpType]; !ok {
|
||||
secgroups[secgrpType] = []string{}
|
||||
}
|
||||
if !utils.IsInStringArray(secgroup, secgroups[secgrpType]) {
|
||||
secgroups[secgrpType] = append(secgroups[secgrpType], secgroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return secgroups, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) _createVM(zone string, desc *cloudprovider.SManagedVMCreateConfig) (*SInstance, error) {
|
||||
network, err := region.GetNetwork(desc.ExternalNetworkId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "region.GetNetwork")
|
||||
}
|
||||
secgroups, err := region.getSecgroupByIds(desc.ExternalSecgroupIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getSecgroupByIds")
|
||||
}
|
||||
serviceAccounts, ok := secgroups[SECGROUP_TYPE_SERVICE_ACCOUNT]
|
||||
if ok && len(serviceAccounts) > 1 {
|
||||
return nil, fmt.Errorf("Security groups are distributed across multiple service accounts")
|
||||
}
|
||||
if len(desc.InstanceType) == 0 {
|
||||
desc.InstanceType = fmt.Sprintf("custom-%d-%d", desc.Cpu, desc.MemoryMB)
|
||||
}
|
||||
disks := []map[string]interface{}{}
|
||||
if len(desc.SysDisk.Name) == 0 {
|
||||
desc.SysDisk.Name = fmt.Sprintf("vdisk-%s-%d", desc.Name, time.Now().UnixNano())
|
||||
}
|
||||
disks = append(disks, map[string]interface{}{
|
||||
"boot": true,
|
||||
"initializeParams": map[string]interface{}{
|
||||
"diskName": strings.Replace(desc.SysDisk.Name, "_", "-", -1),
|
||||
"sourceImage": desc.ExternalImageId,
|
||||
"diskSizeGb": desc.SysDisk.SizeGB,
|
||||
"diskType": fmt.Sprintf("zones/%s/diskTypes/%s", zone, desc.SysDisk.StorageType),
|
||||
},
|
||||
"autoDelete": true,
|
||||
})
|
||||
for _, disk := range desc.DataDisks {
|
||||
if len(disk.Name) == 0 {
|
||||
disk.Name = fmt.Sprintf("vdisk-%s-%d", desc.Name, time.Now().UnixNano())
|
||||
}
|
||||
disks = append(disks, map[string]interface{}{
|
||||
"boot": false,
|
||||
"initializeParams": map[string]interface{}{
|
||||
"diskName": strings.Replace(disk.Name, "_", "-", -1),
|
||||
"diskSizeGb": disk.SizeGB,
|
||||
"diskType": fmt.Sprintf("zones/%s/diskTypes/%s", zone, disk.StorageType),
|
||||
},
|
||||
"autoDelete": true,
|
||||
})
|
||||
}
|
||||
networkInterface := map[string]string{
|
||||
"network": network.Network,
|
||||
"subnetwork": network.SelfLink,
|
||||
}
|
||||
if len(desc.IpAddr) > 0 {
|
||||
networkInterface["networkIp"] = desc.IpAddr
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"name": desc.Name,
|
||||
"description": desc.Description,
|
||||
"machineType": fmt.Sprintf("zones/%s/machineTypes/%s", zone, desc.InstanceType),
|
||||
"networkInterfaces": []map[string]string{
|
||||
networkInterface,
|
||||
},
|
||||
"disks": disks,
|
||||
}
|
||||
if tags, ok := secgroups[SECGROUP_TYPE_TAG]; ok && len(tags) > 0 {
|
||||
for i := range tags {
|
||||
tags[i] = strings.ToLower(tags[i])
|
||||
}
|
||||
params["tags"] = map[string][]string{
|
||||
"items": tags,
|
||||
}
|
||||
}
|
||||
if len(desc.UserData) > 0 {
|
||||
params["metadata"] = map[string]interface{}{
|
||||
"items": []struct {
|
||||
Key string
|
||||
Value string
|
||||
}{
|
||||
{
|
||||
Key: METADATA_STARTUP_SCRIPT,
|
||||
Value: desc.UserData,
|
||||
},
|
||||
{
|
||||
Key: METADATA_POWER_SHELL,
|
||||
Value: desc.UserData,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
if len(serviceAccounts) > 0 {
|
||||
params["serviceAccounts"] = []struct {
|
||||
Email string
|
||||
Scopes []string
|
||||
}{
|
||||
{
|
||||
Email: serviceAccounts[0],
|
||||
Scopes: []string{
|
||||
"https://www.googleapis.com/auth/devstorage.read_only",
|
||||
"https://www.googleapis.com/auth/logging.write",
|
||||
"https://www.googleapis.com/auth/monitoring.write",
|
||||
"https://www.googleapis.com/auth/servicecontrol",
|
||||
"https://www.googleapis.com/auth/service.management.readonly",
|
||||
"https://www.googleapis.com/auth/trace.append",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
log.Debugf("create google instance params: %s", jsonutils.Marshal(params).String())
|
||||
instance := &SInstance{}
|
||||
resource := fmt.Sprintf("zones/%s/instances", zone)
|
||||
err = region.Insert(resource, jsonutils.Marshal(params), instance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) StartInstance(id string) error {
|
||||
params := map[string]string{}
|
||||
return region.Do(id, "start", nil, jsonutils.Marshal(params))
|
||||
}
|
||||
|
||||
func (region *SRegion) StopInstance(id string) error {
|
||||
params := map[string]string{}
|
||||
return region.Do(id, "stop", nil, jsonutils.Marshal(params))
|
||||
}
|
||||
|
||||
func (region *SRegion) ResetInstance(id string) error {
|
||||
params := map[string]string{}
|
||||
return region.Do(id, "reset", nil, jsonutils.Marshal(params))
|
||||
}
|
||||
|
||||
func (region *SRegion) DetachDisk(instanceId, deviceName string) error {
|
||||
body := map[string]string{}
|
||||
params := map[string]string{"deviceName": deviceName}
|
||||
return region.Do(instanceId, "detachDisk", params, jsonutils.Marshal(body))
|
||||
}
|
||||
|
||||
func (instance *SInstance) GetSerialOutput(port int) (string, error) {
|
||||
return instance.host.zone.region.GetSerialPortOutput(instance.SelfLink, port)
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSerialPortOutput(id string, port int) (string, error) {
|
||||
_content, content, next := "", "", 0
|
||||
var err error = nil
|
||||
for {
|
||||
_content, next, err = region.getSerialPortOutput(id, port, next)
|
||||
if err != nil {
|
||||
return content, err
|
||||
}
|
||||
content += _content
|
||||
if len(_content) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) getSerialPortOutput(id string, port int, start int) (string, int, error) {
|
||||
resource := fmt.Sprintf("%s/serialPort?port=%d&start=%d", id, port, start)
|
||||
result := struct {
|
||||
Contents string
|
||||
Start int
|
||||
Next int
|
||||
}{}
|
||||
err := region.Get(resource, &result)
|
||||
if err != nil {
|
||||
return "", result.Next, errors.Wrap(err, "")
|
||||
}
|
||||
return result.Contents, result.Next, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) AttachDisk(instanceId, diskId string, boot bool) error {
|
||||
diskId = strings.TrimPrefix(diskId, fmt.Sprintf("%s/%s/", GOOGLE_COMPUTE_DOMAIN, GOOGLE_API_VERSION))
|
||||
diskId = fmt.Sprintf("%s/%s/%s", GOOGLE_COMPUTE_DOMAIN, GOOGLE_API_VERSION, diskId)
|
||||
body := map[string]interface{}{
|
||||
"source": diskId,
|
||||
"boot": boot,
|
||||
}
|
||||
if boot {
|
||||
body["autoDelete"] = true
|
||||
}
|
||||
params := map[string]string{"forceAttach": "true"}
|
||||
return region.Do(instanceId, "attachDisk", params, jsonutils.Marshal(body))
|
||||
}
|
||||
|
||||
func (region *SRegion) ChangeInstanceConfig(id string, zone string, instanceType string, cpu int, memoryMb int) error {
|
||||
if len(instanceType) == 0 {
|
||||
instanceType = fmt.Sprintf("custom-%d-%d", cpu, memoryMb)
|
||||
}
|
||||
params := map[string]string{
|
||||
"machineType": fmt.Sprintf("zones/%s/machineTypes/%s", zone, instanceType),
|
||||
}
|
||||
return region.Do(id, "setMachineType", nil, jsonutils.Marshal(params))
|
||||
}
|
||||
|
||||
func (region *SRegion) SetMetadata(id string, metadata SMetadata) error {
|
||||
return region.Do(id, "setMetadata", nil, jsonutils.Marshal(metadata))
|
||||
}
|
||||
|
||||
func (region *SRegion) SetTags(id string, tags SInstanceTag) error {
|
||||
return region.Do(id, "setTags", nil, jsonutils.Marshal(tags))
|
||||
}
|
||||
|
||||
func (region *SRegion) SetServiceAccount(id string, email string) error {
|
||||
body := map[string]interface{}{
|
||||
"email": email,
|
||||
"scopes": []string{
|
||||
"https://www.googleapis.com/auth/devstorage.read_only",
|
||||
"https://www.googleapis.com/auth/logging.write",
|
||||
"https://www.googleapis.com/auth/monitoring.write",
|
||||
"https://www.googleapis.com/auth/servicecontrol",
|
||||
"https://www.googleapis.com/auth/service.management.readonly",
|
||||
"https://www.googleapis.com/auth/trace.append",
|
||||
},
|
||||
}
|
||||
return region.Do(id, "setsetServiceAccount", nil, jsonutils.Marshal(body))
|
||||
}
|
||||
|
||||
func (region *SRegion) RebuildRoot(instanceId string, imageId string, sysDiskSizeGb int) (string, error) {
|
||||
oldDisk, diskType, deviceName := "", api.STORAGE_GOOGLE_PD_STANDARD, ""
|
||||
instance, err := region.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "region.GetInstance")
|
||||
}
|
||||
for _, disk := range instance.Disks {
|
||||
if disk.Boot {
|
||||
oldDisk = disk.Source
|
||||
deviceName = disk.DeviceName
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(oldDisk) > 0 {
|
||||
disk, err := region.GetDisk(oldDisk)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "region.GetDisk")
|
||||
}
|
||||
diskType = disk.Type
|
||||
if sysDiskSizeGb == 0 {
|
||||
sysDiskSizeGb = disk.SizeGB
|
||||
}
|
||||
}
|
||||
|
||||
zone, err := region.GetZone(instance.Zone)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "region.GetZone")
|
||||
}
|
||||
|
||||
diskName := fmt.Sprintf("vdisk-%s-%d", instance.Name, time.Now().UnixNano())
|
||||
disk, err := region.CreateDisk(diskName, sysDiskSizeGb, zone.Name, diskType, imageId, "create for replace instance system disk")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "region.CreateDisk.systemDisk")
|
||||
}
|
||||
|
||||
if len(deviceName) > 0 {
|
||||
err = region.DetachDisk(instanceId, deviceName)
|
||||
if err != nil {
|
||||
defer region.Delete(disk.SelfLink)
|
||||
return "", errors.Wrap(err, "region.DetachDisk")
|
||||
}
|
||||
}
|
||||
|
||||
err = region.AttachDisk(instanceId, disk.SelfLink, true)
|
||||
if err != nil {
|
||||
if len(oldDisk) > 0 {
|
||||
defer region.AttachDisk(instanceId, oldDisk, true)
|
||||
}
|
||||
defer region.Delete(disk.SelfLink)
|
||||
return "", errors.Wrap(err, "region.AttachDisk.newSystemDisk")
|
||||
}
|
||||
|
||||
if len(oldDisk) > 0 {
|
||||
defer region.Delete(oldDisk)
|
||||
}
|
||||
return disk.GetGlobalId(), nil
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func (network *SNetwork) GetStatus() string {
|
||||
}
|
||||
|
||||
func (network *SNetwork) Delete() error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return network.wire.vpc.region.Delete(network.SelfLink)
|
||||
}
|
||||
|
||||
func (network *SNetwork) GetAllocTimeoutSeconds() int {
|
||||
@@ -129,3 +129,19 @@ func (network *SNetwork) GetIsPublic() bool {
|
||||
func (network *SNetwork) GetPublicScope() rbacutils.TRbacScope {
|
||||
return rbacutils.ScopeDomain
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateNetwork(name string, vpc string, cidr string, desc string) (*SNetwork, error) {
|
||||
body := map[string]interface{}{
|
||||
"name": name,
|
||||
"description": desc,
|
||||
"network": vpc,
|
||||
"ipCidrRange": cidr,
|
||||
}
|
||||
resource := fmt.Sprintf("regions/%s/subnetworks", region.Name)
|
||||
network := &SNetwork{}
|
||||
err := region.Insert(resource, jsonutils.Marshal(body), network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return network, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
const (
|
||||
OPERATION_STATUS_RUNNING = "RUNNING"
|
||||
OPERATION_STATUS_DONE = "DONE"
|
||||
)
|
||||
|
||||
type SOperation struct {
|
||||
Id string
|
||||
Name string
|
||||
OperationType string
|
||||
TargetLink string
|
||||
TargetId string
|
||||
Status string
|
||||
User string
|
||||
Progress int
|
||||
InsertTime time.Time
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
SelfLink string
|
||||
Region string
|
||||
Kind string
|
||||
}
|
||||
|
||||
func (region *SRegion) GetOperation(id string) (*SOperation, error) {
|
||||
operation := &SOperation{}
|
||||
err := region.Get(id, &operation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return operation, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) WaitOperation(id string, resource, action string) (string, error) {
|
||||
targetLink := ""
|
||||
err := cloudprovider.Wait(time.Second*5, time.Minute*5, func() (bool, error) {
|
||||
operation, err := region.GetOperation(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
log.Debugf("%s %s operation status: %s expect %s", action, resource, operation.Status, OPERATION_STATUS_DONE)
|
||||
if operation.Status == OPERATION_STATUS_DONE {
|
||||
targetLink = operation.TargetLink
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
return targetLink, err
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
@@ -140,6 +141,45 @@ func (region *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudprovider.ICloudVpc, error) {
|
||||
globalnetwork, err := region.CreateGlobalNetwork(name, desc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "region.CreateGlobalNetwork")
|
||||
}
|
||||
vpc := &SVpc{region: region, globalnetwork: globalnetwork}
|
||||
return vpc, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
storage, err := region.GetStorage(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
zone, err := region.GetZone(storage.Zone)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "region.GetZone(%s)", storage.Zone)
|
||||
}
|
||||
zone.region = region
|
||||
storage.zone = zone
|
||||
return storage, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
izones, err := self.GetIZones()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(izones); i += 1 {
|
||||
ihost, err := izones[i].GetIHostById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (region *SRegion) GetProjectId() string {
|
||||
return region.client.projectId
|
||||
}
|
||||
@@ -240,6 +280,32 @@ func (region *SRegion) GetISnapshotById(id string) (cloudprovider.ICloudSnapshot
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) BillingList(resource string, params map[string]string, pageSize int, pageToken string, retval interface{}) error {
|
||||
if pageSize == 0 && len(pageToken) == 0 {
|
||||
return region.BillingListAll(resource, params, retval)
|
||||
}
|
||||
if params == nil {
|
||||
params = map[string]string{}
|
||||
}
|
||||
params["pageSize"] = fmt.Sprintf("%d", pageSize)
|
||||
params["pageToken"] = pageToken
|
||||
resp, err := region.client.billingList(resource, params)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "billingList")
|
||||
}
|
||||
if resp.Contains("skus") && retval != nil {
|
||||
err = resp.Unmarshal(retval, "skus")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "resp.Unmarshal")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) BillingListAll(resource string, params map[string]string, retval interface{}) error {
|
||||
return region.client.billingListAll(resource, params, retval)
|
||||
}
|
||||
|
||||
func (region *SRegion) ListAll(resource string, params map[string]string, retval interface{}) error {
|
||||
return region.client.ecsListAll(resource, params, retval)
|
||||
}
|
||||
@@ -270,6 +336,140 @@ func (region *SRegion) Get(id string, retval interface{}) error {
|
||||
return region.client.ecsGet(id, retval)
|
||||
}
|
||||
|
||||
func (region *SRegion) StorageListAll(resource string, params map[string]string, retval interface{}) error {
|
||||
return region.client.storageListAll(resource, params, retval)
|
||||
}
|
||||
|
||||
func (region *SRegion) StorageList(resource string, params map[string]string, maxResults int, pageToken string, retval interface{}) error {
|
||||
if maxResults == 0 && len(pageToken) == 0 {
|
||||
return region.client.storageListAll(resource, params, retval)
|
||||
}
|
||||
if params == nil {
|
||||
params = map[string]string{}
|
||||
}
|
||||
params["maxResults"] = fmt.Sprintf("%d", maxResults)
|
||||
params["pageToken"] = pageToken
|
||||
resp, err := region.client.storageList(resource, params)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "storageList")
|
||||
}
|
||||
if resp.Contains("items") && retval != nil {
|
||||
err = resp.Unmarshal(retval, "items")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "resp.Unmarshal")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) StorageGet(id string, retval interface{}) error {
|
||||
return region.client.storageGet(id, retval)
|
||||
}
|
||||
|
||||
func (region *SRegion) StorageDo(id string, action string, params map[string]string, body jsonutils.JSONObject) error {
|
||||
opId, err := region.client.storageDo(id, action, params, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Index(opId, "/operations/") > 0 {
|
||||
_, err = region.WaitOperation(opId, id, action)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) Do(id string, action string, params map[string]string, body jsonutils.JSONObject) error {
|
||||
opId, err := region.client.ecsDo(id, action, params, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Index(opId, "/operations/") > 0 {
|
||||
_, err = region.WaitOperation(opId, id, action)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) Patch(id string, action string, params map[string]string, body jsonutils.JSONObject) error {
|
||||
opId, err := region.client.ecsPatch(id, action, params, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Index(opId, "/operations/") > 0 {
|
||||
_, err = region.WaitOperation(opId, id, action)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) StorageDelete(id string) error {
|
||||
return region.client.storageDelete(id, nil)
|
||||
}
|
||||
|
||||
func (region *SRegion) Delete(id string) error {
|
||||
operation := &SOperation{}
|
||||
err := region.client.ecsDelete(id, operation)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "client.ecsDelete")
|
||||
}
|
||||
_, err = region.WaitOperation(operation.SelfLink, id, "delete")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "region.WaitOperation(%s)", operation.SelfLink)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) StorageInsert(resource string, body jsonutils.JSONObject, retval interface{}) error {
|
||||
return region.client.storageInsert(resource, body, retval)
|
||||
}
|
||||
|
||||
func (region *SRegion) CloudbuildInsert(body jsonutils.JSONObject) error {
|
||||
result := &struct {
|
||||
Name string
|
||||
}{}
|
||||
resource := fmt.Sprintf("projects/%s/builds", region.GetProjectId())
|
||||
err := region.client.cloudbuildInsert(resource, body, result)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "insert")
|
||||
}
|
||||
err = cloudprovider.Wait(time.Second*10, time.Minute*40, func() (bool, error) {
|
||||
operation, err := region.GetCloudbuildOperation(result.Name)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "region.GetCloudbuildOperation(%s)", result.Name)
|
||||
}
|
||||
status := operation.Metadata.Build.Status
|
||||
log.Debugf("cloudbuild %s status: %s", result.Name, status)
|
||||
if status == "FAILURE" {
|
||||
return false, fmt.Errorf("cloudbuild failed error log: %s", operation.Metadata.Build.LogUrl)
|
||||
}
|
||||
if status == "SUCCESS" {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cloudprovider.Wait")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) cloudbuildGet(id string, retval interface{}) error {
|
||||
return region.client.cloudbuildGet(id, retval)
|
||||
}
|
||||
|
||||
func (region *SRegion) Insert(resource string, body jsonutils.JSONObject, retval interface{}) error {
|
||||
operation := &SOperation{}
|
||||
err := region.client.ecsInsert(resource, body, operation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceId, err := region.WaitOperation(operation.SelfLink, resource, "insert")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "region.WaitOperation(%s)", operation.SelfLink)
|
||||
}
|
||||
return region.Get(resourceId, retval)
|
||||
}
|
||||
|
||||
func (region *SRegion) fetchResourcePolicies() ([]SResourcePolicy, error) {
|
||||
if len(region.client.resourcepolices) > 0 {
|
||||
return region.client.resourcepolices, nil
|
||||
@@ -307,3 +507,11 @@ func (region *SRegion) GetISnapshotPolicyById(id string) (cloudprovider.ICloudSn
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateEIP(args *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) {
|
||||
eip, err := region.CreateEip(args.Name, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return eip, nil
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
const (
|
||||
SECGROUP_TYPE_SERVICE_ACCOUNT = "serviceAccount"
|
||||
SECGROUP_TYPE_TAG = "tag"
|
||||
)
|
||||
|
||||
type SFirewallAction struct {
|
||||
IPProtocol string
|
||||
Ports []string
|
||||
@@ -66,7 +71,7 @@ func (f FirewallSet) Swap(i, j int) {
|
||||
|
||||
func (f FirewallSet) Less(i, j int) bool {
|
||||
if f[i].Priority != f[j].Priority {
|
||||
return f[i].Priority < f[j].Priority
|
||||
return f[i].Priority > f[j].Priority
|
||||
}
|
||||
return len(f[i].Allowed) < len(f[j].Allowed)
|
||||
}
|
||||
@@ -103,7 +108,7 @@ func (firewall *SFirewall) _toRules(action secrules.TSecurityRuleAction) ([]secr
|
||||
rule := secrules.SecurityRule{
|
||||
Action: action,
|
||||
Direction: secrules.DIR_IN,
|
||||
Description: firewall.Description,
|
||||
Description: firewall.SelfLink,
|
||||
Priority: firewall.Priority,
|
||||
}
|
||||
if firewall.Direction == "EGRESS" {
|
||||
@@ -148,6 +153,12 @@ func (firewall *SFirewall) _toRules(action secrules.TSecurityRuleAction) ([]secr
|
||||
rule.PortEnd = -1
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
if len(allow.Ports) == 0 {
|
||||
rule.Ports = []int{}
|
||||
rule.PortStart = -1
|
||||
rule.PortEnd = -1
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rules, nil
|
||||
@@ -174,10 +185,10 @@ func (secgroup *SSecurityGroup) GetId() string {
|
||||
|
||||
func (secgroup *SSecurityGroup) GetGlobalId() string {
|
||||
if len(secgroup.Tag) > 0 {
|
||||
return fmt.Sprintf("%s/%s", secgroup.GetId(), secgroup.Tag)
|
||||
return fmt.Sprintf("%s/%s/%s", secgroup.GetId(), SECGROUP_TYPE_TAG, secgroup.Tag)
|
||||
}
|
||||
if len(secgroup.ServiceAccount) > 0 {
|
||||
return fmt.Sprintf("%s/%s", secgroup.GetId(), secgroup.ServiceAccount)
|
||||
return fmt.Sprintf("%s/%s/%s", secgroup.GetId(), SECGROUP_TYPE_SERVICE_ACCOUNT, secgroup.ServiceAccount)
|
||||
}
|
||||
return secgroup.GetId()
|
||||
}
|
||||
@@ -213,6 +224,16 @@ func (secgroup *SSecurityGroup) Refresh() error {
|
||||
}
|
||||
|
||||
func (secgroup *SSecurityGroup) Delete() error {
|
||||
rules, err := secgroup.GetRules()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetRules")
|
||||
}
|
||||
for _, rule := range rules {
|
||||
err = secgroup.vpc.region.DeleteSecgroupRule(rule.Description, rule)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "DeleteSecgroupRule(%s)", rule.Description)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -258,8 +279,100 @@ func (self *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) {
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteSecgroupRule(ruleId string, rule secrules.SecurityRule) error {
|
||||
firwall, err := region.GetFirewall(ruleId)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "region.GetFirewall")
|
||||
}
|
||||
currentRule, err := firwall.toRules()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "firwall.toRules")
|
||||
}
|
||||
if len(currentRule) > 1 {
|
||||
for _, _rule := range currentRule {
|
||||
if _rule.String() != rule.String() {
|
||||
for _, tag := range firwall.TargetTags {
|
||||
err = region.CreateSecurityGroupRule(_rule, firwall.Network, tag, "")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "region.CreateSecurityGroupRule")
|
||||
}
|
||||
}
|
||||
for _, serviceAccount := range firwall.TargetServiceAccounts {
|
||||
err = region.CreateSecurityGroupRule(_rule, firwall.Network, "", serviceAccount)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "region.CreateSecurityGroupRule")
|
||||
}
|
||||
}
|
||||
if len(firwall.TargetTags)+len(firwall.TargetServiceAccounts) == 0 {
|
||||
err = region.CreateSecurityGroupRule(_rule, firwall.Network, "", "")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "region.CreateSecurityGroupRule")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return region.Delete(firwall.SelfLink)
|
||||
}
|
||||
|
||||
func (secgroup *SSecurityGroup) SyncRules(rules []secrules.SecurityRule) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
if len(rules) == 0 {
|
||||
rules = append(rules, *secrules.MustParseSecurityRule("in:deny any"))
|
||||
}
|
||||
currentRule, err := secgroup.GetRules()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "secgroup.GetRules")
|
||||
}
|
||||
sort.Sort(secrules.SecurityRuleSet(rules))
|
||||
region := secgroup.vpc.region
|
||||
deleteRules := map[string]secrules.SecurityRule{}
|
||||
addRules := []secrules.SecurityRule{}
|
||||
i, j := 0, 0
|
||||
for i < len(rules) || j < len(currentRule) {
|
||||
if i < len(rules) && j < len(currentRule) {
|
||||
currentRuleStr := currentRule[j].String()
|
||||
ruleStr := rules[i].String()
|
||||
cmp := strings.Compare(currentRuleStr, ruleStr)
|
||||
if cmp == 0 {
|
||||
i += 1
|
||||
j += 1
|
||||
} else if cmp > 0 {
|
||||
// delete rule
|
||||
deleteRules[currentRule[j].Description] = currentRule[j]
|
||||
j += 1
|
||||
} else {
|
||||
rules[i].Priority = 101 - rules[i].Priority
|
||||
addRules = append(addRules, rules[i])
|
||||
i += 1
|
||||
}
|
||||
} else if i >= len(rules) {
|
||||
// delete rule
|
||||
deleteRules[currentRule[j].Description] = currentRule[j]
|
||||
j += 1
|
||||
} else if j >= len(currentRule) {
|
||||
// add rule
|
||||
rules[i].Priority = 101 - rules[i].Priority
|
||||
addRules = append(addRules, rules[i])
|
||||
err = region.CreateSecurityGroupRule(rules[i], secgroup.vpc.globalnetwork.SelfLink, secgroup.Tag, secgroup.ServiceAccount)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "region.CreateSecurityGroupRule(%s)", rules[i].String())
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
for id, rule := range deleteRules {
|
||||
err = region.DeleteSecgroupRule(id, rule)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "DeleteSecgroupRule(%s)", id)
|
||||
}
|
||||
}
|
||||
for _, rule := range addRules {
|
||||
err = region.CreateSecurityGroupRule(rule, secgroup.vpc.globalnetwork.SelfLink, secgroup.Tag, secgroup.ServiceAccount)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "CreateSecurityGroupRule(%s)", rule.String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetISecurityGroupById(id string) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
@@ -293,13 +406,94 @@ func (region *SRegion) GetISecurityGroupByName(vpcId string, name string) (cloud
|
||||
return nil, errors.Wrap(err, "ivpc.GetISecurityGroups")
|
||||
}
|
||||
for _, secgroup := range secgroups {
|
||||
if secgroup.GetName() == name {
|
||||
if strings.ToLower(secgroup.GetName()) == strings.ToLower(name) {
|
||||
return secgroup, nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
return region.GetISecurityGroupByName(conf.VpcId, "")
|
||||
func (region *SRegion) CreateSecurityGroupRule(rule secrules.SecurityRule, vpcId string, tag string, serviceAccount string) error {
|
||||
name := fmt.Sprintf("%s-%d", rule.String(), rule.Priority)
|
||||
if len(tag) > 0 {
|
||||
name = fmt.Sprintf("for-tag-%s-%s", tag, name)
|
||||
}
|
||||
if len(serviceAccount) > 0 {
|
||||
name = fmt.Sprintf("for-service-account-%s-%s", serviceAccount, name)
|
||||
}
|
||||
|
||||
body := map[string]interface{}{
|
||||
"name": strings.ToLower(name),
|
||||
"priority": rule.Priority,
|
||||
"network": vpcId,
|
||||
"direction": "INGRESS",
|
||||
}
|
||||
if len(tag) > 0 {
|
||||
body["targetTags"] = []string{strings.ToLower(tag)}
|
||||
}
|
||||
if len(serviceAccount) > 0 {
|
||||
body["targetServiceAccounts"] = []string{serviceAccount}
|
||||
}
|
||||
if rule.Direction == secrules.DIR_OUT {
|
||||
body["direction"] = "EGRESS"
|
||||
} else {
|
||||
body["sourceRanges"] = []string{rule.IPNet.String()}
|
||||
}
|
||||
|
||||
protocol := string(rule.Protocol)
|
||||
if protocol == secrules.PROTO_ANY {
|
||||
protocol = "all"
|
||||
}
|
||||
|
||||
ports := []string{}
|
||||
if len(rule.Ports) > 0 {
|
||||
for _, port := range rule.Ports {
|
||||
ports = append(ports, fmt.Sprintf("%d", port))
|
||||
}
|
||||
} else if rule.PortStart > 0 && rule.PortEnd > 0 {
|
||||
if rule.PortStart == rule.PortEnd {
|
||||
ports = append(ports, fmt.Sprintf("%d", rule.PortStart))
|
||||
} else {
|
||||
ports = append(ports, fmt.Sprintf("%d-%d", rule.PortStart, rule.PortEnd))
|
||||
}
|
||||
}
|
||||
|
||||
actionInfo := []struct {
|
||||
IPProtocol string
|
||||
Ports []string
|
||||
}{
|
||||
{
|
||||
IPProtocol: protocol,
|
||||
Ports: ports,
|
||||
},
|
||||
}
|
||||
|
||||
if rule.Action == secrules.SecurityRuleDeny {
|
||||
body["denied"] = actionInfo
|
||||
} else {
|
||||
body["allowed"] = actionInfo
|
||||
}
|
||||
|
||||
firwall := &SFirewall{}
|
||||
err := region.Insert("global/firewalls", jsonutils.Marshal(body), firwall)
|
||||
if err != nil {
|
||||
if strings.Index(err.Error(), "already exists") >= 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrap(err, "region.Insert")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
conf.VpcId = fmt.Sprintf("%s/%s", region.GetGlobalId(), conf.VpcId)
|
||||
ivpc, err := region.GetIVpcById(conf.VpcId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "region.GetIVpcById(%s)", conf.VpcId)
|
||||
}
|
||||
|
||||
vpc := ivpc.(*SVpc)
|
||||
|
||||
secgroup := &SSecurityGroup{vpc: vpc, Tag: strings.ToLower(conf.Name)}
|
||||
return secgroup, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/multicloud/google"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type BucketListOptions struct {
|
||||
MaxResults int
|
||||
PageToken string
|
||||
}
|
||||
|
||||
shellutils.R(&BucketListOptions{}, "bucket-list", "List buckets", func(cli *google.SRegion, args *BucketListOptions) error {
|
||||
buckets, err := cli.GetBuckets(args.MaxResults, args.PageToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(buckets, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type BucketCreateOptions struct {
|
||||
NAME string
|
||||
StorageClass string `choices:"STANDARD|NEARLINE|COLDLINE"`
|
||||
}
|
||||
|
||||
shellutils.R(&BucketCreateOptions{}, "bucket-create", "Create buckets", func(cli *google.SRegion, args *BucketCreateOptions) error {
|
||||
bucket, err := cli.CreateBucket(args.NAME, args.StorageClass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(bucket)
|
||||
return nil
|
||||
})
|
||||
|
||||
type BucketNameOptions struct {
|
||||
NAME string
|
||||
}
|
||||
|
||||
shellutils.R(&BucketNameOptions{}, "bucket-show", "Show bucket", func(cli *google.SRegion, args *BucketNameOptions) error {
|
||||
bucket, err := cli.GetBucket(args.NAME)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(bucket)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&BucketNameOptions{}, "bucket-delete", "Delete bucket", func(cli *google.SRegion, args *BucketNameOptions) error {
|
||||
return cli.DeleteBucket(args.NAME)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/multicloud/google"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type CloudbuildOperationShowOptions struct {
|
||||
NAME string
|
||||
}
|
||||
|
||||
shellutils.R(&CloudbuildOperationShowOptions{}, "cloud-build-operation-show", "Show cloudbuild operation", func(cli *google.SRegion, args *CloudbuildOperationShowOptions) error {
|
||||
operation, err := cli.GetCloudbuildOperation(args.NAME)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(operation)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -35,10 +35,10 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskShowOptions struct {
|
||||
type DiskIdOptions struct {
|
||||
ID string
|
||||
}
|
||||
shellutils.R(&DiskShowOptions{}, "disk-show", "Show disk", func(cli *google.SRegion, args *DiskShowOptions) error {
|
||||
shellutils.R(&DiskIdOptions{}, "disk-show", "Show disk", func(cli *google.SRegion, args *DiskIdOptions) error {
|
||||
disk, err := cli.GetDisk(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -47,6 +47,37 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&DiskIdOptions{}, "disk-delete", "Delete disk", func(cli *google.SRegion, args *DiskIdOptions) error {
|
||||
return cli.Delete(args.ID)
|
||||
})
|
||||
|
||||
type DiskCreateOptions struct {
|
||||
NAME string
|
||||
Desc string
|
||||
ZONE string
|
||||
SIZE_GB int
|
||||
Image string
|
||||
STORAGE_TYPE string `choices:"pd-standard|pd-ssd"`
|
||||
}
|
||||
|
||||
shellutils.R(&DiskCreateOptions{}, "disk-create", "Create disks", func(cli *google.SRegion, args *DiskCreateOptions) error {
|
||||
disk, err := cli.CreateDisk(args.NAME, args.SIZE_GB, args.ZONE, args.STORAGE_TYPE, args.Image, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskResizeOptions struct {
|
||||
ID string
|
||||
SIZE_GB int
|
||||
}
|
||||
|
||||
shellutils.R(&DiskResizeOptions{}, "disk-resize", "Resize disk", func(cli *google.SRegion, args *DiskResizeOptions) error {
|
||||
return cli.ResizeDisk(args.ID, args.SIZE_GB)
|
||||
})
|
||||
|
||||
type RegionDiskListOptions struct {
|
||||
StorageType string
|
||||
MaxResults int
|
||||
|
||||
@@ -34,10 +34,10 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipShowOptions struct {
|
||||
type EipIdOptions struct {
|
||||
ID string
|
||||
}
|
||||
shellutils.R(&EipShowOptions{}, "eip-show", "Show eip", func(cli *google.SRegion, args *EipShowOptions) error {
|
||||
shellutils.R(&EipIdOptions{}, "eip-show", "Show eip", func(cli *google.SRegion, args *EipIdOptions) error {
|
||||
eip, err := cli.GetEip(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -46,4 +46,22 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&EipIdOptions{}, "eip-delete", "Delete eip", func(cli *google.SRegion, args *EipIdOptions) error {
|
||||
return cli.Delete(args.ID)
|
||||
})
|
||||
|
||||
type EipCreateOptions struct {
|
||||
NAME string
|
||||
Desc string
|
||||
}
|
||||
|
||||
shellutils.R(&EipCreateOptions{}, "eip-create", "Create eip", func(cli *google.SRegion, args *EipCreateOptions) error {
|
||||
eip, err := cli.CreateEip(args.NAME, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(eip)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -45,4 +45,18 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type GlobalNetworkCreateOptions struct {
|
||||
NAME string
|
||||
Desc string
|
||||
}
|
||||
|
||||
shellutils.R(&GlobalNetworkCreateOptions{}, "global-network-create", "Create globalnetwork", func(cli *google.SRegion, args *GlobalNetworkCreateOptions) error {
|
||||
globalnetwork, err := cli.CreateGlobalNetwork(args.NAME, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(globalnetwork)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
func init() {
|
||||
type ImageListOptions struct {
|
||||
Project string
|
||||
Project string `choices:"centos-cloud|ubuntu-os-cloud|windows-cloud|windows-sql-cloud|suse-cloud|suse-sap-cloud|rhel-cloud|rhel-sap-cloud|cos-cloud|debian-cloud"`
|
||||
MaxResults int
|
||||
PageToken string
|
||||
}
|
||||
@@ -46,4 +46,19 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type ImageCreateOptions struct {
|
||||
NAME string
|
||||
Desc string
|
||||
BUCKET string
|
||||
FILE string
|
||||
}
|
||||
|
||||
shellutils.R(&ImageCreateOptions{}, "image-create", "Create image", func(cli *google.SRegion, args *ImageCreateOptions) error {
|
||||
image, err := cli.CreateImage(args.NAME, args.Desc, args.BUCKET, args.FILE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(image)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/multicloud/google"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
@@ -34,10 +38,10 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceShowOptions struct {
|
||||
type InstanceIdOptions struct {
|
||||
ID string
|
||||
}
|
||||
shellutils.R(&InstanceShowOptions{}, "instance-show", "Show instance", func(cli *google.SRegion, args *InstanceShowOptions) error {
|
||||
shellutils.R(&InstanceIdOptions{}, "instance-show", "Show instance", func(cli *google.SRegion, args *InstanceIdOptions) error {
|
||||
instance, err := cli.GetInstance(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -46,4 +50,138 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceIdOptions{}, "instance-delete", "Delete instance", func(cli *google.SRegion, args *InstanceIdOptions) error {
|
||||
return cli.Delete(args.ID)
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceIdOptions{}, "instance-start", "Start instance", func(cli *google.SRegion, args *InstanceIdOptions) error {
|
||||
return cli.StartInstance(args.ID)
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceIdOptions{}, "instance-stop", "Stop instance", func(cli *google.SRegion, args *InstanceIdOptions) error {
|
||||
return cli.StopInstance(args.ID)
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceIdOptions{}, "instance-reset", "Reset instance", func(cli *google.SRegion, args *InstanceIdOptions) error {
|
||||
return cli.ResetInstance(args.ID)
|
||||
})
|
||||
|
||||
type InstanceEipOptions struct {
|
||||
ID string
|
||||
EIP string `help:"eip address"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceEipOptions{}, "instance-dissociate-eip", "Dissociate instance eip", func(cli *google.SRegion, args *InstanceEipOptions) error {
|
||||
return cli.DissociateInstanceEip(args.ID, args.EIP)
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceEipOptions{}, "instance-associate-eip", "Associate instance eip", func(cli *google.SRegion, args *InstanceEipOptions) error {
|
||||
return cli.AssociateInstanceEip(args.ID, args.EIP)
|
||||
})
|
||||
|
||||
type InstanceDetachDiskOptions struct {
|
||||
ID string
|
||||
DeviceName string
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceDetachDiskOptions{}, "instance-detach-disk", "Detach instance disk", func(cli *google.SRegion, args *InstanceDetachDiskOptions) error {
|
||||
return cli.DetachDisk(args.ID, args.DeviceName)
|
||||
})
|
||||
|
||||
type InstanceSetPublicKeyOptions struct {
|
||||
ID string
|
||||
PublicKey string
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceSetPublicKeyOptions{}, "instance-set-publickey", "Set instance public key", func(cli *google.SRegion, args *InstanceSetPublicKeyOptions) error {
|
||||
instance, err := cli.GetInstance(args.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cli.GetInstance")
|
||||
}
|
||||
items := []google.SMetadataItem{}
|
||||
for _, item := range instance.Metadata.Items {
|
||||
if item.Key != google.METADATA_SSH_KEYS {
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
if len(args.PublicKey) > 0 {
|
||||
items = append(items, google.SMetadataItem{Key: google.METADATA_SSH_KEYS, Value: "root:" + args.PublicKey})
|
||||
}
|
||||
instance.Metadata.Items = items
|
||||
return cli.SetMetadata(args.ID, instance.Metadata)
|
||||
})
|
||||
|
||||
type InstanceAttachDiskOptions struct {
|
||||
ID string
|
||||
DISK string
|
||||
Boot bool
|
||||
}
|
||||
|
||||
type InstanceSerialOutput struct {
|
||||
ID string
|
||||
PORT int
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceSerialOutput{}, "instance-serial-output", "Get instance serial output", func(cli *google.SRegion, args *InstanceSerialOutput) error {
|
||||
content, err := cli.GetSerialPortOutput(args.ID, args.PORT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("content: %s\n", content)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceAttachDiskOptions{}, "instance-attach-disk", "Attach instance disk", func(cli *google.SRegion, args *InstanceAttachDiskOptions) error {
|
||||
return cli.AttachDisk(args.ID, args.DISK, args.Boot)
|
||||
})
|
||||
|
||||
type InstanceRebuildRootOptions struct {
|
||||
ID string
|
||||
IMAGE string
|
||||
DiskSizeGb int
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceRebuildRootOptions{}, "instance-rebuild-root", "Rebuild instance root", func(cli *google.SRegion, args *InstanceRebuildRootOptions) error {
|
||||
diskId, err := cli.RebuildRoot(args.ID, args.IMAGE, args.DiskSizeGb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(diskId)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceChangeConfigOptions struct {
|
||||
ID string
|
||||
ZONE string
|
||||
InstanceType string
|
||||
Cpu int
|
||||
MemoryMb int
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceChangeConfigOptions{}, "instance-change-config", "Change instance config", func(cli *google.SRegion, args *InstanceChangeConfigOptions) error {
|
||||
return cli.ChangeInstanceConfig(args.ID, args.ZONE, args.InstanceType, args.Cpu, args.MemoryMb)
|
||||
})
|
||||
|
||||
type InstanceCreateOptions struct {
|
||||
NAME string
|
||||
ZONE string
|
||||
IMAGE string
|
||||
InstanceType string
|
||||
Cpu int
|
||||
MemoryMb int
|
||||
NETWORK string
|
||||
IpAddr string
|
||||
Desc string
|
||||
DISKS []string `nargs:"+"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceCreateOptions{}, "instance-create", "Create instance", func(cli *google.SRegion, args *InstanceCreateOptions) error {
|
||||
instance, err := cli.CreateInstance(args.ZONE, args.NAME, args.Desc, args.InstanceType, args.Cpu, args.MemoryMb, args.NETWORK, args.IpAddr, args.IMAGE, args.DISKS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(instance)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -34,10 +34,11 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type NetworkShowOptions struct {
|
||||
type NetworkIdOptions struct {
|
||||
ID string
|
||||
}
|
||||
shellutils.R(&NetworkShowOptions{}, "network-show", "Show network", func(cli *google.SRegion, args *NetworkShowOptions) error {
|
||||
|
||||
shellutils.R(&NetworkIdOptions{}, "network-show", "Show network", func(cli *google.SRegion, args *NetworkIdOptions) error {
|
||||
network, err := cli.GetNetwork(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -46,4 +47,24 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&NetworkIdOptions{}, "network-delete", "Delete network", func(cli *google.SRegion, args *NetworkIdOptions) error {
|
||||
return cli.Delete(args.ID)
|
||||
})
|
||||
|
||||
type NetworkCreateOptions struct {
|
||||
NAME string
|
||||
VPC string
|
||||
CIDR string
|
||||
Desc string
|
||||
}
|
||||
|
||||
shellutils.R(&NetworkCreateOptions{}, "network-create", "Create network", func(cli *google.SRegion, args *NetworkCreateOptions) error {
|
||||
network, err := cli.CreateNetwork(args.NAME, args.VPC, args.CIDR, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(network)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package shell
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/google"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type ObjectPutOptions struct {
|
||||
BUCKET string
|
||||
FILE string
|
||||
ContentType string
|
||||
Acl string `choices:"private|public-read|public-read-write|authenticated-read"`
|
||||
}
|
||||
|
||||
shellutils.R(&ObjectPutOptions{}, "object-put", "Put object to buckets", func(cli *google.SRegion, args *ObjectPutOptions) error {
|
||||
file, err := os.Open(args.FILE)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "so.Open")
|
||||
}
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "file.Stat")
|
||||
}
|
||||
return cli.PutObject(args.BUCKET, args.FILE, file, args.ContentType, stat.Size(), cloudprovider.TBucketACLType(args.Acl))
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/multicloud/google"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type SkuBillingListOptions struct {
|
||||
PageSize int
|
||||
PageToken string
|
||||
}
|
||||
shellutils.R(&SkuBillingListOptions{}, "sku-billing-list", "List sku billing", func(cli *google.SRegion, args *SkuBillingListOptions) error {
|
||||
billings, err := cli.ListSkuBilling(args.PageSize, args.PageToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(billings, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&SkuBillingListOptions{}, "compute-sku-billing-list", "List sku billing", func(cli *google.SRegion, args *SkuBillingListOptions) error {
|
||||
billings, err := cli.ListSkuBilling(args.PageSize, args.PageToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info := cli.GetSkuRateInfo(billings)
|
||||
fmt.Println(jsonutils.Marshal(info).PrettyString())
|
||||
return nil
|
||||
})
|
||||
|
||||
type SkuEstimate struct {
|
||||
RATE_FAILE string
|
||||
SKU string
|
||||
REGION string
|
||||
CPU int
|
||||
MEMORY_MB int
|
||||
}
|
||||
|
||||
shellutils.R(&SkuEstimate{}, "sku-estimate", "Estimate sku price", func(cli *google.SRegion, args *SkuEstimate) error {
|
||||
data, err := ioutil.ReadFile(args.RATE_FAILE)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ioutil.ReadFile")
|
||||
}
|
||||
rate := google.SRateInfo{}
|
||||
j, err := jsonutils.Parse(data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "jsonutils.Parse")
|
||||
}
|
||||
err = jsonutils.Update(&rate, j)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "jsonutils.Update")
|
||||
}
|
||||
result, err := rate.GetSkuPrice(args.REGION, args.SKU, args.CPU, args.MEMORY_MB)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetSkuPrice")
|
||||
}
|
||||
fmt.Printf("result: %s\n", jsonutils.Marshal(result).PrettyString())
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
@@ -34,10 +34,10 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
type SnapshotShowOptions struct {
|
||||
type SnapshotIdOptions struct {
|
||||
ID string
|
||||
}
|
||||
shellutils.R(&SnapshotShowOptions{}, "snapshot-show", "Show snapshot", func(cli *google.SRegion, args *SnapshotShowOptions) error {
|
||||
shellutils.R(&SnapshotIdOptions{}, "snapshot-show", "Show snapshot", func(cli *google.SRegion, args *SnapshotIdOptions) error {
|
||||
snapshot, err := cli.GetSnapshot(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -46,4 +46,23 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&SnapshotIdOptions{}, "snapshot-delete", "Delete snapshot", func(cli *google.SRegion, args *SnapshotIdOptions) error {
|
||||
return cli.Delete(args.ID)
|
||||
})
|
||||
|
||||
type SnapshotCreateOptions struct {
|
||||
NAME string
|
||||
Desc string
|
||||
DISK string
|
||||
}
|
||||
|
||||
shellutils.R(&SnapshotCreateOptions{}, "snapshot-create", "Create snapshot", func(cli *google.SRegion, args *SnapshotCreateOptions) error {
|
||||
snapshot, err := cli.CreateSnapshot(args.DISK, args.NAME, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(snapshot)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
)
|
||||
|
||||
type SkuBillingCatetory struct {
|
||||
ServiceDisplayName string
|
||||
ResourceFamily string
|
||||
ResourceGroup string
|
||||
UsageType string
|
||||
}
|
||||
|
||||
type SkuPricingInfo struct {
|
||||
Summary string
|
||||
PricingExpression SPricingExpression
|
||||
currencyConversionRate int
|
||||
EffectiveTime time.Time
|
||||
}
|
||||
|
||||
type SPricingExpression struct {
|
||||
UsageUnit string
|
||||
UsageUnitDescription string
|
||||
BaseUnit string
|
||||
BaseUnitDescription string
|
||||
BaseUnitConversionFactor string
|
||||
DisplayQuantity int
|
||||
TieredRates []STieredRate
|
||||
}
|
||||
|
||||
type STieredRate struct {
|
||||
StartUsageAmount int
|
||||
UnitPrice SUnitPrice
|
||||
}
|
||||
|
||||
type SUnitPrice struct {
|
||||
CurrencyCode string
|
||||
Units string
|
||||
Nanos int
|
||||
}
|
||||
|
||||
type SSkuBilling struct {
|
||||
Name string
|
||||
SkuId string
|
||||
Description string
|
||||
Category SkuBillingCatetory
|
||||
ServiceRegions []string
|
||||
PricingInfo []SkuPricingInfo
|
||||
ServiceProviderName string
|
||||
}
|
||||
|
||||
func (region *SRegion) ListSkuBilling(pageSize int, pageToken string) ([]SSkuBilling, error) {
|
||||
skus := []SSkuBilling{}
|
||||
params := map[string]string{}
|
||||
err := region.BillingList("services/6F81-5844-456A/skus", params, pageSize, pageToken, &skus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return skus, nil
|
||||
}
|
||||
|
||||
type SRateInfo struct {
|
||||
// region: europe-north1
|
||||
// family: Compute, Storage, Network
|
||||
// resource: CPU, Ram, Gpu, N1Standard
|
||||
// category: custome, predefine, optimized
|
||||
// startUsageAmount:
|
||||
// map[region]map[family]map[resource]map[category]map[startUsageAmount][money]
|
||||
|
||||
Info map[string]map[string]map[string]map[string]map[string]float64
|
||||
}
|
||||
|
||||
func (region *SRegion) GetSkuRateInfo(skus []SSkuBilling) SRateInfo {
|
||||
result := SRateInfo{
|
||||
Info: map[string]map[string]map[string]map[string]map[string]float64{},
|
||||
}
|
||||
for _, sku := range skus {
|
||||
if sku.ServiceProviderName == "Google" &&
|
||||
sku.Category.ServiceDisplayName == "Compute Engine" &&
|
||||
utils.IsInStringArray(sku.Category.ResourceFamily, []string{"Compute", "Storage"}) &&
|
||||
sku.Category.UsageType == "OnDemand" {
|
||||
for _, region := range sku.ServiceRegions {
|
||||
if _, ok := result.Info[region]; !ok {
|
||||
result.Info[region] = map[string]map[string]map[string]map[string]float64{}
|
||||
}
|
||||
if _, ok := result.Info[region][sku.Category.ResourceFamily]; !ok {
|
||||
result.Info[region][sku.Category.ResourceFamily] = map[string]map[string]map[string]float64{}
|
||||
}
|
||||
if sku.Category.ResourceGroup == "N1Standard" {
|
||||
if strings.Index(sku.Description, "Core") > 0 {
|
||||
sku.Category.ResourceGroup = "CPU"
|
||||
} else if strings.Index(sku.Description, "Ram") > 0 {
|
||||
sku.Category.ResourceGroup = "RAM"
|
||||
}
|
||||
}
|
||||
if !utils.IsInStringArray(sku.Category.ResourceGroup, []string{"F1Micro", "G1Small", "CPU", "RAM", "PDStandard", "SSD", "LocalSSD", "f1-micro", "g1-small"}) {
|
||||
continue
|
||||
}
|
||||
if utils.IsInStringArray(sku.Category.ResourceGroup, []string{"PDStandard", "SSD"}) && strings.Index(sku.Description, "Regional") >= 0 {
|
||||
continue
|
||||
}
|
||||
convers := map[string]string{
|
||||
"PDStandard": api.STORAGE_GOOGLE_PD_STANDARD,
|
||||
"SSD": api.STORAGE_GOOGLE_PD_SSD,
|
||||
"LocalSSD": api.STORAGE_GOOGLE_LOCAL_SSD,
|
||||
"F1Micro": "f1-micro",
|
||||
"G1Small": "g1-small",
|
||||
}
|
||||
if group, ok := convers[sku.Category.ResourceGroup]; ok {
|
||||
sku.Category.ResourceGroup = group
|
||||
}
|
||||
|
||||
if _, ok := result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup]; !ok {
|
||||
result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup] = map[string]map[string]float64{}
|
||||
}
|
||||
description := strings.ToLower(sku.Description)
|
||||
if strings.Contains("sole", description) { //单租户
|
||||
continue
|
||||
}
|
||||
category := ""
|
||||
keys := []string{"memory optimized", "memory-optimized", "compute optimized", "n1 predefined", "n2 instance", "n2 custom extended", "custom extended", "n2 custom", "custom instance"}
|
||||
categories := map[string]string{
|
||||
"memory optimized": "ultramem",
|
||||
"memory-optimized": "memory-optimized",
|
||||
"compute optimized": "compute-optimized",
|
||||
|
||||
"n1 predefined": "n1-predefined",
|
||||
"n2 instance": "n2-instance",
|
||||
|
||||
"n2 custom extended": "n2-custom-extended",
|
||||
"custom extended": "custom-extended",
|
||||
|
||||
"n2 custom": "n2-custom", //cpu ram
|
||||
"custom instance": "custom-instance", //cpu
|
||||
}
|
||||
for _, key := range keys {
|
||||
_category := categories[key]
|
||||
if strings.Contains(description, key) {
|
||||
category = _category
|
||||
break
|
||||
}
|
||||
}
|
||||
if utils.IsInStringArray(sku.Category.ResourceGroup, []string{api.STORAGE_GOOGLE_PD_STANDARD, api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_LOCAL_SSD, "f1-micro", "g1-small"}) {
|
||||
category = sku.Category.ResourceGroup
|
||||
}
|
||||
if len(category) == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup][category]; !ok {
|
||||
result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup][category] = map[string]float64{}
|
||||
}
|
||||
for _, priceInfo := range sku.PricingInfo {
|
||||
for _, price := range priceInfo.PricingExpression.TieredRates {
|
||||
result.Info[region][sku.Category.ResourceFamily][sku.Category.ResourceGroup][category][fmt.Sprintf("%d", price.StartUsageAmount)] = float64(price.UnitPrice.Nanos) / 1000000000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (rate *SRateInfo) GetDiscount(sku string) []float64 {
|
||||
if strings.Index(sku, "custom") < 0 && strings.HasPrefix(sku, "c") || strings.Index(sku, "n2") >= 0 {
|
||||
return []float64{0, 0.15, 0.25, 0.4}
|
||||
}
|
||||
return []float64{0, 0.2, 0.4, 0.6}
|
||||
}
|
||||
|
||||
func (rate *SRateInfo) GetSkuType(sku string) (string, error) {
|
||||
if strings.Index(sku, "custom") >= 0 {
|
||||
cpuType := "custom-instance"
|
||||
if strings.HasPrefix(sku, "n2") {
|
||||
cpuType = "n2-custom"
|
||||
}
|
||||
return cpuType, nil
|
||||
}
|
||||
if strings.HasPrefix(sku, "n1") {
|
||||
return "n1-predefined", nil
|
||||
}
|
||||
if strings.HasPrefix(sku, "n2") {
|
||||
return "n2-instance", nil
|
||||
}
|
||||
if strings.HasPrefix(sku, "c") {
|
||||
return "compute-optimized", nil
|
||||
}
|
||||
if strings.HasPrefix(sku, "m") {
|
||||
return "memory-optimized", nil
|
||||
}
|
||||
return "", fmt.Errorf("failed to found sku %s type", sku)
|
||||
}
|
||||
|
||||
func (rate *SRateInfo) GetCpuPrice(regionId, cpuType string) (float64, error) {
|
||||
computePrice, err := rate.GetComputePrice(regionId)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "GetComputePrice")
|
||||
}
|
||||
|
||||
_cpuPrice, ok := computePrice["CPU"]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("failed to found region %s compute cpu price info", regionId)
|
||||
}
|
||||
|
||||
cpuPrice, ok := _cpuPrice[cpuType]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("failed to found region %s compute %s cpu price info", regionId, cpuType)
|
||||
}
|
||||
return cpuPrice["0"], nil
|
||||
}
|
||||
|
||||
func (rate *SRateInfo) GetExtendMemoryGb(sku string, cpu int, memoryMb int) float64 {
|
||||
maxMemoryGb := 0.0
|
||||
if strings.Index(sku, "custom") >= 0 {
|
||||
maxMemoryGb = float64(cpu) * 6.5
|
||||
if strings.Index(sku, "n2") >= 0 {
|
||||
maxMemoryGb = float64(cpu) * 8
|
||||
}
|
||||
}
|
||||
if float64(memoryMb)/1024 > maxMemoryGb {
|
||||
return float64(memoryMb)/1024 - maxMemoryGb
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func (rate *SRateInfo) GetMemoryPrice(regionId string, memoryType string) (float64, error) {
|
||||
computePrice, err := rate.GetComputePrice(regionId)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "GetComputePrice")
|
||||
}
|
||||
|
||||
_memoryPrice, ok := computePrice["RAM"]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("failed to found region %s compute memory price info", regionId)
|
||||
}
|
||||
|
||||
memoryPrice, ok := _memoryPrice[memoryType]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("failed to found region %s compute %s memory price info", regionId, memoryType)
|
||||
}
|
||||
return memoryPrice["0"], nil
|
||||
}
|
||||
|
||||
func (rate *SRateInfo) GetComputePrice(regionId string) (map[string]map[string]map[string]float64, error) {
|
||||
regionPrice, ok := rate.Info[regionId]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to found region %s price info", regionId)
|
||||
}
|
||||
computePrice, ok := regionPrice["Compute"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to found region %s compute price info", regionId)
|
||||
}
|
||||
return computePrice, nil
|
||||
}
|
||||
|
||||
func (rate *SRateInfo) GetSharedSkuPrice(regionId string, sku string) (float64, error) {
|
||||
computePrice, err := rate.GetComputePrice(regionId)
|
||||
if err != nil {
|
||||
return 0.0, errors.Wrap(err, "GetComputePrice")
|
||||
}
|
||||
if _sharedSku, ok := computePrice[sku]; ok {
|
||||
if sharedSku, ok := _sharedSku[sku]; ok {
|
||||
return sharedSku["0"], nil
|
||||
}
|
||||
}
|
||||
return 0.0, fmt.Errorf("sku is not shared sku")
|
||||
}
|
||||
|
||||
func (rate *SRateInfo) GetSkuPrice(regionId string, sku string, cpu, memoryMb int) (struct {
|
||||
Hour float64
|
||||
Month float64
|
||||
Year float64
|
||||
}, error) {
|
||||
result := struct {
|
||||
Hour float64
|
||||
Month float64
|
||||
Year float64
|
||||
}{}
|
||||
|
||||
discount := rate.GetDiscount(sku)
|
||||
|
||||
price, err := rate.GetSharedSkuPrice(regionId, sku)
|
||||
if err != nil {
|
||||
skuType, err := rate.GetSkuType(sku)
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "GetSkuType")
|
||||
}
|
||||
|
||||
cpuPrice, err := rate.GetCpuPrice(regionId, skuType)
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "price.GetCpuPrice")
|
||||
}
|
||||
|
||||
price += float64(cpu) * cpuPrice
|
||||
log.Debugf("cpu price: %f", cpuPrice)
|
||||
|
||||
extendMemoryGb := rate.GetExtendMemoryGb(sku, cpu, memoryMb)
|
||||
memoryMb = int(float64(memoryMb) - 1024*extendMemoryGb)
|
||||
|
||||
memoryPrice, err := rate.GetMemoryPrice(regionId, skuType)
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "GetMemoryPrice")
|
||||
}
|
||||
|
||||
price += memoryPrice * float64(memoryMb/1024)
|
||||
log.Debugf("ramPrice: %f", memoryPrice)
|
||||
|
||||
if extendMemoryGb > 0 {
|
||||
memoryType := "custom-extended"
|
||||
if strings.HasPrefix(sku, "n2") {
|
||||
memoryType = "n2-custom-extended"
|
||||
}
|
||||
extendPrice, err := rate.GetMemoryPrice(regionId, memoryType)
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "GetMemoryPrice.Extend")
|
||||
}
|
||||
price += extendPrice * float64(extendMemoryGb)
|
||||
log.Debugf("extendPrice: %f", extendPrice)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("totalPrice: %f", price)
|
||||
|
||||
result.Month = 182.5 * price
|
||||
result.Month += 182.5 * (1 - discount[1]) * price
|
||||
result.Month += 182.5 * (1 - discount[2]) * price
|
||||
result.Month += 172.49999999999997 * (1 - discount[3]) * price
|
||||
result.Hour = result.Month / 30 / 24
|
||||
result.Year = result.Month * 12
|
||||
return result, nil
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type SSnapshot struct {
|
||||
@@ -106,7 +105,7 @@ func (snapshot *SSnapshot) GetDiskType() string {
|
||||
}
|
||||
|
||||
func (snapshot *SSnapshot) Delete() error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
return snapshot.region.Delete(snapshot.SelfLink)
|
||||
}
|
||||
|
||||
func (snapshot *SSnapshot) GetProjectId() string {
|
||||
|
||||
@@ -120,11 +120,21 @@ func (storage *SStorage) GetEnabled() bool {
|
||||
}
|
||||
|
||||
func (storage *SStorage) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
disk, err := storage.zone.region.GetDisk(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disk.storage = storage
|
||||
return disk, nil
|
||||
}
|
||||
|
||||
func (storage *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
disk, err := storage.zone.region.CreateDisk(name, sizeGb, storage.zone.Name, storage.Name, "", desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disk.storage = storage
|
||||
return disk, nil
|
||||
}
|
||||
|
||||
func (storage *SStorage) GetMountPoint() string {
|
||||
|
||||
@@ -16,13 +16,22 @@ package google
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/image/options"
|
||||
"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/qemuimg"
|
||||
)
|
||||
|
||||
type SStoragecache struct {
|
||||
@@ -85,21 +94,111 @@ func (cache *SStoragecache) GetPath() string {
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, isForce bool) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
if len(image.ExternalId) > 0 {
|
||||
_image, err := cache.region.GetImage(image.ExternalId)
|
||||
if err != nil {
|
||||
log.Errorf("GetImage error: %v", err)
|
||||
} else {
|
||||
status := _image.GetStatus()
|
||||
log.Debugf("UploadImage: Image external ID %s exists, status %s", image.ExternalId, status)
|
||||
if status == api.CACHED_IMAGE_STATUS_READY {
|
||||
return image.ExternalId, nil
|
||||
}
|
||||
err = cache.region.Delete(image.ExternalId)
|
||||
if err != nil {
|
||||
log.Errorf("failed to delete %s image %s", status, image.ExternalId)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Debugf("UploadImage: no external ID")
|
||||
}
|
||||
|
||||
return cache.uploadImage(ctx, userCred, image, isForce)
|
||||
}
|
||||
|
||||
func (region *SRegion) checkAndCreateBucket(bucketName string) (*SBucket, error) {
|
||||
bucket, err := region.GetBucket(bucketName)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
bucket, err = region.CreateBucket(bucketName, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "region.CreateBucket(%s)", bucketName)
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Wrapf(err, "region.StorageGet(%s)", bucketName)
|
||||
}
|
||||
}
|
||||
return bucket, nil
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) uploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, isForce bool) (string, error) {
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region, "")
|
||||
|
||||
meta, reader, _, err := modules.Images.Download(s, image.ImageId, string(qemuimg.QCOW2), false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
log.Infof("meta data %s", meta)
|
||||
|
||||
info := struct {
|
||||
Id string
|
||||
Name string
|
||||
Size int64
|
||||
Description string
|
||||
}{}
|
||||
meta.Unmarshal(&info)
|
||||
|
||||
bucketName := fmt.Sprintf("imagecache-%s", info.Id)
|
||||
bucket, err := cache.region.checkAndCreateBucket(bucketName)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "checkAndCreateBucket(%s)", bucketName)
|
||||
}
|
||||
|
||||
defer cache.region.DeleteBucket(bucket.Name)
|
||||
|
||||
err = cache.region.PutObject(bucketName, info.Name, reader, "", info.Size, cloudprovider.ACLPublicRead)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "region.PutObject")
|
||||
}
|
||||
|
||||
images, err := cache.region.GetImages(cache.region.GetProjectId(), 0, "")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "region.GetImages")
|
||||
}
|
||||
imageNames := []string{}
|
||||
for _, image := range images {
|
||||
imageNames = append(imageNames, image.Name)
|
||||
}
|
||||
|
||||
imageName := "img-"
|
||||
for _, s := range strings.ToLower(info.Name) {
|
||||
if unicode.IsDigit(s) || unicode.IsLetter(s) || s == '-' {
|
||||
imageName = fmt.Sprintf("%s%c", imageName, s)
|
||||
} else {
|
||||
imageName = fmt.Sprintf("%s-", imageName)
|
||||
}
|
||||
}
|
||||
|
||||
baseName := imageName
|
||||
for i := 0; i < 30; i++ {
|
||||
if !utils.IsInStringArray(imageName, imageNames) {
|
||||
break
|
||||
}
|
||||
imageName = fmt.Sprintf("%s-%d", baseName, i)
|
||||
}
|
||||
|
||||
_image, err := cache.region.CreateImage(imageName, info.Description, bucketName, info.Name)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "region.CreateImage")
|
||||
}
|
||||
|
||||
return _image.GetGlobalId(), nil
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (cache *SRegion) CheckBucket(bucketName string) (*oss.Bucket, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (cache *SRegion) CreateImage(snapshoutId, imageName, imageDesc string) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (cache *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (vpc *SVpc) GetStatus() string {
|
||||
}
|
||||
|
||||
func (vpc *SVpc) Delete() error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
return vpc.region.Delete(vpc.globalnetwork.SelfLink)
|
||||
}
|
||||
|
||||
func (vpc *SVpc) GetCidrBlock() string {
|
||||
@@ -82,8 +82,8 @@ func (vpc *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, erro
|
||||
return nil, errors.Wrap(err, "GetFirewalls")
|
||||
}
|
||||
isecgroups := []cloudprovider.ICloudSecurityGroup{}
|
||||
tags := []string{}
|
||||
allInstance := false
|
||||
tags := []string{}
|
||||
for _, firewall := range firewalls {
|
||||
if len(firewall.TargetServiceAccounts) > 0 {
|
||||
secgroup := &SSecurityGroup{vpc: vpc, ServiceAccount: firewall.TargetServiceAccounts[0]}
|
||||
|
||||
@@ -39,7 +39,12 @@ func (wire *SWire) GetName() string {
|
||||
}
|
||||
|
||||
func (wire *SWire) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
network, err := wire.vpc.region.CreateNetwork(name, wire.vpc.globalnetwork.SelfLink, cidr, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
network.wire = wire
|
||||
return network, nil
|
||||
}
|
||||
|
||||
func (wire *SWire) GetIVpc() cloudprovider.ICloudVpc {
|
||||
|
||||
@@ -519,25 +519,25 @@ func (self *SInstance) UpdateUserData(userData string) error {
|
||||
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0067876971.html 更换系统盘操作系统
|
||||
// 不支持调整系统盘大小
|
||||
// todo: 支持注入user_data
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
var err error
|
||||
var jobId string
|
||||
|
||||
publicKeyName := ""
|
||||
if len(publicKey) > 0 {
|
||||
publicKeyName, err = self.host.zone.region.syncKeypair(publicKey)
|
||||
if len(desc.PublicKey) > 0 {
|
||||
publicKeyName, err = self.host.zone.region.syncKeypair(desc.PublicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if self.Metadata.MeteringImageID == imageId {
|
||||
jobId, err = self.host.zone.region.RebuildRoot(ctx, self.UserID, self.GetId(), passwd, publicKeyName, publicKey, self.OSEXTSRVATTRUserData)
|
||||
if self.Metadata.MeteringImageID == desc.ImageId {
|
||||
jobId, err = self.host.zone.region.RebuildRoot(ctx, self.UserID, self.GetId(), desc.Password, publicKeyName, desc.PublicKey, self.OSEXTSRVATTRUserData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
jobId, err = self.host.zone.region.ChangeRoot(ctx, self.UserID, self.GetId(), imageId, passwd, publicKeyName, publicKey, self.OSEXTSRVATTRUserData)
|
||||
jobId, err = self.host.zone.region.ChangeRoot(ctx, self.UserID, self.GetId(), desc.ImageId, desc.Password, publicKeyName, desc.PublicKey, self.OSEXTSRVATTRUserData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package multicloud
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
|
||||
type SInstanceBase struct {
|
||||
SResourceBase
|
||||
}
|
||||
@@ -21,3 +23,7 @@ type SInstanceBase struct {
|
||||
func (instance *SInstanceBase) GetIHostId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (instance *SInstanceBase) GetSerialOutput(port int) (string, error) {
|
||||
return "", cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -442,8 +442,8 @@ func (instance *SInstance) DeployVM(ctx context.Context, name string, username s
|
||||
return instance.host.zone.region.DeployVM(instance.ID, name, password, publicKey, deleteKeypair, description)
|
||||
}
|
||||
|
||||
func (instance *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
return instance.ID, instance.host.zone.region.ReplaceSystemDisk(instance.ID, imageId, passwd, publicKey, sysSizeGB)
|
||||
func (instance *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
return instance.ID, instance.host.zone.region.ReplaceSystemDisk(instance.ID, desc.ImageId, desc.Password, desc.PublicKey, desc.SysSizeGB)
|
||||
}
|
||||
|
||||
func (instance *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
|
||||
|
||||
@@ -435,16 +435,16 @@ func (self *SInstance) DeployVM(ctx context.Context, name string, username strin
|
||||
return self.host.zone.region.DeployVM(self.InstanceId, name, password, keypairName, deleteKeypair, description)
|
||||
}
|
||||
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
keypair := ""
|
||||
if len(publicKey) > 0 {
|
||||
if len(desc.PublicKey) > 0 {
|
||||
var err error
|
||||
keypair, err = self.host.zone.region.syncKeypair(publicKey)
|
||||
keypair, err = self.host.zone.region.syncKeypair(desc.PublicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
err := self.host.zone.region.ReplaceSystemDisk(self.InstanceId, imageId, passwd, keypair, sysSizeGB)
|
||||
err := self.host.zone.region.ReplaceSystemDisk(self.InstanceId, desc.ImageId, desc.Password, keypair, desc.SysSizeGB)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -431,8 +431,8 @@ func (self *SInstance) UpdateUserData(userData string) error {
|
||||
// todo:// 3.将原系统重装为不同类型的系统时(Linux->Windows),不可选择保留数据盘;
|
||||
// 4.重装不同版本的系统时(CentOS6->CentOS7),若选择保留数据盘,请注意数据盘的文件系统格式;
|
||||
// 5.若主机CPU低于2核,不可重装为Windows系统。
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
if len(publicKey) > 0 {
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
if len(desc.PublicKey) > 0 {
|
||||
return "", fmt.Errorf("DeployVM not support assign ssh keypair")
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@ func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd s
|
||||
}
|
||||
}
|
||||
|
||||
err := self.host.zone.region.RebuildRoot(self.GetId(), imageId, passwd)
|
||||
err := self.host.zone.region.RebuildRoot(self.GetId(), desc.ImageId, desc.Password)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
)
|
||||
|
||||
@@ -35,6 +36,7 @@ type SInstanceCdrome struct {
|
||||
}
|
||||
|
||||
type SInstance struct {
|
||||
multicloud.SInstanceBase
|
||||
host *SHost
|
||||
|
||||
ZStackBasic
|
||||
@@ -378,8 +380,8 @@ func (instance *SInstance) DeployVM(ctx context.Context, name string, username s
|
||||
return nil
|
||||
}
|
||||
|
||||
func (instance *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
return instance.host.zone.region.RebuildRoot(instance.UUID, imageId, sysSizeGB)
|
||||
func (instance *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
return instance.host.zone.region.RebuildRoot(instance.UUID, desc.ImageId, desc.SysSizeGB)
|
||||
}
|
||||
|
||||
func (region *SRegion) RebuildRoot(instanceId, imageId string, sysSizeGB int) (string, error) {
|
||||
|
||||
@@ -55,7 +55,7 @@ type SWriteFile struct {
|
||||
|
||||
type SUser struct {
|
||||
Name string
|
||||
Passwd string
|
||||
PlainTextPasswd string
|
||||
HashedPasswd string
|
||||
LockPasswd bool
|
||||
SshAuthorizedKeys []string
|
||||
@@ -161,7 +161,7 @@ func (u *SUser) Password(passwd string) *SUser {
|
||||
if err != nil {
|
||||
log.Errorf("GeneratePassword error %s", err)
|
||||
} else {
|
||||
u.Passwd = hash
|
||||
u.PlainTextPasswd = passwd
|
||||
u.HashedPasswd = hash
|
||||
}
|
||||
u.LockPasswd = false
|
||||
@@ -169,12 +169,24 @@ func (u *SUser) Password(passwd string) *SUser {
|
||||
return u
|
||||
}
|
||||
|
||||
func (u *SUser) PowerShellScripts() []string {
|
||||
shells := []string{}
|
||||
shells = append(shells, fmt.Sprintf(`New-LocalUser -Name "%s" -Description "A New Local Account Created By PowerShell" -NoPassword`, u.Name))
|
||||
shells = append(shells, fmt.Sprintf(`Add-LocalGroupMember -Group "Administrators" -Member "%s"`, u.Name))
|
||||
if len(u.PlainTextPasswd) > 0 {
|
||||
shells = append(shells, fmt.Sprintf(`net user "%s" "%s"`, u.Name, u.PlainTextPasswd))
|
||||
}
|
||||
// enable需要再设置密码之后,否则会出现Enable-LocalUser : Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirements of the domain
|
||||
shells = append(shells, fmt.Sprintf(`Enable-LocalUser "%s"`, u.Name))
|
||||
return shells
|
||||
}
|
||||
|
||||
func (u *SUser) ShellScripts() []string {
|
||||
shells := []string{}
|
||||
|
||||
shells = append(shells, fmt.Sprintf("useradd -m %s || true", u.Name))
|
||||
if len(u.Passwd) > 0 {
|
||||
shells = append(shells, fmt.Sprintf("usermod -p '%s' %s", u.Passwd, u.Name))
|
||||
if len(u.HashedPasswd) > 0 {
|
||||
shells = append(shells, fmt.Sprintf("usermod -p '%s' %s", u.HashedPasswd, u.Name))
|
||||
}
|
||||
|
||||
home := "/" + u.Name
|
||||
@@ -216,6 +228,11 @@ func (conf *SCloudConfig) UserDataScript() string {
|
||||
}
|
||||
shells = append(shells, conf.Runcmd...)
|
||||
|
||||
// 允许密码及root登录(谷歌云镜像默认会禁止root及密码登录)
|
||||
shells = append(shells, `sed -i "s/.*PermitRootLogin.*/PermitRootLogin yes/g" /etc/ssh/sshd_config`)
|
||||
shells = append(shells, `sed -i 's/.*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config`)
|
||||
shells = append(shells, `systemctl restart sshd`)
|
||||
|
||||
for _, pkg := range conf.Packages {
|
||||
shells = append(shells, "which yum &>/dev/null && yum install -y "+pkg)
|
||||
shells = append(shells, "which apt-get &>/dev/null && apt-get install -y "+pkg)
|
||||
@@ -226,6 +243,16 @@ func (conf *SCloudConfig) UserDataScript() string {
|
||||
return CLOUD_SHELL_HEADER + strings.Join(shells, "\n")
|
||||
}
|
||||
|
||||
func (conf *SCloudConfig) UserDataPowerShell() string {
|
||||
shells := []string{}
|
||||
for _, u := range conf.Users {
|
||||
shells = append(shells, u.PowerShellScripts()...)
|
||||
}
|
||||
shells = append(shells, conf.Runcmd...)
|
||||
|
||||
return strings.Join(shells, "\n")
|
||||
}
|
||||
|
||||
func (conf *SCloudConfig) UserDataBase64() string {
|
||||
data := conf.UserData()
|
||||
return base64.StdEncoding.EncodeToString([]byte(data))
|
||||
@@ -281,8 +308,8 @@ func (conf *SCloudConfig) MergeUser(u SUser) {
|
||||
for i := 0; i < len(conf.Users); i += 1 {
|
||||
if u.Name == conf.Users[i].Name {
|
||||
// replace conf user password with input
|
||||
if len(u.Passwd) > 0 {
|
||||
conf.Users[i].Passwd = u.Passwd
|
||||
if len(u.PlainTextPasswd) > 0 {
|
||||
conf.Users[i].PlainTextPasswd = u.PlainTextPasswd
|
||||
conf.Users[i].HashedPasswd = u.HashedPasswd
|
||||
conf.Users[i].LockPasswd = u.LockPasswd
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user