diff --git a/pkg/apis/compute/api.go b/pkg/apis/compute/api.go index 8d076f188f..f2794a4020 100644 --- a/pkg/apis/compute/api.go +++ b/pkg/apis/compute/api.go @@ -368,7 +368,8 @@ type ServerCreateInput struct { VcpuCount int `json:"vcpu_count"` // 用户自定义启动脚本 - // 公有云私有云只支持 #cloud-config yaml 格式, 且只有linux系统生效 + // 部分平台只支持 #cloud-config yaml 格式(由于部分平台密码依赖cloud-init注入密码信息,所以不支持特殊类型的user data) + // 支持特殊user data平台: Aliyun, Qcloud, Azure, Apsara, Ucloud // required: false UserData string `json:"user_data"` diff --git a/pkg/cloudprovider/instance.go b/pkg/cloudprovider/instance.go index 3e1363e05f..d03a00d9fc 100644 --- a/pkg/cloudprovider/instance.go +++ b/pkg/cloudprovider/instance.go @@ -15,9 +15,12 @@ package cloudprovider import ( + "encoding/base64" "strings" "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/util/osprofile" "yunion.io/x/onecloud/pkg/util/ansible" @@ -142,6 +145,11 @@ type SManagedVMCreateConfig struct { Tags map[string]string BillingCycle *billing.SBillingCycle + + IsNeedInjectPasswordByCloudInit bool + UserDataType string + WindowsUserDataType string + IsWindowsUserDataTypeNeedEncode bool } type SManagedVMChangeConfig struct { @@ -160,8 +168,15 @@ type SManagedVMRebuildRootConfig struct { } func (vmConfig *SManagedVMCreateConfig) GetConfig(config *jsonutils.JSONDict) error { - if err := config.Unmarshal(vmConfig, "desc"); err != nil { - return err + err := config.Unmarshal(vmConfig, "desc") + if err != nil { + return errors.Wrapf(err, "config.Unmarshal") + } + if !vmConfig.IsNeedInjectPasswordByCloudInit { + _, err := cloudinit.ParseUserData(vmConfig.UserData) + if err != nil { + return nil + } } if publicKey, _ := config.GetString("public_key"); len(publicKey) > 0 { vmConfig.PublicKey = publicKey @@ -178,6 +193,12 @@ func (vmConfig *SManagedVMCreateConfig) GetConfig(config *jsonutils.JSONDict) er if resetPassword && len(vmConfig.Password) == 0 { vmConfig.Password = seclib2.RandomPassword2(12) } + if vmConfig.IsNeedInjectPasswordByCloudInit { + err = vmConfig.InjectPasswordByCloudInit() + if err != nil { + log.Warningf("failed to inject password by cloud-init error: %v", err) + } + } return nil } @@ -207,6 +228,42 @@ func generateUserData(adminPublicKey, projectPublicKey, oUserData string) string return cloudConfig.UserData() } +func (vmConfig *SManagedVMCreateConfig) GetUserData() (string, error) { + if len(vmConfig.UserData) == 0 { + return "", nil + } + oUserData, err := cloudinit.ParseUserData(vmConfig.UserData) + if err != nil { + // 用户输入非标准cloud-init数据 + if !vmConfig.IsNeedInjectPasswordByCloudInit { + return base64.StdEncoding.EncodeToString([]byte(vmConfig.UserData)), nil + } + return "", err + } + if strings.ToLower(vmConfig.OsType) == strings.ToLower(osprofile.OS_TYPE_LINUX) { + switch vmConfig.UserDataType { + case CLOUD_SHELL: + return oUserData.UserDataScriptBase64(), nil + case CLOUD_SHELL_WITHOUT_ENCRYPT: + return oUserData.UserDataScript(), nil + default: + return oUserData.UserDataBase64(), nil + } + } else { + userData := "" + switch vmConfig.WindowsUserDataType { + case CLOUD_EC2: + userData = oUserData.UserDataEc2() + default: + userData = oUserData.UserDataPowerShell() + } + if vmConfig.IsWindowsUserDataTypeNeedEncode { + userData = base64.StdEncoding.EncodeToString([]byte(userData)) + } + return userData, nil + } +} + func (vmConfig *SManagedVMCreateConfig) InjectPasswordByCloudInit() error { loginUser := cloudinit.NewUser(vmConfig.Account) loginUser.SudoPolicy(cloudinit.USER_SUDO_NOPASSWD) diff --git a/pkg/compute/guestdrivers/aws.go b/pkg/compute/guestdrivers/aws.go index d0dddcbf50..15ced67af4 100644 --- a/pkg/compute/guestdrivers/aws.go +++ b/pkg/compute/guestdrivers/aws.go @@ -72,7 +72,7 @@ func fetchAwsUserName(desc cloudprovider.SManagedVMCreateConfig) string { } } -func (self *SAwsGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool { +func (self *SAwsGuestDriver) IsNeedInjectPasswordByCloudInit() bool { return true } diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index b07f205428..6a28bff0f8 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -324,7 +324,7 @@ func (self *SBaseGuestDriver) GetGuestInitialStateAfterRebuild() string { return api.VM_READY } -func (self *SBaseGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool { +func (self *SBaseGuestDriver) IsNeedInjectPasswordByCloudInit() bool { return false } diff --git a/pkg/compute/guestdrivers/google.go b/pkg/compute/guestdrivers/google.go index ad4bc21a57..6ddfcd55da 100644 --- a/pkg/compute/guestdrivers/google.go +++ b/pkg/compute/guestdrivers/google.go @@ -190,7 +190,7 @@ func (self *SGoogleGuestDriver) GetGuestInitialStateAfterRebuild() string { return api.VM_READY } -func (self *SGoogleGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool { +func (self *SGoogleGuestDriver) IsNeedInjectPasswordByCloudInit() bool { return true } diff --git a/pkg/compute/guestdrivers/huawei.go b/pkg/compute/guestdrivers/huawei.go index d920f06087..feb8fb72ea 100644 --- a/pkg/compute/guestdrivers/huawei.go +++ b/pkg/compute/guestdrivers/huawei.go @@ -154,7 +154,7 @@ func (self *SHuaweiGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle return false } -func (self *SHuaweiGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool { +func (self *SHuaweiGuestDriver) IsNeedInjectPasswordByCloudInit() bool { return true } diff --git a/pkg/compute/guestdrivers/huaweistack.go b/pkg/compute/guestdrivers/huaweistack.go index ab7b803d75..b729a61c83 100644 --- a/pkg/compute/guestdrivers/huaweistack.go +++ b/pkg/compute/guestdrivers/huaweistack.go @@ -193,7 +193,7 @@ func (self *SHCSOGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) return false } -func (self *SHCSOGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool { +func (self *SHCSOGuestDriver) IsNeedInjectPasswordByCloudInit() bool { return true } diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index 6ef873bc18..b0d0caaa01 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -16,16 +16,13 @@ package guestdrivers import ( "context" - "encoding/base64" "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" "yunion.io/x/sqlchemy" @@ -221,7 +218,8 @@ func (self *SManagedVirtualizedGuestDriver) ValidateCreateData(ctx context.Conte if input.Cdrom != "" { return nil, httperrors.NewInputParameterError("%s not support cdrom params", input.Hypervisor) } - if len(input.UserData) > 0 { + driver := models.GetDriver(input.Hypervisor) + if len(input.UserData) > 0 && driver != nil && driver.IsNeedInjectPasswordByCloudInit() { _, err := cloudinit.ParseUserData(input.UserData) if err != nil { return nil, err @@ -369,7 +367,12 @@ func (self *SManagedVirtualizedGuestDriver) RequestDeployGuestOnHost(ctx context } log.Debugf("RequestDeployGuestOnHost: %s", config) - desc := cloudprovider.SManagedVMCreateConfig{} + desc := cloudprovider.SManagedVMCreateConfig{ + IsNeedInjectPasswordByCloudInit: guest.GetDriver().IsNeedInjectPasswordByCloudInit(), + UserDataType: guest.GetDriver().GetUserDataType(), + WindowsUserDataType: guest.GetDriver().GetWindowsUserDataType(), + IsWindowsUserDataTypeNeedEncode: guest.GetDriver().IsWindowsUserDataTypeNeedEncode(), + } err = desc.GetConfig(config) if err != nil { return errors.Wrapf(err, "desc.GetConfig") @@ -411,38 +414,9 @@ func (self *SManagedVirtualizedGuestDriver) RequestDeployGuestOnHost(ctx context } desc.Account = guest.GetDriver().GetDefaultAccount(desc) - - if guest.GetDriver().IsNeedInjectPasswordByCloudInit(&desc) { - err = desc.InjectPasswordByCloudInit() - if err != nil { - log.Warningf("failed to inject password by cloud-init error: %v", err) - } - } - - if len(desc.UserData) > 0 { - oUserData, err := cloudinit.ParseUserData(desc.UserData) - if err != nil { - return err - } - 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) { - switch guest.GetDriver().GetWindowsUserDataType() { - case cloudprovider.CLOUD_EC2: - desc.UserData = oUserData.UserDataEc2() - default: - desc.UserData = oUserData.UserDataPowerShell() - } - if guest.GetDriver().IsWindowsUserDataTypeNeedEncode() { - desc.UserData = base64.StdEncoding.EncodeToString([]byte(desc.UserData)) - } - } + desc.UserData, err = desc.GetUserData() + if err != nil { + return errors.Wrapf(err, "GetUserData") } action, err := config.GetString("action") diff --git a/pkg/compute/guestdrivers/nutanix.go b/pkg/compute/guestdrivers/nutanix.go index 9cd24ef821..cbc8777bc3 100644 --- a/pkg/compute/guestdrivers/nutanix.go +++ b/pkg/compute/guestdrivers/nutanix.go @@ -93,7 +93,7 @@ func (self *SNutanixGuestDriver) GetUserDataType() string { return cloudprovider.CLOUD_SHELL } -func (self *SNutanixGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool { +func (self *SNutanixGuestDriver) IsNeedInjectPasswordByCloudInit() bool { return true } diff --git a/pkg/compute/guestdrivers/openstack.go b/pkg/compute/guestdrivers/openstack.go index 0d261e2cf9..c8cccd107c 100644 --- a/pkg/compute/guestdrivers/openstack.go +++ b/pkg/compute/guestdrivers/openstack.go @@ -137,7 +137,7 @@ func (self *SOpenStackGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ( return []string{api.VM_READY, api.VM_RUNNING}, nil } -func (self *SOpenStackGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool { +func (self *SOpenStackGuestDriver) IsNeedInjectPasswordByCloudInit() bool { return true } diff --git a/pkg/compute/guestdrivers/zstack.go b/pkg/compute/guestdrivers/zstack.go index aafeb3afd3..d45ccc942a 100644 --- a/pkg/compute/guestdrivers/zstack.go +++ b/pkg/compute/guestdrivers/zstack.go @@ -145,7 +145,7 @@ func (self *SZStackGuestDriver) GetGuestInitialStateAfterRebuild() string { return api.VM_READY } -func (self *SZStackGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool { +func (self *SZStackGuestDriver) IsNeedInjectPasswordByCloudInit() bool { return true } diff --git a/pkg/compute/models/guestdrivers.go b/pkg/compute/models/guestdrivers.go index 8b5ac8a59a..afb9805691 100644 --- a/pkg/compute/models/guestdrivers.go +++ b/pkg/compute/models/guestdrivers.go @@ -186,7 +186,7 @@ type IGuestDriver interface { ValidateChangeConfig(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, cpuChanged bool, memChanged bool, newDisks []*api.DiskConfig) error ValidateDetachDisk(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, disk *SDisk) error - IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool + IsNeedInjectPasswordByCloudInit() bool GetUserDataType() string GetWindowsUserDataType() string IsWindowsUserDataTypeNeedEncode() bool