Files
cloudpods/pkg/compute/models/isolated_device_models.go

328 lines
11 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"regexp"
"strconv"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/tristate"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)
var IsolatedDeviceModelManager *SIsolatedDeviceModelManager
func init() {
IsolatedDeviceModelManager = &SIsolatedDeviceModelManager{
SStandaloneAnonResourceBaseManager: db.NewStandaloneAnonResourceBaseManager(
SIsolatedDeviceModel{},
"isolated_device_models_tbl",
"isolated_device_model",
"isolated_device_models",
),
}
IsolatedDeviceModelManager.SetVirtualObject(IsolatedDeviceModelManager)
}
type SIsolatedDeviceModelManager struct {
db.SStandaloneAnonResourceBaseManager
}
type SIsolatedDeviceModel struct {
db.SStandaloneAnonResourceBase
Model string `width:"512" charset:"ascii" nullable:"false" list:"domain" create:"domain_required" update:"domain"`
VendorId string `width:"16" charset:"ascii" nullable:"false" list:"domain" create:"domain_required" update:"domain"`
DeviceId string `width:"16" charset:"ascii" nullable:"false" list:"domain" create:"domain_required" update:"domain"`
DevType string `width:"16" charset:"ascii" nullable:"false" list:"domain" create:"domain_required"`
HotPluggable tristate.TriState `default:"false" list:"domain" create:"domain_optional" update:"domain"`
// Disable auto detect isolated devices on host
DisableAutoDetect tristate.TriState `default:"false" list:"domain" create:"domain_optional" update:"domain"`
}
var _ db.IStandaloneModel = new(SIsolatedDeviceModel)
func (self *SIsolatedDeviceModel) SetName(name string) {}
func (manager *SIsolatedDeviceModelManager) ValidateCreateData(ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
input api.IsolatedDeviceModelCreateInput,
) (api.IsolatedDeviceModelCreateInput, error) {
input.VendorId = strings.ToLower(input.VendorId)
input.DeviceId = strings.ToLower(input.DeviceId)
deviceVendorReg := regexp.MustCompile(`^[a-f0-9]{4}$`)
if !deviceVendorReg.MatchString(input.VendorId) {
return input, httperrors.NewInputParameterError("bad vendor id %s", input.VendorId)
}
if !deviceVendorReg.MatchString(input.DeviceId) {
return input, httperrors.NewInputParameterError("bad vendor id %s", input.DeviceId)
}
if cnt := manager.Query().Equals("vendor_id", input.VendorId).Equals("device_id", input.DeviceId).Count(); cnt > 0 {
return input, httperrors.NewDuplicateResourceError("vendor %s device %s has been registered", input.VendorId, input.DeviceId)
}
if cnt := manager.Query().Equals("model", input.Model).Count(); cnt > 0 {
return input, httperrors.NewDuplicateResourceError("model %s has been registered", input.Model)
}
return input, nil
}
func (self *SIsolatedDeviceModel) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
input := api.IsolatedDeviceModelCreateInput{}
err := data.Unmarshal(&input)
if err != nil {
log.Errorf("!!!data.Unmarshal api.IsolatedDeviceModelCreateInput fail %s", err)
}
go func() {
for i := range input.Hosts {
iHost, err := HostManager.FetchByIdOrName(ctx, userCred, input.Hosts[i])
if err != nil {
log.Errorf("failed fetch host %s: %s", input.Hosts[i], err)
continue
}
host := iHost.(*SHost)
if self.DisableAutoDetect.Bool() {
err = self.Attach2Host(ctx, userCred, host)
if err != nil {
log.Errorf("failed attach to host %s: %s", input.Hosts[i], err)
continue
}
}
log.Infof("start request host %s scan isolated devices", host.GetName())
if err := host.RequestScanIsolatedDevices(ctx, userCred); err != nil {
log.Errorf("failed scan isolated device %s", err)
}
}
}()
}
func (self *SIsolatedDeviceModel) Attach2Host(ctx context.Context, userCred mcclient.TokenCredential, host *SHost) error {
hs := SHostIsolatedDeviceModel{}
hs.SetModelManager(HostIsolatedDeviceModelManager, &hs)
hs.IsolatedDeviceModelId = self.Id
hs.HostId = host.Id
err := HostIsolatedDeviceModelManager.TableSpec().Insert(ctx, &hs)
if err != nil {
return err
}
db.OpsLog.LogAttachEvent(ctx, host, self, userCred, nil)
return nil
}
func (self *SIsolatedDeviceModel) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
modelIsEmpty, err := IsolatedDeviceManager.CheckModelIsEmpty(self.Model, self.VendorId, self.DeviceId, self.DevType)
if err != nil {
return err
}
if !modelIsEmpty {
return httperrors.NewNotEmptyError("device model has guests")
}
return nil
}
func (self *SIsolatedDeviceModel) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
hosts, err := IsolatedDeviceManager.GetHostsByModel(self.Model, self.VendorId, self.DeviceId, self.DevType)
if err != nil {
log.Errorf("failed get hosts by isolated device model: %s", err)
return
}
go func() {
for i := range hosts {
iHost, err := HostManager.FetchByIdOrName(ctx, userCred, hosts[i])
if err != nil {
log.Errorf("failed fetch host %s: %s", hosts[i], err)
continue
}
host := iHost.(*SHost)
log.Infof("start request host %s scan isolated devices", host.GetName())
if err := host.RequestScanIsolatedDevices(ctx, userCred); err != nil {
log.Errorf("failed scan isolated device %s", err)
}
}
}()
}
func (self *SIsolatedDeviceModel) ValidateUpdateData(
ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, input api.IsolatedDeviceModelUpdateInput,
) (api.IsolatedDeviceModelUpdateInput, error) {
input.VendorId = strings.ToLower(input.VendorId)
input.DeviceId = strings.ToLower(input.DeviceId)
deviceVendorReg := regexp.MustCompile(`^[a-f0-9]{4}$`)
if input.VendorId != "" && !deviceVendorReg.MatchString(input.VendorId) {
return input, httperrors.NewInputParameterError("bad vendor id %s", input.VendorId)
}
if input.DeviceId != "" && !deviceVendorReg.MatchString(input.DeviceId) {
return input, httperrors.NewInputParameterError("bad vendor id %s", input.DeviceId)
}
if input.VendorId != "" || input.DeviceId != "" {
if input.VendorId == "" {
input.VendorId = self.VendorId
}
if input.DeviceId == "" {
input.DeviceId = self.DeviceId
}
if self.VendorId != input.VendorId || self.DeviceId != input.DeviceId {
if cnt := IsolatedDeviceModelManager.Query().Equals("vendor_id", input.VendorId).Equals("device_id", input.DeviceId).Count(); cnt > 0 {
return input, httperrors.NewDuplicateResourceError("vendor %s device %s has been registered", input.VendorId, input.DeviceId)
}
}
}
if self.Model != input.Model {
if cnt := IsolatedDeviceModelManager.Query().Equals("model", input.Model).Count(); cnt > 0 {
return input, httperrors.NewDuplicateResourceError("model %s has been registered", input.Model)
}
}
return input, nil
}
func (manager *SIsolatedDeviceModelManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.IsolatedDeviceModelListInput,
) (*sqlchemy.SQuery, error) {
q, err := manager.SStandaloneAnonResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StandaloneAnonResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemFilter")
}
if len(query.DevType) > 0 {
q = q.In("dev_type", query.DevType)
}
if len(query.Model) > 0 {
q = q.In("model", query.Model)
}
if len(query.VendorId) > 0 {
q = q.Equals("vendor_id", query.VendorId)
}
if len(query.DeviceId) > 0 {
q = q.Equals("device_id", query.DeviceId)
}
if len(query.HostId) > 0 {
hidmq := HostIsolatedDeviceModelManager.Query("isolated_device_model_id").Equals("host_id", query.HostId).SubQuery()
q = q.Filter(sqlchemy.OR(
sqlchemy.IsFalse(q.Field("disable_auto_detect")),
sqlchemy.IsNull(q.Field("disable_auto_detect")),
sqlchemy.In(q.Field("id"), hidmq)),
)
}
return q, nil
}
func (manager *SIsolatedDeviceModelManager) GetByVendorDevice(vendorId, deviceId string) (*SIsolatedDeviceModel, error) {
devModel := new(SIsolatedDeviceModel)
err := manager.Query().Equals("vendor_id", vendorId).Equals("device_id", deviceId).First(devModel)
if err != nil {
return nil, err
}
devModel.SetModelManager(manager, devModel)
return devModel, nil
}
func (manager *SIsolatedDeviceModelManager) GetByDevModel(model string) (*SIsolatedDeviceModel, error) {
devModel := new(SIsolatedDeviceModel)
err := manager.Query().Equals("model", model).First(devModel)
if err != nil {
return nil, err
}
devModel.SetModelManager(manager, devModel)
return devModel, nil
}
func (manager *SIsolatedDeviceModelManager) GetByDevType(devType string) (*SIsolatedDeviceModel, error) {
devModel := new(SIsolatedDeviceModel)
err := manager.Query().Equals("dev_type", devType).First(devModel)
if err != nil {
return nil, err
}
devModel.SetModelManager(manager, devModel)
return devModel, nil
}
func (obj *SIsolatedDeviceModel) PerformSetHardwareInfo(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, data *api.IsolatedDeviceModelHardwareInfo) (*api.IsolatedDeviceModelHardwareInfo, error) {
settings := map[string]interface{}{
api.ISOLATED_DEVICE_MODEL_METADATA_MEMORY_MB: data.MemoryMB,
api.ISOLATED_DEVICE_MODEL_METADATA_TFLOPS: data.TFLOPS,
api.ISOLATED_DEVICE_MODEL_METADATA_BANDWIDTH: data.Bandwidth,
}
for k, v := range settings {
if err := obj.SetMetadata(ctx, k, v, userCred); err != nil {
return nil, errors.Wrapf(err, "set %s to %v", k, v)
}
}
return data, nil
}
func (obj *SIsolatedDeviceModel) GetDetailsHardwareInfo(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject) (*api.IsolatedDeviceModelHardwareInfo, error) {
info := new(api.IsolatedDeviceModelHardwareInfo)
// parse memory size MB
if memStr := obj.GetMetadata(ctx, api.ISOLATED_DEVICE_MODEL_METADATA_MEMORY_MB, userCred); memStr != "" {
memSize, err := strconv.Atoi(memStr)
if err != nil {
return nil, errors.Wrapf(err, "convert memory size %d to int", memSize)
}
info.MemoryMB = memSize
}
// parse bandwidth
if bwStr := obj.GetMetadata(ctx, api.ISOLATED_DEVICE_MODEL_METADATA_BANDWIDTH, userCred); bwStr != "" {
bw, err := strconv.ParseFloat(bwStr, 64)
if err != nil {
return nil, errors.Wrapf(err, "convert bandwidth %s to float", bwStr)
}
info.Bandwidth = bw
}
// parse TFLOPS
if tflopsStr := obj.GetMetadata(ctx, api.ISOLATED_DEVICE_MODEL_METADATA_TFLOPS, userCred); tflopsStr != "" {
tflops, err := strconv.ParseFloat(tflopsStr, 64)
if err != nil {
return nil, errors.Wrapf(err, "convert TFLOPS %s to float", tflopsStr)
}
info.TFLOPS = tflops
}
return info, nil
}