summaryrefslogtreecommitdiffstats
path: root/src/tools/emcoui/middle_end/app
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/emcoui/middle_end/app')
-rw-r--r--src/tools/emcoui/middle_end/app/app.go1014
-rw-r--r--src/tools/emcoui/middle_end/app/compositeapp.go247
-rw-r--r--src/tools/emcoui/middle_end/app/digp.go322
-rw-r--r--src/tools/emcoui/middle_end/app/intents.go664
-rw-r--r--src/tools/emcoui/middle_end/app/profile.go261
-rw-r--r--src/tools/emcoui/middle_end/app/projects.go187
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
+}