guest_template.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  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. "fmt"
  18. "time"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/rbacscope"
  23. "yunion.io/x/pkg/util/sets"
  24. "yunion.io/x/pkg/utils"
  25. "yunion.io/x/sqlchemy"
  26. "yunion.io/x/onecloud/pkg/apis"
  27. api "yunion.io/x/onecloud/pkg/apis/compute"
  28. computeapis "yunion.io/x/onecloud/pkg/apis/compute"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/cmdline"
  30. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  32. "yunion.io/x/onecloud/pkg/compute/options"
  33. "yunion.io/x/onecloud/pkg/httperrors"
  34. "yunion.io/x/onecloud/pkg/mcclient"
  35. "yunion.io/x/onecloud/pkg/mcclient/auth"
  36. "yunion.io/x/onecloud/pkg/mcclient/modules/image"
  37. "yunion.io/x/onecloud/pkg/util/logclient"
  38. "yunion.io/x/onecloud/pkg/util/stringutils2"
  39. )
  40. const (
  41. IMAGE_TYPE_NORMAL = "normal"
  42. IMAGE_TYPE_GUEST = "guest"
  43. )
  44. type SGuestTemplateManager struct {
  45. db.SSharableVirtualResourceBaseManager
  46. SCloudregionResourceBaseManager
  47. SVpcResourceBaseManager
  48. }
  49. type SGuestTemplate struct {
  50. db.SSharableVirtualResourceBase
  51. SCloudregionResourceBase
  52. SVpcResourceBase
  53. // 虚拟机CPU数量
  54. VcpuCount int `nullable:"false" default:"1" create:"optional" json:"vcpu_count"`
  55. // 虚拟机内存大小(MB)
  56. VmemSize int `nullable:"false" create:"optional" json:"vmem_size"`
  57. // 虚拟机操作系统类型
  58. // pattern:Linux|Windows|VMWare
  59. OsType string `width:"36" charset:"ascii" nullable:"true" create:"optional" json:"os_type" list:"user" get:"user"`
  60. // 镜像类型
  61. ImageType string `width:"10" charset:"ascii" nullabel:"true" default:"normal" create:"optional" json:"image_type"`
  62. // 镜像ID
  63. ImageId string `width:"128" charset:"ascii" create:"optional" json:"image_id"`
  64. // 虚拟机技术
  65. Hypervisor string `width:"16" charset:"ascii" default:"kvm" create:"optional" json:"hypervisor"`
  66. // 计费方式
  67. BillingType string `width:"16" charset:"ascii" default:"postpaid" create:"optional" list:"user" get:"user" json:"billing_type"`
  68. // 其他配置信息
  69. Content jsonutils.JSONObject `nullable:"false" list:"user" update:"user" create:"optional" json:"content"`
  70. LastCheckTime time.Time
  71. }
  72. var GuestTemplateManager *SGuestTemplateManager
  73. func init() {
  74. GuestTemplateManager = &SGuestTemplateManager{
  75. SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
  76. SGuestTemplate{},
  77. "guesttemplates_tbl",
  78. "servertemplate",
  79. "servertemplates",
  80. ),
  81. }
  82. GuestTemplateManager.SetVirtualObject(GuestTemplateManager)
  83. }
  84. func (gtm *SGuestTemplateManager) ValidateCreateData(
  85. ctx context.Context,
  86. userCred mcclient.TokenCredential,
  87. ownerId mcclient.IIdentityProvider,
  88. query jsonutils.JSONObject,
  89. input computeapis.GuestTemplateCreateInput,
  90. ) (computeapis.GuestTemplateCreateInput, error) {
  91. var err error
  92. if input.Content == nil {
  93. return input, httperrors.NewMissingParameterError("content")
  94. }
  95. if !input.Content.Contains("name") && !input.Content.Contains("generate_name") {
  96. input.Content.Set("generate_name", jsonutils.NewString(input.Name))
  97. }
  98. input.GuestTemplateInput, err = gtm.validateData(ctx, userCred, ownerId, query, input.GuestTemplateInput)
  99. if err != nil {
  100. return input, errors.Wrap(err, "gtm.validateData")
  101. }
  102. input.SharableVirtualResourceCreateInput, err = gtm.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
  103. if err != nil {
  104. return input, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ValidateCreateData")
  105. }
  106. return input, nil
  107. }
  108. func (gt *SGuestTemplate) PostCreate(ctx context.Context, userCred mcclient.TokenCredential,
  109. ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  110. gt.SetStatus(ctx, userCred, computeapis.GT_READY, "")
  111. gt.updateCheckTime()
  112. logclient.AddActionLogWithContext(ctx, gt, logclient.ACT_CREATE, nil, userCred, true)
  113. }
  114. func (gt *SGuestTemplate) updateCheckTime() error {
  115. _, err := db.Update(gt, func() error {
  116. gt.LastCheckTime = time.Now()
  117. return nil
  118. })
  119. return err
  120. }
  121. func (gt *SGuestTemplate) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential,
  122. query jsonutils.JSONObject, data jsonutils.JSONObject) {
  123. logclient.AddActionLogWithContext(ctx, gt, logclient.ACT_UPDATE, nil, userCred, true)
  124. }
  125. var HypervisorBrandMap = map[string]string{
  126. computeapis.HYPERVISOR_KVM: computeapis.CLOUD_PROVIDER_ONECLOUD,
  127. computeapis.HYPERVISOR_ESXI: computeapis.CLOUD_PROVIDER_VMWARE,
  128. computeapis.HYPERVISOR_ALIYUN: computeapis.CLOUD_PROVIDER_ALIYUN,
  129. computeapis.HYPERVISOR_QCLOUD: computeapis.CLOUD_PROVIDER_QCLOUD,
  130. computeapis.HYPERVISOR_AZURE: computeapis.CLOUD_PROVIDER_AZURE,
  131. computeapis.HYPERVISOR_AWS: computeapis.CLOUD_PROVIDER_AWS,
  132. computeapis.HYPERVISOR_HUAWEI: computeapis.CLOUD_PROVIDER_HUAWEI,
  133. computeapis.HYPERVISOR_OPENSTACK: computeapis.CLOUD_PROVIDER_OPENSTACK,
  134. computeapis.HYPERVISOR_UCLOUD: computeapis.CLOUD_PROVIDER_UCLOUD,
  135. computeapis.HYPERVISOR_ZSTACK: computeapis.CLOUD_PROVIDER_ZSTACK,
  136. computeapis.HYPERVISOR_GOOGLE: computeapis.CLOUD_PROVIDER_GOOGLE,
  137. computeapis.HYPERVISOR_CTYUN: computeapis.CLOUD_PROVIDER_CTYUN,
  138. computeapis.HYPERVISOR_CNWARE: computeapis.CLOUD_PROVIDER_CNWARE,
  139. }
  140. var BrandHypervisorMap = map[string]string{
  141. computeapis.CLOUD_PROVIDER_ONECLOUD: computeapis.HYPERVISOR_KVM,
  142. computeapis.CLOUD_PROVIDER_VMWARE: computeapis.HYPERVISOR_ESXI,
  143. computeapis.CLOUD_PROVIDER_ALIYUN: computeapis.HYPERVISOR_ALIYUN,
  144. computeapis.CLOUD_PROVIDER_QCLOUD: computeapis.HYPERVISOR_QCLOUD,
  145. computeapis.CLOUD_PROVIDER_AZURE: computeapis.HYPERVISOR_AZURE,
  146. computeapis.CLOUD_PROVIDER_AWS: computeapis.HYPERVISOR_AWS,
  147. computeapis.CLOUD_PROVIDER_HUAWEI: computeapis.HYPERVISOR_HUAWEI,
  148. computeapis.CLOUD_PROVIDER_OPENSTACK: computeapis.HYPERVISOR_OPENSTACK,
  149. computeapis.CLOUD_PROVIDER_UCLOUD: computeapis.HYPERVISOR_UCLOUD,
  150. computeapis.CLOUD_PROVIDER_ZSTACK: computeapis.HYPERVISOR_ZSTACK,
  151. computeapis.CLOUD_PROVIDER_GOOGLE: computeapis.HYPERVISOR_GOOGLE,
  152. computeapis.CLOUD_PROVIDER_CTYUN: computeapis.HYPERVISOR_CTYUN,
  153. computeapis.CLOUD_PROVIDER_CNWARE: computeapis.HYPERVISOR_CNWARE,
  154. }
  155. func Hypervisor2Brand(hypervisor string) string {
  156. brand, ok := HypervisorBrandMap[hypervisor]
  157. if !ok {
  158. return "unkown"
  159. }
  160. return brand
  161. }
  162. func Brand2Hypervisor(brand string) string {
  163. hypervisor, ok := BrandHypervisorMap[brand]
  164. if !ok {
  165. return "unkown"
  166. }
  167. return hypervisor
  168. }
  169. func (gtm *SGuestTemplateManager) validateContent(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, content *jsonutils.JSONDict) (*computeapis.ServerCreateInput, error) {
  170. // hack
  171. if !content.Contains("name") && !content.Contains("generate_name") {
  172. content.Set("generate_name", jsonutils.NewString("fake_name"))
  173. }
  174. input, err := GuestManager.validateCreateData(ctx, userCred, ownerId, query, content)
  175. if err != nil {
  176. return nil, httperrors.NewInputParameterError("%v", err)
  177. }
  178. // check Image
  179. imageId := input.Disks[0].ImageId
  180. image, err := CachedimageManager.getImageInfo(ctx, userCred, imageId, false)
  181. if err != nil {
  182. return nil, errors.Wrapf(err, "getImageInfo of '%s'", imageId)
  183. }
  184. if image == nil {
  185. return nil, fmt.Errorf("no such image %s", imageId)
  186. }
  187. return input, nil
  188. }
  189. func (gtm *SGuestTemplateManager) validateData(
  190. ctx context.Context,
  191. userCred mcclient.TokenCredential,
  192. ownerId mcclient.IIdentityProvider,
  193. query jsonutils.JSONObject,
  194. cinput computeapis.GuestTemplateInput,
  195. ) (computeapis.GuestTemplateInput, error) {
  196. if cinput.Content == nil {
  197. return cinput, nil
  198. }
  199. content := cinput.Content
  200. // data := cinput.JSON(cinput)
  201. // not support guest image and guest snapshot for now
  202. if content.Contains("instance_snapshot_id") {
  203. return cinput, httperrors.NewInputParameterError(
  204. "no support for instance snapshot in guest template for now")
  205. }
  206. // I don't hope cinput.Content same with data["content"] will change in GuestManager.validateCreateData
  207. copy := jsonutils.DeepCopy(content).(*jsonutils.JSONDict)
  208. input, err := gtm.validateContent(ctx, userCred, ownerId, query, copy)
  209. if err != nil {
  210. return cinput, httperrors.NewInputParameterError("%v", err)
  211. }
  212. log.Debugf("data: %#v", input)
  213. // fill field
  214. cinput.VmemSize = input.VmemSize
  215. // data.Add(jsonutils.NewInt(int64(input.VmemSize)), "vmem_size")
  216. cinput.VcpuCount = input.VcpuCount
  217. // data.Add(jsonutils.NewInt(int64(input.VcpuCount)), "vcpu_count")
  218. cinput.OsType = input.OsType
  219. // data.Add(jsonutils.NewString(input.OsType), "os_type")
  220. cinput.Hypervisor = input.Hypervisor
  221. // data.Add(jsonutils.NewString(input.Hypervisor), "hypervisor")
  222. cinput.InstanceType = input.InstanceType
  223. cinput.CloudregionId = input.PreferRegion
  224. cinput.BillingType = input.BillingType
  225. // fill vpc
  226. if len(input.Networks) != 0 && len(input.Networks[0].Network) != 0 {
  227. model, err := NetworkManager.FetchById(input.Networks[0].Network)
  228. if err != nil {
  229. return cinput, errors.Wrap(err, "NetworkManager.FetchById")
  230. }
  231. net := model.(*SNetwork)
  232. vpc, _ := net.GetVpc()
  233. if vpc != nil {
  234. cinput.VpcId = vpc.Id
  235. }
  236. }
  237. if len(input.GuestImageID) > 0 {
  238. cinput.ImageType = IMAGE_TYPE_GUEST
  239. cinput.ImageId = input.GuestImageID
  240. // data.Add(jsonutils.NewString(IMAGE_TYPE_GUEST), "image_type")
  241. // data.Add(jsonutils.NewString(input.GuestImageID), "image_id")
  242. } else {
  243. cinput.ImageType = input.GuestImageID
  244. cinput.ImageId = input.Disks[0].ImageId // if input.Didks is empty???
  245. // data.Add(jsonutils.NewString(input.Disks[0].ImageId), "image_id")
  246. // data.Add(jsonutils.NewString(IMAGE_TYPE_NORMAL), "image_type")
  247. }
  248. // hide some properties
  249. content.Remove("name")
  250. content.Remove("generate_name")
  251. // "__count__" was converted to "count" by apigateway
  252. content.Remove("count")
  253. content.Remove("project_id")
  254. content.Remove("__count__")
  255. cinput.Content = content
  256. // data.Add(contentDict, "content")
  257. return cinput, nil
  258. }
  259. func (gt *SGuestTemplate) ValidateUpdateData(
  260. ctx context.Context,
  261. userCred mcclient.TokenCredential,
  262. query jsonutils.JSONObject,
  263. input computeapis.GuestTemplateUpdateInput,
  264. ) (computeapis.GuestTemplateUpdateInput, error) {
  265. var err error
  266. input.GuestTemplateInput, err = GuestTemplateManager.validateData(ctx, userCred, gt.GetOwnerId(), query, input.GuestTemplateInput)
  267. if err != nil {
  268. return input, errors.Wrap(err, "GuestTemplateManager.validateData")
  269. }
  270. input.SharableVirtualResourceBaseUpdateInput, err = gt.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput)
  271. if err != nil {
  272. return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData")
  273. }
  274. return input, nil
  275. }
  276. func (manager *SGuestTemplateManager) FetchCustomizeColumns(
  277. ctx context.Context,
  278. userCred mcclient.TokenCredential,
  279. query jsonutils.JSONObject,
  280. objs []interface{},
  281. fields stringutils2.SSortedStrings,
  282. isList bool,
  283. ) []computeapis.GuestTemplateDetails {
  284. rows := make([]computeapis.GuestTemplateDetails, len(objs))
  285. virtRows := manager.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  286. crRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  287. vpcRows := manager.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  288. for i := range rows {
  289. rows[i] = computeapis.GuestTemplateDetails{
  290. SharableVirtualResourceDetails: virtRows[i],
  291. CloudregionResourceInfo: crRows[i],
  292. VpcResourceInfo: vpcRows[i],
  293. }
  294. rows[i], _ = objs[i].(*SGuestTemplate).getMoreDetails(ctx, userCred, rows[i])
  295. }
  296. return rows
  297. }
  298. func (gt *SGuestTemplate) getMoreDetails(ctx context.Context, userCred mcclient.TokenCredential,
  299. out computeapis.GuestTemplateDetails) (computeapis.GuestTemplateDetails, error) {
  300. input, err := cmdline.FetchServerCreateInputByJSON(gt.Content)
  301. if err != nil {
  302. return out, err
  303. }
  304. configInfo := computeapis.GuestTemplateConfigInfo{}
  305. if len(input.PreferZone) != 0 {
  306. zone := ZoneManager.FetchZoneById(input.PreferZone)
  307. if zone != nil {
  308. input.PreferZone = zone.GetName()
  309. out.ZoneId = zone.GetId()
  310. }
  311. out.Zone = input.PreferZone
  312. }
  313. out.Brand = Hypervisor2Brand(gt.Hypervisor)
  314. // metadata
  315. configInfo.Metadata = input.Metadata
  316. // sku deal
  317. if len(input.InstanceType) > 0 {
  318. skuOutput := computeapis.GuestTemplateSku{}
  319. sku, err := ServerSkuManager.FetchSkuByNameAndProvider(input.InstanceType, out.Provider, true)
  320. if err != nil {
  321. skuOutput.Name = input.InstanceType
  322. skuOutput.MemorySizeMb = gt.VmemSize
  323. skuOutput.CpuCoreCount = gt.VcpuCount
  324. } else {
  325. skuOutput.Name = sku.Name
  326. skuOutput.MemorySizeMb = sku.MemorySizeMB
  327. skuOutput.CpuCoreCount = sku.CpuCoreCount
  328. skuOutput.InstanceTypeCategory = sku.InstanceTypeCategory
  329. skuOutput.InstanceTypeFamily = sku.InstanceTypeFamily
  330. }
  331. configInfo.Sku = skuOutput
  332. }
  333. // disk deal
  334. disks := make([]computeapis.GuestTemplateDisk, len(input.Disks))
  335. for i := range input.Disks {
  336. disks[i] = computeapis.GuestTemplateDisk{
  337. Backend: input.Disks[i].Backend,
  338. DiskType: input.Disks[i].DiskType,
  339. Index: input.Disks[i].Index,
  340. SizeMb: input.Disks[i].SizeMb,
  341. }
  342. }
  343. configInfo.Disks = disks
  344. // keypair
  345. if len(input.KeypairId) > 0 {
  346. model, err := KeypairManager.FetchByIdOrName(ctx, userCred, input.KeypairId)
  347. if err == nil {
  348. keypair := model.(*SKeypair)
  349. configInfo.Keypair = keypair.GetName()
  350. }
  351. }
  352. // network
  353. if len(input.Networks) > 0 {
  354. networkList := make([]computeapis.GuestTemplateNetwork, 0, len(input.Networks))
  355. networkIdList := make([]string, len(input.Networks))
  356. for i := range input.Networks {
  357. networkIdList[i] = input.Networks[i].Network
  358. }
  359. networkSet := sets.NewString(networkIdList...)
  360. wireQuery := WireManager.Query("id", "vpc_id").SubQuery()
  361. vpcQuery := VpcManager.Query("id", "name").SubQuery()
  362. q := NetworkManager.Query("id", "name", "wire_id", "guest_ip_start", "guest_ip_end", "vlan_id")
  363. if len(networkIdList) == 1 {
  364. q = q.Equals("id", networkIdList[0])
  365. } else {
  366. q = q.In("id", networkIdList)
  367. }
  368. q = q.LeftJoin(wireQuery, sqlchemy.Equals(q.Field("wire_id"), wireQuery.Field("id")))
  369. q = q.LeftJoin(vpcQuery, sqlchemy.Equals(wireQuery.Field("vpc_id"), vpcQuery.Field("id")))
  370. q = q.AppendField(vpcQuery.Field("id", "vpc_id"), vpcQuery.Field("name", "vpc_name"))
  371. q.All(&networkList)
  372. for _, p := range networkList {
  373. if networkSet.Has(p.ID) {
  374. networkSet.Delete(p.ID)
  375. }
  376. }
  377. // some specified network
  378. for _, id := range networkSet.UnsortedList() {
  379. networkList = append(networkList, computeapis.GuestTemplateNetwork{ID: id})
  380. }
  381. configInfo.Nets = networkList
  382. }
  383. if len(input.Secgroups) > 0 {
  384. q := SecurityGroupManager.Query("id", "name").In("id", input.Secgroups)
  385. rows, err := q.Rows()
  386. if err != nil {
  387. return out, errors.Wrap(err, "SQuery.Rows")
  388. }
  389. names := make([]string, 0, len(input.Secgroups))
  390. for rows.Next() {
  391. var id, name string
  392. rows.Scan(&id, &name)
  393. names = append(names, name)
  394. }
  395. rows.Close()
  396. out.Secgroups = names
  397. }
  398. // isolatedDevices
  399. if input.IsolatedDevices != nil && len(input.IsolatedDevices) != 0 {
  400. configInfo.IsolatedDeviceConfig = make([]computeapis.IsolatedDeviceConfig, len(input.IsolatedDevices))
  401. for i := range configInfo.IsolatedDeviceConfig {
  402. configInfo.IsolatedDeviceConfig[i] = *input.IsolatedDevices[i]
  403. }
  404. }
  405. // fill image info
  406. switch gt.ImageType {
  407. case IMAGE_TYPE_NORMAL:
  408. image, err := CachedimageManager.getImageInfo(ctx, userCred, gt.ImageId, false)
  409. if err == nil {
  410. configInfo.Image = image.Name
  411. } else {
  412. configInfo.Image = gt.ImageId
  413. }
  414. case IMAGE_TYPE_GUEST:
  415. s := auth.GetSession(ctx, userCred, options.Options.Region)
  416. ret, err := image.GuestImages.Get(s, gt.ImageId, jsonutils.JSONNull)
  417. if err != nil || !ret.Contains("id") {
  418. configInfo.Image = gt.ImageId
  419. } else {
  420. name, _ := ret.GetString("id")
  421. configInfo.Image = name
  422. }
  423. default:
  424. // no arrivals
  425. }
  426. // reset_password
  427. if input.ResetPassword == nil {
  428. configInfo.ResetPassword = true
  429. } else {
  430. configInfo.ResetPassword = *input.ResetPassword
  431. }
  432. out.ConfigInfo = configInfo
  433. return out, nil
  434. }
  435. func (gt *SGuestTemplate) PerformPublic(
  436. ctx context.Context,
  437. userCred mcclient.TokenCredential,
  438. query jsonutils.JSONObject,
  439. data apis.PerformPublicProjectInput,
  440. ) (jsonutils.JSONObject, error) {
  441. // image, network, secgroup, instancegroup
  442. input, err := cmdline.FetchServerCreateInputByJSON(gt.Content)
  443. if err != nil {
  444. return nil, errors.Wrap(err, "fail to convert content of guest template to ServerCreateInput")
  445. }
  446. // check for below private resource in the guest template
  447. privateResource := map[string]int{
  448. "keypair": len(input.KeypairId),
  449. "instance group": len(input.InstanceGroupIds),
  450. "instance snapshot": len(input.InstanceSnapshotId),
  451. }
  452. for k, v := range privateResource {
  453. if v > 0 {
  454. return nil, gt.genForbiddenError(k, "", "")
  455. }
  456. }
  457. targetScopeStr := data.Scope
  458. targetScope := rbacscope.String2ScopeDefault(targetScopeStr, rbacscope.ScopeSystem)
  459. // check if secgroup is public
  460. if len(input.SecgroupId) > 0 {
  461. model, err := SecurityGroupManager.FetchByIdOrName(ctx, userCred, input.SecgroupId)
  462. if err != nil {
  463. return nil, httperrors.NewResourceNotFoundError("there is no such secgroup %s descripted by guest template",
  464. input.SecgroupId)
  465. }
  466. secgroup := model.(*SSecurityGroup)
  467. sgScope := rbacscope.String2Scope(secgroup.PublicScope)
  468. if !secgroup.IsPublic || !sgScope.HigherEqual(targetScope) {
  469. return nil, gt.genForbiddenError("security group", input.SecgroupId, string(targetScope))
  470. }
  471. }
  472. // check if networks is public
  473. if len(input.Networks) > 0 {
  474. for i := range input.Networks {
  475. str := input.Networks[i].Network
  476. model, err := NetworkManager.FetchByIdOrName(ctx, userCred, str)
  477. if err != nil {
  478. return nil, httperrors.NewResourceNotFoundError(
  479. "there is no such secgroup %s descripted by guest template", str)
  480. }
  481. network := model.(*SNetwork)
  482. netScope := rbacscope.String2Scope(network.PublicScope)
  483. if !network.IsPublic || !netScope.HigherEqual(targetScope) {
  484. return nil, gt.genForbiddenError("network", str, string(targetScope))
  485. }
  486. }
  487. }
  488. // check if image is public
  489. var (
  490. isPublic bool
  491. publicScope string
  492. )
  493. switch gt.ImageType {
  494. case IMAGE_TYPE_NORMAL:
  495. image, err := CachedimageManager.GetImageById(ctx, userCred, gt.ImageId, false)
  496. if err != nil {
  497. return nil, errors.Wrapf(err, "fail to fetch image %s descripted by guest template", gt.ImageId)
  498. }
  499. isPublic, publicScope = image.IsPublic, image.PublicScope
  500. case IMAGE_TYPE_GUEST:
  501. s := auth.GetSession(ctx, userCred, options.Options.Region)
  502. ret, err := image.GuestImages.Get(s, gt.ImageId, jsonutils.JSONNull)
  503. if err != nil {
  504. return nil, errors.Wrapf(err, "fail to fetch guest image %s descripted by guest template", gt.ImageId)
  505. }
  506. isPublic = jsonutils.QueryBoolean(ret, "is_public", false)
  507. publicScope, _ = ret.GetString("public_scope")
  508. default:
  509. //no arrivals
  510. }
  511. igScope := rbacscope.String2Scope(publicScope)
  512. if !isPublic || !igScope.HigherEqual(targetScope) {
  513. return nil, gt.genForbiddenError("image", "", string(targetScope))
  514. }
  515. return gt.SSharableVirtualResourceBase.PerformPublic(ctx, userCred, query, data)
  516. }
  517. func (gt *SGuestTemplate) genForbiddenError(resourceName, resourceStr, scope string) error {
  518. var (
  519. msgFmt string
  520. msgArgs []interface{}
  521. )
  522. if resourceStr == "" {
  523. msgFmt = "the %s in guest template is not a public resource"
  524. msgArgs = []interface{}{resourceName}
  525. } else {
  526. msgFmt = "the %s %q in guest template is not a public resource"
  527. msgArgs = []interface{}{resourceName, resourceStr}
  528. }
  529. if scope != "" {
  530. msgFmt += " in %s scope"
  531. msgArgs = append(msgArgs, scope)
  532. }
  533. return httperrors.NewForbiddenError(msgFmt, msgArgs...)
  534. }
  535. func (gt *SGuestTemplate) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
  536. // check service catelog
  537. q := ServiceCatalogManager.Query("name").Equals("guest_template_id", gt.Id)
  538. names := make([]struct{ Name string }, 0, 1)
  539. err := q.All(&names)
  540. if err != nil {
  541. return errors.Wrap(err, "SQuery.All")
  542. }
  543. if len(names) > 0 {
  544. return httperrors.NewForbiddenError("guest template %s used by service catalog %s", gt.Id, names[0].Name)
  545. }
  546. // check scaling group
  547. q = ScalingGroupManager.Query("name").Equals("guest_template_id", gt.Id)
  548. names = make([]struct{ Name string }, 0, 1)
  549. err = q.All(&names)
  550. if err != nil {
  551. return errors.Wrap(err, "SQuery.All")
  552. }
  553. if len(names) > 0 {
  554. return httperrors.NewForbiddenError("guest template %s used by scalig group %s", gt.Id, names[0].Name)
  555. }
  556. return nil
  557. }
  558. // 主机模板列表
  559. func (manager *SGuestTemplateManager) ListItemFilter(
  560. ctx context.Context,
  561. q *sqlchemy.SQuery,
  562. userCred mcclient.TokenCredential,
  563. input computeapis.GuestTemplateListInput,
  564. ) (*sqlchemy.SQuery, error) {
  565. var err error
  566. q, err = manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.SharableVirtualResourceListInput)
  567. if err != nil {
  568. return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemFilter")
  569. }
  570. q, err = manager.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, input.RegionalFilterListInput)
  571. if err != nil {
  572. return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
  573. }
  574. if len(input.VpcId) > 0 {
  575. q, err = manager.SVpcResourceBaseManager.ListItemFilter(ctx, q, userCred, input.VpcFilterListInput)
  576. if err != nil {
  577. return nil, errors.Wrap(err, "SVpcResourceBaseManager.ListItemFilter")
  578. }
  579. }
  580. if len(input.CloudEnv) > 0 {
  581. cloudregions := CloudregionManager.Query().SubQuery()
  582. q = q.Join(cloudregions, sqlchemy.Equals(q.Field("cloudregion_id"), cloudregions.Field("id")))
  583. switch input.CloudEnv {
  584. case api.CLOUD_ENV_PUBLIC_CLOUD:
  585. q = q.Filter(sqlchemy.In(cloudregions.Field("provider"), CloudproviderManager.GetPublicProviderProvidersQuery()))
  586. case api.CLOUD_ENV_PRIVATE_CLOUD:
  587. q = q.Filter(sqlchemy.In(cloudregions.Field("provider"), CloudproviderManager.GetPrivateProviderProvidersQuery()))
  588. case api.CLOUD_ENV_ON_PREMISE:
  589. q = q.Filter(sqlchemy.Equals(cloudregions.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD))
  590. case api.CLOUD_ENV_PRIVATE_ON_PREMISE:
  591. q = q.Filter(sqlchemy.OR(
  592. sqlchemy.Equals(cloudregions.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD),
  593. sqlchemy.In(cloudregions.Field("provider"), CloudproviderManager.GetPrivateProviderProvidersQuery()),
  594. ))
  595. }
  596. }
  597. if len(input.BillingType) > 0 {
  598. q = q.Equals("billing_type", input.BillingType)
  599. }
  600. if len(input.Brand) > 0 {
  601. q = q.Equals("hypervisor", Brand2Hypervisor(input.Brand))
  602. }
  603. return q, nil
  604. }
  605. func (manager *SGuestTemplateManager) OrderByExtraFields(
  606. ctx context.Context,
  607. q *sqlchemy.SQuery,
  608. userCred mcclient.TokenCredential,
  609. input computeapis.GuestTemplateListInput,
  610. ) (*sqlchemy.SQuery, error) {
  611. var err error
  612. q, err = manager.SSharableVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.SharableVirtualResourceListInput)
  613. if err != nil {
  614. return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.OrderByExtraFields")
  615. }
  616. q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.RegionalFilterListInput)
  617. if err != nil {
  618. return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
  619. }
  620. q, err = manager.SVpcResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.VpcFilterListInput)
  621. if err != nil {
  622. return nil, errors.Wrap(err, "SVpcResourceBaseManager.OrderByExtraFields")
  623. }
  624. return q, nil
  625. }
  626. func (manager *SGuestTemplateManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  627. var err error
  628. q, err = manager.SSharableVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
  629. if err == nil {
  630. return q, nil
  631. }
  632. q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
  633. if err == nil {
  634. return q, nil
  635. }
  636. q, err = manager.SVpcResourceBaseManager.QueryDistinctExtraField(q, field)
  637. if err == nil {
  638. return q, nil
  639. }
  640. return q, httperrors.ErrNotFound
  641. }
  642. type SGuestTemplateValidate struct {
  643. Hypervisor string
  644. CloudregionId string
  645. VpcId string
  646. NetworkIds []string
  647. }
  648. func (gt *SGuestTemplate) Validate(ctx context.Context, userCred mcclient.TokenCredential,
  649. ownerId mcclient.IIdentityProvider, stv SGuestTemplateValidate) (bool, string) {
  650. if stv.Hypervisor != "" && gt.Hypervisor != stv.Hypervisor {
  651. return false, fmt.Sprintf("GuestTemplate has mismatched hypervisor, need %s but %s", stv.Hypervisor, gt.Hypervisor)
  652. }
  653. if stv.CloudregionId != "" && gt.CloudregionId != stv.CloudregionId {
  654. return false, fmt.Sprintf("GuestTemplate has mismatched cloudregion, need %s but %s", stv.CloudregionId, gt.CloudregionId)
  655. }
  656. if stv.VpcId != "" && gt.VpcId != "" && stv.VpcId != gt.VpcId {
  657. return false, fmt.Sprintf("GuestTemplate has mismatched vpc, need %s bu %s", stv.VpcId, gt.VpcId)
  658. }
  659. // check networks
  660. input, err := GuestTemplateManager.validateContent(ctx, userCred, ownerId, jsonutils.NewDict(), gt.Content.(*jsonutils.JSONDict))
  661. if err != nil {
  662. return false, err.Error()
  663. }
  664. if len(input.Networks) != 0 && len(input.Networks[0].Network) != 0 {
  665. for i := range input.Networks {
  666. if !utils.IsInStringArray(input.Networks[i].Network, stv.NetworkIds) {
  667. return false, fmt.Sprintf("GuestTemplate's network '%s' not in networks '%s'", input.Networks[i].Network, stv.NetworkIds)
  668. }
  669. }
  670. }
  671. return true, ""
  672. }
  673. func (manager *SGuestTemplateManager) ListItemExportKeys(ctx context.Context,
  674. q *sqlchemy.SQuery,
  675. userCred mcclient.TokenCredential,
  676. keys stringutils2.SSortedStrings,
  677. ) (*sqlchemy.SQuery, error) {
  678. var err error
  679. q, err = manager.SSharableVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  680. if err != nil {
  681. return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemExportKeys")
  682. }
  683. if keys.ContainsAny(manager.SCloudregionResourceBaseManager.GetExportKeys()...) {
  684. q, err = manager.SCloudregionResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  685. if err != nil {
  686. return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemExportKeys")
  687. }
  688. }
  689. if keys.ContainsAny(manager.SVpcResourceBaseManager.GetExportKeys()...) {
  690. q, err = manager.SVpcResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  691. if err != nil {
  692. return nil, errors.Wrap(err, "SVpcResourceBaseManager.ListItemExportKeys")
  693. }
  694. }
  695. return q, nil
  696. }
  697. func (g *SGuest) PerformSaveTemplate(ctx context.Context, userCred mcclient.TokenCredential,
  698. query jsonutils.JSONObject, input computeapis.GuestSaveToTemplateInput) (jsonutils.JSONObject, error) {
  699. g.SetStatus(ctx, userCred, computeapis.VM_TEMPLATE_SAVING, "save to template")
  700. if len(input.Name) == 0 && len(input.GenerateName) == 0 {
  701. input.GenerateName = fmt.Sprintf("%s-template", g.Name)
  702. }
  703. data := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  704. if task, err := taskman.TaskManager.NewTask(ctx, "GuestSaveTemplateTask", g, userCred, data, "", "", nil); err != nil {
  705. return nil, errors.Wrap(err, "Unbale to init 'GuestSaveTemplateTask'")
  706. } else {
  707. task.ScheduleRun(nil)
  708. }
  709. return nil, nil
  710. }
  711. func (gt *SGuestTemplate) PerformInspect(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  712. return nil, gt.inspect(ctx, userCred)
  713. }
  714. func (gt *SGuestTemplate) inspect(ctx context.Context, userCred mcclient.TokenCredential) error {
  715. _, err := GuestTemplateManager.validateContent(ctx, userCred, gt.GetOwnerId(), jsonutils.NewDict(), gt.Content.(*jsonutils.JSONDict))
  716. if err == nil {
  717. gt.updateCheckTime()
  718. gt.SetStatus(ctx, userCred, computeapis.GT_READY, "inspect successfully")
  719. logclient.AddSimpleActionLog(gt, logclient.ACT_HEALTH_CHECK, "", userCred, true)
  720. return nil
  721. }
  722. // invalid
  723. gt.updateCheckTime()
  724. reason := fmt.Sprintf("During the inspection, the guest template is not available: %s", err.Error())
  725. gt.SetStatus(ctx, userCred, computeapis.GT_INVALID, reason)
  726. logclient.AddSimpleActionLog(gt, logclient.ACT_HEALTH_CHECK, reason, userCred, false)
  727. return nil
  728. }
  729. func (gm *SGuestTemplateManager) InspectAllTemplate(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  730. lastCheckTime := time.Now().Add(time.Duration(-options.Options.GuestTemplateCheckInterval) * time.Hour)
  731. q := gm.Query()
  732. q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("last_check_time")), sqlchemy.LE(q.Field("last_check_time"),
  733. lastCheckTime)))
  734. gts := make([]SGuestTemplate, 0, 10)
  735. err := db.FetchModelObjects(gm, q, &gts)
  736. if err != nil {
  737. log.Errorf("Unable to fetch all guest templates that need to check: %s", err.Error())
  738. return
  739. }
  740. for i := range gts {
  741. gts[i].inspect(ctx, userCred)
  742. }
  743. }