mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-16 20:18:24 +08:00
3199 lines
102 KiB
Go
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
|
||
}
|