15 bpav1alpha1 "github.com/bpa-operator/pkg/apis/bpa/v1alpha1"
16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
18 "k8s.io/apimachinery/pkg/runtime/schema"
19 "k8s.io/apimachinery/pkg/api/errors"
20 "k8s.io/apimachinery/pkg/runtime"
21 "k8s.io/client-go/tools/clientcmd"
22 "k8s.io/client-go/dynamic"
23 "sigs.k8s.io/controller-runtime/pkg/client"
24 "sigs.k8s.io/controller-runtime/pkg/controller"
25 "sigs.k8s.io/controller-runtime/pkg/handler"
26 "sigs.k8s.io/controller-runtime/pkg/manager"
27 "sigs.k8s.io/controller-runtime/pkg/reconcile"
28 logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
29 "sigs.k8s.io/controller-runtime/pkg/source"
33 var log = logf.Log.WithName("controller_provisioning")
34 var dhcpLeaseFile = "/var/lib/dhcp/dhcpd.leases"
37 * USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
38 * business logic. Delete these comments after modifying this file.*
41 // Add creates a new Provisioning Controller and adds it to the Manager. The Manager will set fields on the Controller
42 // and Start it when the Manager is Started.
43 func Add(mgr manager.Manager) error {
44 return add(mgr, newReconciler(mgr))
47 // newReconciler returns a new reconcile.Reconciler
48 func newReconciler(mgr manager.Manager) reconcile.Reconciler {
49 return &ReconcileProvisioning{client: mgr.GetClient(), scheme: mgr.GetScheme()}
52 // add adds a new Controller to mgr with r as the reconcile.Reconciler
53 func add(mgr manager.Manager, r reconcile.Reconciler) error {
54 // Create a new controller
55 c, err := controller.New("provisioning-controller", mgr, controller.Options{Reconciler: r})
60 // Watch for changes to primary resource Provisioning
61 err = c.Watch(&source.Kind{Type: &bpav1alpha1.Provisioning{}}, &handler.EnqueueRequestForObject{})
70 // blank assignment to verify that ReconcileProvisioning implements reconcile.Reconciler
71 var _ reconcile.Reconciler = &ReconcileProvisioning{}
73 // ReconcileProvisioning reconciles a Provisioning object
74 type ReconcileProvisioning struct {
75 // This client, initialized using mgr.Client() above, is a split client
76 // that reads objects from the cache and writes to the apiserver
78 scheme *runtime.Scheme
81 // Reconcile reads that state of the cluster for a Provisioning object and makes changes based on the state read
82 // and what is in the Provisioning.Spec
83 // TODO(user): Modify this Reconcile function to implement your Controller logic. This example creates
84 // a Pod as an example
86 // The Controller will requeue the Request to be processed again if the returned error is non-nil or
87 // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
88 func (r *ReconcileProvisioning) Reconcile(request reconcile.Request) (reconcile.Result, error) {
89 reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
90 reqLogger.Info("Reconciling Provisioning")
92 // Fetch the Provisioning instance
93 provisioningInstance := &bpav1alpha1.Provisioning{}
94 err := r.client.Get(context.TODO(), request.NamespacedName, provisioningInstance)
96 if errors.IsNotFound(err) {
97 // Request object not found, could have been deleted after reconcile request.
98 // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
99 // Return and don't requeue
100 return reconcile.Result{}, nil
102 // Error reading the object - requeue the request.
103 return reconcile.Result{}, err
106 mastersList := provisioningInstance.Spec.Masters
107 workersList := provisioningInstance.Spec.Workers
108 bareMetalHostList, _ := listBareMetalHosts()
112 var masterString string
113 var workerString string
115 //Iterate through mastersList and get all the mac addresses and IP addresses
117 for _, masterMap := range mastersList {
119 for masterLabel, master := range masterMap {
120 containsMac, bmhCR := checkMACaddress(bareMetalHostList, master.MACaddress)
122 //fmt.Println( master.MACaddress)
123 fmt.Printf("BareMetalHost CR %s has NIC with MAC Address %s\n", bmhCR, master.MACaddress)
125 //Get IP address of master
126 hostIPaddress, err := getHostIPaddress(master.MACaddress, dhcpLeaseFile )
128 fmt.Printf("IP address not found for host with MAC address %s \n", master.MACaddress)
133 allString += masterLabel + " ansible_host=" + hostIPaddress + " ip=" + hostIPaddress + "\n"
134 masterString += masterLabel + "\n"
136 fmt.Printf("%s : %s \n", hostIPaddress, master.MACaddress)
142 fmt.Printf("Host with MAC Address %s not found\n", master.MACaddress)
148 //Iterate through workersList and get all the mac addresses
149 for _, workerMap := range workersList {
151 for workerLabel, worker := range workerMap {
152 containsMac, bmhCR := checkMACaddress(bareMetalHostList, worker.MACaddress)
154 //fmt.Println( worker.MACaddress)
155 fmt.Printf("Host %s matches that macAddress\n", bmhCR)
157 //Get IP address of worker
158 hostIPaddress, err := getHostIPaddress(worker.MACaddress, dhcpLeaseFile )
160 fmt.Printf("IP address not found for host with MAC address %s \n", worker.MACaddress)
162 fmt.Printf("%s : %s \n", hostIPaddress, worker.MACaddress)
164 allString += workerLabel + " ansible_host=" + hostIPaddress + " ip=" + hostIPaddress + "\n"
165 workerString += workerLabel + "\n"
169 fmt.Printf("Host with MAC Address %s not found\n", worker.MACaddress)
176 //Create host.ini file
177 iniHostFilePath := provisioningInstance.Spec.HostsFile
178 newFile, err := os.Create(iniHostFilePath)
179 defer newFile.Close()
183 fmt.Printf("Error occured while creating file \n %v", err)
186 hostFile, err := ini.Load(iniHostFilePath)
188 fmt.Printf("Error occured while Loading file \n %v", err)
191 _, err = hostFile.NewRawSection("all", allString)
193 fmt.Printf("Error occured while creating section \n %v", err)
195 _, err = hostFile.NewRawSection("kube-master", masterString)
197 fmt.Printf("Error occured while creating section \n %v", err)
200 _, err = hostFile.NewRawSection("kube-node", workerString)
202 fmt.Printf("Error occured while creating section \n %v", err)
205 _, err = hostFile.NewRawSection("etcd", masterString)
207 fmt.Printf("Error occured while creating section \n %v", err)
210 _, err = hostFile.NewRawSection("k8s-cluser:children", "kube-node\n" + "kube-master")
212 fmt.Printf("Error occured while creating section \n %v", err)
216 hostFile.SaveTo(iniHostFilePath)
217 return reconcile.Result{}, nil
221 //Function to Get List containing baremetal hosts
222 func listBareMetalHosts() (*unstructured.UnstructuredList, error) {
224 //Get Current User and kube config file
225 usr, err := user.Current()
227 fmt.Println("Could not get current user\n")
228 return &unstructured.UnstructuredList{}, err
231 kubeConfig := filepath.Join(usr.HomeDir, ".kube", "config")
234 config, err := clientcmd.BuildConfigFromFlags("", kubeConfig)
236 fmt.Println("Could not build config\n")
237 return &unstructured.UnstructuredList{}, err
240 //Create Dynamic Client for BareMetalHost CRD
241 bmhDynamicClient, err := dynamic.NewForConfig(config)
244 fmt.Println("Could not create dynamic client for bareMetalHosts\n")
245 return &unstructured.UnstructuredList{}, err
248 //Create GVR representing a BareMetalHost CR
249 bmhGVR := schema.GroupVersionResource{
252 Resource: "baremetalhosts",
255 //Get List containing all BareMetalHosts CRs
256 bareMetalHosts, err := bmhDynamicClient.Resource(bmhGVR).List(metav1.ListOptions{})
258 fmt.Println("Error occured, cannot get BareMetalHosts list\n")
259 return &unstructured.UnstructuredList{}, err
262 return bareMetalHosts, nil
265 //Function to check if BareMetalHost containing MAC address exist
266 func checkMACaddress(bareMetalHostList *unstructured.UnstructuredList, macAddress string) (bool, string) {
268 //Convert macAddress to byte array for comparison
269 macAddressByte := []byte(macAddress)
272 for _, bareMetalHost := range bareMetalHostList.Items {
273 bmhJson, _ := bareMetalHost.MarshalJSON()
275 macBool = bytes.Contains(bmhJson, macAddressByte)
277 return macBool, bareMetalHost.GetName()
286 func getHostIPaddress(macAddress string, dhcpLeaseFilePath string ) (string, error) {
288 //Read the dhcp lease file
289 dhcpFile, err := ioutil.ReadFile(dhcpLeaseFilePath)
291 fmt.Println("Failed to read lease file\n")
295 dhcpLeases := string(dhcpFile)
297 //Regex to use to search dhcpLeases
298 regex := "lease.*{|ethernet.*"
299 re, err := regexp.Compile(regex)
301 fmt.Println("Could not create Regexp object\n")
305 //Get String containing leased Ip addresses and Corressponding MAC addresses
306 out := re.FindAllString(dhcpLeases, -1)
307 outString := strings.Join(out, " ")
308 stringReplacer := strings.NewReplacer("lease", "", "{ ethernet ", "", ";", "")
309 replaced := stringReplacer.Replace(outString)
310 ipMacList := strings.Fields(replaced)
313 //Get IP addresses corresponding to Input MAC Address
314 for idx := len(ipMacList)-1 ; idx >= 0; idx -- {
315 item := ipMacList[idx]
316 if item == macAddress {
317 ipAdd := ipMacList[idx -1]