Files
cloudpods/pkg/compute/models/networks.go
2020-04-14 22:23:23 +08:00

2571 lines
81 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"database/sql"
"fmt"
"net"
"sort"
"strconv"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/pkg/util/netutils"
"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/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudprovider"
"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/billing"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/rand"
"yunion.io/x/onecloud/pkg/util/rbacutils"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
var (
ALL_NETWORK_TYPES = api.ALL_NETWORK_TYPES
)
type SNetworkManager struct {
db.SSharableVirtualResourceBaseManager
db.SExternalizedResourceBaseManager
SWireResourceBaseManager
}
var NetworkManager *SNetworkManager
func init() {
NetworkManager = &SNetworkManager{
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
SNetwork{},
"networks_tbl",
"network",
"networks",
),
}
NetworkManager.SetVirtualObject(NetworkManager)
}
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
GuestDns string `width:"16" 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"`
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"`
// 二层网络Id
// WireId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"`
// 服务器类型
// example: server
ServerType string `width:"16" charset:"ascii" default:"guest" nullable:"true" list:"user" update:"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"`
}
func (manager *SNetworkManager) GetContextManagers() [][]db.IModelManager {
return [][]db.IModelManager{
{WireManager},
}
}
func (manager *SNetworkManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowCreate(userCred, manager)
}
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) GetReservedIPs() ([]SReservedip, error) {
reservedIps := []SReservedip{}
q := ReservedipManager.Query().Equals("network_id", self.Id)
err := db.FetchModelObjects(ReservedipManager, q, &reservedIps)
if err != nil {
return nil, errors.Wrap(err, "db.FetchModelObjects")
}
return reservedIps, nil
}
func (self *SNetwork) ValidateDeleteCondition(ctx context.Context) error {
cnt, err := self.GetAllocatedNicCount()
if err != nil {
return httperrors.NewInternalServerError("GetAllocatedNicCount fail %s", err)
}
if cnt > 0 {
return httperrors.NewNotEmptyError("not an empty network")
}
return self.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx)
}
func (self *SNetwork) GetTotalNicCount() (int, error) {
total, err := self.GetAllocatedNicCount()
if err != nil {
return -1, err
}
cnt, err := self.GetReservedNicsCount()
if err != nil {
return -1, err
}
total += cnt
cnt, err = self.GetNetworkInterfacesCount()
if err != nil {
return -1, err
}
total += cnt
return total, nil
}
func (self *SNetwork) GetAllocatedNicCount() (int, error) {
total := 0
cnt, err := self.GetGuestnicsCount()
if err != nil {
return -1, err
}
total += cnt
cnt, err = self.GetGroupNicsCount()
if err != nil {
return -1, err
}
total += cnt
cnt, err = self.GetBaremetalNicsCount()
if err != nil {
return -1, err
}
total += cnt
cnt, err = self.GetLoadbalancerIpsCount()
if err != nil {
return -1, err
}
total += cnt
cnt, err = self.GetDBInstanceIpsCount()
if err != nil {
return -1, err
}
total += cnt
cnt, err = self.GetEipsCount()
if err != nil {
return -1, err
}
total += cnt
return total, nil
}
/*验证elb network可用并返回关联的region, zone,vpc, wire*/
func (self *SNetwork) ValidateElbNetwork(ipAddr net.IP) (*SCloudregion, *SZone, *SVpc, *SWire, error) {
// 验证IP Address可用
if ipAddr != nil {
ipS := ipAddr.String()
ip, err := netutils.NewIPV4Addr(ipS)
if err != nil {
return nil, nil, nil, nil, err
}
if !self.IsAddressInRange(ip) {
return nil, nil, nil, nil, httperrors.NewInputParameterError("address %s is not in the range of network %s(%s)",
ipS, self.Name, self.Id)
}
used, err := self.isAddressUsed(ipS)
if err != nil {
return nil, nil, nil, nil, httperrors.NewInternalServerError("isAddressUsed fail %s", err)
}
if used {
return nil, nil, nil, nil, httperrors.NewInputParameterError("address %s is already occupied", ipS)
}
}
// 验证网络存在剩余地址空间
freeCnt, err := self.getFreeAddressCount()
if err != nil {
return nil, nil, nil, nil, httperrors.NewInternalServerError("getFreeAddressCount fail %s", err)
}
if freeCnt <= 0 {
return nil, nil, nil, nil, httperrors.NewNotAcceptableError("network %s(%s) has no free addresses",
self.Name, self.Id)
}
// 验证网络可用
wire := self.GetWire()
if wire == nil {
return nil, nil, nil, nil, fmt.Errorf("getting wire failed")
}
vpc := wire.GetVpc()
if vpc == nil {
return nil, nil, nil, nil, fmt.Errorf("getting vpc failed")
}
var zone *SZone
if len(wire.ZoneId) > 0 {
zone = wire.GetZone()
if zone == nil {
return nil, nil, nil, nil, fmt.Errorf("getting zone failed")
}
}
region := wire.GetRegion()
if region == nil {
return nil, nil, nil, nil, fmt.Errorf("getting region failed")
}
return region, zone, vpc, wire, nil
}
func (self *SNetwork) GetGuestnicsCount() (int, error) {
return GuestnetworkManager.Query().Equals("network_id", self.Id).IsFalse("virtual").CountWithError()
}
func (self *SNetwork) GetGroupNicsCount() (int, error) {
return GroupnetworkManager.Query().Equals("network_id", self.Id).CountWithError()
}
func (self *SNetwork) GetBaremetalNicsCount() (int, error) {
return HostnetworkManager.Query().Equals("network_id", self.Id).CountWithError()
}
func (self *SNetwork) GetReservedNicsCount() (int, error) {
return ReservedipManager.Query().Equals("network_id", self.Id).CountWithError()
}
func (self *SNetwork) GetLoadbalancerIpsCount() (int, error) {
return LoadbalancernetworkManager.Query().Equals("network_id", self.Id).CountWithError()
}
func (self *SNetwork) GetDBInstanceIpsCount() (int, error) {
return DBInstanceNetworkManager.Query().Equals("network_id", self.Id).CountWithError()
}
func (self *SNetwork) GetEipsCount() (int, error) {
return ElasticipManager.Query().Equals("network_id", self.Id).CountWithError()
}
func (self *SNetwork) GetNetworkInterfacesCount() (int, error) {
sq := NetworkinterfacenetworkManager.Query("networkinterface_id").Equals("network_id", self.Id).Distinct().SubQuery()
return NetworkInterfaceManager.Query().In("id", sq).CountWithError()
}
func (manager *SNetworkManager) NewClassicNetwork(wire *SWire) (*SNetwork, error) {
_network, err := db.FetchByExternalId(manager, wire.Id)
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(&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(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 (self *SNetwork) getIPRange() netutils.IPV4AddrRange {
start, _ := netutils.NewIPV4Addr(self.GuestIpStart)
end, _ := netutils.NewIPV4Addr(self.GuestIpEnd)
return netutils.NewIPV4AddrRange(start, end)
}
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 len(self.AllocPolicy) > 0 && api.IPAllocationDirection(self.AllocPolicy) != api.IPAllocationNone {
allocDir = api.IPAllocationDirection(self.AllocPolicy)
}
if len(allocDir) == 0 || 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) 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() string {
if len(self.GuestDns) > 0 && len(self.GuestDomain) > 0 {
return self.GuestDns
} else {
return options.Options.DNSServer
}
}
func (self *SNetwork) GetDomain() string {
if len(self.GuestDns) > 0 && len(self.GuestDomain) > 0 {
return self.GuestDomain
} else {
return options.Options.DNSDomain
}
}
func (self *SNetwork) GetRoutes() [][]string {
ret := make([][]string, 0)
routes := self.GetMetadataJson("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, []string{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("dns_update_key_name", nil)
keySecret := self.GetMetadata("dns_update_key_secret", nil)
dnsSrv := self.GetMetadata("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(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()
/* 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) ([]SNetwork, []cloudprovider.ICloudNetwork, compare.SyncResult) {
syncOwnerId := provider.GetOwnerId()
lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId))
defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId))
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()
}
}
for i := 0; i < len(commondb); i += 1 {
err = commondb[i].SyncWithCloudNetwork(ctx, userCred, commonext[i], syncOwnerId, provider)
if err != nil {
syncResult.UpdateError(err)
} else {
syncMetadata(ctx, userCred, &commondb[i], commonext[i])
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 {
syncMetadata(ctx, userCred, new, added[i])
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)
if err != nil { // cannot delete
err = self.SetStatus(userCred, api.NETWORK_STATUS_UNKNOWN, "Sync to remove")
} else {
err = self.RealDelete(ctx, userCred)
}
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 {
extNet.Refresh()
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()
return nil
})
if err != nil {
log.Errorf("syncWithCloudNetwork error %s", err)
return err
}
db.OpsLog.LogSyncUpdate(self, diff, userCred)
SyncCloudProject(userCred, self, syncOwnerId, extNet, vpc.ManagerId)
if provider != nil {
self.SyncShareState(ctx, userCred, provider.getAccountShareInfo())
}
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)
newName, err := db.GenerateName(manager, syncOwnerId, extNet.GetName())
if err != nil {
return nil, err
}
net.Name = newName
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()
err = manager.TableSpec().Insert(&net)
if err != nil {
log.Errorf("newFromCloudZone fail %s", err)
return nil, err
}
vpc := wire.GetVpc()
SyncCloudProject(userCred, &net, syncOwnerId, extNet, vpc.ManagerId)
if provider != nil {
net.SyncShareState(ctx, userCred, provider.getAccountShareInfo())
}
db.OpsLog.LogEvent(&net, db.ACT_CREATE, net.GetShortDesc(ctx), userCred)
return &net, nil
}
func (self *SNetwork) IsAddressInRange(address netutils.IPV4Addr) bool {
return self.getIPRange().Contains(address)
}
func (self *SNetwork) isAddressUsed(address string) (bool, error) {
q := self.getUsedAddressQuery(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) GetOnPremiseNetworkOfIP(ipAddr string, serverType string, isPublic tristate.TriState) (*SNetwork, error) {
address, err := netutils.NewIPV4Addr(ipAddr)
if err != nil {
return nil, err
}
q := manager.Query()
wires := WireManager.Query().SubQuery()
vpcs := VpcManager.Query().SubQuery()
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 = q.Filter(sqlchemy.IsNullOrEmpty(vpcs.Field("manager_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, err
}
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()
hostwires := HostwireManager.Query().SubQuery()
hosts := HostManager.Query().SubQuery()
q := networks.Query(networks.Field("id"))
q = q.Join(hostwires, sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id")))
q = q.Join(hosts, sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id")))
q = q.Filter(sqlchemy.IsTrue(hosts.Field("enabled")))
q = q.Filter(sqlchemy.OR(
sqlchemy.Equals(hosts.Field("host_type"), api.HOST_TYPE_BAREMETAL),
sqlchemy.Equals(hosts.Field("host_status"), api.HOST_ONLINE)))
return AttachUsageQuery(q, hosts, nil, nil, providers, brands, cloudEnv, rangeObjs)
}
func (manager *SNetworkManager) totalPortCountQ(
scope rbacutils.TRbacScope,
userCred mcclient.IIdentityProvider,
providers []string,
brands []string,
cloudEnv string,
rangeObjs []db.IStandaloneModel,
) *sqlchemy.SQuery {
q := manager.allNetworksQ(providers, brands, cloudEnv, rangeObjs)
switch scope {
case rbacutils.ScopeSystem:
case rbacutils.ScopeDomain:
q = q.Equals("domain_id", userCred.GetProjectDomainId())
case rbacutils.ScopeProject:
q = q.Equals("tenant_id", userCred.GetProjectId())
}
return manager.Query().In("id", q.Distinct().SubQuery())
}
type NetworkPortStat struct {
Count int
CountExt int
}
func (manager *SNetworkManager) TotalPortCount(
scope rbacutils.TRbacScope,
userCred mcclient.IIdentityProvider,
providers []string, brands []string, cloudEnv string,
rangeObjs []db.IStandaloneModel,
) NetworkPortStat {
nets := make([]SNetwork, 0)
err := manager.totalPortCountQ(
scope,
userCred,
providers, brands, cloudEnv,
rangeObjs,
).All(&nets)
if err != nil {
log.Errorf("TotalPortCount: %v", err)
}
ct := 0
ctExt := 0
for _, net := range nets {
count := net.getIPRange().AddressCount()
if net.IsExitNetwork() {
ctExt += count
} else {
ct += count
}
}
return NetworkPortStat{Count: ct, CountExt: ctExt}
}
type SNicConfig struct {
Mac string
Index int8
Ifname string
}
func parseNetworkInfo(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(userCred, net) && net.DomainId == userCred.GetProjectDomainId()) ||
db.IsAdminAllowGet(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) getFreeAddressCount() (int, error) {
used, err := self.GetTotalNicCount()
if err != nil {
return -1, err
}
return self.getIPRange().AddressCount() - used, nil
}
func isValidNetworkInfo(userCred mcclient.TokenCredential, netConfig *api.NetworkConfig) error {
if len(netConfig.Network) > 0 {
netObj, err := NetworkManager.FetchByIdOrName(userCred, netConfig.Network)
if err != nil {
return httperrors.NewResourceNotFoundError("Network %s not found %s", 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) {
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 {
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)
}
freeCnt, err := net.getFreeAddressCount()
if err != nil {
return httperrors.NewInternalServerError("getFreeAddressCount fail %s", err)
}
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(netConfig *api.NetworkConfig) bool {
if len(netConfig.Network) > 0 {
netObj, _ := NetworkManager.FetchById(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 (self *SNetwork) getMoreDetails(ctx context.Context, out api.NetworkDetails, isList bool) (api.NetworkDetails, error) {
out.Exit = false
if self.IsExitNetwork() {
out.Exit = true
}
out.Ports = self.GetPorts()
out.PortsUsed, _ = self.GetTotalNicCount()
out.Vnics, _ = self.GetGuestnicsCount()
out.BmVnics, _ = self.GetBaremetalNicsCount()
out.LbVnics, _ = self.GetLoadbalancerIpsCount()
out.EipVnics, _ = self.GetEipsCount()
out.GroupVnics, _ = self.GetGroupNicsCount()
out.ReserveVnics, _ = self.GetReservedNicsCount()
out.Routes = self.GetRoutes()
out.Schedtags = GetSchedtagsDetailsToResourceV2(self, ctx)
return out, nil
}
func (self *SNetwork) GetExtraDetails(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
isList bool,
) (api.NetworkDetails, error) {
return api.NetworkDetails{}, nil
}
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)
for i := range rows {
rows[i] = api.NetworkDetails{
SharableVirtualResourceDetails: virtRows[i],
WireResourceInfo: wireRows[i],
}
rows[i], _ = objs[i].(*SNetwork).getMoreDetails(ctx, rows[i], isList)
}
return rows
}
func (self *SNetwork) AllowPerformReserveIp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "reserve-ip")
}
// 预留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
}
func (self *SNetwork) AllowPerformReleaseReservedIp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "release-reserved-ip")
}
// 释放预留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) AllowGetDetailsReservedIps(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowGetSpec(userCred, self, "reserved-ips")
}
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) {
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
}
}
return "", fmt.Errorf("failed finding ifname hint after 3 tries")
}
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) (w *SWire, v *SVpc, cr *SCloudregion, err error) {
zObj, err := ZoneManager.FetchByIdOrName(userCred, input.Zone)
if err != nil {
err = errors.Wrapf(err, "zone %s", input.Zone)
return
}
z := zObj.(*SZone)
vObj, err := VpcManager.FetchByIdOrName(userCred, input.Vpc)
if err != nil {
err = errors.Wrapf(err, "vpc %s", input.Vpc)
return
}
v = vObj.(*SVpc)
var wires []SWire
// 华为云,ucloud wire zone_id 为空
cr = z.GetRegion()
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
} else if len(wires) > 1 {
err = httperrors.NewConflictError("found %d wires for zone %s and vpc %s", len(wires), input.Zone, input.Vpc)
return
} else if len(wires) == 1 {
w = &wires[0]
return
}
// wire not found. We auto create one for OneCloud vpc
if cr.Provider == api.CLOUD_PROVIDER_ONECLOUD {
w, err = v.initWire(ctx, z)
if err != nil {
err = errors.Wrapf(err, "vpc %s init wire", v.Id)
return
}
return
}
err = httperrors.NewNotFoundError("wire not found for zone %s and vpc %s", input.Zone, input.Vpc)
return
}
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, ALL_NETWORK_TYPES) {
return input, httperrors.NewInputParameterError("Invalid server_type: %s", input.ServerType)
}
{
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
)
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()
input.GuestIpMask = int64(prefix.MaskLen)
// 根据掩码得到合法的GuestIpPrefix
input.GuestIpPrefix = prefix.String()
} else {
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)
}
if !isValidMaskLen(input.GuestIpMask) {
return input, httperrors.NewInputParameterError("Invalid masklen %d", input.GuestIpMask)
}
for key, ipStr := range map[string]string{"guest_gateway": input.GuestGateway, "guest_dns": input.GuestDns, "guest_dhcp": input.GuestDHCP} {
if len(ipStr) > 0 {
if key == "guest_dhcp" {
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 !regutils.MatchIPAddr(ipStr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr)
}
}
}
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")
}
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 masklen >= 30 {
return input, httperrors.NewInputParameterError("subnet masklen should be smaller than 30")
}
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 := manager.getAllNetworks(wire.Id, "")
if nets == nil {
return input, httperrors.NewInternalServerError("query all networks fail")
}
if isOverlapNetworks(nets, ipStart, ipEnd) {
return input, httperrors.NewInputParameterError("Conflict address space with existing networks")
}
}
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, endIp netutils.IPV4Addr
var err error
ipStartStr := input.GuestIpStart
ipEndStr := input.GuestIpEnd
if len(ipStartStr) > 0 || len(ipEndStr) > 0 {
if len(ipStartStr) > 0 {
startIp, err = netutils.NewIPV4Addr(ipStartStr)
if err != nil {
return input, httperrors.NewInputParameterError("Invalid start ip: %s %s", ipStartStr, err)
}
} else {
startIp, _ = netutils.NewIPV4Addr(self.GuestIpStart)
}
if len(ipEndStr) > 0 {
endIp, err = netutils.NewIPV4Addr(ipEndStr)
if err != nil {
return input, httperrors.NewInputParameterError("invalid end ip: %s %s", ipEndStr, 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 {
usedIp, _ := netutils.NewIPV4Addr(usedIpStr)
if !netRange.Contains(usedIp) {
return input, httperrors.NewInputParameterError("Address been assigned out of new range")
}
}
input.GuestIpStart = startIp.String()
input.GuestIpEnd = endIp.String()
}
if input.GuestIpMask != nil {
maskLen64 := int64(*input.GuestIpMask)
if !isValidMaskLen(maskLen64) {
return input, httperrors.NewInputParameterError("Invalid masklen %d", maskLen64)
}
}
for key, ipStr := range map[string]string{
"guest_gateway": input.GuestGateway,
"guest_dns": input.GuestDns,
"guest_dhcp": input.GuestDhcp,
} {
if len(ipStr) > 0 {
if key == "guest_dhcp" {
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 !regutils.MatchIPAddr(ipStr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr)
}
}
}
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() && !self.isOneCloudVpcNetwork() {
var err error
input, err = self.validateUpdateData(ctx, userCred, query, input)
if err != nil {
return input, errors.Wrap(err, "validateUpdateData")
}
} else {
input.GuestIpStart = ""
input.GuestIpEnd = ""
input.GuestIpMask = nil
input.GuestGateway = ""
input.GuestDns = ""
input.GuestDomain = ""
input.GuestDhcp = ""
}
var err error
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() {
if db.IsAdminAllowPerform(userCred, self, "public") && ownerId.GetProjectDomainId() == userCred.GetProjectDomainId() {
self.SetShare(rbacutils.ScopeSystem)
} else if db.IsDomainAllowPerform(userCred, self, "public") && ownerId.GetProjectId() == userCred.GetProjectId() {
self.SetShare(rbacutils.ScopeDomain)
} else {
self.SetShare(rbacutils.ScopeNone)
}
} else {
self.SetShare(rbacutils.ScopeNone)
}
data.(*jsonutils.JSONDict).Set("public_scope", jsonutils.NewString(self.PublicScope))
}
return self.SSharableVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
}
func (self *SNetwork) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
self.SSharableVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
vpc := self.GetVpc()
if vpc != nil && vpc.IsManaged() {
task, err := taskman.TaskManager.NewTask(ctx, "NetworkCreateTask", self, userCred, nil, "", "", nil)
if err != nil {
log.Errorf("networkcreateTask create fail: %s", err)
} else {
task.ScheduleRun(nil)
}
} else {
self.SetStatus(userCred, api.NETWORK_STATUS_AVAILABLE, "")
self.ClearSchedDescCache()
}
}
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 {
db.OpsLog.LogEvent(self, db.ACT_DELOCATE, self.GetShortDesc(ctx), userCred)
self.SetStatus(userCred, api.NETWORK_STATUS_DELETED, "real delete")
self.ClearSchedDescCache()
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, err := self.GetReservedIPs()
if err != nil {
return errors.Wrap(err, "GetReservedNicsCount")
}
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)
}
}
return self.SSharableVirtualResourceBase.Delete(ctx, userCred)
}
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() (cloudprovider.ICloudNetwork, error) {
wire := self.GetWire()
if wire == nil {
msg := "No wire for this network????"
log.Errorf(msg)
return nil, fmt.Errorf(msg)
}
iwire, err := wire.GetIWire()
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 {
vpc := self.GetVpc()
region := self.GetRegion()
if region.Provider == api.CLOUD_PROVIDER_ONECLOUD && vpc.Id != api.DEFAULT_VPC_ID {
return true
}
return false
}
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")
}
q, err = manager.SWireResourceBaseManager.ListItemFilter(ctx, q, userCred, input.WireFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SWireResourceBaseManager.ListItemFilter")
}
if input.Usable != nil && *input.Usable {
wires := WireManager.Query().SubQuery()
zones := ZoneManager.Query().SubQuery()
vpcs := VpcManager.Query().SubQuery()
cloudproviders := CloudproviderManager.Query().SubQuery()
providerSQ := cloudproviders.Query(cloudproviders.Field("id")).Filter(
sqlchemy.AND(
sqlchemy.IsTrue(cloudproviders.Field("enabled")),
sqlchemy.In(cloudproviders.Field("status"), api.CLOUD_PROVIDER_VALID_STATUS),
sqlchemy.In(cloudproviders.Field("health_status"), api.CLOUD_PROVIDER_VALID_HEALTH_STATUS),
),
)
regions := CloudregionManager.Query().SubQuery()
sq := wires.Query(wires.Field("id")).
Join(vpcs, sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id"))).
Join(zones, sqlchemy.OR(sqlchemy.Equals(wires.Field("zone_id"), zones.Field("id")), sqlchemy.IsNullOrEmpty(wires.Field("zone_id")))).
Join(regions, sqlchemy.Equals(zones.Field("cloudregion_id"), regions.Field("id"))).
Filter(sqlchemy.AND(
sqlchemy.Equals(vpcs.Field("status"), api.VPC_STATUS_AVAILABLE),
sqlchemy.Equals(zones.Field("status"), api.ZONE_ENABLE),
sqlchemy.Equals(regions.Field("status"), api.CLOUD_REGION_STATUS_INSERVER),
sqlchemy.OR(
sqlchemy.In(vpcs.Field("manager_id"), providerSQ.SubQuery()),
sqlchemy.IsNullOrEmpty(vpcs.Field("manager_id")),
),
))
q = q.In("wire_id", sq.SubQuery()).Equals("status", api.NETWORK_STATUS_AVAILABLE)
}
hostStr := input.Host
if len(hostStr) > 0 {
hostObj, err := HostManager.FetchByIdOrName(userCred, hostStr)
if err != nil {
return nil, httperrors.NewResourceNotFoundError2(HostManager.Keyword(), hostStr)
}
host := hostObj.(*SHost)
sq := HostwireManager.Query("wire_id").Equals("host_id", hostObj.GetId())
if len(host.OvnVersion) > 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()))
}
}
if len(input.Ip) > 0 {
ipIa, err := parseIpToIntArray(input.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"))
ipCondtion := sqlchemy.OR(sqlchemy.Between(ipField, ipStart, ipEnd), sqlchemy.Contains(q.Field("guest_ip_start"), input.Ip), sqlchemy.Contains(q.Field("guest_ip_end"), input.Ip))
q = q.Filter(ipCondtion)
}
if len(input.IfnameHint) > 0 {
q = q.In("ifname_hint", input.IfnameHint)
}
if len(input.GuestIpStart) > 0 {
q = q.In("guest_ip_start", input.GuestIpStart)
}
if len(input.GuestIpEnd) > 0 {
q = q.In("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.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)
}
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")
}
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
})
}
}
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 (self *SNetwork) AllowPerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "purge")
}
// 清除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)
if err != nil {
return nil, err
}
vpc := self.GetVpc()
if vpc != nil && len(vpc.ExternalId) > 0 {
provider := vpc.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 (self *SNetwork) AllowPerformSplit(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "split")
}
func (self *SNetwork) AllowPerformMerge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "merge")
}
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
}
if self.WireId != net.WireId || self.GuestGateway != net.GuestGateway {
err = httperrors.NewInputParameterError("Invalid Target Network: %s", input.Target)
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return nil, 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)
if ipNE.StepUp() == ipSS {
startIp, endIp = net.GuestIpStart, self.GuestIpEnd
} else if ipSE.StepUp() == ipNS {
startIp, endIp = self.GuestIpStart, net.GuestIpEnd
} else {
note := "Incontinuity Network for %s and %s"
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE,
fmt.Sprintf(note, self.Name, net.Name), userCred, false)
return nil, httperrors.NewBadRequestError(note, self.Name, net.Name)
}
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 nil, err
}
if err := NetworkManager.handleNetworkIdChange(ctx, &networkIdChangeArgs{
action: logclient.ACT_MERGE,
oldNet: self,
newNet: net,
userCred: userCred,
}); err != nil {
return nil, 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 nil, 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, 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)
}
lockman.LockClass(ctx, NetworkManager, db.GetLockClassKey(NetworkManager, userCred))
defer lockman.ReleaseClass(ctx, NetworkManager, db.GetLockClassKey(NetworkManager, userCred))
if len(input.Name) > 0 {
if err := db.NewNameValidator(NetworkManager, userCred, input.Name, ""); err != nil {
return nil, httperrors.NewInputParameterError("Duplicate name %s", input.Name)
}
} else {
input.Name, err = db.GenerateName(NetworkManager, userCred, fmt.Sprintf("%s#", self.Name))
if err != nil {
return nil, httperrors.NewInternalServerError("GenerateName fail %s", err)
}
}
network := &SNetwork{}
network.Name = input.Name
network.IfnameHint, err = NetworkManager.newIfnameHint(input.Name)
if err != nil {
return nil, httperrors.NewBadRequestError("Generate ifname hint failed %s", err)
}
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.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
err = NetworkManager.TableSpec().Insert(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) AllowPerformTryCreateNetwork(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowClassPerform(userCred, manager, "try-create-network")
}
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.WireId = nm.WireId
newNetwork.ServerType = input.ServerType
newNetwork.IsPublic = nm.IsPublic
newNetwork.ProjectId = userCred.GetProjectId()
newNetwork.DomainId = userCred.GetProjectDomainId()
newName, err := db.GenerateName(NetworkManager, userCred, fmt.Sprintf("%s#", nm.Name))
if err != nil {
return nil, httperrors.NewInternalServerError("GenerateName fail %s", err)
}
newNetwork.Name = newName
err = NetworkManager.TableSpec().Insert(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))
}
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) AllowPerformSetSchedtag(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return AllowPerformSetResourceSchedtag(network, ctx, userCred, query, data)
}
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) {
ret, err := network.SSharableVirtualResourceBase.PerformChangeOwner(ctx, userCred, query, input)
if err != nil {
return nil, err
}
network.ClearSchedDescCache()
return ret, nil
}
func (network *SNetwork) AllowGetDetailsAddresses(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return network.IsOwner(userCred) || db.IsAdminAllowGetSpec(userCred, network, "addresses")
}
func (network *SNetwork) getUsedAddressQuery(addrOnly bool) *sqlchemy.SQuery {
guestnetworks := GuestnetworkManager.Query().Equals("network_id", network.Id).SubQuery()
var guestNetQ *sqlchemy.SQuery
if addrOnly {
guestNetQ = guestnetworks.Query(
guestnetworks.Field("ip_addr"),
)
} else {
guests := GuestManager.Query().SubQuery()
guestNetQ = guestnetworks.Query(
guestnetworks.Field("ip_addr"),
guestnetworks.Field("mac_addr"),
sqlchemy.NewStringField(GuestManager.KeywordPlural()).Label("owner_type"),
guestnetworks.Field("guest_id").Label("owner_id"),
guests.Field("name").Label("owner"),
sqlchemy.NewStringField("").Label("associate_id"),
sqlchemy.NewStringField("").Label("associate_type"),
guestnetworks.Field("created_at"),
).Join(
guests,
sqlchemy.Equals(
guests.Field("id"),
guestnetworks.Field("guest_id"),
),
)
}
groupnetworks := GroupnetworkManager.Query().Equals("network_id", network.Id).SubQuery()
var groupNetQ *sqlchemy.SQuery
if addrOnly {
groupNetQ = groupnetworks.Query(
groupnetworks.Field("ip_addr"),
)
} else {
groups := GroupManager.Query().SubQuery()
groupNetQ = groupnetworks.Query(
groupnetworks.Field("ip_addr"),
sqlchemy.NewStringField("").Label("mac_addr"),
sqlchemy.NewStringField(GroupManager.KeywordPlural()).Label("owner_type"),
groupnetworks.Field("group_id").Label("owner_id"),
groups.Field("name").Label("owner"),
sqlchemy.NewStringField("").Label("associate_id"),
sqlchemy.NewStringField("").Label("associate_type"),
groupnetworks.Field("created_at"),
).Join(
groups,
sqlchemy.Equals(
groups.Field("id"),
groupnetworks.Field("group_id"),
),
)
}
hostnetworks := HostnetworkManager.Query().Equals("network_id", network.Id).SubQuery()
var hostNetQ *sqlchemy.SQuery
if addrOnly {
hostNetQ = hostnetworks.Query(
hostnetworks.Field("ip_addr"),
)
} else {
hosts := HostManager.Query().SubQuery()
hostNetQ = hostnetworks.Query(
hostnetworks.Field("ip_addr"),
hostnetworks.Field("mac_addr"),
sqlchemy.NewStringField(HostManager.KeywordPlural()).Label("owner_type"),
hostnetworks.Field("baremetal_id").Label("owner_id"),
hosts.Field("name").Label("owner"),
sqlchemy.NewStringField("").Label("associate_id"),
sqlchemy.NewStringField("").Label("associate_type"),
hostnetworks.Field("created_at"),
).Join(
hosts,
sqlchemy.Equals(
hosts.Field("id"),
hostnetworks.Field("baremetal_id"),
),
)
}
reserved := ReservedipManager.Query().Equals("network_id", network.Id).SubQuery()
var reservedQ *sqlchemy.SQuery
if addrOnly {
reservedQ = reserved.Query(
reserved.Field("ip_addr"),
)
} else {
reservedQ = reserved.Query(
reserved.Field("ip_addr"),
sqlchemy.NewStringField("").Label("mac_addr"),
sqlchemy.NewStringField(ReservedipManager.KeywordPlural()).Label("owner_type"),
reserved.Field("id").Label("owner_id"),
reserved.Field("notes").Label("owner"),
sqlchemy.NewStringField("").Label("associate_id"),
sqlchemy.NewStringField("").Label("associate_type"),
reserved.Field("created_at"),
)
}
reservedQ = reservedQ.Filter(sqlchemy.OR(
sqlchemy.IsNullOrEmpty(reserved.Field("expired_at")),
sqlchemy.GT(reserved.Field("expired_at"), time.Now()),
))
lbnetworks := LoadbalancernetworkManager.Query().Equals("network_id", network.Id).SubQuery()
var lbNetQ *sqlchemy.SQuery
if addrOnly {
lbNetQ = lbnetworks.Query(
lbnetworks.Field("ip_addr"),
)
} else {
loadbalancers := LoadbalancerManager.Query().SubQuery()
lbNetQ = lbnetworks.Query(
lbnetworks.Field("ip_addr"),
sqlchemy.NewStringField("").Label("mac_addr"),
sqlchemy.NewStringField(LoadbalancerManager.KeywordPlural()).Label("owner_type"),
lbnetworks.Field("loadbalancer_id").Label("owner_id"),
loadbalancers.Field("name").Label("owner"),
sqlchemy.NewStringField("").Label("associate_id"),
sqlchemy.NewStringField("").Label("associate_type"),
lbnetworks.Field("created_at"),
).Join(
loadbalancers,
sqlchemy.Equals(
loadbalancers.Field("id"),
lbnetworks.Field("loadbalancer_id"),
),
)
}
elasticips := ElasticipManager.Query().Equals("network_id", network.Id).SubQuery()
var eipQ *sqlchemy.SQuery
if addrOnly {
eipQ = elasticips.Query(
elasticips.Field("ip_addr"),
)
} else {
eipQ = elasticips.Query(
elasticips.Field("ip_addr"),
sqlchemy.NewStringField("").Label("mac_addr"),
sqlchemy.NewStringField(ElasticipManager.KeywordPlural()).Label("owner_type"),
elasticips.Field("id").Label("owner_id"),
elasticips.Field("name").Label("owner"),
elasticips.Field("associate_id"),
elasticips.Field("associate_type"),
elasticips.Field("created_at"),
)
}
netifnetworks := NetworkinterfacenetworkManager.Query().Equals("network_id", network.Id).SubQuery()
var netifsQ *sqlchemy.SQuery
if addrOnly {
netifsQ = netifnetworks.Query(
netifnetworks.Field("ip_addr"),
)
} else {
netifs := NetworkInterfaceManager.Query().SubQuery()
netifsQ = netifnetworks.Query(
netifnetworks.Field("ip_addr"),
netifs.Field("mac").Label("mac_addr"),
sqlchemy.NewStringField(NetworkInterfaceManager.KeywordPlural()).Label("owner_type"),
netifnetworks.Field("networkinterface_id").Label("owner_id"),
netifs.Field("name").Label("owner"),
netifs.Field("associate_id"),
netifs.Field("associate_type"),
netifnetworks.Field("created_at"),
).Join(
netifs,
sqlchemy.Equals(
netifnetworks.Field("networkinterface_id"),
netifs.Field("id"),
),
)
}
return sqlchemy.Union(guestNetQ, groupNetQ, hostNetQ, reservedQ, lbNetQ, eipQ, netifsQ).Query()
}
type SNetworkAddressList []api.SNetworkAddress
func (a SNetworkAddressList) Len() int { return len(a) }
func (a SNetworkAddressList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a SNetworkAddressList) 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, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
netAddrs := make([]api.SNetworkAddress, 0)
q := network.getUsedAddressQuery(false)
err := q.All(&netAddrs)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
sort.Sort(SNetworkAddressList(netAddrs))
result := jsonutils.NewDict()
result.Add(jsonutils.Marshal(netAddrs), "addresses")
return result, nil
}
func (net *SNetwork) AllowPerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return net.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, net, "syncstatus")
}
// 同步接入云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)
}
func (net *SNetwork) AllowPerformSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return net.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, net, "sync")
}
// 同步接入云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")
}
func (net *SNetwork) AllowPerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformStatusInput) bool {
return net.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, net, "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{
net.SSharableVirtualResourceBase.GetChangeOwnerCandidateDomainIds(),
}
wire := net.GetWire()
if wire != nil {
candidates = append(candidates, db.ISharableChangeOwnerCandidateDomainIds(wire))
}
return db.ISharableMergeChangeOwnerCandidateDomainIds(net, candidates...)
}