Files
cloudpods/pkg/compute/models/networks.go
T
2019-05-21 22:36:52 +08:00

1917 lines
61 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"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"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"
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/logclient"
)
var (
ALL_NETWORK_TYPES = api.ALL_NETWORK_TYPES
)
type IPAddlocationDirection string
const (
IPAllocationStepdown IPAddlocationDirection = "stepdown"
IPAllocationStepup IPAddlocationDirection = "stepup"
IPAllocationRadnom IPAddlocationDirection = "random"
IPAllocationNone IPAddlocationDirection = "none"
IPAllocationDefault = ""
)
type SNetworkManager struct {
db.SSharableVirtualResourceBaseManager
}
var NetworkManager *SNetworkManager
func init() {
NetworkManager = &SNetworkManager{
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
SNetwork{},
"networks_tbl",
"network",
"networks",
),
}
NetworkManager.NameLength = 9
NetworkManager.NameRequireAscii = true
}
type SNetwork struct {
db.SSharableVirtualResourceBase
GuestIpStart string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` // Column(VARCHAR(16, charset='ascii'), nullable=False)
GuestIpEnd string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` // Column(VARCHAR(16, charset='ascii'), nullable=False)
GuestIpMask int8 `nullable:"false" list:"user" update:"user" create:"required"` // Column(TINYINT, nullable=False)
GuestGateway string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
GuestDns string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
GuestDhcp string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
GuestDomain string `width:"128" charset:"ascii" nullable:"true" get:"user" update:"user"` // Column(VARCHAR(128, charset='ascii'), nullable=True)
GuestIp6Start string `width:"64" charset:"ascii" nullable:"true"` // Column(VARCHAR(64, charset='ascii'), nullable=True)
GuestIp6End string `width:"64" charset:"ascii" nullable:"true"` // Column(VARCHAR(64, charset='ascii'), nullable=True)
GuestIp6Mask int8 `nullable:"true"` // Column(TINYINT, nullable=True)
GuestGateway6 string `width:"64" charset:"ascii" nullable:"true"` // Column(VARCHAR(64, charset='ascii'), nullable=True)
GuestDns6 string `width:"64" charset:"ascii" nullable:"true"` // Column(VARCHAR(64, charset='ascii'), nullable=True)
GuestDomain6 string `width:"128" charset:"ascii" nullable:"true"` // Column(VARCHAR(128, charset='ascii'), nullable=True)
VlanId int `nullable:"false" default:"1" list:"user" update:"user" create:"optional"` // Column(Integer, nullable=False, default=1)
WireId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` // Column(VARCHAR(36, charset='ascii'), nullable=False)
// IsChanged = Column(Boolean, nullable=False, default=False)
ServerType string `width:"16" charset:"ascii" default:"guest" nullable:"true" list:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
AllocPolicy string `width:"16" charset:"ascii" nullable:"true" get:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
AllocTimoutSeconds int `default:"0" nullable:"true" get:"admin"`
}
func (manager *SNetworkManager) GetContextManagers() [][]db.IModelManager {
return [][]db.IModelManager{
{WireManager},
}
}
func (self *SNetwork) GetWire() *SWire {
w, _ := WireManager.FetchById(self.WireId)
if w != nil {
return w.(*SWire)
}
return nil
}
func (self *SNetwork) GetVpc() *SVpc {
wire := self.GetWire()
if wire != nil {
return wire.getVpc()
}
return nil
}
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) ValidateDeleteCondition(ctx context.Context) error {
cnt, err := self.GetTotalNicCount()
if err != nil {
return httperrors.NewInternalServerError("GetTotalNicCount 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 := 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.GetReservedNicsCount()
if err != nil {
return -1, err
}
total += cnt
cnt, err = self.GetLoadbalancerIpsCount()
if err != nil {
return -1, err
}
total += cnt
cnt, err = self.GetEipsCount()
if err != nil {
return -1, err
}
total += cnt
return total, 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) GetEipsCount() (int, error) {
return ElasticipManager.Query().Equals("network_id", self.Id).CountWithError()
}
func (self *SNetwork) GetUsedAddresses() map[string]bool {
used := make(map[string]bool)
for _, tbl := range []*sqlchemy.SSubQuery{
GuestnetworkManager.Query().SubQuery(),
GroupnetworkManager.Query().SubQuery(),
HostnetworkManager.Query().SubQuery(),
ReservedipManager.Query().SubQuery(),
LoadbalancernetworkManager.Query().SubQuery(),
ElasticipManager.Query().SubQuery(),
} {
q := tbl.Query(tbl.Field("ip_addr")).Equals("network_id", self.Id)
rows, err := q.Rows()
if err != nil {
log.Errorf("GetUsedAddresses query fail: %s", err)
return nil
}
defer rows.Close()
for rows.Next() {
var ip string
err = rows.Scan(&ip)
if err != nil {
log.Errorf("GetUsedAddresses scan fail: %s", err)
return nil
}
used[ip] = 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 IPAddlocationDirection) (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 && IPAddlocationDirection(self.AllocPolicy) != IPAllocationNone {
allocDir = IPAddlocationDirection(self.AllocPolicy)
}
if len(allocDir) == 0 || allocDir == 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 == 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 IPAddlocationDirection, reserved bool) (string, error) {
if reserved {
rip := ReservedipManager.GetReservedIP(self, candidate)
if rip == nil {
return "", httperrors.NewInsufficientResourceError("Reserved address %s not found", candidate)
}
rip.Release(ctx, userCred, self)
return candidate, nil
} else {
cand, err := self.getFreeIP(addrTable, recentUsedAddrTable, candidate, allocDir)
if err != nil {
return "", err
}
return cand, nil
}
}
func (self *SNetwork) GetUsedIfnames() map[string]bool {
used := make(map[string]bool)
tbl := GuestnetworkManager.Query().SubQuery()
q := tbl.Query(tbl.Field("ifname")).Equals("network_id", self.Id)
rows, err := q.Rows()
if err != nil {
log.Errorf("GetUsedIfnames query fail: %s", err)
return nil
}
defer rows.Close()
for rows.Next() {
var ifname string
err = rows.Scan(&ifname)
if err != nil {
log.Errorf("GetUsedIfnames scan fail: %s", err)
return nil
}
used[ifname] = true
}
return used
}
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, projectId string) ([]SNetwork, []cloudprovider.ICloudNetwork, compare.SyncResult) {
ownerProjId := projectId
lockman.LockClass(ctx, manager, ownerProjId)
defer lockman.ReleaseClass(ctx, manager, ownerProjId)
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], projectId)
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, ownerProjId)
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)
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, projectId string) error {
vpc := self.GetWire().getVpc()
diff, err := db.UpdateWithLock(ctx, self, func() error {
extNet.Refresh()
self.Name = extNet.GetName()
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.IsPublic = extNet.GetIsPublic()
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, projectId, extNet, vpc.ManagerId)
return nil
}
func (manager *SNetworkManager) newFromCloudNetwork(ctx context.Context, userCred mcclient.TokenCredential, extNet cloudprovider.ICloudNetwork, wire *SWire, projectId string) (*SNetwork, error) {
net := SNetwork{}
net.SetModelManager(manager)
newName, err := db.GenerateName(manager, projectId, 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()
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, projectId, extNet, vpc.ManagerId)
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) {
managers := []db.IModelManager{
GuestnetworkManager,
GroupnetworkManager,
HostnetworkManager,
ReservedipManager,
LoadbalancernetworkManager,
}
for _, manager := range managers {
q := manager.Query().Equals("ip_addr", address).Equals("network_id", self.Id)
cnt, err := q.CountWithError()
if err != nil {
return false, err
}
if cnt > 0 {
return true, nil
}
}
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, rangeObj 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, rangeObj)
}
func (manager *SNetworkManager) totalPortCountQ(userCred mcclient.TokenCredential, providers []string, rangeObj db.IStandaloneModel) *sqlchemy.SQuery {
q := manager.allNetworksQ(providers, rangeObj)
if userCred != nil && !db.IsAdminAllowList(userCred, manager) {
q = q.Filter(sqlchemy.OR(
sqlchemy.Equals(q.Field("tenant_id"), userCred.GetProjectId()),
sqlchemy.IsTrue(q.Field("is_public"))))
}
return manager.Query().In("id", q.Distinct().SubQuery())
}
type NetworkPortStat struct {
Count int
CountExt int
}
func (manager *SNetworkManager) TotalPortCount(
userCred mcclient.TokenCredential,
providers []string,
rangeObj db.IStandaloneModel,
) NetworkPortStat {
nets := make([]SNetwork, 0)
err := manager.totalPortCountQ(userCred, providers, rangeObj).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.IsOwner(userCred) || net.IsPublic || db.IsAdminAllowGet(userCred, net) {
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) getZone() *SZone {
wire := self.GetWire()
if wire != nil {
return wire.GetZone()
}
return nil
}
func (self *SNetwork) getVpc() *SVpc {
wire := self.GetWire()
if wire != nil {
return wire.getVpc()
}
return nil
}
func (self *SNetwork) getRegion() *SCloudregion {
wire := self.GetWire()
if wire != nil {
return wire.getRegion()
}
return nil
}
func (self *SNetwork) GetPorts() int {
return self.getIPRange().AddressCount()
}
func (self *SNetwork) getMoreDetails(ctx context.Context, extra *jsonutils.JSONDict) *jsonutils.JSONDict {
wire := self.GetWire()
zone := self.getZone()
if zone != nil {
extra.Add(jsonutils.NewString(zone.Name), "zone")
extra.Add(jsonutils.NewString(zone.Id), "zone_id")
}
extra.Add(jsonutils.NewString(wire.Name), "wire")
if self.IsExitNetwork() {
extra.Add(jsonutils.JSONTrue, "exit")
} else {
extra.Add(jsonutils.JSONFalse, "exit")
}
extra.Add(jsonutils.NewInt(int64(self.GetPorts())), "ports")
portsUsed, _ := self.GetTotalNicCount()
extra.Add(jsonutils.NewInt(int64(portsUsed)), "ports_used")
vnics, _ := self.GetGuestnicsCount()
extra.Add(jsonutils.NewInt(int64(vnics)), "vnics")
bmVnics, _ := self.GetBaremetalNicsCount()
extra.Add(jsonutils.NewInt(int64(bmVnics)), "bm_vnics")
lbVnics, _ := self.GetLoadbalancerIpsCount()
extra.Add(jsonutils.NewInt(int64(lbVnics)), "lb_vnics")
eips, _ := self.GetEipsCount()
extra.Add(jsonutils.NewInt(int64(eips)), "eip_vnics")
groupVnics, _ := self.GetGroupNicsCount()
extra.Add(jsonutils.NewInt(int64(groupVnics)), "group_vnics")
reserveVnics, _ := self.GetReservedNicsCount()
extra.Add(jsonutils.NewInt(int64(reserveVnics)), "reserve_vnics")
vpc := self.getVpc()
if vpc != nil {
extra.Add(jsonutils.NewString(vpc.GetId()), "vpc_id")
extra.Add(jsonutils.NewString(vpc.GetName()), "vpc")
if len(vpc.GetExternalId()) > 0 {
extra.Add(jsonutils.NewString(vpc.GetExternalId()), "vpc_ext_id")
}
}
routes := self.GetRoutes()
if len(routes) > 0 {
extra.Add(jsonutils.Marshal(routes), "routes")
}
info := vpc.getCloudProviderInfo()
extra.Update(jsonutils.Marshal(&info))
extra = GetSchedtagsDetailsToResource(self, ctx, extra)
return extra
}
func (self *SNetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra = self.getMoreDetails(ctx, extra)
return extra, nil
}
func (self *SNetwork) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SSharableVirtualResourceBase.GetCustomizeColumns(ctx, userCred, query)
extra = self.getMoreDetails(ctx, extra)
return extra
}
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")
}
func (self *SNetwork) PerformReserveIp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
ips, err := data.GetArray("ips")
if err != nil {
if data.Contains("ip") {
ip, _ := data.Get("ip")
ips = []jsonutils.JSONObject{ip}
} else {
return nil, httperrors.NewMissingParameterError("ips")
}
}
notes, err := data.GetString("notes")
if err != nil {
return nil, httperrors.NewMissingParameterError("ips")
}
for _, ip := range ips {
ipstr, _ := ip.GetString()
ipAddr, err := netutils.NewIPV4Addr(ipstr)
if err != nil {
return nil, httperrors.NewInputParameterError("not a valid ip address %s: %s", ipstr, err)
}
if !self.isAddressInRange(ipAddr) {
return nil, httperrors.NewInputParameterError("Address %s not in network", ipstr)
}
used, err := self.isAddressUsed(ipstr)
if err != nil {
return nil, httperrors.NewInternalServerError("isAddressUsed fail %s", err)
}
if used {
return nil, httperrors.NewConflictError("Address %s has been used", ipstr)
}
err = ReservedipManager.ReserveIP(userCred, self, ipstr, notes)
if err != nil {
return nil, err
}
}
return nil, 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")
}
func (self *SNetwork) PerformReleaseReservedIp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
ipstr, _ := data.GetString("ip")
if len(ipstr) == 0 {
return nil, httperrors.NewInputParameterError("Reserved ip to release must be provided")
}
rip := ReservedipManager.GetReservedIP(self, ipstr)
if rip == nil {
return nil, httperrors.NewInvalidStatusError("Address %s not reserved", ipstr)
}
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 (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
prefixStr, _ := data.GetString("guest_ip_prefix")
var maskLen64 int64
var err error
var startIp, endIp netutils.IPV4Addr
if len(prefixStr) > 0 {
prefix, err := netutils.NewIPV4Prefix(prefixStr)
if err != nil {
return nil, httperrors.NewInputParameterError("ip_prefix error: %s", err)
}
iprange := prefix.ToIPRange()
startIp = iprange.StartIp().StepUp()
endIp = iprange.EndIp().StepDown()
maskLen64 = int64(prefix.MaskLen)
} else {
ipStartStr, _ := data.GetString("guest_ip_start")
ipEndStr, _ := data.GetString("guest_ip_end")
startIp, err = netutils.NewIPV4Addr(ipStartStr)
if err != nil {
return nil, httperrors.NewInputParameterError("Invalid start ip: %s %s", ipStartStr, err)
}
endIp, err = netutils.NewIPV4Addr(ipEndStr)
if err != nil {
return nil, httperrors.NewInputParameterError("invalid end ip: %s %s", ipEndStr, err)
}
if startIp > endIp {
tmp := startIp
startIp = endIp
endIp = tmp
}
maskLen64, _ = data.Int("guest_ip_mask")
}
if !isValidMaskLen(maskLen64) {
return nil, httperrors.NewInputParameterError("Invalid masklen %d", maskLen64)
}
data.Add(jsonutils.NewInt(maskLen64), "guest_ip_mask")
data.Add(jsonutils.NewString(startIp.String()), "guest_ip_start")
data.Add(jsonutils.NewString(endIp.String()), "guest_ip_end")
for _, key := range []string{"guest_gateway", "guest_dns", "guest_dhcp"} {
ipStr, _ := data.GetString(key)
if len(ipStr) > 0 && !regutils.MatchIPAddr(ipStr) {
return nil, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr)
}
}
nets := manager.getAllNetworks("")
if nets == nil {
return nil, httperrors.NewInternalServerError("query all networks fail")
}
if isOverlapNetworks(nets, startIp, endIp) {
return nil, httperrors.NewInputParameterError("Conflict address space with existing networks")
}
wireStr := jsonutils.GetAnyString(data, []string{"wire", "wire_id"})
if len(wireStr) > 0 {
wireObj, err := WireManager.FetchByIdOrName(userCred, wireStr)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewNotFoundError("wire %s not found", wireStr)
} else {
return nil, httperrors.NewInternalServerError("query wire %s error %s", wireStr, err)
}
}
data.Add(jsonutils.NewString(wireObj.GetId()), "wire_id")
} else {
zoneStr := jsonutils.GetAnyString(data, []string{"zone", "zone_id"})
if len(zoneStr) > 0 {
vpcStr := jsonutils.GetAnyString(data, []string{"vpc", "vpc_id"})
if len(vpcStr) > 0 {
zoneObj, err := ZoneManager.FetchByIdOrName(userCred, zoneStr)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewNotFoundError("zone %s not found", zoneStr)
} else {
return nil, httperrors.NewInternalServerError("query zone %s error %s", zoneStr, err)
}
}
vpcObj, err := VpcManager.FetchByIdOrName(userCred, vpcStr)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewNotFoundError("vpc %s not found", vpcStr)
} else {
return nil, httperrors.NewInternalServerError("query vpc %s error %s", vpcStr, err)
}
}
vpc := vpcObj.(*SVpc)
zone := zoneObj.(*SZone)
region := zone.GetRegion()
if region == nil {
return nil, httperrors.NewInternalServerError("zone %s related region not found", zone.Id)
}
// 华为云,ucloud wire zone_id 为空
var wires []SWire
if utils.IsInStringArray(region.Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_UCLOUD}) {
wires, err = WireManager.getWiresByVpcAndZone(vpc, nil)
} else {
wires, err = WireManager.getWiresByVpcAndZone(vpc, zone)
}
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewNotFoundError("wire not found for zone %s and vpc %s", zoneStr, vpcStr)
} else {
return nil, httperrors.NewInternalServerError("query wire for zone %s and vpc %s error %s", zoneStr, vpcStr, err)
}
}
if len(wires) == 0 {
return nil, httperrors.NewNotFoundError("wire not found for zone %s and vpc %s", zoneStr, vpcStr)
} else if len(wires) > 1 {
return nil, httperrors.NewConflictError("more than 1 wire found for zone %s and vpc %s", zoneStr, vpcStr)
} else {
data.Add(jsonutils.NewString(wires[0].Id), "wire_id")
}
} else {
return nil, httperrors.NewInputParameterError("No either wire or vpc provided")
}
} else {
return nil, httperrors.NewInvalidStatusError("No either wire or zone provided")
}
}
wireId, _ := data.GetString("wire_id")
if len(wireId) == 0 {
return nil, httperrors.NewMissingParameterError("wire_id")
}
wire := WireManager.FetchWireById(wireId)
if wire == nil {
return nil, httperrors.NewResourceNotFoundError("wire %s not found", wireId)
}
vpc := wire.getVpc()
if vpc == nil {
return nil, httperrors.NewInputParameterError("no valid vpc ???")
}
if vpc.Status != api.VPC_STATUS_AVAILABLE {
return nil, httperrors.NewInvalidStatusError("VPC not ready")
}
vpcRanges := vpc.getIPRanges()
netRange := netutils.NewIPV4AddrRange(startIp, endIp)
inRange := false
for _, vpcRange := range vpcRanges {
if vpcRange.ContainsRange(netRange) {
inRange = true
break
}
}
if !inRange {
return nil, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock)
}
serverTypeStr, _ := data.GetString("server_type")
if len(serverTypeStr) == 0 {
serverTypeStr = api.NETWORK_TYPE_GUEST
} else if !utils.IsInStringArray(serverTypeStr, ALL_NETWORK_TYPES) {
return nil, httperrors.NewInputParameterError("Invalid server_type: %s", serverTypeStr)
}
data.Add(jsonutils.NewString(serverTypeStr), "server_type")
return manager.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data)
}
func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
var startIp, endIp netutils.IPV4Addr
var err error
ipStartStr, _ := data.GetString("guest_ip_start")
ipEndStr, _ := data.GetString("guest_ip_end")
if len(ipStartStr) > 0 || len(ipEndStr) > 0 {
if self.isManaged() {
return nil, httperrors.NewForbiddenError("Cannot update a managed network")
}
if len(ipStartStr) > 0 {
startIp, err = netutils.NewIPV4Addr(ipStartStr)
if err != nil {
return nil, 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 nil, 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.Id)
if nets == nil {
return nil, httperrors.NewInternalServerError("query all networks fail")
}
if isOverlapNetworks(nets, startIp, endIp) {
return nil, httperrors.NewInputParameterError("Conflict address space with existing networks")
}
vpc := self.GetVpc()
vpcRanges := vpc.getIPRanges()
netRange := netutils.NewIPV4AddrRange(startIp, endIp)
inRange := false
for _, vpcRange := range vpcRanges {
if vpcRange.ContainsRange(netRange) {
inRange = true
break
}
}
if !inRange {
return nil, 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 nil, httperrors.NewInputParameterError("Address been assigned out of new range")
}
}
data.Add(jsonutils.NewString(startIp.String()), "guest_ip_start")
data.Add(jsonutils.NewString(endIp.String()), "guest_ip_end")
}
if data.Contains("guest_ip_mask") {
if self.isManaged() {
return nil, httperrors.NewForbiddenError("Cannot update a managed network")
}
maskLen64, _ := data.Int("guest_ip_mask")
if !isValidMaskLen(maskLen64) {
return nil, httperrors.NewInputParameterError("Invalid masklen %d", maskLen64)
}
}
for _, key := range []string{"guest_gateway", "guest_dns", "guest_dhcp"} {
ipStr, _ := data.GetString(key)
if len(ipStr) > 0 {
if self.isManaged() {
return nil, httperrors.NewForbiddenError("Cannot update a managed network")
} else if !regutils.MatchIPAddr(ipStr) {
return nil, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr)
}
}
}
return self.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data)
}
func (manager *SNetworkManager) getAllNetworks(excludeId string) []SNetwork {
nets := make([]SNetwork, 0)
q := manager.Query()
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) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
if db.IsAdminAllowCreate(userCred, self.GetModelManager()) && ownerProjId == userCred.GetProjectId() && self.ServerType == api.NETWORK_TYPE_GUEST {
self.IsPublic = true
} else {
self.IsPublic = false
}
return self.SSharableVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerProjId, query, data)
}
func (self *SNetwork) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) {
self.SSharableVirtualResourceBase.PostCreate(ctx, userCred, ownerProjId, query, data)
wire := self.GetWire()
if wire == nil {
log.Errorf("cannot find wire???")
} else {
wire.clearHostSchedDescCache()
}
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, "")
}
}
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")
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 (manager *SNetworkManager) CustomizeFilterList(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*db.CustomizeListFilters, error) {
filters := db.NewCustomizeListFilters()
if query.Contains("ip") {
ip, _ := query.GetString("ip")
ipInt, err := netutils.NewIPV4Addr(ip)
if err != nil {
return nil, err
}
ipFilter := func(obj jsonutils.JSONObject) (bool, error) {
guestIpStart, _ := obj.GetString("guest_ip_start")
guestIpEnd, _ := obj.GetString("guest_ip_end")
guestIpStartInt, _ := netutils.NewIPV4Addr(guestIpStart)
guestIpEndInt, _ := netutils.NewIPV4Addr(guestIpEnd)
ipRange := netutils.NewIPV4AddrRange(guestIpStartInt, guestIpEndInt)
return ipRange.Contains(ipInt), nil
}
filters.Append(ipFilter)
}
return filters, nil
}
func (manager *SNetworkManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
var err error
q, err = managedResourceFilterByAccount(q, query, "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, err
}
q = managedResourceFilterByCloudType(q, query, "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
})
q, err = manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query)
if err != nil {
return nil, err
}
zoneStr, _ := query.GetString("zone")
if len(zoneStr) > 0 {
zoneObj, err := ZoneManager.FetchByIdOrName(userCred, zoneStr)
if err != nil {
return nil, httperrors.NewNotFoundError("Zone %s not found", zoneStr)
}
sq := WireManager.Query("id").Equals("zone_id", zoneObj.GetId())
q = q.Filter(sqlchemy.In(q.Field("wire_id"), sq.SubQuery()))
}
vpcStr, _ := query.GetString("vpc")
if len(vpcStr) > 0 {
vpcObj, err := VpcManager.FetchByIdOrName(userCred, vpcStr)
if err != nil {
return nil, httperrors.NewNotFoundError("VPC %s not found", vpcStr)
}
sq := WireManager.Query("id").Equals("vpc_id", vpcObj.GetId())
q = q.Filter(sqlchemy.In(q.Field("wire_id"), sq.SubQuery()))
}
regionStr := jsonutils.GetAnyString(query, []string{"region_id", "region", "cloudregion_id", "cloudregion"})
if len(regionStr) > 0 {
region, err := CloudregionManager.FetchByIdOrName(userCred, regionStr)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError("cloud region %s not found", regionStr)
} else {
return nil, httperrors.NewGeneralError(err)
}
}
wires := WireManager.Query().SubQuery()
vpcs := VpcManager.Query().SubQuery()
sq := wires.Query(wires.Field("id")).
Join(vpcs, sqlchemy.AND(
sqlchemy.Equals(vpcs.Field("cloudregion_id"), region.GetId()),
sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id"))))
q = q.Filter(sqlchemy.In(q.Field("wire_id"), sq.SubQuery()))
}
hostStr, _ := query.GetString("host")
if len(hostStr) > 0 {
hostObj, err := HostManager.FetchByIdOrName(userCred, hostStr)
if err != nil {
return nil, httperrors.NewResourceNotFoundError2(HostManager.Keyword(), hostStr)
}
sq := HostwireManager.Query("wire_id").Equals("host_id", hostObj.GetId())
q = q.Filter(sqlchemy.In(q.Field("wire_id"), sq.SubQuery()))
}
cityStr, _ := query.GetString("city")
if len(cityStr) > 0 {
regions := CloudregionManager.Query().SubQuery()
wires := WireManager.Query().SubQuery()
vpcs := VpcManager.Query().SubQuery()
sq := wires.Query(wires.Field("id")).
Join(vpcs, sqlchemy.Equals(wires.Field("vpc_id"), vpcs.Field("id"))).
Join(regions, sqlchemy.Equals(regions.Field("id"), vpcs.Field("cloudregion_id"))).
Filter(sqlchemy.Equals(regions.Field("city"), cityStr))
q = q.Filter(sqlchemy.In(q.Field("wire_id"), sq.SubQuery()))
}
return q, nil
}
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 len(n.ExternalId) == 0 && len(n.WireId) > 0 && n.Status == api.NETWORK_STATUS_INIT {
db.Update(&n, func() error {
n.Status = api.NETWORK_STATUS_AVAILABLE
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")
}
func (self *SNetwork) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (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.Enabled {
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 (self *SNetwork) PerformMerge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
target, err := data.GetString("target")
if err != nil {
return nil, httperrors.NewMissingParameterError("target")
}
iNet, err := NetworkManager.FetchByIdOrName(userCred, target)
if err == sql.ErrNoRows {
err = httperrors.NewNotFoundError("Network %s not found", 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", 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, NetworkManager.GetOwnerId(userCred))
defer lockman.ReleaseClass(ctx, NetworkManager, NetworkManager.GetOwnerId(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
}
guestnetworks := make([]SGuestnetwork, 0)
err = db.FetchModelObjects(GuestnetworkManager, GuestnetworkManager.Query().Equals("network_id", self.Id), &guestnetworks)
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return nil, err
}
for _, gn := range guestnetworks {
addr, _ := netutils.NewIPV4Addr(gn.IpAddr)
if self.isAddressInRange(addr) {
_, err = db.Update(&gn, func() error {
gn.NetworkId = net.Id
return nil
})
if err != nil {
log.Errorln(err)
}
}
}
hostnetworks := make([]SHostnetwork, 0)
err = db.FetchModelObjects(HostnetworkManager, HostnetworkManager.Query().Equals("network_id", self.Id), &hostnetworks)
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return nil, err
}
for _, gn := range hostnetworks {
addr, _ := netutils.NewIPV4Addr(gn.IpAddr)
if self.isAddressInRange(addr) {
_, err = db.Update(&gn, func() error {
gn.NetworkId = net.Id
return nil
})
if err != nil {
log.Errorln(err)
}
}
}
reservedips := make([]SReservedip, 0)
err = db.FetchModelObjects(ReservedipManager, ReservedipManager.Query().Equals("network_id", self.Id), &reservedips)
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return nil, err
}
for _, gn := range reservedips {
addr, _ := netutils.NewIPV4Addr(gn.IpAddr)
if self.isAddressInRange(addr) {
_, err = db.Update(&gn, func() error {
gn.NetworkId = net.Id
return nil
})
if err != nil {
log.Errorln(err)
}
}
}
groupnetwroks := make([]SGroupnetwork, 0)
err = db.FetchModelObjects(GroupnetworkManager, GroupnetworkManager.Query().Equals("network_id", self.Id), &groupnetwroks)
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_MERGE, err.Error(), userCred, false)
return nil, err
}
for _, gn := range groupnetwroks {
addr, _ := netutils.NewIPV4Addr(gn.IpAddr)
if self.isAddressInRange(addr) {
_, err = db.Update(&gn, func() error {
gn.NetworkId = net.Id
return nil
})
if err != nil {
log.Errorln(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
}
func (self *SNetwork) PerformSplit(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
splitIp, err := data.GetString("split_ip")
if err != nil {
return nil, httperrors.NewMissingParameterError("split_ip")
}
name, _ := data.GetString("name")
if !regutils.MatchIPAddr(splitIp) {
return nil, httperrors.NewInputParameterError("Invalid IP %s", splitIp)
}
if splitIp == self.GuestIpStart {
return nil, httperrors.NewInputParameterError("Split IP %s is the start ip", splitIp)
}
iSplitIp, err := netutils.NewIPV4Addr(splitIp)
if err != nil {
return nil, err
}
if !self.isAddressInRange(iSplitIp) {
return nil, httperrors.NewInputParameterError("Split IP %s out of range", splitIp)
}
lockman.LockClass(ctx, NetworkManager, NetworkManager.GetOwnerId(userCred))
defer lockman.ReleaseClass(ctx, NetworkManager, NetworkManager.GetOwnerId(userCred))
if len(name) > 0 {
if err := db.NewNameValidator(NetworkManager, userCred.GetProjectId(), name); err != nil {
return nil, httperrors.NewInputParameterError("Duplicate name %s", name)
}
} else {
newName, err := db.GenerateName(NetworkManager, userCred.GetProjectId(), fmt.Sprintf("%s#", self.Name))
if err != nil {
return nil, httperrors.NewInternalServerError("GenerateName fail %s", err)
}
name = newName
}
network := &SNetwork{}
network.Name = name
network.GuestIpStart = 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.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)
db.Update(self, func() error {
self.GuestIpEnd = iSplitIp.StepDown().String()
return nil
})
guestnetworks := make([]SGuestnetwork, 0)
err = db.FetchModelObjects(GuestnetworkManager, GuestnetworkManager.Query().Equals("network_id", self.Id), &guestnetworks)
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_SPLIT, err.Error(), userCred, false)
return nil, err
}
for _, gn := range guestnetworks {
addr, _ := netutils.NewIPV4Addr(gn.IpAddr)
if network.isAddressInRange(addr) {
_, err := db.Update(&gn, func() error {
gn.NetworkId = network.Id
return nil
})
if err != nil {
log.Errorln(err)
}
}
}
hostnetworks := make([]SHostnetwork, 0)
err = db.FetchModelObjects(HostnetworkManager, HostnetworkManager.Query().Equals("network_id", self.Id), &hostnetworks)
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_SPLIT, err.Error(), userCred, false)
return nil, err
}
for _, gn := range hostnetworks {
addr, _ := netutils.NewIPV4Addr(gn.IpAddr)
if network.isAddressInRange(addr) {
_, err = db.Update(&gn, func() error {
gn.NetworkId = network.Id
return nil
})
if err != nil {
log.Errorln(err)
}
}
}
reservedips := make([]SReservedip, 0)
err = db.FetchModelObjects(ReservedipManager, ReservedipManager.Query().Equals("network_id", self.Id), &reservedips)
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_SPLIT, err.Error(), userCred, false)
return nil, err
}
for _, gn := range reservedips {
addr, _ := netutils.NewIPV4Addr(gn.IpAddr)
if network.isAddressInRange(addr) {
_, err = db.Update(&gn, func() error {
gn.NetworkId = network.Id
return nil
})
if err != nil {
log.Errorln(err)
}
}
}
groupnetworks := make([]SGroupnetwork, 0)
err = db.FetchModelObjects(
GroupnetworkManager, GroupnetworkManager.Query().Equals("network_id", self.Id), &groupnetworks)
if err != nil {
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_SPLIT, err.Error(), userCred, false)
return nil, err
}
for _, gn := range groupnetworks {
addr, _ := netutils.NewIPV4Addr(gn.IpAddr)
if network.isAddressInRange(addr) {
_, err = db.Update(&gn, func() error {
gn.NetworkId = network.Id
return nil
})
if err != nil {
log.Errorln(err)
}
}
}
note := map[string]string{"split_ip": 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 (network *SNetwork) getAllocTimoutDuration() time.Duration {
tos := network.AllocTimoutSeconds
if tos < options.Options.MinimalIpAddrReusedIntervalSeconds {
tos = options.Options.MinimalIpAddrReusedIntervalSeconds
}
return time.Duration(tos) * time.Second
}
func (manager *SNetworkManager) IsValidOnPremiseNetworkIP(ipStr string) bool {
net, _ := manager.GetOnPremiseNetworkOfIP(ipStr, "", tristate.None)
if net != nil {
return true
}
return false
}
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()
}