elasticips.go 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085
  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. "sort"
  20. "time"
  21. "yunion.io/x/cloudmux/pkg/cloudprovider"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/gotypes"
  26. "yunion.io/x/pkg/tristate"
  27. "yunion.io/x/pkg/util/compare"
  28. "yunion.io/x/pkg/util/pinyinutils"
  29. "yunion.io/x/pkg/util/rbacscope"
  30. "yunion.io/x/pkg/utils"
  31. "yunion.io/x/sqlchemy"
  32. "yunion.io/x/onecloud/pkg/apis"
  33. billing_api "yunion.io/x/onecloud/pkg/apis/billing"
  34. api "yunion.io/x/onecloud/pkg/apis/compute"
  35. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  36. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  37. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  38. "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
  39. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  40. "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
  41. "yunion.io/x/onecloud/pkg/cloudcommon/policy"
  42. "yunion.io/x/onecloud/pkg/cloudcommon/validators"
  43. "yunion.io/x/onecloud/pkg/compute/options"
  44. "yunion.io/x/onecloud/pkg/httperrors"
  45. "yunion.io/x/onecloud/pkg/mcclient"
  46. "yunion.io/x/onecloud/pkg/util/rbacutils"
  47. "yunion.io/x/onecloud/pkg/util/stringutils2"
  48. )
  49. // +onecloud:swagger-gen-model-singular=eip
  50. // +onecloud:swagger-gen-model-plural=eips
  51. type SElasticipManager struct {
  52. db.SVirtualResourceBaseManager
  53. db.SExternalizedResourceBaseManager
  54. SManagedResourceBaseManager
  55. SCloudregionResourceBaseManager
  56. }
  57. var ElasticipManager *SElasticipManager
  58. func init() {
  59. ElasticipManager = &SElasticipManager{
  60. SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
  61. SElasticip{},
  62. "elasticips_tbl",
  63. "eip",
  64. "eips",
  65. ),
  66. }
  67. ElasticipManager.SetVirtualObject(ElasticipManager)
  68. ElasticipManager.TableSpec().AddIndex(true, "associate_id", "associate_type")
  69. }
  70. type SElasticip struct {
  71. db.SVirtualResourceBase
  72. db.SExternalizedResourceBase
  73. SManagedResourceBase
  74. SCloudregionResourceBase `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"`
  75. SBillingResourceBase
  76. // IP子网Id, 仅私有云不为空
  77. NetworkId string `width:"36" charset:"ascii" nullable:"true" get:"user" list:"user" create:"optional"`
  78. // 标识弹性或非弹性
  79. // | Mode | 说明 |
  80. // |------------|------------|
  81. // | public_ip | 公网IP |
  82. // | elastic_ip | 弹性公网IP |
  83. //
  84. // example: elastic_ip
  85. Mode string `width:"32" charset:"ascii" get:"user" list:"user" create:"optional"`
  86. // IP地址
  87. IpAddr string `width:"17" charset:"ascii" list:"user" update:"admin"`
  88. // 绑定资源类型
  89. AssociateType string `width:"32" charset:"ascii" list:"user" update:"admin"`
  90. // 绑定资源Id
  91. AssociateId string `width:"256" charset:"ascii" list:"user" update:"admin"`
  92. // 带宽大小
  93. Bandwidth int `list:"user" create:"optional" default:"0"`
  94. SBillingChargeTypeBase
  95. // 计费类型: 流量、带宽
  96. // example: bandwidth
  97. // ChargeType billing_api.TNetChargeType `width:"64" name:"charge_type" list:"user" create:"required"`
  98. // 线路类型
  99. BgpType string `width:"64" charset:"utf8" nullable:"true" get:"user" list:"user" create:"optional"`
  100. // 是否跟随主机删除而自动释放
  101. AutoDellocate tristate.TriState `default:"false" get:"user" create:"optional" update:"user"`
  102. // 区域Id
  103. // CloudregionId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"`
  104. }
  105. // 弹性公网IP列表
  106. func (manager *SElasticipManager) ListItemFilter(
  107. ctx context.Context,
  108. q *sqlchemy.SQuery,
  109. userCred mcclient.TokenCredential,
  110. query api.ElasticipListInput,
  111. ) (*sqlchemy.SQuery, error) {
  112. var err error
  113. q, err = manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
  114. if err != nil {
  115. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
  116. }
  117. q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
  118. if err != nil {
  119. return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
  120. }
  121. q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
  122. if err != nil {
  123. return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
  124. }
  125. q, err = manager.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, query.RegionalFilterListInput)
  126. if err != nil {
  127. return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
  128. }
  129. associateType := query.UsableEipForAssociateType
  130. associateId := query.UsableEipForAssociateId
  131. if len(associateType) > 0 && len(associateId) > 0 {
  132. q = q.Equals("status", api.EIP_STATUS_READY)
  133. switch associateType {
  134. case api.EIP_ASSOCIATE_TYPE_SERVER:
  135. serverObj, err := GuestManager.FetchByIdOrName(ctx, userCred, associateId)
  136. if err != nil {
  137. if errors.Cause(err) == sql.ErrNoRows {
  138. return nil, httperrors.NewResourceNotFoundError("server %s not found", associateId)
  139. }
  140. return nil, httperrors.NewGeneralError(err)
  141. }
  142. guest := serverObj.(*SGuest)
  143. region, err := guest.GetRegion()
  144. if err != nil {
  145. return nil, errors.Wrapf(err, "GetRegion")
  146. }
  147. if guest.Hypervisor == api.HYPERVISOR_KVM || (utils.IsInStringArray(region.Provider, api.PRIVATE_CLOUD_PROVIDERS) &&
  148. guest.Hypervisor != api.HYPERVISOR_HCSO && guest.Hypervisor != api.HYPERVISOR_HCS) {
  149. zone, _ := guest.getZone()
  150. networks := NetworkManager.Query().SubQuery()
  151. wires := WireManager.Query().SubQuery()
  152. sq := networks.Query(networks.Field("id")).Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))).
  153. Filter(sqlchemy.Equals(wires.Field("zone_id"), zone.Id)).SubQuery()
  154. q = q.Filter(sqlchemy.In(q.Field("network_id"), sq))
  155. gns := GuestnetworkManager.Query("network_id").Equals("guest_id", guest.Id).SubQuery()
  156. q = q.Filter(sqlchemy.NotIn(q.Field("network_id"), gns))
  157. } else {
  158. region, _ := guest.getRegion()
  159. q = q.Equals("cloudregion_id", region.Id)
  160. }
  161. host, _ := guest.GetHost()
  162. managerId := host.ManagerId
  163. if managerId != "" {
  164. q = q.Equals("manager_id", managerId)
  165. } else {
  166. q = q.IsNullOrEmpty("manager_id")
  167. }
  168. case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
  169. groupObj, err := GroupManager.FetchByIdOrName(ctx, userCred, associateId)
  170. if err != nil {
  171. if errors.Cause(err) == sql.ErrNoRows {
  172. return nil, httperrors.NewResourceNotFoundError2(GroupManager.Keyword(), associateId)
  173. }
  174. return nil, httperrors.NewGeneralError(err)
  175. }
  176. group := groupObj.(*SGroup)
  177. net, err := group.getAttachedNetwork()
  178. if err != nil {
  179. return nil, errors.Wrap(err, "group.getAttachedNetwork")
  180. }
  181. if net == nil {
  182. return nil, errors.Wrap(httperrors.ErrInvalidStatus, "group is not attached to network")
  183. }
  184. zone, _ := net.GetZone()
  185. networks := NetworkManager.Query().SubQuery()
  186. wires := WireManager.Query().SubQuery()
  187. sq := networks.Query(networks.Field("id")).Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))).
  188. Filter(sqlchemy.Equals(wires.Field("zone_id"), zone.Id)).SubQuery()
  189. q = q.Filter(sqlchemy.In(q.Field("network_id"), sq))
  190. q = q.Filter(sqlchemy.NotEquals(q.Field("network_id"), net.Id))
  191. q = q.IsNullOrEmpty("manager_id")
  192. case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
  193. _nat, err := validators.ValidateModel(ctx, userCred, NatGatewayManager, &query.UsableEipForAssociateId)
  194. if err != nil {
  195. return nil, err
  196. }
  197. nat := _nat.(*SNatGateway)
  198. vpc, err := nat.GetVpc()
  199. if err != nil {
  200. return nil, httperrors.NewGeneralError(errors.Wrapf(err, "nat.GetVpc"))
  201. }
  202. q = q.Equals("cloudregion_id", vpc.CloudregionId)
  203. if len(vpc.ManagerId) > 0 {
  204. q = q.Equals("manager_id", vpc.ManagerId)
  205. }
  206. q = q.Filter(
  207. sqlchemy.OR(
  208. sqlchemy.Equals(q.Field("associate_id"), nat.Id),
  209. sqlchemy.IsNullOrEmpty(q.Field("associate_id")),
  210. ),
  211. )
  212. case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
  213. _lb, err := validators.ValidateModel(ctx, userCred, LoadbalancerManager, &query.UsableEipForAssociateId)
  214. if err != nil {
  215. return nil, err
  216. }
  217. lb := _lb.(*SLoadbalancer)
  218. q = q.Equals("cloudregion_id", lb.CloudregionId)
  219. if len(lb.ManagerId) > 0 {
  220. q = q.Equals("manager_id", lb.ManagerId)
  221. } else {
  222. zone, _ := lb.GetZone()
  223. networks := NetworkManager.Query().SubQuery()
  224. wires := WireManager.Query().SubQuery()
  225. sq := networks.Query(networks.Field("id")).Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))).
  226. Filter(sqlchemy.Equals(wires.Field("zone_id"), zone.Id)).SubQuery()
  227. q = q.Filter(sqlchemy.In(q.Field("network_id"), sq))
  228. gns := LoadbalancernetworkManager.Query("network_id").Equals("loadbalancer_id", lb.Id).SubQuery()
  229. q = q.Filter(sqlchemy.NotIn(q.Field("network_id"), gns))
  230. }
  231. q = q.IsNullOrEmpty("associate_type")
  232. default:
  233. return nil, httperrors.NewInputParameterError("Not support associate type %s, only support %s", associateType, api.EIP_ASSOCIATE_VALID_TYPES)
  234. }
  235. }
  236. if query.Usable != nil && *query.Usable {
  237. q = q.Equals("status", api.EIP_STATUS_READY)
  238. q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("associate_id")), sqlchemy.IsEmpty(q.Field("associate_id"))))
  239. }
  240. if query.IsAssociated != nil {
  241. if *query.IsAssociated {
  242. q = q.IsNotEmpty("associate_type")
  243. } else {
  244. q = q.IsNullOrEmpty("associate_type")
  245. }
  246. }
  247. if len(query.Mode) > 0 {
  248. q = q.In("mode", query.Mode)
  249. }
  250. if len(query.IpAddr) > 0 {
  251. q = q.In("ip_addr", query.IpAddr)
  252. }
  253. if len(query.AssociateType) > 0 {
  254. q = q.In("associate_type", query.AssociateType)
  255. }
  256. if len(query.AssociateId) > 0 {
  257. q = q.In("associate_id", query.AssociateId)
  258. }
  259. if len(query.ChargeType) > 0 {
  260. q = q.In("charge_type", query.ChargeType)
  261. }
  262. if len(query.BgpType) > 0 {
  263. q = q.In("bgp_type", query.BgpType)
  264. }
  265. if query.AutoDellocate != nil {
  266. if *query.AutoDellocate {
  267. q = q.IsTrue("auto_dellocate")
  268. } else {
  269. q = q.IsFalse("auto_dellocate")
  270. }
  271. }
  272. if len(query.AssociateName) > 0 {
  273. filters := []sqlchemy.ICondition{}
  274. likeQuery := func(sq *sqlchemy.SQuery) *sqlchemy.SQuery {
  275. conditions := []sqlchemy.ICondition{}
  276. for _, name := range query.AssociateName {
  277. conditions = append(conditions, sqlchemy.Contains(sq.Field("name"), name))
  278. }
  279. return sq.Filter(sqlchemy.OR(conditions...))
  280. }
  281. for _, m := range []db.IModelManager{
  282. GuestManager,
  283. GroupManager,
  284. LoadbalancerManager,
  285. NatGatewayManager,
  286. } {
  287. sq := m.Query("id")
  288. sq = likeQuery(sq)
  289. filters = append(filters, sqlchemy.In(q.Field("associate_id"), sq))
  290. }
  291. q = q.Filter(sqlchemy.OR(filters...))
  292. }
  293. return q, nil
  294. }
  295. func (manager *SElasticipManager) OrderByExtraFields(
  296. ctx context.Context,
  297. q *sqlchemy.SQuery,
  298. userCred mcclient.TokenCredential,
  299. query api.ElasticipListInput,
  300. ) (*sqlchemy.SQuery, error) {
  301. var err error
  302. q, err = manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
  303. if err != nil {
  304. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
  305. }
  306. q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
  307. if err != nil {
  308. return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
  309. }
  310. q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.RegionalFilterListInput)
  311. if err != nil {
  312. return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
  313. }
  314. if db.NeedOrderQuery([]string{query.OrderByIp}) {
  315. db.OrderByFields(q, []string{query.OrderByIp}, []sqlchemy.IQueryField{sqlchemy.INET_ATON(q.Field("ip_addr"))})
  316. }
  317. return q, nil
  318. }
  319. func (manager *SElasticipManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  320. var err error
  321. q, err = manager.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
  322. if err == nil {
  323. return q, nil
  324. }
  325. q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
  326. if err == nil {
  327. return q, nil
  328. }
  329. q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
  330. if err == nil {
  331. return q, nil
  332. }
  333. return q, httperrors.ErrNotFound
  334. }
  335. func (manager *SElasticipManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) {
  336. var err error
  337. q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraFields(q, resource, fields)
  338. if err == nil {
  339. return q, nil
  340. }
  341. return q, httperrors.ErrNotFound
  342. }
  343. func (self *SElasticip) GetNetwork() (*SNetwork, error) {
  344. network, err := NetworkManager.FetchById(self.NetworkId)
  345. if err != nil {
  346. return nil, err
  347. }
  348. return network.(*SNetwork), nil
  349. }
  350. func (self *SElasticip) GetZone() (*SZone, error) {
  351. network, err := self.GetNetwork()
  352. if err != nil {
  353. return nil, err
  354. }
  355. return network.GetZone()
  356. }
  357. func (self *SElasticip) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
  358. desc := self.SVirtualResourceBase.GetShortDesc(ctx)
  359. // desc.Add(jsonutils.NewString(self.ChargeType), "charge_type")
  360. desc.Add(jsonutils.NewInt(int64(self.Bandwidth)), "bandwidth")
  361. desc.Add(jsonutils.NewString(self.Mode), "mode")
  362. desc.Add(jsonutils.NewString(self.IpAddr), "ip_addr")
  363. desc.Add(jsonutils.NewString(self.BgpType), "bgp_type")
  364. // region := self.GetRegion()
  365. // if len(region.ExternalId) > 0 {
  366. // regionInfo := strings.Split(region.ExternalId, "/")
  367. // if len(regionInfo) == 2 {
  368. // desc.Add(jsonutils.NewString(strings.ToLower(regionInfo[0])), "hypervisor")
  369. // desc.Add(jsonutils.NewString(regionInfo[1]), "region")
  370. // }
  371. //}
  372. billingInfo := SCloudBillingInfo{}
  373. billingInfo.SCloudProviderInfo = self.getCloudProviderInfo()
  374. billingInfo.SBillingBaseInfo = self.getBillingBaseInfo()
  375. billingInfo.InternetChargeType = self.ChargeType
  376. if priceKey := self.GetMetadata(ctx, "ext:price_key", nil); len(priceKey) > 0 {
  377. billingInfo.PriceKey = priceKey
  378. }
  379. desc.Update(jsonutils.Marshal(billingInfo))
  380. return desc
  381. }
  382. func (manager *SElasticipManager) SyncEips(
  383. ctx context.Context,
  384. userCred mcclient.TokenCredential,
  385. provider *SCloudprovider,
  386. region *SCloudregion,
  387. eips []cloudprovider.ICloudEIP,
  388. syncOwnerId mcclient.IIdentityProvider,
  389. xor bool,
  390. ) compare.SyncResult {
  391. lockman.LockRawObject(ctx, manager.KeywordPlural(), region.Id)
  392. defer lockman.ReleaseRawObject(ctx, manager.KeywordPlural(), region.Id)
  393. syncResult := compare.SyncResult{}
  394. dbEips, err := region.GetElasticIps(provider.Id, api.EIP_MODE_STANDALONE_EIP)
  395. if err != nil {
  396. syncResult.Error(err)
  397. return syncResult
  398. }
  399. for i := range dbEips {
  400. if taskman.TaskManager.IsInTask(&dbEips[i]) {
  401. syncResult.Error(fmt.Errorf("object in task"))
  402. return syncResult
  403. }
  404. }
  405. removed := make([]SElasticip, 0)
  406. commondb := make([]SElasticip, 0)
  407. commonext := make([]cloudprovider.ICloudEIP, 0)
  408. added := make([]cloudprovider.ICloudEIP, 0)
  409. err = compare.CompareSets(dbEips, eips, &removed, &commondb, &commonext, &added)
  410. if err != nil {
  411. syncResult.Error(err)
  412. return syncResult
  413. }
  414. for i := 0; i < len(removed); i += 1 {
  415. err = removed[i].syncRemoveCloudEip(ctx, userCred)
  416. if err != nil {
  417. syncResult.DeleteError(err)
  418. } else {
  419. syncResult.Delete()
  420. }
  421. }
  422. if !xor {
  423. for i := 0; i < len(commondb); i += 1 {
  424. err = commondb[i].SyncWithCloudEip(ctx, userCred, provider, commonext[i], syncOwnerId)
  425. if err != nil {
  426. syncResult.UpdateError(err)
  427. continue
  428. }
  429. syncResult.Update()
  430. }
  431. }
  432. for i := 0; i < len(added); i += 1 {
  433. eip := &SElasticip{}
  434. eip.SetModelManager(ElasticipManager, eip)
  435. err := ElasticipManager.Query().
  436. Equals("ip_addr", added[i].GetIpAddr()).
  437. Equals("manager_id", provider.Id).
  438. Equals("cloudregion_id", region.Id).
  439. Equals("mode", api.EIP_MODE_INSTANCE_PUBLICIP).First(eip)
  440. // 公网IP转弹性IP
  441. if err == nil {
  442. err = eip.SyncWithCloudEip(ctx, userCred, provider, added[i], syncOwnerId)
  443. if err != nil {
  444. syncResult.UpdateError(err)
  445. continue
  446. }
  447. syncResult.Update()
  448. continue
  449. }
  450. _, err = manager.newFromCloudEip(ctx, userCred, added[i], provider, region, syncOwnerId)
  451. if err != nil {
  452. syncResult.AddError(err)
  453. } else {
  454. syncResult.Add()
  455. }
  456. }
  457. return syncResult
  458. }
  459. func (self *SElasticip) syncRemoveCloudEip(ctx context.Context, userCred mcclient.TokenCredential) error {
  460. lockman.LockObject(ctx, self)
  461. defer lockman.ReleaseObject(ctx, self)
  462. err := self.RealDelete(ctx, userCred)
  463. if err != nil {
  464. return err
  465. }
  466. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  467. Obj: self,
  468. Action: notifyclient.ActionSyncDelete,
  469. })
  470. return nil
  471. }
  472. func (self *SElasticip) SyncInstanceWithCloudEip(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudEIP) error {
  473. resource := self.GetAssociateResource()
  474. vmExtId := ext.GetAssociationExternalId()
  475. if resource == nil && len(vmExtId) == 0 {
  476. return nil
  477. }
  478. if resource != nil && resource.(db.IExternalizedModel).GetExternalId() == vmExtId {
  479. return nil
  480. }
  481. if resource != nil { // dissociate
  482. err := self.Dissociate(ctx, userCred)
  483. if err != nil {
  484. log.Errorf("fail to dissociate vm: %s", err)
  485. return err
  486. }
  487. }
  488. if len(vmExtId) > 0 {
  489. var manager db.IModelManager
  490. switch ext.GetAssociationType() {
  491. case api.EIP_ASSOCIATE_TYPE_SERVER:
  492. manager = GuestManager
  493. case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
  494. manager = NatGatewayManager
  495. case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
  496. manager = LoadbalancerManager
  497. // case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
  498. // not supported
  499. // manager = GroupManager
  500. default:
  501. return errors.Error("unsupported association type")
  502. }
  503. extRes, err := db.FetchByExternalIdAndManagerId(manager, vmExtId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  504. switch ext.GetAssociationType() {
  505. case api.EIP_ASSOCIATE_TYPE_SERVER:
  506. sq := HostManager.Query().SubQuery()
  507. return q.Join(sq, sqlchemy.Equals(sq.Field("id"), q.Field("host_id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), self.ManagerId))
  508. case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
  509. return q.Equals("manager_id", self.ManagerId)
  510. case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
  511. sq := VpcManager.Query("id").Equals("manager_id", self.ManagerId)
  512. return q.In("vpc_id", sq.SubQuery())
  513. }
  514. return q
  515. })
  516. if err != nil {
  517. return errors.Wrapf(err, "db.FetchByExternalIdAndManagerId %s %s", ext.GetAssociationType(), vmExtId)
  518. }
  519. err = self.AssociateInstance(ctx, userCred, ext.GetAssociationType(), extRes.(db.IStatusStandaloneModel))
  520. if err != nil {
  521. return errors.Wrapf(err, "AssociateInstance")
  522. }
  523. }
  524. return nil
  525. }
  526. func (self *SElasticip) SyncWithCloudEip(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, ext cloudprovider.ICloudEIP, syncOwnerId mcclient.IIdentityProvider) error {
  527. diff, err := db.UpdateWithLock(ctx, self, func() error {
  528. if options.Options.EnableSyncName {
  529. newName, _ := db.GenerateAlterName(self, ext.GetName())
  530. if len(newName) > 0 {
  531. self.Name = newName
  532. }
  533. }
  534. if bandwidth := ext.GetBandwidth(); bandwidth != 0 {
  535. self.Bandwidth = bandwidth
  536. }
  537. self.IpAddr = ext.GetIpAddr()
  538. self.Mode = ext.GetMode()
  539. self.Status = ext.GetStatus()
  540. self.ExternalId = ext.GetGlobalId()
  541. self.IsEmulated = ext.IsEmulated()
  542. self.AssociateType = ext.GetAssociationType()
  543. if chargeType := ext.GetInternetChargeType(); len(chargeType) > 0 {
  544. self.ChargeType = billing_api.TNetChargeType(chargeType)
  545. }
  546. self.BillingType = billing_api.TBillingType(ext.GetBillingType())
  547. self.ExpiredAt = time.Time{}
  548. self.AutoRenew = false
  549. if self.BillingType == billing_api.BILLING_TYPE_PREPAID {
  550. self.ExpiredAt = ext.GetExpiredAt()
  551. self.AutoRenew = ext.IsAutoRenew()
  552. }
  553. if createAt := ext.GetCreatedAt(); !createAt.IsZero() {
  554. self.CreatedAt = createAt
  555. }
  556. return nil
  557. })
  558. if err != nil {
  559. return errors.Wrapf(err, "db.UpdateWithLock")
  560. }
  561. db.OpsLog.LogSyncUpdate(self, diff, userCred)
  562. if len(diff) > 0 {
  563. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  564. Obj: self,
  565. Action: notifyclient.ActionSyncUpdate,
  566. })
  567. }
  568. //err = self.SyncInstanceWithCloudEip(ctx, userCred, ext)
  569. //if err != nil {
  570. // return errors.Wrap(err, "fail to sync associated instance of EIP")
  571. //}
  572. if account := self.GetCloudaccount(); account != nil {
  573. syncVirtualResourceMetadata(ctx, userCred, self, ext, account.ReadOnly)
  574. }
  575. // eip有绑定资源,并且绑定资源是项目资源,eip项目信息跟随绑定资源
  576. if res := self.GetAssociateResource(); res != nil && len(res.GetOwnerId().GetProjectId()) > 0 {
  577. self.SyncCloudProjectId(userCred, res.GetOwnerId())
  578. } else {
  579. SyncCloudProject(ctx, userCred, self, syncOwnerId, ext, provider)
  580. }
  581. return nil
  582. }
  583. func (manager *SElasticipManager) newFromCloudEip(ctx context.Context, userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, provider *SCloudprovider, region *SCloudregion, syncOwnerId mcclient.IIdentityProvider) (*SElasticip, error) {
  584. eip := SElasticip{}
  585. eip.SetModelManager(manager, &eip)
  586. eip.Status = extEip.GetStatus()
  587. eip.ExternalId = extEip.GetGlobalId()
  588. eip.IpAddr = extEip.GetIpAddr()
  589. eip.Mode = extEip.GetMode()
  590. eip.IsEmulated = extEip.IsEmulated()
  591. eip.ManagerId = provider.Id
  592. eip.CloudregionId = region.Id
  593. eip.ChargeType = billing_api.TNetChargeType(extEip.GetInternetChargeType())
  594. eip.AssociateType = extEip.GetAssociationType()
  595. if !extEip.GetCreatedAt().IsZero() {
  596. eip.CreatedAt = extEip.GetCreatedAt()
  597. }
  598. eip.BillingType = billing_api.TBillingType(extEip.GetBillingType())
  599. eip.ExpiredAt = time.Time{}
  600. eip.AutoRenew = false
  601. if eip.BillingType == billing_api.BILLING_TYPE_PREPAID {
  602. eip.ExpiredAt = extEip.GetExpiredAt()
  603. eip.AutoRenew = extEip.IsAutoRenew()
  604. }
  605. if len(eip.ChargeType) == 0 {
  606. eip.ChargeType = billing_api.NET_CHARGE_TYPE_BY_TRAFFIC
  607. }
  608. eip.Bandwidth = extEip.GetBandwidth()
  609. if networkId := extEip.GetINetworkId(); len(networkId) > 0 {
  610. network, err := db.FetchByExternalIdAndManagerId(NetworkManager, networkId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  611. wire := WireManager.Query().SubQuery()
  612. vpc := VpcManager.Query().SubQuery()
  613. return q.Join(wire, sqlchemy.Equals(wire.Field("id"), q.Field("wire_id"))).
  614. Join(vpc, sqlchemy.Equals(vpc.Field("id"), wire.Field("vpc_id"))).
  615. Filter(sqlchemy.Equals(vpc.Field("manager_id"), provider.Id))
  616. })
  617. if err != nil {
  618. return nil, errors.Wrapf(err, "failed to found network by externalId %s", networkId)
  619. }
  620. eip.NetworkId = network.GetId()
  621. }
  622. var err = func() error {
  623. lockman.LockRawObject(ctx, manager.Keyword(), "name")
  624. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
  625. newName, err := db.GenerateName(ctx, manager, syncOwnerId, extEip.GetName())
  626. if err != nil {
  627. return err
  628. }
  629. eip.Name = newName
  630. return manager.TableSpec().Insert(ctx, &eip)
  631. }()
  632. if err != nil {
  633. return nil, errors.Wrapf(err, "newFromCloudEip")
  634. }
  635. //err = eip.SyncInstanceWithCloudEip(ctx, userCred, extEip)
  636. //if err != nil {
  637. // return nil, errors.Wrap(err, "fail to sync associated instance of EIP")
  638. //}
  639. syncVirtualResourceMetadata(ctx, userCred, &eip, extEip, false)
  640. if res := eip.GetAssociateResource(); res != nil && len(res.GetOwnerId().GetProjectId()) > 0 {
  641. eip.SyncCloudProjectId(userCred, res.GetOwnerId())
  642. } else {
  643. SyncCloudProject(ctx, userCred, &eip, syncOwnerId, extEip, provider)
  644. }
  645. db.OpsLog.LogEvent(&eip, db.ACT_CREATE, eip.GetShortDesc(ctx), userCred)
  646. notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
  647. Obj: &eip,
  648. Action: notifyclient.ActionSyncCreate,
  649. })
  650. return &eip, nil
  651. }
  652. func (manager *SElasticipManager) getEipForInstance(instanceType string, instanceId string) (*SElasticip, error) {
  653. return manager.getEip(instanceType, instanceId, api.EIP_MODE_STANDALONE_EIP)
  654. }
  655. func (manager *SElasticipManager) getEip(instanceType string, instanceId string, eipMode string) (*SElasticip, error) {
  656. eip := SElasticip{}
  657. q := manager.Query()
  658. q = q.Equals("associate_type", instanceType)
  659. q = q.Equals("associate_id", instanceId)
  660. if len(eipMode) > 0 {
  661. q = q.Equals("mode", eipMode)
  662. }
  663. err := q.First(&eip)
  664. if err != nil {
  665. if err != sql.ErrNoRows {
  666. log.Errorf("getEipForInstance query fail %s", err)
  667. return nil, err
  668. } else {
  669. return nil, nil
  670. }
  671. }
  672. eip.SetModelManager(manager, &eip)
  673. return &eip, nil
  674. }
  675. func (self *SElasticip) IsAssociated() bool {
  676. if len(self.AssociateId) == 0 {
  677. return false
  678. }
  679. if self.GetAssociateVM() != nil {
  680. return true
  681. }
  682. if self.GetAssociateLoadbalancer() != nil {
  683. return true
  684. }
  685. if self.GetAssociateNatGateway() != nil {
  686. return true
  687. }
  688. if self.GetAssociateInstanceGroup() != nil {
  689. return true
  690. }
  691. return false
  692. }
  693. func (self *SElasticip) GetAssociateVM() *SGuest {
  694. if self.AssociateType == api.EIP_ASSOCIATE_TYPE_SERVER && len(self.AssociateId) > 0 {
  695. return GuestManager.FetchGuestById(self.AssociateId)
  696. }
  697. return nil
  698. }
  699. func (self *SElasticip) GetAssociateInstanceGroup() *SGroup {
  700. if self.AssociateType == api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP && len(self.AssociateId) > 0 {
  701. _grp, err := GroupManager.FetchById(self.AssociateId)
  702. if err != nil {
  703. return nil
  704. }
  705. return _grp.(*SGroup)
  706. }
  707. return nil
  708. }
  709. func (self *SElasticip) GetAssociateLoadbalancer() *SLoadbalancer {
  710. if self.AssociateType == api.EIP_ASSOCIATE_TYPE_LOADBALANCER && len(self.AssociateId) > 0 {
  711. _lb, err := LoadbalancerManager.FetchById(self.AssociateId)
  712. if err != nil {
  713. return nil
  714. }
  715. lb := _lb.(*SLoadbalancer)
  716. if lb.PendingDeleted {
  717. return nil
  718. }
  719. return lb
  720. }
  721. return nil
  722. }
  723. func (self *SElasticip) GetAssociateNatGateway() *SNatGateway {
  724. if self.AssociateType == api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY && len(self.AssociateId) > 0 {
  725. natGateway, err := NatGatewayManager.FetchById(self.AssociateId)
  726. if err != nil {
  727. return nil
  728. }
  729. return natGateway.(*SNatGateway)
  730. }
  731. return nil
  732. }
  733. func (self *SElasticip) GetAssociateResource() db.IModel {
  734. if vm := self.GetAssociateVM(); vm != nil {
  735. return vm
  736. }
  737. if lb := self.GetAssociateLoadbalancer(); lb != nil {
  738. return lb
  739. }
  740. if nat := self.GetAssociateNatGateway(); nat != nil {
  741. return nat
  742. }
  743. if grp := self.GetAssociateInstanceGroup(); grp != nil {
  744. return grp
  745. }
  746. return nil
  747. }
  748. func (self *SElasticip) Dissociate(ctx context.Context, userCred mcclient.TokenCredential) error {
  749. if len(self.AssociateType) == 0 {
  750. return nil
  751. }
  752. var vm *SGuest
  753. var nat *SNatGateway
  754. var lb *SLoadbalancer
  755. var grp *SGroup
  756. switch self.AssociateType {
  757. case api.EIP_ASSOCIATE_TYPE_SERVER:
  758. vm = self.GetAssociateVM()
  759. if vm == nil {
  760. log.Errorf("dissociate VM not exists???")
  761. }
  762. case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
  763. nat = self.GetAssociateNatGateway()
  764. if nat == nil {
  765. log.Errorf("dissociate Nat gateway not exists???")
  766. }
  767. case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
  768. lb = self.GetAssociateLoadbalancer()
  769. if lb == nil {
  770. log.Errorf("dissociate loadbalancer not exists???")
  771. }
  772. case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
  773. grp = self.GetAssociateInstanceGroup()
  774. if grp == nil {
  775. log.Errorf("dissociate instance_group not exists???")
  776. }
  777. }
  778. _, err := db.Update(self, func() error {
  779. self.AssociateId = ""
  780. self.AssociateType = ""
  781. return nil
  782. })
  783. if err != nil {
  784. return err
  785. }
  786. if vm != nil {
  787. db.OpsLog.LogDetachEvent(ctx, vm, self, userCred, self.GetShortDesc(ctx))
  788. db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, vm.GetShortDesc(ctx), userCred)
  789. db.OpsLog.LogEvent(vm, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred)
  790. }
  791. if nat != nil {
  792. db.OpsLog.LogDetachEvent(ctx, nat, self, userCred, self.GetShortDesc(ctx))
  793. db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, nat.GetShortDesc(ctx), userCred)
  794. db.OpsLog.LogEvent(nat, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred)
  795. }
  796. if lb != nil {
  797. db.OpsLog.LogDetachEvent(ctx, lb, self, userCred, self.GetShortDesc(ctx))
  798. db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, lb.GetShortDesc(ctx), userCred)
  799. db.OpsLog.LogEvent(lb, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred)
  800. }
  801. if grp != nil {
  802. db.OpsLog.LogDetachEvent(ctx, grp, self, userCred, self.GetShortDesc(ctx))
  803. db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, grp.GetShortDesc(ctx), userCred)
  804. db.OpsLog.LogEvent(grp, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred)
  805. }
  806. if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
  807. self.RealDelete(ctx, userCred)
  808. }
  809. return nil
  810. }
  811. func (self *SElasticip) AssociateLoadbalancer(ctx context.Context, userCred mcclient.TokenCredential, lb *SLoadbalancer) error {
  812. if lb.PendingDeleted {
  813. return fmt.Errorf("loadbalancer is deleted")
  814. }
  815. if len(self.AssociateType) > 0 && len(self.AssociateId) > 0 {
  816. if self.AssociateType == api.EIP_ASSOCIATE_TYPE_LOADBALANCER && self.AssociateId == lb.Id {
  817. return nil
  818. }
  819. if self.GetAssociateResource() != nil {
  820. return fmt.Errorf("eip has been associated!!")
  821. }
  822. }
  823. _, err := db.Update(self, func() error {
  824. self.AssociateType = api.EIP_ASSOCIATE_TYPE_LOADBALANCER
  825. self.AssociateId = lb.Id
  826. return nil
  827. })
  828. if err != nil {
  829. return err
  830. }
  831. db.OpsLog.LogAttachEvent(ctx, lb, self, userCred, self.GetShortDesc(ctx))
  832. db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, lb.GetShortDesc(ctx), userCred)
  833. db.OpsLog.LogEvent(lb, db.ACT_EIP_ATTACH, self.GetShortDesc(ctx), userCred)
  834. return nil
  835. }
  836. func (self *SElasticip) AssociateInstance(ctx context.Context, userCred mcclient.TokenCredential, insType string, ins db.IStatusStandaloneModel) error {
  837. switch insType {
  838. case api.EIP_ASSOCIATE_TYPE_SERVER:
  839. vm := ins.(*SGuest)
  840. if vm.PendingDeleted || vm.Deleted {
  841. return fmt.Errorf("vm is deleted")
  842. }
  843. }
  844. if len(self.AssociateType) > 0 && len(self.AssociateId) > 0 {
  845. if self.AssociateType == insType && self.AssociateId == ins.GetId() {
  846. return nil
  847. }
  848. if self.GetAssociateResource() != nil {
  849. return fmt.Errorf("eip has been associated!!")
  850. }
  851. }
  852. _, err := db.Update(self, func() error {
  853. self.AssociateType = insType
  854. self.AssociateId = ins.GetId()
  855. return nil
  856. })
  857. if err != nil {
  858. return errors.Wrapf(err, "db.Update")
  859. }
  860. db.OpsLog.LogAttachEvent(ctx, ins, self, userCred, self.GetShortDesc(ctx))
  861. db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, ins.GetShortDesc(ctx), userCred)
  862. db.OpsLog.LogEvent(ins, db.ACT_EIP_ATTACH, self.GetShortDesc(ctx), userCred)
  863. return nil
  864. }
  865. func (self *SElasticip) AssociateInstanceGroup(ctx context.Context, userCred mcclient.TokenCredential, insType string, ins db.IStatusStandaloneModel) error {
  866. switch insType {
  867. case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
  868. vm := ins.(*SGroup)
  869. if vm.PendingDeleted || vm.Deleted {
  870. return fmt.Errorf("group is deleted")
  871. }
  872. }
  873. if len(self.AssociateType) > 0 && len(self.AssociateId) > 0 {
  874. if self.AssociateType == insType && self.AssociateId == ins.GetId() {
  875. return nil
  876. }
  877. if self.GetAssociateResource() != nil {
  878. return fmt.Errorf("eip has been associated!!")
  879. }
  880. }
  881. _, err := db.Update(self, func() error {
  882. self.AssociateType = insType
  883. self.AssociateId = ins.GetId()
  884. return nil
  885. })
  886. if err != nil {
  887. return errors.Wrapf(err, "db.Update")
  888. }
  889. db.OpsLog.LogAttachEvent(ctx, ins, self, userCred, self.GetShortDesc(ctx))
  890. db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, ins.GetShortDesc(ctx), userCred)
  891. db.OpsLog.LogEvent(ins, db.ACT_EIP_ATTACH, self.GetShortDesc(ctx), userCred)
  892. return nil
  893. }
  894. func (self *SElasticip) AssociateNatGateway(ctx context.Context, userCred mcclient.TokenCredential, nat *SNatGateway) error {
  895. if nat.Deleted {
  896. return fmt.Errorf("nat gateway is deleted")
  897. }
  898. if len(self.AssociateId) > 0 && self.AssociateType == api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY && self.AssociateId == nat.Id {
  899. return nil
  900. }
  901. _, err := db.Update(self, func() error {
  902. self.AssociateType = api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
  903. self.AssociateId = nat.Id
  904. return nil
  905. })
  906. if err != nil {
  907. return err
  908. }
  909. db.OpsLog.LogAttachEvent(ctx, nat, self, userCred, self.GetShortDesc(ctx))
  910. db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, nat.GetShortDesc(ctx), userCred)
  911. db.OpsLog.LogEvent(nat, db.ACT_EIP_ATTACH, self.GetShortDesc(ctx), userCred)
  912. return nil
  913. }
  914. func (manager *SElasticipManager) getEipByExtEip(ctx context.Context, userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, provider *SCloudprovider, region *SCloudregion, syncOwnerId mcclient.IIdentityProvider) (*SElasticip, error) {
  915. eipId := extEip.GetGlobalId()
  916. eipObj, err := db.FetchByExternalIdAndManagerId(manager, eipId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
  917. return q.Equals("manager_id", provider.Id)
  918. })
  919. if err == nil {
  920. return eipObj.(*SElasticip), nil
  921. }
  922. if errors.Cause(err) != sql.ErrNoRows {
  923. return nil, errors.Wrapf(err, "FetchByExternalIdAndManagerId %s", eipId)
  924. }
  925. return manager.newFromCloudEip(ctx, userCred, extEip, provider, region, syncOwnerId)
  926. }
  927. func (manager *SElasticipManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.SElasticipCreateInput) (api.SElasticipCreateInput, error) {
  928. if input.CloudregionId == "" {
  929. input.CloudregionId = api.DEFAULT_REGION_ID
  930. }
  931. obj, err := CloudregionManager.FetchByIdOrName(ctx, nil, input.CloudregionId)
  932. if err != nil {
  933. if err != sql.ErrNoRows {
  934. return input, httperrors.NewGeneralError(err)
  935. }
  936. return input, httperrors.NewResourceNotFoundError2("cloudregion", input.CloudregionId)
  937. }
  938. var (
  939. region = obj.(*SCloudregion)
  940. regionDriver = region.GetDriver()
  941. )
  942. input.CloudregionId = region.GetId()
  943. // publicIp cannot be created standalone
  944. input.Mode = api.EIP_MODE_STANDALONE_EIP
  945. if input.ChargeType == "" {
  946. input.ChargeType = billing_api.TNetChargeType(regionDriver.GetEipDefaultChargeType())
  947. }
  948. if !utils.IsInStringArray(string(input.ChargeType), []string{string(billing_api.NET_CHARGE_TYPE_BY_BANDWIDTH), string(billing_api.NET_CHARGE_TYPE_BY_TRAFFIC)}) {
  949. return input, httperrors.NewInputParameterError("charge type %s not supported", string(input.ChargeType))
  950. }
  951. input.VirtualResourceCreateInput, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
  952. if err != nil {
  953. return input, err
  954. }
  955. err = regionDriver.ValidateCreateEipData(ctx, userCred, &input)
  956. if err != nil {
  957. return input, err
  958. }
  959. var provider *SCloudprovider = nil
  960. if input.ManagerId != "" {
  961. providerObj, err := CloudproviderManager.FetchByIdOrName(ctx, nil, input.ManagerId)
  962. if err != nil {
  963. if err != sql.ErrNoRows {
  964. return input, httperrors.NewGeneralError(err)
  965. }
  966. return input, httperrors.NewResourceNotFoundError2("cloudprovider", input.ManagerId)
  967. }
  968. provider = providerObj.(*SCloudprovider)
  969. input.ManagerId = provider.Id
  970. }
  971. //避免参数重名后还有pending.eip残留
  972. eipPendingUsage := &SRegionQuota{Eip: 1}
  973. quotaKeys := fetchRegionalQuotaKeys(rbacscope.ScopeProject, ownerId, region, provider)
  974. eipPendingUsage.SetKeys(quotaKeys)
  975. if err = quotas.CheckSetPendingQuota(ctx, userCred, eipPendingUsage); err != nil {
  976. return input, err
  977. }
  978. return input, nil
  979. }
  980. func (eip *SElasticip) GetQuotaKeys() (quotas.IQuotaKeys, error) {
  981. region, err := eip.GetRegion()
  982. if region == nil {
  983. return nil, errors.Wrapf(err, "eip.GetRegion")
  984. }
  985. return fetchRegionalQuotaKeys(
  986. rbacscope.ScopeProject,
  987. eip.GetOwnerId(),
  988. region,
  989. eip.GetCloudprovider(),
  990. ), nil
  991. }
  992. func (self *SElasticip) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  993. self.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
  994. eipPendingUsage := &SRegionQuota{Eip: 1}
  995. keys, err := self.GetQuotaKeys()
  996. if err != nil {
  997. log.Errorf("GetQuotaKeys fail %s", err)
  998. } else {
  999. eipPendingUsage.SetKeys(keys)
  1000. err := quotas.CancelPendingUsage(ctx, userCred, eipPendingUsage, eipPendingUsage, true)
  1001. if err != nil {
  1002. log.Errorf("SElasticip CancelPendingUsage error: %s", err)
  1003. }
  1004. }
  1005. self.startEipAllocateTask(ctx, userCred, data.(*jsonutils.JSONDict), "")
  1006. }
  1007. func (self *SElasticip) startEipAllocateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
  1008. task, err := taskman.TaskManager.NewTask(ctx, "EipAllocateTask", self, userCred, params, parentTaskId, "", nil)
  1009. if err != nil {
  1010. return errors.Wrapf(err, "NewTask")
  1011. }
  1012. self.SetStatus(ctx, userCred, api.EIP_STATUS_ALLOCATE, "start allocate")
  1013. return task.ScheduleRun(nil)
  1014. }
  1015. func (self *SElasticip) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
  1016. // Elasticip delete do nothing
  1017. return nil
  1018. }
  1019. func (self *SElasticip) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  1020. return self.SVirtualResourceBase.Delete(ctx, userCred)
  1021. }
  1022. func (self *SElasticip) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  1023. return self.StartEipDeallocateTask(ctx, userCred, "")
  1024. }
  1025. func (self *SElasticip) ValidateDeleteCondition(ctx context.Context, info *api.ElasticipDetails) error {
  1026. if gotypes.IsNil(info) {
  1027. if self.IsAssociated() {
  1028. return fmt.Errorf("eip is associated with resources")
  1029. }
  1030. } else if len(info.AssociateName) > 0 {
  1031. return fmt.Errorf("eip is associated with resources")
  1032. }
  1033. return self.SVirtualResourceBase.ValidateDeleteCondition(ctx, nil)
  1034. }
  1035. func (self *SElasticip) StartEipDeallocateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  1036. task, err := taskman.TaskManager.NewTask(ctx, "EipDeallocateTask", self, userCred, nil, parentTaskId, "", nil)
  1037. if err != nil {
  1038. log.Errorf("newTask EipDeallocateTask fail %s", err)
  1039. return err
  1040. }
  1041. self.SetStatus(ctx, userCred, api.EIP_STATUS_DEALLOCATE, "start to delete")
  1042. task.ScheduleRun(nil)
  1043. return nil
  1044. }
  1045. func (self *SElasticip) PerformAssociate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ElasticipAssociateInput) (api.ElasticipAssociateInput, error) {
  1046. if self.IsAssociated() {
  1047. return input, httperrors.NewConflictError("eip has been associated with instance")
  1048. }
  1049. if self.Status != api.EIP_STATUS_READY {
  1050. return input, httperrors.NewInvalidStatusError("eip cannot associate in status %s", self.Status)
  1051. }
  1052. if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
  1053. return input, httperrors.NewUnsupportOperationError("fixed eip cannot be associated")
  1054. }
  1055. if len(input.InstanceId) == 0 {
  1056. return input, httperrors.NewMissingParameterError("instance_id")
  1057. }
  1058. if len(input.InstanceType) == 0 {
  1059. input.InstanceType = api.EIP_ASSOCIATE_TYPE_SERVER
  1060. }
  1061. if !utils.IsInStringArray(input.InstanceType, api.EIP_ASSOCIATE_VALID_TYPES) {
  1062. return input, httperrors.NewUnsupportOperationError("Unsupported instance type %s", input.InstanceType)
  1063. }
  1064. switch input.InstanceType {
  1065. case api.EIP_ASSOCIATE_TYPE_SERVER:
  1066. vmObj, err := GuestManager.FetchByIdOrName(ctx, userCred, input.InstanceId)
  1067. if err != nil {
  1068. if errors.Cause(err) == sql.ErrNoRows {
  1069. return input, httperrors.NewResourceNotFoundError("server %s not found", input.InstanceId)
  1070. }
  1071. return input, httperrors.NewGeneralError(err)
  1072. }
  1073. server := vmObj.(*SGuest)
  1074. lockman.LockObject(ctx, server)
  1075. defer lockman.ReleaseObject(ctx, server)
  1076. if server.PendingDeleted {
  1077. return input, httperrors.NewInvalidStatusError("cannot associate pending delete server")
  1078. }
  1079. // IMPORTANT: this serves as a guard against a guest to have multiple
  1080. // associated elastic_ips
  1081. seip, _ := server.GetEipOrPublicIp()
  1082. if seip != nil && (seip.Mode == api.EIP_MODE_STANDALONE_EIP || seip.GetProviderName() != api.CLOUD_PROVIDER_AWS) {
  1083. return input, httperrors.NewInvalidStatusError("instance is already associated with eip")
  1084. }
  1085. if ok, _ := utils.InStringArray(server.Status, []string{api.VM_READY, api.VM_RUNNING}); !ok {
  1086. return input, httperrors.NewInvalidStatusError("cannot associate server in status %s", server.Status)
  1087. }
  1088. err = ValidateAssociateEip(server)
  1089. if err != nil {
  1090. return input, err
  1091. }
  1092. if len(self.NetworkId) > 0 {
  1093. gns, err := server.GetNetworks("")
  1094. if err != nil {
  1095. return input, httperrors.NewGeneralError(errors.Wrap(err, "GetNetworks"))
  1096. }
  1097. for _, gn := range gns {
  1098. if gn.NetworkId == self.NetworkId {
  1099. return input, httperrors.NewInputParameterError("cannot associate eip with same network")
  1100. }
  1101. }
  1102. }
  1103. serverRegion, _ := server.getRegion()
  1104. if serverRegion == nil {
  1105. return input, httperrors.NewInputParameterError("server region is not found???")
  1106. }
  1107. eipRegion, err := self.GetRegion()
  1108. if err != nil {
  1109. return input, httperrors.NewGeneralError(errors.Wrapf(err, "GetRegion"))
  1110. }
  1111. if serverRegion.Id != eipRegion.Id {
  1112. return input, httperrors.NewInputParameterError("eip and server are not in the same region")
  1113. }
  1114. eipZone, _ := self.GetZone()
  1115. if eipZone != nil {
  1116. serverZone, _ := server.getZone()
  1117. if serverZone.Id != eipZone.Id {
  1118. return input, httperrors.NewInputParameterError("eip and server are not in the same zone")
  1119. }
  1120. }
  1121. srvHost, _ := server.GetHost()
  1122. if srvHost == nil {
  1123. return input, httperrors.NewInputParameterError("server host is not found???")
  1124. }
  1125. if srvHost.ManagerId != self.ManagerId {
  1126. return input, httperrors.NewInputParameterError("server and eip are not managed by the same provider")
  1127. }
  1128. input.InstanceExternalId = server.ExternalId
  1129. case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
  1130. grpObj, err := GroupManager.FetchByIdOrName(ctx, userCred, input.InstanceId)
  1131. if err != nil {
  1132. if errors.Cause(err) == sql.ErrNoRows {
  1133. return input, httperrors.NewResourceNotFoundError("instance group %s not found", input.InstanceId)
  1134. }
  1135. return input, httperrors.NewGeneralError(err)
  1136. }
  1137. group := grpObj.(*SGroup)
  1138. lockman.LockObject(ctx, group)
  1139. defer lockman.ReleaseObject(ctx, group)
  1140. net, err := group.isEipAssociable()
  1141. if err != nil {
  1142. return input, errors.Wrap(err, "grp.isEipAssociable")
  1143. }
  1144. if net.Id == self.NetworkId {
  1145. return input, httperrors.NewInputParameterError("cannot associate eip with same network")
  1146. }
  1147. eipZone, _ := self.GetZone()
  1148. if eipZone != nil {
  1149. insZone, _ := net.GetZone()
  1150. if eipZone.Id != insZone.Id {
  1151. return input, httperrors.NewInputParameterError("cannot associate eip and instance in different zone")
  1152. }
  1153. }
  1154. case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
  1155. natgwObj, err := NatGatewayManager.FetchByIdOrName(ctx, userCred, input.InstanceId)
  1156. if err != nil {
  1157. if errors.Cause(err) == sql.ErrNoRows {
  1158. return input, httperrors.NewResourceNotFoundError("nat gateway %s not found", input.InstanceId)
  1159. }
  1160. return input, httperrors.NewGeneralError(err)
  1161. }
  1162. natgw := natgwObj.(*SNatGateway)
  1163. lockman.LockObject(ctx, natgw)
  1164. defer lockman.ReleaseObject(ctx, natgw)
  1165. case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
  1166. obj, err := LoadbalancerManager.FetchByIdOrName(ctx, userCred, input.InstanceId)
  1167. if err != nil {
  1168. if errors.Cause(err) == sql.ErrNoRows {
  1169. return input, httperrors.NewResourceNotFoundError("loadbalancer %s not found", input.InstanceId)
  1170. }
  1171. return input, httperrors.NewGeneralError(err)
  1172. }
  1173. lb := obj.(*SLoadbalancer)
  1174. if len(self.NetworkId) > 0 {
  1175. nets, err := lb.GetNetworks()
  1176. if err != nil {
  1177. return input, httperrors.NewGeneralError(errors.Wrap(err, "GetNetworks"))
  1178. }
  1179. for _, net := range nets {
  1180. if net.Id == self.NetworkId {
  1181. return input, httperrors.NewInputParameterError("cannot associate eip with same network")
  1182. }
  1183. }
  1184. }
  1185. lockman.LockObject(ctx, lb)
  1186. defer lockman.ReleaseObject(ctx, lb)
  1187. if lb.PendingDeleted {
  1188. return input, httperrors.NewInvalidStatusError("cannot associate with pending deleted loadbalancer")
  1189. }
  1190. eips, _ := lb.GetEips()
  1191. if len(eips) > 0 {
  1192. return input, httperrors.NewInvalidStatusError("loadbalancer is already associated with eip")
  1193. }
  1194. }
  1195. return input, self.StartEipAssociateInstanceTask(ctx, userCred, input, "")
  1196. }
  1197. func (self *SElasticip) StartEipAssociateInstanceTask(ctx context.Context, userCred mcclient.TokenCredential, input api.ElasticipAssociateInput, parentTaskId string) error {
  1198. params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  1199. return self.StartEipAssociateTask(ctx, userCred, params, parentTaskId)
  1200. }
  1201. func (self *SElasticip) StartEipAssociateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
  1202. task, err := taskman.TaskManager.NewTask(ctx, "EipAssociateTask", self, userCred, params, parentTaskId, "", nil)
  1203. if err != nil {
  1204. return errors.Wrapf(err, "NewTask")
  1205. }
  1206. self.SetStatus(ctx, userCred, api.EIP_STATUS_ASSOCIATE, "start to associate")
  1207. return task.ScheduleRun(nil)
  1208. }
  1209. func (self *SElasticip) PerformDissociate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ElasticDissociateInput) (jsonutils.JSONObject, error) {
  1210. if len(self.AssociateId) == 0 {
  1211. return nil, nil // success
  1212. }
  1213. // associate with an invalid vm
  1214. res := self.GetAssociateResource()
  1215. if res == nil {
  1216. return nil, self.Dissociate(ctx, userCred)
  1217. }
  1218. err := db.IsObjectRbacAllowed(ctx, res, userCred, policy.PolicyActionGet)
  1219. if err != nil {
  1220. return nil, errors.Wrap(err, "associated resource is not accessible")
  1221. }
  1222. if self.Status != api.EIP_STATUS_READY {
  1223. return nil, httperrors.NewInvalidStatusError("eip cannot dissociate in status %s", self.Status)
  1224. }
  1225. if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
  1226. return nil, httperrors.NewUnsupportOperationError("fixed public eip cannot be dissociated")
  1227. }
  1228. err = self.StartEipDissociateTask(ctx, userCred, input.AutoDelete, "")
  1229. return nil, err
  1230. }
  1231. func (self *SElasticip) StartEipDissociateTask(ctx context.Context, userCred mcclient.TokenCredential, autoDelete bool, parentTaskId string) error {
  1232. params := jsonutils.NewDict()
  1233. if autoDelete {
  1234. params.Add(jsonutils.JSONTrue, "auto_delete")
  1235. }
  1236. task, err := taskman.TaskManager.NewTask(ctx, "EipDissociateTask", self, userCred, params, parentTaskId, "", nil)
  1237. if err != nil {
  1238. log.Errorf("create EipDissociateTask fail %s", err)
  1239. return nil
  1240. }
  1241. self.SetStatus(ctx, userCred, api.EIP_STATUS_DISSOCIATE, "start to dissociate")
  1242. task.ScheduleRun(nil)
  1243. return nil
  1244. }
  1245. func (self *SElasticip) GetIRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) {
  1246. provider, err := self.GetDriver(ctx)
  1247. if err != nil {
  1248. return nil, errors.Wrap(err, "GetDriver")
  1249. }
  1250. region, err := self.GetRegion()
  1251. if err != nil {
  1252. return nil, errors.Wrapf(err, "self.GetRegion")
  1253. }
  1254. return provider.GetIRegionById(region.GetExternalId())
  1255. }
  1256. func (self *SElasticip) GetIEip(ctx context.Context) (cloudprovider.ICloudEIP, error) {
  1257. if len(self.ExternalId) == 0 {
  1258. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "empty external id")
  1259. }
  1260. iregion, err := self.GetIRegion(ctx)
  1261. if err != nil {
  1262. return nil, err
  1263. }
  1264. return iregion.GetIEipById(self.GetExternalId())
  1265. }
  1266. // 同步弹性公网IP状态
  1267. func (self *SElasticip) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ElasticipSyncstatusInput) (jsonutils.JSONObject, error) {
  1268. if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
  1269. return nil, httperrors.NewUnsupportOperationError("fixed eip cannot sync status")
  1270. }
  1271. if self.IsManaged() {
  1272. return nil, StartResourceSyncStatusTask(ctx, userCred, self, "EipSyncstatusTask", "")
  1273. } else {
  1274. return nil, self.SetStatus(ctx, userCred, api.EIP_STATUS_READY, "eip sync status")
  1275. }
  1276. }
  1277. func (self *SElasticip) PerformSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1278. if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
  1279. return nil, httperrors.NewUnsupportOperationError("fixed eip cannot sync status")
  1280. }
  1281. if self.IsManaged() {
  1282. return nil, StartResourceSyncStatusTask(ctx, userCred, self, "EipSyncstatusTask", "")
  1283. }
  1284. return nil, nil
  1285. }
  1286. func (manager *SElasticipManager) FetchCustomizeColumns(
  1287. ctx context.Context,
  1288. userCred mcclient.TokenCredential,
  1289. query jsonutils.JSONObject,
  1290. objs []interface{},
  1291. fields stringutils2.SSortedStrings,
  1292. isList bool,
  1293. ) []api.ElasticipDetails {
  1294. rows := make([]api.ElasticipDetails, len(objs))
  1295. virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  1296. managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  1297. regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  1298. lbIds := []string{}
  1299. guestIds := []string{}
  1300. natIds := []string{}
  1301. groupIds := []string{}
  1302. for i := range rows {
  1303. rows[i] = api.ElasticipDetails{
  1304. VirtualResourceDetails: virtRows[i],
  1305. ManagedResourceInfo: managerRows[i],
  1306. CloudregionResourceInfo: regionRows[i],
  1307. }
  1308. eip := objs[i].(*SElasticip)
  1309. if len(eip.AssociateId) > 0 {
  1310. switch eip.AssociateType {
  1311. case api.EIP_ASSOCIATE_TYPE_SERVER:
  1312. guestIds = append(guestIds, eip.AssociateId)
  1313. case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
  1314. lbIds = append(lbIds, eip.AssociateId)
  1315. case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
  1316. natIds = append(natIds, eip.AssociateId)
  1317. case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
  1318. groupIds = append(groupIds, eip.AssociateId)
  1319. }
  1320. }
  1321. }
  1322. guests, err := db.FetchIdNameMap2(GuestManager, guestIds)
  1323. if err != nil {
  1324. log.Errorf("FetchIdNameMap2 guests")
  1325. return rows
  1326. }
  1327. lbs, err := db.FetchIdNameMap2(LoadbalancerManager, lbIds)
  1328. if err != nil {
  1329. log.Errorf("FetchIdNameMap2 loadbalancer")
  1330. return rows
  1331. }
  1332. nats, err := db.FetchIdNameMap2(NatGatewayManager, natIds)
  1333. if err != nil {
  1334. log.Errorf("FetchIdNameMap2 natgateway")
  1335. return rows
  1336. }
  1337. groups, err := db.FetchIdNameMap2(GroupManager, groupIds)
  1338. if err != nil {
  1339. log.Errorf("FetchIdNameMap2 group")
  1340. return rows
  1341. }
  1342. gns := []SGuestnetwork{}
  1343. err = GuestnetworkManager.Query().In("guest_id", guestIds).All(&gns)
  1344. if err != nil {
  1345. log.Errorf("FetchModelObjects GuestnetworkManager")
  1346. return rows
  1347. }
  1348. gnMap := map[string]string{}
  1349. for i := range gns {
  1350. gnMap[gns[i].GuestId] = gns[i].IpAddr
  1351. }
  1352. for i := range rows {
  1353. eip := objs[i].(*SElasticip)
  1354. if len(eip.AssociateId) > 0 {
  1355. switch eip.AssociateType {
  1356. case api.EIP_ASSOCIATE_TYPE_SERVER:
  1357. rows[i].AssociateName, _ = guests[eip.AssociateId]
  1358. rows[i].ServerPrivateIp = gnMap[eip.AssociateId]
  1359. case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
  1360. rows[i].AssociateName, _ = lbs[eip.AssociateId]
  1361. case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
  1362. rows[i].AssociateName, _ = nats[eip.AssociateId]
  1363. case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
  1364. rows[i].AssociateName, _ = groups[eip.AssociateId]
  1365. }
  1366. }
  1367. }
  1368. return rows
  1369. }
  1370. type SEipNetwork struct {
  1371. owner mcclient.IIdentityProvider
  1372. user mcclient.TokenCredential
  1373. freeCnt int
  1374. net *SNetwork
  1375. }
  1376. func NewEipNetwork(net *SNetwork, owner mcclient.IIdentityProvider, user mcclient.TokenCredential, freecnt int) SEipNetwork {
  1377. return SEipNetwork{
  1378. owner: owner,
  1379. user: user,
  1380. freeCnt: freecnt,
  1381. net: net,
  1382. }
  1383. }
  1384. func (en SEipNetwork) isOwnerProject() bool {
  1385. return en.owner.GetProjectId() == en.net.ProjectId
  1386. }
  1387. func (en SEipNetwork) isOwnerProjectDomain() bool {
  1388. return en.owner.GetProjectDomainId() == en.net.DomainId
  1389. }
  1390. func (en SEipNetwork) isUserProject() bool {
  1391. return en.user.GetProjectId() == en.net.ProjectId
  1392. }
  1393. func (en SEipNetwork) isUserProjectDomain() bool {
  1394. return en.user.GetProjectDomainId() == en.net.DomainId
  1395. }
  1396. func (en SEipNetwork) GetNetwork() *SNetwork {
  1397. return en.net
  1398. }
  1399. type SEipNetworks []SEipNetwork
  1400. func (a SEipNetworks) Len() int {
  1401. return len(a)
  1402. }
  1403. func (a SEipNetworks) Swap(i, j int) {
  1404. a[i], a[j] = a[j], a[i]
  1405. }
  1406. func (a SEipNetworks) Less(i, j int) bool {
  1407. if a[i].isOwnerProject() && !a[j].isOwnerProject() {
  1408. return true
  1409. } else if !a[i].isOwnerProject() && a[j].isOwnerProject() {
  1410. return false
  1411. }
  1412. if a[i].isOwnerProjectDomain() && !a[j].isOwnerProjectDomain() {
  1413. return true
  1414. } else if !a[i].isOwnerProjectDomain() && a[j].isOwnerProjectDomain() {
  1415. return false
  1416. }
  1417. if a[i].isUserProject() && !a[j].isUserProject() {
  1418. return true
  1419. } else if !a[i].isUserProject() && a[j].isUserProject() {
  1420. return false
  1421. }
  1422. if a[i].isUserProjectDomain() && !a[j].isUserProjectDomain() {
  1423. return true
  1424. } else if !a[i].isUserProjectDomain() && a[j].isUserProjectDomain() {
  1425. return false
  1426. }
  1427. if a[i].freeCnt > a[j].freeCnt {
  1428. return true
  1429. } else if a[i].freeCnt < a[j].freeCnt {
  1430. return false
  1431. }
  1432. if a[i].net.Id < a[j].net.Id {
  1433. return true
  1434. }
  1435. return false
  1436. }
  1437. type NewEipForVMOnHostArgs struct {
  1438. Bandwidth int
  1439. BgpType string
  1440. ChargeType billing_api.TNetChargeType
  1441. AutoDellocate bool
  1442. Group *SGroup
  1443. Guest *SGuest
  1444. Host *SHost
  1445. Natgateway *SNatGateway
  1446. Loadbalancer *SLoadbalancer
  1447. PendingUsage quotas.IQuota
  1448. }
  1449. func (manager *SElasticipManager) NewEipForVMOnHost(ctx context.Context, userCred mcclient.TokenCredential, args *NewEipForVMOnHostArgs) (*SElasticip, error) {
  1450. var (
  1451. bw = args.Bandwidth
  1452. bgpType = args.BgpType
  1453. chargeType = args.ChargeType
  1454. autoDellocate = args.AutoDellocate
  1455. grp = args.Group
  1456. vm = args.Guest
  1457. host = args.Host
  1458. nat = args.Natgateway
  1459. lb = args.Loadbalancer
  1460. pendingUsage = args.PendingUsage
  1461. region *SCloudregion = nil
  1462. )
  1463. if host != nil {
  1464. region, _ = host.GetRegion()
  1465. } else if lb != nil {
  1466. region, _ = lb.GetRegion()
  1467. } else if nat != nil {
  1468. region, _ = nat.GetRegion()
  1469. } else if grp != nil {
  1470. net, _ := grp.getAttachedNetwork()
  1471. region, _ = net.GetRegion()
  1472. } else {
  1473. return nil, fmt.Errorf("invalid host or nat")
  1474. }
  1475. regionDriver := region.GetDriver()
  1476. if len(chargeType) == 0 {
  1477. chargeType = billing_api.TNetChargeType(regionDriver.GetEipDefaultChargeType())
  1478. }
  1479. if err := regionDriver.ValidateEipChargeType(chargeType); err != nil {
  1480. return nil, err
  1481. }
  1482. eip := &SElasticip{}
  1483. eip.SetModelManager(manager, eip)
  1484. eip.Mode = api.EIP_MODE_STANDALONE_EIP
  1485. // do not implicitly auto dellocate EIP, should be set by user explicitly
  1486. // eip.AutoDellocate = tristate.True
  1487. eip.Bandwidth = bw
  1488. eip.ChargeType = billing_api.TNetChargeType(chargeType)
  1489. eip.BgpType = args.BgpType
  1490. eip.AutoDellocate = tristate.NewFromBool(autoDellocate)
  1491. ownerCred := userCred.(mcclient.IIdentityProvider)
  1492. if vm != nil {
  1493. ownerCred = vm.GetOwnerId()
  1494. } else if nat != nil {
  1495. ownerCred = nat.GetOwnerId()
  1496. } else if lb != nil {
  1497. ownerCred = lb.GetOwnerId()
  1498. } else if grp != nil {
  1499. ownerCred = grp.GetOwnerId()
  1500. } else {
  1501. panic("unsupported associate type")
  1502. }
  1503. eip.DomainId = ownerCred.GetProjectDomainId()
  1504. eip.ProjectId = ownerCred.GetProjectId()
  1505. eip.ProjectSrc = string(apis.OWNER_SOURCE_LOCAL)
  1506. if host != nil {
  1507. eip.ManagerId = host.ManagerId
  1508. } else if nat != nil {
  1509. vpc, err := nat.GetVpc()
  1510. if err != nil {
  1511. return nil, errors.Wrapf(err, "nat.GetVpc")
  1512. }
  1513. eip.ManagerId = vpc.ManagerId
  1514. } else if lb != nil {
  1515. vpc, err := lb.GetVpc()
  1516. if err != nil {
  1517. return nil, errors.Wrapf(err, "nat.GetVpc")
  1518. }
  1519. eip.ManagerId = vpc.ManagerId
  1520. } else if grp != nil {
  1521. } else {
  1522. panic("unsupported associate type")
  1523. }
  1524. eip.CloudregionId = region.Id
  1525. if vm != nil {
  1526. eip.Name = fmt.Sprintf("eip-for-srv-%s", pinyinutils.Text2Pinyin(vm.GetName()))
  1527. } else if nat != nil {
  1528. eip.Name = fmt.Sprintf("eip-for-nat-%s", pinyinutils.Text2Pinyin(nat.GetName()))
  1529. } else if grp != nil {
  1530. eip.Name = fmt.Sprintf("eip-for-grp-%s", pinyinutils.Text2Pinyin(grp.GetName()))
  1531. } else if lb != nil {
  1532. eip.Name = fmt.Sprintf("eip-for-lb-%s", pinyinutils.Text2Pinyin(lb.GetName()))
  1533. } else {
  1534. panic("unsupported associate type")
  1535. }
  1536. if (host != nil && host.ManagerId == "") || grp != nil || lb != nil { // kvm
  1537. q := NetworkManager.Query()
  1538. var zoneId string
  1539. if host != nil {
  1540. zoneId = host.ZoneId
  1541. } else if grp != nil {
  1542. net, _ := grp.getAttachedNetwork()
  1543. zone, _ := net.GetZone()
  1544. zoneId = zone.Id
  1545. } else if lb != nil {
  1546. zone, _ := lb.GetZone()
  1547. zoneId = zone.Id
  1548. }
  1549. wireq := WireManager.Query().SubQuery()
  1550. scope, _ := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), NetworkManager.KeywordPlural(), policy.PolicyActionList)
  1551. q = NetworkManager.FilterByOwner(ctx, q, NetworkManager, userCred, userCred, scope)
  1552. q = q.Join(wireq, sqlchemy.Equals(wireq.Field("id"), q.Field("wire_id"))).
  1553. Filter(sqlchemy.Equals(wireq.Field("zone_id"), zoneId))
  1554. q = q.Equals("server_type", api.NETWORK_TYPE_EIP)
  1555. q = q.Equals("bgp_type", bgpType)
  1556. var nets []SNetwork
  1557. if err := db.FetchModelObjects(NetworkManager, q, &nets); err != nil {
  1558. return nil, errors.Wrapf(err, "fetch eip networks usable in host %s(%s)",
  1559. host.Name, host.Id)
  1560. }
  1561. eipNets := make([]SEipNetwork, 0)
  1562. for i := range nets {
  1563. net := &nets[i]
  1564. cnt, _ := net.GetFreeAddressCount()
  1565. if cnt > 0 {
  1566. eipNets = append(eipNets, NewEipNetwork(net, ownerCred, userCred, cnt))
  1567. }
  1568. }
  1569. if len(eipNets) == 0 {
  1570. return nil, httperrors.NewNotFoundError("no usable eip network")
  1571. }
  1572. // prefer networks with identical project, domain, more free address, Id
  1573. sort.Sort(SEipNetworks(eipNets))
  1574. log.Debugf("eipnets: %s", jsonutils.Marshal(eipNets))
  1575. eip.NetworkId = eipNets[0].GetNetwork().Id
  1576. }
  1577. var err = func() error {
  1578. lockman.LockRawObject(ctx, manager.Keyword(), "name")
  1579. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
  1580. var err error
  1581. eip.Name, err = db.GenerateName(ctx, manager, userCred, eip.Name)
  1582. if err != nil {
  1583. return errors.Wrap(err, "db.GenerateName")
  1584. }
  1585. return manager.TableSpec().Insert(ctx, eip)
  1586. }()
  1587. if err != nil {
  1588. return nil, errors.Wrapf(err, "TableSpec().Insert")
  1589. }
  1590. db.OpsLog.LogEvent(eip, db.ACT_CREATE, eip.GetShortDesc(ctx), userCred)
  1591. var ownerId mcclient.IIdentityProvider = nil
  1592. if vm != nil {
  1593. ownerId = vm.GetOwnerId()
  1594. } else if nat != nil {
  1595. ownerId = nat.GetOwnerId()
  1596. } else if grp != nil {
  1597. ownerId = grp.GetOwnerId()
  1598. } else if lb != nil {
  1599. ownerId = lb.GetOwnerId()
  1600. } else {
  1601. panic("unsupported associate type")
  1602. }
  1603. var provider *SCloudprovider = nil
  1604. if host != nil {
  1605. provider = host.GetCloudprovider()
  1606. } else if nat != nil {
  1607. provider = nat.GetCloudprovider()
  1608. } else if lb != nil {
  1609. provider = lb.GetCloudprovider()
  1610. } else if grp != nil {
  1611. } else {
  1612. panic("unsupported associate type")
  1613. }
  1614. eipPendingUsage := &SRegionQuota{Eip: 1}
  1615. keys := fetchRegionalQuotaKeys(
  1616. rbacscope.ScopeProject,
  1617. ownerId,
  1618. region,
  1619. provider,
  1620. )
  1621. eipPendingUsage.SetKeys(keys)
  1622. quotas.CancelPendingUsage(ctx, userCred, pendingUsage, eipPendingUsage, true)
  1623. return eip, nil
  1624. }
  1625. func (eip *SElasticip) AllocateAndAssociateInstance(ctx context.Context, userCred mcclient.TokenCredential, ins IEipAssociateInstance, input api.ElasticipAssociateInput, parentTaskId string) error {
  1626. err := ValidateAssociateEip(ins)
  1627. if err != nil {
  1628. return err
  1629. }
  1630. params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
  1631. db.StatusBaseSetStatus(ctx, ins, userCred, api.INSTANCE_ASSOCIATE_EIP, "allocate and associate EIP")
  1632. return eip.startEipAllocateTask(ctx, userCred, params, parentTaskId)
  1633. }
  1634. func (self *SElasticip) PerformChangeBandwidth(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1635. if self.Status != api.EIP_STATUS_READY {
  1636. return nil, httperrors.NewInvalidStatusError("cannot change bandwidth in status %s", self.Status)
  1637. }
  1638. bandwidth, err := data.Int("bandwidth")
  1639. if err != nil || bandwidth <= 0 {
  1640. return nil, httperrors.NewInputParameterError("Invalid bandwidth")
  1641. }
  1642. if self.IsManaged() {
  1643. factory, err := self.GetProviderFactory()
  1644. if err != nil {
  1645. return nil, err
  1646. }
  1647. if err := factory.ValidateChangeBandwidth(self.AssociateId, bandwidth); err != nil {
  1648. return nil, httperrors.NewInputParameterError("%v", err)
  1649. }
  1650. }
  1651. err = self.StartEipChangeBandwidthTask(ctx, userCred, bandwidth)
  1652. if err != nil {
  1653. return nil, httperrors.NewGeneralError(err)
  1654. }
  1655. return nil, nil
  1656. }
  1657. func (self *SElasticip) StartEipChangeBandwidthTask(ctx context.Context, userCred mcclient.TokenCredential, bandwidth int64) error {
  1658. self.SetStatus(ctx, userCred, api.EIP_STATUS_CHANGE_BANDWIDTH, "change bandwidth")
  1659. params := jsonutils.NewDict()
  1660. params.Add(jsonutils.NewInt(bandwidth), "bandwidth")
  1661. task, err := taskman.TaskManager.NewTask(ctx, "EipChangeBandwidthTask", self, userCred, params, "", "", nil)
  1662. if err != nil {
  1663. log.Errorf("create EipChangeBandwidthTask fail %s", err)
  1664. return err
  1665. }
  1666. task.ScheduleRun(nil)
  1667. return nil
  1668. }
  1669. func (self *SElasticip) DoChangeBandwidth(ctx context.Context, userCred mcclient.TokenCredential, bandwidth int) error {
  1670. changes := jsonutils.NewDict()
  1671. changes.Add(jsonutils.NewInt(int64(self.Bandwidth)), "obw")
  1672. _, err := db.Update(self, func() error {
  1673. self.Bandwidth = bandwidth
  1674. return nil
  1675. })
  1676. self.SetStatus(ctx, userCred, api.EIP_STATUS_READY, "finish change bandwidth")
  1677. if err != nil {
  1678. log.Errorf("DoChangeBandwidth update fail %s", err)
  1679. return err
  1680. }
  1681. changes.Add(jsonutils.NewInt(int64(bandwidth)), "nbw")
  1682. db.OpsLog.LogEvent(self, db.ACT_CHANGE_BANDWIDTH, changes, userCred)
  1683. return nil
  1684. }
  1685. type EipUsage struct {
  1686. PublicIPCount int
  1687. PublicIpBandwidth int
  1688. EIPCount int
  1689. EipBandwidth int
  1690. EIPUsedCount int
  1691. }
  1692. func (u EipUsage) Total() int {
  1693. return u.PublicIPCount + u.EIPCount
  1694. }
  1695. func (manager *SElasticipManager) usageQByCloudEnv(q *sqlchemy.SQuery, providers []string, brands []string, cloudEnv string) *sqlchemy.SQuery {
  1696. return CloudProviderFilter(q, q.Field("manager_id"), providers, brands, cloudEnv)
  1697. }
  1698. func (manager *SElasticipManager) usageQByRanges(q *sqlchemy.SQuery, rangeObjs []db.IStandaloneModel) *sqlchemy.SQuery {
  1699. return RangeObjectsFilter(q, rangeObjs, q.Field("cloudregion_id"), nil, q.Field("manager_id"), nil, nil)
  1700. }
  1701. func (manager *SElasticipManager) usageQ(
  1702. ctx context.Context,
  1703. scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, q *sqlchemy.SQuery, rangeObjs []db.IStandaloneModel, providers []string, brands []string, cloudEnv string, policyResult rbacutils.SPolicyResult) *sqlchemy.SQuery {
  1704. q = manager.usageQByRanges(q, rangeObjs)
  1705. q = manager.usageQByCloudEnv(q, providers, brands, cloudEnv)
  1706. switch scope {
  1707. case rbacscope.ScopeSystem:
  1708. // do nothing
  1709. case rbacscope.ScopeDomain:
  1710. q = q.Equals("domain_id", ownerId.GetProjectDomainId())
  1711. case rbacscope.ScopeProject:
  1712. q = q.Equals("tenant_id", ownerId.GetProjectId())
  1713. }
  1714. q = db.ObjectIdQueryWithPolicyResult(ctx, q, manager, policyResult)
  1715. return q
  1716. }
  1717. func (manager *SElasticipManager) TotalCount(
  1718. ctx context.Context,
  1719. scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObjs []db.IStandaloneModel, providers []string, brands []string, cloudEnv string, policyResult rbacutils.SPolicyResult) EipUsage {
  1720. usage := EipUsage{}
  1721. q1sq := manager.Query().SubQuery()
  1722. q1 := q1sq.Query(
  1723. sqlchemy.COUNT("public_ip_count", q1sq.Field("id")),
  1724. sqlchemy.SUM("public_ip_bandwidth", q1sq.Field("bandwidth")),
  1725. ).Equals("mode", api.EIP_MODE_INSTANCE_PUBLICIP)
  1726. q1 = manager.usageQ(ctx, scope, ownerId, q1, rangeObjs, providers, brands, cloudEnv, policyResult)
  1727. q2sq := manager.Query().SubQuery()
  1728. q2 := q2sq.Query(
  1729. sqlchemy.COUNT("eip_count", q2sq.Field("id")),
  1730. sqlchemy.SUM("eip_bandwidth", q2sq.Field("bandwidth")),
  1731. ).Equals("mode", api.EIP_MODE_STANDALONE_EIP)
  1732. q2 = manager.usageQ(ctx, scope, ownerId, q2, rangeObjs, providers, brands, cloudEnv, policyResult)
  1733. q3sq := manager.Query().SubQuery()
  1734. q3 := q3sq.Query(
  1735. sqlchemy.COUNT("eip_used_count", q3sq.Field("id")),
  1736. ).Equals("mode", api.EIP_MODE_STANDALONE_EIP).IsNotEmpty("associate_type")
  1737. q3 = manager.usageQ(ctx, scope, ownerId, q3, rangeObjs, providers, brands, cloudEnv, policyResult)
  1738. err := q1.First(&usage)
  1739. if err != nil {
  1740. log.Errorf("q.First error: %v", err)
  1741. }
  1742. err = q2.First(&usage)
  1743. if err != nil {
  1744. log.Errorf("q.First error: %v", err)
  1745. }
  1746. err = q3.First(&usage)
  1747. if err != nil {
  1748. log.Errorf("q.First error: %v", err)
  1749. }
  1750. return usage
  1751. }
  1752. func (self *SElasticip) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  1753. err := self.ValidateDeleteCondition(ctx, nil)
  1754. if err != nil {
  1755. return nil, err
  1756. }
  1757. provider := self.GetCloudprovider()
  1758. if provider != nil {
  1759. if provider.GetEnabled() {
  1760. return nil, httperrors.NewInvalidStatusError("Cannot purge elastic_ip on enabled cloud provider")
  1761. }
  1762. }
  1763. err = self.RealDelete(ctx, userCred)
  1764. return nil, err
  1765. }
  1766. func (self *SElasticip) DoPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) {
  1767. if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
  1768. self.SVirtualResourceBase.DoPendingDelete(ctx, userCred)
  1769. return
  1770. }
  1771. self.Dissociate(ctx, userCred)
  1772. }
  1773. func (self *SElasticip) getCloudProviderInfo() SCloudProviderInfo {
  1774. region, _ := self.GetRegion()
  1775. provider := self.GetCloudprovider()
  1776. return MakeCloudProviderInfo(region, nil, provider)
  1777. }
  1778. func (eip *SElasticip) GetUsages() []db.IUsage {
  1779. if eip.PendingDeleted || eip.Deleted {
  1780. return nil
  1781. }
  1782. usage := SRegionQuota{Eip: 1}
  1783. keys, err := eip.GetQuotaKeys()
  1784. if err != nil {
  1785. log.Errorf("disk.GetQuotaKeys fail %s", err)
  1786. return nil
  1787. }
  1788. usage.SetKeys(keys)
  1789. return []db.IUsage{
  1790. &usage,
  1791. }
  1792. }
  1793. func (self *SElasticip) PerformRemoteUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ElasticipRemoteUpdateInput) (jsonutils.JSONObject, error) {
  1794. err := self.StartRemoteUpdateTask(ctx, userCred, (input.ReplaceTags != nil && *input.ReplaceTags), "")
  1795. if err != nil {
  1796. return nil, errors.Wrap(err, "StartRemoteUpdateTask")
  1797. }
  1798. return nil, nil
  1799. }
  1800. func (self *SElasticip) StartRemoteUpdateTask(ctx context.Context, userCred mcclient.TokenCredential, replaceTags bool, parentTaskId string) error {
  1801. data := jsonutils.NewDict()
  1802. data.Add(jsonutils.NewBool(replaceTags), "replace_tags")
  1803. task, err := taskman.TaskManager.NewTask(ctx, "EipRemoteUpdateTask", self, userCred, data, parentTaskId, "", nil)
  1804. if err != nil {
  1805. return errors.Wrap(err, "NewTask")
  1806. }
  1807. self.SetStatus(ctx, userCred, apis.STATUS_UPDATE_TAGS, "StartRemoteUpdateTask")
  1808. return task.ScheduleRun(nil)
  1809. }
  1810. func (self *SElasticip) OnMetadataUpdated(ctx context.Context, userCred mcclient.TokenCredential) {
  1811. if len(self.ExternalId) == 0 || options.Options.KeepTagLocalization {
  1812. return
  1813. }
  1814. if account := self.GetCloudaccount(); account != nil && account.ReadOnly {
  1815. return
  1816. }
  1817. err := self.StartRemoteUpdateTask(ctx, userCred, true, "")
  1818. if err != nil {
  1819. log.Errorf("StartRemoteUpdateTask fail: %s", err)
  1820. }
  1821. }
  1822. func (manager *SElasticipManager) ListItemExportKeys(ctx context.Context,
  1823. q *sqlchemy.SQuery,
  1824. userCred mcclient.TokenCredential,
  1825. keys stringutils2.SSortedStrings,
  1826. ) (*sqlchemy.SQuery, error) {
  1827. var err error
  1828. q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  1829. if err != nil {
  1830. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemExportKeys")
  1831. }
  1832. if keys.ContainsAny(manager.SManagedResourceBaseManager.GetExportKeys()...) {
  1833. q, err = manager.SManagedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
  1834. if err != nil {
  1835. return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemExportKeys")
  1836. }
  1837. }
  1838. return q, nil
  1839. }