- postpaid server support expire
- extend distinct tenant filed
- gpu batch perform action
This commit is contained in:
wanyaoqi
2019-10-08 14:46:11 +08:00
parent 7c2aca5ded
commit a59e171bf5
13 changed files with 205 additions and 47 deletions

View File

@@ -20,6 +20,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/rbacutils"
)
@@ -62,3 +63,16 @@ func (manager *SProjectizedResourceBaseManager) ResourceScope() rbacutils.TRbacS
func (manager *SProjectizedResourceBaseManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
return FetchProjectInfo(ctx, data)
}
func (manager *SProjectizedResourceBaseManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
switch field {
case "tenant":
tenantCacheQuery := TenantCacheManager.Query("name", "id").Distinct().SubQuery()
q.AppendField(tenantCacheQuery.Field("name", "tenant"))
q = q.Join(tenantCacheQuery, sqlchemy.Equals(q.Field("tenant_id"), tenantCacheQuery.Field("id")))
q.GroupBy(tenantCacheQuery.Field("name"))
default:
return nil, httperrors.NewBadRequestError("unsupport field %s", field)
}
return q, nil
}

View File

@@ -313,3 +313,9 @@ func (self *SBaseGuestDriver) GetGuestSecgroupVpcid(guest *models.SGuest) (strin
}
return vpcId, nil
}
func (self *SBaseGuestDriver) CancelExpireTime(
ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest) error {
return httperrors.NewBadRequestError("unsupport cancel expire time")
}

View File

@@ -225,3 +225,8 @@ func (self *SESXiGuestDriver) RequestRenewInstance(guest *models.SGuest, bc bill
func (self *SESXiGuestDriver) IsSupportEip() bool {
return false
}
func (self *SESXiGuestDriver) CancelExpireTime(
ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest) error {
return guest.CancelExpireTime(ctx, userCred)
}

View File

@@ -459,3 +459,8 @@ func (self *SKVMGuestDriver) OnGuestChangeCpuMemFailed(ctx context.Context, gues
}
return nil
}
func (self *SKVMGuestDriver) CancelExpireTime(
ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest) error {
return guest.CancelExpireTime(ctx, userCred)
}

View File

@@ -157,3 +157,8 @@ func (self *SOpenStackGuestDriver) AllowReconfigGuest() bool {
func (self *SOpenStackGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
return false
}
func (self *SOpenStackGuestDriver) CancelExpireTime(
ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest) error {
return guest.CancelExpireTime(ctx, userCred)
}

View File

@@ -164,3 +164,8 @@ func (self *SZStackGuestDriver) AllowReconfigGuest() bool {
func (self *SZStackGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
return false
}
func (self *SZStackGuestDriver) CancelExpireTime(
ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest) error {
return guest.CancelExpireTime(ctx, userCred)
}

View File

@@ -54,6 +54,16 @@ func (self *SBillingResourceBase) IsValidPrePaid() bool {
return false
}
func (self *SBillingResourceBase) IsValidPostPaid() bool {
if self.BillingType == api.BILLING_TYPE_POSTPAID {
now := time.Now().UTC()
if self.ExpiredAt.After(now) {
return true
}
}
return false
}
type SBillingBaseInfo struct {
ChargeType string `json:",omitempty"`
ExpiredAt time.Time `json:",omitempty"`

View File

@@ -556,11 +556,6 @@ func (manager *SBucketManager) ListItemFilter(ctx context.Context, q *sqlchemy.S
func (manager *SBucketManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
switch field {
case "tenant":
tenantCacheQuery := db.TenantCacheManager.Query("name", "id").Distinct().SubQuery()
q.AppendField(tenantCacheQuery.Field("name", "tenant"))
q = q.Join(tenantCacheQuery, sqlchemy.Equals(q.Field("tenant_id"), tenantCacheQuery.Field("id")))
q.GroupBy(tenantCacheQuery.Field("name"))
case "account":
cloudproviders := CloudproviderManager.Query().SubQuery()
cloudaccounts := CloudaccountManager.Query("name", "id").Distinct().SubQuery()

View File

@@ -1501,18 +1501,23 @@ func (self *SGuest) PerformDetachIsolatedDevice(ctx context.Context, userCred mc
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
return nil, httperrors.NewBadRequestError(msg)
}
err = self.startDetachIsolateDevice(ctx, userCred, device)
return nil, err
}
func (self *SGuest) startDetachIsolateDevice(ctx context.Context, userCred mcclient.TokenCredential, device string) error {
iDev, err := IsolatedDeviceManager.FetchByIdOrName(userCred, device)
if err != nil {
msg := fmt.Sprintf("Isolated device %s not found", device)
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_DETACH_ISOLATED_DEVICE, msg, userCred, false)
return nil, httperrors.NewBadRequestError(msg)
return httperrors.NewBadRequestError(msg)
}
dev := iDev.(*SIsolatedDevice)
host := self.GetHost()
lockman.LockObject(ctx, host)
defer lockman.ReleaseObject(ctx, host)
err = self.detachIsolateDevice(ctx, userCred, dev)
return nil, err
return err
}
func (self *SGuest) detachIsolateDevice(ctx context.Context, userCred mcclient.TokenCredential, dev *SIsolatedDevice) error {
@@ -1551,11 +1556,16 @@ func (self *SGuest) PerformAttachIsolatedDevice(ctx context.Context, userCred mc
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_ATTACH_ISOLATED_DEVICE, msg, userCred, false)
return nil, httperrors.NewBadRequestError(msg)
}
err = self.startAttachIsolatedDevice(ctx, userCred, device)
return nil, err
}
func (self *SGuest) startAttachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, device string) error {
iDev, err := IsolatedDeviceManager.FetchByIdOrName(userCred, device)
if err != nil {
msg := fmt.Sprintf("Isolated device %s not found", device)
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_ATTACH_ISOLATED_DEVICE, msg, userCred, false)
return nil, httperrors.NewBadRequestError(msg)
return httperrors.NewBadRequestError(msg)
}
dev := iDev.(*SIsolatedDevice)
host := self.GetHost()
@@ -1567,7 +1577,78 @@ func (self *SGuest) PerformAttachIsolatedDevice(ctx context.Context, userCred mc
msg = err.Error()
}
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_GUEST_ATTACH_ISOLATED_DEVICE, msg, userCred, err == nil)
return nil, err
return err
}
func (self *SGuest) attachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, dev *SIsolatedDevice) error {
if len(dev.GuestId) > 0 {
return fmt.Errorf("Isolated device already attached to another guest: %s", dev.GuestId)
}
if dev.HostId != self.HostId {
return fmt.Errorf("Isolated device and guest are not located in the same host")
}
_, err := db.Update(dev, func() error {
dev.GuestId = self.Id
return nil
})
if err != nil {
return err
}
db.OpsLog.LogEvent(self, db.ACT_GUEST_ATTACH_ISOLATED_DEVICE, dev.GetShortDesc(ctx), userCred)
return nil
}
func (self *SGuest) AllowPerformSetIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "set-isolated-device")
}
func (self *SGuest) PerformSetIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if self.Hypervisor != api.HYPERVISOR_KVM {
return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
}
if self.Status != api.VM_READY {
return nil, httperrors.NewInvalidStatusError("Only allowed to attach isolated device when guest is ready")
}
var addDevs []string
{
addDevices, err := data.Get("add_devices")
if err == nil {
arrAddDev, ok := addDevices.(*jsonutils.JSONArray)
if ok {
addDevs = arrAddDev.GetStringArray()
} else {
return nil, httperrors.NewInputParameterError("attach devices is not string array")
}
}
}
var delDevs []string
{
delDevices, err := data.Get("del_devices")
if err == nil {
arrDelDev, ok := delDevices.(*jsonutils.JSONArray)
if ok {
delDevs = arrDelDev.GetStringArray()
} else {
return nil, httperrors.NewInputParameterError("detach devices is not string array")
}
}
}
// detach first
for i := 0; i < len(delDevs); i++ {
err := self.startDetachIsolateDevice(ctx, userCred, delDevs[i])
if err != nil {
return nil, err
}
}
for i := 0; i < len(addDevs); i++ {
err := self.startAttachIsolatedDevice(ctx, userCred, addDevs[i])
if err != nil {
return nil, err
}
}
return nil, nil
}
func (self *SGuest) AllowPerformChangeIpaddr(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
@@ -2973,6 +3054,18 @@ func (self *SGuest) PerformDelExtraOption(ctx context.Context, userCred mcclient
return nil, self.SetExtraOptions(ctx, userCred, extraOptions)
}
func (self *SGuest) AllowPerformCancelExpire(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "cancel-expire")
}
func (self *SGuest) PerformCancelExpire(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if self.BillingType != billing_api.BILLING_TYPE_POSTPAID {
return nil, httperrors.NewBadRequestError("guest billing type %s not support cancel expire", self.BillingType)
}
err := self.GetDriver().CancelExpireTime(ctx, userCred, self)
return nil, err
}
func (self *SGuest) AllowPerformRenew(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "renew")
}
@@ -3021,11 +3114,9 @@ func (self *SGuest) SaveRenewInfo(ctx context.Context, userCred mcclient.TokenCr
guestdisks := self.GetDisks()
for i := 0; i < len(guestdisks); i += 1 {
disk := guestdisks[i].GetDisk()
if disk.BillingType == billing_api.BILLING_TYPE_PREPAID {
err = disk.SaveRenewInfo(ctx, userCred, bc, expireAt)
if err != nil {
return err
}
err = disk.SaveRenewInfo(ctx, userCred, bc, expireAt)
if err != nil {
return err
}
}
return nil
@@ -3033,7 +3124,7 @@ func (self *SGuest) SaveRenewInfo(ctx context.Context, userCred mcclient.TokenCr
func (self *SGuest) doSaveRenewInfo(ctx context.Context, userCred mcclient.TokenCredential, bc *billing.SBillingCycle, expireAt *time.Time) error {
_, err := db.Update(self, func() error {
if self.BillingType != billing_api.BILLING_TYPE_PREPAID {
if len(self.BillingType) == 0 {
self.BillingType = billing_api.BILLING_TYPE_PREPAID
}
if expireAt != nil && !expireAt.IsZero() {
@@ -3052,6 +3143,23 @@ func (self *SGuest) doSaveRenewInfo(ctx context.Context, userCred mcclient.Token
return nil
}
func (self *SGuest) CancelExpireTime(ctx context.Context, userCred mcclient.TokenCredential) error {
if self.BillingType != billing_api.BILLING_TYPE_POSTPAID {
return fmt.Errorf("billing type %s not support cancel expire", self.BillingType)
}
_, err := sqlchemy.GetDB().Exec(
fmt.Sprintf(
"update %s set expired_at = NULL and billing_cycle = NULL where id = ?",
GuestManager.TableSpec().Name(),
), self.Id,
)
if err != nil {
return errors.Wrap(err, "guest cancel expire time")
}
db.OpsLog.LogEvent(self, db.ACT_RENEW, "guest cancel expire time", userCred)
return nil
}
func (self *SGuest) AllowPerformStreamDisksComplete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "stream-disks-complete")
}

View File

@@ -170,6 +170,7 @@ type IGuestDriver interface {
IsNeedInjectPasswordByCloudInit(desc *cloudprovider.SManagedVMCreateConfig) bool
GetUserDataType() string
CancelExpireTime(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error
}
var guestDrivers map[string]IGuestDriver

View File

@@ -1073,7 +1073,9 @@ func (manager *SGuestManager) validateCreateData(
return nil, httperrors.NewInputParameterError("unsupported duration %s", input.Duration)
}
input.BillingType = billing_api.BILLING_TYPE_PREPAID
if len(input.BillingType) == 0 {
input.BillingType = billing_api.BILLING_TYPE_PREPAID
}
input.BillingCycle = billingCycle.String()
// expired_at will be set later by callback
// data.Add(jsonutils.NewTimeString(billingCycle.EndAt(time.Time{})), "expired_at")
@@ -3039,24 +3041,6 @@ func (self *SGuest) createIsolatedDeviceOnHost(ctx context.Context, userCred mcc
return err
}
func (self *SGuest) attachIsolatedDevice(ctx context.Context, userCred mcclient.TokenCredential, dev *SIsolatedDevice) error {
if len(dev.GuestId) > 0 {
return fmt.Errorf("Isolated device already attached to another guest: %s", dev.GuestId)
}
if dev.HostId != self.HostId {
return fmt.Errorf("Isolated device and guest are not located in the same host")
}
_, err := db.Update(dev, func() error {
dev.GuestId = self.Id
return nil
})
if err != nil {
return err
}
db.OpsLog.LogEvent(self, db.ACT_GUEST_ATTACH_ISOLATED_DEVICE, dev.GetShortDesc(ctx), userCred)
return nil
}
func (self *SGuest) JoinGroups(ctx context.Context, userCred mcclient.TokenCredential, groupIds []string) error {
for _, id := range groupIds {
_, err := GroupguestManager.Attach(ctx, id, self.Id)
@@ -4025,6 +4009,20 @@ func (manager *SGuestManager) getExpiredPrepaidGuests() []SGuest {
return guests
}
func (manager *SGuestManager) getExpiredPostpaidGuests() []SGuest {
deadline := time.Now()
q := manager.Query().Equals("billing_type", billing_api.BILLING_TYPE_POSTPAID).
LT("expired_at", deadline).Limit(options.Options.ExpiredPrepaidMaxCleanBatchSize)
guests := make([]SGuest, 0)
err := db.FetchModelObjects(GuestManager, q, &guests)
if err != nil {
log.Errorf("fetch guests error %s", err)
return nil
}
return guests
}
func (self *SGuest) doExternalSync(ctx context.Context, userCred mcclient.TokenCredential) error {
host := self.GetHost()
if host == nil {
@@ -4059,6 +4057,24 @@ func (manager *SGuestManager) DeleteExpiredPrepaidServers(ctx context.Context, u
}
}
func (manager *SGuestManager) DeleteExpiredPostpaidServers(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
guests := manager.getExpiredPostpaidGuests()
if len(guests) == 0 {
log.Infof("No expired postpaid guest")
return
}
for i := 0; i < len(guests); i++ {
if len(guests[i].ExternalId) > 0 {
err := guests[i].doExternalSync(ctx, userCred)
if err == nil && guests[i].IsValidPostPaid() {
continue
}
}
guests[i].SetDisableDelete(userCred, false)
guests[i].StartDeleteGuestTask(ctx, userCred, "", false, false)
}
}
func (self *SGuest) GetEip() (*SElasticip, error) {
return ElasticipManager.getEipForInstance("server", self.Id)
}

View File

@@ -95,19 +95,6 @@ func (manager *SSecurityGroupManager) ListItemFilter(ctx context.Context, q *sql
return q, nil
}
func (manager *SSecurityGroupManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
switch field {
case "tenant":
tenantCacheQuery := db.TenantCacheManager.Query("name", "id").Distinct().SubQuery()
q.AppendField(tenantCacheQuery.Field("name", "tenant"))
q = q.Join(tenantCacheQuery, sqlchemy.Equals(q.Field("tenant_id"), tenantCacheQuery.Field("id")))
q.GroupBy(tenantCacheQuery.Field("name"))
default:
return nil, httperrors.NewBadRequestError("unsupport field %s", field)
}
return q, nil
}
func (manager *SSecurityGroupManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
q, err := manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query)
if err != nil {

View File

@@ -78,6 +78,7 @@ func StartService() {
if opts.PrepaidExpireCheck {
cron.AddJobAtIntervals("CleanExpiredPrepaidServers", time.Duration(opts.PrepaidExpireCheckSeconds)*time.Second, models.GuestManager.DeleteExpiredPrepaidServers)
}
cron.AddJobAtIntervals("CleanExpiredPostpaidServers", time.Duration(opts.PrepaidExpireCheckSeconds)*time.Second, models.GuestManager.DeleteExpiredPostpaidServers)
cron.AddJobAtIntervals("StartHostPingDetectionTask", time.Duration(opts.HostOfflineDetectionInterval)*time.Second, models.HostManager.PingDetectionTask)
cron.AddJobAtIntervalsWithStartRun("CalculateQuotaUsages", time.Duration(opts.CalculateQuotaUsageIntervalSeconds)*time.Second, models.QuotaManager.CalculateQuotaUsages, true)