isolated_device_models.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package models
  15. import (
  16. "context"
  17. "regexp"
  18. "strconv"
  19. "strings"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/tristate"
  24. "yunion.io/x/pkg/utils"
  25. "yunion.io/x/sqlchemy"
  26. api "yunion.io/x/onecloud/pkg/apis/compute"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  28. "yunion.io/x/onecloud/pkg/httperrors"
  29. "yunion.io/x/onecloud/pkg/mcclient"
  30. )
  31. var IsolatedDeviceModelManager *SIsolatedDeviceModelManager
  32. func init() {
  33. IsolatedDeviceModelManager = &SIsolatedDeviceModelManager{
  34. SStandaloneAnonResourceBaseManager: db.NewStandaloneAnonResourceBaseManager(
  35. SIsolatedDeviceModel{},
  36. "isolated_device_models_tbl",
  37. "isolated_device_model",
  38. "isolated_device_models",
  39. ),
  40. }
  41. IsolatedDeviceModelManager.SetVirtualObject(IsolatedDeviceModelManager)
  42. }
  43. type SIsolatedDeviceModelManager struct {
  44. db.SStandaloneAnonResourceBaseManager
  45. }
  46. type SIsolatedDeviceModel struct {
  47. db.SStandaloneAnonResourceBase
  48. Model string `width:"512" charset:"ascii" nullable:"false" list:"domain" create:"domain_required" update:"domain"`
  49. VendorId string `width:"16" charset:"ascii" nullable:"false" list:"domain" create:"domain_required" update:"domain"`
  50. DeviceId string `width:"16" charset:"ascii" nullable:"false" list:"domain" create:"domain_required" update:"domain"`
  51. DevType string `width:"16" charset:"ascii" nullable:"false" list:"domain" create:"domain_required"`
  52. HotPluggable tristate.TriState `default:"false" list:"domain" create:"domain_optional" update:"domain"`
  53. // Disable auto detect isolated devices on host
  54. DisableAutoDetect tristate.TriState `default:"false" list:"domain" create:"domain_optional" update:"domain"`
  55. }
  56. var _ db.IStandaloneModel = new(SIsolatedDeviceModel)
  57. func (self *SIsolatedDeviceModel) SetName(name string) {}
  58. func (manager *SIsolatedDeviceModelManager) ValidateCreateData(ctx context.Context,
  59. userCred mcclient.TokenCredential,
  60. ownerId mcclient.IIdentityProvider,
  61. query jsonutils.JSONObject,
  62. input api.IsolatedDeviceModelCreateInput,
  63. ) (api.IsolatedDeviceModelCreateInput, error) {
  64. if utils.IsInStringArray(input.DevType, api.VALID_PASSTHROUGH_TYPES) {
  65. return input, httperrors.NewInputParameterError("device type %q unsupported", input.DevType)
  66. }
  67. input.VendorId = strings.ToLower(input.VendorId)
  68. input.DeviceId = strings.ToLower(input.DeviceId)
  69. deviceVendorReg := regexp.MustCompile(`^[a-f0-9]{4}$`)
  70. if !deviceVendorReg.MatchString(input.VendorId) {
  71. return input, httperrors.NewInputParameterError("bad vendor id %s", input.VendorId)
  72. }
  73. if !deviceVendorReg.MatchString(input.DeviceId) {
  74. return input, httperrors.NewInputParameterError("bad vendor id %s", input.DeviceId)
  75. }
  76. if cnt := manager.Query().Equals("vendor_id", input.VendorId).Equals("device_id", input.DeviceId).Count(); cnt > 0 {
  77. return input, httperrors.NewDuplicateResourceError("vendor %s device %s has been registered", input.VendorId, input.DeviceId)
  78. }
  79. if cnt := manager.Query().Equals("model", input.Model).Count(); cnt > 0 {
  80. return input, httperrors.NewDuplicateResourceError("model %s has been registered", input.Model)
  81. }
  82. return input, nil
  83. }
  84. func (self *SIsolatedDeviceModel) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  85. input := api.IsolatedDeviceModelCreateInput{}
  86. err := data.Unmarshal(&input)
  87. if err != nil {
  88. log.Errorf("!!!data.Unmarshal api.IsolatedDeviceModelCreateInput fail %s", err)
  89. }
  90. go func() {
  91. for i := range input.Hosts {
  92. iHost, err := HostManager.FetchByIdOrName(ctx, userCred, input.Hosts[i])
  93. if err != nil {
  94. log.Errorf("failed fetch host %s: %s", input.Hosts[i], err)
  95. continue
  96. }
  97. host := iHost.(*SHost)
  98. if self.DisableAutoDetect.Bool() {
  99. err = self.Attach2Host(ctx, userCred, host)
  100. if err != nil {
  101. log.Errorf("failed attach to host %s: %s", input.Hosts[i], err)
  102. continue
  103. }
  104. }
  105. log.Infof("start request host %s scan isolated devices", host.GetName())
  106. if err := host.RequestScanIsolatedDevices(ctx, userCred); err != nil {
  107. log.Errorf("failed scan isolated device %s", err)
  108. }
  109. }
  110. }()
  111. }
  112. func (self *SIsolatedDeviceModel) Attach2Host(ctx context.Context, userCred mcclient.TokenCredential, host *SHost) error {
  113. hs := SHostIsolatedDeviceModel{}
  114. hs.SetModelManager(HostIsolatedDeviceModelManager, &hs)
  115. hs.IsolatedDeviceModelId = self.Id
  116. hs.HostId = host.Id
  117. err := HostIsolatedDeviceModelManager.TableSpec().Insert(ctx, &hs)
  118. if err != nil {
  119. return err
  120. }
  121. db.OpsLog.LogAttachEvent(ctx, host, self, userCred, nil)
  122. return nil
  123. }
  124. func (self *SIsolatedDeviceModel) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
  125. modelIsEmpty, err := IsolatedDeviceManager.CheckModelIsEmpty(self.Model, self.VendorId, self.DeviceId, self.DevType)
  126. if err != nil {
  127. return err
  128. }
  129. if !modelIsEmpty {
  130. return httperrors.NewNotEmptyError("device model has guests")
  131. }
  132. return nil
  133. }
  134. func (self *SIsolatedDeviceModel) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
  135. hosts, err := IsolatedDeviceManager.GetHostsByModel(self.Model, self.VendorId, self.DeviceId, self.DevType)
  136. if err != nil {
  137. log.Errorf("failed get hosts by isolated device model: %s", err)
  138. return
  139. }
  140. go func() {
  141. for i := range hosts {
  142. iHost, err := HostManager.FetchByIdOrName(ctx, userCred, hosts[i])
  143. if err != nil {
  144. log.Errorf("failed fetch host %s: %s", hosts[i], err)
  145. continue
  146. }
  147. host := iHost.(*SHost)
  148. log.Infof("start request host %s scan isolated devices", host.GetName())
  149. if err := host.RequestScanIsolatedDevices(ctx, userCred); err != nil {
  150. log.Errorf("failed scan isolated device %s", err)
  151. }
  152. }
  153. }()
  154. }
  155. func (self *SIsolatedDeviceModel) ValidateUpdateData(
  156. ctx context.Context, userCred mcclient.TokenCredential,
  157. query jsonutils.JSONObject, input api.IsolatedDeviceModelUpdateInput,
  158. ) (api.IsolatedDeviceModelUpdateInput, error) {
  159. input.VendorId = strings.ToLower(input.VendorId)
  160. input.DeviceId = strings.ToLower(input.DeviceId)
  161. deviceVendorReg := regexp.MustCompile(`^[a-f0-9]{4}$`)
  162. if input.VendorId != "" && !deviceVendorReg.MatchString(input.VendorId) {
  163. return input, httperrors.NewInputParameterError("bad vendor id %s", input.VendorId)
  164. }
  165. if input.DeviceId != "" && !deviceVendorReg.MatchString(input.DeviceId) {
  166. return input, httperrors.NewInputParameterError("bad vendor id %s", input.DeviceId)
  167. }
  168. if input.VendorId != "" || input.DeviceId != "" {
  169. if input.VendorId == "" {
  170. input.VendorId = self.VendorId
  171. }
  172. if input.DeviceId == "" {
  173. input.DeviceId = self.DeviceId
  174. }
  175. if self.VendorId != input.VendorId || self.DeviceId != input.DeviceId {
  176. if cnt := IsolatedDeviceModelManager.Query().Equals("vendor_id", input.VendorId).Equals("device_id", input.DeviceId).Count(); cnt > 0 {
  177. return input, httperrors.NewDuplicateResourceError("vendor %s device %s has been registered", input.VendorId, input.DeviceId)
  178. }
  179. }
  180. }
  181. if self.Model != input.Model {
  182. if cnt := IsolatedDeviceModelManager.Query().Equals("model", input.Model).Count(); cnt > 0 {
  183. return input, httperrors.NewDuplicateResourceError("model %s has been registered", input.Model)
  184. }
  185. }
  186. return input, nil
  187. }
  188. func (manager *SIsolatedDeviceModelManager) ListItemFilter(
  189. ctx context.Context,
  190. q *sqlchemy.SQuery,
  191. userCred mcclient.TokenCredential,
  192. query api.IsolatedDeviceModelListInput,
  193. ) (*sqlchemy.SQuery, error) {
  194. q, err := manager.SStandaloneAnonResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StandaloneAnonResourceListInput)
  195. if err != nil {
  196. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemFilter")
  197. }
  198. if len(query.DevType) > 0 {
  199. q = q.In("dev_type", query.DevType)
  200. }
  201. if len(query.Model) > 0 {
  202. q = q.In("model", query.Model)
  203. }
  204. if len(query.VendorId) > 0 {
  205. q = q.Equals("vendor_id", query.VendorId)
  206. }
  207. if len(query.DeviceId) > 0 {
  208. q = q.Equals("device_id", query.DeviceId)
  209. }
  210. if len(query.HostId) > 0 {
  211. hidmq := HostIsolatedDeviceModelManager.Query("isolated_device_model_id").Equals("host_id", query.HostId).SubQuery()
  212. q = q.Filter(sqlchemy.OR(
  213. sqlchemy.IsFalse(q.Field("disable_auto_detect")),
  214. sqlchemy.IsNull(q.Field("disable_auto_detect")),
  215. sqlchemy.In(q.Field("id"), hidmq)),
  216. )
  217. }
  218. return q, nil
  219. }
  220. func (manager *SIsolatedDeviceModelManager) GetByVendorDevice(vendorId, deviceId string) (*SIsolatedDeviceModel, error) {
  221. devModel := new(SIsolatedDeviceModel)
  222. err := manager.Query().Equals("vendor_id", vendorId).Equals("device_id", deviceId).First(devModel)
  223. if err != nil {
  224. return nil, err
  225. }
  226. devModel.SetModelManager(manager, devModel)
  227. return devModel, nil
  228. }
  229. func (manager *SIsolatedDeviceModelManager) GetByDevModel(model string) (*SIsolatedDeviceModel, error) {
  230. devModel := new(SIsolatedDeviceModel)
  231. err := manager.Query().Equals("model", model).First(devModel)
  232. if err != nil {
  233. return nil, err
  234. }
  235. devModel.SetModelManager(manager, devModel)
  236. return devModel, nil
  237. }
  238. func (manager *SIsolatedDeviceModelManager) GetByDevType(devType string) (*SIsolatedDeviceModel, error) {
  239. devModel := new(SIsolatedDeviceModel)
  240. err := manager.Query().Equals("dev_type", devType).First(devModel)
  241. if err != nil {
  242. return nil, err
  243. }
  244. devModel.SetModelManager(manager, devModel)
  245. return devModel, nil
  246. }
  247. func (obj *SIsolatedDeviceModel) PerformSetHardwareInfo(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, data *api.IsolatedDeviceModelHardwareInfo) (*api.IsolatedDeviceModelHardwareInfo, error) {
  248. settings := map[string]interface{}{
  249. api.ISOLATED_DEVICE_MODEL_METADATA_MEMORY_MB: data.MemoryMB,
  250. api.ISOLATED_DEVICE_MODEL_METADATA_TFLOPS: data.TFLOPS,
  251. api.ISOLATED_DEVICE_MODEL_METADATA_BANDWIDTH: data.Bandwidth,
  252. }
  253. for k, v := range settings {
  254. if err := obj.SetMetadata(ctx, k, v, userCred); err != nil {
  255. return nil, errors.Wrapf(err, "set %s to %v", k, v)
  256. }
  257. }
  258. return data, nil
  259. }
  260. func (obj *SIsolatedDeviceModel) GetDetailsHardwareInfo(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject) (*api.IsolatedDeviceModelHardwareInfo, error) {
  261. info := new(api.IsolatedDeviceModelHardwareInfo)
  262. // parse memory size MB
  263. if memStr := obj.GetMetadata(ctx, api.ISOLATED_DEVICE_MODEL_METADATA_MEMORY_MB, userCred); memStr != "" {
  264. memSize, err := strconv.Atoi(memStr)
  265. if err != nil {
  266. return nil, errors.Wrapf(err, "convert memory size %d to int", memSize)
  267. }
  268. info.MemoryMB = memSize
  269. }
  270. // parse bandwidth
  271. if bwStr := obj.GetMetadata(ctx, api.ISOLATED_DEVICE_MODEL_METADATA_BANDWIDTH, userCred); bwStr != "" {
  272. bw, err := strconv.ParseFloat(bwStr, 64)
  273. if err != nil {
  274. return nil, errors.Wrapf(err, "convert bandwidth %s to float", bwStr)
  275. }
  276. info.Bandwidth = bw
  277. }
  278. // parse TFLOPS
  279. if tflopsStr := obj.GetMetadata(ctx, api.ISOLATED_DEVICE_MODEL_METADATA_TFLOPS, userCred); tflopsStr != "" {
  280. tflops, err := strconv.ParseFloat(tflopsStr, 64)
  281. if err != nil {
  282. return nil, errors.Wrapf(err, "convert TFLOPS %s to float", tflopsStr)
  283. }
  284. info.TFLOPS = tflops
  285. }
  286. return info, nil
  287. }