endpoints.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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. "crypto/tls"
  18. "database/sql"
  19. "fmt"
  20. "strings"
  21. "sync"
  22. "time"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/log"
  25. "yunion.io/x/pkg/errors"
  26. "yunion.io/x/pkg/gotypes"
  27. "yunion.io/x/pkg/tristate"
  28. "yunion.io/x/sqlchemy"
  29. "yunion.io/x/onecloud/pkg/apis"
  30. api "yunion.io/x/onecloud/pkg/apis/identity"
  31. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  32. "yunion.io/x/onecloud/pkg/cloudcommon/etcd"
  33. "yunion.io/x/onecloud/pkg/cloudcommon/informer"
  34. "yunion.io/x/onecloud/pkg/httperrors"
  35. "yunion.io/x/onecloud/pkg/keystone/options"
  36. "yunion.io/x/onecloud/pkg/mcclient"
  37. "yunion.io/x/onecloud/pkg/util/logclient"
  38. "yunion.io/x/onecloud/pkg/util/seclib2"
  39. "yunion.io/x/onecloud/pkg/util/stringutils2"
  40. )
  41. type SEndpointManager struct {
  42. db.SStandaloneResourceBaseManager
  43. SServiceResourceBaseManager
  44. SRegionResourceBaseManager
  45. informerBackends map[string]informer.IInformerBackend
  46. informerSetter sync.Once
  47. }
  48. var EndpointManager *SEndpointManager
  49. func init() {
  50. EndpointManager = &SEndpointManager{
  51. SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
  52. SEndpoint{},
  53. "endpoint",
  54. "endpoint",
  55. "endpoints",
  56. ),
  57. }
  58. EndpointManager.SetVirtualObject(EndpointManager)
  59. }
  60. /*
  61. +--------------------+--------------+------+-----+---------+-------+
  62. | Field | Type | Null | Key | Default | Extra |
  63. +--------------------+--------------+------+-----+---------+-------+
  64. | id | varchar(64) | NO | PRI | NULL | |
  65. | legacy_endpoint_id | varchar(64) | YES | | NULL | |
  66. | interface | varchar(8) | NO | | NULL | |
  67. | service_id | varchar(64) | NO | MUL | NULL | |
  68. | url | text | NO | | NULL | |
  69. | extra | text | YES | | NULL | |
  70. | enabled | tinyint(1) | NO | | 1 | |
  71. | region_id | varchar(255) | YES | MUL | NULL | |
  72. | created_at | datetime | YES | | NULL | |
  73. +--------------------+--------------+------+-----+---------+-------+
  74. */
  75. type SEndpoint struct {
  76. db.SStandaloneResourceBase
  77. LegacyEndpointId string `width:"64" charset:"ascii" nullable:"true"`
  78. Interface string `width:"8" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
  79. ServiceId string `width:"64" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
  80. Url string `charset:"utf8" nullable:"false" list:"admin" update:"admin" create:"admin_required"`
  81. Extra *jsonutils.JSONDict `nullable:"true"`
  82. Enabled tristate.TriState `default:"true" list:"admin" update:"admin" create:"admin_optional"`
  83. RegionId string `width:"255" charset:"utf8" nullable:"true" list:"admin" create:"admin_required"`
  84. ServiceCertificateId string `nullable:"true" create:"admin_optional" update:"admin"`
  85. }
  86. func (manager *SEndpointManager) InitializeData() error {
  87. q := manager.Query()
  88. q = q.IsNullOrEmpty("name")
  89. eps := make([]SEndpoint, 0)
  90. err := db.FetchModelObjects(manager, q, &eps)
  91. if err != nil {
  92. return err
  93. }
  94. for i := range eps {
  95. if gotypes.IsNil(eps[i].Extra) {
  96. continue
  97. }
  98. name, _ := eps[i].Extra.GetString("name")
  99. desc, _ := eps[i].Extra.GetString("description")
  100. if len(name) == 0 {
  101. serv := eps[i].getService()
  102. if serv != nil {
  103. name = fmt.Sprintf("%s-%s", serv.Type, eps[i].Interface)
  104. }
  105. }
  106. if len(name) > 0 {
  107. db.Update(&eps[i], func() error {
  108. eps[i].Name = name
  109. eps[i].Description = desc
  110. return nil
  111. })
  112. }
  113. }
  114. if err := manager.SetInformerBackend(); err != nil {
  115. log.Errorf("init informer backend error: %v", err)
  116. }
  117. return nil
  118. }
  119. func (manager *SEndpointManager) SetInformerBackend() error {
  120. informerEp, err := manager.fetchInformerEndpoint()
  121. if err != nil {
  122. return errors.Wrap(err, "fetch informer endpoint")
  123. }
  124. manager.SetInformerBackendUntilSuccess(informerEp)
  125. return nil
  126. }
  127. func (manager *SEndpointManager) getSessionEndpointType() string {
  128. epType := api.EndpointInterfaceInternal
  129. if options.Options.SessionEndpointType != "" {
  130. epType = options.Options.SessionEndpointType
  131. }
  132. return epType
  133. }
  134. func (manager *SEndpointManager) IsEtcdInformerBackend(ep *SEndpoint) bool {
  135. if ep == nil {
  136. return false
  137. }
  138. svc := ep.getService()
  139. if svc == nil {
  140. return false
  141. }
  142. if svc.GetName() != apis.SERVICE_TYPE_ETCD {
  143. return false
  144. }
  145. epType := manager.getSessionEndpointType()
  146. if ep.Interface != epType {
  147. return false
  148. }
  149. return true
  150. }
  151. func (manager *SEndpointManager) SetInformerBackendUntilSuccess(ep *SEndpoint) {
  152. if informer.GetDefaultBackend() != nil {
  153. log.Infof("Informer backend has been setted")
  154. return
  155. }
  156. manager.informerSetter.Do(func() {
  157. go func() {
  158. for {
  159. if err := manager.SetEtcdInformerBackend(ep); err != nil {
  160. log.Errorf("Set etcd informer backend failed: %s", err)
  161. time.Sleep(time.Second * 30)
  162. } else {
  163. break
  164. }
  165. }
  166. }()
  167. })
  168. }
  169. func (manager *SEndpointManager) SetInformerBackendByEndpoint(ep *SEndpoint) {
  170. if !manager.IsEtcdInformerBackend(ep) {
  171. return
  172. }
  173. manager.SetInformerBackendUntilSuccess(ep)
  174. }
  175. func (manager *SEndpointManager) fetchInformerEndpoint() (*SEndpoint, error) {
  176. epType := manager.getSessionEndpointType()
  177. endpoints := manager.Query().SubQuery()
  178. services := ServiceManager.Query().SubQuery()
  179. regions := RegionManager.Query().SubQuery()
  180. q := endpoints.Query()
  181. q = q.Join(regions, sqlchemy.Equals(endpoints.Field("region_id"), regions.Field("id")))
  182. q = q.Join(services, sqlchemy.Equals(endpoints.Field("service_id"), services.Field("id")))
  183. q = q.Filter(sqlchemy.AND(
  184. sqlchemy.Equals(endpoints.Field("interface"), epType),
  185. sqlchemy.IsTrue(endpoints.Field("enabled"))))
  186. q = q.Filter(sqlchemy.AND(
  187. sqlchemy.IsTrue(services.Field("enabled")),
  188. sqlchemy.Equals(services.Field("type"), apis.SERVICE_TYPE_ETCD)))
  189. informerEp := new(SEndpoint)
  190. if err := q.First(informerEp); err != nil {
  191. return nil, err
  192. }
  193. informerEp.SetModelManager(manager, informerEp)
  194. return informerEp, nil
  195. }
  196. func (manager *SEndpointManager) newEtcdInformerBackend(ep *SEndpoint) (informer.IInformerBackend, error) {
  197. useTLS := false
  198. var (
  199. tlsCfg *tls.Config
  200. )
  201. if ep.ServiceCertificateId != "" {
  202. useTLS = true
  203. cert, err := ep.getServiceCertificate()
  204. if err != nil {
  205. return nil, errors.Wrap(err, "get service certificate")
  206. }
  207. caData := []byte(cert.CaCertificate)
  208. certData := []byte(cert.Certificate)
  209. keyData := []byte(cert.PrivateKey)
  210. cfg, err := seclib2.InitTLSConfigByData(caData, certData, keyData)
  211. if err != nil {
  212. return nil, errors.Wrap(err, "build TLS config")
  213. }
  214. // always set insecure skip verify
  215. cfg.InsecureSkipVerify = true
  216. tlsCfg = cfg
  217. }
  218. opt := &etcd.SEtcdOptions{
  219. EtcdEndpoint: []string{ep.Url},
  220. EtcdTimeoutSeconds: 5,
  221. EtcdRequestTimeoutSeconds: 10,
  222. EtcdLeaseExpireSeconds: 5,
  223. }
  224. if useTLS {
  225. opt.TLSConfig = tlsCfg
  226. opt.EtcdEnabldSsl = true
  227. }
  228. return informer.NewEtcdBackend(opt, nil)
  229. }
  230. func (manager *SEndpointManager) SetEtcdInformerBackend(ep *SEndpoint) error {
  231. be, err := manager.newEtcdInformerBackend(ep)
  232. if err != nil {
  233. return errors.Wrap(err, "new etcd informer backend")
  234. }
  235. informer.Set(be)
  236. log.Infof("Informer set etcd backend success")
  237. return nil
  238. }
  239. func (ep *SEndpoint) getService() *SService {
  240. srvObj, err := ServiceManager.FetchById(ep.ServiceId)
  241. if err != nil {
  242. return nil
  243. }
  244. return srvObj.(*SService)
  245. }
  246. type SEndpointExtended struct {
  247. Id string `json:"id"`
  248. Name string `json:"name"`
  249. Interface string `json:"interface"`
  250. Url string `json:"url"`
  251. Region string `json:"region"`
  252. RegionId string `json:"region_id"`
  253. ServiceId string `json:"service_id"`
  254. ServiceType string `json:"service_type"`
  255. ServiceName string `json:"service_name"`
  256. }
  257. type SServiceCatalog []SEndpointExtended
  258. func (manager *SEndpointManager) FetchAll() (SServiceCatalog, error) {
  259. endpoints := manager.Query().SubQuery()
  260. services := ServiceManager.Query().SubQuery()
  261. regions := RegionManager.Query().SubQuery()
  262. q := endpoints.Query(
  263. endpoints.Field("id"),
  264. endpoints.Field("name"),
  265. endpoints.Field("interface"),
  266. endpoints.Field("url"),
  267. endpoints.Field("region_id"),
  268. regions.Field("name", "region"),
  269. endpoints.Field("service_id"),
  270. services.Field("type", "service_type"),
  271. services.Field("name", "service_name"),
  272. )
  273. q = q.Join(regions, sqlchemy.Equals(endpoints.Field("region_id"), regions.Field("id")))
  274. q = q.Join(services, sqlchemy.Equals(endpoints.Field("service_id"), services.Field("id")))
  275. q = q.Filter(sqlchemy.IsTrue(endpoints.Field("enabled")))
  276. q = q.Filter(sqlchemy.IsTrue(services.Field("enabled")))
  277. eps := make([]SEndpointExtended, 0)
  278. err := q.All(&eps)
  279. if err != nil && err != sql.ErrNoRows {
  280. return nil, err
  281. }
  282. return eps, nil
  283. }
  284. func (cata SServiceCatalog) GetKeystoneCatalogV3() mcclient.KeystoneServiceCatalogV3 {
  285. ksCata := make(map[string]mcclient.KeystoneServiceV3)
  286. for i := range cata {
  287. srvId := cata[i].ServiceId
  288. srv, ok := ksCata[srvId]
  289. if !ok {
  290. srv = mcclient.KeystoneServiceV3{
  291. Id: srvId,
  292. Name: cata[i].ServiceName,
  293. Type: cata[i].ServiceType,
  294. Endpoints: make([]mcclient.KeystoneEndpointV3, 0),
  295. }
  296. }
  297. ep := mcclient.KeystoneEndpointV3{
  298. Id: cata[i].Id,
  299. Name: cata[i].Name,
  300. Interface: cata[i].Interface,
  301. Region: cata[i].Region,
  302. RegionId: cata[i].RegionId,
  303. Url: cata[i].Url,
  304. }
  305. srv.Endpoints = append(srv.Endpoints, ep)
  306. ksCata[cata[i].ServiceId] = srv
  307. }
  308. results := make([]mcclient.KeystoneServiceV3, 0)
  309. for k := range ksCata {
  310. results = append(results, ksCata[k])
  311. }
  312. return results
  313. }
  314. func (cata SServiceCatalog) GetKeystoneCatalogV2() mcclient.KeystoneServiceCatalogV2 {
  315. ksCata := make(map[string]mcclient.KeystoneServiceV2)
  316. for i := range cata {
  317. srvId := cata[i].ServiceId
  318. srv, ok := ksCata[srvId]
  319. if !ok {
  320. srv = mcclient.KeystoneServiceV2{
  321. Name: cata[i].ServiceName,
  322. Type: cata[i].ServiceType,
  323. Endpoints: make([]mcclient.KeystoneEndpointV2, 0),
  324. }
  325. }
  326. findIdx := -1
  327. for j := range srv.Endpoints {
  328. if srv.Endpoints[j].Region == cata[i].RegionId {
  329. findIdx = j
  330. }
  331. }
  332. if findIdx < 0 {
  333. ep := mcclient.KeystoneEndpointV2{
  334. Id: cata[i].Id,
  335. Region: cata[i].RegionId,
  336. }
  337. srv.Endpoints = append(srv.Endpoints, ep)
  338. findIdx = len(srv.Endpoints) - 1
  339. }
  340. switch cata[i].Interface {
  341. case api.EndpointInterfacePublic:
  342. srv.Endpoints[findIdx].PublicURL = cata[i].Url
  343. case api.EndpointInterfaceInternal:
  344. srv.Endpoints[findIdx].InternalURL = cata[i].Url
  345. case api.EndpointInterfaceAdmin:
  346. srv.Endpoints[findIdx].AdminURL = cata[i].Url
  347. }
  348. ksCata[cata[i].ServiceId] = srv
  349. }
  350. results := make([]mcclient.KeystoneServiceV2, 0)
  351. for k := range ksCata {
  352. results = append(results, ksCata[k])
  353. }
  354. return results
  355. }
  356. func (endpoint *SEndpoint) getMoreDetails(details api.EndpointDetails) (api.EndpointDetails, error) {
  357. if len(endpoint.ServiceCertificateId) > 0 {
  358. cert, _ := endpoint.getServiceCertificate()
  359. if cert != nil {
  360. certOutput := cert.ToOutput()
  361. details.CertificateDetails = *certOutput
  362. }
  363. }
  364. return details, nil
  365. }
  366. func (endpoint *SEndpoint) getServiceCertificate() (*SServiceCertificate, error) {
  367. certId := endpoint.ServiceCertificateId
  368. if certId == "" {
  369. return nil, nil
  370. }
  371. icert, err := ServiceCertificateManager.FetchById(certId)
  372. if err != nil {
  373. return nil, err
  374. }
  375. return icert.(*SServiceCertificate), nil
  376. }
  377. func (manager *SEndpointManager) FetchCustomizeColumns(
  378. ctx context.Context,
  379. userCred mcclient.TokenCredential,
  380. query jsonutils.JSONObject,
  381. objs []interface{},
  382. fields stringutils2.SSortedStrings,
  383. isList bool,
  384. ) []api.EndpointDetails {
  385. rows := make([]api.EndpointDetails, len(objs))
  386. stdRows := manager.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  387. serviceIds := stringutils2.SSortedStrings{}
  388. for i := range objs {
  389. rows[i] = api.EndpointDetails{
  390. StandaloneResourceDetails: stdRows[i],
  391. }
  392. ep := objs[i].(*SEndpoint)
  393. serviceIds = stringutils2.Append(serviceIds, ep.ServiceId)
  394. ep.SetModelManager(manager, ep)
  395. rows[i], _ = ep.getMoreDetails(rows[i])
  396. }
  397. if len(fields) == 0 || fields.Contains("service_name") || fields.Contains("service_type") {
  398. svs := fetchServices(serviceIds)
  399. if svs != nil {
  400. for i := range rows {
  401. ep := objs[i].(*SEndpoint)
  402. if srv, ok := svs[ep.ServiceId]; ok {
  403. if len(fields) == 0 || fields.Contains("service_name") {
  404. rows[i].ServiceName = srv.Name
  405. }
  406. if len(fields) == 0 || fields.Contains("service_type") {
  407. rows[i].ServiceType = srv.Type
  408. }
  409. }
  410. }
  411. }
  412. }
  413. return rows
  414. }
  415. func fetchServices(srvIds []string) map[string]SService {
  416. q := ServiceManager.Query().In("id", srvIds)
  417. srvs := make([]SService, 0)
  418. err := db.FetchModelObjects(ServiceManager, q, &srvs)
  419. if err != nil {
  420. return nil
  421. }
  422. ret := make(map[string]SService)
  423. for i := range srvs {
  424. ret[srvs[i].Id] = srvs[i]
  425. }
  426. return ret
  427. }
  428. func (endpoint *SEndpoint) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
  429. if endpoint.Enabled.IsTrue() {
  430. return httperrors.NewInvalidStatusError("endpoint is enabled")
  431. }
  432. return endpoint.SStandaloneResourceBase.ValidateDeleteCondition(ctx, nil)
  433. }
  434. func (manager *SEndpointManager) ValidateCreateData(
  435. ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
  436. query jsonutils.JSONObject, data *jsonutils.JSONDict,
  437. ) (*jsonutils.JSONDict, error) {
  438. infname, _ := data.GetString("interface")
  439. if len(infname) == 0 {
  440. return nil, httperrors.NewInputParameterError("missing input field interface")
  441. }
  442. serviceStr := jsonutils.GetAnyString(data, []string{"service_id", "service"})
  443. if len(serviceStr) > 0 {
  444. servObj, err := ServiceManager.FetchByIdOrName(ctx, userCred, serviceStr)
  445. if err != nil {
  446. if err == sql.ErrNoRows {
  447. return nil, httperrors.NewResourceNotFoundError2(ServiceManager.Keyword(), serviceStr)
  448. } else {
  449. return nil, httperrors.NewGeneralError(err)
  450. }
  451. }
  452. service := servObj.(*SService)
  453. if !data.Contains("name") {
  454. nameStr := fmt.Sprintf("%s-%s", service.Type, infname)
  455. data.Set("name", jsonutils.NewString(nameStr))
  456. }
  457. data.Set("service_id", jsonutils.NewString(service.Id))
  458. } else {
  459. return nil, httperrors.NewInputParameterError("missing input field service/service_id")
  460. }
  461. if certId, _ := data.GetString("service_certificate"); len(certId) > 0 {
  462. cert, err := ServiceCertificateManager.FetchByIdOrName(ctx, userCred, certId)
  463. if err == sql.ErrNoRows {
  464. return nil, httperrors.NewNotFoundError("not found cert %s", certId)
  465. }
  466. if err != nil {
  467. return nil, err
  468. }
  469. data.Set("service_certificate_id", jsonutils.NewString(cert.GetId()))
  470. }
  471. input := apis.StandaloneResourceCreateInput{}
  472. err := data.Unmarshal(&input)
  473. if err != nil {
  474. return nil, httperrors.NewInternalServerError("unmarshal StandaloneResourceCreateInput fail %s", err)
  475. }
  476. input, err = manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input)
  477. if err != nil {
  478. return nil, err
  479. }
  480. data.Update(jsonutils.Marshal(input))
  481. return data, nil
  482. }
  483. // 服务地址列表
  484. func (manager *SEndpointManager) ListItemFilter(
  485. ctx context.Context,
  486. q *sqlchemy.SQuery,
  487. userCred mcclient.TokenCredential,
  488. query api.EndpointListInput,
  489. ) (*sqlchemy.SQuery, error) {
  490. q, err := manager.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StandaloneResourceListInput)
  491. if err != nil {
  492. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemFilter")
  493. }
  494. q, err = manager.SServiceResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ServiceFilterListInput)
  495. if err != nil {
  496. return nil, errors.Wrap(err, "SServiceResourceBaseManager.ListItemFilter")
  497. }
  498. q, err = manager.SRegionResourceBaseManager.ListItemFilter(ctx, q, userCred, query.RegionFilterListInput)
  499. if err != nil {
  500. return nil, errors.Wrap(err, "SRegionResourceBaseManager.ListItemFilter")
  501. }
  502. if query.Enabled != nil {
  503. if *query.Enabled {
  504. q = q.IsTrue("enabled")
  505. } else {
  506. q = q.IsFalse("enabled")
  507. }
  508. }
  509. if len(query.Interface) > 0 {
  510. infType := strings.TrimSuffix(query.Interface, "URL")
  511. q = q.Equals("interface", infType)
  512. }
  513. return q, nil
  514. }
  515. func (manager *SEndpointManager) OrderByExtraFields(
  516. ctx context.Context,
  517. q *sqlchemy.SQuery,
  518. userCred mcclient.TokenCredential,
  519. query api.EndpointListInput,
  520. ) (*sqlchemy.SQuery, error) {
  521. var err error
  522. q, err = manager.SStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StandaloneResourceListInput)
  523. if err != nil {
  524. return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.OrderByExtraFields")
  525. }
  526. if db.NeedOrderQuery([]string{query.OrderByService}) {
  527. services := ServiceManager.Query("id", "name").SubQuery()
  528. q = q.LeftJoin(services, sqlchemy.Equals(q.Field("service_id"), services.Field("id")))
  529. if sqlchemy.SQL_ORDER_ASC.Equals(query.OrderByService) {
  530. q = q.Asc(services.Field("name"))
  531. } else {
  532. q = q.Desc(services.Field("name"))
  533. }
  534. }
  535. return q, nil
  536. }
  537. func (manager *SEndpointManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  538. var err error
  539. q, err = manager.SStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
  540. if err == nil {
  541. return q, nil
  542. }
  543. return q, httperrors.ErrNotFound
  544. }
  545. func (endpoint *SEndpoint) trySetInformerBackend() {
  546. EndpointManager.SetInformerBackendByEndpoint(endpoint)
  547. }
  548. func (endpoint *SEndpoint) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  549. endpoint.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
  550. logclient.AddActionLogWithContext(ctx, endpoint, logclient.ACT_CREATE, data, userCred, true)
  551. refreshDefaultClientServiceCatalog()
  552. endpoint.trySetInformerBackend()
  553. }
  554. func (endpoint *SEndpoint) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  555. endpoint.SStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
  556. logclient.AddActionLogWithContext(ctx, endpoint, logclient.ACT_UPDATE, data, userCred, true)
  557. refreshDefaultClientServiceCatalog()
  558. endpoint.trySetInformerBackend()
  559. }
  560. func (endpoint *SEndpoint) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
  561. endpoint.SStandaloneResourceBase.PostDelete(ctx, userCred)
  562. logclient.AddActionLogWithContext(ctx, endpoint, logclient.ACT_DELETE, nil, userCred, true)
  563. refreshDefaultClientServiceCatalog()
  564. if EndpointManager.IsEtcdInformerBackend(endpoint) {
  565. // remove informer backend
  566. informer.Set(nil)
  567. }
  568. }
  569. func (endpoint *SEndpoint) ValidateUpdateData(
  570. ctx context.Context, userCred mcclient.TokenCredential,
  571. query jsonutils.JSONObject, data *jsonutils.JSONDict,
  572. ) (*jsonutils.JSONDict, error) {
  573. if certId, _ := data.GetString("service_certificate"); len(certId) > 0 {
  574. cert, err := ServiceCertificateManager.FetchByIdOrName(ctx, userCred, certId)
  575. if err == sql.ErrNoRows {
  576. return nil, httperrors.NewNotFoundError("not found cert %s", certId)
  577. }
  578. if err != nil {
  579. return nil, err
  580. }
  581. data.Set("service_certificate_id", jsonutils.NewString(cert.GetId()))
  582. }
  583. return data, nil
  584. }