Files
cloudpods/pkg/compute/models/networks.go
T

3199 lines
102 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"database/sql"
"fmt"
"sort"
"strconv"
"strings"
"time"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/billing"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/rand"
"yunion.io/x/pkg/util/rbacscope"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
"yunion.io/x/onecloud/pkg/compute/options"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/rbacutils"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type SNetworkManager struct {
db.SSharableVirtualResourceBaseManager
db.SExternalizedResourceBaseManager
SWireResourceBaseManager
}
var NetworkManager *SNetworkManager
func GetNetworkManager() *SNetworkManager {
if NetworkManager != nil {
return NetworkManager
}
NetworkManager = &SNetworkManager{
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
SNetwork{},
"networks_tbl",
"network",
"networks",
),
}
NetworkManager.SetVirtualObject(NetworkManager)
return NetworkManager
}
func init() {
GetNetworkManager()
}
type SNetwork struct {
db.SSharableVirtualResourceBase
db.SExternalizedResourceBase
SWireResourceBase
IfnameHint string `width:"9" charset:"ascii" nullable:"true" list:"user" create:"optional"`
// 起始IP地址
GuestIpStart string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"`
// 结束IP地址
GuestIpEnd string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"`
// 掩码
GuestIpMask int8 `nullable:"false" list:"user" update:"user" create:"required"`
// 网关地址
GuestGateway string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
// DNS, allow multiple dns, seperated by ","
GuestDns string `width:"64" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
// allow multiple dhcp, seperated by ","
GuestDhcp string `width:"64" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
// allow mutiple ntp, seperated by ","
GuestNtp string `width:"64" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
GuestDomain string `width:"128" charset:"ascii" nullable:"true" get:"user" update:"user"`
GuestIp6Start string `width:"64" charset:"ascii" nullable:"true"`
GuestIp6End string `width:"64" charset:"ascii" nullable:"true"`
GuestIp6Mask int8 `nullable:"true"`
GuestGateway6 string `width:"64" charset:"ascii" nullable:"true"`
GuestDns6 string `width:"64" charset:"ascii" nullable:"true"`
GuestDomain6 string `width:"128" charset:"ascii" nullable:"true"`
VlanId int `nullable:"false" default:"1" list:"user" update:"user" create:"optional"`
// 服务器类型
// example: server
ServerType string `width:"16" charset:"ascii" default:"guest" nullable:"true" list:"user" create:"optional"`
// 分配策略
AllocPolicy string `width:"16" charset:"ascii" nullable:"true" get:"user" update:"user" create:"optional"`
AllocTimoutSeconds int `default:"0" nullable:"true" get:"admin"`
// 该网段是否用于自动分配IP地址,如果为false,则用户需要明确选择该网段,才会使用该网段分配IP,
// 如果为true,则用户不指定网段时,则自动从该值为true的网络中选择一个分配地址
IsAutoAlloc tristate.TriState `list:"user" get:"user" update:"user" create:"optional"`
// 线路类型
BgpType string `width:"64" charset:"utf8" nullable:"false" list:"user" get:"user" update:"user" create:"optional"`
}
func (manager *SNetworkManager) GetContextManagers() [][]db.IModelManager {
return [][]db.IModelManager{
{WireManager},
}
}
func (self *SNetwork) getMtu() int16 {
baseMtu := options.Options.DefaultMtu
wire, _ := self.GetWire()
if wire != nil {
if IsOneCloudVpcResource(wire) {
return int16(options.Options.OvnUnderlayMtu - api.VPC_OVN_ENCAP_COST)
} else if wire.Mtu != 0 {
return int16(wire.Mtu)
} else {
return int16(baseMtu)
}
}
return int16(baseMtu)
}
func (self *SNetwork) GetNetworkInterfaces() ([]SNetworkInterface, error) {
sq := NetworkinterfacenetworkManager.Query().SubQuery()
q := NetworkInterfaceManager.Query()
q = q.Join(sq, sqlchemy.Equals(q.Field("id"), sq.Field("networkinterface_id"))).
Filter(sqlchemy.Equals(sq.Field("network_id"), self.Id))
networkinterfaces := []SNetworkInterface{}
err := db.FetchModelObjects(NetworkInterfaceManager, q, &networkinterfaces)
if err != nil {
return nil, err
}
return networkinterfaces, nil
}
func (self *SNetwork) ValidateDeleteCondition(ctx context.Context, data *api.NetworkDetails) error {
if data == nil {
data = &api.NetworkDetails{}
nics, err := NetworkManager.TotalNicCount([]string{self.Id})
if err != nil {
return errors.Wrapf(err, "TotalNicCount")
}
if cnt, ok := nics[self.Id]; ok {
data.SNetworkNics = cnt
}
}
if data.Total-data.ReserveVnics-data.NetworkinterfaceVnics > 0 {
return httperrors.NewNotEmptyError("not an empty network %s", jsonutils.Marshal(data.SNetworkNics).String())
}
return self.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx, nil)
}
func (self *SNetwork) GetGuestnetworks() ([]SGuestnetwork, error) {
q := GuestnetworkManager.Query().Equals("network_id", self.Id)
gns := []SGuestnetwork{}
err := db.FetchModelObjects(GuestnetworkManager, q, &gns)
if err != nil {
return nil, errors.Wrapf(err, "db.FetchModelObjects")
}
return gns, nil
}
func (self *SNetwork) GetDBInstanceNetworks() ([]SDBInstanceNetwork, error) {
q := DBInstanceNetworkManager.Query().Equals("network_id", self.Id)
networks := []SDBInstanceNetwork{}
err := db.FetchModelObjects(DBInstanceNetworkManager, q, &networks)
if err != nil {
return nil, errors.Wrapf(err, "db.FetchModelObjects")
}
return networks, nil
}
func (manager *SNetworkManager) GetOrCreateClassicNetwork(ctx context.Context, wire *SWire) (*SNetwork, error) {
_network, err := db.FetchByExternalIdAndManagerId(manager, wire.Id, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
v, _ := wire.GetVpc()
if v != nil {
wire := WireManager.Query().SubQuery()
vpc := VpcManager.Query().SubQuery()
return q.Join(wire, sqlchemy.Equals(wire.Field("id"), q.Field("wire_id"))).
Join(vpc, sqlchemy.Equals(vpc.Field("id"), wire.Field("vpc_id"))).
Filter(sqlchemy.Equals(vpc.Field("manager_id"), v.ManagerId))
}
return q
})
if err == nil {
return _network.(*SNetwork), nil
}
if errors.Cause(err) != sql.ErrNoRows {
return nil, errors.Wrap(err, "db.FetchByExternalId")
}
network := SNetwork{
GuestIpStart: "0.0.0.0",
GuestIpEnd: "255.255.255.255",
GuestIpMask: 0,
GuestGateway: "0.0.0.0",
ServerType: api.NETWORK_TYPE_GUEST,
}
network.WireId = wire.Id
network.SetModelManager(manager, &network)
network.Name = fmt.Sprintf("emulate network for classic network with wire %s", wire.Id)
network.ExternalId = wire.Id
network.IsEmulated = true
network.IsPublic = true
network.PublicScope = "system"
admin := auth.AdminCredential()
network.DomainId = admin.GetProjectDomainId()
network.ProjectId = admin.GetProjectId()
network.Status = api.NETWORK_STATUS_UNAVAILABLE
err = manager.TableSpec().Insert(ctx, &network)
if err != nil {
return nil, errors.Wrap(err, "Insert classic network")
}
return &network, nil
}
func (self *SNetwork) GetUsedAddresses() map[string]bool {
used := make(map[string]bool)
q := self.getUsedAddressQuery(nil, nil, rbacscope.ScopeSystem, true)
results, err := q.AllStringMap()
if err != nil {
log.Errorf("GetUsedAddresses fail %s", err)
return used
}
for _, result := range results {
used[result["ip_addr"]] = true
}
return used
}
func (self *SNetwork) GetIPRange() netutils.IPV4AddrRange {
return self.getIPRange()
}
func (net *SNetwork) getIPRange() netutils.IPV4AddrRange {
start, _ := netutils.NewIPV4Addr(net.GuestIpStart)
end, _ := netutils.NewIPV4Addr(net.GuestIpEnd)
return netutils.NewIPV4AddrRange(start, end)
}
func (net *SNetwork) getNetRange() netutils.IPV4AddrRange {
start, _ := netutils.NewIPV4Addr(net.GuestIpStart)
return netutils.NewIPV4AddrRange(start.NetAddr(net.GuestIpMask), start.BroadcastAddr(net.GuestIpMask))
}
func isIpUsed(ipstr string, addrTable map[string]bool, recentUsedAddrTable map[string]bool) bool {
_, ok := addrTable[ipstr]
if !ok {
recentUsed := false
if recentUsedAddrTable != nil {
if _, ok := recentUsedAddrTable[ipstr]; ok {
recentUsed = true
}
}
return recentUsed
} else {
return true
}
}
func (self *SNetwork) getFreeIP(addrTable map[string]bool, recentUsedAddrTable map[string]bool, candidate string, allocDir api.IPAllocationDirection) (string, error) {
iprange := self.getIPRange()
// Try candidate first
if len(candidate) > 0 {
candIP, err := netutils.NewIPV4Addr(candidate)
if err != nil {
return "", err
}
if !iprange.Contains(candIP) {
return "", httperrors.NewInputParameterError("candidate %s out of range", candidate)
}
if _, ok := addrTable[candidate]; !ok {
return candidate, nil
}
}
// If network's alloc_policy is not none, then use network's alloc_policy
if len(self.AllocPolicy) > 0 && api.IPAllocationDirection(self.AllocPolicy) != api.IPAllocationNone {
allocDir = api.IPAllocationDirection(self.AllocPolicy)
}
// if alloc_dir is not speicified, and network's alloc_policy is not either, use default
if len(allocDir) == 0 {
allocDir = api.IPAllocationDirection(options.Options.DefaultIPAllocationDirection)
}
if allocDir == api.IPAllocationStepdown {
ip, _ := netutils.NewIPV4Addr(self.GuestIpEnd)
for iprange.Contains(ip) {
if !isIpUsed(ip.String(), addrTable, recentUsedAddrTable) {
return ip.String(), nil
}
ip = ip.StepDown()
}
} else {
if allocDir == api.IPAllocationRadnom {
iprange := self.getIPRange()
const MAX_TRIES = 5
for i := 0; i < MAX_TRIES; i += 1 {
ip := iprange.Random()
if !isIpUsed(ip.String(), addrTable, recentUsedAddrTable) {
return ip.String(), nil
}
}
// failed, fallback to IPAllocationStepup
}
ip, _ := netutils.NewIPV4Addr(self.GuestIpStart)
for iprange.Contains(ip) {
if !isIpUsed(ip.String(), addrTable, recentUsedAddrTable) {
return ip.String(), nil
}
ip = ip.StepUp()
}
}
return "", httperrors.NewInsufficientResourceError("Out of IP address")
}
func (self *SNetwork) GetFreeIPWithLock(ctx context.Context, userCred mcclient.TokenCredential, addrTable map[string]bool, recentUsedAddrTable map[string]bool, candidate string, allocDir api.IPAllocationDirection, reserved bool) (string, error) {
lockman.LockObject(ctx, self)
defer lockman.ReleaseObject(ctx, self)
return self.GetFreeIP(ctx, userCred, addrTable, recentUsedAddrTable, candidate, allocDir, reserved)
}
func (self *SNetwork) GetFreeIP(ctx context.Context, userCred mcclient.TokenCredential, addrTable map[string]bool, recentUsedAddrTable map[string]bool, candidate string, allocDir api.IPAllocationDirection, reserved bool) (string, error) {
// if reserved true, first try find IP in reserved IP pool
if reserved {
rip := ReservedipManager.GetReservedIP(self, candidate)
if rip != nil {
rip.Release(ctx, userCred, self)
return candidate, nil
}
// return "", httperrors.NewInsufficientResourceError("Reserved address %s not found", candidate)
// if not find, warning, then fallback to normal procedure
log.Warningf("Reserved address %s not found", candidate)
}
if addrTable == nil {
addrTable = self.GetUsedAddresses()
}
if recentUsedAddrTable == nil {
recentUsedAddrTable = GuestnetworkManager.getRecentlyReleasedIPAddresses(self.Id, self.getAllocTimoutDuration())
}
cand, err := self.getFreeIP(addrTable, recentUsedAddrTable, candidate, allocDir)
if err != nil {
return "", err
}
return cand, nil
}
func (self *SNetwork) GetNetAddr() netutils.IPV4Addr {
startIp, _ := netutils.NewIPV4Addr(self.GuestIpStart)
return startIp.NetAddr(self.GuestIpMask)
}
func (self *SNetwork) GetDNS(zoneName string) string {
if len(self.GuestDns) > 0 {
return self.GuestDns
}
if len(zoneName) == 0 {
wire, _ := self.GetWire()
if wire != nil {
zone, _ := wire.GetZone()
if zone != nil {
zoneName = zone.Name
}
}
}
srvs, _ := auth.GetDNSServers(options.Options.Region, zoneName)
if len(srvs) > 0 {
return strings.Join(srvs, ",")
}
if len(options.Options.DNSServer) > 0 {
return options.Options.DNSServer
}
return api.DefaultDNSServers
}
func (self *SNetwork) GetNTP() string {
if len(self.GuestNtp) > 0 {
return self.GuestNtp
} else {
zoneName := ""
wire, _ := self.GetWire()
if wire != nil {
zone, _ := wire.GetZone()
if zone != nil {
zoneName = zone.Name
}
}
srvs, _ := auth.GetNTPServers(options.Options.Region, zoneName)
if len(srvs) > 0 {
return strings.Join(srvs, ",")
}
return ""
}
}
func (self *SNetwork) GetDomain() string {
if len(self.GuestDomain) > 0 {
return self.GuestDomain
} else {
return options.Options.DNSDomain
}
}
func (self *SNetwork) GetRoutes() []types.SRoute {
ret := make([]types.SRoute, 0)
routes := self.GetMetadataJson(context.Background(), "static_routes", nil)
if routes != nil {
routesMap, err := routes.GetMap()
if err != nil {
return nil
}
for net, routeJson := range routesMap {
route, _ := routeJson.GetString()
ret = append(ret, types.SRoute{net, route})
}
}
return ret
}
func (self *SNetwork) updateDnsRecord(nic *SGuestnetwork, isAdd bool) {
guest := nic.GetGuest()
self._updateDnsRecord(guest.Name, nic.IpAddr, isAdd)
}
func (self *SNetwork) _updateDnsRecord(name string, ipAddr string, isAdd bool) {
if len(self.GuestDns) > 0 && len(self.GuestDomain) > 0 && len(ipAddr) > 0 {
keyName := self.GetMetadata(context.Background(), "dns_update_key_name", nil)
keySecret := self.GetMetadata(context.Background(), "dns_update_key_secret", nil)
dnsSrv := self.GetMetadata(context.Background(), "dns_update_server", nil)
if len(dnsSrv) == 0 || !regutils.MatchIPAddr(dnsSrv) {
dnsSrv = self.GuestDns
}
log.Infof("dns update %s %s isAdd=%t", ipAddr, dnsSrv, isAdd)
if len(keyName) > 0 && len(keySecret) > 0 {
/* netman.get_manager().dns_update(name,
self.guest_domain, ip_addr, None,
dns_srv, self.guest_dns6, key_name, key_secret,
is_add) */
}
targets := self.getDnsUpdateTargets()
if targets != nil {
for srv, keys := range targets {
for _, key := range keys {
log.Debugf("Register %s %s", srv, key)
/*
netman.get_manager().dns_update(name,
self.guest_domain, ip_addr, None,
srv, None,
key.get('key', None),
key.get('secret', None),
is_add)
*/
}
}
}
}
}
func (self *SNetwork) updateGuestNetmap(nic *SGuestnetwork) {
// TODO
}
func (self *SNetwork) UpdateBaremetalNetmap(nic *SHostnetwork, name string) {
self.UpdateNetmap(nic.IpAddr, auth.AdminCredential().GetTenantId(), name)
}
func (self *SNetwork) UpdateNetmap(ip, project, name string) {
// TODO ??
}
type DNSUpdateKeySecret struct {
Key string
Secret string
}
func (self *SNetwork) getDnsUpdateTargets() map[string][]DNSUpdateKeySecret {
targets := make(map[string][]DNSUpdateKeySecret)
targetsJson := self.GetMetadataJson(context.Background(), api.EXTRA_DNS_UPDATE_TARGETS, nil)
if targetsJson == nil {
return nil
} else {
err := targetsJson.Unmarshal(&targets)
if err != nil {
return nil
}
return targets
}
}
func (self *SNetwork) GetGuestIpv4StartAddress() netutils.IPV4Addr {
addr, _ := netutils.NewIPV4Addr(self.GuestIpStart)
return addr
}
func (self *SNetwork) IsExitNetwork() bool {
return netutils.IsExitAddress(self.GetGuestIpv4StartAddress())
}
func (manager *SNetworkManager) getNetworksByWire(wire *SWire) ([]SNetwork, error) {
return wire.getNetworks(nil, nil, rbacscope.ScopeNone)
/* nets := make([]SNetwork, 0)
q := manager.Query().Equals("wire_id", wire.Id)
err := db.FetchModelObjects(manager, q, &nets)
if err != nil {
log.Errorf("getNetworkByWire fail %s", err)
return nil, err
}
return nets, nil */
}
func (manager *SNetworkManager) SyncNetworks(
ctx context.Context,
userCred mcclient.TokenCredential,
wire *SWire,
nets []cloudprovider.ICloudNetwork,
provider *SCloudprovider,
xor bool,
) ([]SNetwork, []cloudprovider.ICloudNetwork, compare.SyncResult) {
syncOwnerId := provider.GetOwnerId()
lockman.LockRawObject(ctx, manager.Keyword(), wire.Id)
defer lockman.ReleaseRawObject(ctx, manager.Keyword(), wire.Id)
localNets := make([]SNetwork, 0)
remoteNets := make([]cloudprovider.ICloudNetwork, 0)
syncResult := compare.SyncResult{}
dbNets, err := manager.getNetworksByWire(wire)
if err != nil {
syncResult.Error(err)
return nil, nil, syncResult
}
for i := range dbNets {
if taskman.TaskManager.IsInTask(&dbNets[i]) {
syncResult.Error(fmt.Errorf("object in task"))
return nil, nil, syncResult
}
}
removed := make([]SNetwork, 0)
commondb := make([]SNetwork, 0)
commonext := make([]cloudprovider.ICloudNetwork, 0)
added := make([]cloudprovider.ICloudNetwork, 0)
err = compare.CompareSets(dbNets, nets, &removed, &commondb, &commonext, &added)
if err != nil {
syncResult.Error(err)
return nil, nil, syncResult
}
for i := 0; i < len(removed); i += 1 {
err = removed[i].syncRemoveCloudNetwork(ctx, userCred)
if err != nil {
syncResult.DeleteError(err)
} else {
syncResult.Delete()
}
}
if !xor {
for i := 0; i < len(commondb); i += 1 {
err = commondb[i].SyncWithCloudNetwork(ctx, userCred, commonext[i], syncOwnerId, provider)
if err != nil {
syncResult.UpdateError(err)
continue
}
localNets = append(localNets, commondb[i])
remoteNets = append(remoteNets, commonext[i])
syncResult.Update()
}
}
for i := 0; i < len(added); i += 1 {
new, err := manager.newFromCloudNetwork(ctx, userCred, added[i], wire, syncOwnerId, provider)
if err != nil {
syncResult.AddError(err)
} else {
localNets = append(localNets, *new)
remoteNets = append(remoteNets, added[i])
syncResult.Add()
}
}
return localNets, remoteNets, syncResult
}
func (self *SNetwork) syncRemoveCloudNetwork(ctx context.Context, userCred mcclient.TokenCredential) error {
lockman.LockObject(ctx, self)
defer lockman.ReleaseObject(ctx, self)
if self.ExternalId == self.WireId {
return nil
}
err := self.ValidateDeleteCondition(ctx, nil)
if err != nil { // cannot delete
err = self.SetStatus(userCred, api.NETWORK_STATUS_UNKNOWN, "Sync to remove")
} else {
err = self.RealDelete(ctx, userCred)
if err == nil {
notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
Obj: self,
Action: notifyclient.ActionSyncDelete,
})
}
}
return err
}
func (self *SNetwork) SyncWithCloudNetwork(ctx context.Context, userCred mcclient.TokenCredential, extNet cloudprovider.ICloudNetwork, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider) error {
vpc, _ := self.GetVpc()
diff, err := db.UpdateWithLock(ctx, self, func() error {
if options.Options.EnableSyncName {
newName, _ := db.GenerateAlterName(self, extNet.GetName())
if len(newName) > 0 {
self.Name = newName
}
}
self.Status = extNet.GetStatus()
self.GuestIpStart = extNet.GetIpStart()
self.GuestIpEnd = extNet.GetIpEnd()
self.GuestIpMask = extNet.GetIpMask()
self.GuestGateway = extNet.GetGateway()
self.ServerType = extNet.GetServerType()
self.AllocTimoutSeconds = extNet.GetAllocTimeoutSeconds()
if createdAt := extNet.GetCreatedAt(); !createdAt.IsZero() {
self.CreatedAt = createdAt
}
return nil
})
if err != nil {
log.Errorf("syncWithCloudNetwork error %s", err)
return err
}
db.OpsLog.LogSyncUpdate(self, diff, userCred)
if len(diff) > 0 {
notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
Obj: self,
Action: notifyclient.ActionSyncUpdate,
})
}
//syncVirtualResourceMetadata(ctx, userCred, self, extNet)
SyncCloudProject(ctx, userCred, self, syncOwnerId, extNet, vpc.ManagerId)
if provider != nil {
shareInfo := provider.getAccountShareInfo()
if utils.IsInStringArray(provider.Provider, api.PRIVATE_CLOUD_PROVIDERS) && extNet.GetPublicScope() == rbacscope.ScopeNone {
shareInfo = apis.SAccountShareInfo{
IsPublic: false,
PublicScope: rbacscope.ScopeNone,
}
}
self.SyncShareState(ctx, userCred, shareInfo)
}
return nil
}
func (manager *SNetworkManager) newFromCloudNetwork(ctx context.Context, userCred mcclient.TokenCredential, extNet cloudprovider.ICloudNetwork, wire *SWire, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider) (*SNetwork, error) {
net := SNetwork{}
net.SetModelManager(manager, &net)
net.Status = extNet.GetStatus()
net.ExternalId = extNet.GetGlobalId()
net.WireId = wire.Id
net.GuestIpStart = extNet.GetIpStart()
net.GuestIpEnd = extNet.GetIpEnd()
net.GuestIpMask = extNet.GetIpMask()
net.GuestGateway = extNet.GetGateway()
net.ServerType = extNet.GetServerType()
// net.IsPublic = extNet.GetIsPublic()
// extScope := extNet.GetPublicScope()
// if extScope == rbacutils.ScopeDomain && !consts.GetNonDefaultDomainProjects() {
// extScope = rbacutils.ScopeSystem
// }
// net.PublicScope = string(extScope)
net.AllocTimoutSeconds = extNet.GetAllocTimeoutSeconds()
if createdAt := extNet.GetCreatedAt(); !createdAt.IsZero() {
net.CreatedAt = createdAt
}
var err = func() error {
lockman.LockRawObject(ctx, manager.Keyword(), "name")
defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
newName, err := db.GenerateName(ctx, manager, syncOwnerId, extNet.GetName())
if err != nil {
return err
}
net.Name = newName
return manager.TableSpec().Insert(ctx, &net)
}()
if err != nil {
return nil, errors.Wrapf(err, "Insert")
}
vpc, _ := wire.GetVpc()
syncVirtualResourceMetadata(ctx, userCred, &net, extNet)
SyncCloudProject(ctx, userCred, &net, syncOwnerId, extNet, vpc.ManagerId)
if provider != nil {
shareInfo := provider.getAccountShareInfo()
if utils.IsInStringArray(provider.Provider, api.PRIVATE_CLOUD_PROVIDERS) && extNet.GetPublicScope() == rbacscope.ScopeNone {
shareInfo = apis.SAccountShareInfo{
IsPublic: false,
PublicScope: rbacscope.ScopeNone,
}
}
net.SyncShareState(ctx, userCred, shareInfo)
}
db.OpsLog.LogEvent(&net, db.ACT_CREATE, net.GetShortDesc(ctx), userCred)
notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
Obj: &net,
Action: notifyclient.ActionSyncCreate,
})
return &net, nil
}
func (net *SNetwork) IsAddressInRange(address netutils.IPV4Addr) bool {
return net.getIPRange().Contains(address)
}
func (net *SNetwork) IsAddressInNet(address netutils.IPV4Addr) bool {
return net.getNetRange().Contains(address)
}
func (self *SNetwork) isAddressUsed(address string) (bool, error) {
q := self.getUsedAddressQuery(nil, nil, rbacscope.ScopeSystem, true)
q = q.Equals("ip_addr", address)
count, err := q.CountWithError()
if err != nil && errors.Cause(err) != sql.ErrNoRows {
return false, errors.Wrap(err, "Query")
}
if count > 0 {
return true, nil
} else {
return false, nil
}
}
func (manager *SNetworkManager) fetchAllOnpremiseNetworks(serverType string, isPublic tristate.TriState) ([]SNetwork, error) {
q := manager.Query()
wires := WireManager.Query().SubQuery()
q = q.Join(wires, sqlchemy.Equals(q.Field("wire_id"), wires.Field("id")))
q = q.Filter(sqlchemy.Equals(wires.Field("vpc_id"), api.DEFAULT_VPC_ID))
if len(serverType) > 0 {
q = q.Filter(sqlchemy.Equals(q.Field("server_type"), serverType))
}
if isPublic.IsTrue() {
q = q.Filter(sqlchemy.IsTrue(q.Field("is_public")))
} else if isPublic.IsFalse() {
q = q.Filter(sqlchemy.IsFalse(q.Field("is_public")))
}
nets := make([]SNetwork, 0)
err := db.FetchModelObjects(manager, q, &nets)
if err != nil {
return nil, errors.Wrap(err, "FetchModelObjects")
}
return nets, nil
}
func (manager *SNetworkManager) GetOnPremiseNetworkOfIP(ipAddr string, serverType string, isPublic tristate.TriState) (*SNetwork, error) {
address, err := netutils.NewIPV4Addr(ipAddr)
if err != nil {
return nil, errors.Wrap(err, "NewIPV4Addr")
}
nets, err := manager.fetchAllOnpremiseNetworks(serverType, isPublic)
if err != nil {
return nil, errors.Wrap(err, "fetchAllOnpremiseNetworks")
}
for _, n := range nets {
if n.IsAddressInRange(address) {
return &n, nil
}
}
return nil, sql.ErrNoRows
}
func (manager *SNetworkManager) allNetworksQ(providers []string, brands []string, cloudEnv string, rangeObjs []db.IStandaloneModel) *sqlchemy.SQuery {
networks := manager.Query().SubQuery()
wires := WireManager.Query().SubQuery()
vpcs := VpcManager.Query().SubQuery()
q := networks.Query(networks.Field("id"))
q = q.Join(wires, sqlchemy.Equals(q.Field("wire_id"), wires.Field("id")))
q = q.Join(vpcs, sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id")))
q = CloudProviderFilter(q, vpcs.Field("manager_id"), providers, brands, cloudEnv)
q = RangeObjectsFilter(q, rangeObjs, vpcs.Field("cloudregion_id"), wires.Field("zone_id"), vpcs.Field("manager_id"), nil, nil)
return q
}
func (manager *SNetworkManager) totalPortCountQ(
scope rbacscope.TRbacScope,
userCred mcclient.IIdentityProvider,
providers []string,
brands []string,
cloudEnv string,
rangeObjs []db.IStandaloneModel,
policyResult rbacutils.SPolicyResult,
) *sqlchemy.SQuery {
q := manager.allNetworksQ(providers, brands, cloudEnv, rangeObjs)
switch scope {
case rbacscope.ScopeSystem:
case rbacscope.ScopeDomain:
q = q.Equals("domain_id", userCred.GetProjectDomainId())
case rbacscope.ScopeProject:
q = q.Equals("tenant_id", userCred.GetProjectId())
}
q = db.ObjectIdQueryWithPolicyResult(q, manager, policyResult)
return manager.Query().In("id", q.Distinct().SubQuery())
}
type NetworkPortStat struct {
Count int
CountExt int
}
func (manager *SNetworkManager) TotalPortCount(
scope rbacscope.TRbacScope,
userCred mcclient.IIdentityProvider,
providers []string, brands []string, cloudEnv string,
rangeObjs []db.IStandaloneModel,
policyResult rbacutils.SPolicyResult,
) map[string]NetworkPortStat {
nets := make([]SNetwork, 0)
err := manager.totalPortCountQ(
scope,
userCred,
providers, brands, cloudEnv,
rangeObjs,
policyResult,
).All(&nets)
if err != nil {
log.Errorf("TotalPortCount: %v", err)
}
ret := make(map[string]NetworkPortStat)
for _, net := range nets {
var stat NetworkPortStat
var allStat NetworkPortStat
if len(net.ServerType) > 0 {
stat, _ = ret[net.ServerType]
}
allStat, _ = ret[""]
count := net.getIPRange().AddressCount()
if net.IsExitNetwork() {
if len(net.ServerType) > 0 {
stat.CountExt += count
}
allStat.CountExt += count
} else {
if len(net.ServerType) > 0 {
stat.Count += count
}
allStat.Count += count
}
if len(net.ServerType) > 0 {
ret[net.ServerType] = stat
}
ret[""] = allStat
}
return ret
}
type SNicConfig struct {
Mac string
Index int8
Ifname string
}
func parseNetworkInfo(ctx context.Context, userCred mcclient.TokenCredential, info *api.NetworkConfig) (*api.NetworkConfig, error) {
if info.Network != "" {
netObj, err := NetworkManager.FetchByIdOrName(userCred, info.Network)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(NetworkManager.Keyword(), info.Network)
} else {
return nil, err
}
}
net := netObj.(*SNetwork)
if net.ProjectId == userCred.GetProjectId() ||
(db.IsDomainAllowGet(ctx, userCred, net) && net.DomainId == userCred.GetProjectDomainId()) ||
db.IsAdminAllowGet(ctx, userCred, net) ||
net.IsSharable(userCred) {
info.Network = netObj.GetId()
} else {
return nil, httperrors.NewForbiddenError("no allow to access network %s", info.Network)
}
}
if info.BwLimit == 0 {
info.BwLimit = options.Options.DefaultBandwidth
}
return info, nil
}
func (self *SNetwork) GetFreeAddressCount() (int, error) {
return self.getFreeAddressCount()
}
func (self *SNetwork) GetTotalAddressCount() int {
return self.getIPRange().AddressCount()
}
func (self *SNetwork) getFreeAddressCount() (int, error) {
vnics, err := NetworkManager.TotalNicCount([]string{self.Id})
if err != nil {
return -1, errors.Wrapf(err, "TotalNicCount")
}
used := 0
if nics, ok := vnics[self.Id]; ok {
used = nics.Total
}
return self.getIPRange().AddressCount() - used, nil
}
func isValidNetworkInfo(ctx context.Context, userCred mcclient.TokenCredential, netConfig *api.NetworkConfig, reuseAddr string) error {
if len(netConfig.Network) > 0 {
netObj, err := NetworkManager.FetchByIdOrName(userCred, netConfig.Network)
if err != nil {
return httperrors.NewResourceNotFoundError("Network %s not found: %v", netConfig.Network, err)
}
net := netObj.(*SNetwork)
/*
// scheduler do the check
if !netConfig.Vip && !netConfig.Reserved && net.getFreeAddressCount() == 0 {
return fmt.Errorf("Address exhausted in network %s")
}*/
if len(netConfig.Address) > 0 {
ipAddr, err := netutils.NewIPV4Addr(netConfig.Address)
if err != nil {
return err
}
if !net.IsAddressInRange(ipAddr) {
return httperrors.NewInputParameterError("Address %s not in range", netConfig.Address)
}
if netConfig.Reserved {
// the privilege to access reserved ip
if db.IsAdminAllowList(userCred, ReservedipManager).Result.IsDeny() {
return httperrors.NewForbiddenError("Only system admin allowed to use reserved ip")
}
if ReservedipManager.GetReservedIP(net, netConfig.Address) == nil {
return httperrors.NewInputParameterError("Address %s not reserved", netConfig.Address)
}
} else {
used, err := net.isAddressUsed(netConfig.Address)
if err != nil {
return httperrors.NewInternalServerError("isAddressUsed fail %s", err)
}
if used && netConfig.Address != reuseAddr {
return httperrors.NewInputParameterError("Address %s has been used", netConfig.Address)
}
}
}
if netConfig.BwLimit > api.MAX_BANDWIDTH {
return httperrors.NewInputParameterError("Bandwidth limit cannot exceed %dMbps", api.MAX_BANDWIDTH)
}
if net.ServerType == api.NETWORK_TYPE_BAREMETAL {
// not check baremetal network free address here
// TODO: find better solution ?
return nil
}
freeCnt, err := net.getFreeAddressCount()
if err != nil {
return httperrors.NewInternalServerError("getFreeAddressCount fail %s", err)
}
if reuseAddr != "" {
freeCnt += 1
}
if freeCnt < 1 {
return httperrors.NewInputParameterError("network %s(%s) has no free addresses", net.Name, net.Id)
}
}
/* scheduler to the check
else if ! netConfig.Vip {
ct, ctExit := NetworkManager.to
}
*/
return nil
}
func IsExitNetworkInfo(userCred mcclient.TokenCredential, netConfig *api.NetworkConfig) bool {
if len(netConfig.Network) > 0 {
netObj, _ := NetworkManager.FetchByIdOrName(userCred, netConfig.Network)
net := netObj.(*SNetwork)
if net.IsExitNetwork() {
return true
}
} else if netConfig.Exit {
return true
}
return false
}
func (self *SNetwork) GetPorts() int {
return self.getIPRange().AddressCount()
}
func (manager *SNetworkManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.NetworkDetails {
rows := make([]api.NetworkDetails, len(objs))
virtRows := manager.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
wireRows := manager.SWireResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
netIds := make([]string, len(objs))
for i := range rows {
rows[i] = api.NetworkDetails{
SharableVirtualResourceDetails: virtRows[i],
WireResourceInfo: wireRows[i],
}
network := objs[i].(*SNetwork)
rows[i].Exit = false
if network.IsExitNetwork() {
rows[i].Exit = true
}
rows[i].Ports = network.GetPorts()
rows[i].Routes = network.GetRoutes()
rows[i].Schedtags = GetSchedtagsDetailsToResourceV2(network, ctx)
rows[i].Dns = network.GetDNS(rows[i].Zone)
rows[i].AdditionalWires = network.fetchAdditionalWires()
netIds[i] = network.Id
}
vnics, err := manager.TotalNicCount(netIds)
if err != nil {
return rows
}
for i := range rows {
rows[i].SNetworkNics, _ = vnics[netIds[i]]
}
return rows
}
func (manager *SNetworkManager) GetTotalNicCount(netIds []string) (map[string]int, error) {
vnics, err := manager.TotalNicCount(netIds)
if err != nil {
return nil, errors.Wrapf(err, "TotalNicCount")
}
result := map[string]int{}
for _, id := range netIds {
result[id] = 0
if nics, ok := vnics[id]; ok {
result[id] = nics.Total
}
}
return result, nil
}
type SNetworkNics struct {
Id string
api.SNetworkNics
}
func (nm *SNetworkManager) query(manager db.IModelManager, field string, netIds []string, filter func(*sqlchemy.SQuery) *sqlchemy.SQuery) *sqlchemy.SSubQuery {
q := manager.Query()
if filter != nil {
q = filter(q)
}
sq := q.SubQuery()
return sq.Query(
sq.Field("network_id"),
sqlchemy.COUNT(field),
).In("network_id", netIds).GroupBy(sq.Field("network_id")).SubQuery()
}
func (nm *SNetworkManager) TotalNicCount(netIds []string) (map[string]api.SNetworkNics, error) {
// guest vnic
vnicSQ := nm.query(GuestnetworkManager, "vnic", netIds, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.IsFalse("virtual")
})
// bm vnic
bmSQ := nm.query(HostnetworkManager, "bm_vnic", netIds, nil)
// lb vnic
lbSQ := nm.query(LoadbalancernetworkManager, "lb_vnic", netIds, nil)
// eip vnic
eipSQ := nm.query(ElasticipManager, "eip_vnic", netIds, nil)
// group vnic
groupSQ := nm.query(GroupnetworkManager, "group_vnic", netIds, nil)
// reserved vnics
reserveSQ := nm.query(ReservedipManager, "reserve_vnic", netIds, filterExpiredReservedIps)
// rds vnics
rdsSQ := nm.query(DBInstanceNetworkManager, "rds_vnic", netIds, nil)
// nat vnics
natSQ := nm.query(NatGatewayManager, "nat_vnic", netIds, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.IsNotEmpty("ip_addr")
})
// networkinterface vncis
nisSQ := nm.query(NetworkinterfacenetworkManager, "networkinterface_vnic", netIds, nil)
// bm reused vnics
bmReusedSQ := nm.query(GuestnetworkManager, "bm_reused_vnic", netIds, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
guest := GuestManager.Query().SubQuery()
bmn := HostnetworkManager.Query().SubQuery()
return q.Join(guest, sqlchemy.Equals(guest.Field("id"), q.Field("guest_id"))).Join(bmn, sqlchemy.AND(
sqlchemy.Equals(q.Field("ip_addr"), bmn.Field("ip_addr")),
sqlchemy.Equals(guest.Field("host_id"), bmn.Field("baremetal_id")),
))
})
nets := nm.Query().SubQuery()
netQ := nets.Query(
sqlchemy.SUM("vnics", vnicSQ.Field("vnic")),
sqlchemy.SUM("bm_vnics", bmSQ.Field("bm_vnic")),
sqlchemy.SUM("lb_vnics", lbSQ.Field("lb_vnic")),
sqlchemy.SUM("eip_vnics", eipSQ.Field("eip_vnic")),
sqlchemy.SUM("group_vnics", groupSQ.Field("group_vnic")),
sqlchemy.SUM("reserve_vnics", reserveSQ.Field("reserve_vnic")),
sqlchemy.SUM("rds_vnics", rdsSQ.Field("rds_vnic")),
sqlchemy.SUM("nat_vnics", natSQ.Field("nat_vnic")),
sqlchemy.SUM("networkinterface_vnics", nisSQ.Field("networkinterface_vnic")),
sqlchemy.SUM("bm_reused_vnics", bmReusedSQ.Field("bm_reused_vnic")),
)
netQ.AppendField(netQ.Field("id"))
netQ = netQ.LeftJoin(vnicSQ, sqlchemy.Equals(netQ.Field("id"), vnicSQ.Field("network_id")))
netQ = netQ.LeftJoin(bmSQ, sqlchemy.Equals(netQ.Field("id"), bmSQ.Field("network_id")))
netQ = netQ.LeftJoin(lbSQ, sqlchemy.Equals(netQ.Field("id"), lbSQ.Field("network_id")))
netQ = netQ.LeftJoin(eipSQ, sqlchemy.Equals(netQ.Field("id"), eipSQ.Field("network_id")))
netQ = netQ.LeftJoin(groupSQ, sqlchemy.Equals(netQ.Field("id"), groupSQ.Field("network_id")))
netQ = netQ.LeftJoin(reserveSQ, sqlchemy.Equals(netQ.Field("id"), reserveSQ.Field("network_id")))
netQ = netQ.LeftJoin(rdsSQ, sqlchemy.Equals(netQ.Field("id"), rdsSQ.Field("network_id")))
netQ = netQ.LeftJoin(natSQ, sqlchemy.Equals(netQ.Field("id"), natSQ.Field("network_id")))
netQ = netQ.LeftJoin(nisSQ, sqlchemy.Equals(netQ.Field("id"), nisSQ.Field("network_id")))
netQ = netQ.LeftJoin(bmReusedSQ, sqlchemy.Equals(netQ.Field("id"), bmReusedSQ.Field("network_id")))
netQ = netQ.Filter(sqlchemy.In(netQ.Field("id"), netIds)).GroupBy(netQ.Field("id"))
nics := []SNetworkNics{}
err := netQ.All(&nics)
if err != nil {
return nil, errors.Wrapf(err, "netQ.All")
}
result := map[string]api.SNetworkNics{}
for i := range nics {
nics[i].SumTotal()
result[nics[i].Id] = nics[i].SNetworkNics
}
return result, nil
}
// 预留IP
// 预留的IP不会被调度使用
func (self *SNetwork) PerformReserveIp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.NetworkReserveIpInput) (jsonutils.JSONObject, error) {
if len(input.Ips) == 0 {
return nil, httperrors.NewMissingParameterError("ips")
}
var duration time.Duration
if len(input.Duration) > 0 {
bc, err := billing.ParseBillingCycle(input.Duration)
if err != nil {
return nil, httperrors.NewInputParameterError("Duration %s invalid", input.Duration)
}
duration = bc.Duration()
}
for _, ip := range input.Ips {
err := self.reserveIpWithDurationAndStatus(ctx, userCred, ip, input.Notes, duration, input.Status)
if err != nil {
return nil, err
}
}
return nil, nil
}
func (self *SNetwork) reserveIpWithDuration(ctx context.Context, userCred mcclient.TokenCredential, ipstr string, notes string, duration time.Duration) error {
return self.reserveIpWithDurationAndStatus(ctx, userCred, ipstr, notes, duration, "")
}
func (self *SNetwork) reserveIpWithDurationAndStatus(ctx context.Context, userCred mcclient.TokenCredential, ipstr string, notes string, duration time.Duration, status string) error {
ipAddr, err := netutils.NewIPV4Addr(ipstr)
if err != nil {
return httperrors.NewInputParameterError("not a valid ip address %s: %s", ipstr, err)
}
if !self.IsAddressInRange(ipAddr) {
return httperrors.NewInputParameterError("Address %s not in network", ipstr)
}
used, err := self.isAddressUsed(ipstr)
if err != nil {
return httperrors.NewInternalServerError("isAddressUsed fail %s", err)
}
if used {
return httperrors.NewConflictError("Address %s has been used", ipstr)
}
err = ReservedipManager.ReserveIPWithDurationAndStatus(userCred, self, ipstr, notes, duration, status)
if err != nil {
return err
}
return nil
}
// 释放预留IP
func (self *SNetwork) PerformReleaseReservedIp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.NetworkReleaseReservedIpInput) (jsonutils.JSONObject, error) {
if len(input.Ip) == 0 {
return nil, httperrors.NewMissingParameterError("ip")
}
rip := ReservedipManager.getReservedIP(self, input.Ip)
if rip == nil {
return nil, httperrors.NewInvalidStatusError("Address %s not reserved", input.Ip)
}
rip.Release(ctx, userCred, self)
return nil, nil
}
func (self *SNetwork) GetDetailsReservedIps(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
rips := ReservedipManager.GetReservedIPs(self)
if rips == nil {
return nil, httperrors.NewInternalServerError("get reserved ip error")
}
ripArray := jsonutils.NewArray()
for i := 0; i < len(rips); i += 1 {
ripArray.Add(jsonutils.NewString(rips[i].IpAddr))
}
ret := jsonutils.NewDict()
ret.Add(ripArray, "reserved_ips")
return ret, nil
}
func isValidMaskLen(maskLen int64) bool {
if maskLen < 12 || maskLen > 30 {
return false
} else {
return true
}
}
func (self *SNetwork) ensureIfnameHint() {
if self.IfnameHint != "" {
return
}
hint, err := NetworkManager.newIfnameHint(self.Name)
if err != nil {
panic(errors.Wrap(err, "ensureIfnameHint: allocate hint"))
}
_, err = db.Update(self, func() error {
self.IfnameHint = hint
return nil
})
if err != nil {
panic(errors.Wrap(err, "ensureIfnameHint: db update"))
}
log.Infof("network %s(%s): initialized ifname hint: %s", self.Name, self.Id, hint)
}
func (manager *SNetworkManager) NewIfnameHint(hint string) (string, error) {
return manager.newIfnameHint(hint)
}
func (manager *SNetworkManager) newIfnameHint(hint string) (string, error) {
isa := func(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
isn := func(c byte) bool {
return (c >= '0' && c <= '9')
}
sani := func(r string) string {
if r != "" && !isa(r[0]) {
r = "a" + r
}
if len(r) > MAX_HINT_LEN {
r = r[:MAX_HINT_LEN]
}
return r
}
rand := func(base string) (string, error) {
if len(base) > HINT_BASE_LEN {
base = base[:HINT_BASE_LEN]
}
for i := 0; i < 3; i++ {
r := sani(base + rand.String(HINT_RAND_LEN))
cnt, err := manager.Query().Equals("ifname_hint", r).CountWithError()
if err == nil && cnt == 0 {
return r, nil
}
}
/* generate ifname by ifname hint failed
* try generate from rand string */
for i := 0; i < 3; i++ {
r := sani(rand.String(MAX_HINT_LEN))
cnt, err := manager.Query().Equals("ifname_hint", r).CountWithError()
if err == nil && cnt == 0 {
return r, nil
}
}
return "", fmt.Errorf("failed finding ifname hint")
}
r := ""
for i := range hint {
c := hint[i]
if isa(c) || isn(c) || c == '_' {
r += string(c)
}
}
r = sani(r)
if len(r) < 3 {
return rand(r)
}
if cnt, err := manager.Query().Equals("ifname_hint", r).CountWithError(); err != nil {
return "", err
} else if cnt > 0 {
return rand(r)
}
return r, nil
}
func (manager *SNetworkManager) validateEnsureWire(ctx context.Context, userCred mcclient.TokenCredential, input api.NetworkCreateInput) (w *SWire, v *SVpc, cr *SCloudregion, err error) {
wObj, err := WireManager.FetchByIdOrName(userCred, input.Wire)
if err != nil {
err = errors.Wrapf(err, "wire %s", input.Wire)
return
}
w = wObj.(*SWire)
v, _ = w.GetVpc()
crObj, err := CloudregionManager.FetchById(v.CloudregionId)
if err != nil {
err = errors.Wrapf(err, "cloudregion %s", v.CloudregionId)
return
}
cr = crObj.(*SCloudregion)
return
}
func (manager *SNetworkManager) validateEnsureZoneVpc(ctx context.Context, userCred mcclient.TokenCredential, input api.NetworkCreateInput) (*SWire, *SVpc, *SCloudregion, error) {
zObj, err := validators.ValidateModel(userCred, ZoneManager, &input.Zone)
if err != nil {
return nil, nil, nil, err
}
z := zObj.(*SZone)
vObj, err := validators.ValidateModel(userCred, VpcManager, &input.Vpc)
if err != nil {
return nil, nil, nil, err
}
v := vObj.(*SVpc)
cr, err := z.GetRegion()
if err != nil {
return nil, nil, nil, err
}
// 华为云,ucloud wire zone_id 为空
var wires []SWire
if utils.IsInStringArray(cr.Provider, api.REGIONAL_NETWORK_PROVIDERS) {
wires, err = WireManager.getWiresByVpcAndZone(v, nil)
} else {
wires, err = WireManager.getWiresByVpcAndZone(v, z)
}
if err != nil {
return nil, nil, nil, err
}
if len(wires) > 1 {
return nil, nil, nil, httperrors.NewConflictError("found %d wires for zone %s and vpc %s", len(wires), input.Zone, input.Vpc)
}
if len(wires) == 1 {
return &wires[0], v, cr, nil
}
externalId := ""
if cr.Provider == api.CLOUD_PROVIDER_CLOUDPODS {
iVpc, err := v.GetIVpc(ctx)
if err != nil {
return nil, nil, nil, err
}
iWire, err := iVpc.CreateIWire(&cloudprovider.SWireCreateOptions{
Name: fmt.Sprintf("vpc-%s", v.Name),
ZoneId: z.ExternalId,
Bandwidth: 10000,
Mtu: 1500,
})
if err != nil {
return nil, nil, nil, errors.Wrapf(err, "CreateIWire")
}
externalId = iWire.GetGlobalId()
}
// wire not found. We auto create one for OneCloud vpc
if cr.Provider == api.CLOUD_PROVIDER_ONECLOUD || cr.Provider == api.CLOUD_PROVIDER_CLOUDPODS {
w, err := v.initWire(ctx, z, externalId)
if err != nil {
return nil, nil, nil, errors.Wrapf(err, "vpc %s init wire", v.Id)
}
return w, v, cr, nil
}
return nil, nil, nil, httperrors.NewNotFoundError("wire not found for zone %s and vpc %s", input.Zone, input.Vpc)
}
func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NetworkCreateInput) (api.NetworkCreateInput, error) {
if input.ServerType == "" {
input.ServerType = api.NETWORK_TYPE_GUEST
} else if !utils.IsInStringArray(input.ServerType, api.ALL_NETWORK_TYPES) {
return input, httperrors.NewInputParameterError("Invalid server_type: %s", input.ServerType)
}
{
defaultVlanId := 1
if input.VlanId == nil {
input.VlanId = &defaultVlanId
} else if *input.VlanId < 1 {
input.VlanId = &defaultVlanId
}
if *input.VlanId > 4095 {
return input, httperrors.NewInputParameterError("valid vlan id")
}
}
{
if len(input.IfnameHint) == 0 {
input.IfnameHint = input.Name
}
var err error
input.IfnameHint, err = manager.newIfnameHint(input.IfnameHint)
if err != nil {
return input, httperrors.NewBadRequestError("cannot derive valid ifname hint: %v", err)
}
}
var (
ipRange netutils.IPV4AddrRange
masklen int8
netAddr netutils.IPV4Addr
)
if len(input.GuestIpPrefix) > 0 {
prefix, err := netutils.NewIPV4Prefix(input.GuestIpPrefix)
if err != nil {
return input, httperrors.NewInputParameterError("ip_prefix error: %s", err)
}
ipRange = prefix.ToIPRange()
masklen = prefix.MaskLen
netAddr = prefix.Address.NetAddr(masklen)
input.GuestIpMask = int64(prefix.MaskLen)
if masklen >= 30 {
return input, httperrors.NewInputParameterError("subnet masklen should be smaller than 30")
}
// 根据掩码得到合法的GuestIpPrefix
input.GuestIpPrefix = prefix.String()
} else {
if !isValidMaskLen(input.GuestIpMask) {
return input, httperrors.NewInputParameterError("Invalid masklen %d", input.GuestIpMask)
}
ipStart, err := netutils.NewIPV4Addr(input.GuestIpStart)
if err != nil {
return input, httperrors.NewInputParameterError("Invalid start ip: %s %s", input.GuestIpStart, err)
}
ipEnd, err := netutils.NewIPV4Addr(input.GuestIpEnd)
if err != nil {
return input, httperrors.NewInputParameterError("invalid end ip: %s %s", input.GuestIpEnd, err)
}
ipRange = netutils.NewIPV4AddrRange(ipStart, ipEnd)
masklen = int8(input.GuestIpMask)
netAddr = ipStart.NetAddr(masklen)
if ipEnd.NetAddr(masklen) != netAddr {
return input, httperrors.NewInputParameterError("start and end ip not in the same subnet")
}
}
// do not set default dns
// if len(input.GuestDns) == 0 {
// input.GuestDns = options.Options.DNSServer
// }
for key, ipStr := range map[string]string{
"guest_gateway": input.GuestGateway,
"guest_dns": input.GuestDns,
"guest_dhcp": input.GuestDHCP,
"guest_ntp": input.GuestNtp,
} {
if ipStr == "" {
continue
}
if key == "guest_dhcp" || key == "guest_dns" {
ipList := strings.Split(ipStr, ",")
for _, ipstr := range ipList {
if !regutils.MatchIPAddr(ipstr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipstr)
}
}
} else if key == "guest_ntp" {
ipList := strings.Split(ipStr, ",")
for _, ipstr := range ipList {
if !regutils.MatchDomainName(ipstr) && !regutils.MatchIPAddr(ipstr) {
return input, httperrors.NewInputParameterError("%s: Invalid domain name or IP address %s", key, ipstr)
}
}
} else if !regutils.MatchIPAddr(ipStr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr)
}
}
if input.GuestGateway != "" {
addr, err := netutils.NewIPV4Addr(input.GuestGateway)
if err != nil {
return input, httperrors.NewInputParameterError("bad gateway ip: %v", err)
}
if addr.NetAddr(masklen) != netAddr {
return input, httperrors.NewInputParameterError("gateway ip must be in the same subnet as start, end ip")
}
}
var (
wire *SWire
vpc *SVpc
region *SCloudregion
err error
)
if input.WireId != "" {
input.Wire = input.WireId
}
if input.Wire != "" {
wire, vpc, region, err = manager.validateEnsureWire(ctx, userCred, input)
if err != nil {
return input, err
}
} else if input.Zone != "" && input.Vpc != "" {
wire, vpc, region, err = manager.validateEnsureZoneVpc(ctx, userCred, input)
if err != nil {
return input, err
}
} else {
return input, httperrors.NewInputParameterError("zone and vpc info required when wire is absent")
}
input.WireId = wire.Id
if vpc.Status != api.VPC_STATUS_AVAILABLE {
return input, httperrors.NewInvalidStatusError("VPC not ready")
}
if input.ServerType == api.NETWORK_TYPE_EIP && vpc.Id != api.DEFAULT_VPC_ID {
return input, httperrors.NewInputParameterError("eip network can only exist in default vpc, got %s(%s)", vpc.Name, vpc.Id)
}
if input.ServerType != api.NETWORK_TYPE_EIP {
input.BgpType = ""
}
// check class metadata
if wire != nil {
var projectId string
if len(input.ProjectId) > 0 {
projectId = input.ProjectId
} else {
projectId = ownerId.GetProjectId()
}
project, err := db.TenantCacheManager.FetchTenantById(ctx, projectId)
if err != nil {
return input, errors.Wrapf(err, "unable to fetch tenant by id %s", projectId)
}
ok, err := db.IsInSameClass(ctx, wire, project)
if err != nil {
return input, errors.Wrapf(err, "unable to check if wire and project is in same class")
}
if !ok {
return input, httperrors.NewForbiddenError("the wire %s and project %s has different class metadata", wire.GetName(), project.GetName())
}
}
var (
ipStart = ipRange.StartIp()
ipEnd = ipRange.EndIp()
)
if region.Provider == api.CLOUD_PROVIDER_ONECLOUD && vpc.Id != api.DEFAULT_VPC_ID {
// reserve addresses for onecloud vpc networks
masklen := int8(input.GuestIpMask)
netAddr := ipStart.NetAddr(masklen)
if netAddr != ipEnd.NetAddr(masklen) {
return input, httperrors.NewInputParameterError("start and end ip when masked are not in the same cidr subnet")
}
gateway := netAddr.StepUp()
brdAddr := ipStart.BroadcastAddr(masklen)
// NOTE
//
// - reserve the 1st addr as gateway
// - reserve the last ip for broadcasting
// - reserve the 2nd-to-last for possible future use
//
// We do not allow split 192.168.1.0/24 into multiple ranges
// like
//
// - 192.168.1.50-192.168.1.100,
// - 192.168.1.100-192.168.1.200
//
// This could complicate gateway setting and topology
// management without much benefit to end users
ipStart = gateway.StepUp()
ipEnd = brdAddr.StepDown().StepDown()
input.GuestGateway = gateway.String()
}
{
netRange := netutils.NewIPV4AddrRange(ipStart, ipEnd)
if !vpc.containsIPV4Range(netRange) {
return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock)
}
}
{
nets, err := vpc.GetNetworks()
if err != nil {
return input, httperrors.NewInternalServerError("fail to GetNetworks of vpc: %v", err)
}
if isOverlapNetworks(nets, ipStart, ipEnd) {
return input, httperrors.NewInputParameterError("Conflict address space with existing networks in vpc %q", vpc.GetName())
}
}
input.GuestIpStart = ipStart.String()
input.GuestIpEnd = ipEnd.String()
input.SharableVirtualResourceCreateInput, err = manager.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
if err != nil {
return input, err
}
return input, nil
}
func (self *SNetwork) validateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.NetworkUpdateInput) (api.NetworkUpdateInput, error) {
var (
startIp netutils.IPV4Addr
endIp netutils.IPV4Addr
netAddr netutils.IPV4Addr
masklen int8
err error
)
if input.GuestIpMask != nil {
maskLen64 := int64(*input.GuestIpMask)
if !self.isManaged() && !isValidMaskLen(maskLen64) {
return input, httperrors.NewInputParameterError("Invalid masklen %d", maskLen64)
}
masklen = int8(maskLen64)
} else {
masklen = int8(self.GuestIpMask)
}
if input.GuestIpStart != "" || input.GuestIpEnd != "" {
if input.GuestIpStart != "" {
startIp, err = netutils.NewIPV4Addr(input.GuestIpStart)
if err != nil {
return input, httperrors.NewInputParameterError("Invalid start ip: %s %s", input.GuestIpStart, err)
}
} else {
startIp, _ = netutils.NewIPV4Addr(self.GuestIpStart)
}
if input.GuestIpEnd != "" {
endIp, err = netutils.NewIPV4Addr(input.GuestIpEnd)
if err != nil {
return input, httperrors.NewInputParameterError("invalid end ip: %s %s", input.GuestIpEnd, err)
}
} else {
endIp, _ = netutils.NewIPV4Addr(self.GuestIpEnd)
}
if startIp > endIp {
tmp := startIp
startIp = endIp
endIp = tmp
}
nets := NetworkManager.getAllNetworks(self.WireId, self.Id)
if nets == nil {
return input, httperrors.NewInternalServerError("query all networks fail")
}
if isOverlapNetworks(nets, startIp, endIp) {
return input, httperrors.NewInputParameterError("Conflict address space with existing networks")
}
netRange := netutils.NewIPV4AddrRange(startIp, endIp)
vpc, _ := self.GetVpc()
if !vpc.containsIPV4Range(netRange) {
return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock)
}
usedMap := self.GetUsedAddresses()
for usedIpStr := range usedMap {
if usedIp, err := netutils.NewIPV4Addr(usedIpStr); err == nil && !netRange.Contains(usedIp) {
return input, httperrors.NewInputParameterError("Address %s been assigned out of new range", usedIpStr)
}
}
input.GuestIpStart = startIp.String()
input.GuestIpEnd = endIp.String()
netAddr = startIp.NetAddr(masklen)
if endIp.NetAddr(masklen) != netAddr {
return input, httperrors.NewInputParameterError("start, end ip must be in the same subnet")
}
} else {
startIp, _ = netutils.NewIPV4Addr(self.GuestIpStart)
endIp, _ = netutils.NewIPV4Addr(self.GuestIpEnd)
netAddr = startIp.NetAddr(masklen)
}
for key, ipStr := range map[string]string{
"guest_gateway": input.GuestGateway,
"guest_dns": input.GuestDns,
"guest_dhcp": input.GuestDhcp,
"guest_ntp": input.GuestNtp,
} {
if ipStr == "" {
continue
}
if key == "guest_dhcp" || key == "guest_dns" {
ipList := strings.Split(ipStr, ",")
for _, ipstr := range ipList {
if !regutils.MatchIPAddr(ipstr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipstr)
}
}
} else if key == "guest_ntp" {
ipList := strings.Split(ipStr, ",")
for _, ipstr := range ipList {
if !regutils.MatchDomainName(ipstr) && !regutils.MatchIPAddr(ipstr) {
return input, httperrors.NewInputParameterError("%s: Invalid domain name or IP address %s", key, ipstr)
}
}
} else if !regutils.MatchIPAddr(ipStr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr)
}
}
if input.GuestGateway != "" {
addr, err := netutils.NewIPV4Addr(input.GuestGateway)
if err != nil {
return input, httperrors.NewInputParameterError("bad gateway ip: %v", err)
}
if addr.NetAddr(masklen) != netAddr {
return input, httperrors.NewInputParameterError("gateway ip must be in the same subnet as start, end ip")
}
}
if input.IsAutoAlloc != nil && *input.IsAutoAlloc {
if self.ServerType != api.NETWORK_TYPE_GUEST {
return input, httperrors.NewInputParameterError("network server_type %s not support auto alloc", self.ServerType)
}
}
return input, nil
}
func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.NetworkUpdateInput) (api.NetworkUpdateInput, error) {
if !self.isManaged() {
if !self.isOneCloudVpcNetwork() {
// classic network
} else {
// vpc network
input.GuestIpStart = self.GuestIpStart
input.GuestIpEnd = self.GuestIpEnd
input.GuestIpMask = &self.GuestIpMask
input.GuestGateway = self.GuestGateway
input.GuestDhcp = self.GuestDhcp
}
} else {
// managed network
input.GuestIpStart = self.GuestIpStart
input.GuestIpEnd = self.GuestIpEnd
input.GuestIpMask = &self.GuestIpMask
input.GuestGateway = self.GuestGateway
input.GuestDns = self.GuestDns
input.GuestDomain = self.GuestDomain
input.GuestDhcp = self.GuestDhcp
input.GuestNtp = self.GuestNtp
}
var err error
input, err = self.validateUpdateData(ctx, userCred, query, input)
if err != nil {
return input, errors.Wrap(err, "validateUpdateData")
}
input.SharableVirtualResourceBaseUpdateInput, err = self.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput)
if err != nil {
return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData")
}
return input, nil
}
func (manager *SNetworkManager) getAllNetworks(wireId, excludeId string) []SNetwork {
nets := make([]SNetwork, 0)
q := manager.Query().Equals("wire_id", wireId)
if len(excludeId) > 0 {
q = q.NotEquals("id", excludeId)
}
err := db.FetchModelObjects(manager, q, &nets)
if err != nil {
log.Errorf("getAllNetworks fail %s", err)
return nil
}
return nets
}
func isOverlapNetworks(nets []SNetwork, startIp netutils.IPV4Addr, endIp netutils.IPV4Addr) bool {
ipRange := netutils.NewIPV4AddrRange(startIp, endIp)
for i := 0; i < len(nets); i += 1 {
ipRange2 := nets[i].getIPRange()
if ipRange2.IsOverlap(ipRange) {
return true
}
}
return false
}
func (self *SNetwork) IsManaged() bool {
wire, _ := self.GetWire()
if wire == nil {
return false
}
return wire.IsManaged()
}
func (self *SNetwork) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
if !data.Contains("public_scope") {
if self.ServerType == api.NETWORK_TYPE_GUEST && !self.IsManaged() {
wire, _ := self.GetWire()
if db.IsAdminAllowPerform(ctx, userCred, self, "public") && ownerId.GetProjectDomainId() == userCred.GetProjectDomainId() && wire != nil && wire.IsPublic && wire.PublicScope == string(rbacscope.ScopeSystem) {
self.SetShare(rbacscope.ScopeSystem)
} else if db.IsDomainAllowPerform(ctx, userCred, self, "public") && ownerId.GetProjectId() == userCred.GetProjectId() && consts.GetNonDefaultDomainProjects() {
// only if non_default_domain_projects turned on, share to domain
self.SetShare(rbacscope.ScopeDomain)
} else {
self.SetShare(rbacscope.ScopeNone)
}
} else {
self.SetShare(rbacscope.ScopeNone)
}
data.(*jsonutils.JSONDict).Set("public_scope", jsonutils.NewString(self.PublicScope))
}
return self.SSharableVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
}
func (net *SNetwork) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
net.SSharableVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
vpc, _ := net.GetVpc()
if vpc != nil && vpc.IsManaged() {
task, err := taskman.TaskManager.NewTask(ctx, "NetworkCreateTask", net, userCred, data.(*jsonutils.JSONDict), "", "", nil)
if err != nil {
log.Errorf("networkcreateTask create fail: %s", err)
} else {
task.ScheduleRun(nil)
}
} else {
{
err := net.syncAdditionalWires(ctx, nil)
if err != nil {
log.Errorf("syncAdditionalWires error: %s", err)
}
}
net.SetStatus(userCred, api.NETWORK_STATUS_AVAILABLE, "")
if err := net.ClearSchedDescCache(); err != nil {
log.Errorf("network post create clear schedcache error: %v", err)
}
}
}
func (self *SNetwork) GetPrefix() (netutils.IPV4Prefix, error) {
addr, err := netutils.NewIPV4Addr(self.GuestIpStart)
if err != nil {
return netutils.IPV4Prefix{}, err
}
addr = addr.NetAddr(self.GuestIpMask)
return netutils.IPV4Prefix{Address: addr, MaskLen: self.GuestIpMask}, nil
}
func (self *SNetwork) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
log.Infof("SNetwork delete do nothing")
self.SetStatus(userCred, api.NETWORK_STATUS_START_DELETE, "")
return nil
}
func (self *SNetwork) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
if len(self.ExternalId) > 0 {
return self.StartDeleteNetworkTask(ctx, userCred)
} else {
return self.RealDelete(ctx, userCred)
}
}
func (self *SNetwork) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
DeleteResourceJointSchedtags(self, ctx, userCred)
db.OpsLog.LogEvent(self, db.ACT_DELOCATE, self.GetShortDesc(ctx), userCred)
self.SetStatus(userCred, api.NETWORK_STATUS_DELETED, "real delete")
networkinterfaces, err := self.GetNetworkInterfaces()
if err != nil {
return errors.Wrap(err, "GetNetworkInterfaces")
}
for i := range networkinterfaces {
err = networkinterfaces[i].purge(ctx, userCred)
if err != nil {
return errors.Wrapf(err, "networkinterface.purge %s(%s)", networkinterfaces[i].Name, networkinterfaces[i].Id)
}
}
reservedIps := ReservedipManager.GetReservedIPs(self)
for i := range reservedIps {
err = reservedIps[i].Release(ctx, userCred, self)
if err != nil {
return errors.Wrapf(err, "reservedIps.Release %s(%d)", reservedIps[i].IpAddr, reservedIps[i].Id)
}
}
gns, err := self.GetGuestnetworks() // delete virtual nics
if err != nil {
return errors.Wrapf(err, "GetGuestnetworks")
}
for i := range gns {
err = gns[i].Delete(ctx, userCred)
if err != nil {
return errors.Wrapf(err, "delete virtual nic %s(%d)", gns[i].Ifname, gns[i].RowId)
}
}
if err := self.SSharableVirtualResourceBase.Delete(ctx, userCred); err != nil {
return err
}
if err := NetworkAdditionalWireManager.DeleteNetwork(ctx, self.Id); err != nil {
return errors.Wrap(err, "NetworkAdditionalWireManager.DeleteNetwork")
}
self.ClearSchedDescCache()
return nil
}
func (self *SNetwork) StartDeleteNetworkTask(ctx context.Context, userCred mcclient.TokenCredential) error {
task, err := taskman.TaskManager.NewTask(ctx, "NetworkDeleteTask", self, userCred, nil, "", "", nil)
if err != nil {
log.Errorf("Start NetworkDeleteTask fail %s", err)
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SNetwork) GetINetwork(ctx context.Context) (cloudprovider.ICloudNetwork, error) {
wire, err := self.GetWire()
if err != nil {
return nil, errors.Wrapf(err, "GetWire")
}
iwire, err := wire.GetIWire(ctx)
if err != nil {
return nil, err
}
return iwire.GetINetworkById(self.GetExternalId())
}
func (self *SNetwork) isManaged() bool {
if len(self.ExternalId) > 0 {
return true
} else {
return false
}
}
func (self *SNetwork) isOneCloudVpcNetwork() bool {
return IsOneCloudVpcResource(self)
}
func parseIpToIntArray(ip string) ([]int, error) {
ipSp := strings.Split(strings.Trim(ip, "."), ".")
if len(ipSp) > 4 {
return nil, httperrors.NewInputParameterError("Parse Ip Failed")
}
ipIa := []int{}
for i := 0; i < len(ipSp); i++ {
val, err := strconv.Atoi(ipSp[i])
if err != nil {
return nil, httperrors.NewInputParameterError("Parse Ip Failed")
}
if val < 0 || val > 255 {
return nil, httperrors.NewInputParameterError("Parse Ip Failed")
}
ipIa = append(ipIa, val)
}
return ipIa, nil
}
// IP子网列表
func (manager *SNetworkManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
input api.NetworkListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.SharableVirtualResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemFilter")
}
q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, input.ExternalizedResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
}
{
wireFilter := input.WireResourceInput
input.Wire = ""
input.WireId = ""
q, err = manager.SWireResourceBaseManager.ListItemFilter(ctx, q, userCred, input.WireFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SWireResourceBaseManager.ListItemFilter")
}
if len(wireFilter.WireId) > 0 {
wireObj, err := WireManager.FetchByIdOrName(userCred, wireFilter.WireId)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(WireManager.Keyword(), wireFilter.WireId)
} else {
return nil, errors.Wrapf(err, "WireManager.FetchByIdOrName %s", wireFilter.WireId)
}
}
wireFilter.WireId = wireObj.GetId()
wireFilter.Wire = wireObj.GetName()
q = q.Filter(sqlchemy.OR(
sqlchemy.Equals(q.Field("wire_id"), wireFilter.WireId),
sqlchemy.In(q.Field("id"), NetworkAdditionalWireManager.networkIdQuery(wireFilter.WireId).SubQuery()),
))
}
input.WireResourceInput = wireFilter
}
if len(input.RouteTableId) > 0 {
sq := RouteTableAssociationManager.Query("associated_resource_id").Equals("route_table_id", input.RouteTableId).Equals("association_type", string(cloudprovider.RouteTableAssociaToSubnet))
q = q.In("id", sq.SubQuery())
}
if input.Usable != nil && *input.Usable {
regions := CloudregionManager.Query("id").Equals("status", api.CLOUD_REGION_STATUS_INSERVER)
zones := ZoneManager.Query("id").Equals("status", api.ZONE_ENABLE).In("cloudregion_id", regions)
providerSQ := usableCloudProviders()
_vpcs := VpcManager.Query("id").Equals("status", api.VPC_STATUS_AVAILABLE)
vpcs := _vpcs.Filter(sqlchemy.OR(
sqlchemy.In(_vpcs.Field("manager_id"), providerSQ),
sqlchemy.IsNullOrEmpty(_vpcs.Field("manager_id")),
))
wires := WireManager.Query("id")
wires = wires.In("vpc_id", vpcs).
Filter(sqlchemy.OR(sqlchemy.IsNullOrEmpty(wires.Field("zone_id")), sqlchemy.In(wires.Field("zone_id"), zones)))
q = q.In("wire_id", wires).Equals("status", api.NETWORK_STATUS_AVAILABLE)
}
if len(input.HostId)+len(input.HostType) > 0 {
type sSimpleHost struct {
Id string
OvnVersion string
}
hq := HostManager.Query("id", "ovn_version")
switch {
case len(input.HostId) > 0 && len(input.HostType) > 0:
hq = hq.Filter(sqlchemy.OR(
sqlchemy.Equals(hq.Field("id"), input.HostId),
sqlchemy.Equals(hq.Field("host_type"), input.HostType),
))
case len(input.HostId) > 0:
hq = hq.Equals("id", input.HostId)
case len(input.HostType) > 0:
hq = hq.Equals("host_type", input.HostType)
}
shs := make([]sSimpleHost, 0)
err := hq.All(&shs)
if err != nil {
return nil, errors.Wrap(err, "unable to filter all host from id and host type")
}
hostids := make([]string, len(shs))
var ovnVersion bool
for i := range shs {
hostids[i] = shs[i].Id
if len(shs[i].OvnVersion) > 0 {
ovnVersion = true
}
}
sq := NetInterfaceManager.Query("wire_id")
switch len(hostids) {
case 0:
// hack for empty hostwire
sq = sq.IsTrue("deleted")
case 1:
sq = sq.Equals("baremetal_id", hostids[0])
default:
sq = sq.In("baremetal_id", hostids)
}
if ovnVersion {
vpcSub := VpcManager.Query("id").Equals("cloudregion_id", "default").NotEquals("id", api.DEFAULT_VPC_ID).SubQuery()
wireQuery := WireManager.Query("id").In("vpc_id", vpcSub)
q = q.Filter(sqlchemy.OR(
sqlchemy.In(q.Field("wire_id"), wireQuery.SubQuery()),
sqlchemy.In(q.Field("wire_id"), sq.SubQuery())),
)
} else {
q = q.Filter(sqlchemy.In(q.Field("wire_id"), sq.SubQuery()))
}
}
storageStr := input.StorageId
if len(storageStr) > 0 {
storage, err := StorageManager.FetchByIdOrName(userCred, storageStr)
if err != nil {
return nil, errors.Wrapf(err, "unable to fetch storage %q", storageStr)
}
hoststorages := HoststorageManager.Query("host_id").Equals("storage_id", storage.GetId()).SubQuery()
hostSq := HostManager.Query("id").In("id", hoststorages).SubQuery()
sq := NetInterfaceManager.Query("wire_id").In("baremetal_id", hostSq)
ovnHosts := HostManager.Query().In("id", hoststorages).IsNotEmpty("ovn_version")
if n, _ := ovnHosts.CountWithError(); n > 0 {
wireQuery := WireManager.Query("id").IsNotNull("vpc_id")
q = q.Filter(sqlchemy.OR(
sqlchemy.In(q.Field("wire_id"), wireQuery.SubQuery()),
sqlchemy.In(q.Field("wire_id"), sq.SubQuery())),
)
} else {
q = q.Filter(sqlchemy.In(q.Field("wire_id"), sq.SubQuery()))
}
}
ips := []string{}
exactIpMatch := false
if len(input.Ip) > 0 {
exactIpMatch = true
ips = input.Ip
} else if len(input.IpMatch) > 0 {
ips = input.IpMatch
}
if len(ips) > 0 {
conditions := []sqlchemy.ICondition{}
for _, ip := range ips {
if len(ip) == 0 {
continue
}
ipIa, err := parseIpToIntArray(ip)
if err != nil {
return nil, err
}
ipSa := []string{"0", "0", "0", "0"}
for i := range ipIa {
ipSa[i] = strconv.Itoa(ipIa[i])
}
fullIp := strings.Join(ipSa, ".")
ipField := sqlchemy.INET_ATON(sqlchemy.NewStringField(fullIp))
ipStart := sqlchemy.INET_ATON(q.Field("guest_ip_start"))
ipEnd := sqlchemy.INET_ATON(q.Field("guest_ip_end"))
var ipCondtion sqlchemy.ICondition
if exactIpMatch {
ipCondtion = sqlchemy.Between(ipField, ipStart, ipEnd)
} else {
ipCondtion = sqlchemy.OR(sqlchemy.Between(ipField, ipStart, ipEnd), sqlchemy.Contains(q.Field("guest_ip_start"), ip), sqlchemy.Contains(q.Field("guest_ip_end"), ip))
}
conditions = append(conditions, ipCondtion)
}
q = q.Filter(sqlchemy.OR(conditions...))
}
if len(input.SchedtagId) > 0 {
schedTag, err := SchedtagManager.FetchByIdOrName(nil, input.SchedtagId)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(SchedtagManager.Keyword(), input.SchedtagId)
}
return nil, httperrors.NewGeneralError(err)
}
sq := NetworkschedtagManager.Query("network_id").Equals("schedtag_id", schedTag.GetId()).SubQuery()
q = q.In("id", sq)
}
if len(input.IfnameHint) > 0 {
q = q.In("ifname_hint", input.IfnameHint)
}
if len(input.GuestIpStart) > 0 {
q = q.Filter(sqlchemy.ContainsAny(q.Field("guest_ip_start"), input.GuestIpStart))
}
if len(input.GuestIpEnd) > 0 {
q = q.Filter(sqlchemy.ContainsAny(q.Field("guest_ip_end"), input.GuestIpEnd))
}
if len(input.GuestIpMask) > 0 {
q = q.In("guest_ip_mask", input.GuestIpMask)
}
if len(input.GuestGateway) > 0 {
q = q.In("guest_gateway", input.GuestGateway)
}
if len(input.GuestDns) > 0 {
q = q.In("guest_dns", input.GuestDns)
}
if len(input.GuestDhcp) > 0 {
q = q.In("guest_dhcp", input.GuestDhcp)
}
if len(input.GuestNtp) > 0 {
q = q.In("guest_ntp", input.GuestNtp)
}
if len(input.GuestDomain) > 0 {
q = q.In("guest_domain", input.GuestDomain)
}
if len(input.GuestIp6Start) > 0 {
q = q.In("guest_ip6_start", input.GuestIp6Start)
}
if len(input.GuestIp6End) > 0 {
q = q.In("guest_ip6_end", input.GuestIp6End)
}
if len(input.GuestIp6Mask) > 0 {
q = q.In("guest_ip6_mask", input.GuestIp6Mask)
}
if len(input.GuestGateway6) > 0 {
q = q.In("guest_gateway6", input.GuestGateway6)
}
if len(input.GuestDns6) > 0 {
q = q.In("guest_dns6", input.GuestDns6)
}
if len(input.GuestDomain6) > 0 {
q = q.In("guest_domain6", input.GuestDomain6)
}
if len(input.VlanId) > 0 {
q = q.In("vlan_id", input.VlanId)
}
if len(input.ServerType) > 0 {
q = q.In("server_type", input.ServerType)
}
if len(input.AllocPolicy) > 0 {
q = q.In("alloc_policy", input.AllocPolicy)
}
if len(input.BgpType) > 0 {
q = q.In("bgp_type", input.BgpType)
}
if input.IsAutoAlloc != nil {
if *input.IsAutoAlloc {
q = q.IsTrue("is_auto_alloc")
} else {
q = q.IsFalse("is_auto_alloc")
}
}
if input.IsClassic != nil {
subq := manager.Query("id")
wires := WireManager.Query("id", "vpc_id").SubQuery()
subq = subq.Join(wires, sqlchemy.Equals(wires.Field("id"), subq.Field("wire_id")))
if *input.IsClassic {
subq = subq.Filter(sqlchemy.Equals(wires.Field("vpc_id"), api.DEFAULT_VPC_ID))
} else {
subq = subq.Filter(sqlchemy.NotEquals(wires.Field("vpc_id"), api.DEFAULT_VPC_ID))
}
q = q.In("id", subq.SubQuery())
}
if len(input.HostSchedtagId) > 0 {
schedTagObj, err := SchedtagManager.FetchByIdOrName(userCred, input.HostSchedtagId)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "%s %s", SchedtagManager.Keyword(), input.HostSchedtagId)
} else {
return nil, errors.Wrap(err, "SchedtagManager.FetchByIdOrName")
}
}
subq := NetInterfaceManager.Query("wire_id")
hostschedtags := HostschedtagManager.Query().Equals("schedtag_id", schedTagObj.GetId()).SubQuery()
subq = subq.Join(hostschedtags, sqlchemy.Equals(hostschedtags.Field("host_id"), subq.Field("baremetal_id")))
q = q.In("wire_id", subq.SubQuery())
}
return q, nil
}
func (manager *SNetworkManager) OrderByExtraFields(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
input api.NetworkListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.SharableVirtualResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.OrderByExtraFields")
}
q, err = manager.SWireResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.WireFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SWireResourceBaseManager.OrderByExtraFields")
}
if db.NeedOrderQuery([]string{input.OrderByIpStart}) {
q = db.OrderByFields(q, []string{input.OrderByIpStart}, []sqlchemy.IQueryField{sqlchemy.INET_ATON(q.Field("guest_ip_start"))})
}
if db.NeedOrderQuery([]string{input.OrderByIpEnd}) {
q = db.OrderByFields(q, []string{input.OrderByIpEnd}, []sqlchemy.IQueryField{sqlchemy.INET_ATON(q.Field("guest_ip_end"))})
}
return q, nil
}
func (manager *SNetworkManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = manager.SWireResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
return q, httperrors.ErrNotFound
}
func (manager *SNetworkManager) InitializeData() error {
// set network status
networks := make([]SNetwork, 0)
q := manager.Query()
err := db.FetchModelObjects(manager, q, &networks)
if err != nil {
return err
}
for _, n := range networks {
if n.ExternalId != "" {
var statusNew string
if n.WireId != "" && n.Status == api.NETWORK_STATUS_INIT {
statusNew = api.NETWORK_STATUS_AVAILABLE
}
db.Update(&n, func() error {
if statusNew != "" {
n.Status = statusNew
}
return nil
})
} else {
var ifnameHintNew string
if n.IfnameHint == "" {
ifnameHintNew = n.Name
}
db.Update(&n, func() error {
if ifnameHintNew != "" {
n.IfnameHint = ifnameHintNew
}
return nil
})
}
if n.IsAutoAlloc.IsNone() {
db.Update(&n, func() error {
if n.IsPublic && n.ServerType == api.NETWORK_TYPE_GUEST {
n.IsAutoAlloc = tristate.True
} else {
n.IsAutoAlloc = tristate.False
}
return nil
})
}
}
return nil
}
func (self *SNetwork) ValidateUpdateCondition(ctx context.Context) error {
/*if len(self.ExternalId) > 0 {
return httperrors.NewConflictError("Cannot update external resource")
}*/
return self.SSharableVirtualResourceBase.ValidateUpdateCondition(ctx)
}
func (net *SNetwork) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query, data jsonutils.JSONObject) {
net.SSharableVirtualResourceBase.PostUpdate(ctx, userCred, query, data)
net.ClearSchedDescCache()
if net.IsClassic() {
err := net.syncAdditionalWires(ctx, nil)
if err != nil {
log.Errorf("syncAdditionalWires error %s", err)
}
}
}
// 清除IP子网数据
// 要求IP子网内没有被分配IP,若清除接入云,要求接入云账号处于禁用状态
func (self *SNetwork) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.NetworkPurgeInput) (jsonutils.JSONObject, error) {
err := self.ValidateDeleteCondition(ctx, nil)
if err != nil {
return nil, err
}
wire, _ := self.GetWire()
if wire != nil && len(wire.ExternalId) > 0 {
provider := wire.GetCloudprovider()
if provider != nil && provider.GetEnabled() {
return nil, httperrors.NewInvalidStatusError("Cannot purge network on enabled cloud provider")
}
}
err = self.RealDelete(ctx, userCred)
return nil, err
}
func (manager *SNetworkManager) handleNetworkIdChange(ctx context.Context, args *networkIdChangeArgs) error {
var handlers = []networkIdChangeHandler{
GuestnetworkManager,
HostnetworkManager,
ReservedipManager,
GroupnetworkManager,
LoadbalancernetworkManager,
LoadbalancerManager,
}
errs := []error{}
for _, h := range handlers {
if err := h.handleNetworkIdChange(ctx, args); err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
err := errors.NewAggregate(errs)
return httperrors.NewGeneralError(err)
}
return nil
}
// 合并IP子网
// 将两个相连的IP子网合并成一个IP子网
func (self *SNetwork) PerformMerge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.NetworkMergeInput) (jsonutils.JSONObject, error) {
if len(input.Target) == 0 {
return nil, httperrors.NewMissingParameterError("target")
}
iNet, err := NetworkManager.FetchByIdOrName(userCred, input.Target)
if err == sql.ErrNoRows {
err = httperrors.NewNotFoundError("Network %s not found", input.Target)
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return nil, err
} else if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return nil, err
}
net := iNet.(*SNetwork)
if net == nil {
err = fmt.Errorf("Network is nil")
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return nil, err
}
startIp, endIp, err := self.CheckInvalidToMerge(ctx, net, nil)
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return nil, err
}
return nil, self.MergeToNetworkAfterCheck(ctx, userCred, net, startIp, endIp)
}
func (self *SNetwork) MergeToNetworkAfterCheck(ctx context.Context, userCred mcclient.TokenCredential, net *SNetwork, startIp string, endIp string) error {
lockman.LockClass(ctx, NetworkManager, db.GetLockClassKey(NetworkManager, userCred))
defer lockman.ReleaseClass(ctx, NetworkManager, db.GetLockClassKey(NetworkManager, userCred))
_, err := db.Update(net, func() error {
net.GuestIpStart = startIp
net.GuestIpEnd = endIp
return nil
})
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return err
}
if err := NetworkManager.handleNetworkIdChange(ctx, &networkIdChangeArgs{
action: logclient.ACT_MERGE,
oldNet: self,
newNet: net,
userCred: userCred,
}); err != nil {
return err
}
note := map[string]string{"start_ip": startIp, "end_ip": endIp}
db.OpsLog.LogEvent(self, db.ACT_MERGE, note, userCred)
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, note, userCred, true)
if err = self.RealDelete(ctx, userCred); err != nil {
return err
}
note = map[string]string{"network": self.Id}
db.OpsLog.LogEvent(self, db.ACT_DELETE, note, userCred)
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_DELOCATE, note, userCred, true)
return nil
}
func (self *SNetwork) CheckInvalidToMerge(ctx context.Context, net *SNetwork, allNets []*SNetwork) (string, string, error) {
failReason := make([]string, 0)
if self.WireId != net.WireId {
failReason = append(failReason, "wire_id")
}
if self.GuestGateway != net.GuestGateway {
failReason = append(failReason, "guest_gateway")
}
if self.VlanId != net.VlanId {
failReason = append(failReason, "vlan_id")
}
// Qiujian: allow merge networks of different server_type
/*if self.ServerType != net.ServerType {
failReason = append(failReason, "server_type")
}*/
if len(failReason) > 0 {
err := httperrors.NewInputParameterError("Invalid Target Network %s: inconsist %s", net.GetId(), strings.Join(failReason, ","))
return "", "", err
}
var startIp, endIp string
ipNE, _ := netutils.NewIPV4Addr(net.GuestIpEnd)
ipNS, _ := netutils.NewIPV4Addr(net.GuestIpStart)
ipSS, _ := netutils.NewIPV4Addr(self.GuestIpStart)
ipSE, _ := netutils.NewIPV4Addr(self.GuestIpEnd)
var wireNets []SNetwork
if allNets == nil {
wireSubq := WireManager.Query("vpc_id").Equals("id", self.WireId).SubQuery()
wiresQ := WireManager.Query("id")
wiresSubQ := wiresQ.Join(wireSubq, sqlchemy.Equals(wiresQ.Field("vpc_id"), wireSubq.Field("vpc_id"))).SubQuery()
q := NetworkManager.Query().In("wire_id", wiresSubQ).NotEquals("id", self.Id).NotEquals("id", net.Id)
err := db.FetchModelObjects(NetworkManager, q, &wireNets)
if err != nil && errors.Cause(err) != sql.ErrNoRows {
return "", "", errors.Wrap(err, "Query nets of same wire")
}
} else {
wireNets = make([]SNetwork, len(allNets))
for i := range wireNets {
wireNets[i] = *allNets[i]
}
}
if ipNE.StepUp() == ipSS || (ipNE.StepUp() < ipSS && !isOverlapNetworks(wireNets, ipNE.StepUp(), ipSS.StepDown())) {
startIp, endIp = net.GuestIpStart, self.GuestIpEnd
} else if ipSE.StepUp() == ipNS || (ipSE.StepUp() < ipNS && !isOverlapNetworks(wireNets, ipSE.StepUp(), ipNS.StepDown())) {
startIp, endIp = self.GuestIpStart, net.GuestIpEnd
} else {
note := "Incontinuity Network for %s and %s"
return "", "", httperrors.NewBadRequestError(note, self.Name, net.Name)
}
return startIp, endIp, nil
}
// 分割IP子网
// 将一个IP子网分割成两个子网,仅本地IDC支持此操作
func (self *SNetwork) PerformSplit(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.NetworkSplitInput) (jsonutils.JSONObject, error) {
if len(self.ExternalId) > 0 {
return nil, httperrors.NewNotSupportedError("only on premise support this operation")
}
if len(input.SplitIp) == 0 {
return nil, httperrors.NewMissingParameterError("split_ip")
}
if !regutils.MatchIPAddr(input.SplitIp) {
return nil, httperrors.NewInputParameterError("Invalid IP %s", input.SplitIp)
}
if input.SplitIp == self.GuestIpStart {
return nil, httperrors.NewInputParameterError("Split IP %s is the start ip", input.SplitIp)
}
iSplitIp, err := netutils.NewIPV4Addr(input.SplitIp)
if err != nil {
return nil, err
}
if !self.IsAddressInRange(iSplitIp) {
return nil, httperrors.NewInputParameterError("Split IP %s out of range", input.SplitIp)
}
network := &SNetwork{}
network.GuestIpStart = input.SplitIp
network.GuestIpEnd = self.GuestIpEnd
network.GuestIpMask = self.GuestIpMask
network.GuestGateway = self.GuestGateway
network.GuestDns = self.GuestDns
network.GuestDhcp = self.GuestDhcp
network.GuestNtp = self.GuestNtp
network.GuestDomain = self.GuestDomain
network.VlanId = self.VlanId
network.WireId = self.WireId
network.ServerType = self.ServerType
network.IsPublic = self.IsPublic
network.Status = self.Status
network.ProjectId = self.ProjectId
network.DomainId = self.DomainId
// network.UserId = self.UserId
network.IsSystem = self.IsSystem
network.Description = self.Description
network.IsAutoAlloc = self.IsAutoAlloc
err = func() error {
lockman.LockRawObject(ctx, NetworkManager.Keyword(), "name")
defer lockman.ReleaseRawObject(ctx, NetworkManager.Keyword(), "name")
if len(input.Name) > 0 {
if err := db.NewNameValidator(NetworkManager, userCred, input.Name, nil); err != nil {
return httperrors.NewInputParameterError("Duplicate name %s", input.Name)
}
} else {
input.Name, err = db.GenerateName(ctx, NetworkManager, userCred, fmt.Sprintf("%s#", self.Name))
if err != nil {
return httperrors.NewInternalServerError("GenerateName fail %s", err)
}
}
network.Name = input.Name
network.IfnameHint, err = NetworkManager.newIfnameHint(input.Name)
if err != nil {
return httperrors.NewBadRequestError("Generate ifname hint failed %s", err)
}
return NetworkManager.TableSpec().Insert(ctx, network)
}()
if err != nil {
return nil, err
}
network.SetModelManager(NetworkManager, network)
db.Update(self, func() error {
self.GuestIpEnd = iSplitIp.StepDown().String()
return nil
})
if err := NetworkManager.handleNetworkIdChange(ctx, &networkIdChangeArgs{
action: logclient.ACT_SPLIT,
oldNet: self,
newNet: network,
userCred: userCred,
}); err != nil {
return nil, err
}
note := map[string]string{"split_ip": input.SplitIp, "end_ip": network.GuestIpEnd}
db.OpsLog.LogEvent(self, db.ACT_SPLIT, note, userCred)
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_SPLIT, note, userCred, true)
db.OpsLog.LogEvent(network, db.ACT_CREATE, map[string]string{"network": self.Id}, userCred)
return nil, nil
}
func (manager *SNetworkManager) PerformTryCreateNetwork(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.NetworkTryCreateNetworkInput) (jsonutils.JSONObject, error) {
if len(input.Ip) == 0 {
return nil, httperrors.NewMissingParameterError("ip")
}
ipV4, err := netutils.NewIPV4Addr(input.Ip)
if err != nil {
return nil, httperrors.NewInputParameterError("ip")
}
if input.Mask == 0 {
return nil, httperrors.NewMissingParameterError("mask")
}
if len(input.ServerType) == 0 {
return nil, httperrors.NewMissingParameterError("server_type")
}
if input.ServerType != api.NETWORK_TYPE_BAREMETAL {
return nil, httperrors.NewBadRequestError("Only support server type %s", api.NETWORK_TYPE_BAREMETAL)
}
if !input.IsOnPremise {
return nil, httperrors.NewBadRequestError("Only support on premise network")
}
var (
ipV4NetAddr = ipV4.NetAddr(int8(input.Mask))
nm *SNetwork
matched bool
)
q := NetworkManager.Query().Equals("server_type", input.ServerType).Equals("guest_ip_mask", input.Mask)
listQuery := api.NetworkListInput{}
err = query.Unmarshal(&listQuery)
if err != nil {
return nil, errors.Wrap(err, "query.Unmarshal")
}
q, err = managedResourceFilterByAccount(q, listQuery.ManagedResourceListInput, "wire_id", func() *sqlchemy.SQuery {
wires := WireManager.Query().SubQuery()
vpcs := VpcManager.Query().SubQuery()
subq := wires.Query(wires.Field("id"))
subq = subq.Join(vpcs, sqlchemy.Equals(vpcs.Field("id"), wires.Field("vpc_id")))
return subq
})
if err != nil {
return nil, errors.Wrap(err, "managedResourceFilterByAccount")
}
rows, err := q.Rows()
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
item, err := db.NewModelObject(NetworkManager)
if err != nil {
return nil, err
}
err = q.Row2Struct(rows, item)
if err != nil {
return nil, err
}
n := item.(*SNetwork)
if n.GetIPRange().Contains(ipV4) {
nm = n
matched = true
break
} else if nIpV4, _ := netutils.NewIPV4Addr(n.GuestIpStart); nIpV4.NetAddr(n.GuestIpMask) == ipV4NetAddr {
nm = n
matched = false
break
}
}
ret := jsonutils.NewDict()
if nm == nil {
ret.Set("find_matched", jsonutils.JSONFalse)
return ret, nil
}
ret.Set("find_matched", jsonutils.JSONTrue)
ret.Set("wire_id", jsonutils.NewString(nm.WireId))
if !matched {
log.Infof("Find same subnet network %s %s/%d", nm.Name, nm.GuestGateway, nm.GuestIpMask)
newNetwork := new(SNetwork)
newNetwork.SetModelManager(NetworkManager, newNetwork)
newNetwork.GuestIpStart = input.Ip
newNetwork.GuestIpEnd = input.Ip
newNetwork.GuestGateway = nm.GuestGateway
newNetwork.GuestIpMask = int8(input.Mask)
newNetwork.GuestDns = nm.GuestDns
newNetwork.GuestDhcp = nm.GuestDhcp
newNetwork.GuestNtp = nm.GuestNtp
newNetwork.WireId = nm.WireId
newNetwork.ServerType = input.ServerType
newNetwork.IsPublic = nm.IsPublic
newNetwork.ProjectId = userCred.GetProjectId()
newNetwork.DomainId = userCred.GetProjectDomainId()
err = func() error {
lockman.LockRawObject(ctx, NetworkManager.Keyword(), "name")
defer lockman.ReleaseRawObject(ctx, NetworkManager.Keyword(), "name")
newNetwork.Name, err = db.GenerateName(ctx, NetworkManager, userCred, fmt.Sprintf("%s#", nm.Name))
if err != nil {
return httperrors.NewInternalServerError("GenerateName fail %s", err)
}
return NetworkManager.TableSpec().Insert(ctx, newNetwork)
}()
if err != nil {
return nil, err
}
err = newNetwork.CustomizeCreate(ctx, userCred, userCred, query, input.JSON(input))
if err != nil {
return nil, err
}
newNetwork.PostCreate(ctx, userCred, userCred, query, input.JSON(input))
// inherit wire's class metadata
wire, err := newNetwork.GetWire()
if err != nil {
return nil, errors.Wrap(err, "unable to get wire")
}
err = db.InheritFromTo(ctx, userCred, wire, newNetwork)
if err != nil {
return nil, errors.Wrap(err, "unable to inherit wire")
}
}
return ret, nil
}
func (network *SNetwork) getAllocTimoutDuration() time.Duration {
tos := network.AllocTimoutSeconds
if tos < options.Options.MinimalIpAddrReusedIntervalSeconds {
tos = options.Options.MinimalIpAddrReusedIntervalSeconds
}
return time.Duration(tos) * time.Second
}
func (network *SNetwork) GetSchedtags() []SSchedtag {
return GetSchedtags(NetworkschedtagManager, network.Id)
}
func (network *SNetwork) GetDynamicConditionInput() *jsonutils.JSONDict {
return jsonutils.Marshal(network).(*jsonutils.JSONDict)
}
func (network *SNetwork) PerformSetSchedtag(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return PerformSetResourceSchedtag(network, ctx, userCred, query, data)
}
func (network *SNetwork) GetSchedtagJointManager() ISchedtagJointManager {
return NetworkschedtagManager
}
func (network *SNetwork) ClearSchedDescCache() error {
wire, _ := network.GetWire()
if wire == nil {
return nil
}
return wire.clearHostSchedDescCache()
}
func (network *SNetwork) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) {
wire, err := network.GetWire()
if err != nil {
return nil, errors.Wrap(err, "unable to get wire")
}
project, err := db.TenantCacheManager.FetchTenantById(ctx, input.ProjectId)
if err != nil {
return nil, errors.Wrapf(err, "unable to get project %s", input.ProjectId)
}
ok, err := db.IsInSameClass(ctx, wire, project)
if err != nil {
return nil, errors.Wrap(err, "unable to check if the wire and project is in same class")
}
if !ok {
return nil, httperrors.NewForbiddenError("the wire %s and the project %s has different class metadata", wire.GetName(), project.GetName())
}
ret, err := network.SSharableVirtualResourceBase.PerformChangeOwner(ctx, userCred, query, input)
if err != nil {
return nil, err
}
network.ClearSchedDescCache()
return ret, nil
}
func (network *SNetwork) getUsedAddressQuery(userCred mcclient.TokenCredential, owner mcclient.IIdentityProvider, scope rbacscope.TRbacScope, addrOnly bool) *sqlchemy.SQuery {
var (
args = &usedAddressQueryArgs{
network: network,
userCred: userCred,
owner: owner,
scope: scope,
addrOnly: addrOnly,
}
queries = make([]sqlchemy.IQuery, len(usedAddressQueryProviders))
)
for i, provider := range usedAddressQueryProviders {
queries[i] = provider.usedAddressQuery(args)
}
return sqlchemy.Union(queries...).Query()
}
func (self *SNetwork) Contains(ip string) bool {
start, _ := netutils.NewIPV4Addr(self.GuestIpStart)
end, _ := netutils.NewIPV4Addr(self.GuestIpEnd)
addr, _ := netutils.NewIPV4Addr(ip)
return netutils.NewIPV4AddrRange(start, end).Contains(addr)
}
type SNetworkUsedAddressList []api.SNetworkUsedAddress
func (a SNetworkUsedAddressList) Len() int { return len(a) }
func (a SNetworkUsedAddressList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a SNetworkUsedAddressList) Less(i, j int) bool {
ipI, _ := netutils.NewIPV4Addr(a[i].IpAddr)
ipJ, _ := netutils.NewIPV4Addr(a[j].IpAddr)
return ipI < ipJ
}
func (network *SNetwork) GetDetailsAddresses(ctx context.Context, userCred mcclient.TokenCredential, input api.GetNetworkAddressesInput) (api.GetNetworkAddressesOutput, error) {
output := api.GetNetworkAddressesOutput{}
allowScope, _ := policy.PolicyManager.AllowScope(userCred, api.SERVICE_TYPE, network.KeywordPlural(), policy.PolicyActionGet, "addresses")
scope := rbacscope.String2ScopeDefault(input.Scope, allowScope)
if scope.HigherThan(allowScope) {
return output, errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", scope, allowScope)
}
netAddrs := make([]api.SNetworkUsedAddress, 0)
q := network.getUsedAddressQuery(userCred, userCred, scope, false)
err := q.All(&netAddrs)
if err != nil {
return output, httperrors.NewGeneralError(err)
}
sort.Sort(SNetworkUsedAddressList(netAddrs))
output.Addresses = netAddrs
return output, nil
}
// 同步接入云IP子网状态
// 本地IDC不支持此操作
func (net *SNetwork) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.NetworkSyncInput) (jsonutils.JSONObject, error) {
return net.PerformSync(ctx, userCred, query, input)
}
// 同步接入云IP子网状态
// 本地IDC不支持此操作
func (net *SNetwork) PerformSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.NetworkSyncInput) (jsonutils.JSONObject, error) {
vpc, _ := net.GetVpc()
if vpc != nil && vpc.IsManaged() {
return nil, StartResourceSyncStatusTask(ctx, userCred, net, "NetworkSyncstatusTask", "")
}
return nil, httperrors.NewUnsupportOperationError("on-premise network cannot sync status")
}
// 更改IP子网状态
func (net *SNetwork) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformStatusInput) (jsonutils.JSONObject, error) {
if len(input.Status) == 0 {
return nil, httperrors.NewMissingParameterError("status")
}
vpc, _ := net.GetVpc()
if vpc != nil && vpc.IsManaged() {
return nil, httperrors.NewUnsupportOperationError("managed network cannot change status")
}
if !utils.IsInStringArray(input.Status, []string{api.NETWORK_STATUS_AVAILABLE, api.NETWORK_STATUS_UNAVAILABLE}) {
return nil, httperrors.NewInputParameterError("invalid status %s", input.Status)
}
return net.SSharableVirtualResourceBase.PerformStatus(ctx, userCred, query, input)
}
func (net *SNetwork) GetChangeOwnerCandidateDomainIds() []string {
candidates := [][]string{}
wire, _ := net.GetWire()
if wire != nil {
vpc, _ := wire.GetVpc()
if vpc != nil {
candidates = append(candidates, vpc.GetChangeOwnerCandidateDomainIds())
}
candidates = append(candidates, db.ISharableChangeOwnerCandidateDomainIds(wire))
}
return db.ISharableMergeChangeOwnerCandidateDomainIds(net, candidates...)
}
func (manager *SNetworkManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
keys stringutils2.SSortedStrings,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemExportKeys")
}
if keys.ContainsAny(manager.SWireResourceBaseManager.GetExportKeys()...) {
q, err = manager.SWireResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SWireResourceBaseManager.ListItemExportKeys")
}
}
return q, nil
}
func (manager *SNetworkManager) AllowScope(userCred mcclient.TokenCredential) rbacscope.TRbacScope {
scope, _ := policy.PolicyManager.AllowScope(userCred, api.SERVICE_TYPE, NetworkManager.KeywordPlural(), policy.PolicyActionGet)
return scope
}
func (self *SNetwork) PerformSetBgpType(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.NetworkSetBgpTypeInput) (jsonutils.JSONObject, error) {
if self.BgpType == input.BgpType {
return nil, nil
}
if self.ServerType != api.NETWORK_TYPE_EIP {
return nil, httperrors.NewInputParameterError("BgpType attribute is only useful for eip network")
}
{
var eips []SElasticip
q := ElasticipManager.Query().
Equals("network_id", self.Id).
NotEquals("bgp_type", input.BgpType)
if err := db.FetchModelObjects(ElasticipManager, q, &eips); err != nil {
return nil, err
}
for i := range eips {
eip := &eips[i]
if diff, err := db.UpdateWithLock(ctx, eip, func() error {
eip.BgpType = input.BgpType
return nil
}); err != nil {
// no need to retry/restore here. return error
// and retry after user resolves the error
return nil, err
} else {
db.OpsLog.LogEvent(eip, db.ACT_UPDATE, diff, userCred)
}
}
}
if diff, err := db.Update(self, func() error {
self.BgpType = input.BgpType
return nil
}); err != nil {
return nil, err
} else {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_UPDATE, diff, userCred, true)
db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
}
return nil, nil
}
func (net *SNetwork) IsClassic() bool {
vpc, _ := net.GetVpc()
if vpc != nil && vpc.Id == api.DEFAULT_VPC_ID {
return true
}
return false
}
func (net *SNetwork) getAttachedHosts() ([]SHost, error) {
guestsQ := GuestManager.Query()
gnsQ := GuestnetworkManager.Query().Equals("network_id", net.Id).SubQuery()
guestsQ = guestsQ.Join(gnsQ, sqlchemy.Equals(guestsQ.Field("id"), gnsQ.Field("guest_id")))
guestsQ = guestsQ.IsNotEmpty("host_id")
guestsQ = guestsQ.AppendField(guestsQ.Field("host_id"))
guestsSubQ := guestsQ.SubQuery()
// unionQ := sqlchemy.Union(hns, guestsQ).Query().SubQuery()
q := HostManager.Query()
q = q.Join(guestsSubQ, sqlchemy.Equals(q.Field("id"), guestsSubQ.Field("host_id")))
q = q.Distinct()
hosts := make([]SHost, 0)
err := db.FetchModelObjects(HostManager, q, &hosts)
if err != nil {
return nil, errors.Wrap(err, "FetchModelObjects")
}
return hosts, nil
}
func (net *SNetwork) PerformSwitchWire(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input *api.NetworkSwitchWireInput,
) (jsonutils.JSONObject, error) {
if !net.IsClassic() {
return nil, errors.Wrap(httperrors.ErrNotSupported, "default vpc only")
}
wireObj, err := WireManager.FetchByIdOrName(userCred, input.WireId)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(WireManager.Keyword(), input.WireId)
} else {
return nil, errors.Wrapf(err, "WireManager.FetchByIdOrName %s", input.WireId)
}
}
wire := wireObj.(*SWire)
if net.WireId == wire.Id {
return nil, nil
}
oldWire, _ := net.GetWire()
if oldWire.VpcId != wire.VpcId {
return nil, errors.Wrapf(httperrors.ErrConflict, "cannot switch wires of other vpc")
}
hosts, err := net.getAttachedHosts()
if err != nil {
return nil, errors.Wrap(err, "getAttachedHosts")
}
unreachedHost := make([]string, 0)
for i := range hosts {
if hosts[i].HostType == api.HOST_TYPE_ESXI {
continue
}
if !hosts[i].IsAttach2Wire(wire.Id) {
unreachedHost = append(unreachedHost, hosts[i].Name)
}
}
if len(unreachedHost) > 0 {
return nil, errors.Wrapf(httperrors.ErrConflict, "wire %s not reachable for hosts %s", wire.Name, strings.Join(unreachedHost, ","))
}
diff, err := db.Update(net, func() error {
net.WireId = wire.Id
return nil
})
if err != nil {
return nil, errors.Wrap(err, "update wire_id")
}
{
err := net.syncAdditionalWires(ctx, nil)
if err != nil {
log.Errorf("syncAdditionalWires fail %s", err)
}
}
logclient.AddActionLogWithContext(ctx, net, logclient.ACT_UPDATE, diff, userCred, true)
db.OpsLog.LogEvent(net, db.ACT_UPDATE, diff, userCred)
// fix vmware hostnics wire
hns, err := HostnetworkManager.fetchHostnetworksByNetwork(net.Id)
if err != nil {
return nil, errors.Wrap(err, "HostnetworkManager.fetchHostnetworksByNetwork")
}
for i := range hns {
nic, err := hns[i].GetNetInterface()
if err != nil {
return nil, errors.Wrap(err, "Hostnetwork.GetNetInterface")
}
log.Errorf("PerformSwitchWire: change wireId for nic %s for hostnetwork %s", jsonutils.Marshal(nic), jsonutils.Marshal(hns[i]))
if len(nic.Bridge) > 0 {
log.Warningf("PerformSwitchWire: non-empty wireId %s for hostnetwork %s", jsonutils.Marshal(nic), jsonutils.Marshal(hns[i]))
continue
}
if nic.WireId != wire.Id {
_, err := db.Update(nic, func() error {
nic.WireId = wire.Id
return nil
})
if err != nil {
return nil, errors.Wrap(err, "Update NetInterface")
}
}
}
return nil, nil
}
func (net *SNetwork) fetchAdditionalWires() []api.SSimpleWire {
wires, err := NetworkAdditionalWireManager.FetchNetworkAdditionalWires(net.Id)
if err != nil {
log.Errorf("NetworkAdditionalWireManager.FetchNetworkAdditionalWires error %s", err)
}
return wires
}
func (net *SNetwork) PerformSyncAdditionalWires(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input *api.NetworSyncAdditionalWiresInput,
) (jsonutils.JSONObject, error) {
if !net.IsClassic() {
return nil, errors.Wrap(httperrors.ErrNotSupported, "default vpc only")
}
wireIds := make([]string, 0)
errs := make([]error, 0)
for _, wireId := range input.WireIds {
wireObj, err := WireManager.FetchByIdOrName(userCred, wireId)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
errs = append(errs, httperrors.NewResourceNotFoundError2(WireManager.Keyword(), wireId))
} else {
errs = append(errs, errors.Wrapf(err, "WireManager.FetchByIdOrNam %s", wireId))
}
}
wireIds = append(wireIds, wireObj.GetId())
}
if len(errs) > 0 {
return nil, errors.NewAggregate(errs)
}
err := net.syncAdditionalWires(ctx, wireIds)
if err != nil {
return nil, errors.Wrap(err, "syncAdditionalWires")
}
return nil, nil
}