diff --git a/cmd/climc/shell/servers.go b/cmd/climc/shell/servers.go index a30a759422..0c4c4cb857 100644 --- a/cmd/climc/shell/servers.go +++ b/cmd/climc/shell/servers.go @@ -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 } diff --git a/pkg/compute/models/billingresource.go b/pkg/compute/models/billingresource.go index 3bb4303bfb..612919afdc 100644 --- a/pkg/compute/models/billingresource.go +++ b/pkg/compute/models/billingresource.go @@ -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"` } diff --git a/pkg/compute/models/capabilities.go b/pkg/compute/models/capabilities.go index c15d20ae4c..c85fe238ba 100644 --- a/pkg/compute/models/capabilities.go +++ b/pkg/compute/models/capabilities.go @@ -51,6 +51,7 @@ func getHypervisors(zone *SZone) []string { if zone != nil { q = q.Equals("zone_id", zone.Id) } + q = q.IsNotEmpty("host_type").IsNotNull("host_type") q = q.Distinct() rows, err := q.Rows() if err != nil { @@ -72,6 +73,8 @@ func getStorageTypes(zone *SZone) []string { if zone != nil { q = q.Equals("zone_id", zone.Id) } + q = q.IsNotEmpty("storage_type").IsNotNull("storage_type") + q = q.IsNotEmpty("medium_type").IsNotNull("medium_type") q = q.Distinct() rows, err := q.Rows() if err != nil { diff --git a/pkg/compute/models/elasticips.go b/pkg/compute/models/elasticips.go index 02d10139eb..8ba88c35bd 100644 --- a/pkg/compute/models/elasticips.go +++ b/pkg/compute/models/elasticips.go @@ -39,6 +39,8 @@ const ( EIP_STATUS_DISSOCIATE = "dissociate" EIP_STATUS_DISSOCIATE_FAIL = "dissociate_fail" + EIP_STATUS_CHANGE_BANDWIDTH = "change_bandwidth" + EIP_CHARGE_TYPE_BY_TRAFFIC = "traffic" EIP_CHARGE_TYPE_BY_BANDWIDTH = "bandwidth" EIP_CHARGE_TYPE_DEFAULT = EIP_CHARGE_TYPE_BY_TRAFFIC @@ -178,7 +180,7 @@ func (manager *SElasticipManager) SyncEips(ctx context.Context, userCred mcclien } func (self *SElasticip) SyncInstanceWithCloudEip(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudEIP) error { - vm := self.getVM() + vm := self.GetAssociateVM() vmExtId := ext.GetAssociationExternalId() if vm == nil && len(vmExtId) == 0 { @@ -281,7 +283,7 @@ func (manager *SElasticipManager) getEipForInstance(instanceType string, instanc return &eip, nil } -func (self *SElasticip) getVM() *SGuest { +func (self *SElasticip) GetAssociateVM() *SGuest { if self.AssociateType == "server" && len(self.AssociateId) > 0 { return GuestManager.FetchGuestById(self.AssociateId) } @@ -292,7 +294,7 @@ func (self *SElasticip) Dissociate(ctx context.Context, userCred mcclient.TokenC if len(self.AssociateType) == 0 { return nil } - vm := self.getVM() + vm := self.GetAssociateVM() if vm == nil { log.Errorf("dissociate VM not exists???") } @@ -305,7 +307,9 @@ func (self *SElasticip) Dissociate(ctx context.Context, userCred mcclient.TokenC return err } if vm != nil { + db.OpsLog.LogDetachEvent(vm, self, userCred, self.GetShortDesc()) db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, vm.GetShortDesc(), userCred) + db.OpsLog.LogEvent(vm, db.ACT_EIP_DETACH, self.GetShortDesc(), userCred) } if self.Mode == EIP_MODE_INSTANCE_PUBLICIP { self.Delete(ctx, userCred) @@ -325,7 +329,11 @@ func (self *SElasticip) AssociateVM(userCred mcclient.TokenCredential, vm *SGues if err != nil { return err } + + db.OpsLog.LogAttachEvent(vm, self, userCred, self.GetShortDesc()) db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, vm.GetShortDesc(), userCred) + db.OpsLog.LogEvent(vm, db.ACT_EIP_ATTACH, self.GetShortDesc(), userCred) + return nil } @@ -638,7 +646,7 @@ func (self *SElasticip) GetCustomizeColumns(ctx context.Context, userCred mcclie } func (self *SElasticip) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict { - vm := self.getVM() + vm := self.GetAssociateVM() if vm != nil { extra.Add(jsonutils.NewString(vm.GetName()), "associate_name") } @@ -704,6 +712,9 @@ func (self *SElasticip) PerformChangeBandwidth(ctx context.Context, userCred mcc } func (self *SElasticip) StartEipChangeBandwidthTask(ctx context.Context, userCred mcclient.TokenCredential, bandwidth int64) error { + + self.SetStatus(userCred, EIP_STATUS_CHANGE_BANDWIDTH, "change bandwidth") + params := jsonutils.NewDict() params.Add(jsonutils.NewInt(bandwidth), "bandwidth") @@ -722,6 +733,8 @@ func (self *SElasticip) DoChangeBandwidth(userCred mcclient.TokenCredential, ban return nil }) + self.SetStatus(userCred, EIP_STATUS_READY, "finish change bandwidth") + if err != nil { log.Errorf("DoChangeBandwidth update fail %s", err) return err diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 78f0fd44c0..2a63983462 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -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 ( @@ -99,6 +100,9 @@ const ( VM_RESTORE_STATE = "restore_state" VM_RESTORE_FAILED = "restore_failed" + VM_ASSOCIATE_EIP = "associate_eip" + VM_DISSOCIATE_EIP = "dissociate_eip" + VM_REMOVE_STATEFILE = "remove_state" VM_ADMIN = "admin" @@ -272,8 +276,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 +285,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 +343,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 +565,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 +2423,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 +2506,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 +2935,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 +2964,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 +3203,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") @@ -3939,7 +4008,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"))). @@ -4138,7 +4207,7 @@ func (self *SGuest) PerformAssociateEip(ctx context.Context, userCred mcclient.T return nil, httperrors.NewUnsupportOperationError("fixed eip cannot be associated") } - eipVm := eip.getVM() + eipVm := eip.GetAssociateVM() if eipVm != nil { return nil, httperrors.NewConflictError("eip has been associated") } @@ -4156,6 +4225,8 @@ func (self *SGuest) PerformAssociateEip(ctx context.Context, userCred mcclient.T return nil, httperrors.NewInputParameterError("cannot associate eip and instance in different provider") } + self.SetStatus(userCred, VM_ASSOCIATE_EIP, "associate eip") + params := jsonutils.NewDict() params.Add(jsonutils.NewString(self.ExternalId), "instance_external_id") params.Add(jsonutils.NewString(self.Id), "instance_id") @@ -4179,6 +4250,9 @@ func (self *SGuest) PerformDissociateEip(ctx context.Context, userCred mcclient. if eip == nil { return nil, httperrors.NewInvalidStatusError("No eip to dissociate") } + + self.SetStatus(userCred, VM_DISSOCIATE_EIP, "associate eip") + err = eip.StartEipDissociateTask(ctx, userCred, "") if err != nil { log.Errorf("fail to start dissociate task %s", err) diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 33a529f53a..d090098234 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -734,6 +734,30 @@ func isExitNetworkInfo(netConfig *SNetworkConfig) bool { return false } +func (self *SNetwork) getZone() *SZone { + wire := self.GetWire() + if wire != nil { + return wire.GetZone() + } + return nil +} + +func (self *SNetwork) getVpc() *SVpc { + wire := self.GetWire() + if wire != nil { + return wire.getVpc() + } + return nil +} + +func (self *SNetwork) getRegion() *SCloudregion { + wire := self.GetWire() + if wire != nil { + return wire.getRegion() + } + return nil +} + func (self *SNetwork) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict { wire := self.GetWire() extra.Add(jsonutils.NewString(wire.Name), "wire") @@ -747,6 +771,34 @@ func (self *SNetwork) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSOND extra.Add(jsonutils.NewInt(int64(self.GetBaremetalNicsCount())), "bm_vnics") extra.Add(jsonutils.NewInt(int64(self.GetGroupNicsCount())), "group_vnics") extra.Add(jsonutils.NewInt(int64(self.GetReservedNicsCount())), "reserve_vnics") + + zone := self.getZone() + if zone != nil { + extra.Add(jsonutils.NewString(zone.GetId()), "zone_id") + extra.Add(jsonutils.NewString(zone.GetName()), "zone") + if len(zone.GetExternalId()) > 0 { + extra.Add(jsonutils.NewString(zone.GetExternalId()), "zone_external_id") + } + } + + region := self.getRegion() + if region != nil { + extra.Add(jsonutils.NewString(region.GetId()), "region_id") + extra.Add(jsonutils.NewString(region.GetName()), "region") + if len(region.GetExternalId()) > 0 { + extra.Add(jsonutils.NewString(region.GetExternalId()), "region_external_id") + } + } + + vpc := self.getVpc() + if vpc != nil { + extra.Add(jsonutils.NewString(vpc.GetId()), "vpc_id") + extra.Add(jsonutils.NewString(vpc.GetName()), "vpc") + if len(vpc.GetExternalId()) > 0 { + extra.Add(jsonutils.NewString(vpc.GetExternalId()), "vpc_external_id") + } + } + return extra } diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index e2d11f3b72..67fd0cbc82 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -124,7 +124,12 @@ func (self *SVpc) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict extra.Add(jsonutils.NewInt(int64(self.GetWireCount())), "wire_count") extra.Add(jsonutils.NewInt(int64(self.GetNetworkCount())), "network_count") region := self.GetRegion() - extra.Add(jsonutils.NewString(region.GetName()), "region") + if region != nil { + extra.Add(jsonutils.NewString(region.GetName()), "region") + if len(region.GetExternalId()) > 0 { + extra.Add(jsonutils.NewString(region.GetExternalId()), "region_external_id") + } + } return extra } diff --git a/pkg/compute/models/wires.go b/pkg/compute/models/wires.go index 7dea974aed..345de3fd12 100644 --- a/pkg/compute/models/wires.go +++ b/pkg/compute/models/wires.go @@ -561,3 +561,48 @@ func (manager *SWireManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQu return q, err } + +func (self *SWire) getRegion() *SCloudregion { + zone := self.GetZone() + if zone != nil { + return zone.GetRegion() + } + return nil +} + +func (self *SWire) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { + extra := self.SStandaloneResourceBase.GetCustomizeColumns(ctx, userCred, query) + return self.getMoreDetails(extra) +} + +func (self *SWire) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { + extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query) + return self.getMoreDetails(extra) +} + +func (self *SWire) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict { + extra.Add(jsonutils.NewInt(int64(self.NetworkCount())), "networks") + zone := self.GetZone() + if zone != nil { + extra.Add(jsonutils.NewString(zone.GetName()), "zone") + if len(zone.GetExternalId()) > 0 { + extra.Add(jsonutils.NewString(zone.GetExternalId()), "zone_external_id") + } + } + region := self.getRegion() + if region != nil { + extra.Add(jsonutils.NewString(region.GetId()), "region_id") + extra.Add(jsonutils.NewString(region.GetName()), "region") + if len(region.GetExternalId()) > 0 { + extra.Add(jsonutils.NewString(region.GetExternalId()), "region_external_id") + } + } + vpc := self.getVpc() + if vpc != nil { + extra.Add(jsonutils.NewString(vpc.GetName()), "vpc") + if len(vpc.GetExternalId()) > 0 { + extra.Add(jsonutils.NewString(vpc.GetExternalId()), "vpc_external_id") + } + } + return extra +} \ No newline at end of file diff --git a/pkg/compute/tasks/eip_associate_task.go b/pkg/compute/tasks/eip_associate_task.go index da81236056..77bf7077d5 100644 --- a/pkg/compute/tasks/eip_associate_task.go +++ b/pkg/compute/tasks/eip_associate_task.go @@ -32,6 +32,11 @@ func (self *EipAssociateTask) OnInit(ctx context.Context, obj db.IStandaloneMode instanceId, _ := self.Params.GetString("instance_id") server := models.GuestManager.FetchGuestById(instanceId) + + if server.Status != models.VM_ASSOCIATE_EIP { + server.SetStatus(self.UserCred, models.VM_ASSOCIATE_EIP, "associate eip") + } + if server == nil { msg := fmt.Sprintf("fail to find server for instanceId %s", instanceId) eip.SetStatus(self.UserCred, models.EIP_STATUS_ASSOCIATE_FAIL, msg) @@ -57,5 +62,7 @@ func (self *EipAssociateTask) OnInit(ctx context.Context, obj db.IStandaloneMode eip.SetStatus(self.UserCred, models.EIP_STATUS_READY, "associate") + server.StartSyncstatus(ctx, self.UserCred, "") + self.SetStageComplete(ctx, nil) } \ No newline at end of file diff --git a/pkg/compute/tasks/eip_dissociate_task.go b/pkg/compute/tasks/eip_dissociate_task.go index 637133792b..cc77e02e8e 100644 --- a/pkg/compute/tasks/eip_dissociate_task.go +++ b/pkg/compute/tasks/eip_dissociate_task.go @@ -23,33 +23,43 @@ func init() { func (self *EipDissociateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { eip := obj.(*models.SElasticip) - extEip, err := eip.GetIEip() - if err != nil { - msg := fmt.Sprintf("fail to find iEIP for eip %s", err) - eip.SetStatus(self.UserCred, models.EIP_STATUS_DISSOCIATE_FAIL, msg) - self.SetStageFailed(ctx, msg) - return - } + server := eip.GetAssociateVM() + if server != nil { - if len(extEip.GetAssociationExternalId()) > 0 { - err = extEip.Dissociate() + if server.Status != models.VM_DISSOCIATE_EIP { + server.SetStatus(self.UserCred, models.VM_DISSOCIATE_EIP, "dissociate eip") + } + + extEip, err := eip.GetIEip() if err != nil { - msg := fmt.Sprintf("fail to remote dissociate eip %s", err) + msg := fmt.Sprintf("fail to find iEIP for eip %s", err) eip.SetStatus(self.UserCred, models.EIP_STATUS_DISSOCIATE_FAIL, msg) self.SetStageFailed(ctx, msg) return } - } - err = eip.Dissociate(ctx, self.UserCred) - if err != nil { - msg := fmt.Sprintf("fail to local dissociate eip %s", err) - eip.SetStatus(self.UserCred, models.EIP_STATUS_DISSOCIATE_FAIL, msg) - self.SetStageFailed(ctx, msg) - return - } + if len(extEip.GetAssociationExternalId()) > 0 { + err = extEip.Dissociate() + if err != nil { + msg := fmt.Sprintf("fail to remote dissociate eip %s", err) + eip.SetStatus(self.UserCred, models.EIP_STATUS_DISSOCIATE_FAIL, msg) + self.SetStageFailed(ctx, msg) + return + } + } - eip.SetStatus(self.UserCred, models.EIP_STATUS_READY, "dissociate") + err = eip.Dissociate(ctx, self.UserCred) + if err != nil { + msg := fmt.Sprintf("fail to local dissociate eip %s", err) + eip.SetStatus(self.UserCred, models.EIP_STATUS_DISSOCIATE_FAIL, msg) + self.SetStageFailed(ctx, msg) + return + } + + eip.SetStatus(self.UserCred, models.EIP_STATUS_READY, "dissociate") + + server.StartSyncstatus(ctx, self.UserCred, "") + } self.SetStageComplete(ctx, nil) diff --git a/pkg/compute/tasks/guest_rebuild_root_task.go b/pkg/compute/tasks/guest_rebuild_root_task.go index 4202c450df..ebceff6cd4 100644 --- a/pkg/compute/tasks/guest_rebuild_root_task.go +++ b/pkg/compute/tasks/guest_rebuild_root_task.go @@ -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) { diff --git a/pkg/httperrors/errors.go b/pkg/httperrors/errors.go index 4df2957513..662b59ec0e 100644 --- a/pkg/httperrors/errors.go +++ b/pkg/httperrors/errors.go @@ -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) diff --git a/pkg/mcclient/options/servers.go b/pkg/mcclient/options/servers.go index ed7e731b02..cd0a866205 100644 --- a/pkg/mcclient/options/servers.go +++ b/pkg/mcclient/options/servers.go @@ -98,7 +98,7 @@ type ServerCreateOptions struct { Bios string `help:"BIOS" choices:"BIOS|UEFI"` Desc string `help:"Description" metavar:"" json:"description"` Boot string `help:"Boot device" metavar:"" 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:"" 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 { diff --git a/pkg/util/aliyun/storagecache.go b/pkg/util/aliyun/storagecache.go index d945c8d054..5f8bd8e41c 100644 --- a/pkg/util/aliyun/storagecache.go +++ b/pkg/util/aliyun/storagecache.go @@ -88,10 +88,17 @@ 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) + log.Debugf("UploadImage: Image external ID exists %s", extId) + + status, err := self.region.GetImageStatus(extId) + if err != nil { + log.Errorf("GetImageStatus error %s", err) + } if status == ImageStatusAvailable && !isForce { return extId, nil } + } else { + log.Debugf("UploadImage: no external ID") } return self.uploadImage(userCred, imageId, osArch, osType, osDist, isForce) diff --git a/pkg/util/seclib2/seclib.go b/pkg/util/seclib2/seclib.go index af4609363b..ef1c0bb3cc 100644 --- a/pkg/util/seclib2/seclib.go +++ b/pkg/util/seclib2/seclib.go @@ -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() +} diff --git a/pkg/util/seclib2/seclib_test.go b/pkg/util/seclib2/seclib_test.go index c869234eba..9921aa09fb 100644 --- a/pkg/util/seclib2/seclib_test.go +++ b/pkg/util/seclib2/seclib_test.go @@ -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) + } + } +}