Newer
Older
/*
Copyright 2024.
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 (
"context"
"errors"
"fmt"
"os"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
l2smv1 "github.com/Networks-it-uc3m/L2S-M/api/v1"
"github.com/Networks-it-uc3m/L2S-M/internal/sdnclient"
"github.com/Networks-it-uc3m/L2S-M/internal/utils"
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
)
// L2NetworkReconciler reconciles a L2Network object
type L2NetworkReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
// Manages interactions with the onos SDN Controller.
InternalClient sdnclient.Client
}
//+kubebuilder:rbac:groups=l2sm.l2sm.k8s.local,resources=l2networks,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=l2sm.l2sm.k8s.local,resources=l2networks/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=l2sm.l2sm.k8s.local,resources=l2networks/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the L2Network object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.17.0/pkg/reconcile
func (r *L2NetworkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
// log := r.Log.WithValues("l2network", req.NamespacedName)
// Fetch the L2Network instance
network := &l2smv1.L2Network{}
err := r.Get(ctx, req.NamespacedName, network)
if err != nil {
log.Error(err, "unable to fetch L2Network")
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Check if the object is being deleted
if network.GetDeletionTimestamp() != nil {
if utils.ContainsString(network.GetFinalizers(), l2smFinalizer) {
// The object is being deleted
if err := r.InternalClient.DeleteNetwork(network.Spec.Type, network.Name); err != nil {
// If fail to delete the external dependency here, return with error
// so that it can be retried
log.Error(err, "couldn't delete network in sdn controller")
return ctrl.Result{}, err
}
// Remove our finalizer from the list and update it.
network.SetFinalizers(utils.RemoveString(network.GetFinalizers(), l2smFinalizer))
if err := r.Update(ctx, network); err != nil {
log.Error(err, "couldn't remove finalizer to l2network")
return ctrl.Result{}, err
}
}
// Stop reconciliation as the item is being deleted
return ctrl.Result{}, nil
}
// Add finalizer for this CR
if !utils.ContainsString(network.GetFinalizers(), l2smFinalizer) {
err := r.InternalClient.CreateNetwork(network.Spec.Type, sdnclient.VnetPayload{NetworkId: network.Name})
if err != nil {
log.Error(err, "failed to create network")
r.updateControllerStatus(ctx, network, l2smv1.OfflineStatus)
return ctrl.Result{}, err
}
log.Info("Network created in SDN controller", "NetworkID", network.Name)
r.updateControllerStatus(ctx, network, l2smv1.OnlineStatus)
network.SetFinalizers(append(network.GetFinalizers(), l2smFinalizer))
if err := r.Update(ctx, network); err != nil {
return ctrl.Result{}, err
}
}
// If network is inter domain
if network.Spec.Provider != nil {
provStatus, err := interDomainReconcile(network, log)
if err != nil {
log.Error(err, "failed to connect to provider")
}
network.Status.ProviderConnectivity = &provStatus
// Update the status in the Kubernetes API
if statusUpdateErr := r.Status().Update(ctx, network); statusUpdateErr != nil {
log.Error(statusUpdateErr, "unable to update L2Network provider status")
return ctrl.Result{}, statusUpdateErr
}
}
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// exists, err := r.InternalClient.CheckNetworkExists(network.Spec.Type, network.Name)
// if err != nil {
// log.Error(err, "failed to check network existence")
// // Update the status to Unknown due to connection issues
// // Update the status in the Kubernetes API
// r.updateControllerStatus(ctx, network, l2smv1.UnknownStatus)
// return ctrl.Result{}, err
// }
// if !exists {
// err := r.InternalClient.CreateNetwork(network.Spec.Type, sdnclient.VnetPayload{NetworkId: network.Name})
// if err != nil {
// log.Error(err, "failed to create network")
// r.updateControllerStatus(ctx, network, l2smv1.OfflineStatus)
// return ctrl.Result{}, err
// }
// log.Info("Network created in SDN controller", "NetworkID", network.Name)
// } else {
// log.Info("Network already exists in SDN controller, no action needed", "NetworkID", network.Name)
// }
// if statusUpdateErr := r.updateControllerStatus(ctx, network, l2smv1.OnlineStatus); statusUpdateErr != nil {
// log.Error(statusUpdateErr, "unable to update L2Network provider status")
// return ctrl.Result{}, statusUpdateErr
// }
log.Info("something in the rain")
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *L2NetworkReconciler) SetupWithManager(mgr ctrl.Manager) error {
var err error
r.Log.Info("this is the controller ip", os.Getenv("CONTROLLER_IP"))
fmt.Println(os.Getenv("CONTROLLER_IP"))
fmt.Println(os.Getenv("CONTROLLER_PORT"))
// Initialize the InternalClient with the base URL of the SDN controller
clientConfig := sdnclient.ClientConfig{BaseURL: fmt.Sprintf("http://%s:%s/onos", os.Getenv("CONTROLLER_IP"), os.Getenv("CONTROLLER_PORT")), Username: "karaf", Password: "karaf"}
r.InternalClient, err = sdnclient.NewClient(sdnclient.InternalType, clientConfig)
if err != nil {
r.Log.Error(err, "failed to initiate session with sdn controller")
return err
}
return ctrl.NewControllerManagedBy(mgr).
For(&l2smv1.L2Network{}). // Watch for changes to primary resource L2Network
Complete(r)
}
func interDomainReconcile(network *l2smv1.L2Network, log logr.Logger) (l2smv1.ConnectivityStatus, error) {
if network.Spec.Provider == nil {
return l2smv1.UnknownStatus, errors.New("ext-vnet doesn't have a provider specified")
}
clientConfig := sdnclient.ClientConfig{BaseURL: fmt.Sprintf("http://%s/onos", network.Spec.Provider.Domain), Username: "karaf", Password: "karaf"}
externalClient, err := sdnclient.NewClient(sdnclient.InternalType, clientConfig)
if err != nil {
return l2smv1.OfflineStatus, fmt.Errorf("could not initialize session with external provider: %v", err)
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
}
exists, err := externalClient.CheckNetworkExists(network.Spec.Type, network.Name)
if err != nil {
log.Error(err, "failed to check network existence")
return l2smv1.UnknownStatus, err
}
if !exists {
err := externalClient.CreateNetwork(network.Spec.Type, sdnclient.VnetPayload{NetworkId: network.Name})
if err != nil {
log.Error(err, "failed to create network")
return l2smv1.OfflineStatus, err
}
log.Info("Network created in Provider controller", "NetworkID", network.Name)
} else {
log.Info("Network already exists in Provider controller, no action needed", "NetworkID", network.Name)
}
return l2smv1.OnlineStatus, nil
}
func (r *L2NetworkReconciler) updateControllerStatus(ctx context.Context, network *l2smv1.L2Network, status l2smv1.ConnectivityStatus) error {
network.Status.InternalConnectivity = &status
return r.Status().Update(ctx, network)
}