From 6d21c7e0d2501a385438c01522ad98a6709b19b7 Mon Sep 17 00:00:00 2001 From: wanyaoqi Date: Tue, 25 Sep 2018 11:36:12 +0800 Subject: [PATCH] baremetal ops some bugfix make dep --- Gopkg.lock | 4 +- cmd/climc/shell/regions.go | 1 + pkg/cloudcommon/db/db_dispatcher.go | 14 +- pkg/cloudcommon/db/interface.go | 3 + pkg/cloudcommon/db/jointbase.go | 38 +- pkg/cloudcommon/db/modelbase.go | 9 + pkg/cloudcommon/db/resourcebase.go | 9 + pkg/cloudcommon/db/taskman/tasks.go | 2 + pkg/compute/baremetal/diskconfig.go | 104 +- pkg/compute/baremetal/diskconfig_test.go | 7 + pkg/compute/baremetal/types.go | 1 + pkg/compute/guestdrivers/baremetals.go | 270 +++- pkg/compute/guestdrivers/base.go | 6 +- pkg/compute/guestdrivers/virtualization.go | 3 +- pkg/compute/handlers.go | 2 +- pkg/compute/hostdrivers/base.go | 140 ++ pkg/compute/hostdrivers/kvm.go | 113 ++ pkg/compute/models/baremetalagents.go | 156 ++ pkg/compute/models/baremetalstatus.go | 22 - pkg/compute/models/guests.go | 36 +- pkg/compute/models/hostdrivers.go | 7 + pkg/compute/models/hosts.go | 1307 ++++++++++++++++- pkg/compute/models/hoststorages.go | 6 +- pkg/compute/models/isolated_devices.go | 20 + pkg/compute/models/netinterfaces.go | 58 +- pkg/compute/models/networks.go | 33 + pkg/compute/models/storages.go | 35 + pkg/compute/models/wires.go | 45 +- pkg/compute/options/options.go | 4 + pkg/compute/tasks/baremetal_base_task.go | 33 + .../baremetal_convert_hypervisor_task.go | 71 +- pkg/compute/tasks/baremetal_delete_task.go | 59 + .../tasks/baremetal_maintenance_task.go | 73 + pkg/compute/tasks/baremetal_prepare_task.go | 48 + .../tasks/baremetal_server_reset_task.go | 35 + .../tasks/baremetal_server_start_task.go | 61 + .../tasks/baremetal_server_stop_task.go | 78 + .../baremetal_server_sync_status_task.go | 79 + .../tasks/baremetal_sync_status_task.go | 68 + .../baremetal_unconvert_hypervisor_task.go | 62 + .../tasks/baremetal_unmaintenance_task.go | 61 + pkg/httperrors/errors.go | 5 + pkg/mcclient/auth/auth.go | 5 + pkg/mcclient/modules/mod_endpoints.go | 2 +- pkg/mcclient/modules/mod_networks.go | 2 +- pkg/mcclient/token.go | 2 + pkg/mcclient/token2.go | 6 + pkg/mcclient/token3.go | 28 + pkg/mcclient/tokensimple.go | 4 + vendor/yunion.io/x/sqlchemy/conditions.go | 18 +- 50 files changed, 3019 insertions(+), 236 deletions(-) create mode 100644 pkg/compute/models/baremetalagents.go delete mode 100644 pkg/compute/models/baremetalstatus.go create mode 100644 pkg/compute/tasks/baremetal_base_task.go create mode 100644 pkg/compute/tasks/baremetal_delete_task.go create mode 100644 pkg/compute/tasks/baremetal_maintenance_task.go create mode 100644 pkg/compute/tasks/baremetal_prepare_task.go create mode 100644 pkg/compute/tasks/baremetal_server_reset_task.go create mode 100644 pkg/compute/tasks/baremetal_server_start_task.go create mode 100644 pkg/compute/tasks/baremetal_server_stop_task.go create mode 100644 pkg/compute/tasks/baremetal_server_sync_status_task.go create mode 100644 pkg/compute/tasks/baremetal_sync_status_task.go create mode 100644 pkg/compute/tasks/baremetal_unconvert_hypervisor_task.go create mode 100644 pkg/compute/tasks/baremetal_unmaintenance_task.go diff --git a/Gopkg.lock b/Gopkg.lock index 323cedba93..3dd1993416 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1292,11 +1292,11 @@ [[projects]] branch = "master" - digest = "1:a22a70d0156f2b836993f4d07b38c4b1c2058d61ced4e55bb7fb77b297424a04" + digest = "1:d8daebdff1c94f0acac6952014812ac02fc10e1894004dd2efd8377a2517ba06" name = "yunion.io/x/sqlchemy" packages = ["."] pruneopts = "UT" - revision = "3342ff524267b69938600968f3f193fc794b309c" + revision = "95a4726da196a27611ca7df4e08f16ece628f708" [[projects]] branch = "master" diff --git a/cmd/climc/shell/regions.go b/cmd/climc/shell/regions.go index 7c52616030..aa5768cca0 100644 --- a/cmd/climc/shell/regions.go +++ b/cmd/climc/shell/regions.go @@ -2,6 +2,7 @@ package shell import ( "fmt" + "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules" diff --git a/pkg/cloudcommon/db/db_dispatcher.go b/pkg/cloudcommon/db/db_dispatcher.go index f9ba0b73b2..e19295e1ba 100644 --- a/pkg/cloudcommon/db/db_dispatcher.go +++ b/pkg/cloudcommon/db/db_dispatcher.go @@ -443,6 +443,14 @@ func listItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok if err != nil { return nil, httperrors.NewGeneralError(err) } + retConut := len(retList) + retList, err = manager.CustomizeFilterList(ctx, userCred, queryDict, retList) + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + if len(retList) != retConut { + totalCnt = int64(len(retList)) + } retResult := modules.ListResult{Data: retList, Total: int(totalCnt), Limit: int(limit), Offset: int(offset)} return &retResult, nil } @@ -864,7 +872,7 @@ func (dispatcher *DBModelDispatcher) PerformClassAction(ctx context.Context, act func (dispatcher *DBModelDispatcher) PerformAction(ctx context.Context, idStr string, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { userCred := fetchUserCredential(ctx) - model, err := fetchItem(dispatcher.modelManager, ctx, userCred, idStr, query) + model, err := fetchItem(dispatcher.modelManager, ctx, userCred, idStr, nil) if err == sql.ErrNoRows { return nil, httperrors.NewResourceNotFoundError(fmt.Sprintf("%s %s not found", dispatcher.modelManager.Keyword(), idStr)) @@ -1022,7 +1030,7 @@ func updateItem(manager IModelManager, item IModel, ctx context.Context, userCre func (dispatcher *DBModelDispatcher) Update(ctx context.Context, idStr string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { userCred := fetchUserCredential(ctx) - model, err := fetchItem(dispatcher.modelManager, ctx, userCred, idStr, query) + model, err := fetchItem(dispatcher.modelManager, ctx, userCred, idStr, nil) if err == sql.ErrNoRows { return nil, httperrors.NewResourceNotFoundError(fmt.Sprintf("%s %s not found", dispatcher.modelManager.Keyword(), idStr)) @@ -1100,7 +1108,7 @@ func deleteItem(manager IModelManager, model IModel, ctx context.Context, userCr func (dispatcher *DBModelDispatcher) Delete(ctx context.Context, idstr string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { userCred := fetchUserCredential(ctx) - model, err := fetchItem(dispatcher.modelManager, ctx, userCred, idstr, query) + model, err := fetchItem(dispatcher.modelManager, ctx, userCred, idstr, nil) if err == sql.ErrNoRows { return nil, httperrors.NewResourceNotFoundError(fmt.Sprintf("%s %s not found", dispatcher.modelManager.Keyword(), idstr)) diff --git a/pkg/cloudcommon/db/interface.go b/pkg/cloudcommon/db/interface.go index 202bfc4a26..9866b6dcdc 100644 --- a/pkg/cloudcommon/db/interface.go +++ b/pkg/cloudcommon/db/interface.go @@ -29,6 +29,7 @@ type IModelManager interface { AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool ValidateListConditions(ctx context.Context, userCred mcclient.TokenCredential, query *jsonutils.JSONDict) (*jsonutils.JSONDict, error) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) + CustomizeFilterList(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, retList []jsonutils.JSONObject) ([]jsonutils.JSONObject, error) ExtraSearchConditions(ctx context.Context, q *sqlchemy.SQuery, like string) []sqlchemy.ICondition // fetch hook @@ -55,6 +56,8 @@ type IModelManager interface { AllowPerformAction(ctx context.Context, userCred mcclient.TokenCredential, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) bool PerformAction(ctx context.Context, userCred mcclient.TokenCredential, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) + DoCreate(ctx context.Context, userCred mcclient.TokenCredential, kwargs jsonutils.JSONObject, data jsonutils.JSONObject, realManager IModelManager) (IModel, error) + InitializeData() error } diff --git a/pkg/cloudcommon/db/jointbase.go b/pkg/cloudcommon/db/jointbase.go index 3679eea408..06dfefce28 100644 --- a/pkg/cloudcommon/db/jointbase.go +++ b/pkg/cloudcommon/db/jointbase.go @@ -108,16 +108,20 @@ func (manager *SJointResourceBaseManager) AllowAttach(ctx context.Context, userC func JointModelExtra(jointModel IJointModel, extra *jsonutils.JSONDict) *jsonutils.JSONDict { master := jointModel.Master() - extra.Add(jsonutils.NewString(master.GetName()), master.GetModelManager().Keyword()) - alias := master.GetModelManager().Alias() - if len(alias) > 0 { - extra.Add(jsonutils.NewString(master.GetName()), alias) + if master != nil { + extra.Add(jsonutils.NewString(master.GetName()), master.GetModelManager().Keyword()) + alias := master.GetModelManager().Alias() + if len(alias) > 0 { + extra.Add(jsonutils.NewString(master.GetName()), alias) + } } slave := jointModel.Slave() - extra.Add(jsonutils.NewString(slave.GetName()), slave.GetModelManager().Keyword()) - alias = slave.GetModelManager().Alias() - if len(alias) > 0 { - extra.Add(jsonutils.NewString(slave.GetName()), alias) + if slave != nil { + extra.Add(jsonutils.NewString(slave.GetName()), slave.GetModelManager().Keyword()) + alias := slave.GetModelManager().Alias() + if len(alias) > 0 { + extra.Add(jsonutils.NewString(slave.GetName()), alias) + } } return extra } @@ -180,13 +184,23 @@ func (joint *SJointResourceBase) Slave() IStandaloneModel { } func (self *SJointResourceBase) AllowGetJointDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, item IJointModel) bool { - masterVirtual := item.Master().(IVirtualModel) - return masterVirtual.IsOwner(userCred) + master := item.Master() + switch master.(type) { + case IVirtualModel: + return master.(IVirtualModel).IsOwner(userCred) + default: // case item implemented customized AllowGetDetails, eg hostjoints + return item.AllowGetDetails(ctx, userCred, query) + } } func (self *SJointResourceBase) AllowUpdateJointItem(ctx context.Context, userCred mcclient.TokenCredential, item IJointModel) bool { - masterVirtual := item.Master().(IVirtualModel) - return masterVirtual.IsOwner(userCred) + master := item.Master() + switch master.(type) { + case IVirtualModel: + return master.(IVirtualModel).IsOwner(userCred) + default: // case item implemented customized AllowGetDetails, eg hostjoints + return item.AllowUpdateItem(ctx, userCred) + } } /* diff --git a/pkg/cloudcommon/db/modelbase.go b/pkg/cloudcommon/db/modelbase.go index b06106bccb..0cfdec9dbc 100644 --- a/pkg/cloudcommon/db/modelbase.go +++ b/pkg/cloudcommon/db/modelbase.go @@ -3,6 +3,7 @@ package db import ( "context" "database/sql" + "fmt" "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" @@ -74,6 +75,10 @@ func (manager *SModelBaseManager) ListItemFilter(ctx context.Context, q *sqlchem return q, nil } +func (manager *SModelBaseManager) CustomizeFilterList(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, retList []jsonutils.JSONObject) ([]jsonutils.JSONObject, error) { + return retList, nil +} + func (manager *SModelBaseManager) ExtraSearchConditions(ctx context.Context, q *sqlchemy.SQuery, like string) []sqlchemy.ICondition { return nil } @@ -149,6 +154,10 @@ func (manager *SModelBaseManager) InitializeData() error { return nil } +func (manager *SModelBaseManager) DoCreate(ctx context.Context, userCred mcclient.TokenCredential, kwargs jsonutils.JSONObject, data jsonutils.JSONObject, realManager IModelManager) (IModel, error) { + return nil, fmt.Errorf("Do create not implement?") +} + func (model *SModelBase) GetId() string { return "" } diff --git a/pkg/cloudcommon/db/resourcebase.go b/pkg/cloudcommon/db/resourcebase.go index 7221c0e67b..7bf0f32cb7 100644 --- a/pkg/cloudcommon/db/resourcebase.go +++ b/pkg/cloudcommon/db/resourcebase.go @@ -4,6 +4,7 @@ import ( "context" "time" + "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/pkg/util/timeutils" "yunion.io/x/sqlchemy" @@ -35,6 +36,14 @@ func (manager *SResourceBaseManager) RawQuery(fields ...string) *sqlchemy.SQuery return manager.SModelBaseManager.Query(fields...) } +func (manager *SResourceBaseManager) DoCreate(ctx context.Context, userCred mcclient.TokenCredential, kwargs jsonutils.JSONObject, data jsonutils.JSONObject, realManager IModelManager) (IModel, error) { + ownerProjId, err := fetchOwnerProjectId(ctx, userCred, kwargs) + if err != nil { + return nil, err + } + return doCreateItem(realManager, ctx, userCred, ownerProjId, nil, data) +} + func CanDelete(model IModel, ctx context.Context) bool { err := model.ValidateDeleteCondition(ctx) if err == nil { diff --git a/pkg/cloudcommon/db/taskman/tasks.go b/pkg/cloudcommon/db/taskman/tasks.go index c95aa63c67..ee9bdbd99a 100644 --- a/pkg/cloudcommon/db/taskman/tasks.go +++ b/pkg/cloudcommon/db/taskman/tasks.go @@ -37,6 +37,8 @@ const ( MULTI_OBJECTS_ID = "[--MULTI_OBJECTS--]" TASK_INIT_STAGE = "on_init" + + CONVERT_TASK = "convert_task" ) type STaskManager struct { diff --git a/pkg/compute/baremetal/diskconfig.go b/pkg/compute/baremetal/diskconfig.go index a7a28e5ce0..c0803bbb70 100644 --- a/pkg/compute/baremetal/diskconfig.go +++ b/pkg/compute/baremetal/diskconfig.go @@ -183,12 +183,6 @@ func isDiskConfigStorageMatch( if (typeIsHybrid || typeIsRotate || typeIsSSD) && (rangeIsNoneAndCountZero || rangeIsNotNoneAndIndexInRange || rangeIsNoneAndSmallThanCount) && adapterIsEqual { - if confDriver == nil { - confDriver = &driver - } - if confAdapter == nil { - confAdapter = &adapter - } return true } return false @@ -215,6 +209,12 @@ func RetrieveStorages(diskConfig *BaremetalDiskConfig, storages []*BaremetalStor } if isDiskConfigStorageMatch(diskConfig, confDriver, confAdapter, storage, selected) { + if confDriver == nil { + confDriver = &storage.Driver + } + if confAdapter == nil { + confAdapter = &storage.Adapter + } selected = append(selected, storage) } else { rest = append(rest, storage) @@ -262,7 +262,9 @@ func (l Layout) String() string { func RetrieveStorageDrivers(storages []*BaremetalStorage) sets.String { ret := sets.NewString() for _, s := range storages { - ret = ret.Union(sets.NewString(s.Driver)) + if !ret.Has(s.Driver) { + ret.Insert(s.Driver) + } } return ret } @@ -405,58 +407,19 @@ func ExpandNoneConf(layouts []Layout) (ret []Layout) { return ret } -func CalculateLayout( - confs []*BaremetalDiskConfig, - storages []*BaremetalStorage, -) (layouts []Layout, err error) { - - if len(confs) == 0 { - err = fmt.Errorf("[]*BaremetalDiskConfig must be provided.") - return - } - - layouts = make([]Layout, 0) - var ( - plainDisks = make([]*BaremetalStorage, 0) - pcieDisks = make([]*BaremetalStorage, 0) - raidDisks = make([]*BaremetalStorage, 0) - ) - - for _, storage := range storages { - if storage.Driver != "" && storage.Driver == DISK_DRIVER_LINUX { - plainDisks = append(plainDisks, storage) - } else if storage.Driver != "" && storage.Driver == DISK_DRIVER_PCIE { - pcieDisks = append(pcieDisks, storage) +func CalculateLayout(confs []*BaremetalDiskConfig, storages []*BaremetalStorage) (layouts []Layout, err error) { + var confIdx = 0 + for len(storages) > 0 { + var conf *BaremetalDiskConfig + if confIdx < len(confs) { + conf = confs[confIdx] + confIdx += 1 } else { - raidDisks = append(raidDisks, storage) + noneConf, _ := ParseDiskConfig("none") + conf = &noneConf } - } - - if len(plainDisks) > 0 { - layouts = append(layouts, Layout{ - Disks: plainDisks, - Conf: &BaremetalDiskConfig{Conf: DISK_CONF_NONE}, - Size: 0, - }) - } - - storages = raidDisks - //totalStorageRequest := 0 - //for _, conf := range confs { - //totalStorageRequest += conf.Count - //} - //if totalStorageRequest > len(storages) { - //err = fmt.Errorf("requested number of disks is more than storages") - //return layouts, err - //} - - for _, conf := range confs { - if len(storages) == 0 { - continue - } - selected, storages1 := RetrieveStorages(conf, storages) - //selected, _ := RetrieveStorages(conf, storages) - storages = storages1 + selected, storage1 := RetrieveStorages(conf, storages) + storages = storage1 if len(selected) == 0 { err = fmt.Errorf("Not found matched storages by config: %#v", conf) return @@ -467,7 +430,6 @@ func CalculateLayout( return } sz := CalculateSize(conf.Conf, selected) - if len(conf.Splits) == 0 { layouts = append(layouts, Layout{ Disks: selected, @@ -475,35 +437,18 @@ func CalculateLayout( Size: sz, }) } else { + splitSizes := GetSplitSizes(sz, conf.Splits) + conf.Size = splitSizes layouts = append(layouts, Layout{ Disks: selected, Conf: conf, Size: sz, }) - /* splitSizes := GetSplitSizes(sz, conf.Splits) - if len(splitSizes) <= 0 { - break - } - - for _, ssz := range splitSizes { - subConfig := conf - layouts = append(layouts, Layout{ - Disks: selected, - Conf: subConfig, - Size: ssz, - }) - }*/ } } - - if len(pcieDisks) > 0 { - layouts = append(layouts, Layout{ - Disks: pcieDisks, - Conf: &BaremetalDiskConfig{Conf: DISK_CONF_NONE}, - Size: 0, - }) + if confIdx < len(confs) { + err = fmt.Errorf("Not enough disks to meet configuration") } - layouts = ExpandNoneConf(layouts) return } @@ -525,6 +470,7 @@ func expandLayoutSplits(layouts []Layout) []Layout { } func CheckDisksAllocable(layouts []Layout, disks []*Disk) bool { + layouts = ExpandNoneConf(layouts) layouts = expandLayoutSplits(layouts) storeIndex := 0 storeFreeSize := int64(-1) diff --git a/pkg/compute/baremetal/diskconfig_test.go b/pkg/compute/baremetal/diskconfig_test.go index b5bd96d0ea..8cc7530084 100644 --- a/pkg/compute/baremetal/diskconfig_test.go +++ b/pkg/compute/baremetal/diskconfig_test.go @@ -373,6 +373,11 @@ func TestCalculateLayout(t *testing.T) { 13 ], "splits": "100g,32g,", + "size": [ + 102400, + 32768, + 818176 + ], "adapter": 0, "cachedbadbbu": false, "strip": 0, @@ -457,6 +462,7 @@ func TestCalculateLayout(t *testing.T) { "count": 6, "range": null, "splits": "", + "size": null, "adapter": 2, "cachedbadbbu": false, "strip": 0, @@ -541,6 +547,7 @@ func TestCalculateLayout(t *testing.T) { "count": 6, "range": null, "splits": "", + "size": null, "adapter": 2, "cachedbadbbu": false, "strip": 0, diff --git a/pkg/compute/baremetal/types.go b/pkg/compute/baremetal/types.go index dcf6bf5b4f..ec66dfa534 100644 --- a/pkg/compute/baremetal/types.go +++ b/pkg/compute/baremetal/types.go @@ -93,6 +93,7 @@ type BaremetalDiskConfig struct { Count int64 `json:"count"` Range []int64 `json:"range"` Splits string `json:"splits"` + Size []int64 `json:"size"` Adapter *int `json:"adapter"` Driver string `json:"driver"` Cachedbadbbu bool `json:"cachedbadbbu"` diff --git a/pkg/compute/guestdrivers/baremetals.go b/pkg/compute/guestdrivers/baremetals.go index d12ebc0716..40af2119ab 100644 --- a/pkg/compute/guestdrivers/baremetals.go +++ b/pkg/compute/guestdrivers/baremetals.go @@ -3,14 +3,22 @@ package guestdrivers import ( "context" "fmt" + "net/http" + "regexp" "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/utils" + "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/baremetal" "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/compute/options" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/logclient" ) type SBaremetalGuestDriver struct { @@ -35,33 +43,93 @@ func (self *SBaremetalGuestDriver) GetMaxVMemSizeGB() int { } func (self *SBaremetalGuestDriver) PrepareDiskRaidConfig(host *models.SHost, params *jsonutils.JSONDict) error { - /*confs := params.GetArray() - baremetal.CalculateLayout() - from clouds.baremetal import diskconfig - confs = task.params.get('baremetal_disk_config', None) - if confs is None: - confs = [diskconfig.parse_diskconfig('')] - layouts = diskconfig.calculate_layout(confs, baremetal.storage_info) - baremetal.update_disk_config(layouts)*/ - - // TODO - - return nil + baremetalStorage := models.ConvertStorageInfo2BaremetalStorages(host.StorageInfo) + if baremetalStorage == nil { + return fmt.Errorf("Convert storage info error") + } + confs, err := params.GetArray("baremetal_disk_config") + if err != nil { + parsedConf, _ := baremetal.ParseDiskConfig("") + nConfs := []*baremetal.BaremetalDiskConfig{&parsedConf} + layouts, err := baremetal.CalculateLayout(nConfs, baremetalStorage) + if err != nil { + return err + } + return host.UpdateDiskConfig(layouts) + } else { + var nConfs = make([]*baremetal.BaremetalDiskConfig, 0) + for i := 0; i < len(confs); i++ { + parsedConf := &baremetal.BaremetalDiskConfig{} + err := confs[i].Unmarshal(parsedConf) + if err != nil { + log.Errorln(err) + return err + } + nConfs = append(nConfs, parsedConf) + } + layouts, err := baremetal.CalculateLayout(nConfs, baremetalStorage) + if err != nil { + return err + } + return host.UpdateDiskConfig(layouts) + } } func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, userCred mcclient.TokenCredential, host *models.SHost, netConfig *models.SNetworkConfig) (*models.SNetwork, string, int8, models.IPAddlocationDirection) { - net, _ := host.GetNetworkWithIdAndCredential(netConfig.Network, userCred, netConfig.Reserved) - return net, netConfig.Mac, -1, models.IPAllocationStepdown + netif, net := host.GetNetinterfaceWithNetworkAndCredential(netConfig.Network, userCred, netConfig.Reserved) + if netif != nil { + return net, netif.Mac, netif.Index, models.IPAllocationStepup + } + return net, "", -1, "" } func (self *SBaremetalGuestDriver) Attach2RandomNetwork(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, netConfig *models.SNetworkConfig, pendingUsage quotas.IQuota) error { - // TODO + netifs := host.GetNetInterfaces() + netsAvaiable := make([]models.SNetwork, 0) + netifIndexs := make(map[string]*models.SNetInterface, 0) - return nil + var wirePattern *regexp.Regexp + if len(netConfig.Wire) > 0 { + wirePattern = regexp.MustCompile(netConfig.Wire) + } + for _, netif := range netifs { + if !netif.IsUsableServernic() { + continue + } + wire := netif.GetWire() + if wire == nil { + continue + } + if wirePattern != nil && !wirePattern.MatchString(wire.Id) && !wirePattern.MatchString(wire.Name) { + continue + } + var net *models.SNetwork + if netConfig.Private { + net, _ = wire.GetCandidatePrivateNetwork(userCred, netConfig.Exit, models.SERVER_TYPE_BAREMETAL) + } else { + net, _ = wire.GetCandidatePublicNetwork(netConfig.Exit, models.SERVER_TYPE_BAREMETAL) + } + if net != nil { + netsAvaiable = append(netsAvaiable, *net) + netifIndexs[net.Id] = &netif + } + } + if len(netsAvaiable) == 0 { + return fmt.Errorf("No appropriate host virtual network...") + } + net := models.ChooseCandidateNetworks(netsAvaiable, netConfig.Exit, models.SERVER_TYPE_BAREMETAL) + if net != nil { + netif := netifIndexs[net.Id] + return guest.Attach2Network(ctx, userCred, net, pendingUsage, "", netif.Mac, netConfig.Driver, netConfig.BwLimit, netConfig.Vip, netif.Index, false, models.IPAllocationStepup, false) + } + return fmt.Errorf("No appropriate host virtual network...") } func (self *SBaremetalGuestDriver) ChooseHostStorage(host *models.SHost, backend string) *models.SStorage { bs := host.GetBaremetalstorage() + if bs == nil { + return nil + } return bs.GetStorage() } @@ -76,47 +144,116 @@ func (self *SBaremetalGuestDriver) RequestGuestCreateInsertIso(ctx context.Conte } func (self *SBaremetalGuestDriver) RequestStartOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) (jsonutils.JSONObject, error) { - data := jsonutils.NewDict() - // TODO - return data, nil + desc := guest.GetJsonDescAtBaremetal(ctx, host) + config := jsonutils.NewDict() + config.Set("desc", desc) + headers := http.Header{} + headers.Set("X-Auth-Token", task.GetUserCred().GetTokenString()) + headers.Set("X-Task-Id", task.GetTaskId()) + url := fmt.Sprintf("/baremetals/%s/servers/%s/start", host.Id, guest.Id) + return host.BaremetalSyncRequest(ctx, "POST", url, headers, config) } func (self *SBaremetalGuestDriver) RequestStopGuestForDelete(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { - // TODO + guestStatus, _ := task.GetParams().GetString("guest_status") + if guestStatus == models.VM_RUNNING && options.Options.EnablePendingDelete && !guest.PendingDeleted && jsonutils.QueryBoolean(task.GetParams(), "purge", false) { + host := guest.GetHost() + if host != nil && host.Enabled { + return guest.StartGuestStopTask(ctx, task.GetUserCred(), true, task.GetTaskId()) + } + } + task.ScheduleRun(nil) return nil } func (self *SBaremetalGuestDriver) RequestStopOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { - // TODO - return nil + body := jsonutils.NewDict() + timeout, err := task.GetParams().Int("timeout") + if err != nil { + timeout = 30 + } + if jsonutils.QueryBoolean(task.GetParams(), "is_force", false) || jsonutils.QueryBoolean(task.GetParams(), "reset", false) { + timeout = 0 + } + body.Set("timeout", jsonutils.NewInt(timeout)) + headers := http.Header{} + headers.Set("X-Auth-Token", task.GetUserCred().GetTokenString()) + headers.Set("X-Task-Id", task.GetTaskId()) + url := fmt.Sprintf("/baremetals/%s/servers/%s/stop", host.Id, guest.Id) + _, err = host.BaremetalSyncRequest(ctx, "POST", url, headers, body) + return err } func (self *SBaremetalGuestDriver) StartGuestStopTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error { - // TODO + task, err := taskman.TaskManager.NewTask(ctx, "GuestStopTask", guest, userCred, params, parentTaskId, "", nil) + if err != nil { + return err + } + task.ScheduleRun(nil) return nil } func (self *SBaremetalGuestDriver) RequestUndeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { - // TODO - return nil + url := fmt.Sprintf("/baremetals/%s/servers/%s", host.Id, guest.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", task.GetUserCred().GetTokenString()) + headers.Set("X-Task-Id", task.GetTaskId()) + _, err := host.BaremetalSyncRequest(ctx, "DELETE", url, headers, nil) + return err } func (self *SBaremetalGuestDriver) RequestSyncstatusOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential) (jsonutils.JSONObject, error) { - data := jsonutils.NewDict() - // TODO - return data, nil + return nil, fmt.Errorf("baremetal doesn't support RequestSyncstatusOnHost") } func (self *SBaremetalGuestDriver) StartGuestSyncstatusTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { - // TODO + task, err := taskman.TaskManager.NewTask(ctx, "BaremetalServerSyncStatusTask", guest, userCred, nil, parentTaskId, "", nil) + if err != nil { + return err + } + task.ScheduleRun(nil) return nil } func (self *SBaremetalGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + var confs = make([]baremetal.BaremetalDiskConfig, 0) + for data.Contains(fmt.Sprintf("baremetal_disk_config.%d", len(confs))) { + desc, _ := data.GetString(fmt.Sprintf("baremetal_disk_config.%d", len(confs))) + data.Remove(fmt.Sprintf("baremetal_disk_config.%d", len(confs))) + bmConf, err := baremetal.ParseDiskConfig(desc) + if err != nil { + return nil, httperrors.NewInputParameterError("baremetal_disk_config.%d", len(confs)) + } + confs = append(confs, bmConf) + } + if len(confs) == 0 { + bmConf, _ := baremetal.ParseDiskConfig("") + confs = append(confs, bmConf) + } + data.Set("baremetal_disk_config", jsonutils.Marshal(confs)) + if !data.Contains("disk.0") { + return nil, httperrors.NewInputParameterError("Root disk must be present") + } + disk0, _ := data.Get("disk.0") + if !disk0.Contains("image_id") { + return nil, httperrors.NewInputParameterError("Root disk must have templete") + } return data, nil } func (self *SBaremetalGuestDriver) ValidateCreateHostData(ctx context.Context, userCred mcclient.TokenCredential, bmName string, host *models.SHost, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + if host.HostType != models.HOST_TYPE_BAREMETAL || !host.IsBaremetal { + return nil, httperrors.NewInputParameterError("Host %s is not a baremetal", bmName) + } + if !utils.IsInStringArray(host.Status, []string{models.BAREMETAL_READY, models.BAREMETAL_RUNNING, models.BAREMETAL_START_CONVERT}) { + return nil, httperrors.NewInvalidStatusError("Baremetal %s is not ready", bmName) + } + if host.GetBaremetalServer() != nil { + return nil, httperrors.NewInsufficientResourceError("Baremetal %s is occupied", bmName) + } + data.Set("prefer_baremetal_id", jsonutils.NewString(host.Id)) + data.Set("vmem_size", jsonutils.NewInt(int64(host.MemSize))) + data.Set("vcpu_count", jsonutils.NewInt(int64(host.CpuCount))) return data, nil } @@ -142,19 +279,61 @@ func (self *SBaremetalGuestDriver) CheckDiskTemplateOnStorage(ctx context.Contex } func (self *SBaremetalGuestDriver) OnGuestDeployTaskComplete(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { - // TODO - return nil + task.SetStage("OnDeployGuestSyncstatusComplete", nil) + return guest.StartSyncstatus(ctx, task.GetUserCred(), task.GetTaskId()) } func (self *SBaremetalGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error { - // TODO + if data.Contains("disks") { + disks, _ := data.GetArray("disks") + for i := 0; i < len(disks); i++ { + diskId, _ := disks[i].GetString("disk_id") + iDisk, _ := models.DiskManager.FetchById(diskId) + if iDisk == nil { + return fmt.Errorf("OnGuestDeployTaskDataReceived fetch disk error") + } + disk := iDisk.(*models.SDisk) + diskSize, _ := disks[i].Int("size") + notes := fmt.Sprintf("%s=>%s", disk.Status, models.DISK_READY) + _, err := disk.GetModelManager().TableSpec().Update(disk, func() error { + if disk.DiskSize < int(diskSize) { + disk.DiskSize = int(diskSize) + } + disk.DiskFormat = "raw" + disk.Status = models.DISK_READY + return nil + }) + if err != nil { + return err + } + db.OpsLog.LogEvent(disk, db.ACT_UPDATE_STATUS, notes, task.GetUserCred()) + logclient.AddActionLog(disk, logclient.ACT_VM_SYNC_STATUS, nil, task.GetUserCred(), false) + db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(), task.GetUserCred()) + if disks[i].Contains("dev") { + dev, _ := disks[i].GetString("dev") + disk.SetMetadata(ctx, "dev", dev, task.GetUserCred()) + } + } + } + guest.SaveDeployInfo(ctx, task.GetUserCred(), data) return nil } func (self *SBaremetalGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { - // TODO - - return nil + config := guest.GetDeployConfigOnHost(ctx, host, task.GetParams()) + val, _ := config.GetString("action") + if len(val) == 0 { + val = "deploy" + } + if val == "rebuild" && jsonutils.QueryBoolean(task.GetParams(), "auto_start", false) { + config.Set("on_finish", jsonutils.NewString("restart")) + } + url := fmt.Sprintf("/baremetals/%s/servers/%s/%s", host.Id, guest.Id, val) + headers := http.Header{} + headers.Set("X-Auth-Token", task.GetUserCred().GetTokenString()) + headers.Set("X-Task-Id", task.GetTaskId()) + _, err := host.BaremetalSyncRequest(ctx, "POST", url, headers, config) + return err } func (self *SBaremetalGuestDriver) CanKeepDetachDisk() bool { @@ -177,3 +356,24 @@ func (self *SBaremetalGuestDriver) StartSuspendTask(ctx context.Context, userCre func (self *SBaremetalGuestDriver) StartGuestSaveImage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, params *jsonutils.JSONDict, parentTaskId string) error { return httperrors.NewUnsupportOperationError("Cannot save image for baremtal") } + +func (self *SBaremetalGuestDriver) StartGuestResetTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, isHard bool, parentTaskId string) error { + task, err := taskman.TaskManager.NewTask(ctx, "BaremetalServerResetTask", guest, userCred, nil, "", parentTaskId, nil) + if err != nil { + return err + } + task.ScheduleRun(nil) + return nil +} + +func (self *SBaremetalGuestDriver) OnDeleteGuestFinalCleanup(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential) error { + err := guest.DeleteAllDisksInDB(ctx, userCred) + if err != nil { + return err + } + baremetal := guest.GetHost() + if baremetal != nil { + return baremetal.UpdateDiskConfig(nil) + } + return nil +} diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index 7de34d2f89..ef383ece36 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -6,12 +6,11 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" - "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" - "yunion.io/x/onecloud/pkg/compute/tasks" + "yunion.io/x/onecloud/pkg/mcclient" ) type SBaseGuestDriver struct { @@ -20,9 +19,10 @@ type SBaseGuestDriver struct { func (self *SBaseGuestDriver) StartGuestCreateTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, pendingUsage quotas.IQuota, parentTaskId string) error { taskName, _ := data.GetString("__task__") if len(taskName) > 0 { + data.Remove("__task__") log.Infof("Start embedded guest start task") switch taskName { - case tasks.CONVERT_TASK: + case taskman.CONVERT_TASK: hostId, _ := data.GetString("prefer_host_id") if len(hostId) == 0 { hostId, _ = data.GetString("prefer_baremetal_id") diff --git a/pkg/compute/guestdrivers/virtualization.go b/pkg/compute/guestdrivers/virtualization.go index 3e7bd39b61..7dcddd8d08 100644 --- a/pkg/compute/guestdrivers/virtualization.go +++ b/pkg/compute/guestdrivers/virtualization.go @@ -55,7 +55,8 @@ func (self *SVirtualizedGuestDriver) Attach2RandomNetwork(guest *models.SGuest, log.Debugf("Wire %#v", wire) - if wirePattern != nil && !wirePattern.MatchString(wire.Id) && wirePattern.MatchString(wire.Name) { + // !! + if wirePattern != nil && !wirePattern.MatchString(wire.Id) && !wirePattern.MatchString(wire.Name) { continue } var net *models.SNetwork diff --git a/pkg/compute/handlers.go b/pkg/compute/handlers.go index 11c30a109e..22550bf00e 100644 --- a/pkg/compute/handlers.go +++ b/pkg/compute/handlers.go @@ -62,7 +62,7 @@ func InitHandlers(app *appsrv.Application) { models.DnsRecordManager, models.ElasticipManager, models.SnapshotManager, - + models.BaremetalagentManager, models.LoadbalancerManager, models.LoadbalancerListenerManager, models.LoadbalancerListenerRuleManager, diff --git a/pkg/compute/hostdrivers/base.go b/pkg/compute/hostdrivers/base.go index 08f3dd0125..7d1c51069e 100644 --- a/pkg/compute/hostdrivers/base.go +++ b/pkg/compute/hostdrivers/base.go @@ -5,8 +5,12 @@ import ( "fmt" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/baremetal" "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/pkg/utils" ) type SBaseHostDriver struct { @@ -23,3 +27,139 @@ func (self *SBaseHostDriver) RequestResetDisk(ctx context.Context, host *models. func (self *SBaseHostDriver) RequestCleanUpDiskSnapshots(ctx context.Context, host *models.SHost, disk *models.SDisk, params *jsonutils.JSONDict, task taskman.ITask) error { return fmt.Errorf("Not Implement") } + +func (self *SBaseHostDriver) PrepareConvert(host *models.SHost, image, raid string, data jsonutils.JSONObject) (*jsonutils.JSONDict, error) { + params := jsonutils.NewDict() + params.Set("description", jsonutils.NewString("Baremetal convered Hypervisor")) + name, err := data.Get("name") + if err == nil { + params.Set("name", name) + } else { + params.Set("name", jsonutils.NewString(host.Name)) + } + params.Set("auto_start", jsonutils.NewBool(true)) + params.Set("is_system", jsonutils.NewBool(true)) + params.Set("prefer_baremetal", jsonutils.NewString(host.Id)) + params.Set("baremetal", jsonutils.NewBool(true)) + return params, nil +} + +func (self *SBaseHostDriver) PrepareUnconvert(host *models.SHost) error { + return nil +} + +func (self *SBaseHostDriver) FinishUnconvert(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost) error { + bss := host.GetBaremetalstorage() + if bss != nil { + bs := bss.GetStorage() + if bs != nil { + bs.SetStatus(userCred, models.STORAGE_ONLINE, "") + } else { + log.Errorf("ERROR: baremetal storage is None???") + } + } else { + log.Errorf("ERROR: baremetal has no valid baremetalstorage????") + } + adminNetif := host.GetAdminNetInterface() + if adminNetif == nil { + return fmt.Errorf("admin netif is nil") + } + adminNic := adminNetif.GetBaremetalNetwork() + if adminNic == nil { + return fmt.Errorf("admin nic is nil") + } + host.GetModelManager().TableSpec().Update(host, func() error { + host.AccessIp = adminNic.IpAddr + host.Enabled = true + host.HostType = models.HOST_TYPE_BAREMETAL + host.HostStatus = models.HOST_OFFLINE + host.ManagerUri = "" + host.Version = "" + host.MemReserved = 0 + return nil + }) + log.Infof("Do finish_unconvert!!!!!!!!") + self.CleanSchedCache(host) + return nil +} + +func (self *SBaseHostDriver) CleanSchedCache(host *models.SHost) error { + return host.ClearSchedDescCache() +} +func (self *SBaseHostDriver) FinishConvert(userCred mcclient.TokenCredential, host *models.SHost, guest *models.SGuest, hostType string) error { + _, err := guest.GetModelManager().TableSpec().Update(guest, func() error { + guest.VmemSize = 0 + guest.VcpuCount = 0 + return nil + }) + if err != nil { + return err + } + for _, guestdisk := range guest.GetDisks() { + disk := guestdisk.GetDisk() + disk.GetModelManager().TableSpec().Update(disk, func() error { + disk.DiskSize = 0 + return nil + }) + } + bs := host.GetBaremetalstorage().GetStorage() + bs.SetStatus(userCred, models.STORAGE_OFFLINE, "") + host.GetModelManager().TableSpec().Update(host, func() error { + host.CpuReserved = 0 + host.MemReserved = 0 + host.AccessIp = guest.GetRealIps()[0] + host.Enabled = false + host.HostStatus = models.HOST_OFFLINE + host.HostType = hostType + host.IsBaremetal = true + return nil + }) + self.CleanSchedCache(host) + return nil +} + +func (self *SBaseHostDriver) ConvertFailed(host *models.SHost) error { + return self.CleanSchedCache(host) +} + +func (self *SBaseHostDriver) GetRaidScheme(host *models.SHost, raid string) (string, error) { + var candidates []string + if len(raid) == 0 { + candidates = []string{baremetal.DISK_CONF_RAID10, baremetal.DISK_CONF_RAID1, baremetal.DISK_CONF_RAID5, baremetal.DISK_CONF_RAID0, baremetal.DISK_CONF_NONE} + } else { + if utils.IsInStringArray(raid, []string{baremetal.DISK_CONF_RAID10, baremetal.DISK_CONF_RAID1}) { + candidates = []string{baremetal.DISK_CONF_RAID10, baremetal.DISK_CONF_RAID1} + } else { + candidates = []string{raid} + } + } + var conf []*baremetal.BaremetalDiskConfig + for i := 0; i < len(candidates); i++ { + if candidates[i] == baremetal.DISK_CONF_NONE { + conf = []*baremetal.BaremetalDiskConfig{} + } else { + parsedConf, err := baremetal.ParseDiskConfig(candidates[i]) + if err != nil { + log.Errorf("try raid %s failed: %s", candidates[i], err.Error()) + return "", err + } + conf = []*baremetal.BaremetalDiskConfig{&parsedConf} + } + baremetalStorage := models.ConvertStorageInfo2BaremetalStorages(host.StorageInfo) + if baremetalStorage == nil { + return "", fmt.Errorf("Convert storage info error") + } + layout, err := baremetal.CalculateLayout(conf, baremetalStorage) + if err != nil { + log.Errorf("try raid %s failed: %s", candidates[i], err.Error()) + continue + } + log.Infof("convert layout %v", layout) + raid = candidates[i] + break + } + if len(raid) == 0 { + return "", fmt.Errorf("Disk misconfiguration") + } + return raid, nil +} diff --git a/pkg/compute/hostdrivers/kvm.go b/pkg/compute/hostdrivers/kvm.go index 5a02c49e9a..57e9fad69a 100644 --- a/pkg/compute/hostdrivers/kvm.go +++ b/pkg/compute/hostdrivers/kvm.go @@ -4,11 +4,15 @@ import ( "context" "fmt" "net/http" + "net/url" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/baremetal" "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/compute/options" + "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/httputils" ) @@ -177,3 +181,112 @@ func (self *SKVMHostDriver) RequestCleanUpDiskSnapshots(ctx context.Context, hos _, err := host.Request(task.GetUserCred(), "POST", url, header, params) return err } + +func (self *SKVMHostDriver) PrepareConvert(host *models.SHost, image, raid string, data jsonutils.JSONObject) (*jsonutils.JSONDict, error) { + params, err := self.SBaseHostDriver.PrepareConvert(host, image, raid, data) + if err != nil { + return nil, err + } + var sysSize = "60g" + log.Infof("%v", data) + if data.Contains("baremetal_disk_config.0") { + for i := 0; data.Contains(fmt.Sprintf("baremetal_disk_config.%d", i)); i++ { + v, _ := data.Get(fmt.Sprintf("baremetal_disk_config.%d", i)) + params.Set(fmt.Sprintf("baremetal_disk_config.%d", i), v) + } + } else { + raid, err = self.GetRaidScheme(host, raid) + if err != nil { + return nil, err + } + if raid != baremetal.DISK_CONF_NONE { + params.Set("baremetal_disk_config.0", jsonutils.NewString(fmt.Sprintf("%s:(%s,)", raid, sysSize))) + } + } + if data.Contains("disk.0") { + for i := 0; data.Contains(fmt.Sprintf("disk.%d", i)); i++ { + v, _ := data.Get(fmt.Sprintf("disk.%d", i)) + params.Set(fmt.Sprintf("disk.%d", i), v) + } + } else { + if len(image) == 0 { + image = options.Options.ConvertHypervisorDefaultTemplate + } + if len(image) == 0 { + return nil, fmt.Errorf("Not default image specified for converting %s", self.GetHostType()) + } + if raid != baremetal.DISK_CONF_NONE { + params.Set("disk.0", jsonutils.NewString(fmt.Sprintf("%s:autoextend", image))) + } else if host.StorageInfo.(*jsonutils.JSONArray).Length() > 1 { + params.Set("disk.0", jsonutils.NewString(fmt.Sprintf("%s:autoextend", image))) + } else { + params.Set("disk.0", jsonutils.NewString(fmt.Sprintf("%s:%s", image, sysSize))) + } + params.Set("disk.1", jsonutils.NewString("ext4:autoextend:/opt/cloud/workspace")) + } + if data.Contains("net.0") { + for i := 0; data.Contains(fmt.Sprintf("net.%d", i)); i++ { + v, _ := data.Get(fmt.Sprintf("net.%d", i)) + params.Set(fmt.Sprintf("net.%d", i), v) + } + } else { + wire := host.GetMasterWire() + if wire == nil { + return nil, fmt.Errorf("No master wire?") + } + params.Set("net.0", jsonutils.NewString(fmt.Sprintf("wire=%s:[private]", wire.GetName()))) + } + params.Set("deploy.0.path", jsonutils.NewString("/etc/sysconfig/yunionauth")) + params.Set("deploy.0.action", jsonutils.NewString("create")) + log.Infof("%v", params) + authLoc, err := url.Parse(options.Options.AuthURL) + if err != nil { + return nil, err + } + authInfo := fmt.Sprintf("YUNION_REGION=%s\n", options.Options.Region) + authInfo += fmt.Sprintf("YUNION_KEYSTONE=%s\n", options.Options.AuthURL) + authInfo += fmt.Sprintf("YUNION_KEYSTONE_HOST=%s\n", authLoc.Hostname()) + authInfo += fmt.Sprintf("YUNION_HOST_NAME=%s\n", host.GetName()) + authInfo += fmt.Sprintf("YUNION_HOST_ADMIN=%s\n", options.Options.AdminUser) + authInfo += fmt.Sprintf("YUNION_HOST_PASSWORD=%s\n", options.Options.AdminPassword) + authInfo += fmt.Sprintf("YUNION_HOST_PROJECT=%s\n", options.Options.AdminProject) + authInfo += fmt.Sprintf("YUNION_START=yes\n") + params.Set("deploy.0.content", jsonutils.NewString(authInfo)) + return params, nil +} + +func (self *SKVMHostDriver) PrepareUnconvert(host *models.SHost) error { + hoststorages := host.GetHoststorages() + if hoststorages == nil { + return self.SBaseHostDriver.PrepareUnconvert(host) + } + for i := 0; i < len(hoststorages); i++ { + storage := hoststorages[i].GetStorage() + if storage.IsLocal() && storage.StorageType != models.STORAGE_BAREMETAL && storage.GetDiskCount() > 0 { + return fmt.Errorf("Local host storage is not empty??? %s", storage.GetName()) + } + } + return self.SBaseHostDriver.PrepareUnconvert(host) +} + +func (self *SKVMHostDriver) FinishUnconvert(ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost) error { + for _, hs := range host.GetHoststorages() { + storage := hs.GetStorage() + if storage == nil { + continue + } + if storage.StorageType != models.STORAGE_BAREMETAL { + hs.Delete(ctx, userCred) + if storage.IsLocal() { + storage.Delete(ctx, userCred) + } + } + } + kwargs := make(map[string]interface{}, 0) + for _, k := range []string{"kernel_version", "nest", "os_distribution", "os_version", + "ovs_version", "qemu_version", "storage_type"} { + kwargs[k] = "None" + } + host.SetAllMetadata(ctx, kwargs, userCred) + return self.SBaseHostDriver.FinishUnconvert(ctx, userCred, host) +} diff --git a/pkg/compute/models/baremetalagents.go b/pkg/compute/models/baremetalagents.go new file mode 100644 index 0000000000..780a5645ec --- /dev/null +++ b/pkg/compute/models/baremetalagents.go @@ -0,0 +1,156 @@ +package models + +import ( + "context" + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/pkg/util/regutils" +) + +const ( + BAREMETAL_AGENT_ENABLED = "enabled" + BAREMETAL_AGENT_DISABLED = "disabled" + BAREMETAL_AGENT_OFFLINE = "offline" +) + +type SBaremetalagentManager struct { + db.SStandaloneResourceBaseManager + SInfrastructureManager +} + +type SBaremetalagent struct { + db.SStandaloneResourceBase + SInfrastructure + + Status string `width:"36" charset:"ascii" nullable:"false" default:"disable" list:"user" create:"optional"` + AccessIp string `width:"16" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` + ManagerUri string `width:"256" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_required"` + ZoneId string `width:"128" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` +} + +var BaremetalagentManager *SBaremetalagentManager + +func init() { + BaremetalagentManager = &SBaremetalagentManager{SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(SBaremetalagent{}, "baremetalagents_tbl", "baremetalagent", "baremetalagents")} +} + +func (self *SBaremetalagent) ValidateDeleteCondition(ctx context.Context) error { + if self.Status == BAREMETAL_AGENT_ENABLED { + return fmt.Errorf("Cannot delete in status %s", self.Status) + } + return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx) +} + +func (self *SBaremetalagent) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + mangerUri, err := data.GetString("manager_uri") + if err == nil { + count := BaremetalagentManager.Query().Equals("manager_uri", mangerUri). + NotEquals("id", self.Id).Count() + if count > 0 { + return nil, httperrors.NewConflictError("Conflict manager_uri %s", mangerUri) + } + } + accessIp, err := data.GetString("access_ip") + if err == nil { + count := BaremetalagentManager.Query().Equals("access_ip", accessIp). + NotEquals("id", self.Id).Count() + if count > 0 { + return nil, httperrors.NewConflictError("Conflict access_ip %s", accessIp) + } + } + return self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) +} + +func (manager *SBaremetalagentManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + mangerUri, _ := data.GetString("manager_uri") + count := manager.TableSpec().Query().Equals("manager_uri", mangerUri).Count() + if count > 0 { + return nil, httperrors.NewDuplicateResourceError("Duplicate manager_uri %s", mangerUri) + } + accessIp, _ := data.GetString("access_ip") + count = manager.TableSpec().Query().Equals("access_ip", accessIp).Count() + if count > 0 { + return nil, httperrors.NewDuplicateResourceError("Duplicate access_ip %s", accessIp) + } + return manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data) +} + +func (self *SBaremetalagent) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SBaremetalagent) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.Status != BAREMETAL_AGENT_ENABLED { + self.GetModelManager().TableSpec().Update(self, func() error { + self.Status = BAREMETAL_AGENT_ENABLED + return nil + }) + db.OpsLog.LogEvent(self, db.ACT_ENABLE, "", userCred) + } + return nil, nil +} + +func (self *SBaremetalagent) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SBaremetalagent) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.Status != BAREMETAL_AGENT_DISABLED { + self.GetModelManager().TableSpec().Update(self, func() error { + self.Status = BAREMETAL_AGENT_DISABLED + return nil + }) + db.OpsLog.LogEvent(self, db.ACT_DISABLE, "", userCred) + } + return nil, nil +} + +func (self *SBaremetalagent) AllowPerformOnline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SBaremetalagent) PerformOnline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.Status != BAREMETAL_AGENT_OFFLINE { + self.GetModelManager().TableSpec().Update(self, func() error { + self.Status = BAREMETAL_AGENT_ENABLED + return nil + }) + db.OpsLog.LogEvent(self, db.ACT_ONLINE, "", userCred) + } + return nil, nil +} + +func (self *SBaremetalagent) AllowPerformOffline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SBaremetalagent) PerformOffline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.Status != BAREMETAL_AGENT_ENABLED { + self.GetModelManager().TableSpec().Update(self, func() error { + self.Status = BAREMETAL_AGENT_OFFLINE + return nil + }) + db.OpsLog.LogEvent(self, db.ACT_OFFLINE, "", userCred) + } + return nil, nil +} + +func (self *SBaremetalagent) GetZone() *SZone { + if len(self.ZoneId) > 0 && regutils.MatchUUIDExact(self.ZoneId) { + return ZoneManager.FetchZoneById(self.ZoneId) + } + return nil +} + +func (self *SBaremetalagent) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { + extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query) + zone := self.GetZone() + if zone != nil { + extra.Set("zone", jsonutils.NewString(zone.GetName())) + } + return extra +} diff --git a/pkg/compute/models/baremetalstatus.go b/pkg/compute/models/baremetalstatus.go deleted file mode 100644 index 5c4561f14c..0000000000 --- a/pkg/compute/models/baremetalstatus.go +++ /dev/null @@ -1,22 +0,0 @@ -package models - -const ( - BAREMETAL_INIT = "init" - BAREMETAL_PREPARE = "prepare" - BAREMETAL_PREPARE_FAIL = "prepare_fail" - BAREMETAL_READY = "ready" - BAREMETAL_RUNNING = "running" - BAREMETAL_MAINTAINING = "maintaining" - BAREMETAL_START_MAINTAIN = "start_maintain" - BAREMETAL_DELETING = "deleting" - BAREMETAL_DELETE = "delete" - BAREMETAL_DELETE_FAIL = "delete_fail" - BAREMETAL_UNKNOWN = "unknown" - BAREMETAL_SYNCING_STATUS = "syncing_status" - BAREMETAL_SYNC = "sync" - BAREMETAL_SYNC_FAIL = "sync_fail" - BAREMETAL_START_CONVERT = "start_convert" - BAREMETAL_CONVERTING = "converting" - BAREMETAL_START_FAIL = "start_fail" - BAREMETAL_STOP_FAIL = "stop_fail" -) diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index a6d1ea497b..5905a431b0 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -578,19 +578,6 @@ func (self *SGuest) ValidateUpdateData(ctx context.Context, userCred mcclient.To } func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - vmemSize, vcpuCount, err := validateMemCpuData(data) - if err != nil { - return nil, err - } - if vmemSize == 0 { - return nil, httperrors.NewInputParameterError("Missing memory size") - } - if vcpuCount == 0 { - vcpuCount = 1 - } - 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 { @@ -676,7 +663,7 @@ func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred m hypervisor = HYPERVISOR_DEFAULT } - data, err = GetDriver(hypervisor).ValidateCreateHostData(ctx, userCred, bmName, baremetal, data) + _, err = GetDriver(hypervisor).ValidateCreateHostData(ctx, userCred, bmName, baremetal, data) if err != nil { return nil, err } @@ -843,12 +830,29 @@ func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred m }*/ data, err = GetDriver(hypervisor).ValidateCreateData(ctx, userCred, data) + if err != nil { + return nil, err + } data, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data) if err != nil { return nil, err } + vmemSize, vcpuCount, err := validateMemCpuData(data) + if err != nil { + return nil, err + } + + if vmemSize == 0 { + return nil, httperrors.NewInputParameterError("Missing memory size") + } + if vcpuCount == 0 { + vcpuCount = 1 + } + data.Add(jsonutils.NewInt(int64(vmemSize)), "vmem_size") + data.Add(jsonutils.NewInt(int64(vcpuCount)), "vcpu_count") + if !jsonutils.QueryBoolean(data, "is_system", false) { err = manager.checkCreateQuota(ctx, userCred, ownerProjId, data) if err != nil { @@ -4326,6 +4330,10 @@ func (self *SGuest) GetEip() (*SElasticip, error) { return ElasticipManager.getEipForInstance("server", self.Id) } +func (self *SGuest) GetRealIps() []string { + return self.getRealIPs() +} + func (self *SGuest) SyncVMEip(ctx context.Context, userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, projectId string) compare.SyncResult { result := compare.SyncResult{} diff --git a/pkg/compute/models/hostdrivers.go b/pkg/compute/models/hostdrivers.go index 294c5f03ea..294968b298 100644 --- a/pkg/compute/models/hostdrivers.go +++ b/pkg/compute/models/hostdrivers.go @@ -7,6 +7,7 @@ import ( "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/mcclient" ) type IHostDriver interface { @@ -21,6 +22,12 @@ type IHostDriver interface { RequestDeleteSnapshotsWithStorage(ctx context.Context, host *SHost, snapshot *SSnapshot, task taskman.ITask) error RequestResetDisk(ctx context.Context, host *SHost, disk *SDisk, params *jsonutils.JSONDict, task taskman.ITask) error RequestCleanUpDiskSnapshots(ctx context.Context, host *SHost, disk *SDisk, params *jsonutils.JSONDict, task taskman.ITask) error + PrepareConvert(host *SHost, image, raid string, data jsonutils.JSONObject) (*jsonutils.JSONDict, error) + PrepareUnconvert(host *SHost) error + FinishUnconvert(ctx context.Context, userCred mcclient.TokenCredential, host *SHost) error + FinishConvert(userCred mcclient.TokenCredential, host *SHost, guest *SGuest, hostType string) error + ConvertFailed(host *SHost) error + GetRaidScheme(host *SHost, raid string) (string, error) } var hostDrivers map[string]IHostDriver diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index f355be26d6..7b72dd82f3 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -7,11 +7,14 @@ import ( "net/url" "sort" "strconv" + "strings" + "time" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/util/compare" + "yunion.io/x/pkg/util/fileutils" "yunion.io/x/pkg/util/netutils" "yunion.io/x/pkg/util/regutils" "yunion.io/x/pkg/util/sysutils" @@ -19,6 +22,8 @@ import ( "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/baremetal" "yunion.io/x/onecloud/pkg/compute/options" @@ -26,6 +31,8 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/auth" "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/util/httputils" + "yunion.io/x/onecloud/pkg/util/logclient" ) const ( @@ -50,22 +57,28 @@ const ( NIC_TYPE_ADMIN = "admin" // #NIC_TYPE_NORMAL = 'normal' - HOST_STATUS_INIT = "init" - HOST_STATUS_PREPARE = "prepare" - HOST_STATUS_PREPARE_FAIL = "prepare_fail" - HOST_STATUS_READY = "ready" - HOST_STATUS_RUNNING = "running" - HOST_STATUS_MAINTAINING = "maintaining" - HOST_STATUS_START_MAINTAIN = "start_maintain" - HOST_STATUS_DELETING = "deleting" - HOST_STATUS_DELETE = "delete" - HOST_STATUS_DELETE_FAIL = "delete_fail" - HOST_STATUS_UNKNOWN = "unknown" - HOST_STATUS_SYNCING_STATUS = "syncing_status" - HOST_STATUS_SYNC = "sync" - HOST_STATUS_SYNC_FAIL = "sync_fail" - HOST_STATUS_START_CONVERT = "start_convert" - HOST_STATUS_CONVERTING = "converting" + BAREMETAL_INIT = "init" + BAREMETAL_PREPARE = "prepare" + BAREMETAL_PREPARE_FAIL = "prepare_fail" + BAREMETAL_READY = "ready" + BAREMETAL_RUNNING = "running" + BAREMETAL_MAINTAINING = "maintaining" + BAREMETAL_START_MAINTAIN = "start_maintain" + BAREMETAL_DELETING = "deleting" + BAREMETAL_DELETE = "delete" + BAREMETAL_DELETE_FAIL = "delete_fail" + BAREMETAL_UNKNOWN = "unknown" + BAREMETAL_SYNCING_STATUS = "syncing_status" + BAREMETAL_SYNC = "sync" + BAREMETAL_SYNC_FAIL = "sync_fail" + BAREMETAL_START_CONVERT = "start_convert" + BAREMETAL_CONVERTING = "converting" + BAREMETAL_START_FAIL = "start_fail" + BAREMETAL_STOP_FAIL = "stop_fail" + + HOST_STATUS_RUNNING = BAREMETAL_RUNNING + HOST_STATUS_READY = BAREMETAL_READY + HOST_STATUS_UNKNOWN = BAREMETAL_UNKNOWN ) var HOST_TYPES = []string{HOST_TYPE_BAREMETAL, HOST_TYPE_HYPERVISOR, HOST_TYPE_ESXI, HOST_TYPE_KUBELET, HOST_TYPE_XEN, HOST_TYPE_ALIYUN, HOST_TYPE_AZURE} @@ -99,7 +112,7 @@ type SHost struct { Slots string `width:"16" charset:"ascii" nullable:"true" get:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True) AccessMac string `width:"32" charset:"ascii" nullable:"false" index:"true" list:"admin" update:"admin" create:"admin_required"` // Column(VARCHAR(32, charset='ascii'), nullable=False, index=True) - AccessIp string `width:"16" charset:"ascii" nillable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True) + AccessIp string `width:"16" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True) ManagerUri string `width:"256" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // Column(VARCHAR(256, charset='ascii'), nullable=True) SysInfo jsonutils.JSONObject `nullable:"true" search:"admin" get:"admin" update:"admin" create:"admin_optional"` // Column(JSONEncodedDict, nullable=True) @@ -136,6 +149,24 @@ type SHost struct { IsBaremetal bool `nullable:"true" default:"false" list:"admin" update:"true" create:"admin_optional"` // Column(Boolean, nullable=True, default=False) IsMaintenance bool `nullable:"true" default:"false" list:"admin"` // Column(Boolean, nullable=True, default=False) + + LastPingAt time.Time `` +} + +func (manager *SHostManager) GetContextManager() []db.IModelManager { + return []db.IModelManager{ZoneManager} +} + +func (manager *SHostManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + zoneId, err := data.GetString("zone_id") + if err != nil { + return false + } + _, err = ZoneManager.FetchById(zoneId) + if err != nil { + return false + } + return userCred.IsSystemAdmin() } func (manager *SHostManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { @@ -327,14 +358,18 @@ func (self *SHost) Delete(ctx context.Context, userCred mcclient.TokenCredential func (self *SHost) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error { if self.IsBaremetal { - return self.StartDeleteBaremetalTask(userCred) + return self.StartDeleteBaremetalTask(ctx, userCred, "") } else { return self.RealDelete(ctx, userCred) } } -func (self *SHost) StartDeleteBaremetalTask(userCred mcclient.TokenCredential) error { - log.Debugf("StartDeleteBaremetalTask") +func (self *SHost) StartDeleteBaremetalTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { + task, err := taskman.TaskManager.NewTask(ctx, "BaremetalDeleteTask", self, userCred, nil, parentTaskId, "", nil) + if err != nil { + return err + } + task.ScheduleRun(nil) return nil } @@ -342,8 +377,9 @@ func (self *SHost) RealDelete(ctx context.Context, userCred mcclient.TokenCreden for _, hostschedtag := range self.GetHostschedtags() { hostschedtag.Delete(ctx, userCred) } - // XXX: TODO - // IsolatedDevices.delete_devices_by_host(self) + + IsolatedDeviceManager.DeleteDevicesByHost(ctx, userCred, self) + for _, hoststorage := range self.GetHoststorages() { storage := hoststorage.GetStorage() if storage != nil && storage.IsLocal() && storage.GetDiskCount() > 0 { @@ -357,9 +393,6 @@ func (self *SHost) RealDelete(ctx context.Context, userCred mcclient.TokenCreden } hoststorage.Delete(ctx, userCred) } - // XXX: TODO - // for hc in self.get_hostcachedimages(): - // hc.delete() for _, bn := range self.GetBaremetalnetworks() { self.DeleteBaremetalnetwork(ctx, userCred, &bn, false) } @@ -368,7 +401,15 @@ func (self *SHost) RealDelete(ctx context.Context, userCred mcclient.TokenCreden } for _, hostwire := range self.GetHostwires() { hostwire.Detach(ctx, userCred) - // hostwire.Delete(ctx, userCred) + // hostwire.Delete(ctx, userCred) ??? + } + baremetalStorage := self.GetBaremetalstorage() + if baremetalStorage != nil { + store := baremetalStorage.GetStorage() + baremetalStorage.Delete(ctx, userCred) + if store != nil { + store.Delete(ctx, userCred) + } } return self.SEnabledStatusStandaloneResourceBase.Delete(ctx, userCred) } @@ -533,7 +574,7 @@ func (self *SHost) ClearSchedDescCache() error { func (self *SHost) GetSpec(statusCheck bool) *jsonutils.JSONDict { if statusCheck { if utils.IsInStringArray(self.Status, []string{BAREMETAL_INIT, BAREMETAL_PREPARE_FAIL, BAREMETAL_PREPARE}) || - self.getBaremetalServer() != nil { + self.GetBaremetalServer() != nil { return nil } if self.MemSize == 0 || self.CpuCount == 0 { @@ -611,20 +652,25 @@ func parseDiskDriverSpec(driver string, spec jsonutils.JSONObject) []string { return ret } -func GetDiskSpecV2(storageInfo jsonutils.JSONObject) jsonutils.JSONObject { +func ConvertStorageInfo2BaremetalStorages(storageInfo jsonutils.JSONObject) []*baremetal.BaremetalStorage { storages := []baremetal.BaremetalStorage{} err := storageInfo.Unmarshal(&storages) if err != nil { log.Errorf("Unmarshal to baremetal storage error: %v", err) return nil } - refStorages := func() []*baremetal.BaremetalStorage { - ret := make([]*baremetal.BaremetalStorage, len(storages)) - for i, s := range storages { - ret[i] = &s - } - return ret - }() + ret := make([]*baremetal.BaremetalStorage, len(storages)) + for i, s := range storages { + ret[i] = &s + } + return ret +} + +func GetDiskSpecV2(storageInfo jsonutils.JSONObject) jsonutils.JSONObject { + refStorages := ConvertStorageInfo2BaremetalStorages(storageInfo) + if refStorages == nil { + return nil + } diskSpec := baremetal.GetDiskSpecV2(refStorages) return jsonutils.Marshal(diskSpec) } @@ -1291,6 +1337,53 @@ func (self *SHost) getNetworkOfIPOnHost(ipAddr string) (*SNetwork, error) { return net, nil } +func (self *SHost) GetNetinterfaceWithNetworkAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) (*SNetInterface, *SNetwork) { + netif, net := self.getNetifWithNetworkAndCredential(netId, userCred, true, reserved) + if netif != nil { + return netif, net + } + return self.getNetifWithNetworkAndCredential(netId, userCred, false, reserved) +} + +func (self *SHost) getNetifWithNetworkAndCredential(netId string, userCred mcclient.TokenCredential, isPublic bool, reserved bool) (*SNetInterface, *SNetwork) { + netifs := self.GetNetInterfaces() + var maxFreeCnt = 0 + var maxFreeNet *SNetwork + var maxFreeNetif *SNetInterface + for i := 0; i < len(netifs); i++ { + if !netifs[i].IsUsableServernic() { + continue + } + wire := netifs[i].GetWire() + if wire != nil { + if isPublic { + nets, _ := wire.getPublicNetworks() + for _, net := range nets { + if net.Id == netId || net.GetName() == netId { + freeCnt := net.getFreeAddressCount() + if maxFreeNet == nil || maxFreeCnt < freeCnt { + maxFreeNetif = &netifs[i] + maxFreeNet = &net + } + } + } + } else { + nets, _ := wire.getPrivateNetworks(userCred) + for _, net := range nets { + if net.Id == netId || net.GetName() == netId { + freeCnt := net.getFreeAddressCount() + if maxFreeNet == nil || maxFreeCnt < freeCnt { + maxFreeNetif = &netifs[i] + maxFreeNet = &net + } + } + } + } + } + } + return maxFreeNetif, maxFreeNet +} + func (self *SHost) GetNetworkWithIdAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) (*SNetwork, error) { net, err := self.getNetworkWithIdAndCredential(netId, userCred, true, reserved) if err == nil { @@ -1532,7 +1625,7 @@ func (self *SHost) getDiskConfig() jsonutils.JSONObject { return nil } -func (self *SHost) getBaremetalServer() *SGuest { +func (self *SHost) GetBaremetalServer() *SGuest { if !self.IsBaremetal { return nil } @@ -1598,7 +1691,7 @@ func (self *SHost) getMoreDetails(ctx context.Context, extra *jsonutils.JSONDict extra.Add(jsonutils.NewString(zone.GetRegion().GetName()), "region") extra.Add(jsonutils.NewString(zone.GetRegion().GetId()), "region_id") } - server := self.getBaremetalServer() + server := self.GetBaremetalServer() if server != nil { extra.Add(jsonutils.NewString(server.Id), "server_id") extra.Add(jsonutils.NewString(server.Name), "server") @@ -1678,6 +1771,26 @@ func (self *SHost) GetDetailsVnc(ctx context.Context, userCred mcclient.TokenCre return jsonutils.NewDict(), nil } +func (self *SHost) AllowGetDetailsIpmi(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) GetDetailsIpmi(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { + ret := self.IpmiInfo.(*jsonutils.JSONDict) + if ret != nil { + password, err := ret.GetString("password") + if err == nil { + descryptedPassword, err := utils.DescryptAESBase64(self.Id, password) + if err != nil { + return nil, err + } + ret.Set("password", jsonutils.NewString(descryptedPassword)) + } + } + + return ret, nil +} + func (manager *SHostManager) GetHostsByManagerAndRegion(managerId string, regionId string) []SHost { hosts := HostManager.Query().SubQuery() zones := ZoneManager.Query().SubQuery() @@ -1726,3 +1839,1123 @@ func (self *SHost) GetLocalStoragecache() *SStoragecache { } return nil } + +func (self *SHost) IsBaremetalAgentReady() bool { + url, err := auth.GetServiceURL("baremetal", options.Options.Region, self.GetZone().GetName(), "") + if err != nil { + log.Errorln("is baremetal agent ready: false") + return false + } + log.Infof("baremetal url:%s", url) + return true +} + +func (self *SHost) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) { + self.SEnabledStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerProjId, query, data) + kwargs := data.(*jsonutils.JSONDict) + ipmiInfo, err := self.FetchIpmiInfo(kwargs) + if err != nil { + log.Errorln(err.Error()) + return + } + if ipmiInfo.Length() > 0 { + _, err := HostManager.TableSpec().Update(self, func() error { + self.IpmiInfo = ipmiInfo + return nil + }) + if err != nil { + log.Errorln(err.Error()) + } + } +} + +func (manager *SHostManager) ValidateSizeParams(data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + memStr, _ := data.GetString("mem_size") + if len(memStr) > 0 { + if !regutils.MatchSize(memStr) { + return nil, fmt.Errorf("Memory size must be number[+unit], like 256M, 1G or 256") + } + memSize, err := fileutils.GetSizeMb(memStr, 'M', 1024) + if err != nil { + return nil, err + } + data.Set("mem_size", jsonutils.NewInt(int64(memSize))) + } + memReservedStr, _ := data.GetString("mem_reserved") + if len(memReservedStr) > 0 { + if !regutils.MatchSize(memReservedStr) { + return nil, fmt.Errorf("Memory size must be number[+unit], like 256M, 1G or 256") + } + memSize, err := fileutils.GetSizeMb(memReservedStr, 'M', 1024) + if err != nil { + return nil, err + } + data.Set("mem_reserved", jsonutils.NewInt(int64(memSize))) + } + cpuCacheStr, _ := data.GetString("cpu_cache") + if len(cpuCacheStr) > 0 { + if !regutils.MatchSize(cpuCacheStr) { + return nil, fmt.Errorf("Illegal cpu cache size %s", cpuCacheStr) + } + cpuCache, err := fileutils.GetSizeKb(cpuCacheStr, 'K', 1024) + if err != nil { + return nil, err + } + data.Set("cpu_cache", jsonutils.NewInt(int64(cpuCache))) + } + return data, nil +} + +func (manager *SHostManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + zoneId, _ := data.GetString("zone_id") + if len(zoneId) == 0 || ZoneManager.Query().Equals("id", zoneId).Count() == 0 { + return nil, httperrors.NewInputParameterError("Zone id %s not found", zoneId) + } + mangerUri, err := data.GetString("manager_uri") + if err == nil { + count := manager.TableSpec().Query().Equals("manager_uri", mangerUri).Count() + if count > 0 { + return nil, httperrors.NewConflictError("Conflict manager_uri %s", mangerUri) + } + } + accessIp, err := data.GetString("access_ip") + if err == nil { + count := manager.TableSpec().Query().Equals("access_ip", accessIp).Count() + if count > 0 { + return nil, httperrors.NewDuplicateResourceError("Duplicate access_ip %s", accessIp) + } + } + accessMac, err := data.GetString("access_mac") + if err == nil { + count := manager.TableSpec().Query().Equals("access_mac", accessMac).Count() + if count > 0 { + return nil, httperrors.NewDuplicateResourceError("Duplicate access_mac %s", accessMac) + } + } + data, err = manager.ValidateSizeParams(data) + if err != nil { + return nil, httperrors.NewInputParameterError(err.Error()) + } + memReserved, err := data.Int("mem_reserved") + if err != nil { + hostType, _ := data.GetString("host_type") + if hostType != HOST_TYPE_BAREMETAL { + memSize, _ := data.Int("mem_size") + memReserved = memSize / 8 + if memReserved > 4096 { + memReserved = 4096 + } + data.Set("mem_reserved", jsonutils.NewInt(memReserved)) + } else { + data.Set("mem_reserved", jsonutils.NewInt(0)) + } + } + return manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data) +} + +func (self *SHost) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + mangerUri, err := data.GetString("manager_uri") + if err == nil { + count := HostManager.Query().Equals("manager_uri", mangerUri). + NotEquals("id", self.Id).Equals("zone_id", self.ZoneId).Count() + if count > 0 { + return nil, httperrors.NewConflictError("Conflict manager_uri %s", mangerUri) + } + } + accessIp, err := data.GetString("access_ip") + if err == nil { + count := HostManager.Query().Equals("access_ip", accessIp). + NotEquals("id", self.Id).Equals("zone_id", self.ZoneId).Count() + if count > 0 { + return nil, httperrors.NewDuplicateResourceError("Duplicate access_ip %s", accessIp) + } + } + accessMac, err := data.GetString("access_mac") + if err == nil { + accessMac = netutils.FormatMacAddr(accessMac) + if len(accessMac) == 0 { + return nil, httperrors.NewInputParameterError("invalid access_mac address") + } + q := HostManager.Query().Equals("access_mac", accessMac).NotEquals("id", self.Id) + if q.Count() > 0 { + return nil, httperrors.NewDuplicateResourceError("Duplicate access_mac %s", accessMac) + } + } + data, err = HostManager.ValidateSizeParams(data) + if err != nil { + return nil, httperrors.NewInputParameterError(err.Error()) + } + ipmiInfo, err := self.FetchIpmiInfo(data) + if err != nil { + return nil, err + } + if ipmiInfo.Length() > 0 { + val := jsonutils.NewDict() + val.Update(self.IpmiInfo) + val.Update(ipmiInfo) + data.Set("impi_info", val) + } + data, err = self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) + if err != nil { + return nil, err + } + if data.Contains("name") { + self.UpdateDnsRecords(false) + } + return data, nil +} + +func (self *SHost) UpdateDnsRecords(isAdd bool) { + for _, netif := range self.GetNetInterfaces() { + self.UpdateDnsRecord(&netif, isAdd) + } +} + +func (self *SHost) UpdateDnsRecord(netif *SNetInterface, isAdd bool) { + name := self.GetNetifName(netif) + if len(name) == 0 { + return + } + bn := netif.GetBaremetalNetwork() + if bn == nil { + log.Errorf("Interface %s not enable", netif.GetId()) + return + } + net := bn.GetNetwork() + if net == nil { + log.Errorf("BaremetalNetwoke %s not found network", bn.GetId()) + } + net._updateDnsRecord(name, bn.IpAddr, isAdd) +} + +func (self *SHost) GetNetifName(netif *SNetInterface) string { + if netif.NicType == NIC_TYPE_IPMI { + return self.GetName() + } else if netif.NicType == NIC_TYPE_ADMIN { + return self.GetName() + "-admin" + } + return "" +} + +func (self *SHost) FetchIpmiInfo(data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + IPMI_KEY_PERFIX := "ipmi_" + ipmiInfo := jsonutils.NewDict() + kv, _ := data.GetMap() + for key, _ := range kv { + value, err := ipmiInfo.GetString(key) + if strings.HasPrefix(value, IPMI_KEY_PERFIX) { + subkey := key[len(IPMI_KEY_PERFIX):] + data.Remove(key) + if subkey == "password" { + value, err = utils.EncryptAESBase64(self.Id, value) + if err != nil { + log.Errorf("encrypt password failed %s", err) + return nil, err + } + } + ipmiInfo.Set(key, jsonutils.NewString(value)) + } + } + return ipmiInfo, nil +} + +func (self *SHost) AllowPerformStart(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, + data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if !self.IsBaremetal { + return nil, httperrors.NewBadRequestError("Cannot start a non-baremetal host") + } + if !utils.IsInStringArray(self.Status, []string{BAREMETAL_READY}) { + return nil, httperrors.NewInvalidStatusError("Cannot start baremetal with active guest") + } + guest := self.GetBaremetalServer() + if guest != nil { + if self.HostType != HOST_TYPE_BAREMETAL { + if !utils.IsInStringArray(guest.Status, []string{VM_ADMIN}) { + return nil, httperrors.NewBadRequestError("Cannot start baremetal with active guest") + } + } else { + if utils.ToBool(guest.GetMetadata("is_fake_baremetal_server", userCred)) { + return nil, self.InitializedGuestStart(ctx, userCred, guest) + } + self.SetStatus(userCred, BAREMETAL_START_MAINTAIN, "") + return guest.PerformStart(ctx, userCred, query, data) + } + } + params := jsonutils.NewDict() + params.Set("force_reboot", jsonutils.NewBool(false)) + params.Set("action", jsonutils.NewString("start")) + return self.PerformMaintenance(ctx, userCred, nil, params) +} + +func (self *SHost) AllowPerformStop(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, + data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if !self.IsBaremetal { + return nil, httperrors.NewBadRequestError("Cannot stop a non-baremetal host") + } + if !utils.IsInStringArray(self.Status, []string{BAREMETAL_RUNNING}) { + return nil, httperrors.NewInvalidStatusError("Cannot stop baremetal with non-active guest") + } + guest := self.GetBaremetalServer() + if guest != nil { + if self.HostType != HOST_TYPE_BAREMETAL { + if !utils.IsInStringArray(guest.Status, []string{VM_ADMIN}) { + return nil, httperrors.NewBadRequestError("Cannot stop baremetal with active guest") + } + } else { + if utils.ToBool(guest.GetMetadata("is_fake_baremetal_server", userCred)) { + return nil, self.InitializedGuestStop(ctx, userCred, guest) + } + self.SetStatus(userCred, BAREMETAL_START_MAINTAIN, "") + return guest.PerformStop(ctx, userCred, query, data) + } + } + return nil, self.StartBaremetalUnmaintenanceTask(ctx, userCred, false, "stop") +} + +func (self *SHost) InitializedGuestStart(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error { + task, err := taskman.TaskManager.NewTask(ctx, "BaremetalServerStartTask", guest, userCred, nil, "", "", nil) + if err != nil { + return err + } + task.ScheduleRun(nil) + return nil +} + +func (self *SHost) InitializedGuestStop(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error { + task, err := taskman.TaskManager.NewTask(ctx, "BaremetalServerStopTask", guest, userCred, nil, "", "", nil) + if err != nil { + return err + } + task.ScheduleRun(nil) + return nil +} + +func (self *SHost) AllowPerformMaintenance(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformMaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if !utils.IsInStringArray(self.Status, []string{BAREMETAL_READY, BAREMETAL_RUNNING}) { + return nil, httperrors.NewInvalidStatusError("Cannot do maintenance in status %s", self.Status) + } + guest := self.GetBaremetalServer() + if guest != nil && !utils.IsInStringArray(guest.Status, []string{VM_READY, VM_RUNNING, VM_ADMIN}) { + return nil, httperrors.NewInvalidStatusError("Cannot do maintenance while guest status %s", guest.Status) + } + params := jsonutils.NewDict() + if guest != nil { + if guest.Status == VM_RUNNING { + params.Set("guest_running", jsonutils.NewBool(true)) + } + guest.SetStatus(userCred, VM_ADMIN, "") + } + if self.Status == BAREMETAL_RUNNING && jsonutils.QueryBoolean(data, "force_reboot", false) { + params.Set("force_reboot", jsonutils.NewBool(true)) + } + action := "maintenance" + if data.Contains("action") { + action, _ = data.GetString("action") + } + params.Set("action", jsonutils.NewString(action)) + self.SetStatus(userCred, BAREMETAL_START_MAINTAIN, "") + task, err := taskman.TaskManager.NewTask(ctx, "BaremetalMaintenanceTask", self, userCred, params, "", "", nil) + if err != nil { + return nil, err + } + task.ScheduleRun(nil) + return nil, nil +} + +func (self *SHost) AllowPerformUnmaintenance(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformUnmaintenance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if !utils.IsInStringArray(self.Status, []string{BAREMETAL_RUNNING, BAREMETAL_READY}) { + return nil, httperrors.NewInvalidStatusError("Cannot do unmaintenance in status %s", self.Status) + } + guest := self.GetBaremetalServer() + if guest != nil && guest.Status != VM_ADMIN { + return nil, httperrors.NewInvalidStatusError("Wrong guest status %s", guest.Status) + } + action, _ := data.GetString("action") + if len(action) == 0 { + action = "unmaintenance" + } + guestRunning := self.GetMetadata("__maint_guest_running", userCred) + var startGuest = false + if utils.ToBool(guestRunning) { + startGuest = true + } + return nil, self.StartBaremetalUnmaintenanceTask(ctx, userCred, startGuest, action) +} + +func (self *SHost) StartBaremetalUnmaintenanceTask(ctx context.Context, userCred mcclient.TokenCredential, startGuest bool, action string) error { + self.SetStatus(userCred, BAREMETAL_START_MAINTAIN, "") + params := jsonutils.NewDict() + params.Set("guest_running", jsonutils.NewBool(startGuest)) + if len(action) == 0 { + action = "unmaintenance" + } + params.Set("action", jsonutils.NewString(action)) + task, err := taskman.TaskManager.NewTask(ctx, "BaremetalUnmaintenanceTask", self, userCred, params, "", "", nil) + if err != nil { + return err + } + task.ScheduleRun(nil) + return nil +} + +func (self *SHost) BaremetalSyncRequest(ctx context.Context, method, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) { + serviceUrl, err := auth.GetServiceURL("baremetal", options.Options.Region, self.GetZone().GetName(), "") + if err != nil { + return nil, err + } + url = serviceUrl + url + _, data, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, method, url, headers, body, false) + return data, err +} + +func (self *SHost) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { + task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncStatusTask", self, userCred, nil, parentTaskId, "", nil) + if err != nil { + return err + } + task.ScheduleRun(nil) + return nil +} + +func (self *SHost) AllowPerformOffline(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformOffline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.Status != HOST_OFFLINE { + self.GetModelManager().TableSpec().Update(self, func() error { + self.Status = HOST_OFFLINE + return nil + }) + db.OpsLog.LogEvent(self, db.ACT_OFFLINE, "", userCred) + logclient.AddActionLog(self, logclient.ACT_ONLINE, nil, userCred, true) + } + return nil, nil +} + +func (self *SHost) AllowPerformOnline(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformOnline(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.HostStatus != HOST_ONLINE { + self.GetModelManager().TableSpec().Update(self, func() error { + self.LastPingAt = time.Now() + self.HostStatus = HOST_ONLINE + self.Status = BAREMETAL_RUNNING + return nil + }) + db.OpsLog.LogEvent(self, db.ACT_ONLINE, "", userCred) + logclient.AddActionLog(self, logclient.ACT_ONLINE, nil, userCred, true) + self.SyncAttachedStorageStatus() + self.StartSyncAllGuestsStatusTask(ctx, userCred) + } + return nil, nil +} + +func (self *SHost) StartSyncAllGuestsStatusTask(ctx context.Context, userCred mcclient.TokenCredential) error { + if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncAllGuestsStatusTask", self, userCred, nil, "", "", nil); err != nil { + log.Errorf(err.Error()) + return err + } else { + task.ScheduleRun(nil) + return nil + } +} + +func (self *SHost) AllowPerformPing(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformPing(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.HostStatus != HOST_ONLINE { + self.PerformOnline(ctx, userCred, query, data) + } else { + self.GetModelManager().TableSpec().Update(self, func() error { + self.LastPingAt = time.Now() + return nil + }) + } + result := jsonutils.NewDict() + result.Set("name", jsonutils.NewString(self.GetName())) + catalog := auth.GetCatalogData([]string{"ntpd", "kafka", "influxdb"}, options.Options.Region) + if catalog == nil { + return nil, fmt.Errorf("Get catalog error") + } + result.Set("catalog", catalog) + return result, nil +} + +func (self *SHost) AllowPerformPrepare(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformPrepare(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if utils.IsInStringArray(self.Status, []string{BAREMETAL_READY, BAREMETAL_RUNNING, BAREMETAL_PREPARE_FAIL}) { + var onfinish string + if self.GetBaremetalServer() != nil { + if self.Status == BAREMETAL_RUNNING { + onfinish = "restart" + } else if self.Status == BAREMETAL_READY { + onfinish = "shotdown" + } + } + return nil, self.StartPrepareTask(ctx, userCred, onfinish, "") + } + return nil, httperrors.NewInvalidStatusError("Cannot prepare baremetal in status %s", self.Status) +} + +func (self *SHost) StartPrepareTask(ctx context.Context, userCred mcclient.TokenCredential, onfinish, parentTaskId string) error { + data := jsonutils.NewDict() + if len(onfinish) > 0 { + data.Set("on_finish", jsonutils.NewString(onfinish)) + } + if task, err := taskman.TaskManager.NewTask(ctx, "BaremetalPrepareTask", self, userCred, data, parentTaskId, "", nil); err != nil { + log.Errorf(err.Error()) + return err + } else { + task.ScheduleRun(nil) + return nil + } +} + +func (self *SHost) AllowPerformAddNetif(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformAddNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + mac, _ := data.GetString("mac") + if len(mac) == 0 || len(netutils.FormatMacAddr(mac)) == 0 { + return nil, httperrors.NewBadRequestError("Invaild mac address") + } + wire, _ := data.GetString("wire") + ipAddr, _ := data.GetString("ip_addr") + var sw *SWire + if len(wire) > 0 && len(ipAddr) == 0 { + iWire, err := WireManager.FetchByIdOrName(userCred.GetProjectId(), wire) + if err != nil { + return nil, httperrors.NewBadRequestError("Wire %s not found", wire) + } + sw = iWire.(*SWire) + } else if len(ipAddr) > 0 && len(wire) == 0 { + ipWire, err := WireManager.GetWireOfIp(ipAddr) + if err != nil { + return nil, httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) + } + sw = ipWire + } else if len(wire) > 0 && len(ipAddr) > 0 { + ipWire, err := WireManager.GetWireOfIp(ipAddr) + if err != nil { + return nil, httperrors.NewBadRequestError("IP %s not attach to any wire", ipAddr) + } + if ipWire.Id != wire && ipWire.GetName() != wire { + httperrors.NewBadRequestError("IP %s not attach to wire %s", ipAddr, wire) + } + sw = ipWire + } + netif, err := NetInterfaceManager.FetchByMac(mac) + if err != nil { + netif = &SNetInterface{} + netif.Mac = mac + netif.BaremetalId = self.Id + if sw != nil { + netif.WireId = sw.Id + } + if rate, err := data.Int("rate"); err == nil { + netif.Rate = int(rate) + } + if nicType, err := data.GetString("nic_type"); err == nil { + netif.NicType = nicType + } + if index, err := data.Int("index"); err == nil { + netif.Index = int8(index) + } + if linkUp, err := data.GetString("link_up"); err == nil { + netif.LinkUp = utils.ToBool(linkUp) + } + if mtu, err := data.Int("mtu"); err == nil { + netif.Mtu = int16(mtu) + } + err = NetInterfaceManager.TableSpec().Insert(netif) + if err != nil { + return nil, err + } + } else { + var changed = false + _, err := NetInterfaceManager.TableSpec().Update(netif, func() error { + if netif.BaremetalId != self.Id { + changed = true + netif.BaremetalId = self.Id + } + if sw != nil && netif.WireId != sw.Id { + changed = true + netif.WireId = sw.Id + } + if rate, err := data.Int("rate"); err == nil { + changed = true + netif.Rate = int(rate) + } + if nicType, err := data.GetString("nic_type"); err == nil { + changed = true + netif.NicType = nicType + } + if index, err := data.Int("index"); err == nil && index >= 0 { + changed = true + netif.Index = int8(index) + } + if linkUp, err := data.GetString("link_up"); err != nil { + changed = true + netif.LinkUp = utils.ToBool(linkUp) + } + if mtu, err := data.Int("mtu"); err != nil { + changed = true + netif.Mtu = int16(mtu) + } + return nil + }) + if err != nil { + return nil, err + } + if changed || jsonutils.QueryBoolean(data, "reset", false) { + self.DisableNetif(ctx, userCred, netif, false) + } + } + sw = netif.GetWire() + if sw != nil { + strInterface, err := data.GetString("interface") + if err != nil { + strInterface = fmt.Sprintf("eth%d", netif.Index) + } + if len(strInterface) > 0 { + bridge, err := data.GetString("bridge") + if err != nil { + bridge = fmt.Sprintf("br%s", sw.GetName()) + } + var isMaster = netif.NicType == NIC_TYPE_ADMIN + ihw, err := HostwireManager.FetchByIds(self.Id, sw.Id) + if err != nil { + hw := &SHostwire{} + hw.Bridge = bridge + hw.Interface = strInterface + hw.HostId = self.Id + hw.WireId = sw.Id + hw.IsMaster = isMaster + hw.MacAddr = mac + err := HostwireManager.TableSpec().Insert(hw) + if err != nil { + return nil, err + } + } else { + hw := ihw.(*SHostwire) + HostwireManager.TableSpec().Update(hw, func() error { + hw.Bridge = bridge + hw.Interface = strInterface + hw.MacAddr = mac + hw.IsMaster = isMaster + return nil + }) + } + } + } + reserve := jsonutils.QueryBoolean(data, "reserve", false) + requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false) + if len(ipAddr) > 0 { + err = self.EnableNetif(ctx, userCred, netif, "", ipAddr, "", reserve, requireDesignatedIp) + if err != nil { + return nil, httperrors.NewBadRequestError(err.Error()) + } + } + return nil, nil +} + +func (self *SHost) AllowPerformEnableNetif(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformEnableNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + mac, _ := data.GetString("mac") + netif := self.GetNetInterface(mac) + if netif == nil { + return nil, httperrors.NewBadRequestError("Interface %s not exist", mac) + } + if !utils.IsInStringArray(netif.NicType, NIC_TYPES) { + return nil, httperrors.NewBadRequestError("Only ADMIN and IPMI nic can be enable") + } + network, _ := data.GetString("network") + ipAddr, _ := data.GetString("ip_addr") + allocDir, _ := data.GetString("alloc_dir") + reserve := jsonutils.QueryBoolean(data, "reserve", false) + requireDesignatedIp := jsonutils.QueryBoolean(data, "require_designated_ip", false) + err := self.EnableNetif(ctx, userCred, netif, network, ipAddr, allocDir, reserve, requireDesignatedIp) + if err != nil { + return nil, httperrors.NewBadRequestError(err.Error()) + } + return nil, nil +} + +func (self *SHost) EnableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, network, ipAddr, allocDir string, reserve, requireDesignatedIp bool) error { + bn := netif.GetBaremetalNetwork() + if bn != nil { + return nil + } + var net *SNetwork + var err error + if len(ipAddr) > 0 { + net, err = netif.GetCandidateNetworkForIp(userCred, ipAddr) + if net != nil { + log.Infof("find network %s for ip %s", net.GetName(), ipAddr) + } else if requireDesignatedIp { + log.Errorf("Cannot allocate IP %s, not reachable", ipAddr) + return fmt.Errorf("Cannot allocate IP %s, not reachable", ipAddr) + } + } + wire := netif.GetWire() + if wire == nil { + return fmt.Errorf("No wire attached") + } + hw, err := HostwireManager.FetchByIds(self.Id, wire.Id) + if hw == nil { + return fmt.Errorf("host not attach to this wire") + } + if net == nil { + if len(network) > 0 { + iNet, err := NetworkManager.FetchByIdOrName(userCred.GetProjectId(), network) + if err != nil { + return fmt.Errorf("Network %s not found: %s", network, err) + } + net = iNet.(*SNetwork) + if len(net.WireId) == 0 || net.WireId != wire.Id { + return fmt.Errorf("Network %s not reacheable on mac %s", network, netif.Mac) + } + } else { + net, err = wire.GetCandidatePrivateNetwork(userCred, false, SERVER_TYPE_GUEST) + if err != nil || net == nil { + return fmt.Errorf("No network found") + } + } + } else if net.WireId != wire.Id { + return fmt.Errorf("conflict??? candiate net is not on wire") + } + return self.Attach2Network(ctx, userCred, netif, net, ipAddr, allocDir, reserve, requireDesignatedIp) +} + +func (self *SHost) AllowPerformDisableNetif(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformDisableNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + mac, _ := data.GetString("mac") + netif := self.GetNetInterface(mac) + if netif == nil { + return nil, httperrors.NewBadRequestError("Interface %s not exists", mac) + } + reserve := jsonutils.QueryBoolean(data, "reserve", false) + err := self.DisableNetif(ctx, userCred, netif, reserve) + if err != nil { + return nil, httperrors.NewBadRequestError(err.Error()) + } + return nil, nil +} + +func (self *SHost) DisableNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error { + bn := netif.GetBaremetalNetwork() + if bn != nil { + self.UpdateDnsRecord(netif, false) + self.DeleteBaremetalnetwork(ctx, userCred, bn, reserve) + } + return nil +} + +func (self *SHost) Attach2Network(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, net *SNetwork, ipAddr, allocDir string, reserved, requireDesignatedIp bool) error { + lockman.LockObject(ctx, net) + defer lockman.ReleaseObject(ctx, net) + usedAddr := net.GetUsedAddresses() + freeIp, err := net.GetFreeIP(ctx, userCred, usedAddr, nil, ipAddr, IPAddlocationDirection(allocDir), reserved) + if err != nil { + log.Errorf("attach2network: %s", err) + return err + } + if len(ipAddr) > 0 && ipAddr != freeIp && requireDesignatedIp { + return fmt.Errorf("IP address %s is occupied", ipAddr) + } + bn := &SHostnetwork{} + bn.BaremetalId = self.Id + bn.NetworkId = net.Id + bn.IpAddr = freeIp + bn.MacAddr = netif.Mac + HostnetworkManager.TableSpec().Insert(bn) + db.OpsLog.LogAttachEvent(self, net, userCred, jsonutils.NewString(freeIp)) + self.UpdateDnsRecord(netif, true) + net.UpdateBaremetalNetmap(bn, self.GetNetifName(netif)) + return nil +} + +func (self *SHost) AllowPerformRemoveNetif(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformRemoveNetif(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + mac, _ := data.GetString("mac") + mac = netutils.FormatMacAddr(mac) + if len(mac) == 0 { + return nil, httperrors.NewBadRequestError("Invalid mac address") + } + netif, err := NetInterfaceManager.FetchByMac(mac) + if err != nil { + return nil, httperrors.NewBadRequestError("Fetch netif error %s", err) + } + return nil, self.RemoveNetif(ctx, userCred, netif, jsonutils.QueryBoolean(data, "reserve", false)) +} + +func (self *SHost) RemoveNetif(ctx context.Context, userCred mcclient.TokenCredential, netif *SNetInterface, reserve bool) error { + wire := netif.GetWire() + self.DisableNetif(ctx, userCred, netif, reserve) + log.Infof("Remove wire") + err := netif.Remove(ctx, userCred) + if err != nil { + return err + } + if wire != nil { + log.Infof("Remove wire") + others := self.GetNetifsOnWire(wire) + if len(others) == 0 { + hw, _ := HostwireManager.FetchByIds(self.Id, wire.Id) + if hw != nil { + db.OpsLog.LogDetachEvent(self, wire, userCred, jsonutils.NewString(fmt.Sprintf("disable netif %s", self.AccessMac))) + log.Infof("Detach host wire because of remove netif %s", netif.Mac) + return hw.Delete(ctx, userCred) + } + } + } + return nil +} + +func (self *SHost) GetNetifsOnWire(wire *SWire) []SNetInterface { + dest := make([]SNetInterface, 0) + q := NetInterfaceManager.Query() + err := q.Filter(sqlchemy.Equals(q.Field("baremetal_id"), self.Id)).Filter(sqlchemy.Equals(q.Field("wire_id"), wire.Id)).Desc(q.Field("index")).All(&dest) + if err != nil { + log.Errorln(err) + return nil + } + return dest +} + +func (self *SHost) AllowPerformSyncstatus(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + self.SetStatus(userCred, BAREMETAL_SYNCING_STATUS, "") + return nil, self.StartSyncstatus(ctx, userCred, "") +} + +func (self *SHost) AllowPerformReset(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformReset(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if !self.IsBaremetal { + return nil, httperrors.NewBadRequestError("Cannot start a non-baremetal host") + } + if self.Status != BAREMETAL_RUNNING { + return nil, httperrors.NewBadRequestError("Cannot reset baremetal in status %s", self.Status) + } + guest := self.GetBaremetalServer() + if guest != nil { + if self.HostType == HOST_TYPE_BAREMETAL { + if guest.Status != VM_ADMIN { + return nil, httperrors.NewBadRequestError("Cannot reset baremetal with active guest") + } + } else { + return guest.PerformReset(ctx, userCred, query, data) + } + } + kwargs := jsonutils.NewDict() + kwargs.Set("force_reboot", jsonutils.JSONTrue) + kwargs.Set("action", jsonutils.NewString("reset")) + return self.PerformMaintenance(ctx, userCred, query, kwargs) +} + +func (self *SHost) AllowPerformRemoveAllNetifs(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformRemoveAllNetifs(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + netifs := self.GetNetInterfaces() + for i := 0; i < len(netifs); i++ { + if !utils.IsInStringArray(netifs[i].NicType, NIC_TYPES) { + self.RemoveNetif(ctx, userCred, &netifs[i], false) + } + } + return nil, nil +} + +func (self *SHost) AllowPerformDisable(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.Enabled { + _, err := self.GetModelManager().TableSpec().Update(self, func() error { + self.Enabled = false + return nil + }) + if err != nil { + return nil, err + } + db.OpsLog.LogEvent(self, db.ACT_DISABLE, "", userCred) + logclient.AddActionLog(self, logclient.ACT_DISABLE, nil, userCred, true) + self.SyncAttachedStorageStatus() + } + return nil, nil +} + +func (self *SHost) AllowPerformCacheImage(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformCacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.HostStatus != HOST_ONLINE { + return nil, httperrors.NewInvalidStatusError("Cannot perform cache image in status %s", self.Status) + } + imageId, _ := data.GetString("image") + img, err := CachedimageManager.getImageInfo(ctx, userCred, imageId, false) + if err != nil { + log.Errorf(err.Error()) + return nil, httperrors.NewNotFoundError("image %s not found", imageId) + } + if len(img.Checksum) != 0 && regutils.MatchUUID(img.Checksum) { + return nil, httperrors.NewInvalidStatusError("Cannot cache image with no checksum") + } + isForce := jsonutils.QueryBoolean(data, "is_force", false) + return nil, self.StartImageCacheTask(ctx, userCred, img.Id, isForce) +} + +func (self *SHost) StartImageCacheTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, isForce bool) error { + sc := self.GetLocalStoragecache() + if sc == nil { + return fmt.Errorf("No local storage cache found") + } + return sc.StartImageCacheTask(ctx, userCred, imageId, isForce, "") +} + +func (self *SHost) AllowPerformConvertHypervisor(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) isAlterNameUnique(name string) bool { + if self.GetModelManager().Query().Equals("name", name).NotEquals("id", self.Id).Equals("zone_id", self.ZoneId).Count() == 0 { + return true + } + return false +} + +func (self *SHost) PerformConvertHypervisor(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + hostType, err := data.GetString("host_type") + if err != nil { + return nil, httperrors.NewNotAcceptableError("host_type must be specified") + } + if self.HostType != HOST_TYPE_BAREMETAL { + return nil, httperrors.NewNotAcceptableError("Must be a baremetal host") + } + if self.GetBaremetalServer() != nil { + return nil, httperrors.NewNotAcceptableError("Baremetal host is aleady occupied") + } + if !utils.IsInStringArray(self.Status, []string{BAREMETAL_READY, BAREMETAL_RUNNING}) { + return nil, httperrors.NewNotAcceptableError("Connot convert hypervisor in status %s", self.Status) + } + driver := GetHostDriver(hostType) + if driver == nil { + return nil, httperrors.NewNotAcceptableError("Unsupport driver type %s", hostType) + } + // err := driver.CheckConvertConfig() do nothing + // if err != nil { + // return nil, httperrors.NewNotAcceptableError("Need more configuration: %s", err.Error()) + // } + if data.Contains("name") { + name, _ := data.GetString("name") + err := self.GetModelManager().ValidateName(name) + if err != nil { + return nil, err + } + if !self.isAlterNameUnique(name) { + return nil, httperrors.NewInputParameterError("Invalid name %s", name) + } + } + image, _ := data.GetString("image") + raid, _ := data.GetString("raid") + params, err := driver.PrepareConvert(self, image, raid, data) + if err != nil { + return nil, httperrors.NewNotAcceptableError("Convert error: %s", err.Error()) + } + guest, err := GuestManager.DoCreate(ctx, userCred, data, params, GuestManager) + if err != nil { + return nil, err + } + log.Infof("Host convert to %s", guest.GetName()) + db.OpsLog.LogEvent(self, db.ACT_CONVERT_START, "", userCred) + db.OpsLog.LogEvent(guest, db.ACT_CREATE, "Convert hypervisor", userCred) + params.Set("__task__", jsonutils.NewString(taskman.CONVERT_TASK)) + params.Set("__convert_host_type__", jsonutils.NewString(hostType)) + GuestManager.OnCreateComplete(ctx, []db.IModel{guest}, userCred, nil, params) + self.SetStatus(userCred, BAREMETAL_START_CONVERT, "") + return nil, nil +} + +func (self *SHost) AllowPerformUndoConvert(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SHost) PerformUndoConvert(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if !self.IsBaremetal { + return nil, httperrors.NewNotAcceptableError("Not a baremetal") + } + if self.HostType == HOST_TYPE_BAREMETAL { + return nil, httperrors.NewNotAcceptableError("Not being convert to hypervisor") + } + if self.Enabled { + return nil, httperrors.NewNotAcceptableError("Host should be disabled") + } + if !utils.IsInStringArray(self.Status, []string{BAREMETAL_READY, BAREMETAL_RUNNING}) { + return nil, httperrors.NewNotAcceptableError("Cannot unconvert in status %s", self.Status) + } + driver := self.GetDriverWithDefault() + if driver == nil { + return nil, httperrors.NewNotAcceptableError("Unsupport driver type %s", self.HostType) + } + err := driver.PrepareUnconvert(self) + if err != nil { + return nil, httperrors.NewNotAcceptableError(err.Error()) + } + guests := self.GetGuests() + if len(guests) > 1 { + return nil, httperrors.NewNotAcceptableError("Not an empty host") + } else if len(guests) == 1 { + guest := guests[0] + if guest.Hypervisor != HYPERVISOR_BAREMETAL { + return nil, httperrors.NewNotAcceptableError("Not an converted hypervisor") + } + _, err := guest.GetModelManager().TableSpec().Update(&guest, func() error { + guest.DisableDelete = tristate.False + return nil + }) + if err != nil { + return nil, err + } + db.OpsLog.LogEvent(&guest, db.ACT_DELETE, "Unconvert baremetal", userCred) + } + db.OpsLog.LogEvent(self, db.ACT_UNCONVERT_START, "", userCred) + task, err := taskman.TaskManager.NewTask(ctx, "BaremetalUnconvertHypervisorTask", self, userCred, nil, "", "", nil) + if err != nil { + return nil, err + } + task.ScheduleRun(nil) + return nil, nil +} + +func (self *SHost) GetDriverWithDefault() IHostDriver { + hostType := self.HostType + if len(hostType) == 0 { + hostType = HOST_TYPE_DEFAULT + } + return GetHostDriver(hostType) +} + +func (self *SHost) UpdateDiskConfig(layouts []baremetal.Layout) error { + bs := self.GetBaremetalstorage() + if bs != nil { + _, err := bs.GetModelManager().TableSpec().Update(bs, func() error { + if len(layouts) != 0 { + bs.Config = jsonutils.Marshal(layouts).(*jsonutils.JSONArray) + var size int64 + for i := 0; i < len(layouts); i++ { + size += layouts[i].Size + } + bs.RealCapacity = int(size) + } else { + bs.Config = jsonutils.NewArray() + bs.RealCapacity = bs.GetStorage().Capacity + } + return nil + }) + if err != nil { + log.Errorln(err) + return err + } + } + return nil +} diff --git a/pkg/compute/models/hoststorages.go b/pkg/compute/models/hoststorages.go index ca4dd038bb..16bf9e7fee 100644 --- a/pkg/compute/models/hoststorages.go +++ b/pkg/compute/models/hoststorages.go @@ -35,10 +35,10 @@ func init() { type SHoststorage struct { SHostJointsBase - MountPoint string `width:"256" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` // Column(VARCHAR(256, charset='ascii'), nullable=False) + MountPoint string `width:"256" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"required"` // Column(VARCHAR(256, charset='ascii'), nullable=False) - HostId string `width:"36" charset:"ascii" nullable:"false" list:"admin" key_index:"true"` // Column(VARCHAR(36, charset='ascii'), nullable=False) - StorageId string `width:"36" charset:"ascii" nullable:"false" list:"admin" key_index:"true"` // Column(VARCHAR(36, charset='ascii'), nullable=False) + HostId string `width:"36" charset:"ascii" nullable:"false" list:"admin" key_index:"true" create:"required"` // Column(VARCHAR(36, charset='ascii'), nullable=False) + StorageId string `width:"36" charset:"ascii" nullable:"false" list:"admin" key_index:"true" create:"required"` // Column(VARCHAR(36, charset='ascii'), nullable=False) Config *jsonutils.JSONArray `nullable:"true" get:"admin"` // Column(JSONEncodedDict, nullable=True) RealCapacity int `nullable:"true" list:"admin"` // Column(Integer, nullable=True) diff --git a/pkg/compute/models/isolated_devices.go b/pkg/compute/models/isolated_devices.go index 4cd6f93e66..0c30205d36 100644 --- a/pkg/compute/models/isolated_devices.go +++ b/pkg/compute/models/isolated_devices.go @@ -549,3 +549,23 @@ func (self *SIsolatedDevice) CustomizeDelete(ctx context.Context, userCred mccli } return self.RealDelete(ctx, userCred) } + +func (manager *SIsolatedDeviceManager) FindByHost(id string) []SIsolatedDevice { + return manager.FindByHosts([]string{id}) +} + +func (manager *SIsolatedDeviceManager) FindByHosts(ids []string) []SIsolatedDevice { + dest := make([]SIsolatedDevice, 0) + err := manager.TableSpec().Query().In("host_id", ids).All(&dest) + if err != nil { + log.Errorln(err) + return nil + } + return dest +} + +func (manager *SIsolatedDeviceManager) DeleteDevicesByHost(ctx context.Context, userCred mcclient.TokenCredential, host *SHost) { + for _, dev := range manager.FindByHost(host.Id) { + dev.Delete(ctx, userCred) + } +} diff --git a/pkg/compute/models/netinterfaces.go b/pkg/compute/models/netinterfaces.go index 8afc8eaa5b..3bc9ff8901 100644 --- a/pkg/compute/models/netinterfaces.go +++ b/pkg/compute/models/netinterfaces.go @@ -50,7 +50,7 @@ func (manager *SNetInterfaceManager) FetchByMac(mac string) (*SNetInterface, err if err != nil { return nil, err } - err = manager.TableSpec().Query().Equals("mac", mac).First(&netif) + err = manager.TableSpec().Query().Equals("mac", mac).First(netif) if err != nil { return nil, err } @@ -93,10 +93,29 @@ func (netif *SNetInterface) GetBaremetalNetwork() *SHostnetwork { return &bn } -func (self *SNetInterface) toJson(ipAddr string, network *SNetwork) *jsonutils.JSONDict { +func (self *SNetInterface) guestNetworkToJson(bn *SGuestnetwork) *jsonutils.JSONDict { jsonDesc := jsonutils.Marshal(self) desc := jsonDesc.(*jsonutils.JSONDict) + if bn == nil { + return desc + } else { + return self.networkToJson(bn.IpAddr, bn.GetNetwork(), desc) + } +} + +func (self *SNetInterface) hostNetworkToJson(bn *SHostnetwork) *jsonutils.JSONDict { + jsonDesc := jsonutils.Marshal(self) + desc := jsonDesc.(*jsonutils.JSONDict) + + if bn == nil { + return desc + } else { + return self.networkToJson(bn.IpAddr, bn.GetNetwork(), desc) + } +} + +func (self *SNetInterface) networkToJson(ipAddr string, network *SNetwork, desc *jsonutils.JSONDict) *jsonutils.JSONDict { if len(ipAddr) > 0 { desc.Add(jsonutils.NewString(ipAddr), "ip_addr") } @@ -126,11 +145,11 @@ func (self *SNetInterface) toJson(ipAddr string, network *SNetwork) *jsonutils.J func (self *SNetInterface) getServernetwork() *SGuestnetwork { host := self.GetBaremetal() - if host != nil { + if host == nil { return nil } - server := host.getBaremetalServer() - if server != nil { + server := host.GetBaremetalServer() + if server == nil { return nil } obj, err := db.NewModelObject(GuestnetworkManager) @@ -152,7 +171,8 @@ func (self *SNetInterface) getServernetwork() *SGuestnetwork { func (self *SNetInterface) getServerJsonDesc() *jsonutils.JSONDict { bn := self.getServernetwork() - desc := self.toJson(bn.IpAddr, bn.GetNetwork()) + var desc = jsonutils.NewDict() + desc = self.guestNetworkToJson(bn) if bn != nil { desc.Add(jsonutils.JSONFalse, "virtual") desc.Add(jsonutils.NewString(bn.IpAddr), "ip") @@ -172,7 +192,7 @@ func (self *SNetInterface) getBaremetalJsonDesc() *jsonutils.JSONDict { if bn == nil { return nil } - return self.toJson(bn.IpAddr, bn.GetNetwork()) + return self.hostNetworkToJson(bn) } /* @@ -226,3 +246,27 @@ func (self *SNetInterface) Remove(ctx context.Context, userCred mcclient.TokenCr } return nil } + +func (self *SNetInterface) GetCandidateNetworkForIp(userCred mcclient.TokenCredential, ipAddr string) (*SNetwork, error) { + wire := self.GetWire() + if wire == nil { + return nil, nil + } + return wire.GetCandidateNetworkForIp(userCred, ipAddr) +} + +func (self *SNetInterface) IsUsableServernic() bool { + if self.NicType == NIC_TYPE_IPMI { + return false + } + if len(self.WireId) == 0 { + return false + } + if !self.LinkUp { + return false + } + if self.getServernetwork() != nil { + return false + } + return true +} diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 4f79a372b0..1d5d4f0e19 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -23,6 +23,7 @@ import ( "yunion.io/x/onecloud/pkg/compute/options" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/auth" ) const ( @@ -381,6 +382,14 @@ func (self *SNetwork) updateGuestNetmap(nic *SGuestnetwork) { } +func (self *SNetwork) UpdateBaremetalNetmap(nic *SHostnetwork, name string) { + self.UpdateNetmap(nic.IpAddr, auth.AdminCredential().GetTenantId(), name) +} + +func (self *SNetwork) UpdateNetmap(ip, project, name string) { + // TODO ?? +} + type DNSUpdateKeySecret struct { Key string Secret string @@ -1282,6 +1291,30 @@ func (self *SNetwork) isManaged() bool { } } +func (manager *SNetworkManager) CustomizeFilterList(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, retList []jsonutils.JSONObject) ([]jsonutils.JSONObject, error) { + if query.Contains("ip") { + ip, _ := query.GetString("ip") + ipInt, err := netutils.NewIPV4Addr(ip) + if err != nil { + return nil, err + } + ret := make([]jsonutils.JSONObject, 0) + for i := 0; i < len(retList); i++ { + guestIpStart, _ := retList[i].GetString("guest_ip_start") + guestIpEnd, _ := retList[i].GetString("guest_ip_end") + guestIpStartInt, _ := netutils.NewIPV4Addr(guestIpStart) + guestIpEndInt, _ := netutils.NewIPV4Addr(guestIpEnd) + ipRange := netutils.NewIPV4AddrRange(guestIpStartInt, guestIpEndInt) + if ipRange.Contains(ipInt) { + ret = append(ret, retList[i]) + } + } + return ret, nil + } else { + return manager.SStatusStandaloneResourceBaseManager.CustomizeFilterList(ctx, userCred, query, retList) + } +} + func (manager *SNetworkManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { q, err := manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query) if err != nil { diff --git a/pkg/compute/models/storages.go b/pkg/compute/models/storages.go index 8e1c0992a6..8a8a5676f8 100644 --- a/pkg/compute/models/storages.go +++ b/pkg/compute/models/storages.go @@ -13,6 +13,7 @@ import ( "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/util/compare" "yunion.io/x/pkg/util/sysutils" + "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" ) @@ -88,6 +89,40 @@ func (manager *SStorageManager) GetContextManager() []db.IModelManager { return []db.IModelManager{ZoneManager, StoragecacheManager} } +func (manager *SStorageManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + storageType, _ := data.GetString("storage_type") + mediumType, _ := data.GetString("medium_type") + if !utils.IsInStringArray(storageType, STORAGE_ALL_TYPES) { + return nil, httperrors.NewInputParameterError("Invalid storage type %s", storageType) + } + if !utils.IsInStringArray(mediumType, DISK_TYPES) { + return nil, httperrors.NewInputParameterError("Invalid medium type %s", mediumType) + } + zoneId, err := data.GetString("zone") + if err != nil { + return nil, httperrors.NewMissingParameterError("zone") + } + zone, _ := ZoneManager.FetchByIdOrName(userCred.GetProjectId(), zoneId) + if zone == nil { + return nil, httperrors.NewResourceNotFoundError("zone %s", zoneId) + } + data.Set("zone_id", jsonutils.NewString(zone.GetId())) + // TODO: ValidateRdbConfData + // if storageType == STORAGE_RBD { + // conf := jsonutils.NewDict() + // for k, v := range data.Value() { + // if strings.HasPrefix(k, fmt.Sprintf("%s_", storageType)) { + // k = k[len(storageType)+1:] + // if len(k) > 0 { + // conf.Set(k, v) + // } + // } + // } + // data.Set("capacity", manager.ValidateRdbConfData(conf)) + // } + return manager.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data) +} + func (self *SStorage) ValidateDeleteCondition(ctx context.Context) error { if self.GetHostCount() > 0 || self.GetDiskCount() > 0 || self.GetSnapshotCount() > 0 { return httperrors.NewNotEmptyError("Not an empty storage provider") diff --git a/pkg/compute/models/wires.go b/pkg/compute/models/wires.go index 3c86babc55..61d1da68e3 100644 --- a/pkg/compute/models/wires.go +++ b/pkg/compute/models/wires.go @@ -11,7 +11,9 @@ import ( "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/util/compare" + "yunion.io/x/pkg/util/netutils" "yunion.io/x/sqlchemy" ) @@ -396,9 +398,35 @@ func (self *SWire) GetCandidatePublicNetwork(isExit bool, serverType string) (*S return ChooseCandidateNetworks(nets, isExit, serverType), nil } +func (self *SWire) GetCandidateNetworkForIp(userCred mcclient.TokenCredential, ipAddr string) (*SNetwork, error) { + ip, err := netutils.NewIPV4Addr(ipAddr) + if err != nil { + return nil, err + } + netPrivates, err := self.getPrivateNetworks(userCred) + if err != nil { + return nil, err + } + for _, net := range netPrivates { + if net.isAddressInRange(ip) { + return &net, nil + } + } + netPublics, err := self.getPublicNetworks() + if err != nil { + return nil, err + } + for _, net := range netPublics { + if net.isAddressInRange(ip) { + return &net, nil + } + } + return nil, nil +} + func chooseNetworkByAddressCount(nets []*SNetwork) (*SNetwork, *SNetwork) { - minCnt := -1 - maxCnt := -1 + minCnt := 65535 + maxCnt := 0 var minSel *SNetwork var maxSel *SNetwork for _, net := range nets { @@ -533,6 +561,19 @@ func (manager *SWireManager) FetchWireById(wireId string) *SWire { return wireObj.(*SWire) } +func (manager *SWireManager) GetWireOfIp(ipAddr string) (*SWire, error) { + net, err := NetworkManager.GetNetworkOfIP(ipAddr, "", tristate.None) + if err != nil { + return nil, err + } + wire := net.GetWire() + if wire != nil { + return wire, nil + } else { + return nil, fmt.Errorf("Wire not found") + } +} + func (manager *SWireManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { q, err := manager.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query) if err != nil { diff --git a/pkg/compute/options/options.go b/pkg/compute/options/options.go index 4557d435ac..e8073288c8 100644 --- a/pkg/compute/options/options.go +++ b/pkg/compute/options/options.go @@ -58,6 +58,10 @@ type ComputeOptions struct { DefaultMaxSnapshotCount int `default:"9" help:"Per Disk max snapshot count, default 9"` DefaultMaxManualSnapshotCount int `default:"2" help:"Per Disk max manual snapshot count, default 2"` + ConvertHypervisorDefaultTemplate string `default:"Default template" help:"Kvm baremetal convert option"` + ConvertEsxiDefaultTemplate string `default:"Default template" help:"ESXI baremetal convert option"` + ConvertKubeletDockerVolumeSize string `default:"256g" help:"Docker volume size"` + cloudcommon.DBOptions } diff --git a/pkg/compute/tasks/baremetal_base_task.go b/pkg/compute/tasks/baremetal_base_task.go new file mode 100644 index 0000000000..532a1b7c12 --- /dev/null +++ b/pkg/compute/tasks/baremetal_base_task.go @@ -0,0 +1,33 @@ +package tasks + +import ( + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type SBaremetalBaseTask struct { + taskman.STask +} + +func (self *SBaremetalBaseTask) Action() string { + actionMap := map[string]string{ + "start": logclient.ACT_VM_START, + "stop": logclient.ACT_VM_STOP, + "maintenance": logclient.ACT_BM_MAINTENANCE, + "unmaintenance": logclient.ACT_BM_UNMAINTENANCE, + } + if self.Params.Contains("action") { + action, _ := self.Params.GetString("action") + self.Params.Remove("action") + if val, ok := actionMap[action]; len(action) > 0 && ok { + return val + } + } + return "" +} + +func (self *SBaremetalBaseTask) GetBaremetal() *models.SHost { + obj := self.GetObject() + return obj.(*models.SHost) +} diff --git a/pkg/compute/tasks/baremetal_convert_hypervisor_task.go b/pkg/compute/tasks/baremetal_convert_hypervisor_task.go index 392a8f7ddd..d3095558c8 100644 --- a/pkg/compute/tasks/baremetal_convert_hypervisor_task.go +++ b/pkg/compute/tasks/baremetal_convert_hypervisor_task.go @@ -2,16 +2,16 @@ package tasks import ( "context" + "fmt" "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/tristate" + "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" -) - -const ( - CONVERT_TASK = "convert_task" + "yunion.io/x/onecloud/pkg/util/logclient" ) type BaremetalConvertHypervisorTask struct { @@ -25,17 +25,12 @@ func init() { func (self *BaremetalConvertHypervisorTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { baremetal := obj.(*models.SHost) - baremetal.SetStatus(self.UserCred, models.HOST_STATUS_CONVERTING, "") + baremetal.SetStatus(self.UserCred, models.BAREMETAL_CONVERTING, "") self.SetStage("on_guest_deploy_complete", nil) guestId, _ := self.Params.GetString("server_id") - guestObj, err := models.GuestManager.FetchById(guestId) - if err != nil { // unlikely - log.Errorf("fail to find server %s", guestId) - return - } - + guestObj, _ := models.GuestManager.FetchById(guestId) guest := guestObj.(*models.SGuest) params, _ := self.Params.Get("server_params") paramsDict := params.(*jsonutils.JSONDict) @@ -43,3 +38,57 @@ func (self *BaremetalConvertHypervisorTask) OnInit(ctx context.Context, obj db.I self.GetPendingUsage(&pendingUsage) guest.StartGuestCreateTask(ctx, self.UserCred, paramsDict, &pendingUsage, self.GetId()) } + +func (self *BaremetalConvertHypervisorTask) OnGuestDeployComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + db.OpsLog.LogEvent(baremetal, db.ACT_CONVERT_COMPLETE, "", self.UserCred) + + guestId, _ := self.Params.GetString("server_id") + guestObj, _ := models.GuestManager.FetchById(guestId) + guest := guestObj.(*models.SGuest) + data, _ := self.Params.Get("server_params") + hypervisor, _ := data.GetString("__convert_host_type__") + driver := models.GetHostDriver(hypervisor) + if driver == nil { + self.SetStageFailed(ctx, fmt.Sprintf("Get Host Driver error %s", hypervisor)) + } + err := driver.FinishConvert(self.UserCred, baremetal, guest, driver.GetHostType()) + if err != nil { + log.Errorln(err) + logclient.AddActionLog(baremetal, logclient.ACT_BM_CONVERT_HYPER, fmt.Sprintf("convert deploy falied %s", err.Error()), self.UserCred, false) + } + logclient.AddActionLog(baremetal, logclient.ACT_BM_CONVERT_HYPER, nil, self.UserCred, true) + self.SetStageComplete(ctx, nil) +} + +func (self *BaremetalConvertHypervisorTask) OnGuestDeployCompleteFailed(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + db.OpsLog.LogEvent(baremetal, db.ACT_CONVERT_FAIL, body, self.UserCred) + guestId, _ := self.Params.GetString("server_id") + guestObj, _ := models.GuestManager.FetchById(guestId) + guest := guestObj.(*models.SGuest) + guest.GetModelManager().TableSpec().Update(guest, func() error { + guest.DisableDelete = tristate.False + return nil + }) + self.SetStage("OnGuestDeleteComplete", nil) + guest.StartDeleteGuestTask(ctx, self.UserCred, self.GetTaskId(), false, true) + logclient.AddActionLog(baremetal, logclient.ACT_BM_CONVERT_HYPER, "convert deploy failed", self.UserCred, false) +} + +func (self *BaremetalConvertHypervisorTask) OnGuestDeleteComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + data, _ := self.Params.Get("server_params") + hypervisor, _ := data.GetString("__convert_host_type__") + driver := models.GetHostDriver(hypervisor) + if driver == nil { + self.SetStageFailed(ctx, fmt.Sprintf("Get Host Driver error %s", hypervisor)) + } + err := driver.ConvertFailed(baremetal) + if err != nil { + logclient.AddActionLog(baremetal, logclient.ACT_BM_CONVERT_HYPER, fmt.Sprintf("convert failed", err.Error()), self.UserCred, false) + } + self.SetStage("OnFailedSyncstatusComplete", nil) + baremetal.StartSyncstatus(ctx, self.UserCred, self.GetTaskId()) +} + +func (self *BaremetalConvertHypervisorTask) OnFailedSyncstatusComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + self.SetStageFailed(ctx, "convert failed") +} diff --git a/pkg/compute/tasks/baremetal_delete_task.go b/pkg/compute/tasks/baremetal_delete_task.go new file mode 100644 index 0000000000..08dd848847 --- /dev/null +++ b/pkg/compute/tasks/baremetal_delete_task.go @@ -0,0 +1,59 @@ +package tasks + +import ( + "context" + "fmt" + "net/http" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/httputils" +) + +type BaremetalDeleteTask struct { + SBaremetalBaseTask +} + +func init() { + taskman.RegisterTask(BaremetalDeleteTask{}) +} + +func (self *BaremetalDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + baremetal := obj.(*models.SHost) + baremetal.SetStatus(self.UserCred, models.BAREMETAL_DELETE, "") + if !baremetal.IsBaremetalAgentReady() { + self.OnDeleteBaremetalComplete(ctx, baremetal, nil) + return + } + url := fmt.Sprintf("/baremetals/%s/delete", baremetal.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", self.UserCred.GetTokenString()) + headers.Set("X-Task-Id", self.GetTaskId()) + self.SetStage("OnDeleteBaremetalComplete", nil) + _, err := baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, nil) + if err != nil { + if errVal, ok := err.(*httputils.JSONClientError); ok && errVal.Code == 404 { + self.OnDeleteBaremetalComplete(ctx, baremetal, nil) + return + } + log.Errorln(err.Error()) + self.OnFailure(ctx, baremetal, nil) + } +} + +func (self *BaremetalDeleteTask) OnDeleteBaremetalComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + baremetal.RealDelete(ctx, self.UserCred) + self.SetStageComplete(ctx, nil) +} + +func (self *BaremetalDeleteTask) OnDeleteBaremetalCompleteFailed(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + self.OnFailure(ctx, baremetal, body) +} + +func (self *BaremetalDeleteTask) OnFailure(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + baremetal.SetStatus(self.UserCred, models.BAREMETAL_DELETE_FAIL, "") + self.SetStageFailed(ctx, "") +} diff --git a/pkg/compute/tasks/baremetal_maintenance_task.go b/pkg/compute/tasks/baremetal_maintenance_task.go new file mode 100644 index 0000000000..4575d9c82f --- /dev/null +++ b/pkg/compute/tasks/baremetal_maintenance_task.go @@ -0,0 +1,73 @@ +package tasks + +import ( + "context" + "fmt" + "net/http" + + "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" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type BaremetalMaintenanceTask struct { + SBaremetalBaseTask +} + +func init() { + taskman.RegisterTask(BaremetalMaintenanceTask{}) +} + +func (self *BaremetalMaintenanceTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + baremetal := obj.(*models.SHost) + url := fmt.Sprintf("/baremetals/%s/maintenance", baremetal.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", self.UserCred.GetTokenString()) + headers.Set("X-Task-Id", self.GetTaskId()) + self.SetStage("OnEnterMaintenantModeSucc", nil) + _, err := baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, self.Params) + if err != nil { + self.OnEnterMaintenantModeSuccFailed(ctx, baremetal, jsonutils.NewString(err.Error())) + } + baremetal.SetStatus(self.UserCred, models.BAREMETAL_MAINTAINING, "") +} + +func (self *BaremetalMaintenanceTask) OnEnterMaintenantModeSucc(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + action := self.Action() + if len(action) > 0 { + logclient.AddActionLog(baremetal, action, "", self.UserCred, true) + } + baremetal.GetModelManager().TableSpec().Update(baremetal, func() error { + baremetal.IsMaintenance = true + return nil + }) + username, _ := body.Get("username") + password, _ := body.Get("password") + ip, _ := body.Get("ip") + metadatas := map[string]interface{}{ + "__maint_username": username, + "__maint_password": password, + "__maint_ip": ip, + } + guestRunning, err := body.Get("guest_running") + if err != nil { + metadatas["__maint_guest_running"] = guestRunning + } + baremetal.SetAllMetadata(ctx, metadatas, self.UserCred) + self.SetStageComplete(ctx, nil) +} + +func (self *BaremetalMaintenanceTask) OnEnterMaintenantModeSuccFailed(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + self.SetStageFailed(ctx, body.String()) + baremetal.StartSyncstatus(ctx, self.UserCred, "") + guest := baremetal.GetBaremetalServer() + if guest != nil { + guest.StartSyncstatus(ctx, self.UserCred, "") + } + action := self.Action() + if len(action) > 0 { + logclient.AddActionLog(baremetal, action, body.String(), self.UserCred, false) + } +} diff --git a/pkg/compute/tasks/baremetal_prepare_task.go b/pkg/compute/tasks/baremetal_prepare_task.go new file mode 100644 index 0000000000..83b9615021 --- /dev/null +++ b/pkg/compute/tasks/baremetal_prepare_task.go @@ -0,0 +1,48 @@ +package tasks + +import ( + "context" + "fmt" + "net/http" + + "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 BaremetalPrepareTask struct { + SBaremetalBaseTask +} + +func init() { + taskman.RegisterTask(BaremetalPrepareTask{}) +} + +func (self *BaremetalPrepareTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + baremetal := obj.(*models.SHost) + url := fmt.Sprintf("/baremetals/%s/prepare", baremetal.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", self.UserCred.GetTokenString()) + headers.Set("X-Task-Id", self.GetTaskId()) + self.SetStage("OnSyncConfigComplete", nil) + _, err := baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, self.Params) + if err != nil { + self.OnFailure(ctx, baremetal, err.Error()) + } +} + +func (self *BaremetalPrepareTask) OnFailure(ctx context.Context, baremetal *models.SHost, reason string) { + baremetal.SetStatus(self.UserCred, models.BAREMETAL_PREPARE_FAIL, reason) + self.SetStageFailed(ctx, reason) +} + +func (self *BaremetalPrepareTask) OnSyncConfigComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + baremetal.ClearSchedDescCache() + self.SetStageComplete(ctx, nil) +} + +func (self *BaremetalPrepareTask) OnSyncConfigCompleteFailed(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + reason, _ := body.GetString("__reason__") + self.OnFailure(ctx, baremetal, reason) +} diff --git a/pkg/compute/tasks/baremetal_server_reset_task.go b/pkg/compute/tasks/baremetal_server_reset_task.go new file mode 100644 index 0000000000..1f5f623163 --- /dev/null +++ b/pkg/compute/tasks/baremetal_server_reset_task.go @@ -0,0 +1,35 @@ +package tasks + +import ( + "context" + "fmt" + "net/http" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type BaremetalServerResetTask struct { + SGuestBaseTask +} + +func (self *BaremetalServerResetTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + baremetal := guest.GetHost() + if baremetal == nil { + self.SetStageFailed(ctx, "Baremetal is None") + return + } + url := fmt.Sprintf("/baremetals/%s/servers/%s/reset", baremetal.Id, guest.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", self.UserCred.GetTokenString()) + _, err := baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, nil) + if err != nil { + log.Errorf(err.Error()) + self.SetStageFailed(ctx, err.Error()) + } else { + self.SetStageComplete(ctx, nil) + } +} diff --git a/pkg/compute/tasks/baremetal_server_start_task.go b/pkg/compute/tasks/baremetal_server_start_task.go new file mode 100644 index 0000000000..90b43230c0 --- /dev/null +++ b/pkg/compute/tasks/baremetal_server_start_task.go @@ -0,0 +1,61 @@ +package tasks + +import ( + "context" + "fmt" + "net/http" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type BaremetalServerStartTask struct { + SGuestBaseTask +} + +func init() { + taskman.RegisterTask(BaremetalServerStartTask{}) +} + +func (self *BaremetalServerStartTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + guest.SetStatus(self.UserCred, models.VM_START_START, "") + db.OpsLog.LogEvent(guest, db.ACT_STARTING, "", self.UserCred) + baremetal := guest.GetHost() + if baremetal == nil { + self.OnStartCompleteFailed(ctx, guest, jsonutils.NewString("Baremetal is None")) + return + } + desc := guest.GetJsonDescAtBaremetal(ctx, baremetal) + config := jsonutils.NewDict() + config.Set("desc", desc) + url := fmt.Sprintf("/baremetals/%s/servers/%s/start", baremetal.Id, guest.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", self.UserCred.GetTokenString()) + headers.Set("X-Task-Id", self.GetTaskId()) + self.SetStage("OnStartComplete", nil) + _, err := baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, config) + if err != nil { + log.Errorln(err) + self.OnStartCompleteFailed(ctx, guest, jsonutils.NewString(err.Error())) + } +} + +func (self *BaremetalServerStartTask) OnStartComplete(ctx context.Context, guest *models.SGuest, body jsonutils.JSONObject) { + guest.SetStatus(self.UserCred, models.VM_RUNNING, "") + baremetal := guest.GetHost() + baremetal.SetStatus(self.UserCred, models.BAREMETAL_RUNNING, "") + db.OpsLog.LogEvent(guest, db.ACT_START, guest.GetShortDesc(), self.UserCred) +} + +func (self *BaremetalServerStartTask) OnStartCompleteFailed(ctx context.Context, guest *models.SGuest, body jsonutils.JSONObject) { + reason := body.String() + guest.SetStatus(self.UserCred, models.VM_START_FAILED, reason) + db.OpsLog.LogEvent(guest, db.ACT_START_FAIL, reason, self.UserCred) + baremetal := guest.GetHost() + baremetal.SetStatus(self.UserCred, models.BAREMETAL_START_FAIL, reason) + self.SetStageFailed(ctx, reason) +} diff --git a/pkg/compute/tasks/baremetal_server_stop_task.go b/pkg/compute/tasks/baremetal_server_stop_task.go new file mode 100644 index 0000000000..40258c42fd --- /dev/null +++ b/pkg/compute/tasks/baremetal_server_stop_task.go @@ -0,0 +1,78 @@ +package tasks + +import ( + "context" + "fmt" + "net/http" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type BaremetalServerStopTask struct { + SGuestBaseTask +} + +func init() { + taskman.RegisterTask(BaremetalServerStopTask{}) +} + +func (self *BaremetalServerStopTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + db.OpsLog.LogEvent(guest, db.ACT_STOPPING, "", self.UserCred) + guest.SetStatus(self.UserCred, models.VM_START_STOP, "") + baremetal := guest.GetHost() + if baremetal != nil { + self.OnStopGuestFail(ctx, guest, "Baremetal is None") + return + } + params := jsonutils.NewDict() + timeout, err := self.Params.Int("timeout") + if err != nil { + timeout = 30 + } + if jsonutils.QueryBoolean(self.Params, "is_force", false) || jsonutils.QueryBoolean(self.Params, "reset", false) { + timeout = 0 + } + params.Set("timeout", jsonutils.NewInt(timeout)) + url := fmt.Sprintf("/baremetals/%s/servers/%s/stop", baremetal.Id, guest.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", self.UserCred.GetTokenString()) + headers.Set("X-Task-Id", self.GetTaskId()) + self.SetStage("OnGuestStopTaskComplete", nil) + _, err = baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, params) + if err != nil { + log.Errorln(err) + self.OnStopGuestFail(ctx, guest, err.Error()) + } +} + +func (self *BaremetalServerStopTask) OnGuestStopTaskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + if guest.Status == models.VM_STOPPING { + guest.SetStatus(self.UserCred, models.VM_READY, "") + db.OpsLog.LogEvent(guest, db.ACT_STOP, "", self.UserCred) + } + baremetal := guest.GetHost() + baremetal.SetStatus(self.UserCred, models.BAREMETAL_READY, "") + self.SetStageComplete(ctx, nil) + if guest.Status == models.VM_READY { + if !jsonutils.QueryBoolean(self.Params, "reset", false) && guest.DisableDelete.IsFalse() && guest.ShutdownBehavior == models.SHUTDOWN_TERMINATE { + guest.StartAutoDeleteGuestTask(ctx, self.UserCred, "") + } + } +} + +func (self *BaremetalServerStopTask) OnGuestStopTaskCompleteFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + guest.SetStatus(self.UserCred, db.ACT_STOP_FAIL, data.String()) + db.OpsLog.LogEvent(guest, db.ACT_STOP_FAIL, data.String(), self.UserCred) + baremetal := guest.GetHost() + baremetal.SetStatus(self.UserCred, models.BAREMETAL_READY, "") + self.SetStageFailed(ctx, data.String()) +} + +func (self *BaremetalServerStopTask) OnStopGuestFail(ctx context.Context, guest *models.SGuest, reason string) { + self.OnGuestStopTaskCompleteFailed(ctx, guest, jsonutils.NewString(reason)) +} diff --git a/pkg/compute/tasks/baremetal_server_sync_status_task.go b/pkg/compute/tasks/baremetal_server_sync_status_task.go new file mode 100644 index 0000000000..8e07cb1ce2 --- /dev/null +++ b/pkg/compute/tasks/baremetal_server_sync_status_task.go @@ -0,0 +1,79 @@ +package tasks + +import ( + "context" + "fmt" + "net/http" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type BaremetalServerSyncStatusTask struct { + SGuestBaseTask +} + +func init() { + taskman.RegisterTask(BaremetalServerSyncStatusTask{}) +} + +func (self *BaremetalServerSyncStatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + baremetal := guest.GetHost() + if baremetal == nil { + kwargs := jsonutils.NewDict() + kwargs.Set("status", jsonutils.NewString(models.VM_INIT)) + guest.PerformStatus(ctx, self.UserCred, nil, kwargs) + self.SetStageComplete(ctx, nil) + return + } + url := fmt.Sprintf("/baremetals/%s/servers/%s/status", baremetal.Id, guest.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", self.GetUserCred().GetTokenString()) + headers.Set("X-Task-Id", self.GetId()) + self.SetStage("OnGuestStatusTaskComplete", nil) + _, err := baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, nil) + if err != nil { + log.Errorln(err) + self.OnGetStatusFail(ctx, guest) + } +} + +func (self *BaremetalServerSyncStatusTask) OnGuestStatusTaskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + var status string + if data.Contains("status") { + statusStr, _ := data.GetString("status") + switch statusStr { + case "running": + status = models.VM_RUNNING + case "stopped", "ready": + status = models.VM_READY + case "admin": + status = models.VM_ADMIN + default: + status = models.VM_INIT + } + } else { + status = models.VM_UNKNOWN + } + kwargs := jsonutils.NewDict() + kwargs.Set("status", jsonutils.NewString(status)) + guest.PerformStatus(ctx, self.UserCred, nil, kwargs) + self.SetStageComplete(ctx, nil) +} + +func (self *BaremetalServerSyncStatusTask) OnGuestStatusTaskCompleteFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + kwargs := jsonutils.NewDict() + kwargs.Set("status", jsonutils.NewString(models.VM_UNKNOWN)) + guest.PerformStatus(ctx, self.UserCred, nil, kwargs) +} + +func (self *BaremetalServerSyncStatusTask) OnGetStatusFail(ctx context.Context, guest *models.SGuest) { + kwargs := jsonutils.NewDict() + kwargs.Set("status", jsonutils.NewString(models.VM_UNKNOWN)) + guest.PerformStatus(ctx, self.UserCred, nil, kwargs) + self.SetStageComplete(ctx, nil) +} diff --git a/pkg/compute/tasks/baremetal_sync_status_task.go b/pkg/compute/tasks/baremetal_sync_status_task.go new file mode 100644 index 0000000000..0fbaf408d1 --- /dev/null +++ b/pkg/compute/tasks/baremetal_sync_status_task.go @@ -0,0 +1,68 @@ +package tasks + +import ( + "context" + "fmt" + "net/http" + + "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 BaremetalSyncStatusTask struct { + SBaremetalBaseTask +} + +func (self *BaremetalSyncStatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + baremetal := obj.(*models.SHost) + if baremetal.IsBaremetal { + self.DoSyncStatus(ctx, baremetal) + } else { + self.SetStageComplete(ctx, nil) + } +} + +func (self *BaremetalSyncStatusTask) DoSyncStatus(ctx context.Context, baremetal *models.SHost) { + url := fmt.Sprintf("/baremetals/%s/syncstatus", baremetal.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", self.UserCred.GetTokenString()) + _, err := baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, nil) + if err == nil { + self.SetStageComplete(ctx, nil) + } else { + self.SetStageFailed(ctx, err.Error()) + } +} + +type BaremetalSyncAllGuestsStatusTask struct { + SBaremetalBaseTask +} + +func (self *BaremetalSyncAllGuestsStatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + baremetal := obj.(*models.SHost) + guest := baremetal.GetBaremetalServer() + if guest != nil { + + } + self.SetStage("OnGuestSyncStatusComplete", nil) + self.OnGuestSyncStatusComplete(ctx, baremetal, nil) +} + +func (self *BaremetalSyncAllGuestsStatusTask) OnGuestSyncStatusComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + guests := baremetal.GetGuests() + for _, guest := range guests { + if guest.Status == models.VM_UNKNOWN && guest.Hypervisor == models.HYPERVISOR_BAREMETAL { + guest.SetStatus(self.UserCred, models.VM_SYNCING_STATUS, "") + // GuestBatchSyncStatusTask + guest.StartSyncstatus(ctx, self.UserCred, "") + } + } + self.SetStageComplete(ctx, nil) +} + +func init() { + taskman.RegisterTask(BaremetalSyncStatusTask{}) + taskman.RegisterTask(BaremetalSyncAllGuestsStatusTask{}) +} diff --git a/pkg/compute/tasks/baremetal_unconvert_hypervisor_task.go b/pkg/compute/tasks/baremetal_unconvert_hypervisor_task.go new file mode 100644 index 0000000000..ab225b4f7e --- /dev/null +++ b/pkg/compute/tasks/baremetal_unconvert_hypervisor_task.go @@ -0,0 +1,62 @@ +package tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type BaremetalUnconvertHypervisorTask struct { + SBaremetalBaseTask +} + +func init() { + taskman.RegisterTask(BaremetalUnconvertHypervisorTask{}) +} + +func (self *BaremetalUnconvertHypervisorTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + baremetal := obj.(*models.SHost) + baremetal.SetStatus(self.UserCred, models.BAREMETAL_CONVERTING, "") + guests := baremetal.GetGuests() + if len(guests) > 1 { + self.SetStageFailed(ctx, "Host guest conut > 1") + } + if len(guests) == 1 { + guest := guests[0] + self.SetStage("OnGuestDeleteComplete", nil) + guest.StartDeleteGuestTask(ctx, self.UserCred, self.GetTaskId(), false, true) + } else { + self.OnGuestDeleteComplete(ctx, baremetal, nil) + } +} + +func (self *BaremetalUnconvertHypervisorTask) OnGuestDeleteComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + db.OpsLog.LogEvent(baremetal, db.ACT_UNCONVERT_COMPLETE, "", self.UserCred) + driver := baremetal.GetDriverWithDefault() + err := driver.FinishUnconvert(ctx, self.UserCred, baremetal) + if err != nil { + log.Errorf("Fail to exec finish_unconvert: %s", err.Error()) + } + self.SetStage("OnPrepareComplete", nil) + baremetal.StartPrepareTask(ctx, self.UserCred, "", self.GetTaskId()) + logclient.AddActionLog(baremetal, logclient.ACT_BM_UNCONVERT_HYPER, nil, self.UserCred, true) +} + +func (self *BaremetalUnconvertHypervisorTask) OnGuestDeleteCompleteFailed(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + db.OpsLog.LogEvent(baremetal, db.ACT_UNCONVERT_FAIL, body, self.UserCred) + self.SetStage("OnFailSyncstatusComplete", nil) + baremetal.StartSyncstatus(ctx, self.UserCred, self.GetTaskId()) + logclient.AddActionLog(baremetal, logclient.ACT_BM_UNCONVERT_HYPER, nil, self.UserCred, false) +} + +func (self *BaremetalUnconvertHypervisorTask) OnPrepareComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + self.SetStageComplete(ctx, nil) +} +func (self *BaremetalUnconvertHypervisorTask) OnFailSyncstatusComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + self.SetStageFailed(ctx, "Delete server failed") +} diff --git a/pkg/compute/tasks/baremetal_unmaintenance_task.go b/pkg/compute/tasks/baremetal_unmaintenance_task.go new file mode 100644 index 0000000000..992dd07c70 --- /dev/null +++ b/pkg/compute/tasks/baremetal_unmaintenance_task.go @@ -0,0 +1,61 @@ +package tasks + +import ( + "context" + "fmt" + "net/http" + + "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" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type BaremetalUnmaintenanceTask struct { + SBaremetalBaseTask +} + +func init() { + taskman.RegisterTask(BaremetalUnmaintenanceTask{}) +} + +func (self *BaremetalUnmaintenanceTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + baremetal := obj.(*models.SHost) + url := fmt.Sprintf("/baremetals/%s/unmaintenance", baremetal.Id) + headers := http.Header{} + headers.Set("X-Auth-Token", self.UserCred.GetTokenString()) + headers.Set("X-Task-Id", self.GetTaskId()) + self.SetStage("OnUnmaintenantComplete", nil) + action := self.Action() + _, err := baremetal.BaremetalSyncRequest(ctx, "POST", url, headers, self.Params) + if err != nil { + if len(action) > 0 { + msg := fmt.Sprintf("unmaintenance error %s", err.Error()) + logclient.AddActionLog(baremetal, action, msg, self.UserCred, false) + } + self.SetStageFailed(ctx, err.Error()) + } else { + if len(action) > 0 { + logclient.AddActionLog(baremetal, action, "", self.UserCred, true) + } + } +} + +func (self *BaremetalUnmaintenanceTask) OnUnmaintenantComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) { + baremetal.GetModelManager().TableSpec().Update(baremetal, func() error { + baremetal.IsMaintenance = false + return nil + }) + metadatas := map[string]interface{}{ + "__maint_username": "None", + "__maint_password": "None", + "__maint_ip": "None", + } + baremetal.SetAllMetadata(ctx, metadatas, self.UserCred) + self.SetStageComplete(ctx, nil) + guest := baremetal.GetBaremetalServer() + if guest != nil { + guest.StartSyncstatus(ctx, self.UserCred, "") + } +} diff --git a/pkg/httperrors/errors.go b/pkg/httperrors/errors.go index 90927a6a10..e574cd6656 100644 --- a/pkg/httperrors/errors.go +++ b/pkg/httperrors/errors.go @@ -170,6 +170,11 @@ func NewDuplicateNameError(resName string, resId string) *httputils.JSONClientEr return NewJsonClientError(409, "DuplicateNameError", msg, err) } +func NewDuplicateResourceError(msg string, params ...interface{}) *httputils.JSONClientError { + msg, err := errorMessage(msg, params) + return NewJsonClientError(409, "DuplicateResourceError", msg, err) +} + func NewConflictError(msg string, params ...interface{}) *httputils.JSONClientError { msg, err := errorMessage(msg, params...) return NewJsonClientError(409, "ConflictError", msg, err) diff --git a/pkg/mcclient/auth/auth.go b/pkg/mcclient/auth/auth.go index 3712bfd378..58a50b4a07 100644 --- a/pkg/mcclient/auth/auth.go +++ b/pkg/mcclient/auth/auth.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/util/cache" @@ -192,6 +193,10 @@ func (a *authManager) init() error { return nil } +func GetCatalogData(serviceTypes []string, region string) jsonutils.JSONObject { + return manager.adminCredential.GetCatalogData(serviceTypes, region) +} + func Verify(tokenId string) (mcclient.TokenCredential, error) { return manager.verify(tokenId) } diff --git a/pkg/mcclient/modules/mod_endpoints.go b/pkg/mcclient/modules/mod_endpoints.go index a2c569dc1a..180245dd0d 100644 --- a/pkg/mcclient/modules/mod_endpoints.go +++ b/pkg/mcclient/modules/mod_endpoints.go @@ -17,7 +17,7 @@ func init() { EndpointsV3 = NewIdentityV3Manager("endpoint", "endpoints", []string{}, []string{"ID", "Region_ID", - "Service_ID", + "Service_ID", "name", "URL", "Interface", "Enabled"}) register(&EndpointsV3) diff --git a/pkg/mcclient/modules/mod_networks.go b/pkg/mcclient/modules/mod_networks.go index 0134005a64..d80674e00d 100644 --- a/pkg/mcclient/modules/mod_networks.go +++ b/pkg/mcclient/modules/mod_networks.go @@ -9,7 +9,7 @@ func init() { []string{"ID", "Name", "Guest_ip_start", "Guest_ip_end", "Guest_ip_mask", "wire_id", "wire", "is_public", "exit", "Ports", - "vnics", + "vnics", "guest_gateway", "group_vnics", "bm_vnics", "reserve_vnics", "server_type", "Status"}, []string{}) diff --git a/pkg/mcclient/token.go b/pkg/mcclient/token.go index a7ca1b1afd..c7eed77a2b 100644 --- a/pkg/mcclient/token.go +++ b/pkg/mcclient/token.go @@ -3,6 +3,7 @@ package mcclient import ( "time" + "yunion.io/x/jsonutils" "yunion.io/x/pkg/gotypes" ) @@ -48,6 +49,7 @@ type TokenCredential interface { GetRegions() []string GetServiceCatalog() IServiceCatalog + GetCatalogData(serviceTypes []string, region string) jsonutils.JSONObject GetInternalServices(region string) []string GetExternalServices(region string) []ExternalService diff --git a/pkg/mcclient/token2.go b/pkg/mcclient/token2.go index daca173d17..109b0e4bab 100644 --- a/pkg/mcclient/token2.go +++ b/pkg/mcclient/token2.go @@ -5,6 +5,8 @@ import ( "math/rand" "strings" "time" + + "yunion.io/x/jsonutils" ) type KeystoneEndpointV2 struct { @@ -246,6 +248,10 @@ func (ep KeystoneEndpointV2) getURL(epType string) string { } } +func (self *TokenCredentialV2) GetCatalogData(serviceTypes []string, region string) jsonutils.JSONObject { + return jsonutils.Marshal(self.GetServiceCatalog()) +} + func (self *TokenCredentialV2) String() string { token := SimplifyToken(self) return token.String() diff --git a/pkg/mcclient/token3.go b/pkg/mcclient/token3.go index 233c336be2..5502571bf6 100644 --- a/pkg/mcclient/token3.go +++ b/pkg/mcclient/token3.go @@ -5,6 +5,9 @@ import ( "math/rand" "strings" "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/utils" ) const REGION_ZONE_SEP = '-' @@ -308,6 +311,31 @@ func (catalog KeystoneServiceCatalogV3) GetServiceURLs(service, region, zone, en return nil, fmt.Errorf("No such service %s", service) } +func (self *TokenCredentialV3) GetCatalogData(serviceTypes []string, region string) jsonutils.JSONObject { + catalog := self.Token.Catalog + ret := make([]map[string]interface{}, 0) + for i := 0; i < len(catalog); i++ { + if !utils.IsInStringArray(catalog[i].Type, serviceTypes) { + continue + } + neps := make([]KeystoneEndpointV3, 0) + for j := 0; j < len(catalog[i].Endpoints); j++ { + if catalog[i].Endpoints[j].Region != region { + continue + } + neps = append(neps, catalog[i].Endpoints[j]) + } + if len(neps) > 0 { + data := map[string]interface{}{ + "type": catalog[i].Type, + "endpoints": neps, + } + ret = append(ret, data) + } + } + return jsonutils.Marshal(ret) +} + func (self *TokenCredentialV3) String() string { token := SimplifyToken(self) return token.String() diff --git a/pkg/mcclient/tokensimple.go b/pkg/mcclient/tokensimple.go index 5be331fbd9..308de2138a 100644 --- a/pkg/mcclient/tokensimple.go +++ b/pkg/mcclient/tokensimple.go @@ -133,6 +133,10 @@ func SimplifyToken(token TokenCredential) TokenCredential { } } +func (self *SSimpleToken) GetCatalogData(serviceTypes []string, region string) jsonutils.JSONObject { + return nil +} + func (self *SSimpleToken) String() string { return jsonutils.Marshal(self).String() } diff --git a/vendor/yunion.io/x/sqlchemy/conditions.go b/vendor/yunion.io/x/sqlchemy/conditions.go index 0a40b89469..d779ac4149 100644 --- a/vendor/yunion.io/x/sqlchemy/conditions.go +++ b/vendor/yunion.io/x/sqlchemy/conditions.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" + "yunion.io/x/log" "yunion.io/x/pkg/util/reflectutils" ) @@ -213,7 +214,9 @@ func tupleConditionWhereClause(t *STupleCondition, op string) string { } func questionMark(count int) string { - if count == 1 { + if count == 0 { + return "" + } else if count == 1 { return "( ? )" } else { var buf bytes.Buffer @@ -273,8 +276,19 @@ type SInCondition struct { STupleCondition } +func inConditionWhereClause(t *STupleCondition, op string) string { + v := varConditionWhereClause(t.right) + if len(v) == 0 { + log.Warningf("The In condition on %s was invoked with an empty sequence, "+ + "consider alternative strategies for improved performance !!!", t.left.Name()) + return NotEquals(t.left, t.left).WhereClause() + } else { + return tupleConditionWhereClause(t, op) + } +} + func (t *SInCondition) WhereClause() string { - return tupleConditionWhereClause(&t.STupleCondition, SQL_OP_IN) + return inConditionWhereClause(&t.STupleCondition, SQL_OP_IN) } func In(f IQueryField, v interface{}) ICondition {