summaryrefslogtreecommitdiffstats
path: root/src/ovnaction/api
diff options
context:
space:
mode:
authorEric Multanen <eric.w.multanen@intel.com>2020-05-28 17:07:20 -0700
committerEric Multanen <eric.w.multanen@intel.com>2020-06-02 14:00:07 -0700
commitad7782cbf83c11f152a6457f3808a4da99a1ae56 (patch)
treee88276d8f0d55bd58a903d1c31ab4e43e4011193 /src/ovnaction/api
parentc257a136355a794f5bf778f670c041e8958c3608 (diff)
Create OVN network action controller from ncm
Split out part of ncm microservice to act as the Onv4k8s network action controller for the orchestrator. No code changes really - just moving around to fit the architectural plan. Issue-ID: MULTICLOUD-1029 Signed-off-by: Eric Multanen <eric.w.multanen@intel.com> Change-Id: I17292ac72d041050269f05fc4a0c2a6ca741aeb5
Diffstat (limited to 'src/ovnaction/api')
-rw-r--r--src/ovnaction/api/api.go113
-rw-r--r--src/ovnaction/api/chainhandler.go290
-rw-r--r--src/ovnaction/api/chainhandler_test.go56
-rw-r--r--src/ovnaction/api/netcontrolintenthandler.go232
-rw-r--r--src/ovnaction/api/workloadifintenthandler.go251
-rw-r--r--src/ovnaction/api/workloadintenthandler.go218
6 files changed, 1160 insertions, 0 deletions
diff --git a/src/ovnaction/api/api.go b/src/ovnaction/api/api.go
new file mode 100644
index 00000000..bffab0a4
--- /dev/null
+++ b/src/ovnaction/api/api.go
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 api
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/gorilla/mux"
+ moduleLib "github.com/onap/multicloud-k8s/src/ovnaction/pkg/module"
+)
+
+var moduleClient *moduleLib.Client
+
+// For the given client and testClient, if the testClient is not null and
+// implements the client manager interface corresponding to client, then
+// return the testClient, otherwise return the client.
+func setClient(client, testClient interface{}) interface{} {
+ switch cl := client.(type) {
+ case *moduleLib.NetControlIntentClient:
+ if testClient != nil && reflect.TypeOf(testClient).Implements(reflect.TypeOf((*moduleLib.NetControlIntentManager)(nil)).Elem()) {
+ c, ok := testClient.(moduleLib.NetControlIntentManager)
+ if ok {
+ return c
+ }
+ }
+ case *moduleLib.WorkloadIntentClient:
+ if testClient != nil && reflect.TypeOf(testClient).Implements(reflect.TypeOf((*moduleLib.WorkloadIntentManager)(nil)).Elem()) {
+ c, ok := testClient.(moduleLib.WorkloadIntentManager)
+ if ok {
+ return c
+ }
+ }
+ case *moduleLib.WorkloadIfIntentClient:
+ if testClient != nil && reflect.TypeOf(testClient).Implements(reflect.TypeOf((*moduleLib.WorkloadIfIntentManager)(nil)).Elem()) {
+ c, ok := testClient.(moduleLib.WorkloadIfIntentManager)
+ if ok {
+ return c
+ }
+ }
+ case *moduleLib.ChainClient:
+ if testClient != nil && reflect.TypeOf(testClient).Implements(reflect.TypeOf((*moduleLib.ChainManager)(nil)).Elem()) {
+ c, ok := testClient.(moduleLib.ChainManager)
+ if ok {
+ return c
+ }
+ }
+ default:
+ fmt.Printf("unknown type %T\n", cl)
+ }
+ return client
+}
+
+// NewRouter creates a router that registers the various urls that are supported
+// testClient parameter allows unit testing for a given client
+func NewRouter(testClient interface{}) *mux.Router {
+
+ moduleClient = moduleLib.NewClient()
+
+ router := mux.NewRouter().PathPrefix("/v2").Subrouter()
+
+ netcontrolintentHandler := netcontrolintentHandler{
+ client: setClient(moduleClient.NetControlIntent, testClient).(moduleLib.NetControlIntentManager),
+ }
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent", netcontrolintentHandler.createHandler).Methods("POST")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent", netcontrolintentHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{name}", netcontrolintentHandler.putHandler).Methods("PUT")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{name}", netcontrolintentHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{name}", netcontrolintentHandler.deleteHandler).Methods("DELETE")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{name}/apply", netcontrolintentHandler.applyHandler).Methods("POST")
+
+ workloadintentHandler := workloadintentHandler{
+ client: setClient(moduleClient.WorkloadIntent, testClient).(moduleLib.WorkloadIntentManager),
+ }
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents", workloadintentHandler.createHandler).Methods("POST")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents", workloadintentHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents/{name}", workloadintentHandler.putHandler).Methods("PUT")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents/{name}", workloadintentHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents/{name}", workloadintentHandler.deleteHandler).Methods("DELETE")
+
+ workloadifintentHandler := workloadifintentHandler{
+ client: setClient(moduleClient.WorkloadIfIntent, testClient).(moduleLib.WorkloadIfIntentManager),
+ }
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents/{workload-intent}/interfaces", workloadifintentHandler.createHandler).Methods("POST")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents/{workload-intent}/interfaces", workloadifintentHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents/{workload-intent}/interfaces/{name}", workloadifintentHandler.putHandler).Methods("PUT")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents/{workload-intent}/interfaces/{name}", workloadifintentHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/workload-intents/{workload-intent}/interfaces/{name}", workloadifintentHandler.deleteHandler).Methods("DELETE")
+
+ chainHandler := chainHandler{
+ client: setClient(moduleClient.Chain, testClient).(moduleLib.ChainManager),
+ }
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/network-chains", chainHandler.createHandler).Methods("POST")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/network-chains", chainHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/network-chains/{name}", chainHandler.putHandler).Methods("PUT")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/network-chains/{name}", chainHandler.getHandler).Methods("GET")
+ router.HandleFunc("/projects/{project}/composite-apps/{composite-app-name}/{version}/network-controller-intent/{net-control-intent}/network-chains/{name}", chainHandler.deleteHandler).Methods("DELETE")
+
+ return router
+}
diff --git a/src/ovnaction/api/chainhandler.go b/src/ovnaction/api/chainhandler.go
new file mode 100644
index 00000000..52ed18e5
--- /dev/null
+++ b/src/ovnaction/api/chainhandler.go
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 api
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+
+ moduleLib "github.com/onap/multicloud-k8s/src/ovnaction/pkg/module"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/validation"
+ pkgerrors "github.com/pkg/errors"
+
+ "github.com/gorilla/mux"
+)
+
+// Used to store backend implementations objects
+// Also simplifies mocking for unit testing purposes
+type chainHandler struct {
+ // Interface that implements workload intent operations
+ // We will set this variable with a mock interface for testing
+ client moduleLib.ChainManager
+}
+
+func validateRoutingNetwork(r moduleLib.RoutingNetwork) error {
+ errs := validation.IsValidName(r.NetworkName)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid routing network name: %v", errs)
+ }
+
+ err := validation.IsIpv4Cidr(r.Subnet)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Invalid routing network subnet")
+ }
+
+ err = validation.IsIpv4(r.GatewayIP)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Invalid routing network gateway IP")
+ }
+
+ return nil
+}
+
+// validateNetworkChain checks that the network chain string input follows the
+// generic format: "app=app1,net1,app=app2,net2, ..... ,netN-1,app=appN"
+// assume "app=app1" can conform to validation.IsValidLabel() with an "="
+func validateNetworkChain(chain string) error {
+ elems := strings.Split(chain, ",")
+
+ // chain needs at least two apps and a network
+ if len(elems) < 3 {
+ return pkgerrors.Errorf("Network chain is too short")
+ }
+
+ // chain needs to have an odd number of elements
+ if len(elems)%2 == 0 {
+ return pkgerrors.Errorf("Invalid network chain - even number of elements")
+ }
+
+ for i, s := range elems {
+ // allows whitespace in comma separated elements
+ t := strings.TrimSpace(s)
+ // if even element, verify a=b format
+ if i%2 == 0 {
+ if strings.Index(t, "=") < 1 {
+ return pkgerrors.Errorf("Invalid deployment label element of network chain")
+ }
+ errs := validation.IsValidLabel(t)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid deployment label element: %v", errs)
+ }
+ } else {
+ errs := validation.IsValidName(t)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid network element of network chain: %v", errs)
+ }
+ }
+ }
+ return nil
+}
+
+// Check for valid format of input parameters
+func validateChainInputs(ch moduleLib.Chain) error {
+ // validate metadata
+ err := moduleLib.IsValidMetadata(ch.Metadata)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Invalid network chain metadata")
+ }
+
+ if strings.ToLower(ch.Spec.ChainType) != moduleLib.RoutingChainType {
+ return pkgerrors.Wrap(err, "Invalid network chain type")
+ }
+
+ for _, r := range ch.Spec.RoutingSpec.LeftNetwork {
+ err = validateRoutingNetwork(r)
+ if err != nil {
+ return err
+ }
+ }
+
+ for _, r := range ch.Spec.RoutingSpec.RightNetwork {
+ err = validateRoutingNetwork(r)
+ if err != nil {
+ return err
+ }
+ }
+
+ err = validateNetworkChain(ch.Spec.RoutingSpec.NetworkChain)
+ if err != nil {
+ return err
+ }
+
+ errs := validation.IsValidName(ch.Spec.RoutingSpec.Namespace)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid network chain route spec namespace: %v", errs)
+ }
+
+ return nil
+}
+
+// Create handles creation of the Chain entry in the database
+func (h chainHandler) createHandler(w http.ResponseWriter, r *http.Request) {
+ var ch moduleLib.Chain
+ vars := mux.Vars(r)
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+
+ err := json.NewDecoder(r.Body).Decode(&ch)
+
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if ch.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateChainInputs(ch)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateChain(ch, project, compositeApp, compositeAppVersion, netControlIntent, false)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Put handles creation/update of the Chain entry in the database
+func (h chainHandler) putHandler(w http.ResponseWriter, r *http.Request) {
+ var ch moduleLib.Chain
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+
+ err := json.NewDecoder(r.Body).Decode(&ch)
+
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if ch.Metadata.Name == "" {
+ http.Error(w, "Missing name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ // Name in URL should match name in body
+ if ch.Metadata.Name != name {
+ fmt.Printf("bodyname = %v, name= %v\n", ch.Metadata.Name, name)
+ http.Error(w, "Mismatched name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateChainInputs(ch)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateChain(ch, project, compositeApp, compositeAppVersion, netControlIntent, true)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Get handles GET operations on a particular Chain Name
+// Returns a Chain
+func (h chainHandler) getHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+ var ret interface{}
+ var err error
+
+ if len(name) == 0 {
+ ret, err = h.client.GetChains(project, compositeApp, compositeAppVersion, netControlIntent)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ } else {
+ ret, err = h.client.GetChain(name, project, compositeApp, compositeAppVersion, netControlIntent)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Delete handles DELETE operations on a particular Chain
+func (h chainHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+
+ err := h.client.DeleteChain(name, project, compositeApp, compositeAppVersion, netControlIntent)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
diff --git a/src/ovnaction/api/chainhandler_test.go b/src/ovnaction/api/chainhandler_test.go
new file mode 100644
index 00000000..f13a90c4
--- /dev/null
+++ b/src/ovnaction/api/chainhandler_test.go
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 api
+
+import (
+ "testing"
+)
+
+func TestIsValidNetworkChain(t *testing.T) {
+ t.Run("Valid Chains", func(t *testing.T) {
+ validchains := []string{
+ "app=abc,net1,app=xyz",
+ "app=abc, net1, app=xyz",
+ " app=abc , net1 , app=xyz ",
+ "app.kubernets.io/name=abc,net1,app.kubernets.io/name=xyz",
+ "app.kubernets.io/name=abc,net1,app.kubernets.io/name=xyz, net2, anotherlabel=wex",
+ }
+ for _, chain := range validchains {
+ err := validateNetworkChain(chain)
+ if err != nil {
+ t.Errorf("Valid network chain failed to pass: %v %v", chain, err)
+ }
+ }
+ })
+
+ t.Run("Invalid Chains", func(t *testing.T) {
+ invalidchains := []string{
+ "",
+ "app=abc,net1,app= xyz",
+ "app=abc,net1,xyz",
+ "app=abc,net1",
+ "app.kubernets.io/name=abc,net1,=xyz",
+ "abcdefg",
+ }
+ for _, chain := range invalidchains {
+ err := validateNetworkChain(chain)
+ if err == nil {
+ t.Errorf("Invalid network chain passed: %v", chain)
+ }
+ }
+ })
+}
diff --git a/src/ovnaction/api/netcontrolintenthandler.go b/src/ovnaction/api/netcontrolintenthandler.go
new file mode 100644
index 00000000..fe2109b6
--- /dev/null
+++ b/src/ovnaction/api/netcontrolintenthandler.go
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 api
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+
+ moduleLib "github.com/onap/multicloud-k8s/src/ovnaction/pkg/module"
+ pkgerrors "github.com/pkg/errors"
+
+ "github.com/gorilla/mux"
+)
+
+// Used to store backend implementations objects
+// Also simplifies mocking for unit testing purposes
+type netcontrolintentHandler struct {
+ // Interface that implements Cluster operations
+ // We will set this variable with a mock interface for testing
+ client moduleLib.NetControlIntentManager
+}
+
+// Check for valid format of input parameters
+func validateNetControlIntentInputs(nci moduleLib.NetControlIntent) error {
+ // validate metadata
+ err := moduleLib.IsValidMetadata(nci.Metadata)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Invalid network controller intent metadata")
+ }
+ return nil
+}
+
+// Create handles creation of the NetControlIntent entry in the database
+func (h netcontrolintentHandler) createHandler(w http.ResponseWriter, r *http.Request) {
+ var nci moduleLib.NetControlIntent
+ vars := mux.Vars(r)
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+
+ err := json.NewDecoder(r.Body).Decode(&nci)
+
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if nci.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateNetControlIntentInputs(nci)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateNetControlIntent(nci, project, compositeApp, compositeAppVersion, false)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Put handles creation/update of the NetControlIntent entry in the database
+func (h netcontrolintentHandler) putHandler(w http.ResponseWriter, r *http.Request) {
+ var nci moduleLib.NetControlIntent
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+
+ err := json.NewDecoder(r.Body).Decode(&nci)
+
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if nci.Metadata.Name == "" {
+ http.Error(w, "Missing name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ // Name in URL should match name in body
+ if nci.Metadata.Name != name {
+ fmt.Printf("bodyname = %v, name= %v\n", nci.Metadata.Name, name)
+ http.Error(w, "Mismatched name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateNetControlIntentInputs(nci)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateNetControlIntent(nci, project, compositeApp, compositeAppVersion, true)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Get handles GET operations on a particular NetControlIntent Name
+// Returns a NetControlIntent
+func (h netcontrolintentHandler) getHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ var ret interface{}
+ var err error
+
+ if len(name) == 0 {
+ ret, err = h.client.GetNetControlIntents(project, compositeApp, compositeAppVersion)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ } else {
+ ret, err = h.client.GetNetControlIntent(name, project, compositeApp, compositeAppVersion)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Delete handles DELETE operations on a particular NetControlIntent Name
+func (h netcontrolintentHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+
+ err := h.client.DeleteNetControlIntent(name, project, compositeApp, compositeAppVersion)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// Apply handles POST operations to Apply a particular NetControlIntent to the App Context
+// TODO: This is a test API - it can be removed once the orchestrator has been implemented to
+// invoke the appcontext update via grpc.
+func (h netcontrolintentHandler) applyHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+
+ var aci struct {
+ AppContextId string `json:"appContextId"`
+ }
+
+ err := json.NewDecoder(r.Body).Decode(&aci)
+
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ err = h.client.ApplyNetControlIntent(name, project, compositeApp, compositeAppVersion, aci.AppContextId)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
diff --git a/src/ovnaction/api/workloadifintenthandler.go b/src/ovnaction/api/workloadifintenthandler.go
new file mode 100644
index 00000000..cf8f45bf
--- /dev/null
+++ b/src/ovnaction/api/workloadifintenthandler.go
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 api
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+
+ moduleLib "github.com/onap/multicloud-k8s/src/ovnaction/pkg/module"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/validation"
+ pkgerrors "github.com/pkg/errors"
+
+ "github.com/gorilla/mux"
+)
+
+// Used to store backend implementations objects
+// Also simplifies mocking for unit testing purposes
+type workloadifintentHandler struct {
+ // Interface that implements workload intent operations
+ // We will set this variable with a mock interface for testing
+ client moduleLib.WorkloadIfIntentManager
+}
+
+// Check for valid format of input parameters
+func validateWorkloadIfIntentInputs(wif moduleLib.WorkloadIfIntent) error {
+ // validate metadata
+ err := moduleLib.IsValidMetadata(wif.Metadata)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Invalid network controller intent metadata")
+ }
+
+ errs := validation.IsValidName(wif.Spec.IfName)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid interface name = [%v], errors: %v", wif.Spec.IfName, errs)
+ }
+
+ errs = validation.IsValidName(wif.Spec.NetworkName)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid network name = [%v], errors: %v", wif.Spec.NetworkName, errs)
+ }
+
+ // optional - only validate if supplied
+ if len(wif.Spec.DefaultGateway) > 0 {
+ errs = validation.IsValidName(wif.Spec.DefaultGateway)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid default interface = [%v], errors: %v", wif.Spec.DefaultGateway, errs)
+ }
+ }
+
+ // optional - only validate if supplied
+ if len(wif.Spec.IpAddr) > 0 {
+ err = validation.IsIp(wif.Spec.IpAddr)
+ if err != nil {
+ return pkgerrors.Errorf("Invalid IP address = [%v], errors: %v", wif.Spec.IpAddr, err)
+ }
+ }
+
+ // optional - only validate if supplied
+ if len(wif.Spec.MacAddr) > 0 {
+ err = validation.IsMac(wif.Spec.MacAddr)
+ if err != nil {
+ return pkgerrors.Errorf("Invalid MAC address = [%v], errors: %v", wif.Spec.MacAddr, err)
+ }
+ }
+ return nil
+}
+
+// Create handles creation of the Network entry in the database
+func (h workloadifintentHandler) createHandler(w http.ResponseWriter, r *http.Request) {
+ var wif moduleLib.WorkloadIfIntent
+ vars := mux.Vars(r)
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+ workloadIntent := vars["workload-intent"]
+
+ err := json.NewDecoder(r.Body).Decode(&wif)
+
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if wif.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ // set default value
+ if len(wif.Spec.DefaultGateway) == 0 {
+ wif.Spec.DefaultGateway = "false" // set default value
+ }
+
+ err = validateWorkloadIfIntentInputs(wif)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateWorkloadIfIntent(wif, project, compositeApp, compositeAppVersion, netControlIntent, workloadIntent, false)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Put handles creation/update of the Network entry in the database
+func (h workloadifintentHandler) putHandler(w http.ResponseWriter, r *http.Request) {
+ var wif moduleLib.WorkloadIfIntent
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+ workloadIntent := vars["workload-intent"]
+
+ err := json.NewDecoder(r.Body).Decode(&wif)
+
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if wif.Metadata.Name == "" {
+ http.Error(w, "Missing name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ // Name in URL should match name in body
+ if wif.Metadata.Name != name {
+ fmt.Printf("bodyname = %v, name= %v\n", wif.Metadata.Name, name)
+ http.Error(w, "Mismatched name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ // set default value
+ if len(wif.Spec.DefaultGateway) == 0 {
+ wif.Spec.DefaultGateway = "false" // set default value
+ }
+
+ err = validateWorkloadIfIntentInputs(wif)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateWorkloadIfIntent(wif, project, compositeApp, compositeAppVersion, netControlIntent, workloadIntent, true)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Get handles GET operations on a particular Network Name
+// Returns a Network
+func (h workloadifintentHandler) getHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+ workloadIntent := vars["workload-intent"]
+ var ret interface{}
+ var err error
+
+ if len(name) == 0 {
+ ret, err = h.client.GetWorkloadIfIntents(project, compositeApp, compositeAppVersion, netControlIntent, workloadIntent)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ } else {
+ ret, err = h.client.GetWorkloadIfIntent(name, project, compositeApp, compositeAppVersion, netControlIntent, workloadIntent)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Delete handles DELETE operations on a particular Network Name
+func (h workloadifintentHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+ workloadIntent := vars["workload-intent"]
+
+ err := h.client.DeleteWorkloadIfIntent(name, project, compositeApp, compositeAppVersion, netControlIntent, workloadIntent)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
diff --git a/src/ovnaction/api/workloadintenthandler.go b/src/ovnaction/api/workloadintenthandler.go
new file mode 100644
index 00000000..cf7ecebc
--- /dev/null
+++ b/src/ovnaction/api/workloadintenthandler.go
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2020 Intel Corporation, Inc
+ *
+ * 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 api
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+
+ moduleLib "github.com/onap/multicloud-k8s/src/ovnaction/pkg/module"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/validation"
+ pkgerrors "github.com/pkg/errors"
+
+ "github.com/gorilla/mux"
+)
+
+// Used to store backend implementations objects
+// Also simplifies mocking for unit testing purposes
+type workloadintentHandler struct {
+ // Interface that implements workload intent operations
+ // We will set this variable with a mock interface for testing
+ client moduleLib.WorkloadIntentManager
+}
+
+// Check for valid format of input parameters
+func validateWorkloadIntentInputs(wi moduleLib.WorkloadIntent) error {
+ // validate metadata
+ err := moduleLib.IsValidMetadata(wi.Metadata)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Invalid network controller intent metadata")
+ }
+
+ errs := validation.IsValidName(wi.Spec.AppName)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid application name = [%v], errors: %v", wi.Spec.AppName, errs)
+ }
+
+ errs = validation.IsValidName(wi.Spec.WorkloadResource)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid workload resource = [%v], errors: %v", wi.Spec.WorkloadResource, errs)
+ }
+
+ errs = validation.IsValidName(wi.Spec.Type)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid workload type = [%v], errors: %v", wi.Spec.Type, errs)
+ }
+ return nil
+}
+
+// Create handles creation of the Network entry in the database
+func (h workloadintentHandler) createHandler(w http.ResponseWriter, r *http.Request) {
+ var wi moduleLib.WorkloadIntent
+ vars := mux.Vars(r)
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+
+ err := json.NewDecoder(r.Body).Decode(&wi)
+
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if wi.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateWorkloadIntentInputs(wi)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateWorkloadIntent(wi, project, compositeApp, compositeAppVersion, netControlIntent, false)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Put handles creation/update of the Network entry in the database
+func (h workloadintentHandler) putHandler(w http.ResponseWriter, r *http.Request) {
+ var wi moduleLib.WorkloadIntent
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+
+ err := json.NewDecoder(r.Body).Decode(&wi)
+
+ switch {
+ case err == io.EOF:
+ http.Error(w, "Empty body", http.StatusBadRequest)
+ return
+ case err != nil:
+ http.Error(w, err.Error(), http.StatusUnprocessableEntity)
+ return
+ }
+
+ // Name is required.
+ if wi.Metadata.Name == "" {
+ http.Error(w, "Missing name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ // Name in URL should match name in body
+ if wi.Metadata.Name != name {
+ fmt.Printf("bodyname = %v, name= %v\n", wi.Metadata.Name, name)
+ http.Error(w, "Mismatched name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateWorkloadIntentInputs(wi)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateWorkloadIntent(wi, project, compositeApp, compositeAppVersion, netControlIntent, true)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Get handles GET operations on a particular Network Name
+// Returns a Network
+func (h workloadintentHandler) getHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+ var ret interface{}
+ var err error
+
+ if len(name) == 0 {
+ ret, err = h.client.GetWorkloadIntents(project, compositeApp, compositeAppVersion, netControlIntent)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ } else {
+ ret, err = h.client.GetWorkloadIntent(name, project, compositeApp, compositeAppVersion, netControlIntent)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ err = json.NewEncoder(w).Encode(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Delete handles DELETE operations on a particular Network Name
+func (h workloadintentHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["name"]
+ project := vars["project"]
+ compositeApp := vars["composite-app-name"]
+ compositeAppVersion := vars["version"]
+ netControlIntent := vars["net-control-intent"]
+
+ err := h.client.DeleteWorkloadIntent(name, project, compositeApp, compositeAppVersion, netControlIntent)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}