mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-24 12:32:55 +08:00
修正:1. 主机创建/rebuild root密码设置问题 2. climc 密码设置问题 3.
billing_type字段为空导致创建主机失败问题
This commit is contained in:
@@ -50,6 +50,11 @@ func init() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.NoAccountInit != nil && *opts.NoAccountInit {
|
||||
params.Add(jsonutils.JSONFalse, "reset_password")
|
||||
}
|
||||
|
||||
count := options.IntV(opts.Count)
|
||||
if options.BoolV(opts.DryRun) {
|
||||
results, err := modules.SchedManager.DoScheduleListResult(s, params, count)
|
||||
@@ -328,6 +333,11 @@ func init() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.NoAccountInit != nil && *opts.NoAccountInit {
|
||||
params.Add(jsonutils.JSONFalse, "reset_password")
|
||||
}
|
||||
|
||||
srv, err := modules.Servers.PerformAction(s, opts.ID, "rebuild-root", params)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -338,6 +348,13 @@ func init() {
|
||||
|
||||
R(&options.ServerChangeConfigOptions{}, "server-change-config", "Change configuration of VM", func(s *mcclient.ClientSession, opts *options.ServerChangeConfigOptions) error {
|
||||
params, err := options.StructToParams(opts)
|
||||
if len(opts.Disk) > 0 {
|
||||
params.Remove("disk.0")
|
||||
for i, d := range opts.Disk {
|
||||
params.Set(fmt.Sprintf("disk.%d", i+1), jsonutils.NewString(d))
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -150,6 +150,9 @@ func fetchIVMinfo(desc SAliyunVMCreateConfig, iVM cloudprovider.ICloudVM, guestI
|
||||
}
|
||||
data.Add(jsonutils.NewString("root"), "account")
|
||||
data.Add(jsonutils.NewString(encpasswd), "key")
|
||||
} else {
|
||||
data.Add(jsonutils.NewString(""), "account")
|
||||
data.Add(jsonutils.NewString(""), "key")
|
||||
}
|
||||
|
||||
if len(desc.OsDistribution) > 0 {
|
||||
|
||||
@@ -8,7 +8,7 @@ const (
|
||||
)
|
||||
|
||||
type SBillingResourceBase struct {
|
||||
BillingType string `width:"36" charset:"ascii" nullable:"true" default:"postpaid" list:"user" create:"required"`
|
||||
BillingType string `width:"36" charset:"ascii" nullable:"true" default:"postpaid" list:"user" create:"optional"`
|
||||
ExpiredAt time.Time `nullable:"true" list:"user" create:"optional"`
|
||||
}
|
||||
|
||||
|
||||
+108
-42
@@ -36,6 +36,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
"yunion.io/x/onecloud/pkg/util/seclib2"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -272,8 +273,8 @@ func (manager *SGuestManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQ
|
||||
if count > 0 {
|
||||
sgq := guestdisks.Query(guestdisks.Field("guest_id")).
|
||||
Filter(sqlchemy.AND(
|
||||
sqlchemy.Equals(guestdisks.Field("disk_id"), disk.Id),
|
||||
sqlchemy.IsFalse(guestdisks.Field("deleted"))))
|
||||
sqlchemy.Equals(guestdisks.Field("disk_id"), disk.Id),
|
||||
sqlchemy.IsFalse(guestdisks.Field("deleted"))))
|
||||
q = q.Filter(sqlchemy.In(q.Field("id"), sgq))
|
||||
} else {
|
||||
hosts := HostManager.Query().SubQuery()
|
||||
@@ -281,11 +282,11 @@ func (manager *SGuestManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQ
|
||||
storages := StorageManager.Query().SubQuery()
|
||||
sq := hosts.Query(hosts.Field("id")).
|
||||
Join(hoststorages, sqlchemy.AND(
|
||||
sqlchemy.Equals(hoststorages.Field("host_id"), hosts.Field("id")),
|
||||
sqlchemy.IsFalse(hoststorages.Field("deleted")))).
|
||||
sqlchemy.Equals(hoststorages.Field("host_id"), hosts.Field("id")),
|
||||
sqlchemy.IsFalse(hoststorages.Field("deleted")))).
|
||||
Join(storages, sqlchemy.AND(
|
||||
sqlchemy.Equals(storages.Field("id"), hoststorages.Field("storage_id")),
|
||||
sqlchemy.IsFalse(storages.Field("deleted")))).
|
||||
sqlchemy.Equals(storages.Field("id"), hoststorages.Field("storage_id")),
|
||||
sqlchemy.IsFalse(storages.Field("deleted")))).
|
||||
Filter(sqlchemy.Equals(storages.Field("id"), disk.StorageId)).SubQuery()
|
||||
q = q.In("host_id", sq)
|
||||
}
|
||||
@@ -339,8 +340,8 @@ func (manager *SGuestManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQ
|
||||
isodev := IsolatedDeviceManager.Query().SubQuery()
|
||||
sgq := isodev.Query(isodev.Field("guest_id")).
|
||||
Filter(sqlchemy.AND(
|
||||
sqlchemy.IsNotNull(isodev.Field("guest_id")),
|
||||
sqlchemy.Startswith(isodev.Field("dev_type"), "GPU")))
|
||||
sqlchemy.IsNotNull(isodev.Field("guest_id")),
|
||||
sqlchemy.Startswith(isodev.Field("dev_type"), "GPU")))
|
||||
showGpu := utils.ToBool(gpu)
|
||||
cond := sqlchemy.NotIn
|
||||
if showGpu {
|
||||
@@ -561,6 +562,14 @@ func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred m
|
||||
data.Add(jsonutils.NewInt(int64(vmemSize)), "vmem_size")
|
||||
data.Add(jsonutils.NewInt(int64(vcpuCount)), "vcpu_count")
|
||||
|
||||
resetPassword := jsonutils.QueryBoolean(data, "reset_password", true)
|
||||
passwd, _ := data.GetString("password")
|
||||
if resetPassword && len(passwd) > 0 {
|
||||
if ! seclib2.MeetComplxity(passwd) {
|
||||
return nil, httperrors.NewWeakPasswordError()
|
||||
}
|
||||
}
|
||||
|
||||
disk0Json, _ := data.Get("disk.0")
|
||||
if disk0Json == nil {
|
||||
return nil, httperrors.NewInputParameterError("No disk information provided")
|
||||
@@ -2411,41 +2420,81 @@ func (self *SGuest) PerformPurge(ctx context.Context, userCred mcclient.TokenCre
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (self *SGuest) setKeypairId(userCred mcclient.TokenCredential, keypairId string) error {
|
||||
diff, err := self.GetModelManager().TableSpec().Update(self, func() error {
|
||||
self.KeypairId = keypairId
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SGuest) AllowPerformRebuildRoot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
return self.IsOwner(userCred)
|
||||
}
|
||||
|
||||
func (self *SGuest) PerformRebuildRoot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
imageId, _ := data.GetString("image_id")
|
||||
if utils.IsInStringArray(self.Status, []string{VM_READY, VM_RUNNING, VM_ADMIN}) {
|
||||
if !data.Contains("image_id") {
|
||||
gdc := self.CategorizeDisks()
|
||||
imageId = gdc.Root.GetTemplateId()
|
||||
if len(imageId) == 0 {
|
||||
return nil, httperrors.NewBadRequestError("No template for root disk")
|
||||
}
|
||||
img, err := CachedimageManager.getImageInfo(ctx, userCred, imageId, false)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError("Template %s not accessible: %s", imageId, err.Error())
|
||||
}
|
||||
osType, _ := img.Properties["os_type"]
|
||||
osName := self.GetMetadata("os_name", userCred)
|
||||
if len(osName) == 0 && len(osType) == 0 && strings.ToLower(osType) != strings.ToLower(osName) {
|
||||
return nil, httperrors.NewBadRequestError("Cannot switch OS between %s-%s", osName, osType)
|
||||
}
|
||||
}
|
||||
autoStart := jsonutils.QueryBoolean(data, "auto_start", false)
|
||||
var needStop = false
|
||||
if self.Status == VM_RUNNING {
|
||||
needStop = true
|
||||
}
|
||||
err := self.StartRebuildRootTask(ctx, userCred, imageId, needStop, autoStart)
|
||||
return nil, err
|
||||
if ! utils.IsInStringArray(self.Status, []string{VM_READY, VM_RUNNING, VM_ADMIN}) {
|
||||
return nil, httperrors.NewInvalidStatusError("Cannot reset root in status %s", self.Status)
|
||||
}
|
||||
return nil, httperrors.NewInvalidStatusError("Cannot reset root in status %s", self.Status)
|
||||
|
||||
if !data.Contains("image_id") {
|
||||
gdc := self.CategorizeDisks()
|
||||
imageId = gdc.Root.GetTemplateId()
|
||||
if len(imageId) == 0 {
|
||||
return nil, httperrors.NewBadRequestError("No template for root disk")
|
||||
}
|
||||
img, err := CachedimageManager.getImageInfo(ctx, userCred, imageId, false)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError("Template %s not accessible: %s", imageId, err.Error())
|
||||
}
|
||||
osType, _ := img.Properties["os_type"]
|
||||
osName := self.GetMetadata("os_name", userCred)
|
||||
if len(osName) == 0 && len(osType) == 0 && strings.ToLower(osType) != strings.ToLower(osName) {
|
||||
return nil, httperrors.NewBadRequestError("Cannot switch OS between %s-%s", osName, osType)
|
||||
}
|
||||
}
|
||||
|
||||
autoStart := jsonutils.QueryBoolean(data, "auto_start", false)
|
||||
var needStop = false
|
||||
if self.Status == VM_RUNNING {
|
||||
needStop = true
|
||||
}
|
||||
resetPasswd := jsonutils.QueryBoolean(data, "reset_password", true)
|
||||
passwd, _ := data.GetString("password")
|
||||
if len(passwd) > 0 {
|
||||
if ! seclib2.MeetComplxity(passwd) {
|
||||
return nil, httperrors.NewWeakPasswordError()
|
||||
}
|
||||
}
|
||||
|
||||
keypairStr := jsonutils.GetAnyString(data, []string{"keypair", "keypair_id"})
|
||||
if len(keypairStr) > 0 {
|
||||
keypairObj, err := KeypairManager.FetchByIdOrName(userCred.GetUserId(), keypairStr)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, httperrors.NewResourceNotFoundError("keypair %s not found", keypairStr)
|
||||
} else {
|
||||
return nil, httperrors.NewGeneralError(err)
|
||||
}
|
||||
}
|
||||
if self.KeypairId != keypairObj.GetId() {
|
||||
err = self.setKeypairId(userCred, keypairObj.GetId())
|
||||
if err != nil {
|
||||
return nil, httperrors.NewGeneralError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := self.StartRebuildRootTask(ctx, userCred, imageId, needStop, autoStart, passwd, resetPasswd)
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
func (self *SGuest) StartRebuildRootTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, needStop, autoStart bool) error {
|
||||
func (self *SGuest) StartRebuildRootTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, needStop, autoStart bool, passwd string, resetPasswd bool) error {
|
||||
data := jsonutils.NewDict()
|
||||
data.Set("image_id", jsonutils.NewString(imageId))
|
||||
if needStop {
|
||||
@@ -2454,6 +2503,14 @@ func (self *SGuest) StartRebuildRootTask(ctx context.Context, userCred mcclient.
|
||||
if autoStart {
|
||||
data.Set("auto_start", jsonutils.JSONTrue)
|
||||
}
|
||||
if resetPasswd {
|
||||
data.Set("reset_password", jsonutils.JSONTrue)
|
||||
} else {
|
||||
data.Set("reset_password", jsonutils.JSONFalse)
|
||||
}
|
||||
if len(passwd) > 0 {
|
||||
data.Set("password", jsonutils.NewString(passwd))
|
||||
}
|
||||
if self.GetHypervisor() == HYPERVISOR_BAREMETAL {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalServerRebuildRootTask", self, userCred, data, "", "", nil)
|
||||
if err != nil {
|
||||
@@ -2875,12 +2932,12 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
|
||||
}
|
||||
|
||||
provider, e := self.GetHost().GetDriver()
|
||||
if e != nil {
|
||||
/*if e != nil {
|
||||
log.Errorf("Get Provider Error: %s", e)
|
||||
return nil, httperrors.NewInsufficientResourceError("Provider Not Found")
|
||||
}
|
||||
}*/
|
||||
|
||||
if !provider.IsPublicCloud() {
|
||||
if e != nil || !provider.IsPublicCloud() {
|
||||
for storageId, needSize := range diskSizes {
|
||||
iStorage, err := StorageManager.FetchById(storageId)
|
||||
if err != nil {
|
||||
@@ -2904,6 +2961,9 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
|
||||
if jsonutils.QueryBoolean(data, "auto_start", false) {
|
||||
confs.Add(jsonutils.NewBool(true), "auto_start")
|
||||
}
|
||||
|
||||
log.Debugf("%s", confs.String())
|
||||
|
||||
pendingUsage := &SQuota{}
|
||||
if addCpu > 0 {
|
||||
pendingUsage.Cpu = addCpu
|
||||
@@ -3140,17 +3200,23 @@ func (self *SGuest) GetDeployConfigOnHost(ctx context.Context, host *SHost, para
|
||||
deployAction = "deploy"
|
||||
}
|
||||
|
||||
resetPasswd := true
|
||||
if deployAction == "deploy" {
|
||||
resetPasswd = jsonutils.QueryBoolean(params, "reset_password", false)
|
||||
}
|
||||
// resetPasswd := true
|
||||
// if deployAction == "deploy" {
|
||||
resetPasswd := jsonutils.QueryBoolean(params, "reset_password", true)
|
||||
//}
|
||||
|
||||
if resetPasswd {
|
||||
config.Add(jsonutils.JSONTrue, "reset_password")
|
||||
passwd, _ := params.GetString("password")
|
||||
if len(passwd) > 0 {
|
||||
config.Add(jsonutils.NewString(passwd), "password")
|
||||
}
|
||||
keypair := self.getKeypair()
|
||||
if keypair != nil {
|
||||
config.Add(jsonutils.NewString(keypair.PublicKey), "public_key")
|
||||
}
|
||||
} else {
|
||||
config.Add(jsonutils.JSONFalse, "reset_password")
|
||||
}
|
||||
|
||||
config.Add(jsonutils.NewString(deployAction), "action")
|
||||
@@ -3883,7 +3949,7 @@ func (manager *SGuestManager) GetIpInProjectWithName(projectId, name string, isE
|
||||
sqlchemy.IsFalse(guests.Field("pending_deleted"))),
|
||||
sqlchemy.IsFalse(guests.Field("deleted")))).
|
||||
Join(networks, sqlchemy.AND(sqlchemy.Equals(networks.Field("id"), guestnics.Field("network_id")),
|
||||
sqlchemy.IsFalse(networks.Field("deleted")))).
|
||||
sqlchemy.IsFalse(networks.Field("deleted")))).
|
||||
Filter(sqlchemy.Equals(guests.Field("name"), name)).
|
||||
Filter(sqlchemy.NotEquals(guestnics.Field("ip_addr"), "")).
|
||||
Filter(sqlchemy.IsNotNull(guestnics.Field("ip_addr"))).
|
||||
|
||||
@@ -139,9 +139,17 @@ func (self *KVMGuestRebuildRootTask) OnRebuildRootDiskComplete(ctx context.Conte
|
||||
|
||||
self.SetStage("OnGuestDeployComplete", nil)
|
||||
guest.SetStatus(self.UserCred, models.VM_DEPLOYING, "")
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("reset_password", jsonutils.JSONTrue)
|
||||
guest.StartGuestDeployTask(ctx, self.UserCred, params, "deploy", self.GetTaskId())
|
||||
// params := jsonutils.NewDict()
|
||||
// params.Set("reset_password", jsonutils.JSONTrue)
|
||||
|
||||
// clear logininfo
|
||||
loginParams := make(map[string]interface{})
|
||||
loginParams["login_account"] = "none"
|
||||
loginParams["login_key"] = "none"
|
||||
loginParams["login_key_timestamp"] = "none"
|
||||
guest.SetAllMetadata(ctx, loginParams, self.UserCred)
|
||||
|
||||
guest.StartGuestDeployTask(ctx, self.UserCred, self.GetParams(), "deploy", self.GetTaskId())
|
||||
}
|
||||
|
||||
func (self *KVMGuestRebuildRootTask) OnRebuildRootDiskCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
|
||||
@@ -100,6 +100,11 @@ func NewInputParameterError(msg string, params ...interface{}) *httputils.JSONCl
|
||||
return NewJsonClientError(400, "InputParameterError", msg, err)
|
||||
}
|
||||
|
||||
func NewWeakPasswordError() *httputils.JSONClientError {
|
||||
msg, err := errorMessage("password must be 12 chars of at least one digit, letter, uppercase letter and punctuate", nil)
|
||||
return NewJsonClientError(400, "WeakPasswordError", msg, err)
|
||||
}
|
||||
|
||||
func NewInsufficientResourceError(msg string, params ...interface{}) *httputils.JSONClientError {
|
||||
msg, err := errorMessage(msg, params)
|
||||
return NewJsonClientError(400, "InsufficientResourceError", msg, err)
|
||||
|
||||
@@ -98,7 +98,7 @@ type ServerCreateOptions struct {
|
||||
Bios string `help:"BIOS" choices:"BIOS|UEFI"`
|
||||
Desc string `help:"Description" metavar:"<DESCRIPTION>" json:"description"`
|
||||
Boot string `help:"Boot device" metavar:"<BOOT_DEVICE>" choices:"disk|cdrom" json:"-"`
|
||||
NoAccountInit *bool `help:"Not reset account password" json:"reset_password"`
|
||||
NoAccountInit *bool `help:"Not reset account password"`
|
||||
AllowDelete *bool `help:"Unlock server to allow deleting" json:"-"`
|
||||
ShutdownBehavior string `help:"Behavior after VM server shutdown, stop or terminate server" metavar:"<SHUTDOWN_BEHAVIOR>" choices:"stop|terminate"`
|
||||
AutoStart *bool `help:"Auto start server after it is created"`
|
||||
@@ -264,11 +264,12 @@ type ServerSaveImageOptions struct {
|
||||
}
|
||||
|
||||
type ServerRebuildRootOptions struct {
|
||||
ID string `help:"Server to rebuild root" json:"-"`
|
||||
Image string `help:"New root Image template ID" json:"image_id"`
|
||||
Keypair string `help:"ssh Keypair used for login"`
|
||||
Password string `help:"Default user password"`
|
||||
AutoStart *bool `help:"Auto start server after it is created"`
|
||||
ID string `help:"Server to rebuild root" json:"-"`
|
||||
Image string `help:"New root Image template ID" json:"image_id"`
|
||||
Keypair string `help:"ssh Keypair used for login"`
|
||||
Password string `help:"Default user password"`
|
||||
NoAccountInit *bool `help:"Not reset account password"`
|
||||
AutoStart *bool `help:"Auto start server after it is created"`
|
||||
}
|
||||
|
||||
type ServerChangeConfigOptions struct {
|
||||
|
||||
@@ -88,7 +88,10 @@ func (self *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) {
|
||||
func (self *SStoragecache) UploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, extId string, isForce bool) (string, error) {
|
||||
|
||||
if len(extId) > 0 {
|
||||
status, _ := self.region.GetImageStatus(extId)
|
||||
status, err := self.region.GetImageStatus(extId)
|
||||
if err != nil {
|
||||
log.Errorf("GetImageStatus error %s", err)
|
||||
}
|
||||
if status == ImageStatusAvailable && !isForce {
|
||||
return extId, nil
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const (
|
||||
ALL_DIGITS = "0123456789"
|
||||
ALL_LETTERS = "abcdefghijklmnopqrstuvwxyz"
|
||||
ALL_UPPERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
ALL_PUNC = PUNC
|
||||
ALL_PUNC = "~`!@#$%^&*()-_=+[]{}|:':\",./<>?"
|
||||
)
|
||||
|
||||
type PasswordStrength struct {
|
||||
@@ -72,3 +72,20 @@ func AnalyzePasswordStrenth(passwd string) PasswordStrength {
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
func (ps PasswordStrength) Len() int {
|
||||
return ps.Punctuats + ps.Uppercases + ps.Lowercases + ps.Digits
|
||||
}
|
||||
|
||||
func (ps PasswordStrength) MeetComplexity() bool {
|
||||
if ps.Punctuats > 0 && ps.Digits > 0 && ps.Lowercases > 0 && ps.Uppercases > 0 && ps.Len() >= 12 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func MeetComplxity(passwd string) bool {
|
||||
ps := AnalyzePasswordStrenth(passwd)
|
||||
return ps.MeetComplexity()
|
||||
}
|
||||
|
||||
@@ -10,3 +10,18 @@ func TestRandomPassword2(t *testing.T) {
|
||||
rand.Seed(time.Now().Unix())
|
||||
t.Logf("%s", RandomPassword2(12))
|
||||
}
|
||||
|
||||
func TestMeetComplxity(t *testing.T) {
|
||||
cases := [] struct {
|
||||
in string
|
||||
want bool
|
||||
} {
|
||||
{"123456", false},
|
||||
{"123abcABC!@#", true},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if c.want != MeetComplxity(c.in) {
|
||||
t.Errorf("%s != %v", c.in, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user