networks: validate onecloud vpc subnet

Also rewrite for more readability, especially the part on finding wire,
vpc, region, etc.
This commit is contained in:
Yousong Zhou
2020-01-17 17:44:31 +08:00
parent f194ad7183
commit d9d343822f
+164 -131
View File
@@ -1252,52 +1252,115 @@ func (manager *SNetworkManager) newIfnameHint(hint string) (string, error) {
return r, nil
}
func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NetworkCreateInput) (api.NetworkCreateInput, error) {
var err error
var startIp, endIp netutils.IPV4Addr
if len(input.GuestIpPrefix) > 0 {
prefix, err := netutils.NewIPV4Prefix(input.GuestIpPrefix)
if err != nil {
return input, httperrors.NewInputParameterError("ip_prefix error: %s", err)
}
iprange := prefix.ToIPRange()
startIp = iprange.StartIp().StepUp()
endIp = iprange.EndIp().StepDown()
input.GuestIpMask = int64(prefix.MaskLen)
// 根据掩码得到合法的GuestIpPrefix
input.GuestIpPrefix = prefix.String()
} else {
startIp, err = netutils.NewIPV4Addr(input.GuestIpStart)
if err != nil {
return input, httperrors.NewInputParameterError("Invalid start ip: %s %s", input.GuestIpStart, err)
}
endIp, err = netutils.NewIPV4Addr(input.GuestIpEnd)
if err != nil {
return input, httperrors.NewInputParameterError("invalid end ip: %s %s", input.GuestIpEnd, err)
}
if startIp > endIp {
tmp := startIp
startIp = endIp
endIp = tmp
}
func (manager *SNetworkManager) validateEnsureWire(ctx context.Context, userCred mcclient.TokenCredential, input api.NetworkCreateInput) (w *SWire, v *SVpc, cr *SCloudregion, err error) {
wObj, err := WireManager.FetchByIdOrName(userCred, input.Wire)
if err != nil {
err = errors.Wrapf(err, "wire %s", input.Wire)
return
}
input.GuestIpStart = startIp.String()
input.GuestIpEnd = endIp.String()
w = wObj.(*SWire)
v = w.getVpc()
crObj, err := CloudregionManager.FetchById(v.CloudregionId)
if err != nil {
err = errors.Wrapf(err, "cloudregion %s", v.CloudregionId)
return
}
cr = crObj.(*SCloudregion)
return
}
if !isValidMaskLen(input.GuestIpMask) {
return input, httperrors.NewInputParameterError("Invalid masklen %d", input.GuestIpMask)
func (manager *SNetworkManager) validateEnsureZoneVpc(ctx context.Context, userCred mcclient.TokenCredential, input api.NetworkCreateInput) (w *SWire, v *SVpc, cr *SCloudregion, err error) {
zObj, err := ZoneManager.FetchByIdOrName(userCred, input.Zone)
if err != nil {
err = errors.Wrapf(err, "zone %s", input.Zone)
return
}
z := zObj.(*SZone)
vObj, err := VpcManager.FetchByIdOrName(userCred, input.Vpc)
if err != nil {
err = errors.Wrapf(err, "vpc %s", input.Vpc)
return
}
v = vObj.(*SVpc)
var wires []SWire
// 华为云,ucloud wire zone_id 为空
cr = z.GetRegion()
if utils.IsInStringArray(cr.Provider, api.REGIONAL_NETWORK_PROVIDERS) {
wires, err = WireManager.getWiresByVpcAndZone(v, nil)
} else {
wires, err = WireManager.getWiresByVpcAndZone(v, z)
}
if err != nil {
return
} else if len(wires) > 1 {
err = httperrors.NewConflictError("found %d wires for zone %s and vpc %s", len(wires), input.Zone, input.Vpc)
return
} else if len(wires) == 1 {
w = &wires[0]
return
}
// wire not found. We auto create one for OneCloud vpc
if cr.Provider == api.CLOUD_PROVIDER_ONECLOUD {
w, err = v.initWire(ctx, z)
if err != nil {
err = errors.Wrapf(err, "vpc %s init wire", v.Id)
return
}
return
}
err = httperrors.NewNotFoundError("wire not found for zone %s and vpc %s", input.Zone, input.Vpc)
return
}
func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NetworkCreateInput) (api.NetworkCreateInput, error) {
if input.ServerType == "" {
input.ServerType = api.NETWORK_TYPE_GUEST
} else if !utils.IsInStringArray(input.ServerType, ALL_NETWORK_TYPES) {
return input, httperrors.NewInputParameterError("Invalid server_type: %s", input.ServerType)
}
{
if len(input.IfnameHint) == 0 {
input.IfnameHint = input.Name
}
var err error
input.IfnameHint, err = manager.newIfnameHint(input.IfnameHint)
if err != nil {
return input, httperrors.NewBadRequestError("cannot derive valid ifname hint: %v", err)
}
}
var (
ipRange netutils.IPV4AddrRange
)
if len(input.GuestIpPrefix) > 0 {
prefix, err := netutils.NewIPV4Prefix(input.GuestIpPrefix)
if err != nil {
return input, httperrors.NewInputParameterError("ip_prefix error: %s", err)
}
ipRange = prefix.ToIPRange()
input.GuestIpMask = int64(prefix.MaskLen)
// 根据掩码得到合法的GuestIpPrefix
input.GuestIpPrefix = prefix.String()
} else {
ipStart, err := netutils.NewIPV4Addr(input.GuestIpStart)
if err != nil {
return input, httperrors.NewInputParameterError("Invalid start ip: %s %s", input.GuestIpStart, err)
}
ipEnd, err := netutils.NewIPV4Addr(input.GuestIpEnd)
if err != nil {
return input, httperrors.NewInputParameterError("invalid end ip: %s %s", input.GuestIpEnd, err)
}
ipRange = netutils.NewIPV4AddrRange(ipStart, ipEnd)
}
if !isValidMaskLen(input.GuestIpMask) {
return input, httperrors.NewInputParameterError("Invalid masklen %d", input.GuestIpMask)
}
for key, ipStr := range map[string]string{"guest_gateway": input.GuestGateway, "guest_dns": input.GuestDns, "guest_dhcp": input.GuestDHCP} {
if len(ipStr) > 0 {
if key == "guest_dhcp" {
@@ -1313,116 +1376,86 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred
}
}
nets := manager.getAllNetworks("")
if nets == nil {
return input, httperrors.NewInternalServerError("query all networks fail")
}
if isOverlapNetworks(nets, startIp, endIp) {
return input, httperrors.NewInputParameterError("Conflict address space with existing networks")
}
if len(input.WireId) > 0 {
var (
wire *SWire
vpc *SVpc
region *SCloudregion
err error
)
if input.WireId != "" {
input.Wire = input.WireId
}
if len(input.Wire) > 0 {
wireObj, err := WireManager.FetchByIdOrName(userCred, input.Wire)
if input.Wire != "" {
wire, vpc, region, err = manager.validateEnsureWire(ctx, userCred, input)
if err != nil {
if err == sql.ErrNoRows {
return input, httperrors.NewNotFoundError("wire %s not found", input.Wire)
} else {
return input, httperrors.NewInternalServerError("query wire %s error %s", input.Wire, err)
}
return input, err
}
} else if input.Zone != "" && input.Vpc != "" {
wire, vpc, region, err = manager.validateEnsureZoneVpc(ctx, userCred, input)
if err != nil {
return input, err
}
input.WireId = wireObj.GetId()
} else {
if len(input.Zone) > 0 {
if len(input.Vpc) > 0 {
zoneObj, err := ZoneManager.FetchByIdOrName(userCred, input.Zone)
if err != nil {
if err == sql.ErrNoRows {
return input, httperrors.NewNotFoundError("zone %s not found", input.Zone)
} else {
return input, httperrors.NewInternalServerError("query zone %s error %s", input.Zone, err)
}
}
vpcObj, err := VpcManager.FetchByIdOrName(userCred, input.Vpc)
if err != nil {
if err == sql.ErrNoRows {
return input, httperrors.NewNotFoundError("vpc %s not found", input.Vpc)
} else {
return input, httperrors.NewInternalServerError("query vpc %s error %s", input.Vpc, err)
}
}
vpc := vpcObj.(*SVpc)
zone := zoneObj.(*SZone)
region := zone.GetRegion()
if region == nil {
return input, httperrors.NewInternalServerError("zone %s related region not found", zone.Id)
}
// 华为云,ucloud wire zone_id 为空
var wires []SWire
if utils.IsInStringArray(region.Provider, api.REGIONAL_NETWORK_PROVIDERS) {
wires, err = WireManager.getWiresByVpcAndZone(vpc, nil)
} else {
wires, err = WireManager.getWiresByVpcAndZone(vpc, zone)
}
if err != nil {
return input, httperrors.NewInternalServerError("query wire for zone %s and vpc %s: %v", input.Zone, input.Vpc, err)
}
if len(wires) == 0 {
if region.Provider == api.CLOUD_PROVIDER_ONECLOUD {
wire, err := vpc.initWire(ctx, zone)
if err != nil {
return input, httperrors.NewNotFoundError("init wire for zone %s and vpc %s: %v", input.Zone, input.Vpc, err)
}
input.WireId = wire.Id
} else {
return input, httperrors.NewNotFoundError("wire not found for zone %s and vpc %s", input.Zone, input.Vpc)
}
} else if len(wires) > 1 {
return input, httperrors.NewConflictError("found %d wires for zone %s and vpc %s", len(wires), input.Zone, input.Vpc)
} else {
input.WireId = wires[0].Id
}
} else {
return input, httperrors.NewInputParameterError("No either wire or vpc provided")
}
} else {
return input, httperrors.NewInvalidStatusError("No either wire or zone provided")
}
return input, httperrors.NewInputParameterError("zone and vpc info required when wire is absent")
}
if len(input.WireId) == 0 {
return input, httperrors.NewMissingParameterError("wire_id")
}
wire := WireManager.FetchWireById(input.WireId)
if wire == nil {
return input, httperrors.NewResourceNotFoundError("wire %s not found", input.WireId)
}
vpc := wire.getVpc()
if vpc == nil {
return input, httperrors.NewInputParameterError("no valid vpc ???")
}
input.WireId = wire.Id
if vpc.Status != api.VPC_STATUS_AVAILABLE {
return input, httperrors.NewInvalidStatusError("VPC not ready")
}
netRange := netutils.NewIPV4AddrRange(startIp, endIp)
if !vpc.containsIPV4Range(netRange) {
return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock)
var (
ipStart = ipRange.StartIp()
ipEnd = ipRange.EndIp()
)
if region.Provider == api.CLOUD_PROVIDER_ONECLOUD && vpc.Id != api.DEFAULT_VPC_ID {
// reserve addresses for onecloud vpc networks
masklen := int8(input.GuestIpMask)
netAddr := ipStart.NetAddr(masklen)
if masklen >= 30 {
return input, httperrors.NewInputParameterError("subnet masklen should be smaller than 30")
}
if netAddr != ipEnd.NetAddr(masklen) {
return input, httperrors.NewInputParameterError("start and end ip when masked are not in the same cidr subnet")
}
gateway := netAddr.StepUp()
brdAddr := ipStart.BroadcastAddr(masklen)
// NOTE
//
// - reserve the 1st addr as gateway
// - reserve the last ip for broadcasting
// - reserve the 2nd-to-last for possible future use
//
// We do not allow split 192.168.1.0/24 into multiple ranges
// like
//
// - 192.168.1.50-192.168.1.100,
// - 192.168.1.100-192.168.1.200
//
// This could complicate gateway setting and topology
// management without much benefit to end users
ipStart = gateway.StepUp()
ipEnd = brdAddr.StepDown().StepDown()
input.GuestGateway = gateway.String()
}
if len(input.ServerType) == 0 {
input.ServerType = api.NETWORK_TYPE_GUEST
} else if !utils.IsInStringArray(input.ServerType, ALL_NETWORK_TYPES) {
return input, httperrors.NewInputParameterError("Invalid server_type: %s", input.ServerType)
{
netRange := netutils.NewIPV4AddrRange(ipStart, ipEnd)
if !vpc.containsIPV4Range(netRange) {
return input, httperrors.NewInputParameterError("Network not in range of VPC cidrblock %s", vpc.CidrBlock)
}
}
{
nets := manager.getAllNetworks(wire.Id, "")
if nets == nil {
return input, httperrors.NewInternalServerError("query all networks fail")
}
if isOverlapNetworks(nets, ipStart, ipEnd) {
return input, httperrors.NewInputParameterError("Conflict address space with existing networks")
}
}
input.GuestIpStart = ipStart.String()
input.GuestIpEnd = ipEnd.String()
input.SharableVirtualResourceCreateInput, err = manager.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
if err != nil {
return input, err
@@ -1465,7 +1498,7 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.
endIp = tmp
}
nets := NetworkManager.getAllNetworks(self.Id)
nets := NetworkManager.getAllNetworks(self.WireId, self.Id)
if nets == nil {
return nil, httperrors.NewInternalServerError("query all networks fail")
}
@@ -1527,9 +1560,9 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.
return self.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data)
}
func (manager *SNetworkManager) getAllNetworks(excludeId string) []SNetwork {
func (manager *SNetworkManager) getAllNetworks(wireId, excludeId string) []SNetwork {
nets := make([]SNetwork, 0)
q := manager.Query()
q := manager.Query().Equals("wire_id", wireId)
if len(excludeId) > 0 {
q = q.NotEquals("id", excludeId)
}