diff --git a/cmd/climc/shell/natdtables.go b/cmd/climc/shell/natdtables.go new file mode 100644 index 0000000000..a1053746dd --- /dev/null +++ b/cmd/climc/shell/natdtables.go @@ -0,0 +1,37 @@ +// 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 shell + +import ( + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +func init() { + + R(&options.NatDTableListOptions{}, "natdtable-list", "List DNAT tables", func(s *mcclient.ClientSession, opts *options.NatDTableListOptions) error { + params, err := options.ListStructToParams(opts) + if err != nil { + return err + } + result, err := modules.NatDTables.List(s, params) + if err != nil { + return err + } + printList(result, modules.NatDTables.GetColumns(s)) + return nil + }) +} diff --git a/cmd/climc/shell/natgateways.go b/cmd/climc/shell/natgateways.go new file mode 100644 index 0000000000..221cc35a5c --- /dev/null +++ b/cmd/climc/shell/natgateways.go @@ -0,0 +1,37 @@ +// 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 shell + +import ( + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +func init() { + + R(&options.NatGatewayListOptions{}, "natgateway-list", "List NAT gateways", func(s *mcclient.ClientSession, opts *options.NatGatewayListOptions) error { + params, err := options.ListStructToParams(opts) + if err != nil { + return err + } + result, err := modules.NatGateways.List(s, params) + if err != nil { + return err + } + printList(result, modules.NatGateways.GetColumns(s)) + return nil + }) +} diff --git a/cmd/climc/shell/natstables.go b/cmd/climc/shell/natstables.go new file mode 100644 index 0000000000..7a4e7058b7 --- /dev/null +++ b/cmd/climc/shell/natstables.go @@ -0,0 +1,37 @@ +// 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 shell + +import ( + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +func init() { + + R(&options.NatSTableListOptions{}, "natstable-list", "List SNAT tables", func(s *mcclient.ClientSession, opts *options.NatSTableListOptions) error { + params, err := options.ListStructToParams(opts) + if err != nil { + return err + } + result, err := modules.NatSTables.List(s, params) + if err != nil { + return err + } + printList(result, modules.NatSTables.GetColumns(s)) + return nil + }) +} diff --git a/pkg/apis/compute/elasticips_const.go b/pkg/apis/compute/elasticips_const.go index 7ea708843a..841c88a7e6 100644 --- a/pkg/apis/compute/elasticips_const.go +++ b/pkg/apis/compute/elasticips_const.go @@ -18,7 +18,8 @@ const ( EIP_MODE_INSTANCE_PUBLICIP = "public_ip" EIP_MODE_STANDALONE_EIP = "elastic_ip" - EIP_ASSOCIATE_TYPE_SERVER = "server" + EIP_ASSOCIATE_TYPE_SERVER = "server" + EIP_ASSOCIATE_TYPE_NAT_GATEWAY = "natgateway" EIP_STATUS_READY = "ready" EIP_STATUS_UNKNOWN = "unknown" diff --git a/pkg/apis/compute/natgateway.go b/pkg/apis/compute/natgateway.go new file mode 100644 index 0000000000..99aedb3fd5 --- /dev/null +++ b/pkg/apis/compute/natgateway.go @@ -0,0 +1,12 @@ +package compute + +const ( + NAT_STAUTS_AVAILABLE = "available" //可用 + NAT_STATUS_ALLOCATE = "allocate" //创建中 + NAT_STATUS_DEPLOYING = "deploying" //配置中 + NAT_STATUS_UNKNOWN = "unknown" + + QCLOUD_NAT_SPEC_SMALL = "Small" + QCLOUD_NAT_SPEC_MIDDLE = "Middle" + QCLOUD_NAT_SPEC_LARGE = "Large" +) diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 6864517843..4f4ae9ecc2 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -393,6 +393,7 @@ type ICloudVpc interface { Delete() error GetIWireById(wireId string) (ICloudWire, error) + GetINatGateways() ([]ICloudNatGateway, error) } type ICloudWire interface { @@ -615,3 +616,33 @@ type ICloudSku interface { type ICloudProject interface { ICloudResource } + +type ICloudNatGateway interface { + ICloudResource + IBillingResource + + // 获取 NAT 规格 + GetNatSpec() string + GetIEips() ([]ICloudEIP, error) + GetINatDTables() ([]ICloudNatDTable, error) + GetINatSTables() ([]ICloudNatSTable, error) +} + +type ICloudNatDTable interface { + ICloudResource + + GetIpProtocol() string + GetExternalIp() string + GetExternalPort() int + + GetInternalIp() string + GetInternalPort() int +} + +type ICloudNatSTable interface { + ICloudResource + + GetIP() string + GetSourceCIDR() string + GetNetworkId() string +} diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index b15f2be75d..ad78b7710c 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -998,6 +998,7 @@ func (self *SCloudprovider) RealDelete(ctx context.Context, userCred mcclient.To LoadbalancerManager, LoadbalancerAclManager, LoadbalancerCertificateManager, + NatGatewayManager, VpcManager, ElasticipManager, CloudproviderRegionManager, diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index d42499fcd0..8687903a14 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -198,7 +198,7 @@ func syncRegionVPCs(ctx context.Context, userCred mcclient.TokenCredential, sync syncVpcWires(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange) syncVpcSecGroup(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange) syncVpcRouteTables(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange) - + syncVpcNatgateways(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange) }() } } @@ -234,6 +234,84 @@ func syncVpcRouteTables(ctx context.Context, userCred mcclient.TokenCredential, } } +func syncVpcNatgateways(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localVpc *SVpc, remoteVpc cloudprovider.ICloudVpc, syncRange *SSyncRange) { + natGateways, err := remoteVpc.GetINatGateways() + if err != nil { + msg := fmt.Sprintf("GetINatGateways for vpc %s failed %s", remoteVpc.GetId(), err) + log.Errorf(msg) + return + } + localNatGateways, remoteNatGateways, result := NatGatewayManager.SyncNatGateways(ctx, userCred, provider, localVpc, natGateways) + + syncResults.Add(NatGatewayManager, result) + + msg := result.Result() + notes := fmt.Sprintf("SyncNatGateways for VPC %s result: %s", localVpc.Name, msg) + log.Infof(notes) + if result.IsError() { + return + } + + for i := 0; i < len(localNatGateways); i++ { + func() { + lockman.LockObject(ctx, &localNatGateways[i]) + defer lockman.ReleaseObject(ctx, &localNatGateways[i]) + + syncNatGatewayEips(ctx, userCred, provider, &localNatGateways[i], remoteNatGateways[i]) + syncNatDtables(ctx, userCred, provider, &localNatGateways[i], remoteNatGateways[i]) + syncNatStables(ctx, userCred, provider, &localNatGateways[i], remoteNatGateways[i]) + }() + } +} + +func syncNatGatewayEips(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, localNatGateway *SNatGateway, remoteNatGateway cloudprovider.ICloudNatGateway) { + eips, err := remoteNatGateway.GetIEips() + if err != nil { + msg := fmt.Sprintf("GetIEIPs for NatGateway %s failed %s", remoteNatGateway.GetName(), err) + log.Errorf(msg) + return + } + result := localNatGateway.SyncNatGatewayEips(ctx, userCred, provider, eips) + msg := result.Result() + log.Infof("SyncNatGatewayEips for NatGateway %s result: %s", localNatGateway.Name, msg) + if result.IsError() { + return + } + db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, userCred) +} + +func syncNatDtables(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, localNatGateway *SNatGateway, remoteNatGateway cloudprovider.ICloudNatGateway) { + dtables, err := remoteNatGateway.GetINatDTables() + if err != nil { + msg := fmt.Sprintf("GetINatDTables for NatGateway %s failed %s", remoteNatGateway.GetName(), err) + log.Errorf(msg) + return + } + result := NatDTableManager.SyncNatDTables(ctx, userCred, provider.GetOwnerId(), provider, localNatGateway, dtables) + msg := result.Result() + log.Infof("SyncNatDTables for NatGateway %s result: %s", localNatGateway.Name, msg) + if result.IsError() { + return + } + db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, userCred) +} + +func syncNatStables(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, localNatGateway *SNatGateway, remoteNatGateway cloudprovider.ICloudNatGateway) { + stables, err := remoteNatGateway.GetINatSTables() + if err != nil { + msg := fmt.Sprintf("GetINatSTables for NatGateway %s failed %s", remoteNatGateway.GetName(), err) + log.Errorf(msg) + return + } + result := NatSTableManager.SyncNatSTables(ctx, userCred, provider.GetOwnerId(), provider, localNatGateway, stables) + msg := result.Result() + log.Infof("SyncNatSTables for NatGateway %s result: %s", localNatGateway.Name, msg) + if result.IsError() { + return + } + db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, userCred) +} + func syncVpcWires(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localVpc *SVpc, remoteVpc cloudprovider.ICloudVpc, syncRange *SSyncRange) { wires, err := remoteVpc.GetIWires() if err != nil { diff --git a/pkg/compute/models/elasticips.go b/pkg/compute/models/elasticips.go index 76de6c570d..38bb859617 100644 --- a/pkg/compute/models/elasticips.go +++ b/pkg/compute/models/elasticips.go @@ -439,24 +439,49 @@ func (self *SElasticip) IsAssociated() bool { if self.GetAssociateVM() != nil { return true } + if self.GetAssociateNatGateway() != nil { + return true + } return false } func (self *SElasticip) GetAssociateVM() *SGuest { - if self.AssociateType == "server" && len(self.AssociateId) > 0 { + if self.AssociateType == api.EIP_ASSOCIATE_TYPE_SERVER && len(self.AssociateId) > 0 { return GuestManager.FetchGuestById(self.AssociateId) } 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) Dissociate(ctx context.Context, userCred mcclient.TokenCredential) error { if len(self.AssociateType) == 0 { return nil } - vm := self.GetAssociateVM() - if vm == nil { - log.Errorf("dissociate VM not exists???") + var vm *SGuest + var nat *SNatGateway + 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???") + } } + _, err := db.Update(self, func() error { self.AssociateId = "" self.AssociateType = "" @@ -470,6 +495,13 @@ func (self *SElasticip) Dissociate(ctx context.Context, userCred mcclient.TokenC 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 self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP { self.Delete(ctx, userCred) } @@ -484,7 +516,7 @@ func (self *SElasticip) AssociateVM(ctx context.Context, userCred mcclient.Token return fmt.Errorf("EIP has been associated!!") } _, err := db.Update(self, func() error { - self.AssociateType = "server" + self.AssociateType = api.EIP_ASSOCIATE_TYPE_SERVER self.AssociateId = vm.Id return nil }) @@ -499,6 +531,29 @@ func (self *SElasticip) AssociateVM(ctx context.Context, userCred mcclient.Token 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.AssociateType) > 0 { + return fmt.Errorf("Eip has been associated!!") + } + _, 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) { eipObj, err := db.FetchByExternalId(manager, extEip.GetGlobalId()) if err == nil { diff --git a/pkg/compute/models/natdtables.go b/pkg/compute/models/natdtables.go new file mode 100644 index 0000000000..8ac6de00fd --- /dev/null +++ b/pkg/compute/models/natdtables.go @@ -0,0 +1,250 @@ +// 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" + + "yunion.io/x/log" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/pkg/util/compare" + "yunion.io/x/sqlchemy" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" + "yunion.io/x/onecloud/pkg/cloudcommon/validators" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SNatDTableManager struct { + db.SStatusStandaloneResourceBaseManager +} + +var NatDTableManager *SNatDTableManager + +func init() { + NatDTableManager = &SNatDTableManager{ + SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager( + SNatDTable{}, + "natdtables_tbl", + "natdtable", + "natdtables", + ), + } +} + +type SNatDTable struct { + db.SStatusStandaloneResourceBase + db.SExternalizedResourceBase + + ExternalIP string `width:"17" charset:"ascii" list:"user" create:"required"` + ExternalPort int `list:"user" create:"required"` + + InternalIP string `width:"17" charset:"ascii" list:"user" create:"required"` + InternalPort int `list:"user" create:"required"` + IpProtocol string `width:"8" charset:"ascii" list:"user" create:"required"` + + NatgatewayId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` +} + +func (manager *SNatDTableManager) GetContextManagers() [][]db.IModelManager { + return [][]db.IModelManager{ + {NatGatewayManager}, + } +} + +func (self *SNatDTableManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsAdminAllowList(userCred, self) +} + +func (self *SNatDTableManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowCreate(userCred, self) +} + +func (self *SNatDTable) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsAdminAllowGet(userCred, self) +} + +func (self *SNatDTable) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool { + return db.IsAdminAllowUpdate(userCred, self) +} + +func (self *SNatDTable) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowDelete(userCred, self) +} + +func (self *SNatDTable) GetNatgateway() (*SNatGateway, error) { + _natgateway, err := NatGatewayManager.FetchById(self.NatgatewayId) + if err != nil { + return nil, err + } + return _natgateway.(*SNatGateway), nil +} + +func (man *SNatDTableManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { + q, err := man.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query) + if err != nil { + return nil, err + } + data := query.(*jsonutils.JSONDict) + q, err = validators.ApplyModelFilters(q, data, []*validators.ModelFilterOptions{ + {Key: "natgateway", ModelKeyword: "natgateway", OwnerId: userCred}, + }) + if err != nil { + return nil, err + } + + q, err = managedResourceFilterByAccount(q, query, "natgateway_id", func() *sqlchemy.SQuery { + natgateways := NatGatewayManager.Query().SubQuery() + return natgateways.Query(natgateways.Field("id")) + }) + + return q, nil +} + +func (man *SNatDTableManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("Not Implemented") +} + +func (manager *SNatDTableManager) SyncNatDTables(ctx context.Context, userCred mcclient.TokenCredential, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider, nat *SNatGateway, extDtables []cloudprovider.ICloudNatDTable) compare.SyncResult { + lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId)) + defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId)) + + result := compare.SyncResult{} + dbNatDTables, err := nat.GetDTables() + if err != nil { + result.Error(err) + return result + } + + removed := make([]SNatDTable, 0) + commondb := make([]SNatDTable, 0) + commonext := make([]cloudprovider.ICloudNatDTable, 0) + added := make([]cloudprovider.ICloudNatDTable, 0) + if err := compare.CompareSets(dbNatDTables, extDtables, &removed, &commondb, &commonext, &added); err != nil { + result.Error(err) + return result + } + + for i := 0; i < len(removed); i += 1 { + err := removed[i].syncRemoveCloudNatDTable(ctx, userCred) + if err != nil { + result.DeleteError(err) + } else { + result.Delete() + } + } + + for i := 0; i < len(commondb); i += 1 { + err := commondb[i].SyncWithCloudNatDTable(ctx, userCred, commonext[i]) + if err != nil { + result.UpdateError(err) + continue + } + syncMetadata(ctx, userCred, &commondb[i], commonext[i]) + result.Update() + } + + for i := 0; i < len(added); i += 1 { + routeTableNew, err := manager.newFromCloudNatDTable(ctx, userCred, nat, added[i]) + if err != nil { + result.AddError(err) + continue + } + syncMetadata(ctx, userCred, routeTableNew, added[i]) + result.Add() + } + return result +} + +func (self *SNatDTable) syncRemoveCloudNatDTable(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, self) + defer lockman.ReleaseObject(ctx, self) + + err := self.ValidateDeleteCondition(ctx) + if err != nil { // cannot delete + return self.SetStatus(userCred, api.VPC_STATUS_UNKNOWN, "sync to delete") + } + return self.Delete(ctx, userCred) +} + +func (self *SNatDTable) SyncWithCloudNatDTable(ctx context.Context, userCred mcclient.TokenCredential, extTable cloudprovider.ICloudNatDTable) error { + diff, err := db.UpdateWithLock(ctx, self, func() error { + self.Status = extTable.GetStatus() + self.ExternalIP = extTable.GetExternalIp() + self.ExternalPort = extTable.GetExternalPort() + self.InternalIP = extTable.GetInternalIp() + self.InternalPort = extTable.GetInternalPort() + self.IpProtocol = extTable.GetIpProtocol() + return nil + }) + if err != nil { + return err + } + db.OpsLog.LogSyncUpdate(self, diff, userCred) + return nil +} + +func (manager *SNatDTableManager) newFromCloudNatDTable(ctx context.Context, userCred mcclient.TokenCredential, nat *SNatGateway, extTable cloudprovider.ICloudNatDTable) (*SNatDTable, error) { + table := SNatDTable{} + table.SetModelManager(manager, &table) + + newName, err := db.GenerateName(manager, manager.GetOwnerId(userCred), extTable.GetName()) + if err != nil { + return nil, err + } + table.Name = newName + table.Status = extTable.GetStatus() + table.ExternalId = extTable.GetGlobalId() + table.IsEmulated = extTable.IsEmulated() + table.NatgatewayId = nat.Id + table.ExternalIP = extTable.GetExternalIp() + table.ExternalPort = extTable.GetExternalPort() + table.InternalIP = extTable.GetInternalIp() + table.InternalPort = extTable.GetInternalPort() + table.IpProtocol = extTable.GetIpProtocol() + + err = manager.TableSpec().Insert(&table) + if err != nil { + log.Errorf("newFromCloudNatDTable fail %s", err) + return nil, err + } + + db.OpsLog.LogEvent(&table, db.ACT_CREATE, table.GetShortDesc(ctx), userCred) + + return &table, nil +} + +func (self *SNatDTable) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) { + extra, err := self.SStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query) + if err != nil { + return nil, err + } + return extra, nil +} + +func (self *SNatDTable) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { + extra := self.SStatusStandaloneResourceBase.GetCustomizeColumns(ctx, userCred, query) + natgateway, err := self.GetNatgateway() + if err != nil { + log.Errorf("failed to get naggateway %s for dtable %s(%s) error: %v", self.NatgatewayId, self.Name, self.Id, err) + return extra + } + extra.Add(jsonutils.NewString(natgateway.Name), "natgateway") + return extra +} diff --git a/pkg/compute/models/natgateways.go b/pkg/compute/models/natgateways.go new file mode 100644 index 0000000000..1f1094b3f8 --- /dev/null +++ b/pkg/compute/models/natgateways.go @@ -0,0 +1,365 @@ +// 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" + + "yunion.io/x/log" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/pkg/util/compare" + "yunion.io/x/sqlchemy" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" + "yunion.io/x/onecloud/pkg/cloudcommon/validators" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SNatGetewayManager struct { + db.SStatusStandaloneResourceBaseManager +} + +var NatGatewayManager *SNatGetewayManager + +func init() { + NatGatewayManager = &SNatGetewayManager{ + SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager( + SNatGateway{}, + "natgateways_tbl", + "natgateway", + "natgateways", + ), + } +} + +type SNatGateway struct { + db.SStatusStandaloneResourceBase + db.SExternalizedResourceBase + SManagedResourceBase + SCloudregionResourceBase + SBillingResourceBase + + VpcId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"optional"` + NatSpec string `list:"user" get:"user" list:"user" create:"optional"` // NAT规格 +} + +func (manager *SNatGetewayManager) GetContextManagers() [][]db.IModelManager { + return [][]db.IModelManager{ + {CloudregionManager}, + } +} + +func (self *SNatGetewayManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsAdminAllowList(userCred, self) +} + +func (self *SNatGetewayManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowCreate(userCred, self) +} + +func (self *SNatGateway) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsAdminAllowGet(userCred, self) +} + +func (self *SNatGateway) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool { + return db.IsAdminAllowUpdate(userCred, self) +} + +func (self *SNatGateway) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowDelete(userCred, self) +} + +func (man *SNatGetewayManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { + q, err := man.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query) + if err != nil { + return nil, err + } + data := query.(*jsonutils.JSONDict) + q, err = validators.ApplyModelFilters(q, data, []*validators.ModelFilterOptions{ + {Key: "vpc", ModelKeyword: "vpc", OwnerId: userCred}, + {Key: "cloudregion", ModelKeyword: "cloudregion", OwnerId: userCred}, + }) + if err != nil { + return nil, err + } + q, err = managedResourceFilterByAccount(q, query, "", nil) + if err != nil { + return nil, err + } + return q, nil +} + +func (man *SNatGetewayManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("Not Implemented") +} + +func (self *SNatGateway) GetVpc() (*SVpc, error) { + _vpc, err := VpcManager.FetchById(self.VpcId) + if err != nil { + return nil, err + } + return _vpc.(*SVpc), nil +} + +func (manager *SNatGetewayManager) getNatgatewaysByProviderId(providerId string) ([]SNatGateway, error) { + nats := []SNatGateway{} + err := fetchByManagerId(manager, providerId, &nats) + if err != nil { + return nil, err + } + return nats, nil +} + +func (self *SNatGateway) GetDTables() ([]SNatDTable, error) { + tables := []SNatDTable{} + q := NatDTableManager.Query().Equals("natgateway_id", self.Id) + err := db.FetchModelObjects(NatDTableManager, q, &tables) + if err != nil { + return nil, err + } + return tables, nil +} + +func (self *SNatGateway) GetSTables() ([]SNatSTable, error) { + tables := []SNatSTable{} + q := NatSTableManager.Query().Equals("natgateway_id", self.Id) + err := db.FetchModelObjects(NatSTableManager, q, &tables) + if err != nil { + return nil, err + } + return tables, nil +} + +func (self *SNatGateway) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) { + extra, err := self.SStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query) + if err != nil { + return nil, err + } + return extra, nil +} + +func (self *SNatGateway) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { + extra := self.SStatusStandaloneResourceBase.GetCustomizeColumns(ctx, userCred, query) + accountInfo := self.SManagedResourceBase.GetCustomizeColumns(ctx, userCred, query) + if accountInfo != nil { + extra.Update(accountInfo) + } + regionInfo := self.SCloudregionResourceBase.GetCustomizeColumns(ctx, userCred, query) + if regionInfo != nil { + extra.Update(regionInfo) + } + vpc, err := self.GetVpc() + if err != nil { + log.Errorf("failed to found vpc info for nat gateway %s(%s) error: %v", self.Name, self.Id, err) + return extra + } + extra.Add(jsonutils.NewString(vpc.Name), "vpc") + return extra +} + +func (manager *SNatGetewayManager) SyncNatGateways(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, vpc *SVpc, cloudNatGateways []cloudprovider.ICloudNatGateway) ([]SNatGateway, []cloudprovider.ICloudNatGateway, compare.SyncResult) { + lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, provider.GetOwnerId())) + defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, provider.GetOwnerId())) + + localNatGateways := make([]SNatGateway, 0) + remoteNatGateways := make([]cloudprovider.ICloudNatGateway, 0) + syncResult := compare.SyncResult{} + + dbNatGateways, err := vpc.GetNatgateways() + if err != nil { + syncResult.Error(err) + return nil, nil, syncResult + } + + removed := make([]SNatGateway, 0) + commondb := make([]SNatGateway, 0) + commonext := make([]cloudprovider.ICloudNatGateway, 0) + added := make([]cloudprovider.ICloudNatGateway, 0) + if err := compare.CompareSets(dbNatGateways, cloudNatGateways, &removed, &commondb, &commonext, &added); err != nil { + syncResult.Error(err) + return nil, nil, syncResult + } + + for i := 0; i < len(removed); i += 1 { + err := removed[i].syncRemoveCloudNatGateway(ctx, userCred) + if err != nil { + syncResult.DeleteError(err) + } else { + syncResult.Delete() + } + } + + for i := 0; i < len(commondb); i += 1 { + err := commondb[i].SyncWithCloudNatGateway(ctx, userCred, provider, commonext[i]) + if err != nil { + syncResult.UpdateError(err) + continue + } + syncMetadata(ctx, userCred, &commondb[i], commonext[i]) + localNatGateways = append(localNatGateways, commondb[i]) + remoteNatGateways = append(remoteNatGateways, commonext[i]) + syncResult.Update() + } + + for i := 0; i < len(added); i += 1 { + routeTableNew, err := manager.newFromCloudNatGateway(ctx, userCred, provider, vpc, added[i]) + if err != nil { + syncResult.AddError(err) + continue + } + syncMetadata(ctx, userCred, routeTableNew, added[i]) + localNatGateways = append(localNatGateways, *routeTableNew) + remoteNatGateways = append(remoteNatGateways, added[i]) + syncResult.Add() + } + return localNatGateways, remoteNatGateways, syncResult +} + +func (self *SNatGateway) syncRemoveCloudNatGateway(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, self) + defer lockman.ReleaseObject(ctx, self) + + err := self.ValidateDeleteCondition(ctx) + if err != nil { // cannot delete + return self.SetStatus(userCred, api.VPC_STATUS_UNKNOWN, "sync to delete") + } + return self.Delete(ctx, userCred) +} + +func (self *SNatGateway) SyncWithCloudNatGateway(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, extNat cloudprovider.ICloudNatGateway) error { + diff, err := db.UpdateWithLock(ctx, self, func() error { + self.Status = extNat.GetStatus() + self.NatSpec = extNat.GetNatSpec() + + factory, _ := provider.GetProviderFactory() + if factory.IsSupportPrepaidResources() { + self.BillingType = extNat.GetBillingType() + self.ExpiredAt = extNat.GetExpiredAt() + } + + return nil + }) + if err != nil { + return err + } + db.OpsLog.LogSyncUpdate(self, diff, userCred) + return nil +} + +func (manager *SNatGetewayManager) newFromCloudNatGateway(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, vpc *SVpc, extNat cloudprovider.ICloudNatGateway) (*SNatGateway, error) { + nat := SNatGateway{} + nat.SetModelManager(manager, &nat) + + region, err := vpc.GetRegion() + if err != nil { + return nil, err + } + + newName, err := db.GenerateName(manager, manager.GetOwnerId(userCred), extNat.GetName()) + if err != nil { + return nil, err + } + nat.Name = newName + nat.VpcId = vpc.Id + nat.Status = extNat.GetStatus() + nat.NatSpec = extNat.GetNatSpec() + if createdAt := extNat.GetCreatedAt(); !createdAt.IsZero() { + nat.CreatedAt = extNat.GetCreatedAt() + } + nat.ExternalId = extNat.GetGlobalId() + nat.CloudregionId = region.Id + nat.ManagerId = provider.Id + nat.IsEmulated = extNat.IsEmulated() + + factory, _ := provider.GetProviderFactory() + if factory.IsSupportPrepaidResources() { + nat.BillingType = extNat.GetBillingType() + nat.ExpiredAt = extNat.GetExpiredAt() + } + + err = manager.TableSpec().Insert(&nat) + if err != nil { + log.Errorf("newFromCloudNatGateway fail %s", err) + return nil, err + } + + db.OpsLog.LogEvent(&nat, db.ACT_CREATE, nat.GetShortDesc(ctx), userCred) + + return &nat, nil +} + +func (self *SNatGateway) GetEips() ([]SElasticip, error) { + q := ElasticipManager.Query().Equals("associate_id", self.Id) + eips := []SElasticip{} + if err := db.FetchModelObjects(ElasticipManager, q, &eips); err != nil { + return nil, err + } + return eips, nil +} + +func (self *SNatGateway) SyncNatGatewayEips(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, extEips []cloudprovider.ICloudEIP) compare.SyncResult { + result := compare.SyncResult{} + + dbEips, err := self.GetEips() + if err != nil { + result.AddError(err) + return result + } + + removed := make([]SElasticip, 0) + commondb := make([]SElasticip, 0) + commonext := make([]cloudprovider.ICloudEIP, 0) + added := make([]cloudprovider.ICloudEIP, 0) + if err := compare.CompareSets(dbEips, extEips, &removed, &commondb, &commonext, &added); err != nil { + result.Error(err) + return result + } + + for i := 0; i < len(removed); i += 1 { + err := removed[i].Dissociate(ctx, userCred) + if err != nil { + result.AddError(err) + } else { + result.Delete() + } + } + + for i := 0; i < len(added); i += 1 { + neip, err := ElasticipManager.getEipByExtEip(ctx, userCred, added[i], provider, self.GetRegion(), provider.GetOwnerId()) + if err != nil { + result.AddError(err) + continue + } + if len(neip.AssociateId) > 0 && neip.AssociateId != self.Id { + err = neip.Dissociate(ctx, userCred) + if err != nil { + result.AddError(err) + continue + } + } + err = neip.AssociateNatGateway(ctx, userCred, self) + if err != nil { + result.AddError(err) + } else { + result.Add() + } + } + + return result +} diff --git a/pkg/compute/models/natstables.go b/pkg/compute/models/natstables.go new file mode 100644 index 0000000000..f533e74c77 --- /dev/null +++ b/pkg/compute/models/natstables.go @@ -0,0 +1,271 @@ +// 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" + + "yunion.io/x/log" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/pkg/util/compare" + "yunion.io/x/sqlchemy" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" + "yunion.io/x/onecloud/pkg/cloudcommon/validators" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SNatSTableManager struct { + db.SStatusStandaloneResourceBaseManager +} + +var NatSTableManager *SNatSTableManager + +func init() { + NatSTableManager = &SNatSTableManager{ + SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager( + SNatSTable{}, + "natstables_tbl", + "natstable", + "natstables", + ), + } +} + +type SNatSTable struct { + db.SStatusStandaloneResourceBase + db.SExternalizedResourceBase + + IP string `width:"17" charset:"ascii" list:"user" create:"required"` + SourceCIDR string `width:"22" charset:"ascii" list:"user" create:"required"` + + NetworkId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"optional"` + NatgatewayId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` +} + +func (manager *SNatSTableManager) GetContextManagers() [][]db.IModelManager { + return [][]db.IModelManager{ + {NatGatewayManager}, + } +} + +func (self *SNatSTableManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsAdminAllowList(userCred, self) +} + +func (self *SNatSTableManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowCreate(userCred, self) +} + +func (self *SNatSTable) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsAdminAllowGet(userCred, self) +} + +func (self *SNatSTable) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool { + return db.IsAdminAllowUpdate(userCred, self) +} + +func (self *SNatSTable) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowDelete(userCred, self) +} + +func (self *SNatSTable) GetNatgateway() (*SNatGateway, error) { + _natgateway, err := NatGatewayManager.FetchById(self.NatgatewayId) + if err != nil { + return nil, err + } + return _natgateway.(*SNatGateway), nil +} + +func (self *SNatSTable) GetNetwork() (*SNetwork, error) { + _network, err := NetworkManager.FetchById(self.NatgatewayId) + if err != nil { + return nil, err + } + return _network.(*SNetwork), nil +} + +func (man *SNatSTableManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { + q, err := man.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query) + if err != nil { + return nil, err + } + data := query.(*jsonutils.JSONDict) + q, err = validators.ApplyModelFilters(q, data, []*validators.ModelFilterOptions{ + {Key: "network", ModelKeyword: "network", OwnerId: userCred}, + {Key: "natgateway", ModelKeyword: "natgateway", OwnerId: userCred}, + }) + if err != nil { + return nil, err + } + + q, err = managedResourceFilterByAccount(q, query, "natgateway_id", func() *sqlchemy.SQuery { + natgateways := NatGatewayManager.Query().SubQuery() + return natgateways.Query(natgateways.Field("id")) + }) + + return q, nil +} + +func (man *SNatSTableManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("Not Implemented") +} + +func (manager *SNatSTableManager) SyncNatSTables(ctx context.Context, userCred mcclient.TokenCredential, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider, nat *SNatGateway, extTables []cloudprovider.ICloudNatSTable) compare.SyncResult { + lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId)) + defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId)) + + result := compare.SyncResult{} + dbNatSTables, err := nat.GetSTables() + if err != nil { + result.Error(err) + return result + } + + removed := make([]SNatSTable, 0) + commondb := make([]SNatSTable, 0) + commonext := make([]cloudprovider.ICloudNatSTable, 0) + added := make([]cloudprovider.ICloudNatSTable, 0) + if err := compare.CompareSets(dbNatSTables, extTables, &removed, &commondb, &commonext, &added); err != nil { + result.Error(err) + return result + } + + for i := 0; i < len(removed); i += 1 { + err := removed[i].syncRemoveCloudNatSTable(ctx, userCred) + if err != nil { + result.DeleteError(err) + } else { + result.Delete() + } + } + + for i := 0; i < len(commondb); i += 1 { + err := commondb[i].SyncWithCloudNatSTable(ctx, userCred, commonext[i]) + if err != nil { + result.UpdateError(err) + continue + } + syncMetadata(ctx, userCred, &commondb[i], commonext[i]) + result.Update() + } + + for i := 0; i < len(added); i += 1 { + routeTableNew, err := manager.newFromCloudNatSTable(ctx, userCred, nat, added[i]) + if err != nil { + result.AddError(err) + continue + } + syncMetadata(ctx, userCred, routeTableNew, added[i]) + result.Add() + } + return result +} + +func (self *SNatSTable) syncRemoveCloudNatSTable(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, self) + defer lockman.ReleaseObject(ctx, self) + + err := self.ValidateDeleteCondition(ctx) + if err != nil { // cannot delete + return self.SetStatus(userCred, api.VPC_STATUS_UNKNOWN, "sync to delete") + } + return self.Delete(ctx, userCred) +} + +func (self *SNatSTable) SyncWithCloudNatSTable(ctx context.Context, userCred mcclient.TokenCredential, extTable cloudprovider.ICloudNatSTable) error { + diff, err := db.UpdateWithLock(ctx, self, func() error { + self.Status = extTable.GetStatus() + self.IP = extTable.GetIP() + self.SourceCIDR = extTable.GetSourceCIDR() + if extNetworkId := extTable.GetNetworkId(); len(extNetworkId) > 0 { + network, err := db.FetchByExternalId(NetworkManager, extNetworkId) + if err != nil { + return err + } + self.NetworkId = network.GetId() + } + return nil + }) + if err != nil { + return err + } + db.OpsLog.LogSyncUpdate(self, diff, userCred) + return nil +} + +func (manager *SNatSTableManager) newFromCloudNatSTable(ctx context.Context, userCred mcclient.TokenCredential, nat *SNatGateway, extTable cloudprovider.ICloudNatSTable) (*SNatSTable, error) { + table := SNatSTable{} + table.SetModelManager(manager, &table) + + newName, err := db.GenerateName(manager, manager.GetOwnerId(userCred), extTable.GetName()) + if err != nil { + return nil, err + } + table.Name = newName + table.Status = extTable.GetStatus() + table.ExternalId = extTable.GetGlobalId() + table.IsEmulated = extTable.IsEmulated() + table.NatgatewayId = nat.Id + + table.IP = extTable.GetIP() + table.SourceCIDR = extTable.GetSourceCIDR() + if extNetworkId := extTable.GetNetworkId(); len(extNetworkId) > 0 { + network, err := db.FetchByExternalId(NetworkManager, extNetworkId) + if err != nil { + return nil, err + } + table.NetworkId = network.GetId() + } + + err = manager.TableSpec().Insert(&table) + if err != nil { + log.Errorf("newFromCloudNatSTable fail %s", err) + return nil, err + } + + db.OpsLog.LogEvent(&table, db.ACT_CREATE, table.GetShortDesc(ctx), userCred) + + return &table, nil +} + +func (self *SNatSTable) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) { + extra, err := self.SStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query) + if err != nil { + return nil, err + } + return extra, nil +} + +func (self *SNatSTable) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { + extra := self.SStatusStandaloneResourceBase.GetCustomizeColumns(ctx, userCred, query) + natgateway, err := self.GetNatgateway() + if err != nil { + log.Errorf("failed to get naggateway %s for stable %s(%s) error: %v", self.NatgatewayId, self.Name, self.Id, err) + return extra + } + extra.Add(jsonutils.NewString(natgateway.Name), "natgateway") + network, err := self.GetNetwork() + if err != nil { + log.Errorf("failed to get network %s for stable %s(%s) error: %v", self.NetworkId, self.Name, self.Id, err) + return extra + } + extra.Add(jsonutils.NewString(network.Name), "network") + return extra +} diff --git a/pkg/compute/models/purge.go b/pkg/compute/models/purge.go index d0238ea326..d03c1aa651 100644 --- a/pkg/compute/models/purge.go +++ b/pkg/compute/models/purge.go @@ -830,3 +830,93 @@ func (manager *SCloudregionManager) purgeAll(ctx context.Context, userCred mccli } return nil } + +func (table *SNatSTable) purge(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, table) + defer lockman.ReleaseObject(ctx, table) + + err := table.ValidateDeleteCondition(ctx) + if err != nil { + return err + } + + return table.Delete(ctx, userCred) +} + +func (nat *SNatGateway) purgeSTables(ctx context.Context, userCred mcclient.TokenCredential) error { + tables, err := nat.GetSTables() + if err != nil { + return err + } + + for i := range tables { + err = tables[i].purge(ctx, userCred) + if err != nil { + return err + } + } + return nil +} + +func (table *SNatDTable) purge(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, table) + defer lockman.ReleaseObject(ctx, table) + + err := table.ValidateDeleteCondition(ctx) + if err != nil { + return err + } + + return table.Delete(ctx, userCred) +} + +func (nat *SNatGateway) purgeDTables(ctx context.Context, userCred mcclient.TokenCredential) error { + tables, err := nat.GetDTables() + if err != nil { + return err + } + + for i := range tables { + err = tables[i].purge(ctx, userCred) + if err != nil { + return err + } + } + return nil +} + +func (nat *SNatGateway) purge(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, nat) + defer lockman.ReleaseObject(ctx, nat) + + err := nat.purgeDTables(ctx, userCred) + if err != nil { + return err + } + + err = nat.purgeSTables(ctx, userCred) + if err != nil { + return err + } + + err = nat.ValidateDeleteCondition(ctx) + if err != nil { + return err + } + + return nat.Delete(ctx, userCred) +} + +func (manager *SNatGetewayManager) purgeAll(ctx context.Context, userCred mcclient.TokenCredential, providerId string) error { + nats, err := manager.getNatgatewaysByProviderId(providerId) + if err != nil { + return err + } + for i := range nats { + err = nats[i].purge(ctx, userCred) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index b73adbded5..6459f90512 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -108,13 +108,37 @@ func (self *SVpc) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCr return self.SEnabledStatusStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) } +func (self *SVpc) getNatgatewayQuery() *sqlchemy.SQuery { + return NatGatewayManager.Query().Equals("vpc_id", self.Id) +} + +func (self *SVpc) GetNatgatewayCount() (int, error) { + return self.getNatgatewayQuery().CountWithError() +} + +func (self *SVpc) GetNatgateways() ([]SNatGateway, error) { + nats := []SNatGateway{} + err := db.FetchModelObjects(NatGatewayManager, self.getNatgatewayQuery(), &nats) + if err != nil { + return nil, err + } + return nats, nil +} + func (self *SVpc) ValidateDeleteCondition(ctx context.Context) error { cnt, err := self.GetNetworkCount() if err != nil { return httperrors.NewInternalServerError("GetNetworkCount fail %s", err) } if cnt > 0 { - return httperrors.NewNotEmptyError("VPC not empty") + return httperrors.NewNotEmptyError("VPC not empty, please delete network first") + } + cnt, err = self.GetNatgatewayCount() + if err != nil { + return httperrors.NewInternalServerError("GetNatgatewayCount fail %v", err) + } + if cnt > 0 { + return httperrors.NewNotEmptyError("VPC not empty, please delete nat gateway first") } if self.Id == api.DEFAULT_VPC_ID { return httperrors.NewProtectedResourceError("not allow to delete default vpc") @@ -183,6 +207,8 @@ func (self *SVpc) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict extra.Add(jsonutils.NewInt(int64(cnt)), "network_count") cnt, _ = self.GetRouteTableCount() extra.Add(jsonutils.NewInt(int64(cnt)), "routetable_count") + cnt, _ = self.GetNatgatewayCount() + extra.Add(jsonutils.NewInt(int64(cnt)), "natgateway_count") /* region, err := self.GetRegion() if err != nil { log.Errorf("failed getting region for vpc %s(%s)", self.Name, self.Id) diff --git a/pkg/compute/service/handlers.go b/pkg/compute/service/handlers.go index 769215a941..bf34005d14 100644 --- a/pkg/compute/service/handlers.go +++ b/pkg/compute/service/handlers.go @@ -81,6 +81,9 @@ func InitHandlers(app *appsrv.Application) { // models.VCenterManager, models.DnsRecordManager, models.ElasticipManager, + models.NatGatewayManager, + models.NatDTableManager, + models.NatSTableManager, models.SnapshotManager, models.SnapshotPolicyManager, models.BaremetalagentManager, diff --git a/pkg/mcclient/modules/mod_natdtables.go b/pkg/mcclient/modules/mod_natdtables.go new file mode 100644 index 0000000000..70a053db04 --- /dev/null +++ b/pkg/mcclient/modules/mod_natdtables.go @@ -0,0 +1,27 @@ +// 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 modules + +var ( + NatDTables ResourceManager +) + +func init() { + NatDTables = NewComputeManager("natdtable", "natdtables", + []string{"ID", "Name", "Status", "Natgateway_Id", "Natgateway", "External_IP", "External_Port", "Internal_IP", "Internal_Port", "Ip_Protocol"}, + []string{}) + + registerCompute(&NatDTables) +} diff --git a/pkg/mcclient/modules/mod_natgateways.go b/pkg/mcclient/modules/mod_natgateways.go new file mode 100644 index 0000000000..84c10d4800 --- /dev/null +++ b/pkg/mcclient/modules/mod_natgateways.go @@ -0,0 +1,27 @@ +// 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 modules + +var ( + NatGateways ResourceManager +) + +func init() { + NatGateways = NewComputeManager("natgateway", "natgateways", + []string{"ID", "Name", "Enabled", "Status", "Cloudregion_Id", "Region", "Vpc_Id", "Charge_Type", "Nat_Spec"}, + []string{}) + + registerCompute(&NatGateways) +} diff --git a/pkg/mcclient/modules/mod_natstables.go b/pkg/mcclient/modules/mod_natstables.go new file mode 100644 index 0000000000..df6049b72f --- /dev/null +++ b/pkg/mcclient/modules/mod_natstables.go @@ -0,0 +1,27 @@ +// 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 modules + +var ( + NatSTables ResourceManager +) + +func init() { + NatSTables = NewComputeManager("natstable", "natstables", + []string{"ID", "Name", "Status", "IP", "Natgateway_Id", "Natgateway", "Network", "Network_Id", "Source_CIDR"}, + []string{}) + + registerCompute(&NatSTables) +} diff --git a/pkg/mcclient/options/natgateways.go b/pkg/mcclient/options/natgateways.go new file mode 100644 index 0000000000..5a9c19705c --- /dev/null +++ b/pkg/mcclient/options/natgateways.go @@ -0,0 +1,35 @@ +// 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 options + +type NatGatewayListOptions struct { + Vpc string `help:"vpc id or name"` + Cloudregion string `help:"cloudreigon id or name"` + + BaseListOptions +} + +type NatDTableListOptions struct { + Natgateway string `help:"Natgateway name or id"` + + BaseListOptions +} + +type NatSTableListOptions struct { + Natgateway string `help:"Natgateway name or id"` + Network string `help:"Network id or name"` + + BaseListOptions +} diff --git a/pkg/multicloud/billing_base.go b/pkg/multicloud/billing_base.go new file mode 100644 index 0000000000..57dca26b64 --- /dev/null +++ b/pkg/multicloud/billing_base.go @@ -0,0 +1,17 @@ +package multicloud + +import "time" + +type SBillingBase struct{} + +func (self *SBillingBase) GetBillingType() string { + return "" +} + +func (self *SBillingBase) GetCreatedAt() time.Time { + return time.Time{} +} + +func (self *SBillingBase) GetExpiredAt() time.Time { + return time.Time{} +} diff --git a/pkg/multicloud/natgateway_base.go b/pkg/multicloud/natgateway_base.go new file mode 100644 index 0000000000..39999f28a6 --- /dev/null +++ b/pkg/multicloud/natgateway_base.go @@ -0,0 +1,24 @@ +package multicloud + +import ( + "fmt" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SNatGatewayBase struct { + SResourceBase + SBillingBase +} + +func (nat *SNatGatewayBase) GetIEips() ([]cloudprovider.ICloudEIP, error) { + return nil, fmt.Errorf("Not Implemented GetIEips") +} + +func (nat *SNatGatewayBase) GetINatDTables() ([]cloudprovider.ICloudNatDTable, error) { + return nil, fmt.Errorf("Not Implemented GetINatDTables") +} + +func (nat *SNatGatewayBase) GetINatSTables() ([]cloudprovider.ICloudNatSTable, error) { + return nil, fmt.Errorf("Not Implemented GetINatSTables") +} diff --git a/pkg/multicloud/resource_base.go b/pkg/multicloud/resource_base.go new file mode 100644 index 0000000000..76ed2a4b76 --- /dev/null +++ b/pkg/multicloud/resource_base.go @@ -0,0 +1,17 @@ +package multicloud + +import "yunion.io/x/jsonutils" + +type SResourceBase struct{} + +func (self *SResourceBase) IsEmulated() bool { + return false +} + +func (self *SResourceBase) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SResourceBase) Refresh() error { + return nil +} diff --git a/pkg/multicloud/vpc_base.go b/pkg/multicloud/vpc_base.go new file mode 100644 index 0000000000..62ee958d4f --- /dev/null +++ b/pkg/multicloud/vpc_base.go @@ -0,0 +1,15 @@ +package multicloud + +import ( + "fmt" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SVpc struct { + SResourceBase +} + +func (self *SVpc) GetINatGateways() ([]cloudprovider.ICloudNatGateway, error) { + return nil, fmt.Errorf("Not Implemented GetNatGateways") +} diff --git a/pkg/util/aliyun/eip.go b/pkg/util/aliyun/eip.go index 916edb0d61..175bb173f8 100644 --- a/pkg/util/aliyun/eip.go +++ b/pkg/util/aliyun/eip.go @@ -16,6 +16,7 @@ package aliyun import ( "fmt" + "strings" "time" "yunion.io/x/jsonutils" @@ -151,7 +152,9 @@ func (self *SEipAddress) GetMode() string { func (self *SEipAddress) GetAssociationType() string { switch self.InstanceType { case EIP_INSTANCE_TYPE_ECS: - return "server" + return api.EIP_ASSOCIATE_TYPE_SERVER + case EIP_INSTANCE_TYPE_NAT: + return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY default: log.Fatalf("unsupported type: %s", self.InstanceType) return "unsupported" @@ -219,7 +222,7 @@ func (self *SEipAddress) ChangeBandwidth(bw int) error { return self.region.UpdateEipBandwidth(self.AllocationId, bw) } -func (region *SRegion) GetEips(eipId string, offset int, limit int) ([]SEipAddress, int, error) { +func (region *SRegion) GetEips(eipId string, associatedId string, offset int, limit int) ([]SEipAddress, int, error) { if limit > 50 || limit <= 0 { limit = 50 } @@ -233,6 +236,15 @@ func (region *SRegion) GetEips(eipId string, offset int, limit int) ([]SEipAddre params["AllocationId"] = eipId } + if len(associatedId) > 0 { + params["AssociatedInstanceId"] = associatedId + for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "ngw-": "Nat", "lb-": "SlbInstance"} { + if strings.HasPrefix(associatedId, prefix) { + params["AssociatedInstanceType"] = instanceType + } + } + } + body, err := region.ecsRequest("DescribeEipAddresses", params) if err != nil { log.Errorf("DescribeEipAddresses fail %s", err) @@ -253,7 +265,7 @@ func (region *SRegion) GetEips(eipId string, offset int, limit int) ([]SEipAddre } func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) { - eips, total, err := region.GetEips(eipId, 0, 1) + eips, total, err := region.GetEips(eipId, "", 0, 1) if err != nil { return nil, err } @@ -312,6 +324,11 @@ func (region *SRegion) AssociateEip(eipId string, instanceId string) error { params := make(map[string]string) params["AllocationId"] = eipId params["InstanceId"] = instanceId + for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "lb-": "SlbInstance", "ngw-": "Nat"} { + if strings.HasPrefix(instanceId, prefix) { + params["InstanceType"] = instanceType + } + } _, err := region.ecsRequest("AssociateEipAddress", params) if err != nil { @@ -324,6 +341,11 @@ func (region *SRegion) DissociateEip(eipId string, instanceId string) error { params := make(map[string]string) params["AllocationId"] = eipId params["InstanceId"] = instanceId + for prefix, instanceType := range map[string]string{"i-": "EcsInstance", "lb-": "SlbInstance", "ngw-": "Nat"} { + if strings.HasPrefix(instanceId, prefix) { + params["InstanceType"] = instanceType + } + } _, err := region.ecsRequest("UnassociateEipAddress", params) if err != nil { diff --git a/pkg/util/aliyun/natdtable.go b/pkg/util/aliyun/natdtable.go new file mode 100644 index 0000000000..52cd47466a --- /dev/null +++ b/pkg/util/aliyun/natdtable.go @@ -0,0 +1,109 @@ +package aliyun + +import ( + "fmt" + "strconv" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SForwardTableEntry struct { + multicloud.SResourceBase + nat *SNatGetway + + ForwardEntryId string + ForwardEntryName string + IpProtocol string + Status string + ExternalIp string + ForwardTableId string + ExternalPort string + InternalPort string + InternalIp string +} + +func (dtable *SForwardTableEntry) GetName() string { + if len(dtable.ForwardEntryName) > 0 { + return dtable.ForwardEntryName + } + return dtable.ForwardEntryId +} + +func (dtable *SForwardTableEntry) GetId() string { + return dtable.ForwardEntryId +} + +func (dtable *SForwardTableEntry) GetGlobalId() string { + return dtable.ForwardEntryId +} + +func (dtable *SForwardTableEntry) GetStatus() string { + switch dtable.Status { + case "Available": + return api.NAT_STAUTS_AVAILABLE + default: + return api.NAT_STATUS_UNKNOWN + } +} + +func (dtable *SForwardTableEntry) GetIpProtocol() string { + return dtable.IpProtocol +} + +func (dtable *SForwardTableEntry) GetExternalIp() string { + return dtable.ExternalIp +} + +func (dtable *SForwardTableEntry) GetExternalPort() int { + port, _ := strconv.Atoi(dtable.ExternalPort) + return port +} + +func (dtable *SForwardTableEntry) GetInternalIp() string { + return dtable.InternalIp +} + +func (dtable *SForwardTableEntry) GetInternalPort() int { + port, _ := strconv.Atoi(dtable.InternalPort) + return port +} + +func (region *SRegion) GetAllDTables(tableId string) ([]SForwardTableEntry, error) { + dtables := []SForwardTableEntry{} + for { + part, total, err := region.GetForwardTableEntries(tableId, len(dtables), 50) + if err != nil { + return nil, err + } + dtables = append(dtables, part...) + if len(dtables) >= total { + break + } + } + return dtables, nil +} + +func (region *SRegion) GetForwardTableEntries(tableId string, offset int, limit int) ([]SForwardTableEntry, int, error) { + if limit > 50 || limit <= 0 { + limit = 50 + } + params := make(map[string]string) + params["RegionId"] = region.RegionId + params["PageSize"] = fmt.Sprintf("%d", limit) + params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1) + params["ForwardTableId"] = tableId + + body, err := region.vpcRequest("DescribeForwardTableEntries", params) + if err != nil { + return nil, 0, err + } + + dtables := []SForwardTableEntry{} + err = body.Unmarshal(&dtables, "ForwardTableEntries", "ForwardTableEntry") + if err != nil { + return nil, 0, err + } + total, _ := body.Int("TotalCount") + return dtables, int(total), nil +} diff --git a/pkg/util/aliyun/natgateway.go b/pkg/util/aliyun/natgateway.go index 2a63b36c00..05c579d770 100644 --- a/pkg/util/aliyun/natgateway.go +++ b/pkg/util/aliyun/natgateway.go @@ -18,7 +18,11 @@ import ( "fmt" "time" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" + "yunion.io/x/log" + api "yunion.io/x/onecloud/pkg/apis/compute" ) type SBandwidthPackageIds struct { @@ -34,15 +38,18 @@ type SSnatTableIds struct { } type SNatGetway struct { + multicloud.SNatGatewayBase + vpc *SVpc BandwidthPackageIds SBandwidthPackageIds BusinessStatus string CreationTime time.Time + ExpiredTime time.Time Description string ForwardTableIds SForwardTableIds SnatTableIds SSnatTableIds - InstanceChargeType string + InstanceChargeType TChargeType Name string NatGatewayId string RegionId string @@ -51,6 +58,99 @@ type SNatGetway struct { VpcId string } +func (nat *SNatGetway) GetId() string { + return nat.NatGatewayId +} + +func (nat *SNatGetway) GetGlobalId() string { + return nat.NatGatewayId +} + +func (nat *SNatGetway) GetName() string { + if len(nat.Name) > 0 { + return nat.Name + } + return nat.NatGatewayId +} + +func (nat *SNatGetway) GetStatus() string { + switch nat.Status { + case "Initiating": + return api.NAT_STATUS_ALLOCATE + case "Available": + return api.NAT_STAUTS_AVAILABLE + case "Pending": + return api.NAT_STATUS_DEPLOYING + default: + return api.NAT_STATUS_UNKNOWN + } + +} + +func (nat *SNatGetway) GetBillingType() string { + return convertChargeType(nat.InstanceChargeType) +} + +func (nat *SNatGetway) GetNatSpec() string { + return nat.Spec +} + +func (nat *SNatGetway) GetCreatedAt() time.Time { + return nat.CreationTime +} + +func (nat *SNatGetway) GetExpiredAt() time.Time { + return nat.ExpiredTime +} + +func (nat *SNatGetway) GetIEips() ([]cloudprovider.ICloudEIP, error) { + eips := []SEipAddress{} + for { + parts, total, err := nat.vpc.region.GetEips("", nat.NatGatewayId, len(eips), 50) + if err != nil { + return nil, err + } + eips = append(eips, parts...) + if len(eips) >= total { + break + } + } + ieips := []cloudprovider.ICloudEIP{} + for i := 0; i < len(eips); i++ { + eips[i].region = nat.vpc.region + ieips = append(ieips, &eips[i]) + } + return ieips, nil +} + +func (nat *SNatGetway) GetINatDTables() ([]cloudprovider.ICloudNatDTable, error) { + itables := []cloudprovider.ICloudNatDTable{} + for _, dtableId := range nat.ForwardTableIds.ForwardTableId { + dtables, err := nat.vpc.region.GetAllDTables(dtableId) + if err != nil { + return nil, err + } + for i := 0; i < len(dtables); i++ { + dtables[i].nat = nat + itables = append(itables, &dtables[i]) + } + } + return itables, nil +} + +func (nat *SNatGetway) GetINatSTables() ([]cloudprovider.ICloudNatSTable, error) { + stables, err := nat.getSnatEntries() + if err != nil { + return nil, err + } + itables := []cloudprovider.ICloudNatSTable{} + for i := 0; i < len(stables); i++ { + stables[i].nat = nat + itables = append(itables, &stables[i]) + } + return itables, nil +} + func (self *SRegion) GetNatGateways(vpcId string, natGwId string, offset, limit int) ([]SNatGetway, int, error) { if limit > 50 || limit <= 0 { limit = 50 @@ -85,96 +185,3 @@ func (self *SRegion) GetNatGateways(vpcId string, natGwId string, offset, limit total, _ := body.Int("TotalCount") return gateways, int(total), nil } - -type SSNATTableEntry struct { - SnatEntryId string - SnatIp string - SnatTableId string `json:"snat_table_id"` - SourceCIDR string `json:"source_cidr"` - SourceVSwitchId string `json:"source_vswitch_id"` - Status string -} - -func (self *SRegion) GetSNATEntries(tableId string, offset, limit int) ([]SSNATTableEntry, int, error) { - if limit > 50 || limit <= 0 { - limit = 50 - } - params := make(map[string]string) - params["RegionId"] = self.RegionId - params["PageSize"] = fmt.Sprintf("%d", limit) - params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1) - params["SnatTableId"] = tableId - - body, err := self.vpcRequest("DescribeSnatTableEntries", params) - if err != nil { - log.Errorf("DescribeSnatTableEntries fail %s", err) - return nil, 0, err - } - - if self.client.Debug { - log.Debugf("%s", body.PrettyString()) - } - - entries := make([]SSNATTableEntry, 0) - err = body.Unmarshal(&entries, "SnatTableEntries", "SnatTableEntry") - if err != nil { - log.Errorf("Unmarshal entries fail %s", err) - return nil, 0, err - } - total, _ := body.Int("TotalCount") - return entries, int(total), nil -} - -func (region *SRegion) DeleteSnatEntry(tableId string, entryId string) error { - params := make(map[string]string) - params["RegionId"] = region.RegionId - params["SnatTableId"] = tableId - params["SnatEntryId"] = entryId - _, err := region.vpcRequest("DeleteSnatEntry", params) - return err -} - -func (nat *SNatGetway) getSnatEntriesForTable(tblId string) ([]SSNATTableEntry, error) { - entries := make([]SSNATTableEntry, 0) - entryTotal := -1 - for entryTotal < 0 || len(entries) < entryTotal { - parts, total, err := nat.vpc.region.GetSNATEntries(tblId, len(entries), 50) - if err != nil { - return nil, err - } - if len(parts) > 0 { - entries = append(entries, parts...) - } - entryTotal = total - } - return entries, nil -} - -func (nat *SNatGetway) getSnatEntries() ([]SSNATTableEntry, error) { - entries := make([]SSNATTableEntry, 0) - for i := range nat.SnatTableIds.SnatTableId { - sentries, err := nat.getSnatEntriesForTable(nat.SnatTableIds.SnatTableId[i]) - if err != nil { - return nil, err - } - entries = append(entries, sentries...) - } - return entries, nil -} - -func (nat *SNatGetway) dissociateWithVswitch(vswitchId string) error { - entries, err := nat.getSnatEntries() - if err != nil { - return err - } - for i := range entries { - log.Debugf("%s", entries[i]) - if entries[i].SourceVSwitchId == vswitchId { - err := nat.vpc.region.DeleteSnatEntry(entries[i].SnatTableId, entries[i].SnatEntryId) - if err != nil { - return err - } - } - } - return nil -} diff --git a/pkg/util/aliyun/natstable.go b/pkg/util/aliyun/natstable.go new file mode 100644 index 0000000000..bf39731aaf --- /dev/null +++ b/pkg/util/aliyun/natstable.go @@ -0,0 +1,143 @@ +package aliyun + +import ( + "fmt" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/multicloud" + + "yunion.io/x/log" +) + +type SSNATTableEntry struct { + multicloud.SResourceBase + nat *SNatGetway + + SnatEntryId string + SnatEntryName string + SnatIp string + SnatTableId string `json:"snat_table_id"` + SourceCIDR string `json:"source_cidr"` + SourceVSwitchId string `json:"source_vswitch_id"` + Status string +} + +func (stable *SSNATTableEntry) GetName() string { + if len(stable.SnatEntryName) > 0 { + return stable.SnatEntryName + } + return stable.SnatEntryId +} + +func (stable *SSNATTableEntry) GetId() string { + return stable.SnatEntryId +} + +func (stable *SSNATTableEntry) GetGlobalId() string { + return stable.SnatEntryId +} + +func (stable *SSNATTableEntry) GetStatus() string { + switch stable.Status { + case "Available": + return api.NAT_STAUTS_AVAILABLE + default: + return api.NAT_STATUS_UNKNOWN + } +} + +func (stable *SSNATTableEntry) GetIP() string { + return stable.SnatIp +} + +func (stable *SSNATTableEntry) GetSourceCIDR() string { + return stable.SourceCIDR +} + +func (stable *SSNATTableEntry) GetNetworkId() string { + return stable.SourceVSwitchId +} + +func (self *SRegion) GetSNATEntries(tableId string, offset, limit int) ([]SSNATTableEntry, int, error) { + if limit > 50 || limit <= 0 { + limit = 50 + } + params := make(map[string]string) + params["RegionId"] = self.RegionId + params["PageSize"] = fmt.Sprintf("%d", limit) + params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1) + params["SnatTableId"] = tableId + + body, err := self.vpcRequest("DescribeSnatTableEntries", params) + if err != nil { + log.Errorf("DescribeSnatTableEntries fail %s", err) + return nil, 0, err + } + + if self.client.Debug { + log.Debugf("%s", body.PrettyString()) + } + + entries := make([]SSNATTableEntry, 0) + err = body.Unmarshal(&entries, "SnatTableEntries", "SnatTableEntry") + if err != nil { + log.Errorf("Unmarshal entries fail %s", err) + return nil, 0, err + } + total, _ := body.Int("TotalCount") + return entries, int(total), nil +} + +func (region *SRegion) DeleteSnatEntry(tableId string, entryId string) error { + params := make(map[string]string) + params["RegionId"] = region.RegionId + params["SnatTableId"] = tableId + params["SnatEntryId"] = entryId + _, err := region.vpcRequest("DeleteSnatEntry", params) + return err +} + +func (nat *SNatGetway) getSnatEntriesForTable(tblId string) ([]SSNATTableEntry, error) { + entries := make([]SSNATTableEntry, 0) + entryTotal := -1 + for entryTotal < 0 || len(entries) < entryTotal { + parts, total, err := nat.vpc.region.GetSNATEntries(tblId, len(entries), 50) + if err != nil { + return nil, err + } + if len(parts) > 0 { + entries = append(entries, parts...) + } + entryTotal = total + } + return entries, nil +} + +func (nat *SNatGetway) getSnatEntries() ([]SSNATTableEntry, error) { + entries := make([]SSNATTableEntry, 0) + for i := range nat.SnatTableIds.SnatTableId { + sentries, err := nat.getSnatEntriesForTable(nat.SnatTableIds.SnatTableId[i]) + if err != nil { + return nil, err + } + entries = append(entries, sentries...) + } + return entries, nil +} + +func (nat *SNatGetway) dissociateWithVswitch(vswitchId string) error { + entries, err := nat.getSnatEntries() + if err != nil { + return err + } + for i := range entries { + log.Debugf("%v", entries[i]) + if entries[i].SourceVSwitchId == vswitchId { + err := nat.vpc.region.DeleteSnatEntry(entries[i].SnatTableId, entries[i].SnatEntryId) + if err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/util/aliyun/region.go b/pkg/util/aliyun/region.go index 3bfa53ff1e..81aa682399 100644 --- a/pkg/util/aliyun/region.go +++ b/pkg/util/aliyun/region.go @@ -115,7 +115,7 @@ func (self *SRegion) vpcRequest(action string, params map[string]string) (jsonut if err != nil { return nil, err } - return jsonRequest(client, "vpc.aliyuncs.com", ALIYUN_API_VERSION_VPC, action, params, self.Debug) + return jsonRequest(client, "vpc.aliyuncs.com", ALIYUN_API_VERSION_VPC, action, params, self.client.Debug) } type LBRegion struct { @@ -689,13 +689,13 @@ func (self *SRegion) UpdateInstancePassword(instId string, passwd string) error // } func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { - eips, total, err := self.GetEips("", 0, 50) + eips, total, err := self.GetEips("", "", 0, 50) if err != nil { return nil, err } for len(eips) < total { var parts []SEipAddress - parts, total, err = self.GetEips("", len(eips), 50) + parts, total, err = self.GetEips("", "", len(eips), 50) if err != nil { return nil, err } @@ -709,7 +709,7 @@ func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { } func (self *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error) { - eips, total, err := self.GetEips(eipId, 0, 1) + eips, total, err := self.GetEips(eipId, "", 0, 1) if err != nil { return nil, err } diff --git a/pkg/util/aliyun/shell/eip.go b/pkg/util/aliyun/shell/eip.go index 350b934a20..8ffdd7322a 100644 --- a/pkg/util/aliyun/shell/eip.go +++ b/pkg/util/aliyun/shell/eip.go @@ -21,11 +21,12 @@ import ( func init() { type EipListOptions struct { - Offset int `help:"List offset"` - Limit int `help:"List limit"` + AssociateId string `help:"Id of associate resource"` + Offset int `help:"List offset"` + Limit int `help:"List limit"` } shellutils.R(&EipListOptions{}, "eip-list", "List eips", func(cli *aliyun.SRegion, args *EipListOptions) error { - eips, total, e := cli.GetEips("", args.Offset, args.Limit) + eips, total, e := cli.GetEips("", args.AssociateId, args.Offset, args.Limit) if e != nil { return e } diff --git a/pkg/util/aliyun/shell/natgateway.go b/pkg/util/aliyun/shell/natgateway.go index 6efbb4cb25..5bc5c75bf1 100644 --- a/pkg/util/aliyun/shell/natgateway.go +++ b/pkg/util/aliyun/shell/natgateway.go @@ -47,4 +47,18 @@ func init() { return nil }) + type DNatEntryListOptions struct { + ID string `help:"DNat Table ID"` + Limit int `help:"page size"` + Offset int `help:"page offset"` + } + shellutils.R(&DNatEntryListOptions{}, "dnat-entry-list", "List DNAT entries", func(cli *aliyun.SRegion, args *DNatEntryListOptions) error { + entries, total, e := cli.GetForwardTableEntries(args.ID, args.Offset, args.Limit) + if e != nil { + return e + } + printList(entries, total, args.Offset, args.Limit, []string{}) + return nil + }) + } diff --git a/pkg/util/aliyun/vpc.go b/pkg/util/aliyun/vpc.go index 944f2aebb0..f1020e3828 100644 --- a/pkg/util/aliyun/vpc.go +++ b/pkg/util/aliyun/vpc.go @@ -18,6 +18,8 @@ import ( "strings" "time" + "yunion.io/x/onecloud/pkg/multicloud" + "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -40,6 +42,8 @@ type SVSwitchIds struct { } type SVpc struct { + multicloud.SVpc + region *SRegion iwires []cloudprovider.ICloudWire @@ -263,3 +267,23 @@ func (self *SVpc) getNatGateways() ([]SNatGetway, error) { } return natgatways, nil } + +func (self *SVpc) GetINatGateways() ([]cloudprovider.ICloudNatGateway, error) { + nats := []SNatGetway{} + for { + parts, total, err := self.region.GetNatGateways(self.VpcId, "", len(nats), 50) + if err != nil { + return nil, err + } + nats = append(nats, parts...) + if len(nats) >= total { + break + } + } + inats := []cloudprovider.ICloudNatGateway{} + for i := 0; i < len(nats); i++ { + nats[i].vpc = self + inats = append(inats, &nats[i]) + } + return inats, nil +} diff --git a/pkg/util/aws/eip.go b/pkg/util/aws/eip.go index b5580a0d33..5cbd68d6fd 100644 --- a/pkg/util/aws/eip.go +++ b/pkg/util/aws/eip.go @@ -113,7 +113,7 @@ func (self *SEipAddress) GetMode() string { func (self *SEipAddress) GetAssociationType() string { // todo : ? - return "server" + return api.EIP_ASSOCIATE_TYPE_SERVER } func (self *SEipAddress) GetAssociationExternalId() string { diff --git a/pkg/util/aws/vpc.go b/pkg/util/aws/vpc.go index eceba1fed1..f1df2c0f2c 100644 --- a/pkg/util/aws/vpc.go +++ b/pkg/util/aws/vpc.go @@ -25,6 +25,7 @@ import ( "yunion.io/x/pkg/util/secrules" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" ) type SUserCIDRs struct { @@ -32,6 +33,8 @@ type SUserCIDRs struct { } type SVpc struct { + multicloud.SVpc + region *SRegion iwires []cloudprovider.ICloudWire diff --git a/pkg/util/azure/classic_eip.go b/pkg/util/azure/classic_eip.go index 1e51968237..3596da76f9 100644 --- a/pkg/util/azure/classic_eip.go +++ b/pkg/util/azure/classic_eip.go @@ -70,7 +70,7 @@ func (self *SClassicEipAddress) GetAssociationExternalId() string { } func (self *SClassicEipAddress) GetAssociationType() string { - return "server" + return api.EIP_ASSOCIATE_TYPE_SERVER } func (self *SClassicEipAddress) GetBandwidth() int { diff --git a/pkg/util/azure/classic_vpc.go b/pkg/util/azure/classic_vpc.go index b0d3be8346..a375f6de67 100644 --- a/pkg/util/azure/classic_vpc.go +++ b/pkg/util/azure/classic_vpc.go @@ -21,6 +21,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" ) type ClassicAddressSpace struct { @@ -42,6 +43,8 @@ type ClassicVpcProperties struct { } type SClassicVpc struct { + multicloud.SVpc + region *SRegion iwires []cloudprovider.ICloudWire diff --git a/pkg/util/azure/eip.go b/pkg/util/azure/eip.go index 9df8eb0041..3475dba0a1 100644 --- a/pkg/util/azure/eip.go +++ b/pkg/util/azure/eip.go @@ -192,7 +192,7 @@ func (self *SEipAddress) GetAssociationExternalId() string { } func (self *SEipAddress) GetAssociationType() string { - return "server" + return api.EIP_ASSOCIATE_TYPE_SERVER } func (self *SEipAddress) GetBandwidth() int { diff --git a/pkg/util/azure/vpc.go b/pkg/util/azure/vpc.go index d5722ed90f..24fe5ac20c 100644 --- a/pkg/util/azure/vpc.go +++ b/pkg/util/azure/vpc.go @@ -20,6 +20,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" ) type AddressSpace struct { @@ -46,6 +47,8 @@ type VirtualNetworkPropertiesFormat struct { } type SVpc struct { + multicloud.SVpc + region *SRegion iwires []cloudprovider.ICloudWire diff --git a/pkg/util/huawei/eip.go b/pkg/util/huawei/eip.go index f6eb28db26..85dd738343 100644 --- a/pkg/util/huawei/eip.go +++ b/pkg/util/huawei/eip.go @@ -156,7 +156,7 @@ func (self *SEipAddress) GetMode() string { } func (self *SEipAddress) GetAssociationType() string { - return "server" + return api.EIP_ASSOCIATE_TYPE_SERVER } func (self *SEipAddress) GetAssociationExternalId() string { diff --git a/pkg/util/huawei/vpc.go b/pkg/util/huawei/vpc.go index 0041cea4c9..ac55623781 100644 --- a/pkg/util/huawei/vpc.go +++ b/pkg/util/huawei/vpc.go @@ -21,10 +21,13 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" ) // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090625.html type SVpc struct { + multicloud.SVpc + region *SRegion iwires []cloudprovider.ICloudWire diff --git a/pkg/util/openstack/eip.go b/pkg/util/openstack/eip.go index 6a6d08ab56..0cefe1e225 100644 --- a/pkg/util/openstack/eip.go +++ b/pkg/util/openstack/eip.go @@ -151,7 +151,7 @@ func (eip *SEipAddress) GetMode() string { } func (eip *SEipAddress) GetAssociationType() string { - return "server" + return api.EIP_ASSOCIATE_TYPE_SERVER } func (eip *SEipAddress) GetAssociationExternalId() string { diff --git a/pkg/util/openstack/vpc.go b/pkg/util/openstack/vpc.go index e81976895a..cb2acbb6cb 100644 --- a/pkg/util/openstack/vpc.go +++ b/pkg/util/openstack/vpc.go @@ -21,6 +21,7 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" ) const ( @@ -31,6 +32,8 @@ const ( ) type SVpc struct { + multicloud.SVpc + region *SRegion iwires []cloudprovider.ICloudWire diff --git a/pkg/util/qcloud/eip.go b/pkg/util/qcloud/eip.go index cb7de2c26f..8b004e07a4 100644 --- a/pkg/util/qcloud/eip.go +++ b/pkg/util/qcloud/eip.go @@ -16,6 +16,7 @@ package qcloud import ( "fmt" + "strings" "time" "yunion.io/x/jsonutils" @@ -133,15 +134,14 @@ func (self *SEipAddress) GetMode() string { } func (self *SEipAddress) GetAssociationType() string { - switch self.AddressType { - case EIP_TYPE_EIP, EIP_TYPE_ANYCASTEIP, EIP_TYPE_WANIP: - return "server" - case EIP_TYPE_CALCIP: - return "server" - default: - log.Fatalf("unsupported type: %s", self.AddressType) - return "unsupported" + if len(self.InstanceId) > 0 { + for prefix, instanceType := range map[string]string{"nat-": api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY, "ins-": api.EIP_ASSOCIATE_TYPE_SERVER} { + if strings.HasPrefix(prefix, self.InstanceId) { + return instanceType + } + } } + return "" } func (self *SEipAddress) GetAssociationExternalId() string { @@ -154,8 +154,10 @@ func (self *SEipAddress) Delete() error { func (self *SEipAddress) GetBandwidth() int { if len(self.InstanceId) > 0 { - if instance, err := self.region.GetInstance(self.InstanceId); err == nil { - return instance.InternetAccessible.InternetMaxBandwidthOut + if strings.HasPrefix(self.InstanceId, "ins-") { + if instance, err := self.region.GetInstance(self.InstanceId); err == nil { + return instance.InternetAccessible.InternetMaxBandwidthOut + } } } return 0 @@ -179,12 +181,14 @@ func (self *SEipAddress) GetExpiredAt() time.Time { func (self *SEipAddress) GetInternetChargeType() string { if len(self.InstanceId) > 0 { - if instance, err := self.region.GetInstance(self.InstanceId); err == nil { - switch instance.InternetAccessible.InternetChargeType { - case InternetChargeTypeTrafficPostpaidByHour: - return api.EIP_CHARGE_TYPE_BY_TRAFFIC - default: - return api.EIP_CHARGE_TYPE_BY_BANDWIDTH + if strings.HasPrefix(self.InstanceId, "ins-") { + if instance, err := self.region.GetInstance(self.InstanceId); err == nil { + switch instance.InternetAccessible.InternetChargeType { + case InternetChargeTypeTrafficPostpaidByHour: + return api.EIP_CHARGE_TYPE_BY_TRAFFIC + default: + return api.EIP_CHARGE_TYPE_BY_BANDWIDTH + } } } } diff --git a/pkg/util/qcloud/natdtable.go b/pkg/util/qcloud/natdtable.go new file mode 100644 index 0000000000..3af59fe0d1 --- /dev/null +++ b/pkg/util/qcloud/natdtable.go @@ -0,0 +1,84 @@ +package qcloud + +import ( + "fmt" + "time" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SDTable struct { + multicloud.SResourceBase + nat *SNatGateway + + Eip string + NatId string + Description string + UniqVpcId string + Proto string + Pport int + Eport int + Owner string + VpcId int + PipType int + Pip string + UniqNatId string + CreateTime time.Time +} + +func (table *SDTable) GetName() string { + if len(table.Description) > 0 { + return table.Description + } + return fmt.Sprintf("%s/%s/%d", table.Eip, table.Proto, table.Eport) +} + +func (table *SDTable) GetId() string { + return fmt.Sprintf("%s/%s/%d", table.NatId, table.Eip, table.Eport) +} + +func (table *SDTable) GetGlobalId() string { + return table.GetId() +} + +func (table *SDTable) GetStatus() string { + return api.NAT_STAUTS_AVAILABLE +} + +func (table *SDTable) GetExternalIp() string { + return table.Eip +} + +func (table *SDTable) GetExternalPort() int { + return table.Eport +} + +func (table *SDTable) GetInternalIp() string { + return table.Pip +} + +func (table *SDTable) GetInternalPort() int { + return table.Pport +} + +func (table *SDTable) GetIpProtocol() string { + return table.Proto +} + +func (region *SRegion) GetDTables(natId, vpcId string) ([]SDTable, error) { + param := map[string]string{} + param["vpcId"] = vpcId + param["natId"] = natId + + body, err := region.vpc2017Request("GetDnaptRule", param) + if err != nil { + return nil, err + } + tables := []SDTable{} + err = body.Unmarshal(&tables, "data", "detail") + if err != nil { + return nil, err + } + return tables, nil +} diff --git a/pkg/util/qcloud/natgateway.go b/pkg/util/qcloud/natgateway.go new file mode 100644 index 0000000000..a4d563cfc4 --- /dev/null +++ b/pkg/util/qcloud/natgateway.go @@ -0,0 +1,130 @@ +package qcloud + +import ( + "fmt" + "time" + + "yunion.io/x/onecloud/pkg/cloudprovider" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SNatGateway struct { + multicloud.SNatGatewayBase + vpc *SVpc + + NatId string `json:"natId"` + NatName string `json:"natName"` + ProductionStatus float32 `json:"productionStatus"` + State float32 `json:"state"` + UnVpcId string `json:"unVpcId"` + VpcId float32 `json:"vpcId"` + VpcName string `json:"vpcName"` + Zone string `json:"zone"` + + Bandwidth float32 `json:"bandwidth"` + CreateTime time.Time `json:"createTime"` + EipCount float32 `json:"eipCount"` + MaxConcurrent float32 `json:"maxConcurrent"` +} + +func (nat *SNatGateway) GetName() string { + if len(nat.NatName) > 0 { + return nat.NatName + } + return nat.NatId +} + +func (nat *SNatGateway) GetId() string { + return nat.NatId +} + +func (nat *SNatGateway) GetGlobalId() string { + return nat.NatId +} + +func (nat *SNatGateway) GetStatus() string { + // NAT网关状态,0:运行中, 1:不可用, 2:欠费停服 + switch int(nat.State) { + case 0: + return api.NAT_STAUTS_AVAILABLE + case 1, 2: + return api.NAT_STATUS_UNKNOWN + default: + return api.NAT_STATUS_UNKNOWN + } +} + +func (nat *SNatGateway) GetNatSpec() string { + switch int(nat.MaxConcurrent) { + case 100 * 10000: + return api.QCLOUD_NAT_SPEC_SMALL + case 300 * 10000: + return api.QCLOUD_NAT_SPEC_MIDDLE + case 1000 * 10000: + return api.QCLOUD_NAT_SPEC_LARGE + } + return "" +} + +func (nat *SNatGateway) GetIEips() ([]cloudprovider.ICloudEIP, error) { + eips := []SEipAddress{} + for { + part, total, err := nat.vpc.region.GetEips("", nat.NatId, len(eips), 50) + if err != nil { + return nil, err + } + eips = append(eips, part...) + if len(eips) >= total { + break + } + } + ieips := []cloudprovider.ICloudEIP{} + for i := 0; i < len(eips); i++ { + eips[i].region = nat.vpc.region + ieips = append(ieips, &eips[i]) + } + return ieips, nil +} + +func (nat *SNatGateway) GetINatSTables() ([]cloudprovider.ICloudNatSTable, error) { + return []cloudprovider.ICloudNatSTable{}, nil +} + +func (nat *SNatGateway) GetINatDTables() ([]cloudprovider.ICloudNatDTable, error) { + tables, err := nat.vpc.region.GetDTables(nat.NatId, nat.vpc.VpcId) + if err != nil { + return nil, err + } + itables := []cloudprovider.ICloudNatDTable{} + for i := 0; i < len(tables); i++ { + tables[i].nat = nat + itables = append(itables, &tables[i]) + } + return itables, nil +} + +func (region *SRegion) GetNatGateways(vpcId string, offset int, limit int) ([]SNatGateway, int, error) { + if limit > 50 || limit <= 0 { + limit = 50 + } + + params := make(map[string]string) + params["Limit"] = fmt.Sprintf("%d", limit) + params["Offset"] = fmt.Sprintf("%d", offset) + if len(vpcId) > 0 { + params["vpcId"] = vpcId + } + body, err := region.vpc2017Request("DescribeNatGateway", params) + if err != nil { + return nil, 0, err + } + nats := []SNatGateway{} + err = body.Unmarshal(&nats, "data") + if err != nil { + return nil, 0, err + } + total, _ := body.Float("totalCount") + return nats, int(total), nil +} diff --git a/pkg/util/qcloud/qcloud.go b/pkg/util/qcloud/qcloud.go index a8424e966e..4f7b02fcc3 100644 --- a/pkg/util/qcloud/qcloud.go +++ b/pkg/util/qcloud/qcloud.go @@ -117,6 +117,12 @@ func wssRequest(client *common.Client, apiName string, params map[string]string, return _phpJsonRequest(client, &wssJsonResponse{}, domain, "/v2/index.php", "", apiName, params, debug) } +// 2017版API +func vpc2017Request(client *common.Client, apiName string, params map[string]string, debug bool) (jsonutils.JSONObject, error) { + domain := "vpc.api.qcloud.com" + return _phpJsonRequest(client, &vpc2017JsonResponse{}, domain, "/v2/index.php", "", apiName, params, debug) +} + func billingRequest(client *common.Client, apiName string, params map[string]string, debug bool) (jsonutils.JSONObject, error) { domain := "billing.tencentcloudapi.com" return _jsonRequest(client, domain, QCLOUD_BILLING_API_VERSION, apiName, params, debug, true) @@ -154,6 +160,40 @@ func (r *phpJsonRequest) GetPath() string { return r.Path } +// 2017vpc专用response +type vpc2017JsonResponse struct { + Code int `json:"code"` + CodeDesc string `json:"codeDesc"` + Message string `json:"message"` + TotalCount int `json:"totalCount"` + Response *interface{} `json:"data"` +} + +func (r *vpc2017JsonResponse) ParseErrorFromHTTPResponse(body []byte) (err error) { + resp := &vpc2017JsonResponse{} + err = json.Unmarshal(body, resp) + if err != nil { + return + } + if resp.Code != 0 { + return errors.NewTencentCloudSDKError(resp.CodeDesc, resp.Message, "") + } + + return nil +} + +func (r *vpc2017JsonResponse) GetResponse() *interface{} { + if r.Response == nil { + result, _ := jsonutils.Parse([]byte(`{"data":[],"totalCount":0}`)) + return func(resp interface{}) *interface{} { + return &resp + }(result) + } + return func(resp interface{}) *interface{} { + return &resp + }(jsonutils.Marshal(r)) +} + // SSL证书专用response type wssJsonResponse struct { Code int `json:"code"` @@ -363,6 +403,14 @@ func (client *SQcloudClient) wssRequest(apiName string, params map[string]string return wssRequest(cli, apiName, params, client.Debug) } +func (client *SQcloudClient) vpc2017Request(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := client.getDefaultClient() + if err != nil { + return nil, err + } + return vpc2017Request(cli, apiName, params, client.Debug) +} + func (client *SQcloudClient) billingRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { cli, err := client.getDefaultClient() if err != nil { diff --git a/pkg/util/qcloud/region.go b/pkg/util/qcloud/region.go index cbd8512221..d204b4757d 100644 --- a/pkg/util/qcloud/region.go +++ b/pkg/util/qcloud/region.go @@ -614,6 +614,11 @@ func (self *SRegion) vpcRequest(apiName string, params map[string]string) (jsonu return self.client.vpcRequest(apiName, params) } +func (self *SRegion) vpc2017Request(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + params["Region"] = self.Region + return self.client.vpc2017Request(apiName, params) +} + func (self *SRegion) cvmRequest(apiName string, params map[string]string, retry bool) (jsonutils.JSONObject, error) { params["Region"] = self.Region return self.client.jsonRequest(apiName, params, retry) diff --git a/pkg/util/qcloud/shell/natdtable.go b/pkg/util/qcloud/shell/natdtable.go new file mode 100644 index 0000000000..8f97fbda1a --- /dev/null +++ b/pkg/util/qcloud/shell/natdtable.go @@ -0,0 +1,35 @@ +// 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 shell + +import ( + "yunion.io/x/onecloud/pkg/util/qcloud" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type NatDTableListOptions struct { + VPCID string `help:"Vpc ID"` + NATID string `help:"Nat ID"` + } + shellutils.R(&NatDTableListOptions{}, "dtable-list", "List nat dtables", func(cli *qcloud.SRegion, args *NatDTableListOptions) error { + tables, err := cli.GetDTables(args.NATID, args.VPCID) + if err != nil { + return err + } + printList(tables, len(tables), 0, 0, []string{}) + return nil + }) +} diff --git a/pkg/util/qcloud/shell/natgateway.go b/pkg/util/qcloud/shell/natgateway.go new file mode 100644 index 0000000000..945152348c --- /dev/null +++ b/pkg/util/qcloud/shell/natgateway.go @@ -0,0 +1,36 @@ +// 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 shell + +import ( + "yunion.io/x/onecloud/pkg/util/qcloud" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type NatGatewayListOptions struct { + VpcId string `help:"Vpc ID"` + Offset int `help:"List offset"` + Limit int `help:"List limit"` + } + shellutils.R(&NatGatewayListOptions{}, "natgateway-list", "List nat gateway", func(cli *qcloud.SRegion, args *NatGatewayListOptions) error { + nats, total, e := cli.GetNatGateways(args.VpcId, args.Offset, args.Limit) + if e != nil { + return e + } + printList(nats, total, args.Offset, args.Limit, []string{}) + return nil + }) +} diff --git a/pkg/util/qcloud/vpc.go b/pkg/util/qcloud/vpc.go index 78881d860a..855bdea06a 100644 --- a/pkg/util/qcloud/vpc.go +++ b/pkg/util/qcloud/vpc.go @@ -21,9 +21,12 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" ) type SVpc struct { + multicloud.SVpc + region *SRegion iwires []cloudprovider.ICloudWire @@ -115,6 +118,26 @@ func (self *SVpc) getWireByZoneId(zoneId string) *SWire { return nil } +func (self *SVpc) GetINatGateways() ([]cloudprovider.ICloudNatGateway, error) { + nats := []SNatGateway{} + for { + part, total, err := self.region.GetNatGateways(self.VpcId, len(nats), 50) + if err != nil { + return nil, err + } + nats = append(nats, part...) + if len(nats) >= total { + break + } + } + inats := []cloudprovider.ICloudNatGateway{} + for i := 0; i < len(nats); i++ { + nats[i].vpc = self + inats = append(inats, &nats[i]) + } + return inats, nil +} + func (self *SVpc) fetchNetworks() error { networks, total, err := self.region.GetNetworks(nil, self.VpcId, 0, 50) if err != nil { diff --git a/pkg/util/ucloud/eip.go b/pkg/util/ucloud/eip.go index d8d8917a86..0a06502d6e 100644 --- a/pkg/util/ucloud/eip.go +++ b/pkg/util/ucloud/eip.go @@ -159,7 +159,7 @@ func (self *SEip) GetMode() string { } func (self *SEip) GetAssociationType() string { - return "server" + return api.EIP_ASSOCIATE_TYPE_SERVER } // 已绑定的资源类型, 枚举值为: uhost, 云主机;natgw:NAT网关;ulb:负载均衡器;upm: 物理机; hadoophost: 大数据集群;fortresshost:堡垒机;udockhost:容器;udhost:私有专区主机;vpngw:IPSec VPN;ucdr:云灾备;dbaudit:数据库审计。 diff --git a/pkg/util/ucloud/vpc.go b/pkg/util/ucloud/vpc.go index 82bee4e77e..035afc1817 100644 --- a/pkg/util/ucloud/vpc.go +++ b/pkg/util/ucloud/vpc.go @@ -21,9 +21,12 @@ import ( "yunion.io/x/jsonutils" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" ) type SVPC struct { + multicloud.SVpc + region *SRegion iwires []cloudprovider.ICloudWire diff --git a/pkg/util/zstack/eip.go b/pkg/util/zstack/eip.go index bdb639107a..0c75fe66dd 100644 --- a/pkg/util/zstack/eip.go +++ b/pkg/util/zstack/eip.go @@ -101,7 +101,7 @@ func (eip *SEipAddress) GetMode() string { } func (eip *SEipAddress) GetAssociationType() string { - return "server" + return api.EIP_ASSOCIATE_TYPE_SERVER } func (eip *SEipAddress) GetAssociationExternalId() string { diff --git a/pkg/util/zstack/vpc.go b/pkg/util/zstack/vpc.go index 17c0a510ac..39278fea84 100644 --- a/pkg/util/zstack/vpc.go +++ b/pkg/util/zstack/vpc.go @@ -19,11 +19,14 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" api "yunion.io/x/onecloud/pkg/apis/compute" ) type SVpc struct { + multicloud.SVpc + region *SRegion iwires []cloudprovider.ICloudWire