| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package models
- import (
- "context"
- "database/sql"
- "fmt"
- "sort"
- "time"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/gotypes"
- "yunion.io/x/pkg/tristate"
- "yunion.io/x/pkg/util/compare"
- "yunion.io/x/pkg/util/pinyinutils"
- "yunion.io/x/pkg/util/rbacscope"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
- "yunion.io/x/onecloud/pkg/apis"
- billing_api "yunion.io/x/onecloud/pkg/apis/billing"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/cloudcommon/consts"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
- "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
- "yunion.io/x/onecloud/pkg/cloudcommon/policy"
- "yunion.io/x/onecloud/pkg/cloudcommon/validators"
- "yunion.io/x/onecloud/pkg/compute/options"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/util/rbacutils"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
- )
- // +onecloud:swagger-gen-model-singular=eip
- // +onecloud:swagger-gen-model-plural=eips
- type SElasticipManager struct {
- db.SVirtualResourceBaseManager
- db.SExternalizedResourceBaseManager
- SManagedResourceBaseManager
- SCloudregionResourceBaseManager
- }
- var ElasticipManager *SElasticipManager
- func init() {
- ElasticipManager = &SElasticipManager{
- SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
- SElasticip{},
- "elasticips_tbl",
- "eip",
- "eips",
- ),
- }
- ElasticipManager.SetVirtualObject(ElasticipManager)
- ElasticipManager.TableSpec().AddIndex(true, "associate_id", "associate_type")
- }
- type SElasticip struct {
- db.SVirtualResourceBase
- db.SExternalizedResourceBase
- SManagedResourceBase
- SCloudregionResourceBase `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"`
- SBillingResourceBase
- // IP子网Id, 仅私有云不为空
- NetworkId string `width:"36" charset:"ascii" nullable:"true" get:"user" list:"user" create:"optional"`
- // 标识弹性或非弹性
- // | Mode | 说明 |
- // |------------|------------|
- // | public_ip | 公网IP |
- // | elastic_ip | 弹性公网IP |
- //
- // example: elastic_ip
- Mode string `width:"32" charset:"ascii" get:"user" list:"user" create:"optional"`
- // IP地址
- IpAddr string `width:"17" charset:"ascii" list:"user" update:"admin"`
- // 绑定资源类型
- AssociateType string `width:"32" charset:"ascii" list:"user" update:"admin"`
- // 绑定资源Id
- AssociateId string `width:"256" charset:"ascii" list:"user" update:"admin"`
- // 带宽大小
- Bandwidth int `list:"user" create:"optional" default:"0"`
- SBillingChargeTypeBase
- // 计费类型: 流量、带宽
- // example: bandwidth
- // ChargeType billing_api.TNetChargeType `width:"64" name:"charge_type" list:"user" create:"required"`
- // 线路类型
- BgpType string `width:"64" charset:"utf8" nullable:"true" get:"user" list:"user" create:"optional"`
- // 是否跟随主机删除而自动释放
- AutoDellocate tristate.TriState `default:"false" get:"user" create:"optional" update:"user"`
- // 区域Id
- // CloudregionId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"`
- }
- // 弹性公网IP列表
- func (manager *SElasticipManager) ListItemFilter(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.ElasticipListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, query.RegionalFilterListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
- }
- associateType := query.UsableEipForAssociateType
- associateId := query.UsableEipForAssociateId
- if len(associateType) > 0 && len(associateId) > 0 {
- q = q.Equals("status", api.EIP_STATUS_READY)
- switch associateType {
- case api.EIP_ASSOCIATE_TYPE_SERVER:
- serverObj, err := GuestManager.FetchByIdOrName(ctx, userCred, associateId)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError("server %s not found", associateId)
- }
- return nil, httperrors.NewGeneralError(err)
- }
- guest := serverObj.(*SGuest)
- region, err := guest.GetRegion()
- if err != nil {
- return nil, errors.Wrapf(err, "GetRegion")
- }
- if guest.Hypervisor == api.HYPERVISOR_KVM || (utils.IsInStringArray(region.Provider, api.PRIVATE_CLOUD_PROVIDERS) &&
- guest.Hypervisor != api.HYPERVISOR_HCSO && guest.Hypervisor != api.HYPERVISOR_HCS) {
- zone, _ := guest.getZone()
- networks := NetworkManager.Query().SubQuery()
- wires := WireManager.Query().SubQuery()
- sq := networks.Query(networks.Field("id")).Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))).
- Filter(sqlchemy.Equals(wires.Field("zone_id"), zone.Id)).SubQuery()
- q = q.Filter(sqlchemy.In(q.Field("network_id"), sq))
- gns := GuestnetworkManager.Query("network_id").Equals("guest_id", guest.Id).SubQuery()
- q = q.Filter(sqlchemy.NotIn(q.Field("network_id"), gns))
- } else {
- region, _ := guest.getRegion()
- q = q.Equals("cloudregion_id", region.Id)
- }
- host, _ := guest.GetHost()
- managerId := host.ManagerId
- if managerId != "" {
- q = q.Equals("manager_id", managerId)
- } else {
- q = q.IsNullOrEmpty("manager_id")
- }
- case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
- groupObj, err := GroupManager.FetchByIdOrName(ctx, userCred, associateId)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2(GroupManager.Keyword(), associateId)
- }
- return nil, httperrors.NewGeneralError(err)
- }
- group := groupObj.(*SGroup)
- net, err := group.getAttachedNetwork()
- if err != nil {
- return nil, errors.Wrap(err, "group.getAttachedNetwork")
- }
- if net == nil {
- return nil, errors.Wrap(httperrors.ErrInvalidStatus, "group is not attached to network")
- }
- zone, _ := net.GetZone()
- networks := NetworkManager.Query().SubQuery()
- wires := WireManager.Query().SubQuery()
- sq := networks.Query(networks.Field("id")).Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))).
- Filter(sqlchemy.Equals(wires.Field("zone_id"), zone.Id)).SubQuery()
- q = q.Filter(sqlchemy.In(q.Field("network_id"), sq))
- q = q.Filter(sqlchemy.NotEquals(q.Field("network_id"), net.Id))
- q = q.IsNullOrEmpty("manager_id")
- case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
- _nat, err := validators.ValidateModel(ctx, userCred, NatGatewayManager, &query.UsableEipForAssociateId)
- if err != nil {
- return nil, err
- }
- nat := _nat.(*SNatGateway)
- vpc, err := nat.GetVpc()
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrapf(err, "nat.GetVpc"))
- }
- q = q.Equals("cloudregion_id", vpc.CloudregionId)
- if len(vpc.ManagerId) > 0 {
- q = q.Equals("manager_id", vpc.ManagerId)
- }
- q = q.Filter(
- sqlchemy.OR(
- sqlchemy.Equals(q.Field("associate_id"), nat.Id),
- sqlchemy.IsNullOrEmpty(q.Field("associate_id")),
- ),
- )
- case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
- _lb, err := validators.ValidateModel(ctx, userCred, LoadbalancerManager, &query.UsableEipForAssociateId)
- if err != nil {
- return nil, err
- }
- lb := _lb.(*SLoadbalancer)
- q = q.Equals("cloudregion_id", lb.CloudregionId)
- if len(lb.ManagerId) > 0 {
- q = q.Equals("manager_id", lb.ManagerId)
- } else {
- zone, _ := lb.GetZone()
- networks := NetworkManager.Query().SubQuery()
- wires := WireManager.Query().SubQuery()
- sq := networks.Query(networks.Field("id")).Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))).
- Filter(sqlchemy.Equals(wires.Field("zone_id"), zone.Id)).SubQuery()
- q = q.Filter(sqlchemy.In(q.Field("network_id"), sq))
- gns := LoadbalancernetworkManager.Query("network_id").Equals("loadbalancer_id", lb.Id).SubQuery()
- q = q.Filter(sqlchemy.NotIn(q.Field("network_id"), gns))
- }
- q = q.IsNullOrEmpty("associate_type")
- default:
- return nil, httperrors.NewInputParameterError("Not support associate type %s, only support %s", associateType, api.EIP_ASSOCIATE_VALID_TYPES)
- }
- }
- if query.Usable != nil && *query.Usable {
- q = q.Equals("status", api.EIP_STATUS_READY)
- q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("associate_id")), sqlchemy.IsEmpty(q.Field("associate_id"))))
- }
- if query.IsAssociated != nil {
- if *query.IsAssociated {
- q = q.IsNotEmpty("associate_type")
- } else {
- q = q.IsNullOrEmpty("associate_type")
- }
- }
- if len(query.Mode) > 0 {
- q = q.In("mode", query.Mode)
- }
- if len(query.IpAddr) > 0 {
- q = q.In("ip_addr", query.IpAddr)
- }
- if len(query.AssociateType) > 0 {
- q = q.In("associate_type", query.AssociateType)
- }
- if len(query.AssociateId) > 0 {
- q = q.In("associate_id", query.AssociateId)
- }
- if len(query.ChargeType) > 0 {
- q = q.In("charge_type", query.ChargeType)
- }
- if len(query.BgpType) > 0 {
- q = q.In("bgp_type", query.BgpType)
- }
- if query.AutoDellocate != nil {
- if *query.AutoDellocate {
- q = q.IsTrue("auto_dellocate")
- } else {
- q = q.IsFalse("auto_dellocate")
- }
- }
- if len(query.AssociateName) > 0 {
- filters := []sqlchemy.ICondition{}
- likeQuery := func(sq *sqlchemy.SQuery) *sqlchemy.SQuery {
- conditions := []sqlchemy.ICondition{}
- for _, name := range query.AssociateName {
- conditions = append(conditions, sqlchemy.Contains(sq.Field("name"), name))
- }
- return sq.Filter(sqlchemy.OR(conditions...))
- }
- for _, m := range []db.IModelManager{
- GuestManager,
- GroupManager,
- LoadbalancerManager,
- NatGatewayManager,
- } {
- sq := m.Query("id")
- sq = likeQuery(sq)
- filters = append(filters, sqlchemy.In(q.Field("associate_id"), sq))
- }
- q = q.Filter(sqlchemy.OR(filters...))
- }
- return q, nil
- }
- func (manager *SElasticipManager) OrderByExtraFields(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.ElasticipListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
- }
- q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
- }
- q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.RegionalFilterListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
- }
- if db.NeedOrderQuery([]string{query.OrderByIp}) {
- db.OrderByFields(q, []string{query.OrderByIp}, []sqlchemy.IQueryField{sqlchemy.INET_ATON(q.Field("ip_addr"))})
- }
- return q, nil
- }
- func (manager *SElasticipManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- return q, httperrors.ErrNotFound
- }
- func (manager *SElasticipManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraFields(q, resource, fields)
- if err == nil {
- return q, nil
- }
- return q, httperrors.ErrNotFound
- }
- func (self *SElasticip) GetNetwork() (*SNetwork, error) {
- network, err := NetworkManager.FetchById(self.NetworkId)
- if err != nil {
- return nil, err
- }
- return network.(*SNetwork), nil
- }
- func (self *SElasticip) GetZone() (*SZone, error) {
- network, err := self.GetNetwork()
- if err != nil {
- return nil, err
- }
- return network.GetZone()
- }
- func (self *SElasticip) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
- desc := self.SVirtualResourceBase.GetShortDesc(ctx)
- // desc.Add(jsonutils.NewString(self.ChargeType), "charge_type")
- desc.Add(jsonutils.NewInt(int64(self.Bandwidth)), "bandwidth")
- desc.Add(jsonutils.NewString(self.Mode), "mode")
- desc.Add(jsonutils.NewString(self.IpAddr), "ip_addr")
- desc.Add(jsonutils.NewString(self.BgpType), "bgp_type")
- // region := self.GetRegion()
- // if len(region.ExternalId) > 0 {
- // regionInfo := strings.Split(region.ExternalId, "/")
- // if len(regionInfo) == 2 {
- // desc.Add(jsonutils.NewString(strings.ToLower(regionInfo[0])), "hypervisor")
- // desc.Add(jsonutils.NewString(regionInfo[1]), "region")
- // }
- //}
- billingInfo := SCloudBillingInfo{}
- billingInfo.SCloudProviderInfo = self.getCloudProviderInfo()
- billingInfo.SBillingBaseInfo = self.getBillingBaseInfo()
- billingInfo.InternetChargeType = self.ChargeType
- if priceKey := self.GetMetadata(ctx, "ext:price_key", nil); len(priceKey) > 0 {
- billingInfo.PriceKey = priceKey
- }
- desc.Update(jsonutils.Marshal(billingInfo))
- return desc
- }
- func (manager *SElasticipManager) SyncEips(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- provider *SCloudprovider,
- region *SCloudregion,
- eips []cloudprovider.ICloudEIP,
- syncOwnerId mcclient.IIdentityProvider,
- xor bool,
- ) compare.SyncResult {
- lockman.LockRawObject(ctx, manager.KeywordPlural(), region.Id)
- defer lockman.ReleaseRawObject(ctx, manager.KeywordPlural(), region.Id)
- syncResult := compare.SyncResult{}
- dbEips, err := region.GetElasticIps(provider.Id, api.EIP_MODE_STANDALONE_EIP)
- if err != nil {
- syncResult.Error(err)
- return syncResult
- }
- for i := range dbEips {
- if taskman.TaskManager.IsInTask(&dbEips[i]) {
- syncResult.Error(fmt.Errorf("object in task"))
- return syncResult
- }
- }
- removed := make([]SElasticip, 0)
- commondb := make([]SElasticip, 0)
- commonext := make([]cloudprovider.ICloudEIP, 0)
- added := make([]cloudprovider.ICloudEIP, 0)
- err = compare.CompareSets(dbEips, eips, &removed, &commondb, &commonext, &added)
- if err != nil {
- syncResult.Error(err)
- return syncResult
- }
- for i := 0; i < len(removed); i += 1 {
- err = removed[i].syncRemoveCloudEip(ctx, userCred)
- if err != nil {
- syncResult.DeleteError(err)
- } else {
- syncResult.Delete()
- }
- }
- if !xor {
- for i := 0; i < len(commondb); i += 1 {
- err = commondb[i].SyncWithCloudEip(ctx, userCred, provider, commonext[i], syncOwnerId)
- if err != nil {
- syncResult.UpdateError(err)
- continue
- }
- syncResult.Update()
- }
- }
- for i := 0; i < len(added); i += 1 {
- eip := &SElasticip{}
- eip.SetModelManager(ElasticipManager, eip)
- err := ElasticipManager.Query().
- Equals("ip_addr", added[i].GetIpAddr()).
- Equals("manager_id", provider.Id).
- Equals("cloudregion_id", region.Id).
- Equals("mode", api.EIP_MODE_INSTANCE_PUBLICIP).First(eip)
- // 公网IP转弹性IP
- if err == nil {
- err = eip.SyncWithCloudEip(ctx, userCred, provider, added[i], syncOwnerId)
- if err != nil {
- syncResult.UpdateError(err)
- continue
- }
- syncResult.Update()
- continue
- }
- _, err = manager.newFromCloudEip(ctx, userCred, added[i], provider, region, syncOwnerId)
- if err != nil {
- syncResult.AddError(err)
- } else {
- syncResult.Add()
- }
- }
- return syncResult
- }
- func (self *SElasticip) syncRemoveCloudEip(ctx context.Context, userCred mcclient.TokenCredential) error {
- lockman.LockObject(ctx, self)
- defer lockman.ReleaseObject(ctx, self)
- err := self.RealDelete(ctx, userCred)
- if err != nil {
- return err
- }
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: self,
- Action: notifyclient.ActionSyncDelete,
- })
- return nil
- }
- func (self *SElasticip) SyncInstanceWithCloudEip(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudEIP) error {
- resource := self.GetAssociateResource()
- vmExtId := ext.GetAssociationExternalId()
- if resource == nil && len(vmExtId) == 0 {
- return nil
- }
- if resource != nil && resource.(db.IExternalizedModel).GetExternalId() == vmExtId {
- return nil
- }
- if resource != nil { // dissociate
- err := self.Dissociate(ctx, userCred)
- if err != nil {
- log.Errorf("fail to dissociate vm: %s", err)
- return err
- }
- }
- if len(vmExtId) > 0 {
- var manager db.IModelManager
- switch ext.GetAssociationType() {
- case api.EIP_ASSOCIATE_TYPE_SERVER:
- manager = GuestManager
- case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
- manager = NatGatewayManager
- case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
- manager = LoadbalancerManager
- // case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
- // not supported
- // manager = GroupManager
- default:
- return errors.Error("unsupported association type")
- }
- extRes, err := db.FetchByExternalIdAndManagerId(manager, vmExtId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- switch ext.GetAssociationType() {
- case api.EIP_ASSOCIATE_TYPE_SERVER:
- sq := HostManager.Query().SubQuery()
- return q.Join(sq, sqlchemy.Equals(sq.Field("id"), q.Field("host_id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), self.ManagerId))
- case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
- return q.Equals("manager_id", self.ManagerId)
- case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
- sq := VpcManager.Query("id").Equals("manager_id", self.ManagerId)
- return q.In("vpc_id", sq.SubQuery())
- }
- return q
- })
- if err != nil {
- return errors.Wrapf(err, "db.FetchByExternalIdAndManagerId %s %s", ext.GetAssociationType(), vmExtId)
- }
- err = self.AssociateInstance(ctx, userCred, ext.GetAssociationType(), extRes.(db.IStatusStandaloneModel))
- if err != nil {
- return errors.Wrapf(err, "AssociateInstance")
- }
- }
- return nil
- }
- func (self *SElasticip) SyncWithCloudEip(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, ext cloudprovider.ICloudEIP, syncOwnerId mcclient.IIdentityProvider) error {
- diff, err := db.UpdateWithLock(ctx, self, func() error {
- if options.Options.EnableSyncName {
- newName, _ := db.GenerateAlterName(self, ext.GetName())
- if len(newName) > 0 {
- self.Name = newName
- }
- }
- if bandwidth := ext.GetBandwidth(); bandwidth != 0 {
- self.Bandwidth = bandwidth
- }
- self.IpAddr = ext.GetIpAddr()
- self.Mode = ext.GetMode()
- self.Status = ext.GetStatus()
- self.ExternalId = ext.GetGlobalId()
- self.IsEmulated = ext.IsEmulated()
- self.AssociateType = ext.GetAssociationType()
- if chargeType := ext.GetInternetChargeType(); len(chargeType) > 0 {
- self.ChargeType = billing_api.TNetChargeType(chargeType)
- }
- self.BillingType = billing_api.TBillingType(ext.GetBillingType())
- self.ExpiredAt = time.Time{}
- self.AutoRenew = false
- if self.BillingType == billing_api.BILLING_TYPE_PREPAID {
- self.ExpiredAt = ext.GetExpiredAt()
- self.AutoRenew = ext.IsAutoRenew()
- }
- if createAt := ext.GetCreatedAt(); !createAt.IsZero() {
- self.CreatedAt = createAt
- }
- return nil
- })
- if err != nil {
- return errors.Wrapf(err, "db.UpdateWithLock")
- }
- db.OpsLog.LogSyncUpdate(self, diff, userCred)
- if len(diff) > 0 {
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: self,
- Action: notifyclient.ActionSyncUpdate,
- })
- }
- //err = self.SyncInstanceWithCloudEip(ctx, userCred, ext)
- //if err != nil {
- // return errors.Wrap(err, "fail to sync associated instance of EIP")
- //}
- if account := self.GetCloudaccount(); account != nil {
- syncVirtualResourceMetadata(ctx, userCred, self, ext, account.ReadOnly)
- }
- // eip有绑定资源,并且绑定资源是项目资源,eip项目信息跟随绑定资源
- if res := self.GetAssociateResource(); res != nil && len(res.GetOwnerId().GetProjectId()) > 0 {
- self.SyncCloudProjectId(userCred, res.GetOwnerId())
- } else {
- SyncCloudProject(ctx, userCred, self, syncOwnerId, ext, provider)
- }
- return nil
- }
- func (manager *SElasticipManager) newFromCloudEip(ctx context.Context, userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, provider *SCloudprovider, region *SCloudregion, syncOwnerId mcclient.IIdentityProvider) (*SElasticip, error) {
- eip := SElasticip{}
- eip.SetModelManager(manager, &eip)
- eip.Status = extEip.GetStatus()
- eip.ExternalId = extEip.GetGlobalId()
- eip.IpAddr = extEip.GetIpAddr()
- eip.Mode = extEip.GetMode()
- eip.IsEmulated = extEip.IsEmulated()
- eip.ManagerId = provider.Id
- eip.CloudregionId = region.Id
- eip.ChargeType = billing_api.TNetChargeType(extEip.GetInternetChargeType())
- eip.AssociateType = extEip.GetAssociationType()
- if !extEip.GetCreatedAt().IsZero() {
- eip.CreatedAt = extEip.GetCreatedAt()
- }
- eip.BillingType = billing_api.TBillingType(extEip.GetBillingType())
- eip.ExpiredAt = time.Time{}
- eip.AutoRenew = false
- if eip.BillingType == billing_api.BILLING_TYPE_PREPAID {
- eip.ExpiredAt = extEip.GetExpiredAt()
- eip.AutoRenew = extEip.IsAutoRenew()
- }
- if len(eip.ChargeType) == 0 {
- eip.ChargeType = billing_api.NET_CHARGE_TYPE_BY_TRAFFIC
- }
- eip.Bandwidth = extEip.GetBandwidth()
- if networkId := extEip.GetINetworkId(); len(networkId) > 0 {
- network, err := db.FetchByExternalIdAndManagerId(NetworkManager, networkId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- wire := WireManager.Query().SubQuery()
- vpc := VpcManager.Query().SubQuery()
- return q.Join(wire, sqlchemy.Equals(wire.Field("id"), q.Field("wire_id"))).
- Join(vpc, sqlchemy.Equals(vpc.Field("id"), wire.Field("vpc_id"))).
- Filter(sqlchemy.Equals(vpc.Field("manager_id"), provider.Id))
- })
- if err != nil {
- return nil, errors.Wrapf(err, "failed to found network by externalId %s", networkId)
- }
- eip.NetworkId = network.GetId()
- }
- var err = func() error {
- lockman.LockRawObject(ctx, manager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
- newName, err := db.GenerateName(ctx, manager, syncOwnerId, extEip.GetName())
- if err != nil {
- return err
- }
- eip.Name = newName
- return manager.TableSpec().Insert(ctx, &eip)
- }()
- if err != nil {
- return nil, errors.Wrapf(err, "newFromCloudEip")
- }
- //err = eip.SyncInstanceWithCloudEip(ctx, userCred, extEip)
- //if err != nil {
- // return nil, errors.Wrap(err, "fail to sync associated instance of EIP")
- //}
- syncVirtualResourceMetadata(ctx, userCred, &eip, extEip, false)
- if res := eip.GetAssociateResource(); res != nil && len(res.GetOwnerId().GetProjectId()) > 0 {
- eip.SyncCloudProjectId(userCred, res.GetOwnerId())
- } else {
- SyncCloudProject(ctx, userCred, &eip, syncOwnerId, extEip, provider)
- }
- db.OpsLog.LogEvent(&eip, db.ACT_CREATE, eip.GetShortDesc(ctx), userCred)
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: &eip,
- Action: notifyclient.ActionSyncCreate,
- })
- return &eip, nil
- }
- func (manager *SElasticipManager) getEipForInstance(instanceType string, instanceId string) (*SElasticip, error) {
- return manager.getEip(instanceType, instanceId, api.EIP_MODE_STANDALONE_EIP)
- }
- func (manager *SElasticipManager) getEip(instanceType string, instanceId string, eipMode string) (*SElasticip, error) {
- eip := SElasticip{}
- q := manager.Query()
- q = q.Equals("associate_type", instanceType)
- q = q.Equals("associate_id", instanceId)
- if len(eipMode) > 0 {
- q = q.Equals("mode", eipMode)
- }
- err := q.First(&eip)
- if err != nil {
- if err != sql.ErrNoRows {
- log.Errorf("getEipForInstance query fail %s", err)
- return nil, err
- } else {
- return nil, nil
- }
- }
- eip.SetModelManager(manager, &eip)
- return &eip, nil
- }
- func (self *SElasticip) IsAssociated() bool {
- if len(self.AssociateId) == 0 {
- return false
- }
- if self.GetAssociateVM() != nil {
- return true
- }
- if self.GetAssociateLoadbalancer() != nil {
- return true
- }
- if self.GetAssociateNatGateway() != nil {
- return true
- }
- if self.GetAssociateInstanceGroup() != nil {
- return true
- }
- return false
- }
- func (self *SElasticip) GetAssociateVM() *SGuest {
- if self.AssociateType == api.EIP_ASSOCIATE_TYPE_SERVER && len(self.AssociateId) > 0 {
- return GuestManager.FetchGuestById(self.AssociateId)
- }
- return nil
- }
- func (self *SElasticip) GetAssociateInstanceGroup() *SGroup {
- if self.AssociateType == api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP && len(self.AssociateId) > 0 {
- _grp, err := GroupManager.FetchById(self.AssociateId)
- if err != nil {
- return nil
- }
- return _grp.(*SGroup)
- }
- return nil
- }
- func (self *SElasticip) GetAssociateLoadbalancer() *SLoadbalancer {
- if self.AssociateType == api.EIP_ASSOCIATE_TYPE_LOADBALANCER && len(self.AssociateId) > 0 {
- _lb, err := LoadbalancerManager.FetchById(self.AssociateId)
- if err != nil {
- return nil
- }
- lb := _lb.(*SLoadbalancer)
- if lb.PendingDeleted {
- return nil
- }
- return lb
- }
- return nil
- }
- func (self *SElasticip) GetAssociateNatGateway() *SNatGateway {
- if self.AssociateType == api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY && len(self.AssociateId) > 0 {
- natGateway, err := NatGatewayManager.FetchById(self.AssociateId)
- if err != nil {
- return nil
- }
- return natGateway.(*SNatGateway)
- }
- return nil
- }
- func (self *SElasticip) GetAssociateResource() db.IModel {
- if vm := self.GetAssociateVM(); vm != nil {
- return vm
- }
- if lb := self.GetAssociateLoadbalancer(); lb != nil {
- return lb
- }
- if nat := self.GetAssociateNatGateway(); nat != nil {
- return nat
- }
- if grp := self.GetAssociateInstanceGroup(); grp != nil {
- return grp
- }
- return nil
- }
- func (self *SElasticip) Dissociate(ctx context.Context, userCred mcclient.TokenCredential) error {
- if len(self.AssociateType) == 0 {
- return nil
- }
- var vm *SGuest
- var nat *SNatGateway
- var lb *SLoadbalancer
- var grp *SGroup
- switch self.AssociateType {
- case api.EIP_ASSOCIATE_TYPE_SERVER:
- vm = self.GetAssociateVM()
- if vm == nil {
- log.Errorf("dissociate VM not exists???")
- }
- case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
- nat = self.GetAssociateNatGateway()
- if nat == nil {
- log.Errorf("dissociate Nat gateway not exists???")
- }
- case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
- lb = self.GetAssociateLoadbalancer()
- if lb == nil {
- log.Errorf("dissociate loadbalancer not exists???")
- }
- case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
- grp = self.GetAssociateInstanceGroup()
- if grp == nil {
- log.Errorf("dissociate instance_group not exists???")
- }
- }
- _, err := db.Update(self, func() error {
- self.AssociateId = ""
- self.AssociateType = ""
- return nil
- })
- if err != nil {
- return err
- }
- if vm != nil {
- db.OpsLog.LogDetachEvent(ctx, vm, self, userCred, self.GetShortDesc(ctx))
- db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, vm.GetShortDesc(ctx), userCred)
- db.OpsLog.LogEvent(vm, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred)
- }
- if nat != nil {
- db.OpsLog.LogDetachEvent(ctx, nat, self, userCred, self.GetShortDesc(ctx))
- db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, nat.GetShortDesc(ctx), userCred)
- db.OpsLog.LogEvent(nat, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred)
- }
- if lb != nil {
- db.OpsLog.LogDetachEvent(ctx, lb, self, userCred, self.GetShortDesc(ctx))
- db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, lb.GetShortDesc(ctx), userCred)
- db.OpsLog.LogEvent(lb, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred)
- }
- if grp != nil {
- db.OpsLog.LogDetachEvent(ctx, grp, self, userCred, self.GetShortDesc(ctx))
- db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, grp.GetShortDesc(ctx), userCred)
- db.OpsLog.LogEvent(grp, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred)
- }
- if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
- self.RealDelete(ctx, userCred)
- }
- return nil
- }
- func (self *SElasticip) AssociateLoadbalancer(ctx context.Context, userCred mcclient.TokenCredential, lb *SLoadbalancer) error {
- if lb.PendingDeleted {
- return fmt.Errorf("loadbalancer is deleted")
- }
- if len(self.AssociateType) > 0 && len(self.AssociateId) > 0 {
- if self.AssociateType == api.EIP_ASSOCIATE_TYPE_LOADBALANCER && self.AssociateId == lb.Id {
- return nil
- }
- if self.GetAssociateResource() != nil {
- return fmt.Errorf("eip has been associated!!")
- }
- }
- _, err := db.Update(self, func() error {
- self.AssociateType = api.EIP_ASSOCIATE_TYPE_LOADBALANCER
- self.AssociateId = lb.Id
- return nil
- })
- if err != nil {
- return err
- }
- db.OpsLog.LogAttachEvent(ctx, lb, self, userCred, self.GetShortDesc(ctx))
- db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, lb.GetShortDesc(ctx), userCred)
- db.OpsLog.LogEvent(lb, db.ACT_EIP_ATTACH, self.GetShortDesc(ctx), userCred)
- return nil
- }
- func (self *SElasticip) AssociateInstance(ctx context.Context, userCred mcclient.TokenCredential, insType string, ins db.IStatusStandaloneModel) error {
- switch insType {
- case api.EIP_ASSOCIATE_TYPE_SERVER:
- vm := ins.(*SGuest)
- if vm.PendingDeleted || vm.Deleted {
- return fmt.Errorf("vm is deleted")
- }
- }
- if len(self.AssociateType) > 0 && len(self.AssociateId) > 0 {
- if self.AssociateType == insType && self.AssociateId == ins.GetId() {
- return nil
- }
- if self.GetAssociateResource() != nil {
- return fmt.Errorf("eip has been associated!!")
- }
- }
- _, err := db.Update(self, func() error {
- self.AssociateType = insType
- self.AssociateId = ins.GetId()
- return nil
- })
- if err != nil {
- return errors.Wrapf(err, "db.Update")
- }
- db.OpsLog.LogAttachEvent(ctx, ins, self, userCred, self.GetShortDesc(ctx))
- db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, ins.GetShortDesc(ctx), userCred)
- db.OpsLog.LogEvent(ins, db.ACT_EIP_ATTACH, self.GetShortDesc(ctx), userCred)
- return nil
- }
- func (self *SElasticip) AssociateInstanceGroup(ctx context.Context, userCred mcclient.TokenCredential, insType string, ins db.IStatusStandaloneModel) error {
- switch insType {
- case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
- vm := ins.(*SGroup)
- if vm.PendingDeleted || vm.Deleted {
- return fmt.Errorf("group is deleted")
- }
- }
- if len(self.AssociateType) > 0 && len(self.AssociateId) > 0 {
- if self.AssociateType == insType && self.AssociateId == ins.GetId() {
- return nil
- }
- if self.GetAssociateResource() != nil {
- return fmt.Errorf("eip has been associated!!")
- }
- }
- _, err := db.Update(self, func() error {
- self.AssociateType = insType
- self.AssociateId = ins.GetId()
- return nil
- })
- if err != nil {
- return errors.Wrapf(err, "db.Update")
- }
- db.OpsLog.LogAttachEvent(ctx, ins, self, userCred, self.GetShortDesc(ctx))
- db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, ins.GetShortDesc(ctx), userCred)
- db.OpsLog.LogEvent(ins, db.ACT_EIP_ATTACH, self.GetShortDesc(ctx), userCred)
- return nil
- }
- func (self *SElasticip) AssociateNatGateway(ctx context.Context, userCred mcclient.TokenCredential, nat *SNatGateway) error {
- if nat.Deleted {
- return fmt.Errorf("nat gateway is deleted")
- }
- if len(self.AssociateId) > 0 && self.AssociateType == api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY && self.AssociateId == nat.Id {
- return nil
- }
- _, err := db.Update(self, func() error {
- self.AssociateType = api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
- self.AssociateId = nat.Id
- return nil
- })
- if err != nil {
- return err
- }
- db.OpsLog.LogAttachEvent(ctx, nat, self, userCred, self.GetShortDesc(ctx))
- db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, nat.GetShortDesc(ctx), userCred)
- db.OpsLog.LogEvent(nat, db.ACT_EIP_ATTACH, self.GetShortDesc(ctx), userCred)
- return nil
- }
- func (manager *SElasticipManager) getEipByExtEip(ctx context.Context, userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, provider *SCloudprovider, region *SCloudregion, syncOwnerId mcclient.IIdentityProvider) (*SElasticip, error) {
- eipId := extEip.GetGlobalId()
- eipObj, err := db.FetchByExternalIdAndManagerId(manager, eipId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- return q.Equals("manager_id", provider.Id)
- })
- if err == nil {
- return eipObj.(*SElasticip), nil
- }
- if errors.Cause(err) != sql.ErrNoRows {
- return nil, errors.Wrapf(err, "FetchByExternalIdAndManagerId %s", eipId)
- }
- return manager.newFromCloudEip(ctx, userCred, extEip, provider, region, syncOwnerId)
- }
- func (manager *SElasticipManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.SElasticipCreateInput) (api.SElasticipCreateInput, error) {
- if input.CloudregionId == "" {
- input.CloudregionId = api.DEFAULT_REGION_ID
- }
- obj, err := CloudregionManager.FetchByIdOrName(ctx, nil, input.CloudregionId)
- if err != nil {
- if err != sql.ErrNoRows {
- return input, httperrors.NewGeneralError(err)
- }
- return input, httperrors.NewResourceNotFoundError2("cloudregion", input.CloudregionId)
- }
- var (
- region = obj.(*SCloudregion)
- regionDriver = region.GetDriver()
- )
- input.CloudregionId = region.GetId()
- // publicIp cannot be created standalone
- input.Mode = api.EIP_MODE_STANDALONE_EIP
- if input.ChargeType == "" {
- input.ChargeType = billing_api.TNetChargeType(regionDriver.GetEipDefaultChargeType())
- }
- if !utils.IsInStringArray(string(input.ChargeType), []string{string(billing_api.NET_CHARGE_TYPE_BY_BANDWIDTH), string(billing_api.NET_CHARGE_TYPE_BY_TRAFFIC)}) {
- return input, httperrors.NewInputParameterError("charge type %s not supported", string(input.ChargeType))
- }
- input.VirtualResourceCreateInput, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
- if err != nil {
- return input, err
- }
- err = regionDriver.ValidateCreateEipData(ctx, userCred, &input)
- if err != nil {
- return input, err
- }
- var provider *SCloudprovider = nil
- if input.ManagerId != "" {
- providerObj, err := CloudproviderManager.FetchByIdOrName(ctx, nil, input.ManagerId)
- if err != nil {
- if err != sql.ErrNoRows {
- return input, httperrors.NewGeneralError(err)
- }
- return input, httperrors.NewResourceNotFoundError2("cloudprovider", input.ManagerId)
- }
- provider = providerObj.(*SCloudprovider)
- input.ManagerId = provider.Id
- }
- //避免参数重名后还有pending.eip残留
- eipPendingUsage := &SRegionQuota{Eip: 1}
- quotaKeys := fetchRegionalQuotaKeys(rbacscope.ScopeProject, ownerId, region, provider)
- eipPendingUsage.SetKeys(quotaKeys)
- if err = quotas.CheckSetPendingQuota(ctx, userCred, eipPendingUsage); err != nil {
- return input, err
- }
- return input, nil
- }
- func (eip *SElasticip) GetQuotaKeys() (quotas.IQuotaKeys, error) {
- region, err := eip.GetRegion()
- if region == nil {
- return nil, errors.Wrapf(err, "eip.GetRegion")
- }
- return fetchRegionalQuotaKeys(
- rbacscope.ScopeProject,
- eip.GetOwnerId(),
- region,
- eip.GetCloudprovider(),
- ), nil
- }
- func (self *SElasticip) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
- self.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
- eipPendingUsage := &SRegionQuota{Eip: 1}
- keys, err := self.GetQuotaKeys()
- if err != nil {
- log.Errorf("GetQuotaKeys fail %s", err)
- } else {
- eipPendingUsage.SetKeys(keys)
- err := quotas.CancelPendingUsage(ctx, userCred, eipPendingUsage, eipPendingUsage, true)
- if err != nil {
- log.Errorf("SElasticip CancelPendingUsage error: %s", err)
- }
- }
- self.startEipAllocateTask(ctx, userCred, data.(*jsonutils.JSONDict), "")
- }
- func (self *SElasticip) startEipAllocateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
- task, err := taskman.TaskManager.NewTask(ctx, "EipAllocateTask", self, userCred, params, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrapf(err, "NewTask")
- }
- self.SetStatus(ctx, userCred, api.EIP_STATUS_ALLOCATE, "start allocate")
- return task.ScheduleRun(nil)
- }
- func (self *SElasticip) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
- // Elasticip delete do nothing
- return nil
- }
- func (self *SElasticip) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- return self.SVirtualResourceBase.Delete(ctx, userCred)
- }
- func (self *SElasticip) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
- return self.StartEipDeallocateTask(ctx, userCred, "")
- }
- func (self *SElasticip) ValidateDeleteCondition(ctx context.Context, info *api.ElasticipDetails) error {
- if gotypes.IsNil(info) {
- if self.IsAssociated() {
- return fmt.Errorf("eip is associated with resources")
- }
- } else if len(info.AssociateName) > 0 {
- return fmt.Errorf("eip is associated with resources")
- }
- return self.SVirtualResourceBase.ValidateDeleteCondition(ctx, nil)
- }
- func (self *SElasticip) StartEipDeallocateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- task, err := taskman.TaskManager.NewTask(ctx, "EipDeallocateTask", self, userCred, nil, parentTaskId, "", nil)
- if err != nil {
- log.Errorf("newTask EipDeallocateTask fail %s", err)
- return err
- }
- self.SetStatus(ctx, userCred, api.EIP_STATUS_DEALLOCATE, "start to delete")
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SElasticip) PerformAssociate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ElasticipAssociateInput) (api.ElasticipAssociateInput, error) {
- if self.IsAssociated() {
- return input, httperrors.NewConflictError("eip has been associated with instance")
- }
- if self.Status != api.EIP_STATUS_READY {
- return input, httperrors.NewInvalidStatusError("eip cannot associate in status %s", self.Status)
- }
- if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
- return input, httperrors.NewUnsupportOperationError("fixed eip cannot be associated")
- }
- if len(input.InstanceId) == 0 {
- return input, httperrors.NewMissingParameterError("instance_id")
- }
- if len(input.InstanceType) == 0 {
- input.InstanceType = api.EIP_ASSOCIATE_TYPE_SERVER
- }
- if !utils.IsInStringArray(input.InstanceType, api.EIP_ASSOCIATE_VALID_TYPES) {
- return input, httperrors.NewUnsupportOperationError("Unsupported instance type %s", input.InstanceType)
- }
- switch input.InstanceType {
- case api.EIP_ASSOCIATE_TYPE_SERVER:
- vmObj, err := GuestManager.FetchByIdOrName(ctx, userCred, input.InstanceId)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return input, httperrors.NewResourceNotFoundError("server %s not found", input.InstanceId)
- }
- return input, httperrors.NewGeneralError(err)
- }
- server := vmObj.(*SGuest)
- lockman.LockObject(ctx, server)
- defer lockman.ReleaseObject(ctx, server)
- if server.PendingDeleted {
- return input, httperrors.NewInvalidStatusError("cannot associate pending delete server")
- }
- // IMPORTANT: this serves as a guard against a guest to have multiple
- // associated elastic_ips
- seip, _ := server.GetEipOrPublicIp()
- if seip != nil && (seip.Mode == api.EIP_MODE_STANDALONE_EIP || seip.GetProviderName() != api.CLOUD_PROVIDER_AWS) {
- return input, httperrors.NewInvalidStatusError("instance is already associated with eip")
- }
- if ok, _ := utils.InStringArray(server.Status, []string{api.VM_READY, api.VM_RUNNING}); !ok {
- return input, httperrors.NewInvalidStatusError("cannot associate server in status %s", server.Status)
- }
- err = ValidateAssociateEip(server)
- if err != nil {
- return input, err
- }
- if len(self.NetworkId) > 0 {
- gns, err := server.GetNetworks("")
- if err != nil {
- return input, httperrors.NewGeneralError(errors.Wrap(err, "GetNetworks"))
- }
- for _, gn := range gns {
- if gn.NetworkId == self.NetworkId {
- return input, httperrors.NewInputParameterError("cannot associate eip with same network")
- }
- }
- }
- serverRegion, _ := server.getRegion()
- if serverRegion == nil {
- return input, httperrors.NewInputParameterError("server region is not found???")
- }
- eipRegion, err := self.GetRegion()
- if err != nil {
- return input, httperrors.NewGeneralError(errors.Wrapf(err, "GetRegion"))
- }
- if serverRegion.Id != eipRegion.Id {
- return input, httperrors.NewInputParameterError("eip and server are not in the same region")
- }
- eipZone, _ := self.GetZone()
- if eipZone != nil {
- serverZone, _ := server.getZone()
- if serverZone.Id != eipZone.Id {
- return input, httperrors.NewInputParameterError("eip and server are not in the same zone")
- }
- }
- srvHost, _ := server.GetHost()
- if srvHost == nil {
- return input, httperrors.NewInputParameterError("server host is not found???")
- }
- if srvHost.ManagerId != self.ManagerId {
- return input, httperrors.NewInputParameterError("server and eip are not managed by the same provider")
- }
- input.InstanceExternalId = server.ExternalId
- case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
- grpObj, err := GroupManager.FetchByIdOrName(ctx, userCred, input.InstanceId)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return input, httperrors.NewResourceNotFoundError("instance group %s not found", input.InstanceId)
- }
- return input, httperrors.NewGeneralError(err)
- }
- group := grpObj.(*SGroup)
- lockman.LockObject(ctx, group)
- defer lockman.ReleaseObject(ctx, group)
- net, err := group.isEipAssociable()
- if err != nil {
- return input, errors.Wrap(err, "grp.isEipAssociable")
- }
- if net.Id == self.NetworkId {
- return input, httperrors.NewInputParameterError("cannot associate eip with same network")
- }
- eipZone, _ := self.GetZone()
- if eipZone != nil {
- insZone, _ := net.GetZone()
- if eipZone.Id != insZone.Id {
- return input, httperrors.NewInputParameterError("cannot associate eip and instance in different zone")
- }
- }
- case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
- natgwObj, err := NatGatewayManager.FetchByIdOrName(ctx, userCred, input.InstanceId)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return input, httperrors.NewResourceNotFoundError("nat gateway %s not found", input.InstanceId)
- }
- return input, httperrors.NewGeneralError(err)
- }
- natgw := natgwObj.(*SNatGateway)
- lockman.LockObject(ctx, natgw)
- defer lockman.ReleaseObject(ctx, natgw)
- case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
- obj, err := LoadbalancerManager.FetchByIdOrName(ctx, userCred, input.InstanceId)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return input, httperrors.NewResourceNotFoundError("loadbalancer %s not found", input.InstanceId)
- }
- return input, httperrors.NewGeneralError(err)
- }
- lb := obj.(*SLoadbalancer)
- if len(self.NetworkId) > 0 {
- nets, err := lb.GetNetworks()
- if err != nil {
- return input, httperrors.NewGeneralError(errors.Wrap(err, "GetNetworks"))
- }
- for _, net := range nets {
- if net.Id == self.NetworkId {
- return input, httperrors.NewInputParameterError("cannot associate eip with same network")
- }
- }
- }
- lockman.LockObject(ctx, lb)
- defer lockman.ReleaseObject(ctx, lb)
- if lb.PendingDeleted {
- return input, httperrors.NewInvalidStatusError("cannot associate with pending deleted loadbalancer")
- }
- eips, _ := lb.GetEips()
- if len(eips) > 0 {
- return input, httperrors.NewInvalidStatusError("loadbalancer is already associated with eip")
- }
- }
- return input, self.StartEipAssociateInstanceTask(ctx, userCred, input, "")
- }
- func (self *SElasticip) StartEipAssociateInstanceTask(ctx context.Context, userCred mcclient.TokenCredential, input api.ElasticipAssociateInput, parentTaskId string) error {
- params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
- return self.StartEipAssociateTask(ctx, userCred, params, parentTaskId)
- }
- func (self *SElasticip) StartEipAssociateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
- task, err := taskman.TaskManager.NewTask(ctx, "EipAssociateTask", self, userCred, params, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrapf(err, "NewTask")
- }
- self.SetStatus(ctx, userCred, api.EIP_STATUS_ASSOCIATE, "start to associate")
- return task.ScheduleRun(nil)
- }
- func (self *SElasticip) PerformDissociate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ElasticDissociateInput) (jsonutils.JSONObject, error) {
- if len(self.AssociateId) == 0 {
- return nil, nil // success
- }
- // associate with an invalid vm
- res := self.GetAssociateResource()
- if res == nil {
- return nil, self.Dissociate(ctx, userCred)
- }
- err := db.IsObjectRbacAllowed(ctx, res, userCred, policy.PolicyActionGet)
- if err != nil {
- return nil, errors.Wrap(err, "associated resource is not accessible")
- }
- if self.Status != api.EIP_STATUS_READY {
- return nil, httperrors.NewInvalidStatusError("eip cannot dissociate in status %s", self.Status)
- }
- if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
- return nil, httperrors.NewUnsupportOperationError("fixed public eip cannot be dissociated")
- }
- err = self.StartEipDissociateTask(ctx, userCred, input.AutoDelete, "")
- return nil, err
- }
- func (self *SElasticip) StartEipDissociateTask(ctx context.Context, userCred mcclient.TokenCredential, autoDelete bool, parentTaskId string) error {
- params := jsonutils.NewDict()
- if autoDelete {
- params.Add(jsonutils.JSONTrue, "auto_delete")
- }
- task, err := taskman.TaskManager.NewTask(ctx, "EipDissociateTask", self, userCred, params, parentTaskId, "", nil)
- if err != nil {
- log.Errorf("create EipDissociateTask fail %s", err)
- return nil
- }
- self.SetStatus(ctx, userCred, api.EIP_STATUS_DISSOCIATE, "start to dissociate")
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SElasticip) GetIRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) {
- provider, err := self.GetDriver(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetDriver")
- }
- region, err := self.GetRegion()
- if err != nil {
- return nil, errors.Wrapf(err, "self.GetRegion")
- }
- return provider.GetIRegionById(region.GetExternalId())
- }
- func (self *SElasticip) GetIEip(ctx context.Context) (cloudprovider.ICloudEIP, error) {
- if len(self.ExternalId) == 0 {
- return nil, errors.Wrapf(cloudprovider.ErrNotFound, "empty external id")
- }
- iregion, err := self.GetIRegion(ctx)
- if err != nil {
- return nil, err
- }
- return iregion.GetIEipById(self.GetExternalId())
- }
- // 同步弹性公网IP状态
- func (self *SElasticip) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ElasticipSyncstatusInput) (jsonutils.JSONObject, error) {
- if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
- return nil, httperrors.NewUnsupportOperationError("fixed eip cannot sync status")
- }
- if self.IsManaged() {
- return nil, StartResourceSyncStatusTask(ctx, userCred, self, "EipSyncstatusTask", "")
- } else {
- return nil, self.SetStatus(ctx, userCred, api.EIP_STATUS_READY, "eip sync status")
- }
- }
- func (self *SElasticip) PerformSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
- return nil, httperrors.NewUnsupportOperationError("fixed eip cannot sync status")
- }
- if self.IsManaged() {
- return nil, StartResourceSyncStatusTask(ctx, userCred, self, "EipSyncstatusTask", "")
- }
- return nil, nil
- }
- func (manager *SElasticipManager) FetchCustomizeColumns(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- objs []interface{},
- fields stringutils2.SSortedStrings,
- isList bool,
- ) []api.ElasticipDetails {
- rows := make([]api.ElasticipDetails, len(objs))
- virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- lbIds := []string{}
- guestIds := []string{}
- natIds := []string{}
- groupIds := []string{}
- for i := range rows {
- rows[i] = api.ElasticipDetails{
- VirtualResourceDetails: virtRows[i],
- ManagedResourceInfo: managerRows[i],
- CloudregionResourceInfo: regionRows[i],
- }
- eip := objs[i].(*SElasticip)
- if len(eip.AssociateId) > 0 {
- switch eip.AssociateType {
- case api.EIP_ASSOCIATE_TYPE_SERVER:
- guestIds = append(guestIds, eip.AssociateId)
- case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
- lbIds = append(lbIds, eip.AssociateId)
- case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
- natIds = append(natIds, eip.AssociateId)
- case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
- groupIds = append(groupIds, eip.AssociateId)
- }
- }
- }
- guests, err := db.FetchIdNameMap2(GuestManager, guestIds)
- if err != nil {
- log.Errorf("FetchIdNameMap2 guests")
- return rows
- }
- lbs, err := db.FetchIdNameMap2(LoadbalancerManager, lbIds)
- if err != nil {
- log.Errorf("FetchIdNameMap2 loadbalancer")
- return rows
- }
- nats, err := db.FetchIdNameMap2(NatGatewayManager, natIds)
- if err != nil {
- log.Errorf("FetchIdNameMap2 natgateway")
- return rows
- }
- groups, err := db.FetchIdNameMap2(GroupManager, groupIds)
- if err != nil {
- log.Errorf("FetchIdNameMap2 group")
- return rows
- }
- gns := []SGuestnetwork{}
- err = GuestnetworkManager.Query().In("guest_id", guestIds).All(&gns)
- if err != nil {
- log.Errorf("FetchModelObjects GuestnetworkManager")
- return rows
- }
- gnMap := map[string]string{}
- for i := range gns {
- gnMap[gns[i].GuestId] = gns[i].IpAddr
- }
- for i := range rows {
- eip := objs[i].(*SElasticip)
- if len(eip.AssociateId) > 0 {
- switch eip.AssociateType {
- case api.EIP_ASSOCIATE_TYPE_SERVER:
- rows[i].AssociateName, _ = guests[eip.AssociateId]
- rows[i].ServerPrivateIp = gnMap[eip.AssociateId]
- case api.EIP_ASSOCIATE_TYPE_LOADBALANCER:
- rows[i].AssociateName, _ = lbs[eip.AssociateId]
- case api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY:
- rows[i].AssociateName, _ = nats[eip.AssociateId]
- case api.EIP_ASSOCIATE_TYPE_INSTANCE_GROUP:
- rows[i].AssociateName, _ = groups[eip.AssociateId]
- }
- }
- }
- return rows
- }
- type SEipNetwork struct {
- owner mcclient.IIdentityProvider
- user mcclient.TokenCredential
- freeCnt int
- net *SNetwork
- }
- func NewEipNetwork(net *SNetwork, owner mcclient.IIdentityProvider, user mcclient.TokenCredential, freecnt int) SEipNetwork {
- return SEipNetwork{
- owner: owner,
- user: user,
- freeCnt: freecnt,
- net: net,
- }
- }
- func (en SEipNetwork) isOwnerProject() bool {
- return en.owner.GetProjectId() == en.net.ProjectId
- }
- func (en SEipNetwork) isOwnerProjectDomain() bool {
- return en.owner.GetProjectDomainId() == en.net.DomainId
- }
- func (en SEipNetwork) isUserProject() bool {
- return en.user.GetProjectId() == en.net.ProjectId
- }
- func (en SEipNetwork) isUserProjectDomain() bool {
- return en.user.GetProjectDomainId() == en.net.DomainId
- }
- func (en SEipNetwork) GetNetwork() *SNetwork {
- return en.net
- }
- type SEipNetworks []SEipNetwork
- func (a SEipNetworks) Len() int {
- return len(a)
- }
- func (a SEipNetworks) Swap(i, j int) {
- a[i], a[j] = a[j], a[i]
- }
- func (a SEipNetworks) Less(i, j int) bool {
- if a[i].isOwnerProject() && !a[j].isOwnerProject() {
- return true
- } else if !a[i].isOwnerProject() && a[j].isOwnerProject() {
- return false
- }
- if a[i].isOwnerProjectDomain() && !a[j].isOwnerProjectDomain() {
- return true
- } else if !a[i].isOwnerProjectDomain() && a[j].isOwnerProjectDomain() {
- return false
- }
- if a[i].isUserProject() && !a[j].isUserProject() {
- return true
- } else if !a[i].isUserProject() && a[j].isUserProject() {
- return false
- }
- if a[i].isUserProjectDomain() && !a[j].isUserProjectDomain() {
- return true
- } else if !a[i].isUserProjectDomain() && a[j].isUserProjectDomain() {
- return false
- }
- if a[i].freeCnt > a[j].freeCnt {
- return true
- } else if a[i].freeCnt < a[j].freeCnt {
- return false
- }
- if a[i].net.Id < a[j].net.Id {
- return true
- }
- return false
- }
- type NewEipForVMOnHostArgs struct {
- Bandwidth int
- BgpType string
- ChargeType billing_api.TNetChargeType
- AutoDellocate bool
- Group *SGroup
- Guest *SGuest
- Host *SHost
- Natgateway *SNatGateway
- Loadbalancer *SLoadbalancer
- PendingUsage quotas.IQuota
- }
- func (manager *SElasticipManager) NewEipForVMOnHost(ctx context.Context, userCred mcclient.TokenCredential, args *NewEipForVMOnHostArgs) (*SElasticip, error) {
- var (
- bw = args.Bandwidth
- bgpType = args.BgpType
- chargeType = args.ChargeType
- autoDellocate = args.AutoDellocate
- grp = args.Group
- vm = args.Guest
- host = args.Host
- nat = args.Natgateway
- lb = args.Loadbalancer
- pendingUsage = args.PendingUsage
- region *SCloudregion = nil
- )
- if host != nil {
- region, _ = host.GetRegion()
- } else if lb != nil {
- region, _ = lb.GetRegion()
- } else if nat != nil {
- region, _ = nat.GetRegion()
- } else if grp != nil {
- net, _ := grp.getAttachedNetwork()
- region, _ = net.GetRegion()
- } else {
- return nil, fmt.Errorf("invalid host or nat")
- }
- regionDriver := region.GetDriver()
- if len(chargeType) == 0 {
- chargeType = billing_api.TNetChargeType(regionDriver.GetEipDefaultChargeType())
- }
- if err := regionDriver.ValidateEipChargeType(chargeType); err != nil {
- return nil, err
- }
- eip := &SElasticip{}
- eip.SetModelManager(manager, eip)
- eip.Mode = api.EIP_MODE_STANDALONE_EIP
- // do not implicitly auto dellocate EIP, should be set by user explicitly
- // eip.AutoDellocate = tristate.True
- eip.Bandwidth = bw
- eip.ChargeType = billing_api.TNetChargeType(chargeType)
- eip.BgpType = args.BgpType
- eip.AutoDellocate = tristate.NewFromBool(autoDellocate)
- ownerCred := userCred.(mcclient.IIdentityProvider)
- if vm != nil {
- ownerCred = vm.GetOwnerId()
- } else if nat != nil {
- ownerCred = nat.GetOwnerId()
- } else if lb != nil {
- ownerCred = lb.GetOwnerId()
- } else if grp != nil {
- ownerCred = grp.GetOwnerId()
- } else {
- panic("unsupported associate type")
- }
- eip.DomainId = ownerCred.GetProjectDomainId()
- eip.ProjectId = ownerCred.GetProjectId()
- eip.ProjectSrc = string(apis.OWNER_SOURCE_LOCAL)
- if host != nil {
- eip.ManagerId = host.ManagerId
- } else if nat != nil {
- vpc, err := nat.GetVpc()
- if err != nil {
- return nil, errors.Wrapf(err, "nat.GetVpc")
- }
- eip.ManagerId = vpc.ManagerId
- } else if lb != nil {
- vpc, err := lb.GetVpc()
- if err != nil {
- return nil, errors.Wrapf(err, "nat.GetVpc")
- }
- eip.ManagerId = vpc.ManagerId
- } else if grp != nil {
- } else {
- panic("unsupported associate type")
- }
- eip.CloudregionId = region.Id
- if vm != nil {
- eip.Name = fmt.Sprintf("eip-for-srv-%s", pinyinutils.Text2Pinyin(vm.GetName()))
- } else if nat != nil {
- eip.Name = fmt.Sprintf("eip-for-nat-%s", pinyinutils.Text2Pinyin(nat.GetName()))
- } else if grp != nil {
- eip.Name = fmt.Sprintf("eip-for-grp-%s", pinyinutils.Text2Pinyin(grp.GetName()))
- } else if lb != nil {
- eip.Name = fmt.Sprintf("eip-for-lb-%s", pinyinutils.Text2Pinyin(lb.GetName()))
- } else {
- panic("unsupported associate type")
- }
- if (host != nil && host.ManagerId == "") || grp != nil || lb != nil { // kvm
- q := NetworkManager.Query()
- var zoneId string
- if host != nil {
- zoneId = host.ZoneId
- } else if grp != nil {
- net, _ := grp.getAttachedNetwork()
- zone, _ := net.GetZone()
- zoneId = zone.Id
- } else if lb != nil {
- zone, _ := lb.GetZone()
- zoneId = zone.Id
- }
- wireq := WireManager.Query().SubQuery()
- scope, _ := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), NetworkManager.KeywordPlural(), policy.PolicyActionList)
- q = NetworkManager.FilterByOwner(ctx, q, NetworkManager, userCred, userCred, scope)
- q = q.Join(wireq, sqlchemy.Equals(wireq.Field("id"), q.Field("wire_id"))).
- Filter(sqlchemy.Equals(wireq.Field("zone_id"), zoneId))
- q = q.Equals("server_type", api.NETWORK_TYPE_EIP)
- q = q.Equals("bgp_type", bgpType)
- var nets []SNetwork
- if err := db.FetchModelObjects(NetworkManager, q, &nets); err != nil {
- return nil, errors.Wrapf(err, "fetch eip networks usable in host %s(%s)",
- host.Name, host.Id)
- }
- eipNets := make([]SEipNetwork, 0)
- for i := range nets {
- net := &nets[i]
- cnt, _ := net.GetFreeAddressCount()
- if cnt > 0 {
- eipNets = append(eipNets, NewEipNetwork(net, ownerCred, userCred, cnt))
- }
- }
- if len(eipNets) == 0 {
- return nil, httperrors.NewNotFoundError("no usable eip network")
- }
- // prefer networks with identical project, domain, more free address, Id
- sort.Sort(SEipNetworks(eipNets))
- log.Debugf("eipnets: %s", jsonutils.Marshal(eipNets))
- eip.NetworkId = eipNets[0].GetNetwork().Id
- }
- var err = func() error {
- lockman.LockRawObject(ctx, manager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
- var err error
- eip.Name, err = db.GenerateName(ctx, manager, userCred, eip.Name)
- if err != nil {
- return errors.Wrap(err, "db.GenerateName")
- }
- return manager.TableSpec().Insert(ctx, eip)
- }()
- if err != nil {
- return nil, errors.Wrapf(err, "TableSpec().Insert")
- }
- db.OpsLog.LogEvent(eip, db.ACT_CREATE, eip.GetShortDesc(ctx), userCred)
- var ownerId mcclient.IIdentityProvider = nil
- if vm != nil {
- ownerId = vm.GetOwnerId()
- } else if nat != nil {
- ownerId = nat.GetOwnerId()
- } else if grp != nil {
- ownerId = grp.GetOwnerId()
- } else if lb != nil {
- ownerId = lb.GetOwnerId()
- } else {
- panic("unsupported associate type")
- }
- var provider *SCloudprovider = nil
- if host != nil {
- provider = host.GetCloudprovider()
- } else if nat != nil {
- provider = nat.GetCloudprovider()
- } else if lb != nil {
- provider = lb.GetCloudprovider()
- } else if grp != nil {
- } else {
- panic("unsupported associate type")
- }
- eipPendingUsage := &SRegionQuota{Eip: 1}
- keys := fetchRegionalQuotaKeys(
- rbacscope.ScopeProject,
- ownerId,
- region,
- provider,
- )
- eipPendingUsage.SetKeys(keys)
- quotas.CancelPendingUsage(ctx, userCred, pendingUsage, eipPendingUsage, true)
- return eip, nil
- }
- func (eip *SElasticip) AllocateAndAssociateInstance(ctx context.Context, userCred mcclient.TokenCredential, ins IEipAssociateInstance, input api.ElasticipAssociateInput, parentTaskId string) error {
- err := ValidateAssociateEip(ins)
- if err != nil {
- return err
- }
- params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
- db.StatusBaseSetStatus(ctx, ins, userCred, api.INSTANCE_ASSOCIATE_EIP, "allocate and associate EIP")
- return eip.startEipAllocateTask(ctx, userCred, params, parentTaskId)
- }
- func (self *SElasticip) PerformChangeBandwidth(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if self.Status != api.EIP_STATUS_READY {
- return nil, httperrors.NewInvalidStatusError("cannot change bandwidth in status %s", self.Status)
- }
- bandwidth, err := data.Int("bandwidth")
- if err != nil || bandwidth <= 0 {
- return nil, httperrors.NewInputParameterError("Invalid bandwidth")
- }
- if self.IsManaged() {
- factory, err := self.GetProviderFactory()
- if err != nil {
- return nil, err
- }
- if err := factory.ValidateChangeBandwidth(self.AssociateId, bandwidth); err != nil {
- return nil, httperrors.NewInputParameterError("%v", err)
- }
- }
- err = self.StartEipChangeBandwidthTask(ctx, userCred, bandwidth)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- return nil, nil
- }
- func (self *SElasticip) StartEipChangeBandwidthTask(ctx context.Context, userCred mcclient.TokenCredential, bandwidth int64) error {
- self.SetStatus(ctx, userCred, api.EIP_STATUS_CHANGE_BANDWIDTH, "change bandwidth")
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewInt(bandwidth), "bandwidth")
- task, err := taskman.TaskManager.NewTask(ctx, "EipChangeBandwidthTask", self, userCred, params, "", "", nil)
- if err != nil {
- log.Errorf("create EipChangeBandwidthTask fail %s", err)
- return err
- }
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SElasticip) DoChangeBandwidth(ctx context.Context, userCred mcclient.TokenCredential, bandwidth int) error {
- changes := jsonutils.NewDict()
- changes.Add(jsonutils.NewInt(int64(self.Bandwidth)), "obw")
- _, err := db.Update(self, func() error {
- self.Bandwidth = bandwidth
- return nil
- })
- self.SetStatus(ctx, userCred, api.EIP_STATUS_READY, "finish change bandwidth")
- if err != nil {
- log.Errorf("DoChangeBandwidth update fail %s", err)
- return err
- }
- changes.Add(jsonutils.NewInt(int64(bandwidth)), "nbw")
- db.OpsLog.LogEvent(self, db.ACT_CHANGE_BANDWIDTH, changes, userCred)
- return nil
- }
- type EipUsage struct {
- PublicIPCount int
- PublicIpBandwidth int
- EIPCount int
- EipBandwidth int
- EIPUsedCount int
- }
- func (u EipUsage) Total() int {
- return u.PublicIPCount + u.EIPCount
- }
- func (manager *SElasticipManager) usageQByCloudEnv(q *sqlchemy.SQuery, providers []string, brands []string, cloudEnv string) *sqlchemy.SQuery {
- return CloudProviderFilter(q, q.Field("manager_id"), providers, brands, cloudEnv)
- }
- func (manager *SElasticipManager) usageQByRanges(q *sqlchemy.SQuery, rangeObjs []db.IStandaloneModel) *sqlchemy.SQuery {
- return RangeObjectsFilter(q, rangeObjs, q.Field("cloudregion_id"), nil, q.Field("manager_id"), nil, nil)
- }
- func (manager *SElasticipManager) usageQ(
- ctx context.Context,
- scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, q *sqlchemy.SQuery, rangeObjs []db.IStandaloneModel, providers []string, brands []string, cloudEnv string, policyResult rbacutils.SPolicyResult) *sqlchemy.SQuery {
- q = manager.usageQByRanges(q, rangeObjs)
- q = manager.usageQByCloudEnv(q, providers, brands, cloudEnv)
- switch scope {
- case rbacscope.ScopeSystem:
- // do nothing
- case rbacscope.ScopeDomain:
- q = q.Equals("domain_id", ownerId.GetProjectDomainId())
- case rbacscope.ScopeProject:
- q = q.Equals("tenant_id", ownerId.GetProjectId())
- }
- q = db.ObjectIdQueryWithPolicyResult(ctx, q, manager, policyResult)
- return q
- }
- func (manager *SElasticipManager) TotalCount(
- ctx context.Context,
- scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObjs []db.IStandaloneModel, providers []string, brands []string, cloudEnv string, policyResult rbacutils.SPolicyResult) EipUsage {
- usage := EipUsage{}
- q1sq := manager.Query().SubQuery()
- q1 := q1sq.Query(
- sqlchemy.COUNT("public_ip_count", q1sq.Field("id")),
- sqlchemy.SUM("public_ip_bandwidth", q1sq.Field("bandwidth")),
- ).Equals("mode", api.EIP_MODE_INSTANCE_PUBLICIP)
- q1 = manager.usageQ(ctx, scope, ownerId, q1, rangeObjs, providers, brands, cloudEnv, policyResult)
- q2sq := manager.Query().SubQuery()
- q2 := q2sq.Query(
- sqlchemy.COUNT("eip_count", q2sq.Field("id")),
- sqlchemy.SUM("eip_bandwidth", q2sq.Field("bandwidth")),
- ).Equals("mode", api.EIP_MODE_STANDALONE_EIP)
- q2 = manager.usageQ(ctx, scope, ownerId, q2, rangeObjs, providers, brands, cloudEnv, policyResult)
- q3sq := manager.Query().SubQuery()
- q3 := q3sq.Query(
- sqlchemy.COUNT("eip_used_count", q3sq.Field("id")),
- ).Equals("mode", api.EIP_MODE_STANDALONE_EIP).IsNotEmpty("associate_type")
- q3 = manager.usageQ(ctx, scope, ownerId, q3, rangeObjs, providers, brands, cloudEnv, policyResult)
- err := q1.First(&usage)
- if err != nil {
- log.Errorf("q.First error: %v", err)
- }
- err = q2.First(&usage)
- if err != nil {
- log.Errorf("q.First error: %v", err)
- }
- err = q3.First(&usage)
- if err != nil {
- log.Errorf("q.First error: %v", err)
- }
- return usage
- }
- func (self *SElasticip) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- err := self.ValidateDeleteCondition(ctx, nil)
- if err != nil {
- return nil, err
- }
- provider := self.GetCloudprovider()
- if provider != nil {
- if provider.GetEnabled() {
- return nil, httperrors.NewInvalidStatusError("Cannot purge elastic_ip on enabled cloud provider")
- }
- }
- err = self.RealDelete(ctx, userCred)
- return nil, err
- }
- func (self *SElasticip) DoPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) {
- if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP {
- self.SVirtualResourceBase.DoPendingDelete(ctx, userCred)
- return
- }
- self.Dissociate(ctx, userCred)
- }
- func (self *SElasticip) getCloudProviderInfo() SCloudProviderInfo {
- region, _ := self.GetRegion()
- provider := self.GetCloudprovider()
- return MakeCloudProviderInfo(region, nil, provider)
- }
- func (eip *SElasticip) GetUsages() []db.IUsage {
- if eip.PendingDeleted || eip.Deleted {
- return nil
- }
- usage := SRegionQuota{Eip: 1}
- keys, err := eip.GetQuotaKeys()
- if err != nil {
- log.Errorf("disk.GetQuotaKeys fail %s", err)
- return nil
- }
- usage.SetKeys(keys)
- return []db.IUsage{
- &usage,
- }
- }
- func (self *SElasticip) PerformRemoteUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ElasticipRemoteUpdateInput) (jsonutils.JSONObject, error) {
- err := self.StartRemoteUpdateTask(ctx, userCred, (input.ReplaceTags != nil && *input.ReplaceTags), "")
- if err != nil {
- return nil, errors.Wrap(err, "StartRemoteUpdateTask")
- }
- return nil, nil
- }
- func (self *SElasticip) StartRemoteUpdateTask(ctx context.Context, userCred mcclient.TokenCredential, replaceTags bool, parentTaskId string) error {
- data := jsonutils.NewDict()
- data.Add(jsonutils.NewBool(replaceTags), "replace_tags")
- task, err := taskman.TaskManager.NewTask(ctx, "EipRemoteUpdateTask", self, userCred, data, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- self.SetStatus(ctx, userCred, apis.STATUS_UPDATE_TAGS, "StartRemoteUpdateTask")
- return task.ScheduleRun(nil)
- }
- func (self *SElasticip) OnMetadataUpdated(ctx context.Context, userCred mcclient.TokenCredential) {
- if len(self.ExternalId) == 0 || options.Options.KeepTagLocalization {
- return
- }
- if account := self.GetCloudaccount(); account != nil && account.ReadOnly {
- return
- }
- err := self.StartRemoteUpdateTask(ctx, userCred, true, "")
- if err != nil {
- log.Errorf("StartRemoteUpdateTask fail: %s", err)
- }
- }
- func (manager *SElasticipManager) ListItemExportKeys(ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- keys stringutils2.SSortedStrings,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemExportKeys")
- }
- if keys.ContainsAny(manager.SManagedResourceBaseManager.GetExportKeys()...) {
- q, err = manager.SManagedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemExportKeys")
- }
- }
- return q, nil
- }
|