isolated_devices.go 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790
  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. "database/sql"
  18. "fmt"
  19. "math"
  20. "reflect"
  21. "sort"
  22. "strconv"
  23. "strings"
  24. "time"
  25. "yunion.io/x/cloudmux/pkg/cloudprovider"
  26. "yunion.io/x/jsonutils"
  27. "yunion.io/x/log"
  28. "yunion.io/x/pkg/errors"
  29. "yunion.io/x/pkg/gotypes"
  30. "yunion.io/x/pkg/util/rbacscope"
  31. "yunion.io/x/pkg/util/sets"
  32. "yunion.io/x/pkg/utils"
  33. "yunion.io/x/sqlchemy"
  34. "yunion.io/x/onecloud/pkg/apis"
  35. api "yunion.io/x/onecloud/pkg/apis/compute"
  36. hostapi "yunion.io/x/onecloud/pkg/apis/host"
  37. "yunion.io/x/onecloud/pkg/apis/notify"
  38. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  39. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  40. "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
  41. "yunion.io/x/onecloud/pkg/httperrors"
  42. "yunion.io/x/onecloud/pkg/mcclient"
  43. "yunion.io/x/onecloud/pkg/util/rbacutils"
  44. "yunion.io/x/onecloud/pkg/util/stringutils2"
  45. )
  46. const (
  47. //DIRECT_PCI_TYPE = api.DIRECT_PCI_TYPE
  48. //GPU_HPC_TYPE = api.GPU_HPC_TYPE // # for compute
  49. //GPU_VGA_TYPE = api.GPU_VGA_TYPE // # for display
  50. //USB_TYPE = api.USB_TYPE
  51. //NIC_TYPE = api.NIC_TYPE
  52. // NVIDIA_VENDOR_ID = api.NVIDIA_VENDOR_ID
  53. // AMD_VENDOR_ID = api.AMD_VENDOR_ID
  54. )
  55. var VALID_GPU_TYPES = api.VALID_GPU_TYPES
  56. var VALID_PASSTHROUGH_TYPES = api.VALID_PASSTHROUGH_TYPES
  57. var ID_VENDOR_MAP = api.ID_VENDOR_MAP
  58. var VENDOR_ID_MAP = api.VENDOR_ID_MAP
  59. type SIsolatedDeviceManager struct {
  60. db.SStandaloneResourceBaseManager
  61. db.SExternalizedResourceBaseManager
  62. db.SSharableBaseResourceManager
  63. SHostResourceBaseManager
  64. }
  65. var IsolatedDeviceManager *SIsolatedDeviceManager
  66. func init() {
  67. gotypes.RegisterSerializable(reflect.TypeOf(&api.IsolatedDevicePCIEInfo{}), func() gotypes.ISerializable {
  68. return &api.IsolatedDevicePCIEInfo{}
  69. })
  70. IsolatedDeviceManager = &SIsolatedDeviceManager{
  71. SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
  72. SIsolatedDevice{},
  73. "isolated_devices_tbl",
  74. "isolated_device",
  75. "isolated_devices",
  76. ),
  77. }
  78. IsolatedDeviceManager.SetVirtualObject(IsolatedDeviceManager)
  79. }
  80. type SIsolatedDevice struct {
  81. db.SStandaloneResourceBase
  82. db.SExternalizedResourceBase
  83. db.SSharableBaseResource `"is_public->create":"domain_optional" "public_scope->create":"domain_optional"`
  84. SHostResourceBase `width:"36" charset:"ascii" nullable:"false" default:"" index:"true" list:"domain" create:"domain_required"`
  85. // # PCI / GPU-HPC / GPU-VGA / USB / NIC
  86. // 设备类型
  87. DevType string `width:"128" charset:"ascii" nullable:"false" default:"" index:"true" list:"domain" create:"domain_required" update:"domain"`
  88. // # Specific device name read from lspci command, e.g. `Tesla K40m` ...
  89. Model string `width:"512" charset:"ascii" nullable:"false" default:"" index:"true" list:"domain" create:"domain_required" update:"domain"`
  90. // 云主机Id
  91. GuestId string `width:"36" charset:"ascii" nullable:"true" index:"true" list:"domain"`
  92. // guest network index
  93. NetworkIndex int `nullable:"true" default:"-1" list:"user" update:"user"`
  94. // Nic wire id
  95. WireId string `width:"36" charset:"ascii" nullable:"true" index:"true" list:"domain" update:"domain" create:"domain_optional"`
  96. // Offload interface name
  97. OvsOffloadInterface string `width:"16" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
  98. // Is infiniband nic
  99. IsInfinibandNic bool `nullable:"false" default:"false" list:"user" create:"optional"`
  100. // NVME disk size
  101. NvmeSizeMB int `nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
  102. // guest disk index
  103. DiskIndex int8 `nullable:"true" default:"-1" list:"user" update:"user"`
  104. // # pci address of `Bus:Device.Function` format, or usb bus address of `bus.addr`
  105. Addr string `width:"16" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
  106. DevicePath string `width:"128" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"optional"`
  107. // GPU card path, like /dev/dri/cardX
  108. CardPath string `width:"128" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"optional"`
  109. // GPU render path, like /dev/dri/renderDX
  110. RenderPath string `width:"128" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"optional"`
  111. // Nvidia GPU index
  112. Index int `nullable:"true" default:"-1" list:"user" update:"domain"`
  113. // Nvidia GPU minor number, parsing from /proc/driver/nvidia/gpus/*/information
  114. DeviceMinor int `nullable:"true" default:"-1" list:"user" update:"domain"`
  115. // Is vgpu physical funcion, That means it cannot be attached to guest
  116. // VGPUPhysicalFunction bool `nullable:"true" default:"false" list:"domain" create:"domain_optional"`
  117. // nvidia vgpu config
  118. // vgpu uuid generated on create
  119. MdevId string `width:"36" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
  120. // The frame rate limiter (FRL) configuration in frames per second
  121. FRL string `nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
  122. // The frame buffer size in Mbytes
  123. Framebuffer string `nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
  124. // The maximum resolution per display head, eg: 5120x2880
  125. MaxResolution string `width:"16" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
  126. // The maximum number of virtual display heads that the vGPU type supports
  127. // In computer graphics and display technology, the term "head" is commonly used to
  128. // describe the physical interface of a display device or display output.
  129. // It refers to a connection point on the monitor, such as HDMI, DisplayPort, or VGA interface.
  130. NumHeads string `nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
  131. // The maximum number of vGPU instances per physical GPU
  132. MaxInstance string `nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
  133. // MPS perdevice memory limit MB
  134. MpsMemoryLimit int `nullable:"true" default:"-1" list:"domain" update:"domain" create:"domain_optional"`
  135. // MPS device memory total MB
  136. MpsMemoryTotal int `nullable:"true" default:"-1" list:"domain" update:"domain" create:"domain_optional"`
  137. // MPS device thread percentage
  138. MpsThreadPercentage int `nullable:"true" default:"-1" list:"domain" update:"domain" create:"domain_optional"`
  139. VendorDeviceId string `width:"16" charset:"ascii" nullable:"true" list:"domain" create:"domain_optional"`
  140. // reserved memory size for isolated device
  141. ReservedMemory int `nullable:"true" default:"0" list:"domain" update:"domain" create:"domain_optional"`
  142. // reserved cpu count for isolated device
  143. ReservedCpu int `nullable:"true" default:"0" list:"domain" update:"domain" create:"domain_optional"`
  144. // reserved storage size for isolated device
  145. ReservedStorage int `nullable:"true" default:"0" list:"domain" update:"domain" create:"domain_optional"`
  146. // PciInfo stores extra PCIE information
  147. PcieInfo *api.IsolatedDevicePCIEInfo `nullable:"true" create:"optional" list:"user" get:"user" update:"domain"`
  148. // device numa node
  149. NumaNode int8 `nullable:"true" default:"-1" list:"domain" update:"domain" create:"domain_optional"`
  150. }
  151. func (manager *SIsolatedDeviceManager) ExtraSearchConditions(ctx context.Context, q *sqlchemy.SQuery, like string) []sqlchemy.ICondition {
  152. sq := HostManager.Query("id").Contains("name", like).SubQuery()
  153. return []sqlchemy.ICondition{sqlchemy.In(q.Field("host_id"), sq)}
  154. }
  155. func (manager *SIsolatedDeviceManager) ValidateCreateData(ctx context.Context,
  156. userCred mcclient.TokenCredential,
  157. ownerId mcclient.IIdentityProvider,
  158. query jsonutils.JSONObject,
  159. input api.IsolatedDeviceCreateInput,
  160. ) (api.IsolatedDeviceCreateInput, error) {
  161. var err error
  162. var host *SHost
  163. host, input.HostResourceInput, err = ValidateHostResourceInput(ctx, userCred, input.HostResourceInput)
  164. if err != nil {
  165. return input, errors.Wrap(err, "ValidateHostResourceInput")
  166. }
  167. if len(input.Name) == 0 {
  168. input.Name = fmt.Sprintf("dev_%s_%d", host.GetName(), time.Now().UnixNano())
  169. }
  170. // validate DevType
  171. if input.DevType == "" {
  172. return input, httperrors.NewNotEmptyError("dev_type is empty")
  173. }
  174. if !utils.IsInStringArray(input.DevType, api.VALID_PASSTHROUGH_TYPES) {
  175. if _, err := IsolatedDeviceModelManager.GetByDevType(input.DevType); err != nil {
  176. return input, httperrors.NewInputParameterError("device type %q not supported", input.DevType)
  177. }
  178. }
  179. input.StandaloneResourceCreateInput, err = manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneResourceCreateInput)
  180. if err != nil {
  181. return input, errors.Wrap(err, "SStandaloneResourceBaseManager.ValidateCreateData")
  182. }
  183. if input.HostId != "" && input.Addr != "" {
  184. if hasDevAddr, err := manager.hostHasDevAddr(input.HostId, input.Addr, input.MdevId); err != nil {
  185. return input, errors.Wrap(err, "check hostHasDevAddr")
  186. } else if hasDevAddr {
  187. return input, httperrors.NewBadRequestError("dev addr %s registed", input.Addr)
  188. }
  189. }
  190. // validate reserverd resource
  191. // inject default reserverd resource for gpu:
  192. if utils.IsInStringArray(input.DevType, []string{api.GPU_HPC_TYPE, api.GPU_VGA_TYPE}) {
  193. defaultCPU := 8 // 8
  194. defaultMem := 8192 // 8g
  195. defaultStore := 102400 // 100g
  196. if input.ReservedCpu == nil {
  197. input.ReservedCpu = &defaultCPU
  198. }
  199. if input.ReservedMemory == nil {
  200. input.ReservedMemory = &defaultMem
  201. }
  202. if input.ReservedStorage == nil {
  203. input.ReservedStorage = &defaultStore
  204. }
  205. }
  206. if input.ReservedCpu != nil && *input.ReservedCpu < 0 {
  207. return input, httperrors.NewInputParameterError("reserved cpu must >= 0")
  208. }
  209. if input.ReservedMemory != nil && *input.ReservedMemory < 0 {
  210. return input, httperrors.NewInputParameterError("reserved memory must >= 0")
  211. }
  212. if input.ReservedStorage != nil && *input.ReservedStorage < 0 {
  213. return input, httperrors.NewInputParameterError("reserved storage must >= 0")
  214. }
  215. return input, nil
  216. }
  217. func (self *SIsolatedDevice) ValidateUpdateData(
  218. ctx context.Context, userCred mcclient.TokenCredential,
  219. query jsonutils.JSONObject, input api.IsolatedDeviceUpdateInput,
  220. ) (api.IsolatedDeviceUpdateInput, error) {
  221. var err error
  222. input.StandaloneResourceBaseUpdateInput, err = self.SStandaloneResourceBase.ValidateUpdateData(
  223. ctx, userCred, query, input.StandaloneResourceBaseUpdateInput)
  224. if err != nil {
  225. return input, err
  226. }
  227. if input.ReservedCpu != nil && *input.ReservedCpu < 0 {
  228. return input, httperrors.NewInputParameterError("reserved cpu must >= 0")
  229. }
  230. if input.ReservedMemory != nil && *input.ReservedMemory < 0 {
  231. return input, httperrors.NewInputParameterError("reserved memory must >= 0")
  232. }
  233. if input.ReservedStorage != nil && *input.ReservedStorage < 0 {
  234. return input, httperrors.NewInputParameterError("reserved storage must >= 0")
  235. }
  236. if input.DevType != "" && input.DevType != self.DevType {
  237. if !utils.IsInStringArray(input.DevType, api.VALID_GPU_TYPES) {
  238. if _, err := IsolatedDeviceModelManager.GetByDevType(input.DevType); err != nil {
  239. return input, httperrors.NewInputParameterError("device type %q not support update", input.DevType)
  240. }
  241. } else {
  242. if !self.IsGPU() {
  243. return input, httperrors.NewInputParameterError("Can't update for device %q", self.DevType)
  244. }
  245. }
  246. }
  247. return input, nil
  248. }
  249. func (device *SIsolatedDevice) isolateDeviceNotifyForHost(ctx context.Context, userCred mcclient.TokenCredential, action notify.SAction) {
  250. model, err := HostManager.FetchById(device.HostId)
  251. if err != nil {
  252. return
  253. }
  254. host := model.(*SHost)
  255. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  256. Action: action,
  257. Obj: host,
  258. ObjDetailsDecorator: func(ctx context.Context, details *jsonutils.JSONDict) {
  259. details.Set("customize_details", jsonutils.Marshal(device))
  260. },
  261. })
  262. }
  263. func (device *SIsolatedDevice) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  264. device.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
  265. device.isolateDeviceNotifyForHost(ctx, userCred, notify.ActionIsolatedDeviceCreate)
  266. }
  267. func (device *SIsolatedDevice) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
  268. device.SStandaloneResourceBase.PostDelete(ctx, userCred)
  269. device.isolateDeviceNotifyForHost(ctx, userCred, notify.ActionIsolatedDeviceDelete)
  270. }
  271. func (device *SIsolatedDevice) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  272. HostManager.ClearSchedDescCache(device.HostId)
  273. device.isolateDeviceNotifyForHost(ctx, userCred, notify.ActionIsolatedDeviceUpdate)
  274. }
  275. // 直通设备(GPU等)列表
  276. func (manager *SIsolatedDeviceManager) ListItemFilter(
  277. ctx context.Context,
  278. q *sqlchemy.SQuery,
  279. userCred mcclient.TokenCredential,
  280. query api.IsolatedDeviceListInput,
  281. ) (*sqlchemy.SQuery, error) {
  282. q, err := manager.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StandaloneResourceListInput)
  283. if err != nil {
  284. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemFilter")
  285. }
  286. q, err = manager.SHostResourceBaseManager.ListItemFilter(ctx, q, userCred, query.HostFilterListInput)
  287. if err != nil {
  288. return nil, errors.Wrap(err, "SHostResourceBaseManager.ListItemFilter")
  289. }
  290. q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
  291. if err != nil {
  292. return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
  293. }
  294. if query.Gpu != nil && *query.Gpu {
  295. q = q.Startswith("dev_type", "GPU")
  296. }
  297. if query.Usb != nil && *query.Usb {
  298. q = q.Equals("dev_type", "USB")
  299. }
  300. if query.Unused != nil && *query.Unused {
  301. q = q.IsEmpty("guest_id")
  302. }
  303. if len(query.DevType) > 0 {
  304. q = q.In("dev_type", query.DevType)
  305. }
  306. if len(query.Model) > 0 {
  307. q = q.In("model", query.Model)
  308. }
  309. if len(query.Addr) > 0 {
  310. q = q.In("addr", query.Addr)
  311. }
  312. if len(query.DevicePath) > 0 {
  313. q = q.In("device_path", query.DevicePath)
  314. }
  315. if len(query.VendorDeviceId) > 0 {
  316. q = q.In("vendor_device_id", query.VendorDeviceId)
  317. }
  318. if len(query.NumaNode) > 0 {
  319. q = q.In("numa_node", query.NumaNode)
  320. }
  321. if query.Index != nil && *query.Index >= 0 {
  322. q = q.Equals("index", query.Index)
  323. }
  324. if query.DeviceMinor != nil && *query.DeviceMinor >= 0 {
  325. q = q.Equals("device_minor", query.DeviceMinor)
  326. }
  327. if !query.ShowBaremetalIsolatedDevices {
  328. sq := HostManager.Query("id").In("host_type", []string{api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_CONTAINER, api.HOST_TYPE_ZETTAKIT}).SubQuery()
  329. q = q.In("host_id", sq)
  330. }
  331. if query.GuestId != "" {
  332. obj, err := GuestManager.FetchByIdOrName(ctx, userCred, query.GuestId)
  333. if err != nil {
  334. return nil, errors.Wrapf(err, "Fetch guest by %q", query.GuestId)
  335. }
  336. q = q.Equals("guest_id", obj.GetId())
  337. }
  338. return q, nil
  339. }
  340. func (manager *SIsolatedDeviceManager) OrderByExtraFields(
  341. ctx context.Context,
  342. q *sqlchemy.SQuery,
  343. userCred mcclient.TokenCredential,
  344. query api.IsolatedDeviceListInput,
  345. ) (*sqlchemy.SQuery, error) {
  346. var err error
  347. q, err = manager.SStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StandaloneResourceListInput)
  348. if err != nil {
  349. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.OrderByExtraFields")
  350. }
  351. q, err = manager.SHostResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.HostFilterListInput)
  352. if err != nil {
  353. return nil, errors.Wrap(err, "SHostResourceBaseManager.OrderByExtraFields")
  354. }
  355. return q, nil
  356. }
  357. func (manager *SIsolatedDeviceManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  358. var err error
  359. q, err = manager.SStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
  360. if err == nil {
  361. return q, nil
  362. }
  363. q, err = manager.SHostResourceBaseManager.QueryDistinctExtraField(q, field)
  364. if err == nil {
  365. return q, nil
  366. }
  367. return q, httperrors.ErrNotFound
  368. }
  369. func (manager *SIsolatedDeviceManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
  370. var err error
  371. q, err = manager.SStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  372. if err != nil {
  373. return nil, err
  374. }
  375. if keys.Contains("guest") {
  376. guestNameQuery := GuestManager.Query("name", "id").SubQuery()
  377. q.LeftJoin(guestNameQuery, sqlchemy.Equals(q.Field("guest_id"), guestNameQuery.Field("id")))
  378. q.AppendField(guestNameQuery.Field("name", "guest"))
  379. }
  380. if keys.Contains("host") {
  381. hostNameQuery := HostManager.Query("name", "id").SubQuery()
  382. q.LeftJoin(hostNameQuery, sqlchemy.Equals(q.Field("host_id"), hostNameQuery.Field("id")))
  383. q.AppendField(hostNameQuery.Field("name", "host"))
  384. }
  385. return q, nil
  386. }
  387. func (manager *SIsolatedDeviceManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict {
  388. res := manager.SStandaloneResourceBaseManager.GetExportExtraKeys(ctx, keys, rowMap)
  389. if guest, ok := rowMap["guest"]; ok && len(guest) > 0 {
  390. res.Set("guest", jsonutils.NewString(guest))
  391. }
  392. if host, ok := rowMap["host"]; ok {
  393. res.Set("host", jsonutils.NewString(host))
  394. }
  395. return res
  396. }
  397. func (self *SIsolatedDevice) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
  398. if len(self.GuestId) > 0 {
  399. return httperrors.NewNotEmptyError("Isolated device used by server")
  400. }
  401. return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx, nil)
  402. }
  403. func (self *SIsolatedDevice) getDetailedString() string {
  404. return fmt.Sprintf("%s:%s/%s/%s", self.Addr, self.Model, self.VendorDeviceId, self.DevType)
  405. }
  406. func (manager *SIsolatedDeviceManager) findAttachedDevicesOfGuest(guest *SGuest) []SIsolatedDevice {
  407. devs := make([]SIsolatedDevice, 0)
  408. q := manager.Query().Equals("guest_id", guest.Id)
  409. err := db.FetchModelObjects(manager, q, &devs)
  410. if err != nil {
  411. log.Errorf("findAttachedDevicesOfGuest error %s", err)
  412. return nil
  413. }
  414. return devs
  415. }
  416. func (manager *SIsolatedDeviceManager) fuzzyMatchModel(fuzzyStr string, devType string) *SIsolatedDevice {
  417. dev := SIsolatedDevice{}
  418. dev.SetModelManager(manager, &dev)
  419. q := manager.Query()
  420. if devType != "" {
  421. q = q.Equals("dev_type", devType)
  422. }
  423. qe := q.Equals("model", fuzzyStr)
  424. cnt, err := qe.CountWithError()
  425. if err != nil || cnt == 0 {
  426. qe = q.Contains("model", fuzzyStr)
  427. }
  428. err = qe.First(&dev)
  429. if err == nil {
  430. return &dev
  431. }
  432. return nil
  433. }
  434. func (self *SIsolatedDevice) getVendorId() string {
  435. parts := strings.Split(self.VendorDeviceId, ":")
  436. return parts[0]
  437. }
  438. func (self *SIsolatedDevice) getVendor() string {
  439. vendorId := self.getVendorId()
  440. vendor, ok := ID_VENDOR_MAP[vendorId]
  441. if ok {
  442. return vendor
  443. } else {
  444. return vendorId
  445. }
  446. }
  447. func GetVendorByVendorDeviceId(vendorDeviceId string) string {
  448. parts := strings.Split(vendorDeviceId, ":")
  449. vendorId := parts[0]
  450. vendor, ok := ID_VENDOR_MAP[vendorId]
  451. if ok {
  452. return vendor
  453. } else {
  454. return vendorId
  455. }
  456. }
  457. func (self *SIsolatedDevice) IsGPU() bool {
  458. return strings.HasPrefix(self.DevType, "GPU") || sets.NewString(api.CONTAINER_GPU_TYPES...).Has(self.DevType)
  459. }
  460. func (manager *SIsolatedDeviceManager) parseDeviceInfo(userCred mcclient.TokenCredential, devConfig *api.IsolatedDeviceConfig) (*api.IsolatedDeviceConfig, error) {
  461. var devId, devType, devVendor string
  462. var matchDev *SIsolatedDevice
  463. devId = devConfig.Id
  464. matchDev = manager.fuzzyMatchModel(devConfig.Model, devConfig.DevType)
  465. devVendor = devConfig.Vendor
  466. devType = devConfig.DevType
  467. if len(devId) == 0 {
  468. if matchDev == nil {
  469. return nil, httperrors.NewNotFoundError("Not found matched device by model: %q, dev_type: %q", devConfig.Model, devConfig.DevType)
  470. }
  471. devConfig.Model = matchDev.Model
  472. if len(devVendor) > 0 {
  473. vendorId, ok := VENDOR_ID_MAP[devVendor]
  474. if ok {
  475. devConfig.Vendor = vendorId
  476. } else {
  477. devConfig.Vendor = devVendor
  478. }
  479. } else {
  480. devConfig.Vendor = matchDev.getVendorId()
  481. }
  482. } else {
  483. devObj, err := manager.FetchById(devId)
  484. if err != nil {
  485. return nil, fmt.Errorf("IsolatedDevice %s not found: %s", devId, err)
  486. }
  487. dev := devObj.(*SIsolatedDevice)
  488. devConfig.Id = dev.Id
  489. devConfig.Model = dev.Model
  490. devConfig.DevType = dev.DevType
  491. devConfig.Vendor = dev.getVendor()
  492. devConfig.WireId = dev.WireId
  493. if dev.IsGPU() && len(devType) > 0 {
  494. if !utils.IsInStringArray(devType, VALID_GPU_TYPES) {
  495. return nil, fmt.Errorf("%s not valid for GPU device", devType)
  496. }
  497. }
  498. }
  499. if len(devType) > 0 {
  500. devConfig.DevType = devType
  501. }
  502. return devConfig, nil
  503. }
  504. func (manager *SIsolatedDeviceManager) isValidDeviceInfo(config *api.IsolatedDeviceConfig) error {
  505. if len(config.Id) > 0 {
  506. devObj, err := manager.FetchById(config.Id)
  507. if err != nil {
  508. return httperrors.NewResourceNotFoundError("IsolatedDevice %s not found", config.Id)
  509. }
  510. dev := devObj.(*SIsolatedDevice)
  511. if len(dev.GuestId) > 0 {
  512. return httperrors.NewConflictError("Isolated device already attached to another guest: %s", dev.GuestId)
  513. }
  514. }
  515. return nil
  516. }
  517. func (manager *SIsolatedDeviceManager) isValidNicDeviceInfo(config *api.IsolatedDeviceConfig) error {
  518. return manager._isValidDeviceInfo(config, api.NIC_TYPE)
  519. }
  520. func (manager *SIsolatedDeviceManager) isValidNVMEDeviceInfo(config *api.IsolatedDeviceConfig) error {
  521. return manager._isValidDeviceInfo(config, api.NVME_PT_TYPE)
  522. }
  523. func (manager *SIsolatedDeviceManager) _isValidDeviceInfo(config *api.IsolatedDeviceConfig, devType string) error {
  524. if len(config.Id) > 0 {
  525. devObj, err := manager.FetchById(config.Id)
  526. if err != nil {
  527. return httperrors.NewResourceNotFoundError("IsolatedDevice %s not found", config.Id)
  528. }
  529. dev := devObj.(*SIsolatedDevice)
  530. if len(dev.GuestId) > 0 {
  531. return httperrors.NewConflictError("Isolated device already attached to another guest: %s", dev.GuestId)
  532. }
  533. if dev.DevType != devType {
  534. return httperrors.NewBadRequestError("IsolatedDevice is not device type %s", devType)
  535. }
  536. } else if config.DevType != "" && config.DevType != devType {
  537. return httperrors.NewBadRequestError("request dev type %s not match %s", config.DevType, devType)
  538. }
  539. return nil
  540. }
  541. func (manager *SIsolatedDeviceManager) attachHostDeviceToGuestByDesc(
  542. ctx context.Context, guest *SGuest, host *SHost, devConfig *api.IsolatedDeviceConfig,
  543. userCred mcclient.TokenCredential, usedDevMap map[string]*SIsolatedDevice, preferNumaNodes []int,
  544. ) error {
  545. if len(devConfig.Id) > 0 {
  546. return manager.attachSpecificDeviceToGuest(ctx, guest, devConfig, userCred)
  547. } else if len(devConfig.DevicePath) > 0 {
  548. return manager.attachHostDeviceToGuestByDevicePath(ctx, guest, host, devConfig, userCred, usedDevMap, preferNumaNodes)
  549. } else {
  550. return manager.attachHostDeviceToGuestByModel(ctx, guest, host, devConfig, userCred, usedDevMap, preferNumaNodes)
  551. }
  552. }
  553. func (manager *SIsolatedDeviceManager) attachSpecificDeviceToGuest(ctx context.Context, guest *SGuest, devConfig *api.IsolatedDeviceConfig, userCred mcclient.TokenCredential) error {
  554. devObj, err := manager.FetchById(devConfig.Id)
  555. if devObj == nil {
  556. return fmt.Errorf("Device %s not found: %s", devConfig.Id, err)
  557. }
  558. dev := devObj.(*SIsolatedDevice)
  559. if len(devConfig.DevType) > 0 && devConfig.DevType != dev.DevType {
  560. dev.DevType = devConfig.DevType
  561. }
  562. return guest.attachIsolatedDevice(ctx, userCred, dev, devConfig.NetworkIndex, devConfig.DiskIndex)
  563. }
  564. func (manager *SIsolatedDeviceManager) attachHostDeviceToGuestByDevicePath(ctx context.Context, guest *SGuest, host *SHost, devConfig *api.IsolatedDeviceConfig, userCred mcclient.TokenCredential, usedDevMap map[string]*SIsolatedDevice, preferNumaNodes []int) error {
  565. if len(devConfig.Model) == 0 || len(devConfig.DevicePath) == 0 {
  566. return fmt.Errorf("Model or DevicePath is empty: %#v", devConfig)
  567. }
  568. // if dev type is not nic, wire is empty string
  569. devs, err := manager.findHostUnusedByDevAttr(devConfig.Model, "device_path", devConfig.DevicePath, host.Id, devConfig.WireId)
  570. if err != nil || len(devs) == 0 {
  571. return fmt.Errorf("Can't found model %s device_path %s on host %s", devConfig.Model, devConfig.DevicePath, host.Id)
  572. }
  573. var selectedDev SIsolatedDevice
  574. for i := range devs {
  575. if _, ok := usedDevMap[devs[i].DevicePath]; !ok {
  576. selectedDev = devs[i]
  577. usedDevMap[devs[i].DevicePath] = &selectedDev
  578. }
  579. }
  580. if selectedDev.Id == "" {
  581. selectedDev = devs[0]
  582. }
  583. return guest.attachIsolatedDevice(ctx, userCred, &selectedDev, devConfig.NetworkIndex, devConfig.DiskIndex)
  584. }
  585. type GroupDevs struct {
  586. DevPath string
  587. Devs []SIsolatedDevice
  588. }
  589. type SorttedGroupDevs []*GroupDevs
  590. func (pq SorttedGroupDevs) Len() int { return len(pq) }
  591. func (pq SorttedGroupDevs) Less(i, j int) bool {
  592. return len(pq[i].Devs) > len(pq[j].Devs)
  593. }
  594. func (pq SorttedGroupDevs) Swap(i, j int) {
  595. pq[i], pq[j] = pq[j], pq[i]
  596. }
  597. func (pq *SorttedGroupDevs) Push(item interface{}) {
  598. *pq = append(*pq, item.(*GroupDevs))
  599. }
  600. func (pq *SorttedGroupDevs) Pop() interface{} {
  601. old := *pq
  602. n := len(old)
  603. item := old[n-1]
  604. old[n-1] = nil // avoid memory leak
  605. *pq = old[0 : n-1]
  606. return item
  607. }
  608. type SNodeIsolateDevicesInfo struct {
  609. TotalDevCount int
  610. ReservedRate float32
  611. }
  612. func (manager *SIsolatedDeviceManager) getDevNodesUsedRate(
  613. ctx context.Context, host *SHost, devConfig *api.IsolatedDeviceConfig, topo *hostapi.HostTopology,
  614. ) (map[string]SNodeIsolateDevicesInfo, error) {
  615. devs, err := manager.findHostDevsByDevConfig(devConfig.Model, devConfig.DevType, host.Id, devConfig.WireId)
  616. if err != nil || len(devs) == 0 {
  617. return nil, fmt.Errorf("Can't found model %s on host %s", devConfig.Model, host.Id)
  618. }
  619. mapDevs := map[string][]SIsolatedDevice{}
  620. for i := range devs {
  621. dev := devs[i]
  622. devPath := dev.DevicePath
  623. var gdevs []SIsolatedDevice
  624. gdevs, ok := mapDevs[devPath]
  625. if !ok {
  626. gdevs = []SIsolatedDevice{dev}
  627. } else {
  628. gdevs = append(gdevs, dev)
  629. }
  630. mapDevs[devPath] = gdevs
  631. }
  632. nodesGroupDevs := map[string]SorttedGroupDevs{}
  633. for devPath, mappedDevs := range mapDevs {
  634. numaNode := strconv.Itoa(int(mappedDevs[0].NumaNode))
  635. if _, ok := nodesGroupDevs[numaNode]; ok {
  636. nodesGroupDevs[numaNode] = append(nodesGroupDevs[numaNode], &GroupDevs{
  637. DevPath: devPath,
  638. Devs: mappedDevs,
  639. })
  640. } else {
  641. groupDevs := make(SorttedGroupDevs, 0)
  642. nodesGroupDevs[numaNode] = append(groupDevs, &GroupDevs{
  643. DevPath: devPath,
  644. Devs: mappedDevs,
  645. })
  646. }
  647. }
  648. reserveRate := map[string]float32{}
  649. reserveRateStr := host.GetMetadata(ctx, api.HOSTMETA_RESERVED_CPUS_RATE, nil)
  650. reserveRateJ, err := jsonutils.ParseString(reserveRateStr)
  651. if err != nil {
  652. return nil, errors.Wrap(err, "parse reserveRateStr")
  653. }
  654. err = reserveRateJ.Unmarshal(&reserveRate)
  655. if err != nil {
  656. return nil, errors.Wrap(err, "unmarshal reserveRateStr")
  657. }
  658. nodeNoDevIds := map[int]int{}
  659. for i := range topo.Nodes {
  660. nodeId := strconv.Itoa(topo.Nodes[i].ID)
  661. if _, ok := nodesGroupDevs[nodeId]; !ok {
  662. nodeInt, _ := strconv.Atoi(nodeId)
  663. nodeNoDevIds[nodeInt] = -1
  664. }
  665. }
  666. //
  667. //for nodeId, _ := range reserveRate {
  668. // if _, ok := nodesGroupDevs[nodeId]; !ok {
  669. // nodeInt, _ := strconv.Atoi(nodeId)
  670. // nodeNoDevIds[nodeInt] = -1
  671. // }
  672. //}
  673. reserveNodes := map[string][]string{}
  674. for i := range topo.Nodes {
  675. if _, ok := nodeNoDevIds[topo.Nodes[i].ID]; ok {
  676. minDistance := int(math.MaxInt16)
  677. selectNodeId := ""
  678. for nodeId, _ := range nodesGroupDevs {
  679. nodeInt, _ := strconv.Atoi(nodeId)
  680. if topo.Nodes[i].Distances[nodeInt] < minDistance {
  681. selectNodeId = strconv.Itoa(nodeInt)
  682. minDistance = topo.Nodes[i].Distances[nodeInt]
  683. }
  684. }
  685. noDevNodeId := strconv.Itoa(topo.Nodes[i].ID)
  686. log.Debugf("node %s select node %s", noDevNodeId, selectNodeId)
  687. if nodes, ok := reserveNodes[selectNodeId]; ok {
  688. reserveNodes[selectNodeId] = append(nodes, noDevNodeId)
  689. } else {
  690. reserveNodes[selectNodeId] = []string{noDevNodeId}
  691. }
  692. }
  693. }
  694. reserveRates := map[string]SNodeIsolateDevicesInfo{}
  695. for nodeId, devGroups := range nodesGroupDevs {
  696. nodeCnt := 1
  697. nodeReserveRate := reserveRate[nodeId]
  698. if nodes, ok := reserveNodes[nodeId]; ok {
  699. for i := range nodes {
  700. nodeReserveRate += reserveRate[nodes[i]]
  701. nodeCnt += 1
  702. }
  703. }
  704. nodeReserveRate = nodeReserveRate / float32(nodeCnt)
  705. devCnt := 0
  706. for i := range devGroups {
  707. devCnt += len(devGroups[i].Devs)
  708. }
  709. reserveRates[nodeId] = SNodeIsolateDevicesInfo{
  710. TotalDevCount: devCnt,
  711. ReservedRate: nodeReserveRate,
  712. }
  713. log.Debugf("node %v nodeCnt %v nodeReserveRate %v", nodeId, nodeCnt, nodeReserveRate)
  714. }
  715. return reserveRates, nil
  716. }
  717. func (manager *SIsolatedDeviceManager) attachHostDeviceToGuestByModel(
  718. ctx context.Context, guest *SGuest, host *SHost, devConfig *api.IsolatedDeviceConfig,
  719. userCred mcclient.TokenCredential, usedDevMap map[string]*SIsolatedDevice, preferNumaNodes []int,
  720. ) error {
  721. if len(devConfig.Model) == 0 {
  722. return fmt.Errorf("Not found model from info: %#v", devConfig)
  723. }
  724. // if dev type is not nic, wire is empty string
  725. devs, err := manager.findHostUnusedByDevConfig(devConfig.Model, devConfig.DevType, host.Id, devConfig.WireId)
  726. if err != nil || len(devs) == 0 {
  727. return fmt.Errorf("Can't found model %s on host %s", devConfig.Model, host.Id)
  728. }
  729. // 1. group devices by device_path and numa nodes
  730. //groupDevs := make(SorttedGroupDevs, 0)
  731. mapDevs := map[string][]SIsolatedDevice{}
  732. for i := range devs {
  733. dev := devs[i]
  734. devPath := dev.DevicePath
  735. var gdevs []SIsolatedDevice
  736. gdevs, ok := mapDevs[devPath]
  737. if !ok {
  738. gdevs = []SIsolatedDevice{dev}
  739. } else {
  740. gdevs = append(gdevs, dev)
  741. }
  742. mapDevs[devPath] = gdevs
  743. }
  744. var groupDevs SorttedGroupDevs
  745. if len(preferNumaNodes) > 0 {
  746. groupDevs = make(SorttedGroupDevs, 0)
  747. for devPath, mappedDevs := range mapDevs {
  748. groupDevs = append(groupDevs, &GroupDevs{
  749. DevPath: devPath,
  750. Devs: mappedDevs,
  751. })
  752. }
  753. } else {
  754. nodesGroupDevs := map[int8]SorttedGroupDevs{}
  755. for devPath, mappedDevs := range mapDevs {
  756. numaNode := mappedDevs[0].NumaNode
  757. if _, ok := nodesGroupDevs[numaNode]; ok {
  758. nodesGroupDevs[numaNode] = append(nodesGroupDevs[numaNode], &GroupDevs{
  759. DevPath: devPath,
  760. Devs: mappedDevs,
  761. })
  762. } else {
  763. groupDevs := make(SorttedGroupDevs, 0)
  764. nodesGroupDevs[numaNode] = append(groupDevs, &GroupDevs{
  765. DevPath: devPath,
  766. Devs: mappedDevs,
  767. })
  768. }
  769. }
  770. var selectedNode int8 = -1
  771. if len(nodesGroupDevs) == 1 {
  772. for nodeId := range nodesGroupDevs {
  773. selectedNode = nodeId
  774. }
  775. } else {
  776. reservedCpusStr := host.GetMetadata(ctx, api.HOSTMETA_RESERVED_CPUS_INFO, nil)
  777. if len(reservedCpusStr) > 0 {
  778. topoObj, err := host.SysInfo.Get("topology")
  779. if err != nil {
  780. return errors.Wrap(err, "get topology from host sys_info")
  781. }
  782. topo := new(hostapi.HostTopology)
  783. if err := topoObj.Unmarshal(topo); err != nil {
  784. return errors.Wrap(err, "Unmarshal host topology struct")
  785. }
  786. nodesReserveRate, err := manager.getDevNodesUsedRate(ctx, host, devConfig, topo)
  787. if err != nil {
  788. return err
  789. }
  790. var selectedNodeUtil float32 = 1.0
  791. for nodeId, gds := range nodesGroupDevs {
  792. freeDevCnt := 0
  793. for i := range gds {
  794. freeDevCnt += len(gds[i].Devs)
  795. }
  796. nodeTotalCnt := nodesReserveRate[strconv.Itoa(int(nodeId))].TotalDevCount
  797. usedDevCnt := nodeTotalCnt - freeDevCnt
  798. nodeReserveRate := nodesReserveRate[strconv.Itoa(int(nodeId))].ReservedRate
  799. nodeCnt := (1 - nodeReserveRate) * float32(nodeTotalCnt)
  800. nodeutil := float32(usedDevCnt) / nodeCnt
  801. log.Debugf("selectedNodeUtil node %v util %v usedDevCnt %v totalDevCnt %v", nodeId, nodeutil, usedDevCnt, nodeCnt)
  802. if nodeutil < selectedNodeUtil {
  803. selectedNodeUtil = nodeutil
  804. selectedNode = nodeId
  805. }
  806. }
  807. } else {
  808. var selectedNodeDevCnt = 0
  809. for nodeId, gds := range nodesGroupDevs {
  810. devCnt := 0
  811. for i := range gds {
  812. devCnt += len(gds[i].Devs)
  813. }
  814. if devCnt > selectedNodeDevCnt {
  815. selectedNodeDevCnt = devCnt
  816. selectedNode = nodeId
  817. }
  818. }
  819. }
  820. }
  821. log.Debugf("selectedNodeUtil node %v", selectedNode)
  822. groupDevs = nodesGroupDevs[selectedNode]
  823. }
  824. sort.Sort(groupDevs)
  825. var selectedDev *SIsolatedDevice
  826. if len(preferNumaNodes) > 0 {
  827. topoObj, err := host.SysInfo.Get("topology")
  828. if err != nil {
  829. return errors.Wrap(err, "get topology from host sys_info")
  830. }
  831. hostTopo := new(hostapi.HostTopology)
  832. if err := topoObj.Unmarshal(hostTopo); err != nil {
  833. return errors.Wrap(err, "Unmarshal host topology struct")
  834. }
  835. if len(groupDevs) == 1 && groupDevs[0].DevPath == "" {
  836. minDistancesDevIdx := -1
  837. minDistances := math.MaxInt32
  838. for i := range groupDevs[0].Devs {
  839. if groupDevs[0].Devs[i].NumaNode < 0 {
  840. continue
  841. }
  842. devNodeId := groupDevs[0].Devs[i].NumaNode
  843. for j := range hostTopo.Nodes {
  844. if hostTopo.Nodes[j].ID == int(devNodeId) {
  845. devDistance := 0
  846. for k := range preferNumaNodes {
  847. devDistance += hostTopo.Nodes[j].Distances[preferNumaNodes[k]]
  848. }
  849. if devDistance < minDistances {
  850. minDistances = devDistance
  851. minDistancesDevIdx = i
  852. }
  853. }
  854. }
  855. }
  856. if minDistancesDevIdx >= 0 {
  857. selectedDev = &groupDevs[0].Devs[minDistancesDevIdx]
  858. }
  859. } else {
  860. minDistancesGroupIdx := -1
  861. minDistances := math.MaxInt32
  862. log.Infof("devtype %s grouplength %d", groupDevs[0].Devs[0].DevType, len(groupDevs))
  863. for i := range groupDevs {
  864. if groupDevs[i].Devs[0].NumaNode < 0 {
  865. continue
  866. }
  867. devNodeId := groupDevs[i].Devs[0].NumaNode
  868. for j := range hostTopo.Nodes {
  869. if hostTopo.Nodes[j].ID == int(devNodeId) {
  870. devDistance := 0
  871. for k := range preferNumaNodes {
  872. devDistance += hostTopo.Nodes[j].Distances[preferNumaNodes[k]]
  873. }
  874. if devDistance < minDistances {
  875. minDistances = devDistance
  876. minDistancesGroupIdx = i
  877. }
  878. }
  879. }
  880. }
  881. if minDistancesGroupIdx >= 0 {
  882. selectedDev = &groupDevs[minDistancesGroupIdx].Devs[0]
  883. }
  884. }
  885. }
  886. if selectedDev == nil {
  887. for i := range groupDevs {
  888. if groupDevs[i].DevPath != "" {
  889. for j := range groupDevs[i].Devs {
  890. dev := groupDevs[i].Devs[j]
  891. devAddr := strings.Split(dev.Addr, "-")[0]
  892. if _, ok := usedDevMap[devAddr]; ok {
  893. continue
  894. } else {
  895. selectedDev = &groupDevs[i].Devs[j]
  896. break
  897. }
  898. }
  899. } else {
  900. dev := groupDevs[i].Devs[0]
  901. devAddr := strings.Split(dev.Addr, "-")[0]
  902. if _, ok := usedDevMap[devAddr]; ok {
  903. continue
  904. } else {
  905. selectedDev = &groupDevs[i].Devs[0]
  906. break
  907. }
  908. }
  909. if selectedDev != nil {
  910. break
  911. }
  912. }
  913. }
  914. if selectedDev == nil {
  915. selectedDev = &groupDevs[0].Devs[0]
  916. }
  917. devAddr := strings.Split(selectedDev.Addr, "-")[0]
  918. usedDevMap[devAddr] = selectedDev
  919. return guest.attachIsolatedDevice(ctx, userCred, selectedDev, devConfig.NetworkIndex, devConfig.DiskIndex)
  920. }
  921. func (manager *SIsolatedDeviceManager) findUnusedQuery() *sqlchemy.SQuery {
  922. isolateddevs := manager.Query().SubQuery()
  923. q := isolateddevs.Query().Filter(sqlchemy.OR(sqlchemy.IsNull(isolateddevs.Field("guest_id")),
  924. sqlchemy.IsEmpty(isolateddevs.Field("guest_id"))))
  925. return q
  926. }
  927. func (manager *SIsolatedDeviceManager) UnusedGpuQuery() *sqlchemy.SQuery {
  928. q := manager.findUnusedQuery()
  929. q = q.Filter(sqlchemy.OR(
  930. sqlchemy.Equals(q.Field("dev_type"), api.GPU_HPC_TYPE),
  931. sqlchemy.Equals(q.Field("dev_type"), api.GPU_VGA_TYPE)))
  932. return q
  933. }
  934. func (manager *SIsolatedDeviceManager) FindUnusedByModels(models []string) ([]SIsolatedDevice, error) {
  935. devs := make([]SIsolatedDevice, 0)
  936. q := manager.findUnusedQuery()
  937. q = q.In("model", models)
  938. err := db.FetchModelObjects(manager, q, &devs)
  939. if err != nil {
  940. return nil, err
  941. }
  942. return devs, nil
  943. }
  944. func (manager *SIsolatedDeviceManager) FindUnusedNicWiresByModel(modelName string) ([]string, error) {
  945. q := manager.Query().IsNullOrEmpty("guest_id").Equals("dev_type", api.NIC_TYPE)
  946. if len(modelName) > 0 {
  947. q = q.Equals("model", modelName)
  948. }
  949. q = q.GroupBy("wire_id")
  950. devs := make([]SIsolatedDevice, 0)
  951. err := q.All(&devs)
  952. if err != nil {
  953. return nil, err
  954. }
  955. wires := make([]string, len(devs))
  956. for i := 0; i < len(devs); i++ {
  957. wires[i] = devs[i].WireId
  958. }
  959. return wires, err
  960. }
  961. func (manager *SIsolatedDeviceManager) FindUnusedGpusOnHost(hostId string) ([]SIsolatedDevice, error) {
  962. devs := make([]SIsolatedDevice, 0)
  963. q := manager.UnusedGpuQuery()
  964. q = q.Equals("host_id", hostId)
  965. err := db.FetchModelObjects(manager, q, &devs)
  966. if err != nil {
  967. return nil, err
  968. }
  969. return devs, nil
  970. }
  971. func (manager *SIsolatedDeviceManager) findHostUnusedByDevConfig(model, devType, hostId, wireId string) ([]SIsolatedDevice, error) {
  972. return manager.findHostUnusedByDevAttr(model, "dev_type", devType, hostId, wireId)
  973. }
  974. func (manager *SIsolatedDeviceManager) findHostDevsByDevConfig(model, devType, hostId, wireId string) ([]SIsolatedDevice, error) {
  975. return manager.findHostDevsByDevAttr(model, "dev_type", devType, hostId, wireId)
  976. }
  977. func (manager *SIsolatedDeviceManager) findHostDevsByDevAttr(model, attrKey, attrVal, hostId, wireId string) ([]SIsolatedDevice, error) {
  978. devs := make([]SIsolatedDevice, 0)
  979. q := manager.Query()
  980. q = q.Equals("model", model).Equals("host_id", hostId)
  981. if attrVal != "" {
  982. q.Equals(attrKey, attrVal)
  983. }
  984. if wireId != "" {
  985. wire := WireManager.FetchWireById(wireId)
  986. if wire.VpcId == api.DEFAULT_VPC_ID {
  987. q = q.Equals("wire_id", wireId)
  988. }
  989. }
  990. err := db.FetchModelObjects(manager, q, &devs)
  991. if err != nil {
  992. return nil, err
  993. }
  994. return devs, nil
  995. }
  996. func (manager *SIsolatedDeviceManager) findHostUnusedByDevAttr(model, attrKey, attrVal, hostId, wireId string) ([]SIsolatedDevice, error) {
  997. devs := make([]SIsolatedDevice, 0)
  998. q := manager.findUnusedQuery()
  999. q = q.Equals("model", model).Equals("host_id", hostId)
  1000. if attrVal != "" {
  1001. q.Equals(attrKey, attrVal)
  1002. }
  1003. if wireId != "" {
  1004. wire := WireManager.FetchWireById(wireId)
  1005. if wire.VpcId == api.DEFAULT_VPC_ID {
  1006. q = q.Equals("wire_id", wireId)
  1007. }
  1008. }
  1009. err := db.FetchModelObjects(manager, q, &devs)
  1010. if err != nil {
  1011. return nil, err
  1012. }
  1013. return devs, nil
  1014. }
  1015. func (manager *SIsolatedDeviceManager) ReleaseGPUDevicesOfGuest(ctx context.Context, guest *SGuest, userCred mcclient.TokenCredential) error {
  1016. devs := manager.findAttachedDevicesOfGuest(guest)
  1017. if devs == nil {
  1018. return fmt.Errorf("fail to find attached devices")
  1019. }
  1020. for _, dev := range devs {
  1021. if !utils.IsInStringArray(dev.DevType, api.VALID_GPU_TYPES) {
  1022. continue
  1023. }
  1024. _, err := db.Update(&dev, func() error {
  1025. dev.GuestId = ""
  1026. dev.NetworkIndex = -1
  1027. dev.DiskIndex = -1
  1028. return nil
  1029. })
  1030. if err != nil {
  1031. db.OpsLog.LogEvent(guest, db.ACT_GUEST_DETACH_ISOLATED_DEVICE_FAIL, dev.GetShortDesc(ctx), userCred)
  1032. return err
  1033. }
  1034. db.OpsLog.LogEvent(guest, db.ACT_GUEST_DETACH_ISOLATED_DEVICE, dev.GetShortDesc(ctx), userCred)
  1035. }
  1036. return nil
  1037. }
  1038. func (manager *SIsolatedDeviceManager) ReleaseDevicesOfGuest(ctx context.Context, guest *SGuest, userCred mcclient.TokenCredential) error {
  1039. devs := manager.findAttachedDevicesOfGuest(guest)
  1040. if devs == nil {
  1041. return fmt.Errorf("fail to find attached devices")
  1042. }
  1043. for _, dev := range devs {
  1044. _, err := db.Update(&dev, func() error {
  1045. dev.GuestId = ""
  1046. dev.NetworkIndex = -1
  1047. return nil
  1048. })
  1049. if err != nil {
  1050. db.OpsLog.LogEvent(guest, db.ACT_GUEST_DETACH_ISOLATED_DEVICE_FAIL, dev.GetShortDesc(ctx), userCred)
  1051. return err
  1052. }
  1053. db.OpsLog.LogEvent(guest, db.ACT_GUEST_DETACH_ISOLATED_DEVICE, dev.GetShortDesc(ctx), userCred)
  1054. }
  1055. return nil
  1056. }
  1057. func (manager *SIsolatedDeviceManager) totalCountQ(
  1058. ctx context.Context,
  1059. scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, devType []string, hostTypes []string,
  1060. resourceTypes []string,
  1061. providers []string, brands []string, cloudEnv string,
  1062. rangeObjs []db.IStandaloneModel,
  1063. policyResult rbacutils.SPolicyResult,
  1064. ) *sqlchemy.SQuery {
  1065. hq := HostManager.Query()
  1066. if scope == rbacscope.ScopeDomain {
  1067. hq = hq.Filter(sqlchemy.Equals(hq.Field("domain_id"), ownerId.GetProjectDomainId()))
  1068. }
  1069. hq = db.ObjectIdQueryWithPolicyResult(ctx, hq, HostManager, policyResult)
  1070. hosts := hq.SubQuery()
  1071. devs := manager.Query().SubQuery()
  1072. q := devs.Query().Join(hosts, sqlchemy.Equals(devs.Field("host_id"), hosts.Field("id")))
  1073. q = q.Filter(sqlchemy.IsTrue(hosts.Field("enabled")))
  1074. if len(devType) != 0 {
  1075. q = q.Filter(sqlchemy.In(devs.Field("dev_type"), devType))
  1076. }
  1077. return AttachUsageQuery(q, hosts, hostTypes, resourceTypes, providers, brands, cloudEnv, rangeObjs)
  1078. }
  1079. type IsolatedDeviceCountStat struct {
  1080. Devices int
  1081. Gpus int
  1082. DevicesUsed int
  1083. GpusUsed int
  1084. }
  1085. type IsolatedDeviceStat struct {
  1086. DevType string
  1087. GuestId string
  1088. Count int
  1089. }
  1090. func (manager *SIsolatedDeviceManager) totalCount(
  1091. ctx context.Context,
  1092. scope rbacscope.TRbacScope,
  1093. ownerId mcclient.IIdentityProvider,
  1094. devType,
  1095. hostTypes []string,
  1096. resourceTypes []string,
  1097. providers []string,
  1098. brands []string,
  1099. cloudEnv string,
  1100. rangeObjs []db.IStandaloneModel,
  1101. policyResult rbacutils.SPolicyResult,
  1102. ) ([]IsolatedDeviceStat, error) {
  1103. iq := manager.totalCountQ(
  1104. ctx,
  1105. scope,
  1106. ownerId,
  1107. devType,
  1108. hostTypes,
  1109. resourceTypes,
  1110. providers,
  1111. brands,
  1112. cloudEnv,
  1113. rangeObjs,
  1114. policyResult,
  1115. )
  1116. sq := iq.SubQuery()
  1117. q := sq.Query(
  1118. sq.Field("dev_type"),
  1119. sq.Field("guest_id"),
  1120. sqlchemy.COUNT("count", sq.Field("id")),
  1121. )
  1122. q = q.GroupBy(q.Field("dev_type"), q.Field("guest_id"))
  1123. ret := []IsolatedDeviceStat{}
  1124. err := q.All(&ret)
  1125. if err != nil {
  1126. return nil, err
  1127. }
  1128. return ret, nil
  1129. }
  1130. func (manager *SIsolatedDeviceManager) TotalCount(
  1131. ctx context.Context,
  1132. scope rbacscope.TRbacScope,
  1133. ownerId mcclient.IIdentityProvider,
  1134. hostType []string,
  1135. resourceTypes []string,
  1136. providers []string,
  1137. brands []string,
  1138. cloudEnv string,
  1139. rangeObjs []db.IStandaloneModel,
  1140. policyResult rbacutils.SPolicyResult,
  1141. ) (IsolatedDeviceCountStat, error) {
  1142. ret := IsolatedDeviceCountStat{}
  1143. stat, err := manager.totalCount(
  1144. ctx,
  1145. scope, ownerId, nil, hostType, resourceTypes,
  1146. providers, brands, cloudEnv,
  1147. rangeObjs, policyResult)
  1148. if err != nil {
  1149. return ret, err
  1150. }
  1151. for _, s := range stat {
  1152. ret.Devices += s.Count
  1153. if utils.IsInStringArray(s.DevType, VALID_GPU_TYPES) {
  1154. ret.Gpus += s.Count
  1155. }
  1156. if len(s.GuestId) > 0 {
  1157. ret.DevicesUsed += s.Count
  1158. if utils.IsInStringArray(s.DevType, VALID_GPU_TYPES) {
  1159. ret.GpusUsed += s.Count
  1160. }
  1161. }
  1162. }
  1163. return ret, nil
  1164. }
  1165. func (self *SIsolatedDevice) getDesc() *api.IsolatedDeviceJsonDesc {
  1166. return &api.IsolatedDeviceJsonDesc{
  1167. Id: self.Id,
  1168. DevType: self.DevType,
  1169. Model: self.Model,
  1170. Addr: self.Addr,
  1171. VendorDeviceId: self.VendorDeviceId,
  1172. Vendor: self.getVendor(),
  1173. NetworkIndex: self.NetworkIndex,
  1174. OvsOffloadInterface: self.OvsOffloadInterface,
  1175. DiskIndex: self.DiskIndex,
  1176. NvmeSizeMB: self.NvmeSizeMB,
  1177. MdevId: self.MdevId,
  1178. NumaNode: self.NumaNode,
  1179. }
  1180. }
  1181. func (man *SIsolatedDeviceManager) GetSpecShouldCheckStatus(query *jsonutils.JSONDict) (bool, error) {
  1182. return true, nil
  1183. }
  1184. func (man *SIsolatedDeviceManager) BatchGetModelSpecs(statusCheck bool) (jsonutils.JSONObject, error) {
  1185. hostQ := HostManager.Query()
  1186. q := man.Query("vendor_device_id", "model", "dev_type")
  1187. if statusCheck {
  1188. q = q.IsNullOrEmpty("guest_id")
  1189. hostQ = hostQ.Equals("status", api.BAREMETAL_RUNNING).IsTrue("enabled").
  1190. In("host_type", []string{api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_CONTAINER, api.HOST_TYPE_ZETTAKIT})
  1191. }
  1192. hostSQ := hostQ.SubQuery()
  1193. q.Join(hostSQ, sqlchemy.Equals(q.Field("host_id"), hostSQ.Field("id")))
  1194. q.AppendField(hostSQ.Field("host_type"))
  1195. q.GroupBy(hostSQ.Field("host_type"), q.Field("vendor_device_id"), q.Field("model"), q.Field("dev_type"))
  1196. q.AppendField(sqlchemy.COUNT("*"))
  1197. rows, err := q.Rows()
  1198. if err != nil {
  1199. return nil, errors.Wrap(err, "failed get specs")
  1200. }
  1201. defer rows.Close()
  1202. res := jsonutils.NewDict()
  1203. for rows.Next() {
  1204. var hostType, vendorDeviceId, m, t string
  1205. var count int
  1206. if err := rows.Scan(&vendorDeviceId, &m, &t, &hostType, &count); err != nil {
  1207. return nil, errors.Wrap(err, "get model spec scan rows")
  1208. }
  1209. vendor := GetVendorByVendorDeviceId(vendorDeviceId)
  1210. specKeys := man.getSpecKeys(vendor, m, t)
  1211. specKey := GetSpecIdentKey(specKeys)
  1212. spec := man.getSpecByRows(hostType, vendorDeviceId, m, t, &count)
  1213. res.Set(specKey, spec)
  1214. }
  1215. return res, nil
  1216. }
  1217. func (man *SIsolatedDeviceManager) getSpecByRows(hostType, vendorDeviceId, model, devType string, count *int) *jsonutils.JSONDict {
  1218. var vdev bool
  1219. var hypervisor string
  1220. if utils.IsInStringArray(devType, api.VITRUAL_DEVICE_TYPES) {
  1221. vdev = true
  1222. }
  1223. if utils.IsInStringArray(devType, api.VALID_CONTAINER_DEVICE_TYPES) {
  1224. hypervisor = api.HYPERVISOR_POD
  1225. } else {
  1226. hypervisor = api.HYPERVISOR_KVM
  1227. }
  1228. if hostType == api.HOST_TYPE_ZETTAKIT {
  1229. hypervisor = api.HYPERVISOR_ZETTAKIT
  1230. }
  1231. ret := jsonutils.NewDict()
  1232. ret.Set("virtual_dev", jsonutils.NewBool(vdev))
  1233. ret.Set("hypervisor", jsonutils.NewString(hypervisor))
  1234. ret.Set("dev_type", jsonutils.NewString(devType))
  1235. ret.Set("model", jsonutils.NewString(model))
  1236. ret.Set("pci_id", jsonutils.NewString(vendorDeviceId))
  1237. ret.Set("vendor", jsonutils.NewString(GetVendorByVendorDeviceId(vendorDeviceId)))
  1238. if count != nil {
  1239. ret.Set("count", jsonutils.NewInt(int64(*count)))
  1240. }
  1241. return ret
  1242. }
  1243. type GpuSpec struct {
  1244. DevType string `json:"dev_type,allowempty"`
  1245. Model string `json:"model,allowempty"`
  1246. Amount string `json:"amount,allowemtpy"`
  1247. Vendor string `json:"vendor,allowempty"`
  1248. PciId string `json:"pci_id,allowempty"`
  1249. }
  1250. func (self *SIsolatedDevice) GetSpec(statusCheck bool) *jsonutils.JSONDict {
  1251. host := self.getHost()
  1252. if statusCheck {
  1253. if len(self.GuestId) > 0 {
  1254. return nil
  1255. }
  1256. if host.Status != api.BAREMETAL_RUNNING || !host.GetEnabled() ||
  1257. (host.HostType != api.HOST_TYPE_HYPERVISOR && host.HostType != api.HOST_TYPE_CONTAINER && host.HostType != api.HOST_TYPE_ZETTAKIT) {
  1258. return nil
  1259. }
  1260. }
  1261. return IsolatedDeviceManager.getSpecByRows(host.HostType, self.VendorDeviceId, self.Model, self.DevType, nil)
  1262. }
  1263. func (self *SIsolatedDevice) GetGpuSpec() *GpuSpec {
  1264. return &GpuSpec{
  1265. DevType: self.DevType,
  1266. Model: self.Model,
  1267. PciId: self.VendorDeviceId,
  1268. Vendor: self.getVendor(),
  1269. Amount: "1",
  1270. }
  1271. }
  1272. func (man *SIsolatedDeviceManager) GetSpecIdent(spec *jsonutils.JSONDict) []string {
  1273. devType, _ := spec.GetString("dev_type")
  1274. vendor, _ := spec.GetString("vendor")
  1275. model, _ := spec.GetString("model")
  1276. return man.getSpecKeys(vendor, model, devType)
  1277. }
  1278. func (man *SIsolatedDeviceManager) getSpecKeys(vendor, model, devType string) []string {
  1279. keys := []string{
  1280. fmt.Sprintf("type:%s", devType),
  1281. fmt.Sprintf("vendor:%s", vendor),
  1282. fmt.Sprintf("model:%s", model),
  1283. }
  1284. return keys
  1285. }
  1286. func (self *SIsolatedDevice) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
  1287. desc := jsonutils.NewDict()
  1288. desc.Update(jsonutils.Marshal(self.getDesc()))
  1289. desc.Add(jsonutils.NewString(IsolatedDeviceManager.Keyword()), "res_name")
  1290. return desc
  1291. }
  1292. func (self *SIsolatedDevice) getHost() *SHost {
  1293. return HostManager.FetchHostById(self.HostId)
  1294. }
  1295. func (self *SIsolatedDevice) getGuest() *SGuest {
  1296. if len(self.GuestId) > 0 {
  1297. return GuestManager.FetchGuestById(self.GuestId)
  1298. }
  1299. return nil
  1300. }
  1301. func (manager *SIsolatedDeviceManager) FetchCustomizeColumns(
  1302. ctx context.Context,
  1303. userCred mcclient.TokenCredential,
  1304. query jsonutils.JSONObject,
  1305. objs []interface{},
  1306. fields stringutils2.SSortedStrings,
  1307. isList bool,
  1308. ) []api.IsolateDeviceDetails {
  1309. rows := make([]api.IsolateDeviceDetails, len(objs))
  1310. stdRows := manager.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  1311. hostRows := manager.SHostResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  1312. shareRows := manager.SSharableBaseResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  1313. guestIds := make([]string, len(rows))
  1314. for i := range rows {
  1315. rows[i] = api.IsolateDeviceDetails{
  1316. StandaloneResourceDetails: stdRows[i],
  1317. HostResourceInfo: hostRows[i],
  1318. SharableResourceBaseInfo: shareRows[i],
  1319. }
  1320. guestIds[i] = objs[i].(*SIsolatedDevice).GuestId
  1321. }
  1322. guests := make(map[string]SGuest)
  1323. err := db.FetchStandaloneObjectsByIds(GuestManager, guestIds, &guests)
  1324. if err != nil {
  1325. log.Errorf("db.FetchStandaloneObjectsByIds fail %s", err)
  1326. return rows
  1327. }
  1328. for i := range rows {
  1329. if guest, ok := guests[guestIds[i]]; ok {
  1330. rows[i].Guest = guest.Name
  1331. rows[i].GuestStatus = guest.Status
  1332. }
  1333. }
  1334. return rows
  1335. }
  1336. func (self *SIsolatedDevice) ClearSchedDescCache() error {
  1337. if len(self.HostId) == 0 {
  1338. return nil
  1339. }
  1340. host := self.getHost()
  1341. return host.ClearSchedDescCache()
  1342. }
  1343. func (self *SIsolatedDevice) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  1344. err := self.SStandaloneResourceBase.Delete(ctx, userCred)
  1345. if err != nil {
  1346. return err
  1347. }
  1348. return self.ClearSchedDescCache()
  1349. }
  1350. func (self *SIsolatedDevice) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1351. return nil, self.CustomizeDelete(ctx, userCred, query, data)
  1352. }
  1353. func (self *SIsolatedDevice) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  1354. if len(self.GuestId) > 0 {
  1355. if !jsonutils.QueryBoolean(data, "purge", false) {
  1356. return httperrors.NewBadRequestError("%s: %s", api.ErrMsgIsolatedDeviceUsedByServer, self.GuestId)
  1357. }
  1358. iGuest, err := GuestManager.FetchById(self.GuestId)
  1359. if err != nil {
  1360. return err
  1361. }
  1362. guest := iGuest.(*SGuest)
  1363. err = guest.detachIsolateDevice(ctx, userCred, self)
  1364. if err != nil {
  1365. return err
  1366. }
  1367. }
  1368. host := self.getHost()
  1369. if host != nil {
  1370. db.OpsLog.LogEvent(host, db.ACT_HOST_DETACH_ISOLATED_DEVICE, self.GetShortDesc(ctx), userCred)
  1371. }
  1372. return self.RealDelete(ctx, userCred)
  1373. }
  1374. func (manager *SIsolatedDeviceManager) FindByHost(id string) []SIsolatedDevice {
  1375. return manager.FindByHosts([]string{id})
  1376. }
  1377. func (manager *SIsolatedDeviceManager) FindByHosts(ids []string) []SIsolatedDevice {
  1378. dest := make([]SIsolatedDevice, 0)
  1379. q := manager.Query().In("host_id", ids)
  1380. err := db.FetchModelObjects(manager, q, &dest)
  1381. if err != nil {
  1382. log.Errorln(err)
  1383. return nil
  1384. }
  1385. return dest
  1386. }
  1387. func (manager *SIsolatedDeviceManager) DeleteDevicesByHost(ctx context.Context, userCred mcclient.TokenCredential, host *SHost) {
  1388. for _, dev := range manager.FindByHost(host.Id) {
  1389. dev.Delete(ctx, userCred)
  1390. }
  1391. }
  1392. func (manager *SIsolatedDeviceManager) GetAllDevsOnHost(hostId string) ([]SIsolatedDevice, error) {
  1393. devs := make([]SIsolatedDevice, 0)
  1394. q := manager.Query().Equals("host_id", hostId)
  1395. err := db.FetchModelObjects(manager, q, &devs)
  1396. if err != nil {
  1397. return nil, err
  1398. }
  1399. if len(devs) == 0 {
  1400. return nil, nil
  1401. }
  1402. return devs, nil
  1403. }
  1404. func (manager *SIsolatedDeviceManager) GetUnusedDevsOnHost(hostId string, model string, count int) ([]SIsolatedDevice, error) {
  1405. devs := make([]SIsolatedDevice, 0)
  1406. q := manager.Query().Equals("host_id", hostId).Equals("model", model).IsNullOrEmpty("guest_id")
  1407. if count > 0 {
  1408. q = q.Limit(count)
  1409. }
  1410. err := db.FetchModelObjects(manager, q, &devs)
  1411. if err != nil {
  1412. return nil, err
  1413. }
  1414. if len(devs) == 0 {
  1415. return nil, nil
  1416. }
  1417. return devs, nil
  1418. }
  1419. func (manager *SIsolatedDeviceManager) hostHasDevAddr(hostId, addr, mdevId string) (bool, error) {
  1420. cnt, err := manager.Query().Equals("addr", addr).Equals("mdev_id", mdevId).
  1421. Equals("host_id", hostId).CountWithError()
  1422. if err != nil {
  1423. return false, err
  1424. }
  1425. return cnt != 0, nil
  1426. }
  1427. func (manager *SIsolatedDeviceManager) CheckModelIsEmpty(model, vendor, device, devType string) (bool, error) {
  1428. cnt, err := manager.Query().Equals("model", model).
  1429. Equals("dev_type", devType).
  1430. Equals("vendor_device_id", fmt.Sprintf("%s:%s", vendor, device)).
  1431. IsNotEmpty("guest_id").CountWithError()
  1432. if err != nil {
  1433. return false, err
  1434. }
  1435. return cnt == 0, nil
  1436. }
  1437. func (manager *SIsolatedDeviceManager) GetHostsByModel(model, vendor, device, devType string) ([]string, error) {
  1438. q := manager.Query("host_id").Equals("model", model).
  1439. Equals("dev_type", devType).
  1440. Equals("vendor_device_id", fmt.Sprintf("%s:%s", vendor, device)).GroupBy("host_id")
  1441. rows, err := q.Rows()
  1442. if err != nil && err != sql.ErrNoRows {
  1443. return nil, errors.Wrap(err, "q.Rows")
  1444. }
  1445. if rows == nil {
  1446. return nil, nil
  1447. }
  1448. defer rows.Close()
  1449. ret := make([]string, 0)
  1450. for rows.Next() {
  1451. var hostId string
  1452. err = rows.Scan(&hostId)
  1453. if err != nil {
  1454. return nil, errors.Wrap(err, "rows.Scan")
  1455. }
  1456. ret = append(ret, hostId)
  1457. }
  1458. return ret, nil
  1459. }
  1460. func (self *SIsolatedDevice) GetUniqValues() jsonutils.JSONObject {
  1461. return jsonutils.Marshal(map[string]string{"host_id": self.HostId})
  1462. }
  1463. func (manager *SIsolatedDeviceManager) FetchUniqValues(ctx context.Context, data jsonutils.JSONObject) jsonutils.JSONObject {
  1464. hostId, _ := data.GetString("host_id")
  1465. return jsonutils.Marshal(map[string]string{"host_id": hostId})
  1466. }
  1467. func (manager *SIsolatedDeviceManager) FilterByUniqValues(q *sqlchemy.SQuery, values jsonutils.JSONObject) *sqlchemy.SQuery {
  1468. hostId, _ := values.GetString("host_id")
  1469. if len(hostId) > 0 {
  1470. q = q.Equals("host_id", hostId)
  1471. }
  1472. return q
  1473. }
  1474. func (manager *SIsolatedDeviceManager) NamespaceScope() rbacscope.TRbacScope {
  1475. if consts.IsDomainizedNamespace() {
  1476. return rbacscope.ScopeDomain
  1477. } else {
  1478. return rbacscope.ScopeSystem
  1479. }
  1480. }
  1481. func (manager *SIsolatedDeviceManager) ResourceScope() rbacscope.TRbacScope {
  1482. return rbacscope.ScopeProject
  1483. }
  1484. func (manager *SIsolatedDeviceManager) FilterByOwner(ctx context.Context, q *sqlchemy.SQuery, man db.FilterByOwnerProvider, userCred mcclient.TokenCredential, owner mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
  1485. if owner != nil {
  1486. switch scope {
  1487. case rbacscope.ScopeProject, rbacscope.ScopeDomain:
  1488. hostsQ := HostManager.Query("id")
  1489. hostsQ = HostManager.FilterByOwner(ctx, hostsQ, HostManager, userCred, owner, scope)
  1490. hosts := hostsQ.SubQuery()
  1491. q = q.Join(hosts, sqlchemy.Equals(q.Field("host_id"), hosts.Field("id")))
  1492. }
  1493. }
  1494. return q
  1495. }
  1496. func (manager *SIsolatedDeviceManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
  1497. return db.FetchDomainInfo(ctx, data)
  1498. }
  1499. func (model *SIsolatedDevice) GetOwnerId() mcclient.IIdentityProvider {
  1500. host := model.getHost()
  1501. if host != nil {
  1502. return host.GetOwnerId()
  1503. }
  1504. return nil
  1505. }
  1506. func (model *SIsolatedDevice) syncWithCloudIsolateDevice(ctx context.Context, userCred mcclient.TokenCredential, dev cloudprovider.IsolateDevice) error {
  1507. _, err := db.Update(model, func() error {
  1508. model.Name = dev.GetName()
  1509. model.Model = dev.GetModel()
  1510. model.Addr = dev.GetAddr()
  1511. model.DevType = dev.GetDevType()
  1512. model.NumaNode = dev.GetNumaNode()
  1513. model.VendorDeviceId = dev.GetVendorDeviceId()
  1514. return nil
  1515. })
  1516. if err != nil {
  1517. return err
  1518. }
  1519. sharedProjectIds, err := dev.GetSharedProjectIds()
  1520. if err != nil {
  1521. if errors.Cause(err) == cloudprovider.ErrNotImplemented {
  1522. return nil
  1523. }
  1524. return err
  1525. }
  1526. log.Infof("share projectIds: %s", sharedProjectIds)
  1527. if len(sharedProjectIds) == 0 {
  1528. return nil
  1529. }
  1530. host := model.getHost()
  1531. if host == nil {
  1532. return nil
  1533. }
  1534. if len(sharedProjectIds) > 0 {
  1535. projectIds, err := db.FetchField(ExternalProjectManager, "tenant_id", func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  1536. return q.Equals("manager_id", host.ManagerId).In("external_id", sharedProjectIds)
  1537. })
  1538. if err != nil {
  1539. return err
  1540. }
  1541. input := apis.PerformPublicProjectInput{SharedProjectIds: projectIds}
  1542. input.Scope = "project"
  1543. err = db.SharablePerformPublic(model, ctx, userCred, input)
  1544. if err != nil {
  1545. return errors.Wrapf(err, "SharablePerformPublic")
  1546. }
  1547. }
  1548. return nil
  1549. }
  1550. func (model *SIsolatedDevice) SetNetworkIndex(idx int) error {
  1551. _, err := db.Update(model, func() error {
  1552. model.NetworkIndex = idx
  1553. return nil
  1554. })
  1555. return err
  1556. }
  1557. func (model *SIsolatedDevice) GetRequiredSharedDomainIds() []string {
  1558. host := model.getHost()
  1559. if host != nil {
  1560. return []string{host.DomainId}
  1561. }
  1562. return []string{}
  1563. }
  1564. func (model *SIsolatedDevice) GetSharableTargetDomainIds() []string {
  1565. return nil
  1566. }
  1567. func (model *SIsolatedDevice) GetSharedDomains() []string {
  1568. return db.SharableGetSharedProjects(model, db.SharedTargetDomain)
  1569. }
  1570. func (model *SIsolatedDevice) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicProjectInput) (jsonutils.JSONObject, error) {
  1571. err := db.SharablePerformPublic(model, ctx, userCred, input)
  1572. if err != nil {
  1573. return nil, errors.Wrap(err, "SharablePerformPublic")
  1574. }
  1575. return nil, nil
  1576. }
  1577. func (model *SIsolatedDevice) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) (jsonutils.JSONObject, error) {
  1578. err := db.SharablePerformPrivate(model, ctx, userCred)
  1579. if err != nil {
  1580. return nil, errors.Wrap(err, "SharablePerformPrivate")
  1581. }
  1582. return nil, nil
  1583. }
  1584. type HostIsolatedDevicesNumaStat struct {
  1585. NumaNode int8
  1586. NumaNodeDevCount int
  1587. }
  1588. func (manager *SIsolatedDeviceManager) GetHostAllocatedIsolatedDeviceNumaStats(devType, hostId string) ([]HostIsolatedDevicesNumaStat, error) {
  1589. q := manager.Query().Equals("host_id", hostId).Equals("dev_type", devType)
  1590. guestQ := GuestManager.Query().SubQuery()
  1591. q = q.Join(guestQ, sqlchemy.Equals(q.Field("guest_id"), guestQ.Field("id")))
  1592. q = q.Filter(sqlchemy.NotEquals(guestQ.Field("status"), api.VM_READY)).GroupBy(q.Field("numa_node"))
  1593. q = q.AppendField(sqlchemy.COUNT("numa_node_dev_count", q.Field("numa_node")))
  1594. subQ := q.SubQuery()
  1595. numaQ := subQ.Query(subQ.Field("numa_node"), subQ.Field("numa_node_dev_count"))
  1596. stats := make([]HostIsolatedDevicesNumaStat, 0)
  1597. err := numaQ.All(&stats)
  1598. if err != nil {
  1599. return nil, err
  1600. }
  1601. return stats, nil
  1602. }
  1603. func (host *SHost) VirtualDeviceNumaBalance(devType string, numaNode int8) (bool, error) {
  1604. if numaNode < 0 {
  1605. return true, nil
  1606. }
  1607. stats, err := IsolatedDeviceManager.GetHostAllocatedIsolatedDeviceNumaStats(devType, host.Id)
  1608. if err != nil {
  1609. return true, err
  1610. }
  1611. log.Debugf("VirtualDeviceNumaBalance numa stats %s", jsonutils.Marshal(stats))
  1612. if len(stats) <= 1 {
  1613. return true, nil
  1614. }
  1615. sort.Slice(stats, func(i, j int) bool {
  1616. return stats[i].NumaNodeDevCount > stats[j].NumaNodeDevCount
  1617. })
  1618. if stats[0].NumaNodeDevCount-stats[len(stats)-1].NumaNodeDevCount > 1 {
  1619. return false, nil
  1620. }
  1621. return true, nil
  1622. }