From d9d343822f193ee4d834cb1eeb11c029914c6107 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 13 Dec 2019 09:40:42 +0000 Subject: [PATCH] networks: validate onecloud vpc subnet Also rewrite for more readability, especially the part on finding wire, vpc, region, etc. --- pkg/compute/models/networks.go | 295 ++++++++++++++++++--------------- 1 file changed, 164 insertions(+), 131 deletions(-) diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index aef9f7de24..605255b17e 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -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) }