16 bpav1alpha1 "github.com/bpa-operator/pkg/apis/bpa/v1alpha1"
17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
19 "k8s.io/apimachinery/pkg/runtime/schema"
20 "k8s.io/apimachinery/pkg/api/errors"
21 "k8s.io/apimachinery/pkg/runtime"
22 "k8s.io/client-go/tools/clientcmd"
23 "k8s.io/client-go/dynamic"
24 "sigs.k8s.io/controller-runtime/pkg/client"
25 "sigs.k8s.io/controller-runtime/pkg/controller"
26 "sigs.k8s.io/controller-runtime/pkg/handler"
27 "sigs.k8s.io/controller-runtime/pkg/manager"
28 "sigs.k8s.io/controller-runtime/pkg/reconcile"
29 logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
30 "sigs.k8s.io/controller-runtime/pkg/source"
34 var log = logf.Log.WithName("controller_provisioning")
35 //Todo: Should be an input from the user
36 var dhcpLeaseFile = "/var/lib/dhcp/dhcpd.leases"
37 var kudInstallerScript = "/root/icn/deploy/kud/multicloud-k8s/kud/hosting_providers/vagrant"
40 * USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
41 * business logic. Delete these comments after modifying this file.*
44 // Add creates a new Provisioning Controller and adds it to the Manager. The Manager will set fields on the Controller
45 // and Start it when the Manager is Started.
46 func Add(mgr manager.Manager) error {
47 return add(mgr, newReconciler(mgr))
50 // newReconciler returns a new reconcile.Reconciler
51 func newReconciler(mgr manager.Manager) reconcile.Reconciler {
52 return &ReconcileProvisioning{client: mgr.GetClient(), scheme: mgr.GetScheme()}
55 // add adds a new Controller to mgr with r as the reconcile.Reconciler
56 func add(mgr manager.Manager, r reconcile.Reconciler) error {
57 // Create a new controller
58 c, err := controller.New("provisioning-controller", mgr, controller.Options{Reconciler: r})
63 // Watch for changes to primary resource Provisioning
64 err = c.Watch(&source.Kind{Type: &bpav1alpha1.Provisioning{}}, &handler.EnqueueRequestForObject{})
73 // blank assignment to verify that ReconcileProvisioning implements reconcile.Reconciler
74 var _ reconcile.Reconciler = &ReconcileProvisioning{}
76 // ReconcileProvisioning reconciles a Provisioning object
77 type ReconcileProvisioning struct {
78 // This client, initialized using mgr.Client() above, is a split client
79 // that reads objects from the cache and writes to the apiserver
81 scheme *runtime.Scheme
84 // Reconcile reads that state of the cluster for a Provisioning object and makes changes based on the state read
85 // and what is in the Provisioning.Spec
86 // TODO(user): Modify this Reconcile function to implement your Controller logic. This example creates
87 // a Pod as an example
89 // The Controller will requeue the Request to be processed again if the returned error is non-nil or
90 // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
91 func (r *ReconcileProvisioning) Reconcile(request reconcile.Request) (reconcile.Result, error) {
92 reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
93 reqLogger.Info("Reconciling Provisioning")
95 // Fetch the Provisioning instance
96 provisioningInstance := &bpav1alpha1.Provisioning{}
97 err := r.client.Get(context.TODO(), request.NamespacedName, provisioningInstance)
99 if errors.IsNotFound(err) {
100 // Request object not found, could have been deleted after reconcile request.
101 // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
102 // Return and don't requeue
103 return reconcile.Result{}, nil
105 // Error reading the object - requeue the request.
106 return reconcile.Result{}, err
109 mastersList := provisioningInstance.Spec.Masters
110 workersList := provisioningInstance.Spec.Workers
111 bareMetalHostList, _ := listBareMetalHosts()
115 var masterString string
116 var workerString string
118 //Iterate through mastersList and get all the mac addresses and IP addresses
120 for _, masterMap := range mastersList {
122 for masterLabel, master := range masterMap {
123 containsMac, bmhCR := checkMACaddress(bareMetalHostList, master.MACaddress)
125 //fmt.Println( master.MACaddress)
126 fmt.Printf("BareMetalHost CR %s has NIC with MAC Address %s\n", bmhCR, master.MACaddress)
128 //Get IP address of master
129 hostIPaddress, err := getHostIPaddress(master.MACaddress, dhcpLeaseFile )
131 fmt.Printf("IP address not found for host with MAC address %s \n", master.MACaddress)
136 allString += masterLabel + " ansible_ssh_host=" + hostIPaddress + " ansible_ssh_port=22" + "\n"
137 masterString += masterLabel + "\n"
139 fmt.Printf("%s : %s \n", hostIPaddress, master.MACaddress)
145 fmt.Printf("Host with MAC Address %s not found\n", master.MACaddress)
151 //Iterate through workersList and get all the mac addresses
152 for _, workerMap := range workersList {
154 for workerLabel, worker := range workerMap {
155 containsMac, bmhCR := checkMACaddress(bareMetalHostList, worker.MACaddress)
157 //fmt.Println( worker.MACaddress)
158 fmt.Printf("Host %s matches that macAddress\n", bmhCR)
160 //Get IP address of worker
161 hostIPaddress, err := getHostIPaddress(worker.MACaddress, dhcpLeaseFile )
163 fmt.Printf("IP address not found for host with MAC address %s \n", worker.MACaddress)
165 fmt.Printf("%s : %s \n", hostIPaddress, worker.MACaddress)
167 allString += workerLabel + " ansible_ssh_host=" + hostIPaddress + " ansible_ssh_port=22" + "\n"
168 workerString += workerLabel + "\n"
172 fmt.Printf("Host with MAC Address %s not found\n", worker.MACaddress)
179 //Create host.ini file
180 iniHostFilePath := provisioningInstance.Spec.HostsFile
181 newFile, err := os.Create(iniHostFilePath)
182 defer newFile.Close()
186 fmt.Printf("Error occured while creating file \n %v", err)
189 hostFile, err := ini.Load(iniHostFilePath)
191 fmt.Printf("Error occured while Loading file \n %v", err)
194 _, err = hostFile.NewRawSection("all", allString)
196 fmt.Printf("Error occured while creating section \n %v", err)
198 _, err = hostFile.NewRawSection("kube-master", masterString)
200 fmt.Printf("Error occured while creating section \n %v", err)
203 _, err = hostFile.NewRawSection("kube-node", workerString)
205 fmt.Printf("Error occured while creating section \n %v", err)
208 _, err = hostFile.NewRawSection("etcd", masterString)
210 fmt.Printf("Error occured while creating section \n %v", err)
213 _, err = hostFile.NewRawSection("k8s-cluser:children", "kube-node\n" + "kube-master")
215 fmt.Printf("Error occured while creating section \n %v", err)
219 hostFile.SaveTo(iniHostFilePath)
221 //TODO: Test KUD installer part
222 //Copy host.ini file to the right path and install KUD
223 dstIniPath := kudInstallerScript + "/inventory/hosts.ini"
224 kudInstaller(iniHostFilePath, dstIniPath, kudInstallerScript)
226 return reconcile.Result{}, nil
230 //Function to Get List containing baremetal hosts
231 func listBareMetalHosts() (*unstructured.UnstructuredList, error) {
233 //Get Current User and kube config file
234 usr, err := user.Current()
236 fmt.Println("Could not get current user\n")
237 return &unstructured.UnstructuredList{}, err
240 kubeConfig := filepath.Join(usr.HomeDir, ".kube", "config")
243 config, err := clientcmd.BuildConfigFromFlags("", kubeConfig)
245 fmt.Println("Could not build config\n")
246 return &unstructured.UnstructuredList{}, err
249 //Create Dynamic Client for BareMetalHost CRD
250 bmhDynamicClient, err := dynamic.NewForConfig(config)
253 fmt.Println("Could not create dynamic client for bareMetalHosts\n")
254 return &unstructured.UnstructuredList{}, err
257 //Create GVR representing a BareMetalHost CR
258 bmhGVR := schema.GroupVersionResource{
261 Resource: "baremetalhosts",
264 //Get List containing all BareMetalHosts CRs
265 bareMetalHosts, err := bmhDynamicClient.Resource(bmhGVR).List(metav1.ListOptions{})
267 fmt.Println("Error occured, cannot get BareMetalHosts list\n")
268 return &unstructured.UnstructuredList{}, err
271 return bareMetalHosts, nil
274 //Function to check if BareMetalHost containing MAC address exist
275 func checkMACaddress(bareMetalHostList *unstructured.UnstructuredList, macAddress string) (bool, string) {
277 //Convert macAddress to byte array for comparison
278 macAddressByte := []byte(macAddress)
281 for _, bareMetalHost := range bareMetalHostList.Items {
282 bmhJson, _ := bareMetalHost.MarshalJSON()
284 macBool = bytes.Contains(bmhJson, macAddressByte)
286 return macBool, bareMetalHost.GetName()
295 func getHostIPaddress(macAddress string, dhcpLeaseFilePath string ) (string, error) {
297 //Read the dhcp lease file
298 dhcpFile, err := ioutil.ReadFile(dhcpLeaseFilePath)
300 fmt.Println("Failed to read lease file\n")
304 dhcpLeases := string(dhcpFile)
306 //Regex to use to search dhcpLeases
307 regex := "lease.*{|ethernet.*"
308 re, err := regexp.Compile(regex)
310 fmt.Println("Could not create Regexp object\n")
314 //Get String containing leased Ip addresses and Corressponding MAC addresses
315 out := re.FindAllString(dhcpLeases, -1)
316 outString := strings.Join(out, " ")
317 stringReplacer := strings.NewReplacer("lease", "", "{ ethernet ", "", ";", "")
318 replaced := stringReplacer.Replace(outString)
319 ipMacList := strings.Fields(replaced)
322 //Get IP addresses corresponding to Input MAC Address
323 for idx := len(ipMacList)-1 ; idx >= 0; idx -- {
324 item := ipMacList[idx]
325 if item == macAddress {
326 ipAdd := ipMacList[idx -1]
334 func kudInstaller(srcIniPath, dstIniPath, kudInstallerPath string) {
336 err := os.Chdir(kudInstallerPath)
338 fmt.Printf("Could not change directory %v", err)
342 commands := "cp " + srcIniPath + " " + dstIniPath + "; ./installer.sh| tee kud_installer.log"
344 cmd := exec.Command("/bin/bash", "-c", commands)
348 fmt.Printf("Error Occured while running KUD install scripts %v", err)