mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-06-22 19:41:01 +08:00
2571 lines
81 KiB
Go
2571 lines
81 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"
|
||
"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...)
|
||
}
|