mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 02:37:24 +08:00
取消userCred.IsSystemAdmin(),在使用policy的应用中,通过userCred.IsAdminAllow判断是否是管理员且具备相应的权限。获取token时,需要传入policy.FilterPolicyCredential,将普通userCred转换成支持rbac的userCred 2. 在应用启动时候,必须设置 SetServiceType,否则退出
1420 lines
45 KiB
Go
1420 lines
45 KiB
Go
package models
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/tristate"
|
|
"yunion.io/x/pkg/util/compare"
|
|
"yunion.io/x/pkg/util/fileutils"
|
|
"yunion.io/x/pkg/util/netutils"
|
|
"yunion.io/x/pkg/util/regutils"
|
|
"yunion.io/x/pkg/util/sets"
|
|
"yunion.io/x/pkg/utils"
|
|
"yunion.io/x/sqlchemy"
|
|
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
|
"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"
|
|
)
|
|
|
|
const (
|
|
// # DEFAULT_BANDWIDTH = options.default_bandwidth
|
|
MAX_BANDWIDTH = 100000
|
|
|
|
SERVER_TYPE_GUEST = "guest"
|
|
SERVER_TYPE_BAREMETAL = "baremetal"
|
|
SERVER_TYPE_CONTAINER = "container"
|
|
|
|
STATIC_ALLOC = "static"
|
|
|
|
MAX_NETWORK_NAME_LEN = 11
|
|
|
|
EXTRA_DNS_UPDATE_TARGETS = "__extra_dns_update_targets"
|
|
|
|
NETWORK_STATUS_INIT = "init"
|
|
NETWORK_STATUS_PENDING = "pending"
|
|
NETWORK_STATUS_AVAILABLE = "available"
|
|
NETWORK_STATUS_FAILED = "failed"
|
|
NETWORK_STATUS_UNKNOWN = "unknown"
|
|
NETWORK_STATUS_START_DELETE = "start_delete"
|
|
NETWORK_STATUS_DELETING = "deleting"
|
|
NETWORK_STATUS_DELETED = "deleted"
|
|
NETWORK_STATUS_DELETE_FAILED = "delete_failed"
|
|
)
|
|
|
|
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" get:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True)
|
|
GuestDhcp string `width:"16" charset:"ascii" nullable:"true" get:"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" 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) GetContextManager() []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 userCred.IsAdminAllow(consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionCreate)
|
|
}
|
|
|
|
func (self *SNetwork) ValidateDeleteCondition(ctx context.Context) error {
|
|
if self.GetTotalNicCount() > 0 {
|
|
return httperrors.NewNotEmptyError("not an empty network")
|
|
}
|
|
return self.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx)
|
|
}
|
|
|
|
func (self *SNetwork) GetTotalNicCount() int {
|
|
total := self.GetGuestnicsCount() +
|
|
self.GetGroupNicsCount() +
|
|
self.GetBaremetalNicsCount() +
|
|
self.GetReservedNicsCount() +
|
|
self.GetLoadbalancerIpsCount()
|
|
return total
|
|
}
|
|
|
|
func (self *SNetwork) GetGuestnicsCount() int {
|
|
return GuestnetworkManager.Query().Equals("network_id", self.Id).IsFalse("virtual").Count()
|
|
}
|
|
|
|
func (self *SNetwork) GetGroupNicsCount() int {
|
|
return GroupnetworkManager.Query().Equals("network_id", self.Id).Count()
|
|
}
|
|
|
|
func (self *SNetwork) GetBaremetalNicsCount() int {
|
|
return HostnetworkManager.Query().Equals("network_id", self.Id).Count()
|
|
}
|
|
|
|
func (self *SNetwork) GetReservedNicsCount() int {
|
|
return ReservedipManager.Query().Equals("network_id", self.Id).Count()
|
|
}
|
|
|
|
func (self *SNetwork) GetLoadbalancerIpsCount() int {
|
|
return LoadbalancernetworkManager.Query().Equals("network_id", self.Id).Count()
|
|
}
|
|
|
|
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(),
|
|
} {
|
|
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
|
|
}
|
|
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 {
|
|
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(fmt.Sprintf("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
|
|
}
|
|
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(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) {
|
|
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, projectSync bool) ([]SNetwork, []cloudprovider.ICloudNetwork, compare.SyncResult) {
|
|
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
|
|
}
|
|
|
|
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].ValidateDeleteCondition(ctx)
|
|
if err != nil { // cannot delete
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
err = removed[i].Delete(ctx, userCred)
|
|
if err != nil {
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
syncResult.Delete()
|
|
}
|
|
}*/
|
|
err = removed[i].SetStatus(userCred, NETWORK_STATUS_UNKNOWN, "Sync to remove")
|
|
if err != nil {
|
|
syncResult.DeleteError(err)
|
|
} else {
|
|
syncResult.Delete()
|
|
}
|
|
}
|
|
for i := 0; i < len(commondb); i += 1 {
|
|
err = commondb[i].SyncWithCloudNetwork(userCred, commonext[i], projectId, projectSync)
|
|
if err != nil {
|
|
syncResult.UpdateError(err)
|
|
} else {
|
|
localNets = append(localNets, commondb[i])
|
|
remoteNets = append(remoteNets, commonext[i])
|
|
syncResult.Update()
|
|
}
|
|
}
|
|
for i := 0; i < len(added); i += 1 {
|
|
new, err := manager.newFromCloudNetwork(userCred, added[i], wire, projectId)
|
|
if err != nil {
|
|
syncResult.AddError(err)
|
|
} else {
|
|
localNets = append(localNets, *new)
|
|
remoteNets = append(remoteNets, added[i])
|
|
syncResult.Add()
|
|
}
|
|
}
|
|
|
|
return localNets, remoteNets, syncResult
|
|
}
|
|
|
|
func (self *SNetwork) SyncWithCloudNetwork(userCred mcclient.TokenCredential, extNet cloudprovider.ICloudNetwork, projectId string, projectSync bool) error {
|
|
_, err := self.GetModelManager().TableSpec().Update(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()
|
|
|
|
self.ProjectId = userCred.GetProjectId()
|
|
if projectSync && len(projectId) > 0 {
|
|
self.ProjectId = projectId
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
log.Errorf("syncWithCloudNetwork error %s", err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (manager *SNetworkManager) newFromCloudNetwork(userCred mcclient.TokenCredential, extNet cloudprovider.ICloudNetwork, wire *SWire, projectId string) (*SNetwork, error) {
|
|
net := SNetwork{}
|
|
net.SetModelManager(manager)
|
|
|
|
net.Name = extNet.GetName()
|
|
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()
|
|
|
|
net.ProjectId = userCred.GetProjectId()
|
|
if len(projectId) > 0 {
|
|
net.ProjectId = projectId
|
|
}
|
|
err := manager.TableSpec().Insert(&net)
|
|
if err != nil {
|
|
log.Errorf("newFromCloudZone fail %s", err)
|
|
return nil, err
|
|
}
|
|
return &net, nil
|
|
}
|
|
|
|
func (self *SNetwork) isAddressInRange(address netutils.IPV4Addr) bool {
|
|
return self.getIPRange().Contains(address)
|
|
}
|
|
|
|
func (self *SNetwork) isAddressUsed(address string) bool {
|
|
managers := []db.IModelManager{
|
|
GuestnetworkManager,
|
|
GroupnetworkManager,
|
|
HostnetworkManager,
|
|
ReservedipManager,
|
|
LoadbalancernetworkManager,
|
|
}
|
|
for _, manager := range managers {
|
|
q := manager.Query().Equals("ip_addr", address).Equals("network_id", self.Id)
|
|
if q.Count() > 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (manager *SNetworkManager) GetNetworkOfIP(ipAddr string, serverType string, isPublic tristate.TriState) (*SNetwork, error) {
|
|
address, err := netutils.NewIPV4Addr(ipAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
q := manager.Query()
|
|
if len(serverType) > 0 {
|
|
q = q.Equals("server_type", serverType)
|
|
}
|
|
if isPublic.IsTrue() {
|
|
q = q.IsTrue("is_public")
|
|
} else if isPublic.IsFalse() {
|
|
q = q.IsFalse("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(rangeObj db.IStandaloneModel) *sqlchemy.SQuery {
|
|
networks := manager.Query().SubQuery()
|
|
hostwires := HostwireManager.Query().SubQuery()
|
|
hosts := HostManager.Query().SubQuery()
|
|
q := networks.Query().
|
|
Join(hostwires, sqlchemy.AND(
|
|
sqlchemy.Equals(hostwires.Field("wire_id"), networks.Field("wire_id")),
|
|
sqlchemy.IsFalse(hostwires.Field("deleted")))).
|
|
Join(hosts, sqlchemy.AND(
|
|
sqlchemy.Equals(hosts.Field("id"), hostwires.Field("host_id")),
|
|
sqlchemy.IsFalse(hosts.Field("deleted")),
|
|
sqlchemy.IsTrue(hosts.Field("enabled")),
|
|
sqlchemy.OR(
|
|
sqlchemy.Equals(hosts.Field("host_type"), HOST_TYPE_BAREMETAL),
|
|
sqlchemy.Equals(hosts.Field("host_status"), HOST_ONLINE))))
|
|
return AttachUsageQuery(q, hosts, hostwires.Field("host_id"), nil, rangeObj)
|
|
}
|
|
|
|
func (manager *SNetworkManager) totalPortCountQ(userCred mcclient.TokenCredential, rangeObj db.IStandaloneModel) *sqlchemy.SQuery {
|
|
q := manager.allNetworksQ(rangeObj)
|
|
networks := manager.Query().SubQuery()
|
|
if userCred != nil && !userCred.IsAdminAllow(consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList) {
|
|
q = q.Filter(sqlchemy.OR(
|
|
sqlchemy.Equals(networks.Field("tenant_id"), userCred.GetProjectId()),
|
|
sqlchemy.IsTrue(networks.Field("is_public"))))
|
|
}
|
|
return q
|
|
}
|
|
|
|
type NetworkPortStat struct {
|
|
Count int
|
|
CountExt int
|
|
}
|
|
|
|
func (manager *SNetworkManager) TotalPortCount(userCred mcclient.TokenCredential, rangeObj db.IStandaloneModel) NetworkPortStat {
|
|
nets := make([]SNetwork, 0)
|
|
err := manager.totalPortCountQ(userCred, 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 SNetworkConfig struct {
|
|
Network string
|
|
Wire string
|
|
Exit bool
|
|
Private bool
|
|
Mac string
|
|
Address string
|
|
Address6 string
|
|
Driver string
|
|
BwLimit int
|
|
Vip bool
|
|
Reserved bool
|
|
}
|
|
|
|
func parseNetworkInfo(userCred mcclient.TokenCredential, info jsonutils.JSONObject) (*SNetworkConfig, error) {
|
|
netConfig := SNetworkConfig{}
|
|
|
|
netJson, ok := info.(*jsonutils.JSONDict)
|
|
if ok {
|
|
err := netJson.Unmarshal(&netConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &netConfig, nil
|
|
}
|
|
netStr, err := info.GetString()
|
|
if err != nil {
|
|
log.Errorf("invalid networkinfo format %s", err)
|
|
return nil, err
|
|
}
|
|
parts := strings.Split(netStr, ":")
|
|
for _, p := range parts {
|
|
if len(p) == 0 {
|
|
continue
|
|
}
|
|
if regutils.MatchIP4Addr(p) {
|
|
netConfig.Address = p
|
|
} else if regutils.MatchIP6Addr(p) {
|
|
netConfig.Address6 = p
|
|
} else if regutils.MatchCompactMacAddr(p) {
|
|
netConfig.Mac = netutils.MacUnpackHex(p)
|
|
} else if strings.HasPrefix(p, "wire=") {
|
|
netConfig.Wire = p[len("wire="):]
|
|
} else if p == "[random_exit]" {
|
|
netConfig.Exit = true
|
|
} else if p == "[random]" {
|
|
netConfig.Exit = false
|
|
} else if p == "[private]" {
|
|
netConfig.Private = true
|
|
} else if p == "[reserved]" {
|
|
netConfig.Reserved = true
|
|
} else if utils.IsInStringArray(p, []string{"virtio", "e1000", "vmxnet3"}) {
|
|
netConfig.Driver = p
|
|
} else if regutils.MatchSize(p) {
|
|
bw, err := fileutils.GetSizeMb(p, 'M', 1000)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
netConfig.BwLimit = bw
|
|
} else if p == "[vip]" {
|
|
netConfig.Vip = true
|
|
} else {
|
|
netObj, err := NetworkManager.FetchByIdOrName(userCred, p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
netConfig.Network = netObj.GetId()
|
|
}
|
|
}
|
|
if netConfig.BwLimit == 0 {
|
|
netConfig.BwLimit = options.Options.DefaultBandwidth
|
|
}
|
|
return &netConfig, nil
|
|
}
|
|
|
|
func (self *SNetwork) getFreeAddressCount() int {
|
|
return self.getIPRange().AddressCount() - self.GetTotalNicCount()
|
|
}
|
|
|
|
func isValidNetworkInfo(userCred mcclient.TokenCredential, netConfig *SNetworkConfig) 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 !userCred.IsAdminAllow(consts.GetServiceType(), ReservedipManager.KeywordPlural(), policy.PolicyActionGet) {
|
|
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 if net.isAddressUsed(netConfig.Address) {
|
|
return httperrors.NewInputParameterError("Address %s has been used", netConfig.Address)
|
|
}
|
|
}
|
|
if netConfig.BwLimit > MAX_BANDWIDTH {
|
|
return httperrors.NewInputParameterError("Bandwidth limit cannot exceed %dMbps", MAX_BANDWIDTH)
|
|
}
|
|
}
|
|
/* scheduler to the check
|
|
else if ! netConfig.Vip {
|
|
ct, ctExit := NetworkManager.to
|
|
}
|
|
*/
|
|
return nil
|
|
}
|
|
|
|
func isExitNetworkInfo(netConfig *SNetworkConfig) 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) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict {
|
|
wire := self.GetWire()
|
|
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.getIPRange().AddressCount())), "ports")
|
|
extra.Add(jsonutils.NewInt(int64(self.GetTotalNicCount())), "ports_used")
|
|
extra.Add(jsonutils.NewInt(int64(self.GetGuestnicsCount())), "vnics")
|
|
extra.Add(jsonutils.NewInt(int64(self.GetBaremetalNicsCount())), "bm_vnics")
|
|
extra.Add(jsonutils.NewInt(int64(self.GetGroupNicsCount())), "group_vnics")
|
|
extra.Add(jsonutils.NewInt(int64(self.GetReservedNicsCount())), "reserve_vnics")
|
|
|
|
zone := self.getZone()
|
|
if zone != nil {
|
|
extra.Add(jsonutils.NewString(zone.GetId()), "zone_id")
|
|
extra.Add(jsonutils.NewString(zone.GetName()), "zone")
|
|
if len(zone.GetExternalId()) > 0 {
|
|
extra.Add(jsonutils.NewString(zone.GetExternalId()), "zone_external_id")
|
|
}
|
|
}
|
|
|
|
region := self.getRegion()
|
|
if region != nil {
|
|
extra.Add(jsonutils.NewString(region.GetId()), "region_id")
|
|
extra.Add(jsonutils.NewString(region.GetName()), "region")
|
|
if len(region.GetExternalId()) > 0 {
|
|
extra.Add(jsonutils.NewString(region.GetExternalId()), "region_external_id")
|
|
}
|
|
}
|
|
|
|
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_external_id")
|
|
}
|
|
}
|
|
|
|
return extra
|
|
}
|
|
|
|
func (self *SNetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
|
|
extra := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
|
|
extra = self.getMoreDetails(extra)
|
|
return extra
|
|
}
|
|
|
|
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(extra)
|
|
return extra
|
|
}
|
|
|
|
func (self *SNetwork) AllowPerformReserveIp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
|
return self.IsOwner(userCred) || userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "reserve-ip")
|
|
}
|
|
|
|
func (self *SNetwork) PerformReserveIp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
|
ipstr, _ := data.GetString("ip")
|
|
notes, _ := data.GetString("notes")
|
|
if len(ipstr) == 0 || len(notes) == 0 {
|
|
return nil, httperrors.NewInputParameterError("both reserved ip and notes should be provided")
|
|
}
|
|
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)
|
|
}
|
|
if self.isAddressUsed(ipstr) {
|
|
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) || userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionPerform, "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) || userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionGet, "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)
|
|
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.NewInputParameterError("missing wire_id")
|
|
}
|
|
wire := WireManager.FetchWireById(wireId)
|
|
if wire == nil {
|
|
return nil, httperrors.NewInputParameterError("wire_id %s not valid", wireId)
|
|
}
|
|
vpc := wire.getVpc()
|
|
if vpc == nil {
|
|
return nil, httperrors.NewInputParameterError("no valid vpc ???")
|
|
}
|
|
|
|
if vpc.Status != VPC_STATUS_AVAILABLE {
|
|
return nil, httperrors.NewInvalidStatusError("VPC not ready")
|
|
}
|
|
|
|
vpcRange := vpc.getIPRange()
|
|
|
|
netRange := netutils.NewIPV4AddrRange(startIp, endIp)
|
|
|
|
if !vpcRange.ContainsRange(netRange) {
|
|
return nil, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock)
|
|
}
|
|
|
|
serverTypeStr, _ := data.GetString("server_type")
|
|
if len(serverTypeStr) == 0 {
|
|
serverTypeStr = SERVER_TYPE_GUEST
|
|
} else if !sets.NewString(SERVER_TYPE_GUEST, SERVER_TYPE_BAREMETAL, SERVER_TYPE_CONTAINER).Has(serverTypeStr) {
|
|
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()
|
|
|
|
vpcRange := vpc.getIPRange()
|
|
|
|
netRange := netutils.NewIPV4AddrRange(startIp, endIp)
|
|
|
|
if !vpcRange.ContainsRange(netRange) {
|
|
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 userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionCreate) && ownerProjId == userCred.GetProjectId() {
|
|
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, 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, 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(), userCred)
|
|
self.SetStatus(userCred, 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) {
|
|
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()))
|
|
}
|
|
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 == NETWORK_STATUS_INIT {
|
|
manager.TableSpec().Update(&n, func() error {
|
|
n.Status = 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 userCred.IsAdminAllow(consts.GetServiceType(), self.KeywordPlural(), policy.PolicyActionDelete)
|
|
}
|
|
|
|
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
|
|
}
|