diff options
Diffstat (limited to 'src/tools/emcoui/middle_end/app')
-rw-r--r-- | src/tools/emcoui/middle_end/app/app.go | 1014 | ||||
-rw-r--r-- | src/tools/emcoui/middle_end/app/compositeapp.go | 247 | ||||
-rw-r--r-- | src/tools/emcoui/middle_end/app/digp.go | 322 | ||||
-rw-r--r-- | src/tools/emcoui/middle_end/app/intents.go | 664 | ||||
-rw-r--r-- | src/tools/emcoui/middle_end/app/profile.go | 261 | ||||
-rw-r--r-- | src/tools/emcoui/middle_end/app/projects.go | 187 |
6 files changed, 2695 insertions, 0 deletions
diff --git a/src/tools/emcoui/middle_end/app/app.go b/src/tools/emcoui/middle_end/app/app.go new file mode 100644 index 00000000..a8511698 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/app.go @@ -0,0 +1,1014 @@ +/* +======================================================================= +Copyright (c) 2017-2020 Aarna Networks, Inc. +All rights reserved. +====================================================================== +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 app + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + + "github.com/gorilla/mux" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +type deployServiceData struct { + Name string `json:"name"` + Description string `json:"description"` + Spec struct { + ProjectName string `json:"projectName"` + Apps []appsData `json:"appsData"` + } `json:"spec"` +} + +type deployDigData struct { + Name string `json:"name"` + Description string `json:"description"` + CompositeAppName string `json:"compositeApp"` + CompositeProfile string `json:"compositeProfile"` + DigVersion string `json:"version"` + CompositeAppVersion string `json:"compositeAppVersion"` + Spec struct { + ProjectName string `json:"projectName"` + Apps []appsData `json:"appsData"` + } `json:"spec"` +} + +// Exists is for mongo $exists filter +type Exists struct { + Exists string `json:"$exists"` +} + +// This is the json payload that the orchesration API expexts. +type appsData struct { + Metadata struct { + Name string `json:"name"` + Description string `json:"description"` + FileName string `json:"filename"` + } `json:"metadata"` + ProfileMetadata struct { + Name string `json:"name"` + FileName string `json:"filename"` + } `json:"profileMetadata"` + Clusters []struct { + Provider string `json:"provider"` + SelectedClusters []struct { + Name string `json:"name"` + Interfaces []struct { + NetworkName string `json:"networkName"` + IP string `json:"ip"` + Subnet string `json:"subnet"` + } `json:"interfaces"` + } `json:"selectedClusters"` + } `json:"clusters"` +} + +type DigsInProject struct { + Metadata struct { + Name string `json:"name"` + CompositeAppName string `json:"compositeAppName"` + CompositeAppVersion string `json:"compositeAppVersion"` + Description string `json:"description"` + UserData1 string `userData1:"userData1"` + UserData2 string `userData2:"userData2"` + } `json:"metadata"` + Spec struct { + DigIntentsData []DigDeployedIntents `json:"deployedIntents"` + Profile string `json:"profile"` + Version string `json:"version"` + Lcloud string `json:"logicalCloud"` + OverrideValuesObj []OverrideValues `json:"overrideValues"` + GpintArray []*DigsGpint `json:"GenericPlacementIntents,omitempty"` + NwintArray []*DigsNwint `json:"networkCtlIntents,omitempty"` + } `json:"spec"` +} + +type DigsGpint struct { + Metadata apiMetaData `json:"metadata,omitempty"` + Spec struct { + AppIntentArray []PlacementIntentExport `json:"placementIntent,omitempty"` + } `json:"spec,omitempty"` +} + +type DigsNwint struct { + Metadata apiMetaData `json:"metadata,omitempty"` + Spec struct { + WorkloadIntentsArray []*WorkloadIntents `json:"WorkloadIntents,omitempty"` + } `json:"spec,omitempty"` +} +type WorkloadIntents struct { + Metadata apiMetaData `json:"metadata,omitempty"` + Spec struct { + Interfaces []NwInterface `json:"interfaces,omitempty"` + } `json:"spec,omitempty"` +} + +// Project Tree +type ProjectTree struct { + Metadata ProjectMetadata + compositeAppMap map[string]*CompositeAppTree +} + +type treeTraverseFilter struct { + compositeAppName string + compositeAppVersion string + digName string +} + +// Composite app tree +type CompositeAppTree struct { + Metadata CompositeApp + AppsDataArray map[string]*AppsData + ProfileDataArray map[string]*ProfilesData + DigMap map[string]*DigReadData +} + +type DigReadData struct { + DigpData DeploymentIGP + DigIntentsData DigpIntents + GpintMap map[string]*GpintData + NwintMap map[string]*NwintData +} + +type GpintData struct { + Gpint GenericPlacementIntent + AppIntentArray []PlacementIntent +} + +type NwintData struct { + Nwint NetworkCtlIntent + WrkintMap map[string]*WrkintData +} + +type WrkintData struct { + Wrkint NetworkWlIntent + Interfaces []NwInterface +} + +type AppsData struct { + App CompositeApp + CompositeProfile ProfileMeta +} + +type ProfilesData struct { + Profile ProfileMeta + AppProfiles []ProfileMeta +} + +type ClusterMetadata struct { + Metadata apiMetaData `json:"Metadata"` +} + +type apiMetaData struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `userData1:"userData1"` + UserData2 string `userData2:"userData2"` +} + +// The interface +type orchWorkflow interface { + createAnchor() interface{} + createObject() interface{} + getObject() (interface{}, interface{}) + getAnchor() (interface{}, interface{}) + deleteObject() interface{} + deleteAnchor() interface{} +} + +// MiddleendConfig The configmap of the middleent +type MiddleendConfig struct { + OwnPort string `json:"ownport"` + Clm string `json:"clm"` + OrchService string `json:"orchestrator"` + OvnService string `json:"ovnaction"` + Mongo string `json:"mongo"` +} + +// OrchestrationHandler interface, handling the composite app APIs +type OrchestrationHandler struct { + MiddleendConf MiddleendConfig + client http.Client + compositeAppName string + compositeAppDesc string + AppName string + meta []appsData + DigData deployDigData + file map[string]*multipart.FileHeader + dataRead *ProjectTree + treeFilter *treeTraverseFilter + DigpReturnJson []DigsInProject + projectName string + projectDesc string + version string + response struct { + payload map[string][]byte + status map[string]int + } + digpIntents map[string]string + nwCtlIntents map[string]string +} + +// NewAppHandler interface implementing REST callhandler +func NewAppHandler() *OrchestrationHandler { + return &OrchestrationHandler{} +} + +// GetHealth to check connectivity +func (h OrchestrationHandler) GetHealth(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + +} + +func (h OrchestrationHandler) apiGet(url string, statusKey string) (interface{}, []byte, error) { + // prepare and DEL API + request, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + resp, err := h.client.Do(request) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + // Prepare the response + data, _ := ioutil.ReadAll(resp.Body) + h.response.payload[statusKey] = data + h.response.status[statusKey] = resp.StatusCode + + return resp.StatusCode, data, nil +} + +func (h OrchestrationHandler) apiDel(url string, statusKey string) (interface{}, error) { + // prepare and DEL API + request, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return nil, err + } + resp, err := h.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Prepare the response + data, _ := ioutil.ReadAll(resp.Body) + h.response.payload[statusKey] = data + h.response.status[statusKey] = resp.StatusCode + + return resp.StatusCode, nil +} + +func (h OrchestrationHandler) apiPost(jsonLoad []byte, url string, statusKey string) (interface{}, error) { + // prepare and POST API + request, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonLoad)) + if err != nil { + return nil, err + } + resp, err := h.client.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Prepare the response + data, _ := ioutil.ReadAll(resp.Body) + h.response.payload[statusKey] = data + h.response.status[statusKey] = resp.StatusCode + + return resp.StatusCode, nil +} + +func (h OrchestrationHandler) apiPostMultipart(jsonLoad []byte, + fh *multipart.FileHeader, url string, statusKey string, fileName string) (interface{}, error) { + // Open the file + file, err := fh.Open() + if err != nil { + return nil, err + } + // Close the file later + defer file.Close() + // Buffer to store our request body as bytes + var requestBody bytes.Buffer + // Create a multipart writer + multiPartWriter := multipart.NewWriter(&requestBody) + // Initialize the file field. Arguments are the field name and file name + // It returns io.Writer + fileWriter, err := multiPartWriter.CreateFormFile("file", fileName) + if err != nil { + return nil, err + } + // Copy the actual file content to the field field's writer + _, err = io.Copy(fileWriter, file) + if err != nil { + return nil, err + } + // Populate other fields + fieldWriter, err := multiPartWriter.CreateFormField("metadata") + if err != nil { + return nil, err + } + + _, err = fieldWriter.Write([]byte(jsonLoad)) + if err != nil { + return nil, err + } + + // We completed adding the file and the fields, let's close the multipart writer + // So it writes the ending boundary + multiPartWriter.Close() + + // By now our original request body should have been populated, + // so let's just use it with our custom request + req, err := http.NewRequest("POST", url, &requestBody) + if err != nil { + return nil, err + } + // We need to set the content type from the writer, it includes necessary boundary as well + req.Header.Set("Content-Type", multiPartWriter.FormDataContentType()) + + // Do the request + resp, err := h.client.Do(req) + if err != nil { + log.Fatalln(err) + return nil, err + } + defer resp.Body.Close() + // Prepare the response + data, _ := ioutil.ReadAll(resp.Body) + h.response.payload[statusKey] = data + h.response.status[statusKey] = resp.StatusCode + + return resp.StatusCode, nil +} +func (h *OrchestrationHandler) prepTreeReq(vars map[string]string) { + // Initialise the project tree with target composite application. + h.treeFilter = &treeTraverseFilter{} + h.treeFilter.compositeAppName = vars["composite-app-name"] + h.treeFilter.compositeAppVersion = vars["version"] + h.treeFilter.digName = vars["deployment-intent-group-name"] +} + +func (h *OrchestrationHandler) DelDig(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + h.projectName = vars["project-name"] + h.treeFilter = nil + + dataPoints := []string{"projectHandler", "compAppHandler", + "digpHandler", + "placementIntentHandler", + "networkIntentHandler"} + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + + // Initialise the project tree with target composite application. + h.prepTreeReq(vars) + + h.dataRead = &ProjectTree{} + retcode := h.constructTree(dataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + + // 1. Call DIG delte workflow + fmt.Printf("Delete wflow start") + deleteDataPoints := []string{"networkIntentHandler", + "placementIntentHandler", + "digpHandler"} + retcode = h.deleteTree(deleteDataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + w.WriteHeader(204) +} + +// Delete service workflow +func (h *OrchestrationHandler) DelSvc(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + h.projectName = vars["project-name"] + h.treeFilter = nil + + dataPoints := []string{"projectHandler", "compAppHandler", + "digpHandler", + "ProfileHandler"} + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + + // Initialise the project tree with target composite application. + h.prepTreeReq(vars) + + h.dataRead = &ProjectTree{} + retcode := h.constructTree(dataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + fmt.Printf("tree %+v\n", h.dataRead) + // Check if a dig is present in this composite application + if len(h.dataRead.compositeAppMap[vars["composite-app-name"]].DigMap) != 0 { + w.WriteHeader(409) + w.Write([]byte("Non emtpy DIG in service\n")) + return + } + + // 1. Call delte workflow + fmt.Printf("Delete wflow start") + deleteDataPoints := []string{"ProfileHandler", + "compAppHandler"} + retcode = h.deleteTree(deleteDataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + w.WriteHeader(204) +} + +func (h *OrchestrationHandler) getData(I orchWorkflow) (interface{}, interface{}) { + _, retcode := I.getAnchor() + if retcode != 200 { + return nil, retcode + } + dataPointData, retcode := I.getObject() + if retcode != 200 { + return nil, retcode + } + return dataPointData, retcode +} + +func (h *OrchestrationHandler) deleteData(I orchWorkflow) (interface{}, interface{}) { + _ = I.deleteObject() + _ = I.deleteAnchor() + return nil, 204 //FIXME +} + +func (h *OrchestrationHandler) deleteTree(dataPoints []string) interface{} { + //1. Fetch App data + var I orchWorkflow + for _, dataPoint := range dataPoints { + switch dataPoint { + case "projectHandler": + temp := &projectHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "compAppHandler": + temp := &compAppHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "ProfileHandler": + temp := &ProfileHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "digpHandler": + temp := &digpHandler{} + temp.orchInstance = h + I = temp + fmt.Printf("delete digp\n") + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "placementIntentHandler": + temp := &placementIntentHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + case "networkIntentHandler": + temp := &networkIntentHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.deleteData(I) + if retcode != 204 { + return retcode + } + break + default: + fmt.Printf("%s\n", dataPoint) + } + } + return nil +} + +func (h *OrchestrationHandler) constructTree(dataPoints []string) interface{} { + //1. Fetch App data + var I orchWorkflow + for _, dataPoint := range dataPoints { + switch dataPoint { + case "projectHandler": + temp := &projectHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "compAppHandler": + temp := &compAppHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "ProfileHandler": + temp := &ProfileHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "digpHandler": + temp := &digpHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "placementIntentHandler": + temp := &placementIntentHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + case "networkIntentHandler": + temp := &networkIntentHandler{} + temp.orchInstance = h + I = temp + _, retcode := h.getData(I) + if retcode != 200 { + return retcode + } + break + default: + fmt.Printf("%s\n", dataPoint) + } + } + return nil +} + +// This function partest he compositeapp tree read and populates the +// Dig tree +func (h *OrchestrationHandler) copyDigTree() { + dataRead := h.dataRead + h.DigpReturnJson = nil + + for compositeAppName, value := range dataRead.compositeAppMap { + for _, digValue := range dataRead.compositeAppMap[compositeAppName].DigMap { + Dig := DigsInProject{} + SourceDigMetadata := digValue.DigpData.Metadata + + // Copy the metadata + Dig.Metadata.Name = SourceDigMetadata.Name + Dig.Metadata.CompositeAppName = compositeAppName + Dig.Metadata.CompositeAppVersion = value.Metadata.Spec.Version + Dig.Metadata.Description = SourceDigMetadata.Description + Dig.Metadata.UserData1 = SourceDigMetadata.UserData1 + Dig.Metadata.UserData2 = SourceDigMetadata.UserData2 + + // Populate the Spec of dig + SourceDigSpec := digValue.DigpData.Spec + Dig.Spec.DigIntentsData = digValue.DigIntentsData.Intent + Dig.Spec.Profile = SourceDigSpec.Profile + Dig.Spec.Version = SourceDigSpec.Version + Dig.Spec.Lcloud = SourceDigSpec.Lcloud + Dig.Spec.OverrideValuesObj = SourceDigSpec.OverrideValuesObj + + // Pupolate the generic placement intents + SourceGpintMap := digValue.GpintMap + for t, gpintValue := range SourceGpintMap { + fmt.Printf("gpName value %s\n", t) + localGpint := DigsGpint{} + localGpint.Metadata = gpintValue.Gpint.Metadata + //localGpint.Spec.AppIntentArray = gpintValue.AppIntentArray + localGpint.Spec.AppIntentArray = make([]PlacementIntentExport, len(gpintValue.AppIntentArray)) + for k, _ := range gpintValue.AppIntentArray { + localGpint.Spec.AppIntentArray[k].Metadata = gpintValue.AppIntentArray[k].Metadata + localGpint.Spec.AppIntentArray[k].Spec.AppName = + gpintValue.AppIntentArray[k].Spec.AppName + localGpint.Spec.AppIntentArray[k].Spec.Intent.AllofCluster = + make([]AllofExport, len(gpintValue.AppIntentArray[k].Spec.Intent.AllofCluster)) + for i, _ := range gpintValue.AppIntentArray[k].Spec.Intent.AllofCluster { + localGpint.Spec.AppIntentArray[k].Spec.Intent.AllofCluster[i].ProviderName = + gpintValue.AppIntentArray[k].Spec.Intent.AllofCluster[i].ProviderName + localGpint.Spec.AppIntentArray[k].Spec.Intent.AllofCluster[i].ClusterName = + gpintValue.AppIntentArray[k].Spec.Intent.AllofCluster[i].ClusterName + } + } + + Dig.Spec.GpintArray = append(Dig.Spec.GpintArray, &localGpint) + } + // Populate the Nwint intents + SourceNwintMap := digValue.NwintMap + for _, nwintValue := range SourceNwintMap { + localNwint := DigsNwint{} + localNwint.Metadata = nwintValue.Nwint.Metadata + for _, wrkintValue := range nwintValue.WrkintMap { + localWrkint := WorkloadIntents{} + localWrkint.Metadata = wrkintValue.Wrkint.Metadata + localWrkint.Spec.Interfaces = wrkintValue.Interfaces + localNwint.Spec.WorkloadIntentsArray = append(localNwint.Spec.WorkloadIntentsArray, + &localWrkint) + } + Dig.Spec.NwintArray = append(Dig.Spec.NwintArray, &localNwint) + } + h.DigpReturnJson = append(h.DigpReturnJson, Dig) + } + } +} + +// GetSvc get the entrire tree under project/<composite app>/<version> +func (h *OrchestrationHandler) GetAllDigs(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + h.version = vars["version"] + h.projectName = vars["project-name"] + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + dataPoints := []string{"projectHandler", "compAppHandler", + "digpHandler", + "placementIntentHandler", + "networkIntentHandler"} + + h.dataRead = &ProjectTree{} + h.treeFilter = nil + retcode := h.constructTree(dataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + // copy dig tree + h.copyDigTree() + retval, _ := json.Marshal(h.DigpReturnJson) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + w.Write(retval) +} + +// GetSvc get the entrire tree under project/<composite app>/<version> +func (h *OrchestrationHandler) GetSvc(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + h.treeFilter = nil + h.compositeAppName = vars["composite-app-name"] + h.version = vars["version"] + h.projectName = vars["project-name"] + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + + dataPoints := []string{"compAppHandler", "ProfileHandler", + "digpHandler", + "placementIntentHandler", + "networkIntentHandler"} + h.dataRead = &ProjectTree{} + retcode := h.constructTree(dataPoints) + if retcode != nil { + if intval, ok := retcode.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) +} + +// CreateApp exported function which creates the composite application +func (h *OrchestrationHandler) CreateDig(w http.ResponseWriter, r *http.Request) { + var jsonData deployDigData + + decoder := json.NewDecoder(r.Body) + err := decoder.Decode(&jsonData) + if err != nil { + log.Printf("Failed to parse json") + log.Fatalln(err) + } + + h.DigData = jsonData + + if len(h.DigData.Spec.Apps) == 0 { + w.WriteHeader(400) + w.Write([]byte("Bad request, no app metadata\n")) + return + } + + h.client = http.Client{} + + // These maps will get populated by the return status and respones of each V2 API + // that is called during the execution of the workflow. + h.response.payload = make(map[string][]byte) + h.response.status = make(map[string]int) + + // 4. Create DIG + h.digpIntents = make(map[string]string) + h.nwCtlIntents = make(map[string]string) + igHandler := &digpHandler{} + igHandler.orchInstance = h + igpStatus := createDInents(igHandler) + if igpStatus != nil { + if intval, ok := igpStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName+"_digp"]) + return + } + + // 3. Create intents + intentHandler := &placementIntentHandler{} + intentHandler.orchInstance = h + intentStatus := addPlacementIntent(intentHandler) + if intentStatus != nil { + if intval, ok := intentStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName+"_gpint"]) + return + } + + // If the metadata contains network interface request then call the + // network intent related part of the workflow. + if len(h.DigData.Spec.Apps[0].Clusters[0].SelectedClusters[0].Interfaces) != 0 { + nwHandler := &networkIntentHandler{} + nwHandler.orchInstance = h + nwIntentStatus := addNetworkIntent(nwHandler) + if nwIntentStatus != nil { + if intval, ok := nwIntentStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName+"_nwctlint"]) + return + } + } + + w.WriteHeader(201) + w.Write(h.response.payload[h.DigData.Name]) +} + +func (h *OrchestrationHandler) CreateApp(w http.ResponseWriter, r *http.Request) { + var jsonData deployServiceData + + err := r.ParseMultipartForm(16777216) + if err != nil { + log.Fatalln(err) + } + + // Populate the multipart.FileHeader MAP. The key will be the + // filename itself. The metadata Map will be keyed on the application + // name. The metadata has a field file name, so later we can parse the metadata + // Map, and fetch the file headers from this file Map with keys as the filename. + h.file = make(map[string]*multipart.FileHeader) + for _, v := range r.MultipartForm.File { + fh := v[0] + h.file[fh.Filename] = fh + } + + jsn := ([]byte(r.FormValue("servicePayload"))) + err = json.Unmarshal(jsn, &jsonData) + if err != nil { + log.Printf("Failed to parse json") + log.Fatalln(err) + } + + h.compositeAppName = jsonData.Name + h.compositeAppDesc = jsonData.Description + h.projectName = jsonData.Spec.ProjectName + h.meta = jsonData.Spec.Apps + + // Sanity check. For each metadata there should be a + // corresponding file in the multipart request. If it + // not found we fail this API call. + for i := range h.meta { + switch { + case h.file[h.meta[i].Metadata.FileName] == nil: + t := fmt.Sprintf("File %s not in request", h.meta[i].Metadata.FileName) + w.WriteHeader(400) + w.Write([]byte(t)) + fmt.Printf("app file not found\n") + return + case h.file[h.meta[i].ProfileMetadata.FileName] == nil: + t := fmt.Sprintf("File %s not in request", h.meta[i].ProfileMetadata.FileName) + w.WriteHeader(400) + w.Write([]byte(t)) + fmt.Printf("profile file not found\n") + return + default: + fmt.Println("Good request") + } + } + + if len(h.meta) == 0 { + w.WriteHeader(400) + w.Write([]byte("Bad request, no app metadata\n")) + return + } + + h.client = http.Client{} + + // These maps will get populated by the return status and respones of each V2 API + // that is called during the execution of the workflow. + h.response.payload = make(map[string][]byte) + h.response.status = make(map[string]int) + + // 1. create the composite application. the compAppHandler implements the + // orchWorkflow interface. + appHandler := &compAppHandler{} + appHandler.orchInstance = h + appStatus := createCompositeapp(appHandler) + if appStatus != nil { + if intval, ok := appStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName]) + return + } + + // 2. create the composite application profiles + profileHandler := &ProfileHandler{} + profileHandler.orchInstance = h + profileStatus := createProfile(profileHandler) + if profileStatus != nil { + if intval, ok := profileStatus.(int); ok { + w.WriteHeader(intval) + } else { + w.WriteHeader(500) + } + w.Write(h.response.payload[h.compositeAppName+"_profile"]) + return + } + + w.WriteHeader(201) + w.Write(h.response.payload[h.compositeAppName]) +} + +func (h *OrchestrationHandler) createCluster(filename string, fh *multipart.FileHeader, clusterName string, + jsonData ClusterMetadata) interface{} { + url := "http://" + h.MiddleendConf.Clm + "/v2/cluster-providers/" + clusterName + "/clusters" + + jsonLoad, _ := json.Marshal(jsonData) + + status, err := h.apiPostMultipart(jsonLoad, fh, url, clusterName, filename) + if err != nil { + return err + } + if status != 201 { + return status + } + fmt.Printf("cluster creation %s status %s\n", clusterName, status) + return nil +} + +func (h *OrchestrationHandler) CheckConnection(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + parse_err := r.ParseMultipartForm(16777216) + if parse_err != nil { + fmt.Printf("multipart error: %s", parse_err.Error()) + w.WriteHeader(500) + return + } + + var fh *multipart.FileHeader + for _, v := range r.MultipartForm.File { + fh = v[0] + } + file, err := fh.Open() + if err != nil { + fmt.Printf("Failed to open the file: %s", err.Error()) + w.WriteHeader(500) + return + } + defer file.Close() + + // Read the kconfig + kubeconfig, _ := ioutil.ReadAll(file) + + jsonData := ClusterMetadata{} + jsn := ([]byte(r.FormValue("metadata"))) + err = json.Unmarshal(jsn, &jsonData) + if err != nil { + fmt.Printf("Failed to parse json") + w.WriteHeader(500) + return + } + fmt.Printf("metadata %+v\n", jsonData) + + // RESTConfigFromKubeConfig is a convenience method to give back + // a restconfig from your kubeconfig bytes. + config, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + fmt.Printf("Error while reading the kubeconfig: %s", err.Error()) + w.WriteHeader(500) + return + } + + // create the clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + fmt.Printf("Failed to create clientset: %s", err.Error()) + w.WriteHeader(500) + return + } + + _, err = clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{}) + if err != nil { + fmt.Printf("Failed to establish the connection: %s", err.Error()) + w.WriteHeader(403) + w.Write([]byte("Cluster connectivity failed\n")) + return + } + + fmt.Printf("Successfully established the connection\n") + h.client = http.Client{} + h.response.status = make(map[string]int) + h.response.payload = make(map[string][]byte) + + status := h.createCluster(fh.Filename, fh, vars["cluster-provider-name"], jsonData) + if status != nil { + w.WriteHeader(500) + return + } + + w.WriteHeader(200) + w.Write(h.response.payload[vars["cluster-provider-name"]]) + return +} diff --git a/src/tools/emcoui/middle_end/app/compositeapp.go b/src/tools/emcoui/middle_end/app/compositeapp.go new file mode 100644 index 00000000..daae5b00 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/compositeapp.go @@ -0,0 +1,247 @@ +/* +======================================================================= +Copyright (c) 2017-2020 Aarna Networks, Inc. +All rights reserved. +====================================================================== +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 app + +import ( + "encoding/json" + "fmt" +) + +// CompositeApp application structure +type CompositeApp struct { + Metadata apiMetaData `json:"metadata"` + Spec compositeAppSpec `json:"spec"` +} + +type compositeAppSpec struct { + Version string `json:"version"` +} + +// compAppHandler , This implements the orchworkflow interface +type compAppHandler struct { + orchURL string + orchInstance *OrchestrationHandler +} + +// CompositeAppKey is the mongo key to fetch apps in a composite app +type CompositeAppKey struct { + Cname string `json:"compositeapp"` + Project string `json:"project"` + Cversion string `json:"compositeappversion"` + App interface{} `json:"app"` +} + +func (h *compAppHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + respcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/apps" + + respcode, respdata, err := orch.apiGet(h.orchURL, orch.compositeAppName+"_getapps") + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get app status %s\n", respcode) + compositeAppValue.AppsDataArray = make(map[string]*AppsData, len(respdata)) + var appList []CompositeApp + json.Unmarshal(respdata, &appList) + for _, value := range appList { + var appsDataInstance AppsData + appName := value.Metadata.Name + appsDataInstance.App = value + compositeAppValue.AppsDataArray[appName] = &appsDataInstance + } + } + return nil, respcode +} + +func (h *compAppHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + respcode := 200 + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + respcode, _, err := orch.apiGet(h.orchURL, orch.compositeAppName+"_getcompositeapp") + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get composite App %s\n", respcode) + //json.Unmarshal(respdata, &dataRead.CompositeApp) + } + return nil, respcode +} + +func (h *compAppHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + appList := compositeAppValue.AppsDataArray + for _, value := range appList { + url := h.orchURL + "/apps/" + value.App.Metadata.Name + fmt.Printf("Delete app %s\n", url) + resp, err := orch.apiDel(url, compositeAppMetadata.Name+"_delapp") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete app status %s\n", resp) + } + } + return nil +} + +func (h *compAppHandler) deleteAnchor() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + fmt.Printf("Delete composite app %s\n", h.orchURL) + resp, err := orch.apiDel(h.orchURL, compositeAppMetadata.Name+"_delcompapp") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete compapp status %s\n", resp) + } + return nil +} + +// CreateAnchor creates the anchor point for composite applications, +// profiles, intents etc. For example Anchor for the composite application +// will create the composite application resource in the the DB, and all apps +// will get created and uploaded under this anchor point. +func (h *compAppHandler) createAnchor() interface{} { + orch := h.orchInstance + + compAppCreate := CompositeApp{ + Metadata: apiMetaData{ + Name: orch.compositeAppName, + Description: orch.compositeAppDesc, + UserData1: "data 1", + UserData2: "data 2"}, + Spec: compositeAppSpec{ + Version: "v1"}, + } + + jsonLoad, _ := json.Marshal(compAppCreate) + tem := CompositeApp{} + json.Unmarshal(jsonLoad, &tem) + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps" + resp, err := orch.apiPost(jsonLoad, h.orchURL, orch.compositeAppName) + if err != nil { + return err + } + if resp != 201 { + return resp + } + orch.version = "v1" + fmt.Printf("compAppHandler resp %s\n", resp) + + return nil +} + +func (h *compAppHandler) createObject() interface{} { + orch := h.orchInstance + for i := range orch.meta { + fileName := orch.meta[i].Metadata.FileName + appName := orch.meta[i].Metadata.Name + appDesc := orch.meta[i].Metadata.Description + + // Upload the application helm chart + fh := orch.file[fileName] + compAppAdd := CompositeApp{ + Metadata: apiMetaData{ + Name: appName, + Description: appDesc, + UserData1: "data 1", + UserData2: "data2"}, + } + url := h.orchURL + "/" + orch.compositeAppName + "/" + orch.version + "/apps" + + jsonLoad, _ := json.Marshal(compAppAdd) + + status, err := orch.apiPostMultipart(jsonLoad, fh, url, appName, fileName) + if err != nil { + return err + } + if status != 201 { + return status + } + fmt.Printf("Composite app %s createObject status %s\n", appName, status) + } + + return nil +} + +func createCompositeapp(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + // 2. Create the Objects + err = I.createObject() + if err != nil { + return err + } + return nil +} + +func delCompositeapp(I orchWorkflow) interface{} { + // 1. Delete the object + err := I.deleteObject() + if err != nil { + return err + } + // 2. Delete the Anchor + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} diff --git a/src/tools/emcoui/middle_end/app/digp.go b/src/tools/emcoui/middle_end/app/digp.go new file mode 100644 index 00000000..b4c83e15 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/digp.go @@ -0,0 +1,322 @@ +/* +======================================================================= +Copyright (c) 2017-2020 Aarna Networks, Inc. +All rights reserved. +====================================================================== +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 app + +import ( + "encoding/json" + "fmt" + "log" +) + +type DeploymentIGP struct { + Metadata apiMetaData `json:"metadata"` + Spec DigpSpec `json:"spec"` +} + +type DigpSpec struct { + Profile string `json:"profile"` + Version string `json:"version"` + Lcloud string `json:"logical-cloud"` + OverrideValuesObj []OverrideValues `json:"override-values"` +} + +// OverrideValues ... +type OverrideValues struct { + AppName string `json:"app-name"` + ValuesObj map[string]string `json:"values"` +} + +type IgpIntents struct { + Metadata apiMetaData `json:"metadata"` + Spec AppIntents `json:"spec"` +} + +type AppIntents struct { + Intent map[string]string `json:"intent"` +} + +type DigpIntents struct { + Intent []DigDeployedIntents `json:"intent"` +} +type DigDeployedIntents struct { + GenericPlacementIntent string `json:"genericPlacementIntent"` + Ovnaction string `json:"ovnaction"` +} + +// digpHandler implements the orchworkflow interface +type digpHandler struct { + orchURL string + orchInstance *OrchestrationHandler +} + +func (h *digpHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + retcode := 200 + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + var digpList []DeploymentIGP + // This for the cases where the dig name is in the URL + if orch.treeFilter != nil && orch.treeFilter.digName != ""{ + temp:=DeploymentIGP{} + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + orch.treeFilter.digName + retcode, retval, err := orch.apiGet(h.orchURL, orch.compositeAppName+"_digp") + fmt.Printf("Get Digp in composite app %s status %d\n", compositeAppMetadata.Name, retcode) + if err != nil { + fmt.Printf("Failed to read digp") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read digp") + return nil, retcode + } + json.Unmarshal(retval, &temp) + digpList = append(digpList, temp) + } else { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups" + retcode, retval, err := orch.apiGet(h.orchURL, orch.compositeAppName+"_digp") + fmt.Printf("Get Digp in composite app %s status %d\n", compositeAppMetadata.Name, retcode) + if err != nil { + fmt.Printf("Failed to read digp") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read digp") + return nil, retcode + } + json.Unmarshal(retval, &digpList) + } + + compositeAppValue.DigMap = make(map[string]*DigReadData, len(digpList)) + for _, digpValue := range digpList { + var Dig DigReadData + Dig.DigpData = digpValue + compositeAppValue.DigMap[digpValue.Metadata.Name] = &Dig + } + } + return nil, retcode +} + +func (h *digpHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digpList := compositeAppValue.DigMap + for digName, digValue := range digpList { + url := h.orchURL + digName + "/intents" + retcode, retval, err := orch.apiGet(url, compositeAppMetadata.Name+"_digpIntents") + fmt.Printf("Get Dig int composite app %s Dig %s status %d \n", orch.compositeAppName, + digName, retcode) + if err != nil { + fmt.Printf("Failed to read digp intents") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read digp intents") + return nil, retcode + + } + err = json.Unmarshal(retval, &digValue.DigIntentsData) + if err != nil { + fmt.Printf("Failed to read intents %s\n", err) + } + } + } + return nil, 200 +} + +func (h *digpHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + digpList := compositeAppValue.DigMap + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + + for digName, _ := range digpList { + url := h.orchURL + digName + "/intents/PlacementIntent" + fmt.Printf("dlete intents %s\n", url) + resp, err := orch.apiDel(url, orch.compositeAppName+"_deldigintents") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete dig intents resp %s\n", resp) + } + } + return nil +} + +func (h *digpHandler) deleteAnchor() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + digpList := compositeAppValue.DigMap + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + + // loop through all the intents in the dig + for digName, _ := range digpList { + url := h.orchURL + digName + turl := h.orchURL + digName + "/terminate" + fmt.Printf("delete intents %s\n", url) + jsonLoad, _ := json.Marshal("{}") + resp, err := orch.apiPost(jsonLoad, turl, orch.compositeAppName+"_terminatedig") + //Not checking the status of terminate FIXME + resp, err = orch.apiDel(url, orch.compositeAppName+"_deldig") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete dig resp %s\n", resp) + } + } + return nil +} + +func (h *digpHandler) createAnchor() interface{} { + digData := h.orchInstance.DigData + orch := h.orchInstance + + digp := DeploymentIGP{ + Metadata: apiMetaData{ + Name: digData.Name, + Description: digData.Description, + UserData1: "data 1", + UserData2: "data2"}, + Spec: DigpSpec{ + Profile: digData.CompositeProfile, + Version: digData.DigVersion, + Lcloud: "unused_logical_cloud", + OverrideValuesObj: make([]OverrideValues, len(digData.Spec.Apps)), + }, + } + overrideVals := digp.Spec.OverrideValuesObj + for i, value := range digData.Spec.Apps { + overrideVals[i].ValuesObj = make(map[string]string) + overrideVals[i].AppName = value.Metadata.Name + overrideVals[i].ValuesObj["Values.global.dcaeCollectorIp"] = "1.8.0" + } + + jsonLoad, _ := json.Marshal(digp) + + // POST the generic placement intent + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + digData.Spec.ProjectName + + "/composite-apps/" + digData.CompositeAppName + "/" + digData.CompositeAppVersion + + "/deployment-intent-groups" + resp, err := orch.apiPost(jsonLoad, h.orchURL, digData.Name) + if err != nil { + return err + } + if resp != 201 { + return resp + } + orch.digpIntents["generic-placement-intent"] = digData.CompositeAppName + "_gpint" + orch.nwCtlIntents["network-controller-intent"] = digData.CompositeAppName + "_nwctlint" + fmt.Printf("Deloyment intent group resp %s\n", resp) + + return nil +} + +func (h *digpHandler) createObject() interface{} { + digData := h.orchInstance.DigData + orch := h.orchInstance + intentName := "PlacementIntent" + igp := IgpIntents{ + Metadata: apiMetaData{ + Name: intentName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + } + if len(digData.Spec.Apps[0].Clusters[0].SelectedClusters[0].Interfaces) != 0 { + igp.Spec.Intent = make(map[string]string) + igp.Spec.Intent["genericPlacementIntent"] = orch.digpIntents["generic-placement-intent"] + igp.Spec.Intent["ovnaction"] = orch.nwCtlIntents["network-controller-intent"] + } else { + igp.Spec.Intent = make(map[string]string) + igp.Spec.Intent["genericPlacementIntent"] = orch.digpIntents["generic-placement-intent"] + } + + url := h.orchURL + "/" + digData.Name + "/intents" + jsonLoad, _ := json.Marshal(igp) + status, err := orch.apiPost(jsonLoad, url, intentName) + fmt.Printf("DIG name req %s", string(jsonLoad)) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("Placement intent %s status %s %s\n", intentName, status, url) + + return nil +} + +func createDInents(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + // 2. Create the Objects + err = I.createObject() + if err != nil { + return err + } + return nil +} + +func delDigp(I orchWorkflow) interface{} { + // 1. Delete the object + err := I.deleteObject() + if err != nil { + return err + } + // 2. Delete the Anchor + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} diff --git a/src/tools/emcoui/middle_end/app/intents.go b/src/tools/emcoui/middle_end/app/intents.go new file mode 100644 index 00000000..992f7b66 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/intents.go @@ -0,0 +1,664 @@ +/* +======================================================================= +Copyright (c) 2017-2020 Aarna Networks, Inc. +All rights reserved. +====================================================================== +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 app + +import ( + "encoding/json" + "fmt" + "log" + "strconv" +) + +type GenericPlacementIntent struct { + Metadata apiMetaData `json:"metadata"` +} + +type PlacementIntent struct { + Metadata apiMetaData `json:"metadata"` + Spec AppPlacementIntentSpec `json:"spec"` +} +type PlacementIntentExport struct { + Metadata apiMetaData `json:"metadata"` + Spec AppPlacementIntentSpecExport `json:"spec"` +} + +// appPlacementIntentSpec is the spec for per app intent +type AppPlacementIntentSpec struct { + AppName string `json:"app-name"` + Intent arrayIntent `json:"intent"` +} +type arrayIntent struct { + AllofCluster []Allof `json:"allof"` +} +type Allof struct { + ProviderName string `json:"provider-name"` + ClusterName string `json:"cluster-name"` +} +type AppPlacementIntentSpecExport struct { + AppName string `json:"appName"` + Intent arrayIntentExport `json:"intent"` +} +type arrayIntentExport struct { + AllofCluster []AllofExport `json:"allof"` +} +type AllofExport struct { + ProviderName string `json:"providerName"` + ClusterName string `json:"clusterName"` +} + +// plamcentIntentHandler implements the orchworkflow interface +type placementIntentHandler struct { + orchURL string + orchInstance *OrchestrationHandler +} + +type NetworkCtlIntent struct { + Metadata apiMetaData `json:"metadata"` +} + +type NetworkWlIntent struct { + Metadata apiMetaData `json:"metadata"` + Spec WorkloadIntentSpec `json:"spec"` +} + +type WorkloadIntentSpec struct { + AppName string `json:"application-name"` + Resource string `json:"workload-resource"` + Type string `json:"type"` +} +type WorkloadIntentSpecExport struct { + AppName string `json:"applicationName"` + Resource string `json:"workloadResource"` + Type string `json:"type"` +} + +type NwInterface struct { + Metadata apiMetaData `json:"metadata"` + Spec InterfaceSpec `json:"spec"` +} + +type InterfaceSpec struct { + Interface string `json:"interface"` + Name string `json:"name"` + DefaultGateway string `json:"defaultGateway"` + IPAddress string `json:"ipAddress"` + MacAddress string `json:"macAddress"` +} + +// networkIntentHandler implements the orchworkflow interface +type networkIntentHandler struct { + ovnURL string + orchInstance *OrchestrationHandler +} + +func (h *placementIntentHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + Apps := compositeAppValue.AppsDataArray + for digName, digValue := range Dig { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + "/generic-placement-intents" + for gpintName, gpintValue := range digValue.GpintMap { + for appName, _ := range Apps { + var appPint PlacementIntent + url := h.orchURL + "/" + gpintName + "/app-intents/" + appName + "_pint" + retcode, retval, err := orch.apiGet(url, compositeAppMetadata.Name+"_getappPint") + fmt.Printf("Get Gpint App intent in Composite app %s dig %s Gpint %s status %s\n", + orch.compositeAppName, digName, gpintName, retcode) + if err != nil { + fmt.Printf("Failed to read app pint\n") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read app pint\n") + return nil, 200 + } + err = json.Unmarshal(retval, &appPint) + if err != nil { + fmt.Printf("Failed to unmarshal json %s\n", err) + return nil, 500 + } + gpintValue.AppIntentArray = append(gpintValue.AppIntentArray, appPint) + } + } + } + } + return nil, retcode +} + +func (h *placementIntentHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + var gpintList []GenericPlacementIntent + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + "/generic-placement-intents" + retcode, retval, err := orch.apiGet(h.orchURL, compositeAppMetadata.Name+"_getgpint") + fmt.Printf("Get Gpint in Composite app %s dig %s status %s\n", orch.compositeAppName, + digName, retcode) + if err != nil { + fmt.Printf("Failed to read gpint\n") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read gpint\n") + return nil, retcode + } + json.Unmarshal(retval, &gpintList) + digValue.GpintMap = make(map[string]*GpintData, len(gpintList)) + for _, value := range gpintList { + var GpintDataInstance GpintData + GpintDataInstance.Gpint = value + digValue.GpintMap[value.Metadata.Name] = &GpintDataInstance + } + } + } + return nil, retcode +} + +func (h *placementIntentHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + Apps := compositeAppValue.AppsDataArray + + // loop through all app intens in the gpint + for digName, digValue := range Dig { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + "/generic-placement-intents/" + for gpintName, _ := range digValue.GpintMap { + for appName, _ := range Apps { + url := h.orchURL + gpintName + + "/app-intents/" + appName + "_pint" // FIXME when query API works, change this API call to + // query based on app name. + fmt.Printf("Delete gping app intents %s\n", url) + resp, err := orch.apiDel(url, orch.compositeAppName+"_delgpintintents") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete gpint intents resp %s\n", resp) + } + } + } + } + return nil +} + +func (h placementIntentHandler) deleteAnchor() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + + // loop through all app intens in the gpint + for digName, digValue := range Dig { + for gpintName, _ := range digValue.GpintMap { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + "/generic-placement-intents/" + + gpintName + fmt.Printf("Delete gpint %s\n", h.orchURL) + resp, err := orch.apiDel(h.orchURL, compositeAppMetadata.Name+"_delgpints") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete gpint resp %s\n", resp) + } + } + } + return nil +} + +func (h *placementIntentHandler) createAnchor() interface{} { + orch := h.orchInstance + intentData := h.orchInstance.DigData + + gpi := GenericPlacementIntent{ + Metadata: apiMetaData{ + Name: intentData.CompositeAppName + "_gpint", + Description: "Generic placement intent created from middleend", + UserData1: "data 1", + UserData2: "data2"}, + } + + jsonLoad, _ := json.Marshal(gpi) + // POST the generic placement intent + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + intentData.Spec.ProjectName + + "/composite-apps/" + intentData.CompositeAppName + "/" + intentData.CompositeAppVersion + + "/deployment-intent-groups/" + intentData.Name + url := h.orchURL + "/generic-placement-intents" + resp, err := orch.apiPost(jsonLoad, url, orch.digpIntents["generic-placement-intent"]) + if err != nil { + return err + } + if resp != 201 { + return resp + } + fmt.Printf("Generic placement intent resp %s\n", resp) + + return nil +} + +func (h *placementIntentHandler) createObject() interface{} { + orch := h.orchInstance + intentData := h.orchInstance.DigData + + for _, value := range intentData.Spec.Apps { + appName := value.Metadata.Name + intentName := appName + "_pint" + genericAppIntentName := intentData.CompositeAppName + "_gpint" + providerName := value.Clusters[0].Provider + clusterName := value.Clusters[0].SelectedClusters[0].Name + + pint := PlacementIntent{ + Metadata: apiMetaData{ + Name: intentName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + Spec: AppPlacementIntentSpec{ + AppName: appName, + Intent: arrayIntent{ + AllofCluster: []Allof{ // FIXME: the logic requires to handle allof/anyof and multi cluster. + Allof{ + ProviderName: providerName, + ClusterName: clusterName}, + }, + }, + }, + } + + url := h.orchURL + "/generic-placement-intents/" + genericAppIntentName + "/app-intents" + jsonLoad, _ := json.Marshal(pint) + status, err := orch.apiPost(jsonLoad, url, intentName) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("Placement intent %s status %s %s\n", intentName, status, url) + } + + return nil +} + +func addPlacementIntent(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + // 2. Create the Objects + err = I.createObject() + if err != nil { + return err + } + return nil +} + +func delGpint(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.deleteObject() + if err != nil { + return err + } + // 2. Create the Objects + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} + +func (h *networkIntentHandler) createAnchor() interface{} { + orch := h.orchInstance + intentData := h.orchInstance.DigData + + nwIntent := NetworkCtlIntent{ + Metadata: apiMetaData{ + Name: intentData.CompositeAppName + "_nwctlint", + Description: "Network Controller created from middleend", + UserData1: "data 1", + UserData2: "data2"}, + } + jsonLoad, _ := json.Marshal(nwIntent) + // POST the network controller intent + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + intentData.Spec.ProjectName + + "/composite-apps/" + intentData.CompositeAppName + "/" + intentData.CompositeAppVersion + + "/deployment-intent-groups/" + intentData.Name + url := h.ovnURL + "/network-controller-intent" + resp, err := orch.apiPost(jsonLoad, url, orch.nwCtlIntents["network-controller-intent"]) + if err != nil { + return err + } + if resp != 201 { + return resp + } + fmt.Printf("Network contoller intent resp %s\n", resp) + + return nil +} + +func (h *networkIntentHandler) createObject() interface{} { + orch := h.orchInstance + intentData := h.orchInstance.DigData + + for _, value := range intentData.Spec.Apps { + + appName := value.Metadata.Name + intentName := value.Metadata.Name + "_wnwlint" + genericAppIntentName := intentData.CompositeAppName + "_nwctlint" + + wlIntent := NetworkWlIntent{ + Metadata: apiMetaData{ + Name: intentName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + Spec: WorkloadIntentSpec{ + AppName: appName, + Resource: appName, + Type: "deployment", + }, + } + + url := h.ovnURL + "/network-controller-intent/" + genericAppIntentName + "/workload-intents" + jsonLoad, _ := json.Marshal(wlIntent) + status, err := orch.apiPost(jsonLoad, url, intentName) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("Workload intent %s status %s %s\n", intentName, status, url) + } + + // Add interfaces for to each application + for _, value := range intentData.Spec.Apps { + interfaces := value.Clusters[0].SelectedClusters[0].Interfaces + for j := range interfaces { + interfaceNum := strconv.Itoa(j) + interfaceName := value.Metadata.Name + "_interface" + interfaceNum + genericAppIntentName := intentData.CompositeAppName + "_nwctlint" + workloadIntent := value.Metadata.Name + "_wnwlint" + + iface := NwInterface{ + Metadata: apiMetaData{ + Name: interfaceName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + Spec: InterfaceSpec{ + Interface: "eth" + interfaceNum, + Name: interfaces[j].NetworkName, + DefaultGateway: "false", + IPAddress: interfaces[j].IP, + }, + } + + url := h.ovnURL + "/network-controller-intent" + "/" + genericAppIntentName + + "/workload-intents/" + workloadIntent + "/interfaces" + jsonLoad, _ := json.Marshal(iface) + status, err := orch.apiPost(jsonLoad, url, interfaceName) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("interface %s status %s %s\n", interfaceName, status, url) + } + } + + return nil +} + +func (h *networkIntentHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + for nwintName, nwintValue := range digValue.NwintMap { + var wrlintList []NetworkWlIntent + wlurl := h.ovnURL + "/network-controller-intent/" + nwintName + "/workload-intents" + retcode, retval, err := orch.apiGet(wlurl, orch.compositeAppName+"_getnwwlint") + fmt.Printf("Get Wrkld intents in Composite app %s dig %s nw intent %s status %d\n", + orch.compositeAppName, digName, nwintName, retcode) + if err != nil { + fmt.Printf("Failed to read nw workload int") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read nw workload int") + return nil, retcode + } + json.Unmarshal(retval, &wrlintList) + nwintValue.WrkintMap = make(map[string]*WrkintData, len(wrlintList)) + for _, wrlIntValue := range wrlintList { + var WrkintDataInstance WrkintData + WrkintDataInstance.Wrkint = wrlIntValue + + var ifaceList []NwInterface + ifaceurl := h.ovnURL + "/network-controller-intent/" + nwintName + + "/workload-intents/" + wrlIntValue.Metadata.Name + "/interfaces" + retcode, retval, err := orch.apiGet(ifaceurl, orch.compositeAppName+"_getnwiface") + fmt.Printf("Get interface in Composite app %s dig %s nw intent %s wrkld intent %s status %d\n", + orch.compositeAppName, digName, nwintName, wrlIntValue.Metadata.Name, retcode) + if err != nil { + fmt.Printf("Failed to read nw interface") + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read nw interface") + return nil, retcode + } + json.Unmarshal(retval, &ifaceList) + WrkintDataInstance.Interfaces = ifaceList + nwintValue.WrkintMap[wrlIntValue.Metadata.Name] = &WrkintDataInstance + } + } + } + } + return nil, retcode +} + +func (h *networkIntentHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + var nwintList []NetworkCtlIntent + + url := h.ovnURL + "/network-controller-intent" + retcode, retval, err := orch.apiGet(url, orch.compositeAppName+"_getnwint") + fmt.Printf("Get Network Ctl intent in Composite app %s dig %s status %d\n", + orch.compositeAppName, digName, retcode) + if err != nil { + fmt.Printf("Failed to read nw int %s\n", err) + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read nw int") + return nil, retcode + } + json.Unmarshal(retval, &nwintList) + digValue.NwintMap = make(map[string]*NwintData, len(nwintList)) + for _, nwIntValue := range nwintList { + var NwintDataInstance NwintData + NwintDataInstance.Nwint = nwIntValue + digValue.NwintMap[nwIntValue.Metadata.Name] = &NwintDataInstance + } + } + } + return nil, retcode +} + +func (h *networkIntentHandler) deleteObject() interface{} { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + + for nwintName, nwintValue := range digValue.NwintMap { + for wrkintName, wrkintValue := range nwintValue.WrkintMap { + // Delete the interfaces per workload intent. + for _, value := range wrkintValue.Interfaces { + url := h.ovnURL + "network-controller-intent/" + nwintName + "/workload-intents/" + + wrkintName + "/interfaces/" + value.Spec.Name + fmt.Printf("Delete app nw interface %s\n", url) + retcode, err := orch.apiDel(url, orch.compositeAppName+"_delnwinterface") + if err != nil { + return err + } + if retcode != 204 { + return retcode + } + fmt.Printf("Delete nw interface resp %s\n", retcode) + } + // Delete the workload intents. + url := h.ovnURL + "network-controller-intent/" + nwintName + "/workload-intents/" + wrkintName + fmt.Printf("Delete app nw wl intent %s\n", url) + retcode, err := orch.apiDel(url, orch.compositeAppName+"_delnwwrkintent") + if err != nil { + return err + } + if retcode != 204 { + return retcode + } + fmt.Printf("Delete nw wl intent resp %s\n", retcode) + } // For workload intents in network controller intent. + } // For network controller intents in Dig. + } // For Dig. + } // For composite app. + return retcode +} + +func (h networkIntentHandler) deleteAnchor() interface{} { + orch := h.orchInstance + retcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + Dig := compositeAppValue.DigMap + for digName, digValue := range Dig { + h.ovnURL = "http://" + orch.MiddleendConf.OvnService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + + "/deployment-intent-groups/" + digName + for nwintName, _ := range digValue.NwintMap { + // loop through all app intens in the gpint + url := h.ovnURL + "/network-controller-intent/" + nwintName + fmt.Printf("Delete app nw controller intent %s\n", url) + retcode, err := orch.apiDel(url, compositeAppMetadata.Name+"_delnwctlintent") + if err != nil { + return err + } + if retcode != 204 { + return retcode + } + fmt.Printf("Delete nw controller intent %s\n", retcode) + } + } + } + return retcode +} + +func addNetworkIntent(I orchWorkflow) interface{} { + //1. Add network controller Intent + err := I.createAnchor() + if err != nil { + return err + } + + //2. Add network workload intent + err = I.createObject() + if err != nil { + return err + } + + return nil +} + +func delNwintData(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.deleteObject() + if err != nil { + return err + } + // 2. Create the Objects + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} diff --git a/src/tools/emcoui/middle_end/app/profile.go b/src/tools/emcoui/middle_end/app/profile.go new file mode 100644 index 00000000..fff43cdc --- /dev/null +++ b/src/tools/emcoui/middle_end/app/profile.go @@ -0,0 +1,261 @@ +/* +======================================================================= +Copyright (c) 2017-2020 Aarna Networks, Inc. +All rights reserved. +====================================================================== +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 app + +import ( + "encoding/json" + "fmt" + "log" +) + +// ProfileData captures per app profile +type ProfileData struct { + Name string `json:"profileName"` + AppProfiles map[string]string `json:"appProfile"` +} + +// ProfileMeta is metadta for the profile APIs +type ProfileMeta struct { + Metadata apiMetaData `json:"metadata"` + Spec ProfileSpec `json:"spec"` +} + +// ProfileSpec is the spec for the profile APIs +type ProfileSpec struct { + AppName string `json:"app-name"` +} + +// ProfileHandler This implements the orchworkflow interface +type ProfileHandler struct { + orchURL string + orchInstance *OrchestrationHandler + response struct { + payload map[string][]byte + status map[string]string + } +} + +func (h *ProfileHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + retcode := 200 + for _, compositeAppValue := range dataRead.compositeAppMap { + var profileList []ProfileMeta + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/composite-profiles" + for profileName, profileValue := range compositeAppValue.ProfileDataArray { + url := h.orchURL + "/" + profileName + "/profiles" + retcode, respval, err := orch.apiGet(url, compositeAppMetadata.Name+"_getprofiles") + fmt.Printf("Get app profiles status %d\n", retcode) + if err != nil { + fmt.Printf("Failed to read profile %s\n", profileName) + return nil, 500 + } + if retcode != 200 { + fmt.Printf("Failed to read profile %s\n", profileName) + return nil, retcode + } + json.Unmarshal(respval, &profileList) + profileValue.AppProfiles = make([]ProfileMeta, len(profileList)) + for appProfileIndex, appProfile := range profileList { + profileValue.AppProfiles[appProfileIndex] = appProfile + } + } + } + return nil, retcode +} + +func (h *ProfileHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + respcode := 200 + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + var profilemetaList []ProfileMeta + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/composite-profiles" + + respcode, respdata, err := orch.apiGet(h.orchURL, compositeAppMetadata.Name+"_getcprofile") + if err != nil { + fmt.Printf("Failed to get composite profiles\n") + return nil, 500 + } + if respcode != 200 { + fmt.Printf("composite profile GET status %d\n", respcode) + return nil, respcode + } + json.Unmarshal(respdata, &profilemetaList) + compositeAppValue.ProfileDataArray = make(map[string]*ProfilesData, len(profilemetaList)) + for _, value := range profilemetaList { + ProfilesDataInstance := ProfilesData{} + ProfilesDataInstance.Profile = value + compositeAppValue.ProfileDataArray[value.Metadata.Name] = &ProfilesDataInstance + } + } + return nil, respcode +} + +func (h *ProfileHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/composite-profiles/" + for profileName, profileValue := range compositeAppValue.ProfileDataArray { + for _, appProfileValue := range profileValue.AppProfiles { + url := h.orchURL + profileName + "/profiles/" + appProfileValue.Metadata.Name + + fmt.Printf("Delete app profiles %s\n", url) + resp, err := orch.apiDel(url, compositeAppMetadata.Name+"_delappProfiles") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete profiles status %s\n", resp) + } + } + } + return nil +} + +func (h *ProfileHandler) deleteAnchor() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + for _, compositeAppValue := range dataRead.compositeAppMap { + compositeAppMetadata := compositeAppValue.Metadata.Metadata + compositeAppSpec := compositeAppValue.Metadata.Spec + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + compositeAppMetadata.Name + + "/" + compositeAppSpec.Version + "/composite-profiles/" + + for profileName, _ := range compositeAppValue.ProfileDataArray { + url := h.orchURL + profileName + fmt.Printf("Delete profile %s\n", url) + resp, err := orch.apiDel(url, compositeAppMetadata.Name+"_delProfile") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete profile status %s\n", resp) + } + } + return nil +} + +func (h *ProfileHandler) createAnchor() interface{} { + orch := h.orchInstance + + profileCreate := ProfileMeta{ + Metadata: apiMetaData{ + Name: orch.compositeAppName + "_profile", + Description: "Profile created from middleend", + UserData1: "data 1", + UserData2: "data2"}, + } + jsonLoad, _ := json.Marshal(profileCreate) + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps" + url := h.orchURL + "/" + orch.compositeAppName + "/" + "v1" + "/composite-profiles" + resp, err := orch.apiPost(jsonLoad, url, orch.compositeAppName+"_profile") + if err != nil { + return err + } + if resp != 201 { + return resp + } + fmt.Printf("ProfileHandler resp %s\n", resp) + + return nil +} + +func (h *ProfileHandler) createObject() interface{} { + orch := h.orchInstance + + for i := range orch.meta { + fileName := orch.meta[i].ProfileMetadata.FileName + appName := orch.meta[i].Metadata.Name + profileName := orch.meta[i].Metadata.Name + "_profile" + + // Upload the application helm chart + fh := orch.file[fileName] + profileAdd := ProfileMeta{ + Metadata: apiMetaData{ + Name: profileName, + Description: "NA", + UserData1: "data 1", + UserData2: "data2"}, + Spec: ProfileSpec{ + AppName: appName}, + } + compositeProfilename := orch.compositeAppName + "_profile" + + url := h.orchURL + "/" + orch.compositeAppName + "/" + "v1" + "/" + + "composite-profiles" + "/" + compositeProfilename + "/profiles" + jsonLoad, _ := json.Marshal(profileAdd) + status, err := orch.apiPostMultipart(jsonLoad, fh, url, profileName, fileName) + if err != nil { + log.Fatalln(err) + } + if status != 201 { + return status + } + fmt.Printf("CompositeProfile Profile %s status %s %s\n", profileName, status, url) + } + + return nil +} + +func createProfile(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + // 2. Create the Objects + err = I.createObject() + if err != nil { + return err + } + return nil +} + +func delProfileData(I orchWorkflow) interface{} { + // 1. Delete the object + err := I.deleteObject() + if err != nil { + return err + } + // 2. Delete the Anchor + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} diff --git a/src/tools/emcoui/middle_end/app/projects.go b/src/tools/emcoui/middle_end/app/projects.go new file mode 100644 index 00000000..39fe7573 --- /dev/null +++ b/src/tools/emcoui/middle_end/app/projects.go @@ -0,0 +1,187 @@ +/* +======================================================================= +Copyright (c) 2017-2020 Aarna Networks, Inc. +All rights reserved. +====================================================================== +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 app + +import ( + "encoding/json" + "fmt" +) + +// CompositeApp application structure +type ProjectMetadata struct { + Metadata apiMetaData `json:"metadata"` +} + +// CompAppHandler , This implements the orchworkflow interface +type projectHandler struct { + orchURL string + orchInstance *OrchestrationHandler +} + +func (h *projectHandler) getObject() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + var cappList []CompositeApp + if orch.treeFilter != nil { + temp:=CompositeApp{} + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps/" + orch.treeFilter.compositeAppName+"/"+ + orch.treeFilter.compositeAppVersion + respcode, respdata, err := orch.apiGet(h.orchURL, orch.projectName+"_getcapps") + fmt.Printf("Get capp status %s\n", respcode) + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get capp status %s\n", respcode) + json.Unmarshal(respdata, &temp) + cappList = append(cappList, temp) + } else { + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps" + respcode, respdata, err := orch.apiGet(h.orchURL, orch.projectName+"_getcapps") + fmt.Printf("Get capp status %s\n", respcode) + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get capp status %s\n", respcode) + json.Unmarshal(respdata, &cappList) + } + + dataRead.compositeAppMap = make(map[string]*CompositeAppTree, len(cappList)) + for k, value := range cappList { + fmt.Printf("%+v", cappList[k]) + var cappsDataInstance CompositeAppTree + cappName := value.Metadata.Name + cappsDataInstance.Metadata = value + dataRead.compositeAppMap[cappName] = &cappsDataInstance + } + return nil, 200 +} + +func (h *projectHandler) getAnchor() (interface{}, interface{}) { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + + respcode, respdata, err := orch.apiGet(h.orchURL, orch.projectName+"_getProject") + if err != nil { + return nil, 500 + } + if respcode != 200 { + return nil, respcode + } + fmt.Printf("Get project %s\n", respcode) + json.Unmarshal(respdata, &dataRead.Metadata) + return nil, respcode +} + +func (h *projectHandler) deleteObject() interface{} { + orch := h.orchInstance + dataRead := h.orchInstance.dataRead + cappList := dataRead.compositeAppMap + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + + orch.projectName + "/composite-apps" + for compositeAppName, compositeAppValue := range cappList { + url := h.orchURL + "/" + compositeAppName + "/" + compositeAppValue.Metadata.Spec.Version + fmt.Printf("Delete composite app %s\n", url) + resp, err := orch.apiDel(url, compositeAppName+"_delcapp") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete composite app status %s\n", resp) + } + return nil +} + +func (h *projectHandler) deleteAnchor() interface{} { + orch := h.orchInstance + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + orch.projectName + fmt.Printf("Delete Project %s \n", h.orchURL) + resp, err := orch.apiDel(h.orchURL, orch.projectName+"_delProject") + if err != nil { + return err + } + if resp != 204 { + return resp + } + fmt.Printf("Delete Project status %s\n", resp) + return nil +} + +func (h *projectHandler) createAnchor() interface{} { + orch := h.orchInstance + + projectCreate := ProjectMetadata{ + Metadata: apiMetaData{ + Name: orch.projectName, + Description: orch.projectDesc, + UserData1: "data 1", + UserData2: "data 2"}, + } + + jsonLoad, _ := json.Marshal(projectCreate) + h.orchURL = "http://" + orch.MiddleendConf.OrchService + "/v2/projects/" + orch.projectName + resp, err := orch.apiPost(jsonLoad, h.orchURL, orch.projectName) + if err != nil { + return err + } + if resp != 201 { + return resp + } + orch.version = "v1" + fmt.Printf("projectHandler resp %s\n", resp) + + return nil +} + +func (h *projectHandler) createObject() interface{} { + return nil +} + +func createProject(I orchWorkflow) interface{} { + // 1. Create the Anchor point + err := I.createAnchor() + if err != nil { + return err + } + return nil +} + +func delProject(I orchWorkflow) interface{} { + // 1. Delete the object + err := I.deleteObject() + if err != nil { + return err + } + // 2. Delete the Anchor + err = I.deleteAnchor() + if err != nil { + return err + } + return nil +} |