Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/v1/ipaddress_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type IpAddressSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

//+kubebuilder:validation:Pattern=`^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\/([1-9]|[12][0-9]|3[0-2])$`
//+kubebuilder:validation:Format=cidr
//+kubebuilder:validation:XValidation:rule="self == oldSelf",message="Field 'ipAddress' is immutable"
//+kubebuilder:validation:Required
IpAddress string `json:"ipAddress"`
Expand Down
2 changes: 1 addition & 1 deletion api/v1/ipaddressclaim_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type IpAddressClaimSpec struct {
// Important: Run "make" to regenerate code after modifying this file

//+kubebuilder:validation:Required
//+kubebuilder:validation:Pattern=`^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\/([1-9]|[12][0-9]|3[0-2])$`
//+kubebuilder:validation:Format=cidr
//+kubebuilder:validation:XValidation:rule="self == oldSelf",message="Field 'parentPrefix' is immutable"
ParentPrefix string `json:"parentPrefix"`

Expand Down
2 changes: 1 addition & 1 deletion api/v1/prefix_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type PrefixSpec struct {
// Important: Run "make" to regenerate code after modifying this file

//+kubebuilder:validation:Required
//+kubebuilder:validation:Pattern=`^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\/([1-9]|[12][0-9]|3[0-2])$`
//+kubebuilder:validation:Format=cidr
//+kubebuilder:validation:XValidation:rule="self == oldSelf",message="Field 'prefix' is immutable"
Prefix string `json:"prefix"`

Expand Down
4 changes: 2 additions & 2 deletions api/v1/prefixclaim_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ type PrefixClaimSpec struct {
// Important: Run "make" to regenerate code after modifying this file

//+kubebuilder:validation:Required
//+kubebuilder:validation:Pattern=`^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\/([1-9]|[12][0-9]|3[0-2])$`
//+kubebuilder:validation:Format=cidr
//+kubebuilder:validation:XValidation:rule="self == oldSelf",message="Field 'parentPrefix' is immutable"
ParentPrefix string `json:"parentPrefix"`

//+kubebuilder:validation:Required
//+kubebuilder:validation:Pattern=`^\/([1-9]|[12][0-9]|3[0-2])$`
//+kubebuilder:validation:Pattern=`^\/[0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]$`
//+kubebuilder:validation:XValidation:rule="self == oldSelf",message="Field 'prefixLength' is immutable"
PrefixLength string `json:"prefixLength"`

Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/netbox.dev_ipaddressclaims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ spec:
description:
type: string
parentPrefix:
pattern: ^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\/([1-9]|[12][0-9]|3[0-2])$
format: cidr
type: string
x-kubernetes-validations:
- message: Field 'parentPrefix' is immutable
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/netbox.dev_ipaddresses.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ spec:
description:
type: string
ipAddress:
pattern: ^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\/([1-9]|[12][0-9]|3[0-2])$
format: cidr
type: string
x-kubernetes-validations:
- message: Field 'ipAddress' is immutable
Expand Down
4 changes: 2 additions & 2 deletions config/crd/bases/netbox.dev_prefixclaims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ spec:
description:
type: string
parentPrefix:
pattern: ^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\/([1-9]|[12][0-9]|3[0-2])$
format: cidr
type: string
x-kubernetes-validations:
- message: Field 'parentPrefix' is immutable
rule: self == oldSelf
prefixLength:
pattern: ^\/([1-9]|[12][0-9]|3[0-2])$
pattern: ^\/[0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]$
type: string
x-kubernetes-validations:
- message: Field 'prefixLength' is immutable
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/netbox.dev_prefixes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ spec:
description:
type: string
prefix:
pattern: ^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\/([1-9]|[12][0-9]|3[0-2])$
format: cidr
type: string
x-kubernetes-validations:
- message: Field 'prefix' is immutable
Expand Down
14 changes: 8 additions & 6 deletions internal/controller/ipaddress_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,10 @@ func (r *IpAddressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
}

// get name of parent prefix
parentPrefixName := strings.Replace(ipAddressClaim.Spec.ParentPrefix, "/", "-", -1)

leaseLockerNSN := types.NamespacedName{Name: parentPrefixName, Namespace: r.OperatorNamespace}
leaseLockerNSN := types.NamespacedName{
Name: convertCIDRToLeaseLockName(ipAddressClaim.Spec.ParentPrefix),
Namespace: r.OperatorNamespace,
}
ll, err = leaselocker.NewLeaseLocker(r.RestConfig, leaseLockerNSN, req.NamespacedName.String())
if err != nil {
return ctrl.Result{}, err
Expand All @@ -132,13 +133,14 @@ func (r *IpAddressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
// create lock
locked := ll.TryLock(lockCtx)
if !locked {
logger.Info(fmt.Sprintf("failed to lock parent prefix %s", parentPrefixName))
r.Recorder.Eventf(o, corev1.EventTypeWarning, "FailedToLockParentPrefix", "failed to lock parent prefix %s", parentPrefixName)
logger.Info(fmt.Sprintf("failed to lock parent prefix %s", ipAddressClaim.Spec.ParentPrefix))
r.Recorder.Eventf(o, corev1.EventTypeWarning, "FailedToLockParentPrefix", "failed to lock parent prefix %s",
ipAddressClaim.Spec.ParentPrefix)
return ctrl.Result{
RequeueAfter: 2 * time.Second,
}, nil
}
debugLogger.Info(fmt.Sprintf("sucessfully locked parent prefix %s", parentPrefixName))
debugLogger.Info(fmt.Sprintf("successfully locked parent prefix %s", ipAddressClaim.Spec.ParentPrefix))
}

// 2. reserve or update ip address in netbox
Expand Down
26 changes: 14 additions & 12 deletions internal/controller/ipaddressclaim_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,10 @@ func (r *IpAddressClaimReconciler) Reconcile(ctx context.Context, req ctrl.Reque
debugLogger.Info("ipaddress object matching ipaddress claim was not found, creating new ipaddress object")

// 2. check if lease for parent prefix is available
parentPrefixName := strings.ReplaceAll(o.Spec.ParentPrefix, "/", "-")

leaseLockerNSN := types.NamespacedName{Name: parentPrefixName, Namespace: r.OperatorNamespace}
leaseLockerNSN := types.NamespacedName{
Name: convertCIDRToLeaseLockName(o.Spec.ParentPrefix),
Namespace: r.OperatorNamespace,
}
ll, err := leaselocker.NewLeaseLocker(r.RestConfig, leaseLockerNSN, req.Namespace+"/"+ipAddressName)
if err != nil {
return ctrl.Result{}, err
Expand All @@ -110,21 +111,24 @@ func (r *IpAddressClaimReconciler) Reconcile(ctx context.Context, req ctrl.Reque
locked := ll.TryLock(lockCtx)
if !locked {
// lock for parent prefix was not available, rescheduling
logger.Info(fmt.Sprintf("failed to lock parent prefix %s", parentPrefixName))
r.Recorder.Eventf(o, corev1.EventTypeWarning, "FailedToLockParentPrefix", "failed to lock parent prefix %s", parentPrefixName)
logger.Info(fmt.Sprintf("failed to lock parent prefix %s", o.Spec.ParentPrefix))
r.Recorder.Eventf(o, corev1.EventTypeWarning, "FailedToLockParentPrefix", "failed to lock parent prefix %s",
o.Spec.ParentPrefix)
return ctrl.Result{
RequeueAfter: 2 * time.Second,
}, nil
}
debugLogger.Info(fmt.Sprintf("successfully locked parent prefix %s", parentPrefixName))
debugLogger.Info(fmt.Sprintf("successfully locked parent prefix %s", o.Spec.ParentPrefix))

// 4. try to reclaim ip address
h := generateIpAddressRestorationHash(o)
ipAddressModel, err := r.NetboxClient.RestoreExistingIpByHash(config.GetOperatorConfig().NetboxRestorationHashFieldName, h)
if err != nil {
return ctrl.Result{}, err
if setConditionErr := r.SetConditionAndCreateEvent(ctx, o, netboxv1.ConditionIpAssignedFalse, corev1.EventTypeWarning, err.Error()); setConditionErr != nil {
return ctrl.Result{}, fmt.Errorf("error updating status: %w, looking up ip by hash failed: %w", setConditionErr, err)
}
return ctrl.Result{Requeue: true}, nil
}
// TODO: set condition for each error

if ipAddressModel == nil {
// ip address cannot be restored from netbox
Expand All @@ -137,11 +141,9 @@ func (r *IpAddressClaimReconciler) Reconcile(ctx context.Context, req ctrl.Reque
},
})
if err != nil {
setConditionErr := r.SetConditionAndCreateEvent(ctx, o, netboxv1.ConditionIpAssignedFalse, corev1.EventTypeWarning, err.Error())
if setConditionErr != nil {
return ctrl.Result{}, fmt.Errorf("error updating status: %w, when getting an available IP address failed: %w", setConditionErr, err)
if setConditionErr := r.SetConditionAndCreateEvent(ctx, o, netboxv1.ConditionIpAssignedFalse, corev1.EventTypeWarning, err.Error()); setConditionErr != nil {
return ctrl.Result{}, fmt.Errorf("error updating status: %w, when assignment of ip address failed: %w", setConditionErr, err)
}

return ctrl.Result{Requeue: true}, nil
}
debugLogger.Info(fmt.Sprintf("ip address is not reserved in netbox, assigned new ip address: %s", ipAddressModel.IpAddress))
Expand Down
3 changes: 3 additions & 0 deletions internal/controller/netbox_testdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/netbox-community/go-netbox/v3/netbox/client/tenancy"
netboxModels "github.com/netbox-community/go-netbox/v3/netbox/models"
netboxv1 "github.com/netbox-community/netbox-operator/api/v1"
"github.com/netbox-community/netbox-operator/pkg/netbox/api"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand All @@ -40,6 +41,7 @@ var comments = "integration test comment"
var siteSlug = "mars-ip-claim"

var ipAddress = "1.0.0.1/32"
var ipAddressFamily = int64(api.IPv4Family)
var parentPrefix = "1.0.0.0/28"

var siteId = int64(2)
Expand Down Expand Up @@ -136,6 +138,7 @@ func mockedResponseExpectedAvailableIpAddress() []*netboxModels.AvailableIP {
return []*netboxModels.AvailableIP{
{
Address: ipAddress,
Family: ipAddressFamily,
},
}
}
Expand Down
14 changes: 8 additions & 6 deletions internal/controller/prefix_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,10 @@ func (r *PrefixReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
}

// get the name of the parent prefix
parentPrefixName := strings.Replace(prefixClaim.Spec.ParentPrefix, "/", "-", -1)

leaseLockerNSN := types.NamespacedName{Name: parentPrefixName, Namespace: r.OperatorNamespace}
leaseLockerNSN := types.NamespacedName{
Name: convertCIDRToLeaseLockName(prefixClaim.Spec.ParentPrefix),
Namespace: r.OperatorNamespace,
}
ll, err = leaselocker.NewLeaseLocker(r.RestConfig, leaseLockerNSN, req.NamespacedName.String())
if err != nil {
return ctrl.Result{}, err
Expand All @@ -132,13 +133,14 @@ func (r *PrefixReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr

// create lock
if locked := ll.TryLock(lockCtx); !locked {
logger.Info(fmt.Sprintf("failed to lock parent prefix %s", parentPrefixName))
r.Recorder.Eventf(prefix, corev1.EventTypeWarning, "FailedToLockParentPrefix", "failed to lock parent prefix %s", parentPrefixName)
logger.Info(fmt.Sprintf("failed to lock parent prefix %s", prefixClaim.Spec.ParentPrefix))
r.Recorder.Eventf(prefix, corev1.EventTypeWarning, "FailedToLockParentPrefix", "failed to lock parent prefix %s",
prefixClaim.Spec.ParentPrefix)
return ctrl.Result{
RequeueAfter: 2 * time.Second,
}, nil
}
debugLogger.Info("sucessfully locked parent prefix %s", parentPrefixName)
debugLogger.Info("successfully locked parent prefix %s", prefixClaim.Spec.ParentPrefix)
}

/* 2. reserve or update Prefix in netbox */
Expand Down
58 changes: 14 additions & 44 deletions internal/controller/prefixclaim_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package controller
import (
"context"
"fmt"
"strings"
"time"

"github.com/netbox-community/netbox-operator/pkg/netbox/models"
Expand Down Expand Up @@ -91,8 +90,10 @@ func (r *PrefixClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request)
debugLogger.Info("the prefix was not found, will create a new prefix object now")

/* 2. check if the lease for parent prefix is available */
parentPrefixName := strings.ReplaceAll(prefixClaim.Spec.ParentPrefix, "/", "-")
leaseLockerNSN := types.NamespacedName{Name: parentPrefixName, Namespace: r.OperatorNamespace}
leaseLockerNSN := types.NamespacedName{
Name: convertCIDRToLeaseLockName(prefixClaim.Spec.ParentPrefix),
Namespace: r.OperatorNamespace,
}
ll, err := leaselocker.NewLeaseLocker(r.RestConfig, leaseLockerNSN, req.Namespace+"/"+prefixName)
if err != nil {
return ctrl.Result{}, err
Expand All @@ -105,21 +106,24 @@ func (r *PrefixClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request)
locked := ll.TryLock(lockCtx)
if !locked {
// lock for parent prefix was not available, rescheduling
logger.Info(fmt.Sprintf("failed to lock parent prefix %s", parentPrefixName))
r.Recorder.Eventf(prefixClaim, corev1.EventTypeWarning, "FailedToLockParentPrefix", "failed to lock parent prefix %s", parentPrefixName)
logger.Info(fmt.Sprintf("failed to lock parent prefix %s", prefixClaim.Spec.ParentPrefix))
r.Recorder.Eventf(prefixClaim, corev1.EventTypeWarning, "FailedToLockParentPrefix", "failed to lock parent prefix %s",
prefixClaim.Spec.ParentPrefix)
return ctrl.Result{
RequeueAfter: 2 * time.Second,
}, nil
}
debugLogger.Info(fmt.Sprintf("successfully locked parent prefix %s", parentPrefixName))
debugLogger.Info(fmt.Sprintf("successfully locked parent prefix %s", prefixClaim.Spec.ParentPrefix))

// 4. try to reclaim Prefix using restorationHash
h := generatePrefixRestorationHash(prefixClaim)
prefixModel, err := r.NetboxClient.RestoreExistingPrefixByHash(h)
if err != nil {
return ctrl.Result{}, err
if setConditionErr := r.SetConditionAndCreateEvent(ctx, prefixClaim, netboxv1.ConditionPrefixAssignedFalse, corev1.EventTypeWarning, err.Error()); setConditionErr != nil {
return ctrl.Result{}, fmt.Errorf("error updating status: %w, when look up of prefix by hash failed: %w", setConditionErr, err)
}
return ctrl.Result{Requeue: true}, nil
}
// TODO: set condition for each error

if prefixModel == nil {
// Prefix cannot be restored from netbox
Expand All @@ -135,11 +139,9 @@ func (r *PrefixClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request)
},
})
if err != nil {
setConditionErr := r.SetConditionAndCreateEvent(ctx, prefixClaim, netboxv1.ConditionPrefixAssignedFalse, corev1.EventTypeWarning, err.Error())
if setConditionErr != nil {
return ctrl.Result{}, fmt.Errorf("error updating status: %w, when getting an available Prefix failed: %w", setConditionErr, err)
if setConditionErr := r.SetConditionAndCreateEvent(ctx, prefixClaim, netboxv1.ConditionPrefixAssignedFalse, corev1.EventTypeWarning, err.Error()); setConditionErr != nil {
return ctrl.Result{}, fmt.Errorf("error updating status: %w, when failed to get matching prefix: %w", setConditionErr, err)
}

return ctrl.Result{Requeue: true}, nil
}
debugLogger.Info(fmt.Sprintf("prefix is not reserved in netbox, assignined new prefix: %s", prefixModel.Prefix))
Expand Down Expand Up @@ -220,38 +222,6 @@ func (r *PrefixClaimReconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}

func (r *PrefixClaimReconciler) GetAvailablePrefix(o *netboxv1.PrefixClaim) (*models.Prefix, error) {
var availablePrefix *models.Prefix
var err error
if availablePrefix, err = r.NetboxClient.GetAvailablePrefixByClaim(
&models.PrefixClaim{
ParentPrefix: o.Spec.ParentPrefix,
PrefixLength: o.Spec.PrefixLength,
Metadata: &models.NetboxMetadata{
Tenant: o.Spec.Tenant,
},
},
); err != nil {
return nil, err
}

if _, err = r.NetboxClient.ReserveOrUpdatePrefix(
&models.Prefix{
Prefix: availablePrefix.Prefix,
Metadata: &models.NetboxMetadata{
Comments: o.Spec.Comments,
Custom: map[string]string{},
Description: o.Spec.Description,
Site: o.Spec.Site,
Tenant: o.Spec.Tenant,
},
}); err != nil {
return nil, err
}

return availablePrefix, nil
}

// TODO(henrybear327): Duplicated code, consider refactoring this
func (r *PrefixClaimReconciler) SetConditionAndCreateEvent(ctx context.Context, o *netboxv1.PrefixClaim, condition metav1.Condition, eventType string, conditionMessageAppend string) error {
if len(conditionMessageAppend) > 0 {
Expand Down
25 changes: 25 additions & 0 deletions internal/controller/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
Copyright 2024 Swisscom (Schweiz) AG.

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 controller

import (
"strings"
)

func convertCIDRToLeaseLockName(cidr string) string {
return strings.ReplaceAll(strings.ReplaceAll(cidr, "/", "-"), ":", "-")
}
20 changes: 19 additions & 1 deletion pkg/netbox/api/ip_address_claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,17 @@ import (
"github.com/netbox-community/netbox-operator/pkg/netbox/utils"
)

type IPFamily int64

const (
IPv4Family IPFamily = iota + 4
_ // Skip 5
IPv6Family
)

const (
ipMask = "/32"
ipMaskIPv4 = "/32"
ipMaskIPv6 = "/128"
)

func (r *NetboxClient) RestoreExistingIpByHash(customFieldName string, hash string) (*models.IPAddress, error) {
Expand Down Expand Up @@ -80,6 +89,15 @@ func (r *NetboxClient) GetAvailableIpAddressByClaim(ipAddressClaim *models.IPAdd
return nil, err
}

var ipMask string
if responseAvailableIPs.Payload[0].Family == int64(IPv4Family) {
ipMask = ipMaskIPv4
} else if responseAvailableIPs.Payload[0].Family == int64(IPv6Family) {
ipMask = ipMaskIPv6
} else {
return nil, errors.New("available ip has unknown IP family")
}

ipAddress, err := r.SetIpAddressMask(responseAvailableIPs.Payload[0].Address, ipMask)
if err != nil {
return nil, err
Expand Down
Loading