Files
cloudpods/pkg/compute/models/guestnetworks.go

1648 lines
48 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"database/sql"
"fmt"
"regexp"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/gotypes"
"yunion.io/x/pkg/util/netutils"
randutil "yunion.io/x/pkg/util/rand"
"yunion.io/x/pkg/util/rbacscope"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/sqlchemy"
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/compute/options"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
const (
MAX_IFNAME_SIZE = 13
MAX_HINT_LEN = MAX_IFNAME_SIZE - 4 // 9
HINT_BASE_LEN = 6 // 6
HINT_RAND_LEN = MAX_HINT_LEN - HINT_BASE_LEN // 3
MAX_GUESTNIC_TO_SAME_NETWORK = 2
)
// +onecloud:swagger-gen-ignore
type SGuestnetworkManager struct {
SGuestJointsManager
SNetworkResourceBaseManager
}
var GuestnetworkManager *SGuestnetworkManager
func init() {
db.InitManager(func() {
GuestnetworkManager = &SGuestnetworkManager{
SGuestJointsManager: NewGuestJointsManager(
SGuestnetwork{},
"guestnetworks_tbl",
"guestnetwork",
"guestnetworks",
NetworkManager,
),
}
GuestnetworkManager.SetVirtualObject(GuestnetworkManager)
GuestnetworkManager.TableSpec().AddIndex(true, "ip_addr", "guest_id", "deleted")
GuestnetworkManager.TableSpec().AddIndex(true, "ip6_addr", "guest_id", "deleted")
GuestnetworkManager.TableSpec().AddIndex(false, "mac_addr", "deleted")
})
}
// +onecloud:model-api-gen
type SGuestnetwork struct {
SGuestJointsBase
NetworkId string `width:"36" charset:"ascii" nullable:"false" list:"user" `
// MAC地址
MacAddr string `width:"32" charset:"ascii" nullable:"false" list:"user" index:"true"`
// IPv4地址
IpAddr string `width:"16" charset:"ascii" nullable:"true" list:"user"`
// IPv6地址
Ip6Addr string `width:"64" charset:"ascii" nullable:"true" list:"user"`
// 虚拟网卡驱动
Driver string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user"`
// 网卡队列数
NumQueues int `nullable:"true" default:"1" list:"user" update:"user"`
// 带宽限制单位mbps
BwLimit int `nullable:"false" default:"0" list:"user"`
// 下行流量限制,单位 bytes
RxTrafficLimit int64 `nullable:"false" default:"0" list:"user"`
RxTrafficUsed int64 `nullable:"false" default:"0" list:"user"`
// 上行流量限制,单位 bytes
TxTrafficLimit int64 `nullable:"false" default:"0" list:"user"`
TxTrafficUsed int64 `nullable:"false" default:"0" list:"user"`
// 网卡序号
Index int `nullable:"false" default:"0" list:"user" update:"user"`
// 是否为虚拟接口无IP
Virtual bool `default:"false" list:"user"`
// 虚拟网卡设备名称
Ifname string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user"`
// bind配对网卡MAC地址
TeamWith string `width:"32" charset:"ascii" nullable:"false" list:"user"`
// IPv4映射地址当子网属于私有云vpc的时候分配用于访问外网
MappedIpAddr string `width:"16" charset:"ascii" nullable:"true" list:"user"`
// IPv6映射地址当子网属于私有云vpc的时候分配用于访问外网
MappedIp6Addr string `width:"64" charset:"ascii" nullable:"true" list:"user"`
// 网卡关联的Eip实例
EipId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
// 是否为缺省路由
IsDefault bool `default:"false" list:"user"`
// 端口映射
PortMappings api.GuestPortMappings `length:"long" list:"user" update:"user"`
SBillingTypeBase `billing_type->default:"prepaid"`
SBillingChargeTypeBase `charge_type->default:"bandwidth"`
// 下行带宽限制单位mbps
RxBwLimit int `nullable:"false" default:"0" list:"user"`
// 上行带宽限制单位mbps
TxBwLimit int `nullable:"false" default:"0" list:"user"`
}
func (gn SGuestnetwork) GetIP() string {
return gn.IpAddr
}
func (gn SGuestnetwork) GetMAC() string {
return gn.MacAddr
}
func (manager *SGuestnetworkManager) GetSlaveFieldName() string {
return "network_id"
}
func (manager *SGuestnetworkManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.GuestnetworkDetails {
rows := make([]api.GuestnetworkDetails, len(objs))
guestRows := manager.SGuestJointsManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
netIds := make([]string, len(rows))
eipIds := make([]string, len(rows))
for i := range rows {
rows[i] = api.GuestnetworkDetails{
GuestJointResourceDetails: guestRows[i],
}
netIds[i] = objs[i].(*SGuestnetwork).NetworkId
eipIds[i] = objs[i].(*SGuestnetwork).EipId
ipnets, err := NetworkAddressManager.fetchAddressesByGuestnetworkId(objs[i].(*SGuestnetwork).RowId)
if err != nil {
log.Errorf("NetworkAddressManager.fetchAddressesByGuestnetworkId %s", err)
} else if len(ipnets) > 0 {
rows[i].NetworkAddresses = ipnets
}
}
netMap := make(map[string]SNetwork)
err := db.FetchModelObjectsByIds(NetworkManager, "id", netIds, netMap)
if err != nil {
log.Errorf("FetchIdNameMap2 fail %s", err)
return rows
}
for i := range rows {
if net, ok := netMap[netIds[i]]; ok {
rows[i].Network = net.Name
rows[i].WireId = net.WireId
rows[i].GuestIpMask = net.GuestIpMask
rows[i].GuestGateway = net.GuestGateway
rows[i].GuestIp6Mask = net.GuestIp6Mask
rows[i].GuestGateway6 = net.GuestGateway6
}
}
eipIdMaps, err := db.FetchIdFieldMap2(ElasticipManager, "ip_addr", eipIds)
if err != nil {
log.Errorf("FetchIdFieldMap2 fail %s", err)
return rows
}
for i := range rows {
if ip, ok := eipIdMaps[eipIds[i]]; ok {
rows[i].EipAddr = ip
}
}
return rows
}
func (manager *SGuestnetworkManager) fetchByRowIds(
ctx context.Context,
userCred mcclient.TokenCredential,
rowIds []int64,
) ([]SGuestnetwork, error) {
var gns []SGuestnetwork
q := manager.Query().In("row_id", rowIds)
if err := db.FetchModelObjects(manager, q, &gns); err != nil {
return nil, errors.Wrapf(err, "fetch guestnetworks by row_id list")
}
idmap := map[int64]int{}
for i := range gns {
idmap[gns[i].RowId] = i
}
ret := make([]SGuestnetwork, len(rowIds))
for i, rowId := range rowIds {
if j, ok := idmap[rowId]; ok {
ret[i] = gns[j]
} else {
return nil, errors.Wrapf(errors.ErrNotFound, "guestnetwork row %d", rowId)
}
}
return ret, nil
}
func (manager *SGuestnetworkManager) fetchByRowId(
ctx context.Context,
userCred mcclient.TokenCredential,
rowId int64,
) (*SGuestnetwork, error) {
gns, err := manager.fetchByRowIds(ctx, userCred, []int64{rowId})
if err != nil {
return nil, err
}
if len(gns) != 1 {
return nil, errors.Errorf("row_id %d: got %d guesnetwork entries", rowId, len(gns))
}
return &gns[0], nil
}
func (manager *SGuestnetworkManager) GenerateMac(suggestion string) (string, error) {
return generateMac(suggestion)
}
func (manager *SGuestnetworkManager) FilterByMac(mac string) *sqlchemy.SQuery {
return manager.Query().Equals("mac_addr", mac)
}
type newGuestNetworkArgs struct {
guest *SGuest
network *SNetwork
index int
ipAddr string
allocDir api.IPAllocationDirection
tryReserved bool
requireDesignatedIP bool
useDesignatedIP bool
ip6Addr string
requireIPv6 bool
strictIPv6 bool
// 是否为缺省路由
isDefault bool
ifname string
macAddr string
bwLimit int
rxBwLimit int
txBwLimit int
nicDriver string
numQueues int
teamWithMac string
rxTrafficLimit int64
txTrafficLimit int64
virtual bool
portMappings api.GuestPortMappings
billingType billing_api.TBillingType
chargeType billing_api.TNetChargeType
}
func (manager *SGuestnetworkManager) newGuestNetwork(
ctx context.Context,
userCred mcclient.TokenCredential,
args newGuestNetworkArgs,
) (*SGuestnetwork, error) {
gn := SGuestnetwork{}
gn.SetModelManager(GuestnetworkManager, &gn)
var (
guest = args.guest
network = args.network
index = args.index
address = args.ipAddr
mac = args.macAddr
driver = args.nicDriver
numQueues = args.numQueues
bwLimit = args.bwLimit
rxBwLimit = args.rxBwLimit
txBwLimit = args.txBwLimit
virtual = args.virtual
reserved = args.tryReserved
allocDir = args.allocDir
requiredDesignatedIp = args.requireDesignatedIP
reUseAddr = args.useDesignatedIP
ifname = args.ifname
teamWithMac = args.teamWithMac
isDefault = args.isDefault
address6 = args.ip6Addr
requireIPv6 = args.requireIPv6
strictIPv6 = args.strictIPv6
)
gn.GuestId = guest.Id
gn.NetworkId = network.Id
gn.Index = index
gn.Virtual = virtual
if len(driver) == 0 {
driver = api.NETWORK_DRIVER_VIRTIO
}
gn.Driver = driver
gn.NumQueues = numQueues
gn.RxTrafficLimit = args.rxTrafficLimit
gn.TxTrafficLimit = args.txTrafficLimit
if bwLimit >= 0 {
gn.BwLimit = bwLimit
}
if rxBwLimit >= 0 {
gn.RxBwLimit = rxBwLimit
}
if txBwLimit >= 0 {
gn.TxBwLimit = txBwLimit
}
gn.PortMappings = args.portMappings
gn.BillingType = args.billingType
gn.ChargeType = args.chargeType
lockman.LockObject(ctx, network)
defer lockman.ReleaseObject(ctx, network)
vpc, _ := network.GetVpc()
if vpc == nil {
return nil, fmt.Errorf("cannot find vpc of network %s(%s)", network.Id, network.Name)
}
provider := vpc.GetProviderName()
if !virtual && !strictIPv6 && network.IsSupportIPv4() {
if len(address) > 0 && reUseAddr {
ipAddr, err := netutils.NewIPV4Addr(address)
if err != nil {
return nil, errors.Wrapf(err, "Reuse invalid address %s", address)
}
if !network.IsAddressInRange(ipAddr) {
return nil, errors.Wrapf(httperrors.ErrOutOfRange, "%s not in network address range", address)
}
// if reuse Ip address, no need to check address availability
// assign it anyway
gn.IpAddr = address
} else {
// 如果是不具备IPAM能力的平台主要是OneCloud和VMware也就是VPC为ONECLOUD的平台 provider == api.CLOUD_PROVIDER_ONECLOUD 则需要分配IP地址
// 如果是其他云平台具体IPAM能力的平台
// * 开启options.Options.EnablePreAllocateIpAddr也就是把IPAM的任务交给平台的则需要分配IP地址
// * IP地址为空并且 !options.Options.EnablePreAllocateIpAddr 时不需要分配IP等创建后自动同步过来
// * 否则,还是需要先分配了
if provider == api.CLOUD_PROVIDER_ONECLOUD || options.Options.EnablePreAllocateIpAddr || (!options.Options.EnablePreAllocateIpAddr && len(address) > 0) {
addrTable := network.GetUsedAddresses(ctx)
recentAddrTable := manager.getRecentlyReleasedIPAddresses(network.Id, network.getAllocTimoutDuration())
ipAddr, err := network.GetFreeIP(ctx, userCred, addrTable, recentAddrTable, address, allocDir, reserved, api.AddressTypeIPv4)
if err != nil {
return nil, errors.Wrap(err, "GetFreeIPv4")
}
if len(address) > 0 && ipAddr != address && requiredDesignatedIp {
usedAddr, _ := network.GetUsedAddressDetails(ctx, address)
return nil, errors.Wrapf(httperrors.ErrConflict, "candidate ip %s is occupied with %#v!", address, usedAddr)
}
gn.IpAddr = ipAddr
}
}
}
var err error
if ipBindMac := NetworkIpMacManager.GetMacFromIp(network.Id, gn.IpAddr); ipBindMac != "" {
gn.MacAddr = ipBindMac
} else {
gn.MacAddr, err = manager.GenerateMac(mac)
if err != nil {
return nil, err
}
}
if len(gn.MacAddr) == 0 {
log.Errorf("Mac address generate fails")
return nil, fmt.Errorf("mac address generate fails")
}
// assign ipv6 address
if !virtual {
if len(address6) > 0 || (requireIPv6 || len(gn.IpAddr) == 0) {
if provider == api.CLOUD_PROVIDER_ONECLOUD || options.Options.EnablePreAllocateIpAddr || (!options.Options.EnablePreAllocateIpAddr && len(address6) > 0) {
addrTable := network.GetUsedAddresses6(ctx)
recentAddrTable := manager.getRecentlyReleasedIPAddresses6(network.Id, network.getAllocTimoutDuration())
derived := false
if len(address6) == 0 {
// try to derive ipv6 address from mac and ipv4 address
deriveAddr6 := netutils.DeriveIPv6AddrFromIPv4AddrMac(gn.IpAddr, gn.MacAddr, network.GuestIp6Start, network.GuestIp6End, network.GuestIp6Mask)
if !isIpUsed(deriveAddr6, addrTable, recentAddrTable) {
address6 = deriveAddr6
derived = true
}
}
ip6Addr, err := network.GetFreeIP(ctx, userCred, addrTable, recentAddrTable, address6, allocDir, reserved, api.AddressTypeIPv6)
if err != nil {
return nil, errors.Wrap(err, "GetFreeIPv6")
}
if len(address6) > 0 && ip6Addr != address6 && !derived && requiredDesignatedIp {
usedAddr, _ := network.GetUsedAddressDetails(ctx, address6)
return nil, errors.Wrapf(httperrors.ErrConflict, "candidate v6 ip %s is occupied with %#v!", address6, usedAddr)
}
gn.Ip6Addr = ip6Addr
}
}
}
if vpc.Id != api.DEFAULT_VPC_ID && provider == api.CLOUD_PROVIDER_ONECLOUD && (len(gn.IpAddr) > 0 || len(gn.Ip6Addr) > 0) {
var err error
GuestnetworkManager.lockAllocMappedAddr(ctx)
defer GuestnetworkManager.unlockAllocMappedAddr(ctx)
gn.MappedIpAddr, err = GuestnetworkManager.allocMappedIpAddr(ctx)
if err != nil {
return nil, errors.Wrap(err, "GuestnetworkManager.allocMappedIpAddr")
}
gn.MappedIp6Addr = api.GenVpcMappedIP6(gn.MappedIpAddr)
}
ifname, err = gn.checkOrAllocateIfname(network, ifname)
if err != nil {
return nil, err
}
gn.Ifname = ifname
gn.TeamWith = teamWithMac
if isDefault && ((len(gn.IpAddr) > 0 && len(network.GuestGateway) > 0) || (len(gn.Ip6Addr) > 0 && len(network.GuestGateway6) > 0)) {
gn.IsDefault = isDefault
}
if len(gn.Ip6Addr) > 0 && len(gn.IpAddr) == 0 {
gn.NumQueues = 1
}
err = manager.TableSpec().Insert(ctx, &gn)
if err != nil {
return nil, err
}
return &gn, nil
}
func (gn *SGuestnetwork) generateIfname(network *SNetwork, virtual bool, randomized bool) string {
// It may happen that external networks when synced can miss ifname hint
network.ensureIfnameHint()
pattern := regexp.MustCompile(`\W+`)
nName := pattern.ReplaceAllString(network.IfnameHint, "")
if len(nName) > MAX_IFNAME_SIZE-4 {
nName = nName[:(MAX_IFNAME_SIZE - 4)]
}
if virtual {
return fmt.Sprintf("%s-%s", nName, randutil.String(3))
} else {
ip, _ := netutils.NewIPV4Addr(gn.IpAddr)
cliaddr := ip.CliAddr(network.GuestIpMask)
return fmt.Sprintf("%s-%d", nName, uint32(cliaddr))
}
}
func (man *SGuestnetworkManager) ifnameUsed(ifname string) bool {
// inviable names are always used
if ifname == "" {
return true
}
if len(ifname) > MAX_IFNAME_SIZE {
return true
}
isa := func(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
if !isa(ifname[0]) {
return true
}
for i := range ifname[1:] {
c := ifname[i]
if isa(c) || c >= '0' || c <= '9' || c == '_' || c == '-' {
continue
}
return true
}
count, err := GuestnetworkManager.Query().Equals("ifname", ifname).CountWithError()
if err != nil {
panic(errors.Wrap(err, "query if ifname is used"))
}
return count > 0
}
func (gn *SGuestnetwork) checkOrAllocateIfname(network *SNetwork, preferIfname string) (string, error) {
man := GuestnetworkManager
if !man.ifnameUsed(preferIfname) {
return preferIfname, nil
}
ifname := gn.generateIfname(network, gn.Virtual, false)
if !man.ifnameUsed(ifname) {
return ifname, nil
}
if !gn.Virtual {
ifname = gn.generateIfname(network, true, false)
}
found := false
for i := 0; i < 5; i++ {
if !man.ifnameUsed(ifname) {
found = true
break
}
ifname = gn.generateIfname(network, true, true)
}
if !found {
return "", httperrors.NewConflictError("cannot allocate ifname")
}
return ifname, nil
}
func (gn *SGuestnetwork) GetGuest() *SGuest {
guest, _ := GuestManager.FetchById(gn.GuestId)
if guest != nil {
return guest.(*SGuest)
}
return nil
}
func (gn *SGuestnetwork) GetNetwork() (*SNetwork, error) {
net, err := NetworkManager.FetchById(gn.NetworkId)
if err != nil {
return nil, errors.Wrapf(err, "FetchById %s", gn.NetworkId)
}
return net.(*SNetwork), nil
}
func (gn *SGuestnetwork) GetTeamGuestnetwork() (*SGuestnetwork, error) {
if len(gn.TeamWith) > 0 {
return GuestnetworkManager.FetchByIdsAndIpMac(gn.GuestId, gn.NetworkId, "", gn.TeamWith)
}
return nil, nil
}
func (gn *SGuestnetwork) getJsonDescAtBaremetal(host *SHost) *api.GuestnetworkJsonDesc {
net, _ := gn.GetNetwork()
netif := guestGetHostNetifFromNetwork(host, net)
if netif == nil {
log.Errorf("fail to find a valid net interface on baremetal %s for network %s", host.String(), net.String())
}
return gn.getJsonDescHostwire(netif)
}
func guestGetHostNetifFromNetwork(host *SHost, network *SNetwork) *SNetInterface {
netifs := host.getNetifsOnWire(network.WireId)
var netif *SNetInterface
for i := range netifs {
if netifs[i].NicType != api.NIC_TYPE_IPMI {
if netif == nil || netif.NicType != api.NIC_TYPE_ADMIN {
netif = &netifs[i]
}
}
}
return netif
}
func (gn *SGuestnetwork) getJsonDescAtHost(ctx context.Context, host *SHost) *api.GuestnetworkJsonDesc {
var (
ret *api.GuestnetworkJsonDesc = nil
network, _ = gn.GetNetwork()
)
if network.isOneCloudVpcNetwork() {
// onecloud vpc nic
ret = gn.getJsonDescOneCloudVpc(network)
} else if network.WireId == api.DEFAULT_HOST_LOCAL_WIRE_ID {
// host local nic
ret = gn.getJsonDescHostLocal(network)
} else {
// classic nic
netifs := host.getNetifsOnWire(network.WireId)
var netif *SNetInterface
for i := range netifs {
if len(netifs[i].Bridge) > 0 {
netif = &netifs[i]
break
}
}
if netif == nil && len(netifs) > 0 {
log.Errorf("fail to find a bridged net_interface on host %s for network %s?????", host.String(), network.String())
netif = &netifs[0]
}
ret = gn.getJsonDescHostwire(netif)
}
{
ipnets, err := NetworkAddressManager.fetchAddressesByGuestnetworkId(gn.RowId)
if err != nil {
log.Errorln(err)
}
if len(ipnets) > 0 {
ret.Networkaddresses = jsonutils.Marshal(ipnets)
}
}
return ret
}
func (gn *SGuestnetwork) getJsonDescHostwire(netif *SNetInterface) *api.GuestnetworkJsonDesc {
desc := gn.getJsonDesc()
if netif != nil {
desc.Bridge = netif.Bridge
desc.WireId = netif.WireId
desc.Interface = netif.Interface
}
return desc
}
func (gn *SGuestnetwork) getJsonDescOneCloudVpc(network *SNetwork) *api.GuestnetworkJsonDesc {
if gn.MappedIpAddr == "" {
var (
err error
addr string
)
addr, err = GuestnetworkManager.allocMappedIpAddr(context.TODO())
if err != nil {
log.Errorf("getJsonDescOneCloudVpc: row %d: alloc mapped ipaddr: %v", gn.RowId, err)
} else {
if _, err := db.Update(gn, func() error {
gn.MappedIpAddr = addr
gn.MappedIp6Addr = api.GenVpcMappedIP6(addr)
return nil
}); err != nil {
log.Errorf("getJsonDescOneCloudVpc: row %d: db update mapped addr: %v", gn.RowId, err)
gn.MappedIpAddr = ""
gn.MappedIp6Addr = ""
}
}
} else if gn.MappedIp6Addr == "" {
if _, err := db.Update(gn, func() error {
gn.MappedIp6Addr = api.GenVpcMappedIP6(gn.MappedIpAddr)
return nil
}); err != nil {
log.Errorf("getJsonDescOneCloudVpc for v6: row %d: db update mapped addr6: %v", gn.RowId, err)
gn.MappedIp6Addr = ""
}
}
desc := gn.getJsonDesc()
vpc, _ := network.GetVpc()
desc.Vpc.Id = vpc.Id
desc.Vpc.Provider = api.VPC_PROVIDER_OVN
desc.Vpc.MappedIpAddr = gn.MappedIpAddr
desc.Vpc.MappedIp6Addr = gn.MappedIp6Addr
return desc
}
func (gn *SGuestnetwork) getJsonDescHostLocal(network *SNetwork) *api.GuestnetworkJsonDesc {
desc := gn.getJsonDesc()
desc.Bridge = api.HostLocalBridge
desc.WireId = api.DEFAULT_HOST_LOCAL_WIRE_ID
return desc
}
func (gn *SGuestnetwork) getJsonDesc() *api.GuestnetworkJsonDesc {
net, _ := gn.GetNetwork()
var wire *SWire
if net != nil {
wire, _ = net.GetWire()
}
desc := &api.GuestnetworkJsonDesc{
GuestnetworkBaseDesc: api.GuestnetworkBaseDesc{
Net: net.Name,
NetId: gn.NetworkId,
Mac: gn.MacAddr,
Virtual: gn.Virtual,
IsDefault: gn.IsDefault,
PortMappings: gn.PortMappings,
},
}
if gn.Virtual {
if len(gn.TeamWith) > 0 {
teamGN, _ := gn.GetTeamGuestnetwork()
if teamGN != nil {
desc.Ip = teamGN.IpAddr
}
} else {
desc.Ip = net.GetNetAddr().String()
}
} else {
desc.Ip = gn.IpAddr
}
desc.Gateway = net.GuestGateway
desc.Dns = net.GetDNS("")
desc.Domain = net.GetDomain()
desc.Ntp = net.GetNTP()
desc.Ip6 = gn.Ip6Addr
desc.Masklen6 = net.GuestIp6Mask
desc.Gateway6 = net.GuestGateway6
routes := net.GetRoutes()
if routes != nil && len(routes) > 0 {
desc.Routes = jsonutils.Marshal(routes)
}
desc.Masklen = net.GuestIpMask
desc.Driver = gn.Driver
desc.NumQueues = gn.NumQueues
desc.RxTrafficLimit = gn.RxTrafficLimit
desc.TxTrafficLimit = gn.TxTrafficLimit
desc.Vlan = net.VlanId
desc.Bw = gn.getBandwidth(net, wire)
desc.RxBwLimit = gn.getRxBwLimit(net, wire)
desc.TxBwLimit = gn.getTxBwLimit(net, wire)
desc.Mtu = gn.getMtu(net, wire)
desc.Index = gn.Index
desc.VirtualIps = gn.GetVirtualIPs()
desc.ExternalId = net.ExternalId
desc.TeamWith = gn.TeamWith
desc.BillingType = gn.BillingType
desc.ChargeType = gn.ChargeType
guest := gn.getGuest()
if ifname, ok := gn.OvsOffloadIfname(); ok {
desc.Ifname = ifname
} else {
desc.Ifname = gn.Ifname
}
if guest.GetHypervisor() != api.HYPERVISOR_KVM {
manual := true
desc.Manual = &manual
} else {
if gn.Driver == api.NETWORK_DRIVER_VFIO {
dev, _ := gn.GetIsolatedDevice()
if dev != nil {
if dev.OvsOffloadInterface == "" {
manual := true
desc.Manual = &manual
}
if dev.IsInfinibandNic {
desc.NicType = api.NIC_TYPE_INFINIBAND
}
}
}
}
if options.Options.NetworkAlwaysManualConfig {
manual := true
desc.Manual = &manual
}
return desc
}
func (gn *SGuestnetwork) getSecgroupDesc() *api.GuestnetworkSecgroupDesc {
secgroupJson, _ := GuestnetworksecgroupManager.getNetworkSecgroupJson(gn.GuestId, gn.Index)
if len(secgroupJson) == 0 {
return nil
}
guest := gn.GetGuest()
securityRules := guest.getNetworkSecurityGroupsRules(gn.Index)
return &api.GuestnetworkSecgroupDesc{
Secgroups: secgroupJson,
SecurityRules: securityRules,
Index: gn.Index,
Mac: gn.MacAddr,
}
}
func (gn *SGuestnetwork) IsSriovWithoutOffload() bool {
if gn.Driver != api.NETWORK_DRIVER_VFIO {
return false
}
if dev, _ := gn.GetIsolatedDevice(); dev != nil && dev.OvsOffloadInterface != "" {
return false
}
return true
}
func (gn *SGuestnetwork) OvsOffloadIfname() (string, bool) {
if gn.Driver != api.NETWORK_DRIVER_VFIO {
return "", false
}
if dev, _ := gn.GetIsolatedDevice(); dev != nil && dev.OvsOffloadInterface != "" {
return dev.OvsOffloadInterface, true
}
return "", false
}
func (gn *SGuestnetwork) UpdateNicTrafficUsed(ctx context.Context, guest *SGuest, matrics *api.SNicTrafficRecord, tm time.Time, isReset bool) error {
if gn.BillingType == billing_api.BILLING_TYPE_POSTPAID && gn.ChargeType == billing_api.NET_CHARGE_TYPE_BY_TRAFFIC {
err := GuestNetworkTrafficLogManager.logTraffic(ctx, guest, gn, matrics, tm, isReset)
if err != nil {
return errors.Wrap(err, "log traffic")
}
}
_, err := db.Update(gn, func() error {
gn.RxTrafficUsed = matrics.RxTraffic
gn.TxTrafficUsed = matrics.TxTraffic
return nil
})
return errors.Wrap(err, "update guestnetwork traffic used")
}
func (gn *SGuestnetwork) UpdateBillingMode(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerNicTrafficLimit) error {
billingChange := false
if len(input.BillingType) > 0 && input.BillingType != gn.BillingType {
billingChange = true
}
if len(input.ChargeType) > 0 && input.ChargeType != gn.ChargeType {
billingChange = true
}
var oldDesc *jsonutils.JSONDict
if billingChange {
oldDesc = gn.GetShortDesc(ctx)
}
_, err := db.Update(gn, func() error {
if input.RxTrafficLimit != nil {
gn.RxTrafficLimit = *input.RxTrafficLimit
}
if input.TxTrafficLimit != nil {
gn.TxTrafficLimit = *input.TxTrafficLimit
}
if len(input.BillingType) > 0 && input.BillingType != gn.BillingType {
gn.BillingType = input.BillingType
}
if len(input.ChargeType) > 0 && input.ChargeType != gn.ChargeType {
gn.ChargeType = input.ChargeType
}
return nil
})
if err != nil {
return errors.Wrap(err, "update guestnetwork billing mode")
}
if billingChange {
guest := gn.GetGuest()
net, _ := gn.GetNetwork()
db.OpsLog.LogDetachEvent(ctx, guest, net, userCred, oldDesc)
db.OpsLog.LogAttachEvent(ctx, guest, net, userCred, gn.GetShortDesc(ctx))
}
return nil
}
func (gn *SGuestnetwork) UpdatePortMappings(pms api.GuestPortMappings) error {
_, err := db.Update(gn, func() error {
gn.PortMappings = pms
return nil
})
return err
}
func (manager *SGuestnetworkManager) GetGuestByAddress(address string, projectId string) *SGuest {
gnQ := manager.Query()
ipField := "ip_addr"
if regutils.MatchIP6Addr(address) {
ipField = "ip6_addr"
}
gnQ = gnQ.Equals(ipField, address)
gnSubQ := gnQ.SubQuery()
q := GuestManager.Query("hostname", "tenant_id")
if len(projectId) > 0 {
q = q.Equals("tenant_id", projectId)
}
q = q.Join(gnSubQ, sqlchemy.Equals(gnSubQ.Field("guest_id"), q.Field("id")))
guests := make([]SGuest, 0)
err := q.All(&guests)
if err != nil {
log.Errorf("GetGuestByAddress %s fail %s", address, err)
return nil
}
if len(guests) == 0 {
return nil
}
return &guests[0]
}
func (gn *SGuestnetwork) GetDetailedString() string {
network, err := gn.GetNetwork()
if err != nil {
return ""
}
naCount, _ := NetworkAddressManager.fetchAddressCountByGuestnetworkId(gn.RowId)
parts := []string{
gn.IpAddr, fmt.Sprintf("%d", network.GuestIpMask),
}
if len(gn.Ip6Addr) > 0 {
parts = append(parts, gn.Ip6Addr, fmt.Sprintf("%d", network.GuestIp6Mask))
}
parts = append(parts,
gn.MacAddr,
fmt.Sprintf("%d", network.VlanId),
network.Name,
gn.Driver,
fmt.Sprintf("%d", gn.getBandwidth(network, nil)),
fmt.Sprintf("rx%d", gn.getRxBwLimit(network, nil)),
fmt.Sprintf("tx%d", gn.getTxBwLimit(network, nil)),
fmt.Sprintf("%d", naCount),
)
return fmt.Sprintf("eth%d:%s", gn.Index, strings.Join(parts, "/"))
}
func (gn *SGuestnetwork) ValidateUpdateData(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input api.GuestnetworkUpdateInput,
) (api.GuestnetworkUpdateInput, error) {
if input.Index != nil {
index := *input.Index
q := GuestnetworkManager.Query().SubQuery()
count, err := q.Query().Filter(sqlchemy.Equals(q.Field("guest_id"), gn.GuestId)).
Filter(sqlchemy.NotEquals(q.Field("network_id"), gn.NetworkId)).
Filter(sqlchemy.Equals(q.Field("index"), index)).CountWithError()
if err != nil {
return input, httperrors.NewInternalServerError("check NIC index uniqueness failed %s", err)
}
if count > 0 {
return input, httperrors.NewDuplicateResourceError("NIC Index %d has been occupied", index)
}
}
if input.IsDefault != nil && *input.IsDefault {
if gn.Virtual || len(gn.TeamWith) > 0 {
return input, errors.Wrap(httperrors.ErrInvalidStatus, "cannot set virtual/slave interface as default")
}
net, err := gn.GetNetwork()
if err != nil {
return input, errors.Wrapf(err, "GetNetwork")
}
if len(net.GuestGateway) == 0 && len(net.GuestGateway6) == 0 {
return input, errors.Wrap(httperrors.ErrInvalidStatus, "network of default gateway has no gateway")
}
if (len(gn.IpAddr) == 0 || (len(gn.IpAddr) > 0 && len(net.GuestGateway) == 0)) && (len(gn.Ip6Addr) == 0 || (len(gn.Ip6Addr) > 0 && len(net.GuestGateway6) == 0)) {
return input, errors.Wrap(httperrors.ErrInvalidStatus, "nic of default gateway has no ip")
}
}
for _, pm := range input.PortMappings {
if err := validatePortMapping(pm); err != nil {
return input, err
}
}
var err error
input.GuestJointBaseUpdateInput, err = gn.SGuestJointsBase.ValidateUpdateData(ctx, userCred, query, input.GuestJointBaseUpdateInput)
if err != nil {
return input, errors.Wrap(err, "SGuestJointsBase.ValidateUpdateData")
}
return input, nil
}
func (gn *SGuestnetwork) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query, data jsonutils.JSONObject) {
gn.SGuestJointsBase.PostUpdate(ctx, userCred, query, data)
input := api.GuestnetworkUpdateInput{}
err := data.Unmarshal(&input)
if err != nil {
log.Errorf("GuestnetworkUpdateInput unmarshal fail %s", err)
return
}
if input.IsDefault != nil && *input.IsDefault {
// make this nic as default, unset others
guest := gn.GetGuest()
err := guest.setDefaultGateway(ctx, userCred, gn.MacAddr)
if err != nil {
log.Errorf("fail to setDefaultGateway: %s", err)
}
} else {
// try fix default gateway
guest := gn.GetGuest()
err := guest.fixDefaultGateway(ctx, userCred)
if err != nil {
log.Errorf("fail to fixDefaultGateway %s", err)
}
}
}
func (manager *SGuestnetworkManager) DeleteGuestNics(ctx context.Context, userCred mcclient.TokenCredential, gns []SGuestnetwork, reserve bool) error {
for i := range gns {
gn := gns[i]
if len(gn.EipId) > 0 {
return errors.Wrapf(httperrors.ErrInvalidStatus, "eip associate with %s", gn.IpAddr)
}
guest := gn.GetGuest()
dev, err := guest.GetGuestIsolatedDeviceByNetworkIndex(gn.Index)
if err != nil {
return errors.Wrap(err, "GetIsolatedDeviceByNetworkIndex")
}
if dev != nil {
err = guest.detachIsolateDevice(ctx, userCred, dev)
if err != nil {
return errors.Wrapf(err, "detachIsolateDevice %s", dev.GetName())
}
}
net, _ := gn.GetNetwork()
if !gotypes.IsNil(net) && (regutils.MatchIP4Addr(gn.IpAddr) || regutils.MatchIP6Addr(gn.Ip6Addr)) {
net.updateDnsRecord(&gn, false)
if regutils.MatchIP4Addr(gn.IpAddr) {
// ??
// netman.get_manager().netmap_remove_node(gn.ip_addr)
}
}
err = gn.Delete(ctx, userCred)
if err != nil {
log.Errorf("guest network %s delete fail %s", gn.GetDetailedString(), err)
return errors.Wrapf(err, "Delete %s", gn.GetDetailedString())
}
gn.LogDetachEvent(ctx, userCred, guest, net)
if !gotypes.IsNil(net) {
if reserve && regutils.MatchIP4Addr(gn.IpAddr) {
ReservedipManager.ReserveIP(ctx, userCred, net, gn.IpAddr, "Delete to reserve", api.AddressTypeIPv4)
}
if reserve && regutils.MatchIP6Addr(gn.Ip6Addr) {
ReservedipManager.ReserveIP(ctx, userCred, net, gn.Ip6Addr, "Delete to reserve", api.AddressTypeIPv6)
}
}
}
return nil
}
func (manager *SGuestnetworkManager) getGuestNicByIP(ip string, networkId string, addrType api.TAddressType) (*SGuestnetwork, error) {
gn := SGuestnetwork{}
q := manager.Query()
field := "ip_addr"
if addrType == api.AddressTypeIPv6 {
field = "ip6_addr"
}
q = q.Equals(field, ip).Equals("network_id", networkId)
err := q.First(&gn)
if err != nil {
if err != sql.ErrNoRows {
log.Errorf("getGuestNicByIP fail %s", err)
return nil, err
}
return nil, nil
}
gn.SetModelManager(manager, &gn)
return &gn, nil
}
func (gn *SGuestnetwork) LogDetachEvent(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, network *SNetwork) {
if network == nil {
netTmp, err := NetworkManager.FetchById(gn.NetworkId)
if err != nil {
return
}
network = netTmp.(*SNetwork)
}
db.OpsLog.LogDetachEvent(ctx, guest, network, userCred, gn.GetShortDesc(ctx))
}
func (gn *SGuestnetwork) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
if err := NetworkAddressManager.deleteByGuestnetworkId(ctx, userCred, gn.RowId); err != nil {
return errors.Wrap(err, "delete attached network addresses")
}
return db.DeleteModel(ctx, userCred, gn)
}
func (gn *SGuestnetwork) Detach(ctx context.Context, userCred mcclient.TokenCredential) error {
return db.DetachJoint(ctx, userCred, gn)
}
func totalGuestNicCount(
scope rbacscope.TRbacScope,
ownerId mcclient.IIdentityProvider,
rangeObjs []db.IStandaloneModel,
includeSystem bool,
providers []string,
brands []string,
cloudEnv string,
) GuestnicsCount {
guests := GuestManager.Query().SubQuery()
hosts := HostManager.Query().SubQuery()
guestnics := GuestnetworkManager.Query().SubQuery()
q := guestnics.Query()
q = q.Join(guests, sqlchemy.Equals(guests.Field("id"), guestnics.Field("guest_id")))
q = q.Join(hosts, sqlchemy.Equals(guests.Field("host_id"), hosts.Field("id")))
q = q.Filter(sqlchemy.IsFalse(guests.Field("pending_deleted")))
q = CloudProviderFilter(q, hosts.Field("manager_id"), providers, brands, cloudEnv)
q = RangeObjectsFilter(q, rangeObjs, nil, hosts.Field("zone_id"), hosts.Field("manager_id"), hosts.Field("id"), nil)
switch scope {
case rbacscope.ScopeSystem:
// do nothing
case rbacscope.ScopeDomain:
q = q.Filter(sqlchemy.Equals(guests.Field("domain_id"), ownerId.GetProjectDomainId()))
case rbacscope.ScopeProject:
q = q.Filter(sqlchemy.Equals(guests.Field("tenant_id"), ownerId.GetProjectId()))
}
if !includeSystem {
q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(guests.Field("is_system")),
sqlchemy.IsFalse(guests.Field("is_system"))))
}
return calculateNics(q)
}
type GuestnicsCount struct {
InternalNicCount int
InternalVirtualNicCount int
ExternalNicCount int
ExternalVirtualNicCount int
InternalBandwidth int
InternalRxBwLimit int
InternalTxBwLimit int
ExternalBandwidth int
ExternalRxBwLimit int
ExternalTxBwLimit int
}
func calculateNics(q *sqlchemy.SQuery) GuestnicsCount {
cnt := GuestnicsCount{}
gns := make([]SGuestnetwork, 0)
err := db.FetchModelObjects(GuestnetworkManager, q, &gns)
if err != nil {
log.Errorf("guestnics total count query error %s", err)
}
for _, gn := range gns {
if gn.IsExit(nil) {
if gn.Virtual {
cnt.ExternalVirtualNicCount += 1
} else {
cnt.ExternalNicCount += 1
}
cnt.ExternalBandwidth += gn.BwLimit
cnt.ExternalRxBwLimit += gn.RxBwLimit
cnt.ExternalTxBwLimit += gn.TxBwLimit
} else {
if gn.Virtual {
cnt.InternalVirtualNicCount += 1
} else {
cnt.InternalNicCount += 1
}
cnt.InternalBandwidth += gn.BwLimit
cnt.InternalRxBwLimit += gn.RxBwLimit
cnt.InternalTxBwLimit += gn.TxBwLimit
}
}
return cnt
}
func (gn *SGuestnetwork) IsExit(net *SNetwork) bool {
if gn.IpAddr != "" {
addr, err := netutils.NewIPV4Addr(gn.IpAddr)
if err == nil {
return netutils.IsExitAddress(addr)
}
}
if net == nil {
net, _ = gn.GetNetwork()
}
if net != nil {
return net.IsExitNetwork()
}
return false
}
func (gn *SGuestnetwork) getBandwidth(net *SNetwork, wire *SWire) int {
if gn.BwLimit == 0 {
return 0
}
if gn.BwLimit > 0 && gn.BwLimit <= api.MAX_BANDWIDTH {
return gn.BwLimit
} else {
if wire == nil {
if net == nil {
net, _ = gn.GetNetwork()
}
if net != nil {
wire, _ = net.GetWire()
}
}
if wire != nil {
return wire.Bandwidth
}
return options.Options.DefaultBandwidth
}
}
func (gn *SGuestnetwork) getRxBwLimit(net *SNetwork, wire *SWire) int {
if gn.RxBwLimit > 0 && gn.RxBwLimit <= api.MAX_BANDWIDTH {
return gn.RxBwLimit
}
return gn.getBandwidth(net, wire)
}
func (gn *SGuestnetwork) getTxBwLimit(net *SNetwork, wire *SWire) int {
if gn.TxBwLimit > 0 && gn.TxBwLimit <= api.MAX_BANDWIDTH {
return gn.TxBwLimit
}
return gn.getBandwidth(net, wire)
}
func (gn *SGuestnetwork) getMtu(net *SNetwork, wire *SWire) int16 {
return net.getMtu(wire)
}
func (gn *SGuestnetwork) IsAllocated() bool {
region, _ := gn.GetGuest().getRegion()
provider := region.Provider
if regutils.MatchMacAddr(gn.MacAddr) && (gn.Virtual || regutils.MatchIP4Addr(gn.IpAddr) || regutils.MatchIP6Addr(gn.Ip6Addr) || (provider != api.CLOUD_PROVIDER_ONECLOUD && !options.Options.EnablePreAllocateIpAddr)) {
return true
}
return false
}
/* func GetIPTenantIdPairs() {
from guests import Guests
from hosts import Hosts
from sqlalchemy.sql.expression import bindparam
q = Guestnics.query(Guestnics.ip_addr, Guestnics.mac_addr,
bindparam('tunnel_key', None), # XXX: tunnel_key for VPC
Guests.tenant_id, Guests.name,
bindparam('tunnel_ip', None), Hosts.access_ip) \
.join(Guests, and_(Guests.id==Guestnics.guest_id,
Guests.deleted==False)) \
.join(Hosts, and_(Hosts.id==Guests.host_id,
Hosts.deleted==False))
return q.all()
} */
func (gn *SGuestnetwork) GetVirtualIPs() []string {
ips := make([]string, 0)
guest := gn.GetGuest()
net, _ := gn.GetNetwork()
for _, guestgroup := range guest.GetGroups() {
group := guestgroup.GetGroup()
groupnets, err := group.GetNetworks()
if err != nil {
continue
}
for _, groupnetwork := range groupnets {
gnet := groupnetwork.GetNetwork()
if net != nil && gnet.WireId == net.WireId {
ips = append(ips, groupnetwork.IpAddr)
}
}
}
return ips
}
func (gn *SGuestnetwork) GetIsolatedDevice() (*SIsolatedDevice, error) {
dev := SIsolatedDevice{}
q := IsolatedDeviceManager.Query()
gidq := GuestIsolatedDeviceManager.Query().
Equals("guest_id", gn.GuestId).Equals("network_index", gn.Index).SubQuery()
q = q.Join(gidq, sqlchemy.Equals(q.Field("id"), gidq.Field("isolated_device_id")))
if cnt, err := q.CountWithError(); err != nil {
return nil, err
} else if cnt == 0 {
return nil, nil
}
err := q.First(&dev)
if err != nil {
return nil, err
}
dev.SetModelManager(IsolatedDeviceManager, &dev)
return &dev, nil
}
func (manager *SGuestnetworkManager) getRecentlyReleasedIPAddresses(networkId string, recentDuration time.Duration) map[string]bool {
return manager.getRecentlyReleasedIPAddressesInternal(networkId, recentDuration, api.AddressTypeIPv4)
}
func (manager *SGuestnetworkManager) getRecentlyReleasedIPAddresses6(networkId string, recentDuration time.Duration) map[string]bool {
return manager.getRecentlyReleasedIPAddressesInternal(networkId, recentDuration, api.AddressTypeIPv6)
}
func (manager *SGuestnetworkManager) getRecentlyReleasedIPAddressesInternal(networkId string, recentDuration time.Duration, addrType api.TAddressType) map[string]bool {
if recentDuration == 0 {
return nil
}
field := "ip_addr"
if addrType == api.AddressTypeIPv6 {
field = "ip6_addr"
}
since := time.Now().UTC().Add(-recentDuration)
q := manager.RawQuery(field)
q = q.Equals("network_id", networkId).IsTrue("deleted")
q = q.IsNotEmpty(field)
q = q.GT("deleted_at", since).Distinct()
rows, err := q.Rows()
if err != nil {
log.Errorf("GetRecentlyReleasedIPAddresses fail %s", err)
return nil
}
defer rows.Close()
ret := make(map[string]bool)
for rows.Next() {
var ip string
err = rows.Scan(&ip)
if err != nil {
log.Errorf("scan error %s", err)
} else {
ret[ip] = true
}
}
return ret
}
func (manager *SGuestnetworkManager) FilterByParams(q *sqlchemy.SQuery, params jsonutils.JSONObject) *sqlchemy.SQuery {
macStr := jsonutils.GetAnyString(params, []string{"mac", "mac_addr"})
if len(macStr) > 0 {
q = q.Filter(sqlchemy.Equals(q.Field("mac_addr"), macStr))
}
ipStr := jsonutils.GetAnyString(params, []string{"ipaddr", "ip_addr", "ip"})
if len(ipStr) > 0 {
q = q.Filter(sqlchemy.Equals(q.Field("ip_addr"), ipStr))
}
ip6Str := jsonutils.GetAnyString(params, []string{"ip6addr", "ip6_addr", "ip6"})
if len(ip6Str) > 0 {
q = q.Filter(sqlchemy.Equals(q.Field("ip6_addr"), ip6Str))
}
return q
}
func (manager *SGuestnetworkManager) FetchByIdsAndIpMac(guestId string, netId string, ipAddr string, mac string) (*SGuestnetwork, error) {
query := jsonutils.NewDict()
if len(mac) > 0 {
query.Add(jsonutils.NewString(mac), "mac_addr")
}
if len(ipAddr) > 0 {
query.Add(jsonutils.NewString(ipAddr), "ip_addr")
}
ign, err := db.FetchJointByIds(manager, guestId, netId, query)
if err != nil {
return nil, err
}
return ign.(*SGuestnetwork), nil
}
func (manager *SGuestnetworkManager) FetchByGuestId(guestId string) ([]SGuestnetwork, error) {
return manager.fetchGuestNetworks(func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
q = q.Equals("guest_id", guestId).Asc(q.Field("index"))
return q
})
}
func (manager *SGuestnetworkManager) fetchGuestNetworks(filter func(q *sqlchemy.SQuery) *sqlchemy.SQuery) ([]SGuestnetwork, error) {
q := manager.Query()
q = filter(q)
var rets []SGuestnetwork
if err := db.FetchModelObjects(manager, q, &rets); err != nil {
return nil, errors.Wrap(err, "FetchModelObjects")
}
return rets, nil
}
func (manager *SGuestnetworkManager) FetchByGuestIdIndex(guestId string, index int8) (*SGuestnetwork, error) {
rets, err := manager.FetchByGuestId(guestId)
if err != nil {
return nil, errors.Wrap(err, "FetchByGuestId")
}
if index >= 0 && int(index) < len(rets) {
return &rets[index], nil
}
return nil, errors.ErrNotFound
}
func (gn *SGuestnetwork) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
var net *SNetwork
var wire *SWire
if net == nil {
net, _ = gn.GetNetwork()
}
if wire == nil {
if net != nil {
wire, _ = net.GetWire()
}
}
desc := api.GuestnetworkShortDesc{}
if net != nil {
desc.BgpType = net.BgpType
}
if wire != nil {
desc.VpcId = wire.VpcId
}
if len(gn.IpAddr) > 0 {
desc.IpAddr = gn.IpAddr
desc.IsExit = gn.IsExit(net)
if desc.IsExit {
desc.NicType = "exit"
} else {
desc.NicType = "internal"
}
} else {
desc.NicType = "unused"
}
if len(gn.Ip6Addr) > 0 {
desc.Ip6Addr = gn.Ip6Addr
}
desc.Mac = gn.MacAddr
if len(gn.TeamWith) > 0 {
desc.TeamWith = gn.TeamWith
}
desc.BwLimitMbps = gn.getBandwidth(net, wire)
desc.Ifname = gn.Ifname
desc.IsDefault = gn.IsDefault
desc.BillingType = gn.BillingType
desc.ChargeType = gn.ChargeType
desc.GuestId = gn.GuestId
desc.NetworkId = gn.NetworkId
desc.PortMappings = gn.PortMappings
desc.SubIps = gn.GetSubIps()
return jsonutils.Marshal(desc).(*jsonutils.JSONDict)
}
func (gn *SGuestnetwork) GetWire() (*SWire, error) {
net, err := gn.GetNetwork()
if err != nil {
return nil, errors.Wrap(err, "GetNetwork")
}
return net.GetWire()
}
func (gn *SGuestnetwork) GetVpc() (*SVpc, error) {
net, err := gn.GetNetwork()
if err != nil {
return nil, errors.Wrap(err, "GetNetwork")
}
return net.GetVpc()
}
func (gn *SGuestnetwork) ToNetworkConfig() *api.NetworkConfig {
net, err := gn.GetNetwork()
if err != nil {
return nil
}
wire, _ := net.GetWire()
ret := &api.NetworkConfig{
Index: int(gn.Index),
Network: net.Id,
Wire: wire.Id,
Mac: gn.MacAddr,
Address: gn.IpAddr,
Address6: gn.Ip6Addr,
Driver: gn.Driver,
BwLimit: gn.BwLimit,
Project: net.ProjectId,
Domain: net.DomainId,
Ifname: gn.Ifname,
NetType: net.ServerType,
Exit: net.IsExitNetwork(),
RxBwLimit: gn.RxBwLimit,
TxBwLimit: gn.TxBwLimit,
}
return ret
}
func (manager *SGuestnetworkManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.GuestnetworkListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SGuestJointsManager.ListItemFilter(ctx, q, userCred, query.GuestJointsListInput)
if err != nil {
return nil, errors.Wrap(err, "SGuestJointsManager.ListItemFilter")
}
q, err = manager.SNetworkResourceBaseManager.ListItemFilter(ctx, q, userCred, query.NetworkFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SNetworkResourceBaseManager.ListItemFilter")
}
if len(query.MacAddr) > 0 {
q = q.In("mac_addr", query.MacAddr)
}
if len(query.IpAddr) > 0 {
naSubQ := NetworkAddressManager.Query("parent_id").Equals("type", api.NetworkAddressTypeSubIP).Equals("parent_type", api.NetworkAddressParentTypeGuestnetwork).In("ip_addr", query.IpAddr).SubQuery()
q = q.Filter(sqlchemy.OR(
sqlchemy.In(q.Field("ip_addr"), query.IpAddr),
sqlchemy.In(q.Field("row_id"), naSubQ),
))
}
if len(query.Ip6Addr) > 0 {
q = q.In("ip6_addr", query.Ip6Addr)
}
if len(query.Driver) > 0 {
q = q.In("driver", query.Driver)
}
if len(query.Ifname) > 0 {
q = q.In("ifname", query.Ifname)
}
if len(query.TeamWith) > 0 {
q = q.In("team_with", query.TeamWith)
}
return q, nil
}
func (manager *SGuestnetworkManager) OrderByExtraFields(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.GuestnetworkListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SGuestJointsManager.OrderByExtraFields(ctx, q, userCred, query.GuestJointsListInput)
if err != nil {
return nil, errors.Wrap(err, "SGuestJointsManager.OrderByExtraFields")
}
q, err = manager.SNetworkResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.NetworkFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SNetworkResourceBaseManager.OrderByExtraFields")
}
return q, nil
}
func (manager *SGuestnetworkManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
keys stringutils2.SSortedStrings,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SGuestJointsManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SGuestJointsManager.ListItemExportKeys")
}
if keys.ContainsAny(manager.SNetworkResourceBaseManager.GetExportKeys()...) {
q, err = manager.SNetworkResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SNetworkResourceBaseManager.ListItemExportKeys")
}
}
return q, nil
}
func (manager *SGuestnetworkManager) InitializeData() error {
err := manager.initOvnMappedIps()
if err != nil {
return errors.Wrap(err, "initOvnMappedIps")
}
return nil
}
func (manager *SGuestnetworkManager) initOvnMappedIps() error {
q := manager.Query()
networksQ := NetworkManager.Query().SubQuery()
wiresQ := WireManager.Query().SubQuery()
vpcQ := VpcManager.Query().SubQuery()
regionQ := CloudregionManager.Query().SubQuery()
q = q.Join(networksQ, sqlchemy.Equals(q.Field("network_id"), networksQ.Field("id")))
q = q.Join(wiresQ, sqlchemy.Equals(networksQ.Field("wire_id"), wiresQ.Field("id")))
q = q.Join(vpcQ, sqlchemy.Equals(wiresQ.Field("vpc_id"), vpcQ.Field("id")))
q = q.Join(regionQ, sqlchemy.Equals(vpcQ.Field("cloudregion_id"), regionQ.Field("id")))
q = q.Filter(sqlchemy.Equals(regionQ.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD))
q = q.Filter(sqlchemy.NotEquals(vpcQ.Field("id"), api.DEFAULT_VPC_ID))
q = q.Filter(sqlchemy.OR(
sqlchemy.IsEmpty(q.Field("mapped_ip_addr")),
sqlchemy.IsEmpty(q.Field("mapped_ip6_addr")),
))
gns := make([]SGuestnetwork, 0)
err := db.FetchModelObjects(manager, q, &gns)
if err != nil {
return errors.Wrap(err, "FetchModelObjects")
}
for i := range gns {
gn := &gns[i]
var v4addr string
if gn.MappedIpAddr == "" {
addr, err := manager.allocMappedIpAddr(context.Background())
if err != nil {
return errors.Wrap(err, "allocMappedIpAddr")
}
v4addr = addr
} else {
v4addr = gn.MappedIpAddr
}
if _, err := db.Update(gn, func() error {
gn.MappedIpAddr = v4addr
gn.MappedIp6Addr = api.GenVpcMappedIP6(v4addr)
return nil
}); err != nil {
return errors.Wrap(err, "db.Update")
}
}
return nil
}
func (guest *SGuest) IsStrictIpv6() (bool, error) {
q := GuestnetworkManager.Query().Equals("guest_id", guest.Id)
q = q.IsNotEmpty("ip6_addr").IsNullOrEmpty("ip_addr").IsFalse("virtual")
cnt, err := q.CountWithError()
if err != nil {
return false, errors.Wrap(err, "CountWithError")
}
return cnt > 0, nil
}
func (gn *SGuestnetwork) GetSubIps() string {
subIPQ := NetworkAddressManager.fetchSubIpsQuery(api.NetworkAddressParentTypeGuestnetwork)
subIPQ = subIPQ.Equals("parent_id", gn.RowId)
result := struct {
SubIps string `json:"sub_ips"`
}{}
err := subIPQ.First(&result)
if err != nil {
log.Errorf("Guestnetwork %s/%s/%s GetSubIps fail %s", gn.GuestId, gn.NetworkId, gn.MacAddr, err)
return ""
}
return result.SubIps
}