This commit is contained in:
wanyaoqi
2019-03-27 21:34:49 +08:00
committed by Yousong Zhou
parent a5774f4fd1
commit a759f34819
11 changed files with 287 additions and 35 deletions
+10
View File
@@ -152,6 +152,16 @@ func init() {
return nil
})
R(&options.ServerCloneOptions{}, "server-clone", "Clone a server", func(s *mcclient.ClientSession, opts *options.ServerCloneOptions) error {
params := jsonutils.Marshal(opts).(*jsonutils.JSONDict)
res, err := modules.Servers.PerformAction(s, opts.SOURCE, "clone", params)
if err != nil {
return err
}
printObject(res)
return nil
})
R(&options.ServerLoginInfoOptions{}, "server-logininfo", "Get login info of a server", func(s *mcclient.ClientSession, opts *options.ServerLoginInfoOptions) error {
srvid, e := modules.Servers.GetId(s, opts.ID, nil)
if e != nil {
+13
View File
@@ -164,6 +164,19 @@ type ServerCreateInput struct {
Baremetal bool `json:"baremetal"`
}
type ServerCloneInput struct {
apis.Meta
Name string `json:"name"`
AutoStart bool `json:"auto_start"`
EipBw int `json:"eip_bw,omitzero"`
EipChargeType string `json:"eip_charge_type,omitempty"`
Eip string `json:"eip,omitempty"`
PreferHost string `json:"prefer_host_id"`
}
type ServerDeployInput struct {
apis.Meta
+4
View File
@@ -465,3 +465,7 @@ func (self *SBaremetalGuestDriver) OnDeleteGuestFinalCleanup(ctx context.Context
}
return nil
}
func (self *SBaremetalGuestDriver) IsSupportGuestClone() bool {
return false
}
+4
View File
@@ -272,3 +272,7 @@ func (self *SBaseGuestDriver) OnGuestChangeCpuMemFailed(ctx context.Context, gue
func (self *SBaseGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
return fmt.Errorf("SBaseGuestDriver: Not Implement")
}
func (self *SBaseGuestDriver) IsSupportGuestClone() bool {
return true
}
+4
View File
@@ -174,3 +174,7 @@ func (self *SContainerDriver) GetRandomNetworkTypes() []string {
func (self *SContainerDriver) StartGuestRestartTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, isForce bool, parentTaskId string) error {
return fmt.Errorf("Not Implement")
}
func (self *SContainerDriver) IsSupportGuestClone() bool {
return false
}
+66
View File
@@ -303,6 +303,72 @@ func (self *SGuest) StartGuestLiveMigrateTask(ctx context.Context, userCred mccl
return nil
}
func (self *SGuest) AllowPerformClone(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "clone")
}
func (self *SGuest) PerformClone(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if len(self.BackupHostId) > 0 {
return nil, httperrors.NewBadRequestError("Can't clone guest with backup guest")
}
if !self.GetDriver().IsSupportGuestClone() {
return nil, httperrors.NewBadRequestError("Guest hypervisor %s does not support clone", self.Hypervisor)
}
cloneInput := new(api.ServerCloneInput)
err := data.Unmarshal(cloneInput)
if err != nil {
return nil, httperrors.NewInputParameterError("Unmarshal input error %s", err)
}
if len(cloneInput.Name) == 0 {
return nil, httperrors.NewMissingParameterError("name")
}
err = db.NewNameValidator(GuestManager, userCred.GetProjectId(), cloneInput.Name)
if err != nil {
return nil, err
}
createInput := self.ToCreateInput()
createInput.Name = cloneInput.Name
createInput.AutoStart = cloneInput.AutoStart
createInput.EipBw = cloneInput.EipBw
createInput.Eip = cloneInput.Eip
createInput.EipChargeType = cloneInput.EipChargeType
if err := GuestManager.validateEip(userCred, createInput, createInput.PreferRegion); err != nil {
return nil, err
}
dataDict := jsonutils.Marshal(createInput)
model, err := db.NewModelObject(GuestManager)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
err = dataDict.Unmarshal(model)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
err = model.CustomizeCreate(ctx, userCred, self.ProjectId, query, dataDict)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
err = GuestManager.TableSpec().Insert(model)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
pendingUsage := getGuestResourceRequirements(ctx, userCred, createInput, 1, false)
if task, err := taskman.TaskManager.NewTask(ctx, "GuestCloneTask", model, userCred,
createInput.JSON(createInput), "", "", &pendingUsage); err != nil {
log.Errorf(err.Error())
return nil, err
} else {
task.ScheduleRun(nil)
}
return nil, nil
}
func (self *SGuest) AllowPerformDeploy(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "deploy")
}
+1
View File
@@ -137,6 +137,7 @@ type IGuestDriver interface {
NeedStopForChangeSpec(guest *SGuest) bool
OnGuestChangeCpuMemFailed(ctx context.Context, guest *SGuest, data *jsonutils.JSONDict, task taskman.ITask) error
IsSupportGuestClone() bool
}
var guestDrivers map[string]IGuestDriver
+147 -34
View File
@@ -1029,40 +1029,9 @@ func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred m
input.SecgroupId = "default"
}
eipStr := input.Eip
eipBw := input.EipBw
if len(eipStr) > 0 || eipBw > 0 {
if !GetDriver(hypervisor).IsSupportEip() {
return nil, httperrors.NewNotImplementedError("eip not supported for %s", hypervisor)
}
if len(eipStr) > 0 {
eipObj, err := ElasticipManager.FetchByIdOrName(userCred, eipStr)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(ElasticipManager.Keyword(), eipStr)
} else {
return nil, httperrors.NewGeneralError(err)
}
}
eip := eipObj.(*SElasticip)
if eip.Status != EIP_STATUS_READY {
return nil, httperrors.NewInvalidStatusError("eip %s status invalid %s", eipStr, eip.Status)
}
if eip.IsAssociated() {
return nil, httperrors.NewResourceBusyError("eip %s has been associated", eipStr)
}
input.Eip = eipObj.GetId()
eipRegion := eip.GetRegion()
preferRegionId, _ := data.GetString("prefer_region_id")
if len(preferRegionId) > 0 && preferRegionId != eipRegion.Id {
return nil, httperrors.NewConflictError("cannot assoicate with eip %s: different region", eipStr)
}
input.PreferRegion = eipRegion.Id
} else {
// create new eip
}
preferRegionId, _ := data.GetString("prefer_region_id")
if err := manager.validateEip(userCred, input, preferRegionId); err != nil {
return nil, err
}
/*
@@ -1097,6 +1066,45 @@ func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred m
return input.JSON(input), nil
}
func (manager *SGuestManager) validateEip(userCred mcclient.TokenCredential, input *api.ServerCreateInput, preferRegionId string) error {
eipStr := input.Eip
eipBw := input.EipBw
if len(eipStr) > 0 || eipBw > 0 {
if !GetDriver(input.Hypervisor).IsSupportEip() {
return httperrors.NewNotImplementedError("eip not supported for %s", input.Hypervisor)
}
if len(eipStr) > 0 {
eipObj, err := ElasticipManager.FetchByIdOrName(userCred, eipStr)
if err != nil {
if err == sql.ErrNoRows {
return httperrors.NewResourceNotFoundError2(ElasticipManager.Keyword(), eipStr)
} else {
return httperrors.NewGeneralError(err)
}
}
eip := eipObj.(*SElasticip)
if eip.Status != EIP_STATUS_READY {
return httperrors.NewInvalidStatusError("eip %s status invalid %s", eipStr, eip.Status)
}
if eip.IsAssociated() {
return httperrors.NewResourceBusyError("eip %s has been associated", eipStr)
}
input.Eip = eipObj.GetId()
eipRegion := eip.GetRegion()
// preferRegionId, _ := data.GetString("prefer_region_id")
if len(preferRegionId) > 0 && preferRegionId != eipRegion.Id {
return httperrors.NewConflictError("cannot assoicate with eip %s: different region", eipStr)
}
input.PreferRegion = eipRegion.Id
} else {
// create new eip
}
}
return nil
}
func (manager *SGuestManager) checkCreateQuota(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, input *api.ServerCreateInput, hasBackup bool) error {
req := getGuestResourceRequirements(ctx, userCred, input, 1, hasBackup)
err := QuotaManager.CheckSetPendingQuota(ctx, userCred, ownerProjId, &req)
@@ -4019,3 +4027,108 @@ func (guest *SGuest) GetDetailsTasks(ctx context.Context, userCred mcclient.Toke
func (guest *SGuest) GetDynamicConditionInput() *jsonutils.JSONDict {
return guest.ToSchedDesc().ToConditionInput()
}
func (self *SGuest) ToCreateInput() *api.ServerCreateInput {
r := new(api.ServerCreateInput)
r.VmemSize = self.VmemSize
r.VcpuCount = int(self.VcpuCount)
r.KeypairId = self.KeypairId
if guestCdrom := self.getCdrom(); guestCdrom != nil {
r.Cdrom = guestCdrom.ImageId
}
r.Vga = self.Vga
r.Vdi = self.Vdi
r.Bios = self.Bios
r.Description = self.Description
r.BootOrder = self.BootOrder
r.DisableDelete = new(bool)
*r.DisableDelete = self.DisableDelete.Bool()
r.ShutdownBehavior = self.ShutdownBehavior
// r.DeployConfigs
r.IsSystem = self.IsSystem
// r.Duration
// r.AutoPrepaidRecycle
r.SecgroupId = self.SecgrpId
r.ServerConfigs = new(api.ServerConfigs)
host := self.GetHost()
r.Hypervisor = self.Hypervisor
r.ResourceType = host.ResourceType
r.InstanceType = self.InstanceType
r.Project = self.ProjectId
r.Count = 1
r.Disks = self.ToDisksConfig()
r.Networks = self.ToNetworksConfig()
r.IsolatedDevices = self.ToIsolatedDevicesConfig()
zone := self.getZone()
r.PreferRegion = zone.GetRegion().GetId()
r.PreferZone = zone.GetId()
return r
}
func (self *SGuest) ToDisksConfig() []*api.DiskConfig {
guestDisks := self.GetDisks()
if len(guestDisks) == 0 {
return nil
}
ret := make([]*api.DiskConfig, len(guestDisks))
for idx, guestDisk := range guestDisks {
diskConf := new(api.DiskConfig)
disk := guestDisk.GetDisk()
diskConf.Index = int(guestDisk.Index)
diskConf.ImageId = disk.GetTemplateId()
diskConf.SnapshotId = disk.SnapshotId
diskConf.DiskType = disk.DiskType
diskConf.SizeMb = disk.DiskSize
diskConf.Fs = disk.FsFormat
diskConf.Format = disk.DiskFormat
diskConf.Driver = guestDisk.Driver
diskConf.Cache = guestDisk.CacheMode
diskConf.Mountpoint = guestDisk.Mountpoint
storage := disk.GetStorage()
diskConf.Backend = storage.StorageType
diskConf.Medium = storage.MediumType
ret[idx] = diskConf
}
return ret
}
func (self *SGuest) ToNetworksConfig() []*api.NetworkConfig {
guestNetworks, _ := self.GetNetworks("")
if len(guestNetworks) == 0 {
return nil
}
ret := make([]*api.NetworkConfig, len(guestNetworks))
for idx, guestNetwork := range guestNetworks {
netConf := new(api.NetworkConfig)
network := guestNetwork.GetNetwork()
netConf.Index = int(guestNetwork.Index)
// XXX: same wire
netConf.Wire = network.WireId
netConf.Exit = guestNetwork.IsExit()
// netConf.Private
// netConf.Reserved
netConf.Driver = guestNetwork.Driver
netConf.BwLimit = guestNetwork.BwLimit
// netConf.NetType
ret[idx] = netConf
}
return ret
}
func (self *SGuest) ToIsolatedDevicesConfig() []*api.IsolatedDeviceConfig {
guestIsolatedDevices := self.GetIsolatedDevices()
if len(guestIsolatedDevices) == 0 {
return nil
}
ret := make([]*api.IsolatedDeviceConfig, len(guestIsolatedDevices))
for idx, guestIsolatedDevice := range guestIsolatedDevices {
devConf := new(api.IsolatedDeviceConfig)
devConf.Model = guestIsolatedDevice.Model
ret[idx] = devConf
}
return ret
}
+27
View File
@@ -0,0 +1,27 @@
package tasks
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
)
type GuestCloneTask struct {
GuestBatchCreateTask
}
func init() {
taskman.RegisterTask(GuestCloneTask{})
}
func (self *GuestCloneTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
StartScheduleObjects(ctx, self, []db.IStandaloneModel{obj})
}
func (self *GuestCloneTask) OnScheduleComplete(ctx context.Context, guest *models.SGuest, data *jsonutils.JSONDict) {
self.SetStageComplete(ctx, nil)
}
+1 -1
View File
@@ -43,7 +43,7 @@ type IScheduleTask interface {
OnStartSchedule(obj IScheduleModel)
OnScheduleFailCallback(ctx context.Context, obj IScheduleModel, reason string)
OnScheduleComplete(ctx context.Context, items []db.IStandaloneModel, data *jsonutils.JSONDict)
// OnScheduleComplete(ctx context.Context, items []db.IStandaloneModel, data *jsonutils.JSONDict)
SaveScheduleResult(ctx context.Context, obj IScheduleModel, candidate *schedapi.CandidateResource)
SaveScheduleResultWithBackup(ctx context.Context, obj IScheduleModel, master, slave *schedapi.CandidateResource)
OnScheduleFailed(ctx context.Context, reason string)
+10
View File
@@ -194,6 +194,16 @@ func (o ServerConfigs) Data() (*computeapi.ServerConfigs, error) {
return data, nil
}
type ServerCloneOptions struct {
SOURCE string `help:"Source server id or name" json:"-"`
TARGET_NAME string `help:"Name of newly server" json:"name"`
AutoStart bool `help:"Auto start server after it is created"`
EipBw int `help:"allocate EIP with bandwidth in MB when server is created" json:"eip_bw,omitzero"`
EipChargeType string `help:"newly allocated EIP charge type, either traffic or bandwidth" choices:"traffic|bandwidth" json:"eip_charge_type,omitempty"`
Eip string `help:"associate with an existing EIP when server is created" json:"eip,omitempty"`
}
type ServerCreateOptions struct {
ServerConfigs