aboutsummaryrefslogtreecommitdiffstats
path: root/src/ncm/api
diff options
context:
space:
mode:
authorEric Multanen <eric.w.multanen@intel.com>2020-03-13 17:39:40 -0700
committerRitu Sood <Ritu.Sood@intel.com>2020-03-23 22:40:28 +0000
commit529906640a6844dd371de37631e3948d328a390b (patch)
treea8e56469d3fa70726d0a4623fecae43dc987c121 /src/ncm/api
parente201896e60374698da18cf4258448a0d97617e37 (diff)
Add Network and Provider Network Intent API support
Add API for CRUD operations to manage network and provider-network intent resources. Issue-ID: MULTICLOUD-1029 Signed-off-by: Eric Multanen <eric.w.multanen@intel.com> Change-Id: If3c71691b3825db50eacdb0ea87b0d5c436ad80f
Diffstat (limited to 'src/ncm/api')
-rw-r--r--src/ncm/api/api.go39
-rw-r--r--src/ncm/api/networkhandler.go215
-rw-r--r--src/ncm/api/providernethandler.go268
3 files changed, 518 insertions, 4 deletions
diff --git a/src/ncm/api/api.go b/src/ncm/api/api.go
index c26f54e9..34b46c67 100644
--- a/src/ncm/api/api.go
+++ b/src/ncm/api/api.go
@@ -25,7 +25,7 @@ import (
var moduleClient *moduleLib.Client
-// for the given client and testClient, if the testClient is not null and
+// 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{} {
@@ -37,6 +37,20 @@ func setClient(client, testClient interface{}) interface{} {
return c
}
}
+ case *moduleLib.NetworkClient:
+ if testClient != nil && reflect.TypeOf(testClient).Implements(reflect.TypeOf((*moduleLib.NetworkManager)(nil)).Elem()) {
+ c, ok := testClient.(moduleLib.NetworkManager)
+ if ok {
+ return c
+ }
+ }
+ case *moduleLib.ProviderNetClient:
+ if testClient != nil && reflect.TypeOf(testClient).Implements(reflect.TypeOf((*moduleLib.ProviderNetManager)(nil)).Elem()) {
+ c, ok := testClient.(moduleLib.ProviderNetManager)
+ if ok {
+ return c
+ }
+ }
default:
fmt.Printf("unknown type %T\n", cl)
}
@@ -49,12 +63,11 @@ func NewRouter(testClient interface{}) *mux.Router {
moduleClient = moduleLib.NewClient()
+ router := mux.NewRouter().PathPrefix("/v2").Subrouter()
+
clusterHandler := clusterHandler{
client: setClient(moduleClient.Cluster, testClient).(moduleLib.ClusterManager),
}
-
- router := mux.NewRouter().PathPrefix("/v2").Subrouter()
-
router.HandleFunc("/cluster-providers", clusterHandler.createClusterProviderHandler).Methods("POST")
router.HandleFunc("/cluster-providers", clusterHandler.getClusterProviderHandler).Methods("GET")
router.HandleFunc("/cluster-providers/{name}", clusterHandler.getClusterProviderHandler).Methods("GET")
@@ -72,5 +85,23 @@ func NewRouter(testClient interface{}) *mux.Router {
router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/kv-pairs/{kvpair}", clusterHandler.getClusterKvPairsHandler).Methods("GET")
router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/kv-pairs/{kvpair}", clusterHandler.deleteClusterKvPairsHandler).Methods("DELETE")
+ networkHandler := networkHandler{
+ client: setClient(moduleClient.Network, testClient).(moduleLib.NetworkManager),
+ }
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/networks", networkHandler.createNetworkHandler).Methods("POST")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/networks", networkHandler.getNetworkHandler).Methods("GET")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/networks/{name}", networkHandler.putNetworkHandler).Methods("PUT")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/networks/{name}", networkHandler.getNetworkHandler).Methods("GET")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/networks/{name}", networkHandler.deleteNetworkHandler).Methods("DELETE")
+
+ providernetHandler := providernetHandler{
+ client: setClient(moduleClient.ProviderNet, testClient).(moduleLib.ProviderNetManager),
+ }
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/provider-networks", providernetHandler.createProviderNetHandler).Methods("POST")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/provider-networks", providernetHandler.getProviderNetHandler).Methods("GET")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/provider-networks/{name}", providernetHandler.putProviderNetHandler).Methods("PUT")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/provider-networks/{name}", providernetHandler.getProviderNetHandler).Methods("GET")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/provider-networks/{name}", providernetHandler.deleteProviderNetHandler).Methods("DELETE")
+
return router
}
diff --git a/src/ncm/api/networkhandler.go b/src/ncm/api/networkhandler.go
new file mode 100644
index 00000000..01d077a7
--- /dev/null
+++ b/src/ncm/api/networkhandler.go
@@ -0,0 +1,215 @@
+/*
+ * 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/ncm/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 networkHandler struct {
+ // Interface that implements Cluster operations
+ // We will set this variable with a mock interface for testing
+ client moduleLib.NetworkManager
+}
+
+// Check for valid format of input parameters
+func validateNetworkInputs(p moduleLib.Network) error {
+ // validate name
+ errs := validation.IsValidName(p.Metadata.Name)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid network name - name=[%v], errors: %v", p.Metadata.Name, errs)
+ }
+
+ // validate cni type
+ found := false
+ for _, val := range moduleLib.CNI_TYPES {
+ if p.Spec.CniType == val {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return pkgerrors.Errorf("Invalid cni type: %v", p.Spec.CniType)
+ }
+
+ subnets := p.Spec.Ipv4Subnets
+ for _, subnet := range subnets {
+ err := moduleLib.ValidateSubnet(subnet)
+ if err != nil {
+ return pkgerrors.Wrap(err, "invalid subnet")
+ }
+ }
+ return nil
+}
+
+// Create handles creation of the Network entry in the database
+func (h networkHandler) createNetworkHandler(w http.ResponseWriter, r *http.Request) {
+ var p moduleLib.Network
+ vars := mux.Vars(r)
+ clusterProvider := vars["provider-name"]
+ cluster := vars["cluster-name"]
+
+ err := json.NewDecoder(r.Body).Decode(&p)
+
+ 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 p.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateNetworkInputs(p)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateNetwork(p, clusterProvider, cluster, 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 networkHandler) putNetworkHandler(w http.ResponseWriter, r *http.Request) {
+ var p moduleLib.Network
+ vars := mux.Vars(r)
+ clusterProvider := vars["provider-name"]
+ cluster := vars["cluster-name"]
+ name := vars["name"]
+
+ err := json.NewDecoder(r.Body).Decode(&p)
+
+ 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 p.Metadata.Name == "" {
+ http.Error(w, "Missing name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ // Name in URL should match name in body
+ if p.Metadata.Name != name {
+ fmt.Printf("bodyname = %v, name= %v\n", p.Metadata.Name, name)
+ http.Error(w, "Mismatched name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateNetworkInputs(p)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateNetwork(p, clusterProvider, cluster, 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 networkHandler) getNetworkHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ clusterProvider := vars["provider-name"]
+ cluster := vars["cluster-name"]
+ name := vars["name"]
+ var ret interface{}
+ var err error
+
+ if len(name) == 0 {
+ ret, err = h.client.GetNetworks(clusterProvider, cluster)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ } else {
+ ret, err = h.client.GetNetwork(name, clusterProvider, cluster)
+ 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 networkHandler) deleteNetworkHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ clusterProvider := vars["provider-name"]
+ cluster := vars["cluster-name"]
+ name := vars["name"]
+
+ err := h.client.DeleteNetwork(name, clusterProvider, cluster)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
diff --git a/src/ncm/api/providernethandler.go b/src/ncm/api/providernethandler.go
new file mode 100644
index 00000000..b38a16c5
--- /dev/null
+++ b/src/ncm/api/providernethandler.go
@@ -0,0 +1,268 @@
+/*
+ * 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/ncm/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 providernetHandler struct {
+ // Interface that implements Cluster operations
+ // We will set this variable with a mock interface for testing
+ client moduleLib.ProviderNetManager
+}
+
+// Check for valid format of input parameters
+func validateProviderNetInputs(p moduleLib.ProviderNet) error {
+ // validate name
+ errs := validation.IsValidName(p.Metadata.Name)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid provider network name=[%v], errors: %v", p.Metadata.Name, errs)
+ }
+
+ // validate cni type
+ found := false
+ for _, val := range moduleLib.CNI_TYPES {
+ if p.Spec.CniType == val {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return pkgerrors.Errorf("Invalid cni type: %v", p.Spec.CniType)
+ }
+
+ // validate the provider network type
+ found = false
+ for _, val := range moduleLib.PROVIDER_NET_TYPES {
+ if strings.ToUpper(p.Spec.ProviderNetType) == val {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return pkgerrors.Errorf("Invalid provider network type: %v", p.Spec.ProviderNetType)
+ }
+
+ // validate the subnets
+ subnets := p.Spec.Ipv4Subnets
+ for _, subnet := range subnets {
+ err := moduleLib.ValidateSubnet(subnet)
+ if err != nil {
+ return pkgerrors.Wrap(err, "invalid subnet")
+ }
+ }
+
+ // validate the VLAN ID
+ errs = validation.IsValidNumber(p.Spec.Vlan.VlanId, 0, 4095)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid VlAN ID %v - error: %v", p.Spec.Vlan.VlanId, errs)
+ }
+
+ // validate the VLAN Node Selector value
+ expectLabels := false
+ found = false
+ for _, val := range moduleLib.VLAN_NODE_SELECTORS {
+ if strings.ToLower(p.Spec.Vlan.VlanNodeSelector) == val {
+ found = true
+ if val == moduleLib.VLAN_NODE_SPECIFIC {
+ expectLabels = true
+ }
+ break
+ }
+ }
+ if !found {
+ return pkgerrors.Errorf("Invalid VlAN Node Selector %v", p.Spec.Vlan.VlanNodeSelector)
+ }
+
+ // validate the node label list
+ gotLabels := false
+ for _, label := range p.Spec.Vlan.NodeLabelList {
+ errs = validation.IsValidLabel(label)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid Label=%v - errors: %v", label, errs)
+ }
+ gotLabels = true
+ }
+
+ // Need at least one label if node selector value was "specific"
+ // (if selector is "any" - don't care if labels were supplied or not
+ if expectLabels && !gotLabels {
+ return pkgerrors.Errorf("Node Labels required for VlAN node selector \"%v\"", moduleLib.VLAN_NODE_SPECIFIC)
+ }
+
+ return nil
+}
+
+// Create handles creation of the ProviderNet entry in the database
+func (h providernetHandler) createProviderNetHandler(w http.ResponseWriter, r *http.Request) {
+ var p moduleLib.ProviderNet
+ vars := mux.Vars(r)
+ clusterProvider := vars["provider-name"]
+ cluster := vars["cluster-name"]
+
+ err := json.NewDecoder(r.Body).Decode(&p)
+
+ 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 p.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateProviderNetInputs(p)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateProviderNet(p, clusterProvider, cluster, 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 ProviderNet entry in the database
+func (h providernetHandler) putProviderNetHandler(w http.ResponseWriter, r *http.Request) {
+ var p moduleLib.ProviderNet
+ vars := mux.Vars(r)
+ clusterProvider := vars["provider-name"]
+ cluster := vars["cluster-name"]
+ name := vars["name"]
+
+ err := json.NewDecoder(r.Body).Decode(&p)
+
+ 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 p.Metadata.Name == "" {
+ http.Error(w, "Missing name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ // Name in URL should match name in body
+ if p.Metadata.Name != name {
+ fmt.Printf("bodyname = %v, name= %v\n", p.Metadata.Name, name)
+ http.Error(w, "Mismatched name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ err = validateProviderNetInputs(p)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateProviderNet(p, clusterProvider, cluster, 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 ProviderNet Name
+// Returns a ProviderNet
+func (h providernetHandler) getProviderNetHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ clusterProvider := vars["provider-name"]
+ cluster := vars["cluster-name"]
+ name := vars["name"]
+ var ret interface{}
+ var err error
+
+ if len(name) == 0 {
+ ret, err = h.client.GetProviderNets(clusterProvider, cluster)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ } else {
+ ret, err = h.client.GetProviderNet(name, clusterProvider, cluster)
+ 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 ProviderNet Name
+func (h providernetHandler) deleteProviderNetHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ clusterProvider := vars["provider-name"]
+ cluster := vars["cluster-name"]
+ name := vars["name"]
+
+ err := h.client.DeleteProviderNet(name, clusterProvider, cluster)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}