-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basically the idea is that a set of validating functions will be mapped to specific API paths, and automatically executed. If any of them return an error, we block the object provisioning. The framework enables the easy expansion of the wbehook with other APIs... which we might need preeeeettyyyy sooon in the future ;) Several functions in ipam and netcontrol packages are moved around to better fitting places.
- Loading branch information
Showing
6 changed files
with
262 additions
and
214 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,93 @@ | ||
package netadmit | ||
|
||
import ( | ||
"errors" | ||
"net" | ||
"encoding/json" | ||
"io/ioutil" | ||
"net/http" | ||
"k8s.io/api/admission/v1beta1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/runtime/serializer" | ||
danmtypes "github.com/nokia/danm/crd/apis/danm/v1" | ||
"github.com/nokia/danm/pkg/bitarray" | ||
"github.com/nokia/danm/pkg/ipam" | ||
) | ||
|
||
func ValidateNetwork(responseWriter http.ResponseWriter, request *http.Request) { | ||
admissionReview, err := DecodeAdmissionReview(request) | ||
if err != nil { | ||
return | ||
} | ||
manifest, err := getNetworkManifest(admissionReview.Request.Object.Raw) | ||
if err != nil { | ||
return | ||
} | ||
isManifestValid, err := validateNetworkByType(manifest) | ||
if !isManifestValid { | ||
return | ||
} | ||
/* bitArray, err := CreateAllocationArray(dnet) | ||
if err != nil { | ||
return err | ||
} | ||
dnet.Spec.Options.Alloc = bitArray.Encode()*/ | ||
return | ||
} | ||
|
||
func DecodeAdmissionReview(httpRequest *http.Request) (*v1beta1.AdmissionReview,error) { | ||
var payload []byte | ||
if httpRequest.Body == nil { | ||
return nil, errors.New("Received review request is empty!") | ||
} | ||
payload, err := ioutil.ReadAll(httpRequest.Body); | ||
if err != nil { | ||
return nil, err | ||
} | ||
codecs := serializer.NewCodecFactory(runtime.NewScheme()) | ||
deserializer := codecs.UniversalDeserializer() | ||
reviewRequest := v1beta1.AdmissionReview{} | ||
_, _, err = deserializer.Decode(payload, nil, &reviewRequest) | ||
return &reviewRequest, err | ||
} | ||
|
||
func getNetworkManifest(objectToReview []byte) (*danmtypes.DanmNet,error) { | ||
networkManifest := danmtypes.DanmNet{} | ||
err := json.Unmarshal(objectToReview, &networkManifest) | ||
return &networkManifest, err | ||
} | ||
|
||
func validateNetworkByType(manifest *danmtypes.DanmNet) (bool,error) { | ||
for _, validatorMapping := range danmValidationConfig.ValidatorMappings { | ||
if validatorMapping.ApiType == manifest.TypeMeta.Kind { | ||
for _, validator := range validatorMapping.Validators { | ||
err := validator(manifest) | ||
if err != nil { | ||
return false, err | ||
} | ||
} | ||
} | ||
} | ||
return true, nil | ||
} | ||
|
||
func CreateAllocationArray(dnet *danmtypes.DanmNet) (*bitarray.BitArray,error) { | ||
_,ipnet,_ := net.ParseCIDR(dnet.Spec.Options.Cidr) | ||
bitArray, err := bitarray.CreateBitArrayFromIpnet(ipnet) | ||
if err != nil { | ||
return nil, err | ||
} | ||
err = reserveGatewayIps(dnet.Spec.Options.Routes, bitArray, ipnet) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return bitArray, nil | ||
} | ||
|
||
func reserveGatewayIps(routes map[string]string, bitArray *bitarray.BitArray, ipnet *net.IPNet) error { | ||
for _, gw := range routes { | ||
gatewayPosition := ipam.Ip2int(net.ParseIP(gw)) - ipam.Ip2int(ipnet.IP) | ||
bitArray.Set(gatewayPosition) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package netadmit | ||
|
||
import ( | ||
"errors" | ||
"net" | ||
"encoding/binary" | ||
danmtypes "github.com/nokia/danm/crd/apis/danm/v1" | ||
"github.com/nokia/danm/pkg/ipam" | ||
) | ||
|
||
type Validator func(netInfo *danmtypes.DanmNet) error | ||
|
||
type ValidatorConfig struct { | ||
ValidatorMappings []ValidatorMapping | ||
} | ||
|
||
type ValidatorMapping struct { | ||
ApiType string | ||
Validators []Validator | ||
} | ||
|
||
const ( | ||
MaxNidLength = 12 | ||
) | ||
|
||
var ( | ||
DanmNetMapping = ValidatorMapping { | ||
ApiType: "DanmNet", | ||
Validators: []Validator{validateIpv4Fields,validateIpv6Fields,validateAllocationPool,validateVids,validateNetworkId}, | ||
} | ||
danmValidationConfig = ValidatorConfig { | ||
ValidatorMappings: []ValidatorMapping{DanmNetMapping}, | ||
} | ||
) | ||
|
||
func validateIpv4Fields(dnet *danmtypes.DanmNet) error { | ||
return validateIpFields(dnet.Spec.Options.Cidr, dnet.Spec.Options.Routes) | ||
} | ||
|
||
func validateIpv6Fields(dnet *danmtypes.DanmNet) error { | ||
return validateIpFields(dnet.Spec.Options.Net6, dnet.Spec.Options.Routes6) | ||
} | ||
|
||
func validateIpFields(cidr string, routes map[string]string) error { | ||
if cidr == "" { | ||
if routes != nil { | ||
return errors.New("IP routes cannot be defined for a L2 network") | ||
} | ||
return nil | ||
} | ||
_, ipnet, err := net.ParseCIDR(cidr) | ||
if err != nil { | ||
return errors.New("Invalid CIDR: " + cidr) | ||
} | ||
for _, gw := range routes { | ||
if !ipnet.Contains(net.ParseIP(gw)) { | ||
return errors.New("Specified GW address:" + gw + " is not part of CIDR:" + cidr) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func validateAllocationPool(dnet *danmtypes.DanmNet) error { | ||
cidr := dnet.Spec.Options.Cidr | ||
apStart := dnet.Spec.Options.Pool.Start | ||
apEnd := dnet.Spec.Options.Pool.End | ||
if cidr == "" { | ||
if apStart != "" || apEnd != "" { | ||
return errors.New("Allocation pool cannot be defined without CIDR!") | ||
} | ||
return nil | ||
} | ||
_, ipnet, err := net.ParseCIDR(dnet.Spec.Options.Cidr) | ||
if err != nil { | ||
return errors.New("Invalid CIDR parameter: " + dnet.Spec.Options.Cidr) | ||
} | ||
if dnet.Spec.Options.Pool.Start == "" { | ||
dnet.Spec.Options.Pool.Start = (ipam.Int2ip(ipam.Ip2int(ipnet.IP) + 1)).String() | ||
} | ||
if dnet.Spec.Options.Pool.End == "" { | ||
dnet.Spec.Options.Pool.End = (ipam.Int2ip(ipam.Ip2int(getBroadcastAddress(ipnet)) - 1)).String() | ||
} | ||
if !ipnet.Contains(net.ParseIP(dnet.Spec.Options.Pool.Start)) || !ipnet.Contains(net.ParseIP(dnet.Spec.Options.Pool.End)) { | ||
return errors.New("Allocation pool is outside of defined CIDR") | ||
} | ||
if ipam.Ip2int(net.ParseIP(dnet.Spec.Options.Pool.End)) - ipam.Ip2int(net.ParseIP(dnet.Spec.Options.Pool.Start)) <= 0 { | ||
return errors.New("Allocation pool start:" + dnet.Spec.Options.Pool.Start + " is bigger than end:" + dnet.Spec.Options.Pool.End) | ||
} | ||
return nil | ||
} | ||
|
||
func getBroadcastAddress(subnet *net.IPNet) (net.IP) { | ||
ip := make(net.IP, len(subnet.IP.To4())) | ||
binary.BigEndian.PutUint32(ip, binary.BigEndian.Uint32(subnet.IP.To4())|^binary.BigEndian.Uint32(net.IP(subnet.Mask).To4())) | ||
return ip | ||
} | ||
|
||
func validateVids(dnet *danmtypes.DanmNet) error { | ||
isVlanDefined := (dnet.Spec.Options.Vlan!=0) | ||
isVxlanDefined := (dnet.Spec.Options.Vxlan!=0) | ||
if isVlanDefined && isVxlanDefined { | ||
return errors.New("VLAN ID and VxLAN ID parameters are mutually exclusive") | ||
} | ||
return nil | ||
} | ||
|
||
func validateNetworkId(dnet *danmtypes.DanmNet) error { | ||
if dnet.Spec.NetworkID == "" { | ||
return errors.New("Spec.NetworkID mandatory parameter is missing!") | ||
} | ||
if len(dnet.Spec.NetworkID) > MaxNidLength { | ||
return errors.New("Spec.NetworkID cannot be longer than 12 characters (otherwise VLAN and VxLAN host interface creation might fail)!") | ||
} | ||
return nil | ||
} |
Oops, something went wrong.