From bfc1f0125f95ab17eb0b3a95a08fb6c1715adfde Mon Sep 17 00:00:00 2001 From: tangbin Date: Fri, 14 Feb 2020 12:29:14 +0800 Subject: [PATCH] huawei userdata fix --- pkg/compute/guestdrivers/huawei.go | 9 +- pkg/multicloud/ctyun/disk.go | 4 + pkg/multicloud/huawei/host.go | 19 +++- pkg/multicloud/huawei/instance.go | 138 ++++++++++++++---------- pkg/multicloud/huawei/shell/instance.go | 3 +- 5 files changed, 108 insertions(+), 65 deletions(-) diff --git a/pkg/compute/guestdrivers/huawei.go b/pkg/compute/guestdrivers/huawei.go index e3b8718aee..eeaedf70ab 100644 --- a/pkg/compute/guestdrivers/huawei.go +++ b/pkg/compute/guestdrivers/huawei.go @@ -112,11 +112,6 @@ func (self *SHuaweiGuestDriver) GetGuestInitialStateAfterRebuild() string { func (self *SHuaweiGuestDriver) GetLinuxDefaultAccount(desc cloudprovider.SManagedVMCreateConfig) string { userName := "root" - if desc.ImageType == "system" { - if desc.OsDistribution == "Ubuntu" { - userName = "ubuntu" - } - } if desc.OsType == "Windows" { userName = "Administrator" } @@ -131,3 +126,7 @@ func (self *SHuaweiGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle return false } + +func (self *SHuaweiGuestDriver) IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool { + return true +} diff --git a/pkg/multicloud/ctyun/disk.go b/pkg/multicloud/ctyun/disk.go index 068deed93e..d1dc8986d7 100644 --- a/pkg/multicloud/ctyun/disk.go +++ b/pkg/multicloud/ctyun/disk.go @@ -113,6 +113,10 @@ func (self *SDisk) GetCreatedAt() time.Time { } func (self *SDisk) GetExpiredAt() time.Time { + if self.ExpireTime == 0 { + return time.Time{} + } + return time.Unix(self.ExpireTime/1000, 0) } diff --git a/pkg/multicloud/huawei/host.go b/pkg/multicloud/huawei/host.go index c8ec251dc2..52c3dbe68e 100644 --- a/pkg/multicloud/huawei/host.go +++ b/pkg/multicloud/huawei/host.go @@ -16,9 +16,12 @@ package huawei import ( "fmt" + "strings" "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/osprofile" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -177,7 +180,7 @@ func (self *SHost) GetInstanceById(instanceId string) (*SInstance, error) { } func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) { - vmId, err := self._createVM(desc.Name, desc.ExternalImageId, desc.SysDisk, desc.Cpu, desc.MemoryMB, desc.InstanceType, desc.ExternalNetworkId, desc.IpAddr, desc.Description, desc.Password, desc.DataDisks, desc.PublicKey, desc.ExternalSecgroupId, desc.UserData, desc.BillingCycle) + vmId, err := self._createVM(desc.Name, desc.ExternalImageId, desc.SysDisk, desc.Cpu, desc.MemoryMB, desc.InstanceType, desc.ExternalNetworkId, desc.IpAddr, desc.Description, desc.Account, desc.Password, desc.DataDisks, desc.PublicKey, desc.ExternalSecgroupId, desc.UserData, desc.BillingCycle) if err != nil { return nil, err } @@ -195,7 +198,7 @@ func (self *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error } func (self *SHost) _createVM(name string, imgId string, sysDisk cloudprovider.SDiskInfo, cpu int, memMB int, instanceType string, - networkId string, ipAddr string, desc string, passwd string, + networkId string, ipAddr string, desc string, account string, passwd string, diskSizes []cloudprovider.SDiskInfo, publicKey string, secgroupId string, userData string, bc *billing.SBillingCycle) (string, error) { net := self.zone.getNetworkById(networkId) @@ -233,6 +236,18 @@ func (self *SHost) _createVM(name string, imgId string, sysDisk cloudprovider.SD log.Errorf("image %s status %s", imgId, img.Status) return "", fmt.Errorf("image not ready") } + // passwd, windows机型直接使用密码比较方便 + if strings.ToLower(img.Platform) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) && len(passwd) > 0 { + keypair = "" + } + + if strings.ToLower(img.Platform) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) { + if u, err := updateWindowsUserData(userData, img.OSVersion, account, passwd); err == nil { + userData = u + } else { + return "", errors.Wrap(err, "SHost.CreateVM.updateWindowsUserData") + } + } disks := make([]SDisk, len(diskSizes)+1) disks[0].SizeGB = img.SizeGB diff --git a/pkg/multicloud/huawei/instance.go b/pkg/multicloud/huawei/instance.go index b07ac33e42..09d298b710 100644 --- a/pkg/multicloud/huawei/instance.go +++ b/pkg/multicloud/huawei/instance.go @@ -16,6 +16,7 @@ package huawei import ( "context" + "encoding/base64" "fmt" "sort" "strconv" @@ -518,7 +519,6 @@ func (self *SInstance) UpdateUserData(userData string) error { // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0067876349.html 使用原镜像重装 // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0067876971.html 更换系统盘操作系统 // 不支持调整系统盘大小 -// todo: 支持注入user_data func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { var err error var jobId string @@ -531,13 +531,28 @@ func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SMan } } + image, err := self.host.zone.region.GetImage(desc.ImageId) + if err != nil { + return "", errors.Wrap(err, "SInstance.RebuildRoot.GetImage") + } + + // Password存在的情况下,windows 系统直接使用密码 + if strings.ToLower(image.Platform) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) && len(desc.Password) > 0 { + publicKeyName = "" + } + + userData, err := updateUserData(self.OSEXTSRVATTRUserData, image.OSVersion, desc.Account, desc.Password, desc.PublicKey) + if err != nil { + return "", errors.Wrap(err, "SInstance.RebuildRoot.updateUserData") + } + 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) + jobId, err = self.host.zone.region.RebuildRoot(ctx, self.UserID, self.GetId(), desc.Password, publicKeyName, userData) if err != nil { return "", err } } else { - jobId, err = self.host.zone.region.ChangeRoot(ctx, self.UserID, self.GetId(), desc.ImageId, desc.Password, publicKeyName, desc.PublicKey, self.OSEXTSRVATTRUserData) + jobId, err = self.host.zone.region.ChangeRoot(ctx, self.UserID, self.GetId(), desc.ImageId, desc.Password, publicKeyName, userData) if err != nil { return "", err } @@ -830,20 +845,7 @@ func (self *SRegion) CreateInstance(name string, imageId string, instanceType st } if len(userData) > 0 { - pwd := "" - k := "" - if len(keypair) > 0 { - k = publicKey - } else { - pwd = passwd - } - - udata, err := updateUserData(userData, "root", pwd, k) - if err != nil { - return "", errors.Wrap(err, "region.CreateInstance.UpdateUserData") - } - - params.UserData = udata + params.UserData = userData } serverObj := jsonutils.Marshal(params) @@ -1062,35 +1064,21 @@ func (self *SRegion) UpdateVM(instanceId, name string) error { // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0067876349.html // 返回job id -func (self *SRegion) RebuildRoot(ctx context.Context, userId, instanceId, passwd, publicKeyName, publicKey, userData string) (string, error) { +func (self *SRegion) RebuildRoot(ctx context.Context, userId, instanceId, passwd, publicKeyName, userData string) (string, error) { params := jsonutils.NewDict() reinstallObj := jsonutils.NewDict() - var udata string - var err error if len(publicKeyName) > 0 { reinstallObj.Add(jsonutils.NewString(publicKeyName), "keyname") - - if len(userData) > 0 { - if udata, err = updateUserData(userData, "root", "", publicKey); err != nil { - return "", errors.Wrap(err, "region.RebuildRoot.UpdateUserData set root publicKey") - } - } } else if len(passwd) > 0 { reinstallObj.Add(jsonutils.NewString(passwd), "adminpass") - - if len(userData) > 0 { - if udata, err = updateUserData(userData, "root", passwd, ""); err != nil { - return "", errors.Wrap(err, "region.RebuildRoot.UpdateUserData set root password") - } - } } else { return "", fmt.Errorf("both password and publicKey are empty.") } - if len(udata) > 0 { + if len(userData) > 0 { meta := jsonutils.NewDict() - meta.Add(jsonutils.NewString(udata), "user_data") + meta.Add(jsonutils.NewString(userData), "user_data") reinstallObj.Add(meta, "metadata") } @@ -1109,35 +1097,21 @@ func (self *SRegion) RebuildRoot(ctx context.Context, userId, instanceId, passwd // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0067876971.html // 返回job id -func (self *SRegion) ChangeRoot(ctx context.Context, userId, instanceId, imageId, passwd, publicKeyName, publicKey, userData string) (string, error) { +func (self *SRegion) ChangeRoot(ctx context.Context, userId, instanceId, imageId, passwd, publicKeyName, userData string) (string, error) { params := jsonutils.NewDict() changeOsObj := jsonutils.NewDict() - var udata string - var err error if len(publicKeyName) > 0 { changeOsObj.Add(jsonutils.NewString(publicKeyName), "keyname") - - if len(userData) > 0 { - if udata, err = updateUserData(userData, "root", "", publicKey); err != nil { - return "", errors.Wrap(err, "region.ChangeRoot.UpdateUserData set root publicKey") - } - } } else if len(passwd) > 0 { changeOsObj.Add(jsonutils.NewString(passwd), "adminpass") - - if len(userData) > 0 { - if udata, err = updateUserData(userData, "root", passwd, ""); err != nil { - return "", errors.Wrap(err, "region.ChangeRoot.UpdateUserData set root password") - } - } } else { return "", fmt.Errorf("both password and publicKey are empty.") } - if len(udata) > 0 { + if len(userData) > 0 { meta := jsonutils.NewDict() - meta.Add(jsonutils.NewString(udata), "user_data") + meta.Add(jsonutils.NewString(userData), "user_data") changeOsObj.Add(meta, "metadata") } @@ -1353,15 +1327,29 @@ func (self *SInstance) GetError() error { return nil } -func updateUserData(userData, username, password, publicKey string) (string, error) { - config, err := cloudinit.ParseUserDataBase64(userData) - if err != nil { - return "", fmt.Errorf("invalid userdata %s", userData) +func updateUserData(userData, osVersion, username, password, publicKey string) (string, error) { + winOS := strings.ToLower(osprofile.OS_TYPE_WINDOWS) + osVersion = strings.ToLower(osVersion) + config := &cloudinit.SCloudConfig{} + if strings.Contains(osVersion, winOS) { + if _config, err := cloudinit.ParseUserDataBase64(userData); err == nil { + config = _config + } else { + log.Debugf("updateWindowsUserData invalid userdata %s", userData) + } + } else { + if _config, err := cloudinit.ParseUserDataBase64(userData); err == nil { + config = _config + } else { + return "", fmt.Errorf("updateLinuxUserData invalid userdata %s", userData) + } } user := cloudinit.NewUser(username) config.RemoveUser(user) + config.DisableRoot = 0 if len(password) > 0 { + config.SshPwauth = cloudinit.SSH_PASSWORD_AUTH_ON user.Password(password) config.MergeUser(user) } @@ -1371,5 +1359,43 @@ func updateUserData(userData, username, password, publicKey string) (string, err config.MergeUser(user) } - return config.UserDataBase64(), nil + if strings.Contains(osVersion, winOS) { + userData, err := updateWindowsUserData(config.UserDataPowerShell(), osVersion, username, password) + if err != nil { + return "", errors.Wrap(err, "updateUserData.updateWindowsUserData") + } + return userData, nil + } else { + return config.UserDataBase64(), nil + } +} + +func updateWindowsUserData(userData string, osVersion string, username, password string) (string, error) { + // Windows Server 2003, Windows Vista, Windows Server 2008, Windows Server 2003 R2, Windows Server 2000, Windows Server 2012, Windows Server 2003 with SP1, Windows 8 + oldVersions := []string{"2000", "2003", "2008", "2012", "Vista"} + isOldVersion := false + for i := range oldVersions { + if strings.Contains(osVersion, oldVersions[i]) { + isOldVersion = true + } + } + + shells := "" + if isOldVersion { + shells += fmt.Sprintf("rem cmd\n") + if username == "Administrator" { + shells += fmt.Sprintf("net user %s %s\n", username, password) + } else { + shells += fmt.Sprintf("net user %s %s /add\n", username, password) + shells += fmt.Sprintf("net localgroup administrators %s /add\n", username) + } + + shells += fmt.Sprintf("net user %s /active:yes", username) + } else { + if !strings.HasPrefix(userData, "#ps1") { + shells = fmt.Sprintf("#ps1\n%s", userData) + } + } + + return base64.StdEncoding.EncodeToString([]byte(shells)), nil } diff --git a/pkg/multicloud/huawei/shell/instance.go b/pkg/multicloud/huawei/shell/instance.go index 5229e51e14..cef83398c9 100644 --- a/pkg/multicloud/huawei/shell/instance.go +++ b/pkg/multicloud/huawei/shell/instance.go @@ -120,13 +120,12 @@ func init() { Image string `help:"Image ID"` Password string `help:"admin password"` PublicKeyName string `help:"public key name"` - PublicKey string `help:"public key"` UserData string `help:"cloud-init user data"` } shellutils.R(&InstanceRebuildRootOptions{}, "instance-rebuild-root", "Reinstall virtual server system image", func(cli *huawei.SRegion, args *InstanceRebuildRootOptions) error { ctx := context.Background() - jobId, err := cli.ChangeRoot(ctx, args.UserId, args.ID, args.Image, args.Password, args.PublicKeyName, args.PublicKey, args.UserData) + jobId, err := cli.ChangeRoot(ctx, args.UserId, args.ID, args.Image, args.Password, args.PublicKeyName, args.UserData) if err != nil { return err }