diff options
-rw-r--r-- | src/ncm/api/api.go | 39 | ||||
-rw-r--r-- | src/ncm/api/networkhandler.go | 215 | ||||
-rw-r--r-- | src/ncm/api/providernethandler.go | 268 | ||||
-rw-r--r-- | src/ncm/pkg/module/cluster.go | 74 | ||||
-rw-r--r-- | src/ncm/pkg/module/module.go | 6 | ||||
-rw-r--r-- | src/ncm/pkg/module/module_definitions.go | 101 | ||||
-rw-r--r-- | src/ncm/pkg/module/network.go | 170 | ||||
-rw-r--r-- | src/ncm/pkg/module/providernet.go | 172 |
8 files changed, 999 insertions, 46 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) +} diff --git a/src/ncm/pkg/module/cluster.go b/src/ncm/pkg/module/cluster.go index c9ddad6e..e3260b7e 100644 --- a/src/ncm/pkg/module/cluster.go +++ b/src/ncm/pkg/module/cluster.go @@ -23,14 +23,6 @@ import ( ) // ClusterProvider contains the parameters needed for ClusterProviders -// It implements the interface for managing the ClusterProviders -type Metadata struct { - Name string `json:"name"` - Description string `json:"description"` - UserData1 string `json:"userData1"` - UserData2 string `json:"userData2"` -} - type ClusterProvider struct { Metadata Metadata `json:"metadata"` } @@ -105,18 +97,18 @@ type ClusterManager interface { // ClusterClient implements the Manager // It will also be used to maintain some localized state type ClusterClient struct { - storeName string - tagMeta string - tagContent string + db ClientDbInfo } // NewClusterClient returns an instance of the ClusterClient // which implements the Manager func NewClusterClient() *ClusterClient { return &ClusterClient{ - storeName: "cluster", - tagMeta: "clustermetadata", - tagContent: "clustercontent", + db: ClientDbInfo{ + storeName: "cluster", + tagMeta: "clustermetadata", + tagContent: "clustercontent", + }, } } @@ -134,7 +126,7 @@ func (v *ClusterClient) CreateClusterProvider(p ClusterProvider) (ClusterProvide return ClusterProvider{}, pkgerrors.New("ClusterProvider already exists") } - err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p) + err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagMeta, p) if err != nil { return ClusterProvider{}, pkgerrors.Wrap(err, "Creating DB Entry") } @@ -150,7 +142,7 @@ func (v *ClusterClient) GetClusterProvider(name string) (ClusterProvider, error) ClusterProviderName: name, } - value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return ClusterProvider{}, pkgerrors.Wrap(err, "Get ClusterProvider") } @@ -160,7 +152,7 @@ func (v *ClusterClient) GetClusterProvider(name string) (ClusterProvider, error) cp := ClusterProvider{} err = db.DBconn.Unmarshal(value[0], &cp) if err != nil { - return ClusterProvider{}, pkgerrors.Wrap(err, "Unmarshaling Value") + return ClusterProvider{}, pkgerrors.Wrap(err, "Unmarshalling Value") } return cp, nil } @@ -177,7 +169,7 @@ func (v *ClusterClient) GetClusterProviders() ([]ClusterProvider, error) { } var resp []ClusterProvider - values, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + values, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return []ClusterProvider{}, pkgerrors.Wrap(err, "Get ClusterProviders") } @@ -186,7 +178,7 @@ func (v *ClusterClient) GetClusterProviders() ([]ClusterProvider, error) { cp := ClusterProvider{} err = db.DBconn.Unmarshal(value, &cp) if err != nil { - return []ClusterProvider{}, pkgerrors.Wrap(err, "Unmarshaling Value") + return []ClusterProvider{}, pkgerrors.Wrap(err, "Unmarshalling Value") } resp = append(resp, cp) } @@ -202,7 +194,7 @@ func (v *ClusterClient) DeleteClusterProvider(name string) error { ClusterProviderName: name, } - err := db.DBconn.Remove(v.storeName, key) + err := db.DBconn.Remove(v.db.storeName, key) if err != nil { return pkgerrors.Wrap(err, "Delete ClusterProvider Entry;") } @@ -231,11 +223,11 @@ func (v *ClusterClient) CreateCluster(provider string, p Cluster, q ClusterConte return Cluster{}, pkgerrors.New("Cluster already exists") } - err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p) + err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagMeta, p) if err != nil { return Cluster{}, pkgerrors.Wrap(err, "Creating DB Entry") } - err = db.DBconn.Insert(v.storeName, key, nil, v.tagContent, q) + err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagContent, q) if err != nil { return Cluster{}, pkgerrors.Wrap(err, "Creating DB Entry") } @@ -251,7 +243,7 @@ func (v *ClusterClient) GetCluster(provider, name string) (Cluster, error) { ClusterName: name, } - value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return Cluster{}, pkgerrors.Wrap(err, "Get Cluster") } @@ -261,7 +253,7 @@ func (v *ClusterClient) GetCluster(provider, name string) (Cluster, error) { cl := Cluster{} err = db.DBconn.Unmarshal(value[0], &cl) if err != nil { - return Cluster{}, pkgerrors.Wrap(err, "Unmarshaling Value") + return Cluster{}, pkgerrors.Wrap(err, "Unmarshalling Value") } return cl, nil } @@ -277,7 +269,7 @@ func (v *ClusterClient) GetClusterContent(provider, name string) (ClusterContent ClusterName: name, } - value, err := db.DBconn.Find(v.storeName, key, v.tagContent) + value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagContent) if err != nil { return ClusterContent{}, pkgerrors.Wrap(err, "Get Cluster Content") } @@ -287,7 +279,7 @@ func (v *ClusterClient) GetClusterContent(provider, name string) (ClusterContent cc := ClusterContent{} err = db.DBconn.Unmarshal(value[0], &cc) if err != nil { - return ClusterContent{}, pkgerrors.Wrap(err, "Unmarshaling Value") + return ClusterContent{}, pkgerrors.Wrap(err, "Unmarshalling Value") } return cc, nil } @@ -303,7 +295,7 @@ func (v *ClusterClient) GetClusters(provider string) ([]Cluster, error) { ClusterName: "", } - values, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + values, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return []Cluster{}, pkgerrors.Wrap(err, "Get Clusters") } @@ -314,7 +306,7 @@ func (v *ClusterClient) GetClusters(provider string) ([]Cluster, error) { cp := Cluster{} err = db.DBconn.Unmarshal(value, &cp) if err != nil { - return []Cluster{}, pkgerrors.Wrap(err, "Unmarshaling Value") + return []Cluster{}, pkgerrors.Wrap(err, "Unmarshalling Value") } resp = append(resp, cp) } @@ -330,7 +322,7 @@ func (v *ClusterClient) DeleteCluster(provider, name string) error { ClusterName: name, } - err := db.DBconn.Remove(v.storeName, key) + err := db.DBconn.Remove(v.db.storeName, key) if err != nil { return pkgerrors.Wrap(err, "Delete Cluster Entry;") } @@ -359,7 +351,7 @@ func (v *ClusterClient) CreateClusterLabel(provider string, cluster string, p Cl return ClusterLabel{}, pkgerrors.New("Cluster Label already exists") } - err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p) + err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagMeta, p) if err != nil { return ClusterLabel{}, pkgerrors.Wrap(err, "Creating DB Entry") } @@ -376,7 +368,7 @@ func (v *ClusterClient) GetClusterLabel(provider, cluster, label string) (Cluste ClusterLabelName: label, } - value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return ClusterLabel{}, pkgerrors.Wrap(err, "Get Cluster") } @@ -386,7 +378,7 @@ func (v *ClusterClient) GetClusterLabel(provider, cluster, label string) (Cluste cl := ClusterLabel{} err = db.DBconn.Unmarshal(value[0], &cl) if err != nil { - return ClusterLabel{}, pkgerrors.Wrap(err, "Unmarshaling Value") + return ClusterLabel{}, pkgerrors.Wrap(err, "Unmarshalling Value") } return cl, nil } @@ -403,7 +395,7 @@ func (v *ClusterClient) GetClusterLabels(provider, cluster string) ([]ClusterLab ClusterLabelName: "", } - values, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + values, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return []ClusterLabel{}, pkgerrors.Wrap(err, "Get Cluster Labels") } @@ -414,7 +406,7 @@ func (v *ClusterClient) GetClusterLabels(provider, cluster string) ([]ClusterLab cp := ClusterLabel{} err = db.DBconn.Unmarshal(value, &cp) if err != nil { - return []ClusterLabel{}, pkgerrors.Wrap(err, "Unmarshaling Value") + return []ClusterLabel{}, pkgerrors.Wrap(err, "Unmarshalling Value") } resp = append(resp, cp) } @@ -431,7 +423,7 @@ func (v *ClusterClient) DeleteClusterLabel(provider, cluster, label string) erro ClusterLabelName: label, } - err := db.DBconn.Remove(v.storeName, key) + err := db.DBconn.Remove(v.db.storeName, key) if err != nil { return pkgerrors.Wrap(err, "Delete ClusterLabel Entry;") } @@ -459,7 +451,7 @@ func (v *ClusterClient) CreateClusterKvPairs(provider string, cluster string, p return ClusterKvPairs{}, pkgerrors.New("Cluster KV Pair already exists") } - err = db.DBconn.Insert(v.storeName, key, nil, v.tagMeta, p) + err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagMeta, p) if err != nil { return ClusterKvPairs{}, pkgerrors.Wrap(err, "Creating DB Entry") } @@ -476,7 +468,7 @@ func (v *ClusterClient) GetClusterKvPairs(provider, cluster, kvpair string) (Clu ClusterKvPairsName: kvpair, } - value, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return ClusterKvPairs{}, pkgerrors.Wrap(err, "Get Cluster") } @@ -486,7 +478,7 @@ func (v *ClusterClient) GetClusterKvPairs(provider, cluster, kvpair string) (Clu ckvp := ClusterKvPairs{} err = db.DBconn.Unmarshal(value[0], &ckvp) if err != nil { - return ClusterKvPairs{}, pkgerrors.Wrap(err, "Unmarshaling Value") + return ClusterKvPairs{}, pkgerrors.Wrap(err, "Unmarshalling Value") } return ckvp, nil } @@ -503,7 +495,7 @@ func (v *ClusterClient) GetAllClusterKvPairs(provider, cluster string) ([]Cluste ClusterKvPairsName: "", } - values, err := db.DBconn.Find(v.storeName, key, v.tagMeta) + values, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return []ClusterKvPairs{}, pkgerrors.Wrap(err, "Get Cluster KV Pairs") } @@ -514,7 +506,7 @@ func (v *ClusterClient) GetAllClusterKvPairs(provider, cluster string) ([]Cluste cp := ClusterKvPairs{} err = db.DBconn.Unmarshal(value, &cp) if err != nil { - return []ClusterKvPairs{}, pkgerrors.Wrap(err, "Unmarshaling Value") + return []ClusterKvPairs{}, pkgerrors.Wrap(err, "Unmarshalling Value") } resp = append(resp, cp) } @@ -531,7 +523,7 @@ func (v *ClusterClient) DeleteClusterKvPairs(provider, cluster, kvpair string) e ClusterKvPairsName: kvpair, } - err := db.DBconn.Remove(v.storeName, key) + err := db.DBconn.Remove(v.db.storeName, key) if err != nil { return pkgerrors.Wrap(err, "Delete ClusterKvPairs Entry;") } diff --git a/src/ncm/pkg/module/module.go b/src/ncm/pkg/module/module.go index c1c24510..a9950901 100644 --- a/src/ncm/pkg/module/module.go +++ b/src/ncm/pkg/module/module.go @@ -18,7 +18,9 @@ package module // Client for using the services in the orchestrator type Client struct { - Cluster *ClusterClient + Cluster *ClusterClient + Network *NetworkClient + ProviderNet *ProviderNetClient // Add Clients for API's here } @@ -26,6 +28,8 @@ type Client struct { func NewClient() *Client { c := &Client{} c.Cluster = NewClusterClient() + c.Network = NewNetworkClient() + c.ProviderNet = NewProviderNetClient() // Add Client API handlers here return c } diff --git a/src/ncm/pkg/module/module_definitions.go b/src/ncm/pkg/module/module_definitions.go new file mode 100644 index 00000000..1e839014 --- /dev/null +++ b/src/ncm/pkg/module/module_definitions.go @@ -0,0 +1,101 @@ +/* + * 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 module + +import ( + "strings" + + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/validation" + pkgerrors "github.com/pkg/errors" +) + +const VLAN_PROVIDER_NET_TYPE_VLAN string = "VLAN" +const VLAN_PROVIDER_NET_TYPE_DIRECT string = "DIRECT" + +var PROVIDER_NET_TYPES = [...]string{VLAN_PROVIDER_NET_TYPE_VLAN, VLAN_PROVIDER_NET_TYPE_DIRECT} + +const CNI_TYPE_OVN4NFV string = "ovn4nfv" + +var CNI_TYPES = [...]string{CNI_TYPE_OVN4NFV} + +// It implements the interface for managing the ClusterProviders +type Metadata struct { + Name string `json:"name"` + Description string `json:"description"` + UserData1 string `json:"userData1"` + UserData2 string `json:"userData2"` +} + +type ClientDbInfo struct { + storeName string // name of the mongodb collection to use for client documents + tagMeta string // attribute key name for the json data of a client document + tagContent string // attribute key name for the file data of a client document +} + +type Ipv4Subnet struct { + Subnet string `json:"subnet"` // CIDR notation, e.g. 172.16.33.0/24 + Name string `json:"name"` + Gateway string `json:"gateway"` // IPv4 addre, e.g. 172.16.33.1/24 + Exclude string `json:"excludeIps"` // space separated list of single IPs or ranges e.g. "172.16.33.2 172.16.33.5..172.16.33.10" +} + +const VLAN_NODE_ANY = "any" +const VLAN_NODE_SPECIFIC = "specific" + +var VLAN_NODE_SELECTORS = [...]string{VLAN_NODE_ANY, VLAN_NODE_SPECIFIC} + +type Vlan struct { + VlanId int `json:"vlanID"` + ProviderInterfaceName string `json:"providerInterfaceName"` + LogicalInterfaceName string `json:"logicalInterfaceName"` + VlanNodeSelector string `json:"vlanNodeSelector"` + NodeLabelList []string `json:"nodeLabelList"` +} + +// Check for valid format of an Ipv4Subnet +func ValidateSubnet(sub Ipv4Subnet) error { + // verify subnet is in valid cidr format + err := validation.IsIpv4Cidr(sub.Subnet) + if err != nil { + return pkgerrors.Wrap(err, "invalid subnet") + } + + // just a size check on interface name - system dependent + errs := validation.IsValidName(sub.Name) + if len(errs) > 0 { + return pkgerrors.Errorf("Invalid subnet name=[%v], errors: %v", sub.Name, errs) + } + + // verify gateway is in valid cidr format + if len(sub.Gateway) > 0 { + err = validation.IsIpv4Cidr(sub.Gateway) + if err != nil { + return pkgerrors.Wrap(err, "invalid gateway") + } + } + + // verify excludeIps is composed of space separated ipv4 addresses and + // ipv4 address ranges separated by '..' + for _, value := range strings.Fields(sub.Exclude) { + for _, ip := range strings.SplitN(value, "..", 2) { + err = validation.IsIpv4(ip) + if err != nil { + return pkgerrors.Errorf("invalid ipv4 exclude list %v", sub.Exclude) + } + } + } + return nil +} diff --git a/src/ncm/pkg/module/network.go b/src/ncm/pkg/module/network.go new file mode 100644 index 00000000..2d4121e9 --- /dev/null +++ b/src/ncm/pkg/module/network.go @@ -0,0 +1,170 @@ +/* + * 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 module + +import ( + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + + pkgerrors "github.com/pkg/errors" +) + +// Network contains the parameters needed for dynamic networks +type Network struct { + Metadata Metadata `json:"metadata"` + Spec NetworkSpec `json:"spec"` +} + +type NetworkSpec struct { + CniType string `json:"cniType"` + Ipv4Subnets []Ipv4Subnet `json:"ipv4Subnets"` +} + +// NetworkKey is the key structure that is used in the database +type NetworkKey struct { + ClusterProviderName string `json:"provider"` + ClusterName string `json:"cluster"` + NetworkName string `json:"network"` +} + +// Manager is an interface exposing the Network functionality +type NetworkManager interface { + CreateNetwork(pr Network, clusterProvider, cluster string, exists bool) (Network, error) + GetNetwork(name, clusterProvider, cluster string) (Network, error) + GetNetworks(clusterProvider, cluster string) ([]Network, error) + DeleteNetwork(name, clusterProvider, cluster string) error +} + +// NetworkClient implements the Manager +// It will also be used to maintain some localized state +type NetworkClient struct { + db ClientDbInfo +} + +// NewNetworkClient returns an instance of the NetworkClient +// which implements the Manager +func NewNetworkClient() *NetworkClient { + return &NetworkClient{ + db: ClientDbInfo{ + storeName: "cluster", + tagMeta: "networkmetadata", + }, + } +} + +// CreateNetwork - create a new Network +func (v *NetworkClient) CreateNetwork(p Network, clusterProvider, cluster string, exists bool) (Network, error) { + + //Construct key and tag to select the entry + key := NetworkKey{ + ClusterProviderName: clusterProvider, + ClusterName: cluster, + NetworkName: p.Metadata.Name, + } + + //Check if cluster exists + _, err := NewClusterClient().GetCluster(clusterProvider, cluster) + if err != nil { + return Network{}, pkgerrors.New("Unable to find the cluster") + } + + //Check if this Network already exists + _, err = v.GetNetwork(p.Metadata.Name, clusterProvider, cluster) + if err == nil && !exists { + return Network{}, pkgerrors.New("Network already exists") + } + + err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagMeta, p) + if err != nil { + return Network{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return p, nil +} + +// GetNetwork returns the Network for corresponding name +func (v *NetworkClient) GetNetwork(name, clusterProvider, cluster string) (Network, error) { + + //Construct key and tag to select the entry + key := NetworkKey{ + ClusterProviderName: clusterProvider, + ClusterName: cluster, + NetworkName: name, + } + + value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) + if err != nil { + return Network{}, pkgerrors.Wrap(err, "Get Network") + } + + //value is a byte array + if value != nil { + cp := Network{} + err = db.DBconn.Unmarshal(value[0], &cp) + if err != nil { + return Network{}, pkgerrors.Wrap(err, "Unmarshalling Value") + } + return cp, nil + } + + return Network{}, pkgerrors.New("Error getting Network") +} + +// GetNetworkList returns all of the Network for corresponding name +func (v *NetworkClient) GetNetworks(clusterProvider, cluster string) ([]Network, error) { + + //Construct key and tag to select the entry + key := NetworkKey{ + ClusterProviderName: clusterProvider, + ClusterName: cluster, + NetworkName: "", + } + + var resp []Network + values, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) + if err != nil { + return []Network{}, pkgerrors.Wrap(err, "Get Networks") + } + + for _, value := range values { + cp := Network{} + err = db.DBconn.Unmarshal(value, &cp) + if err != nil { + return []Network{}, pkgerrors.Wrap(err, "Unmarshalling Value") + } + resp = append(resp, cp) + } + + return resp, nil +} + +// Delete the Network from database +func (v *NetworkClient) DeleteNetwork(name, clusterProvider, cluster string) error { + + //Construct key and tag to select the entry + key := NetworkKey{ + ClusterProviderName: clusterProvider, + ClusterName: cluster, + NetworkName: name, + } + + err := db.DBconn.Remove(v.db.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete Network Entry;") + } + + return nil +} diff --git a/src/ncm/pkg/module/providernet.go b/src/ncm/pkg/module/providernet.go new file mode 100644 index 00000000..5e2c0343 --- /dev/null +++ b/src/ncm/pkg/module/providernet.go @@ -0,0 +1,172 @@ +/* + * 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 module + +import ( + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + + pkgerrors "github.com/pkg/errors" +) + +// ProviderNet contains the parameters needed for dynamic networks +type ProviderNet struct { + Metadata Metadata `json:"metadata"` + Spec ProviderNetSpec `json:"spec"` +} + +type ProviderNetSpec struct { + CniType string `json:"cniType"` + Ipv4Subnets []Ipv4Subnet `json:"ipv4Subnets"` + ProviderNetType string `json:"providerNetType"` + Vlan Vlan `json:"vlan"` +} + +// ProviderNetKey is the key structure that is used in the database +type ProviderNetKey struct { + ClusterProviderName string `json:"provider"` + ClusterName string `json:"cluster"` + ProviderNetName string `json:"providernet"` +} + +// Manager is an interface exposing the ProviderNet functionality +type ProviderNetManager interface { + CreateProviderNet(pr ProviderNet, clusterProvider, cluster string, exists bool) (ProviderNet, error) + GetProviderNet(name, clusterProvider, cluster string) (ProviderNet, error) + GetProviderNets(clusterProvider, cluster string) ([]ProviderNet, error) + DeleteProviderNet(name, clusterProvider, cluster string) error +} + +// ProviderNetClient implements the Manager +// It will also be used to maintain some localized state +type ProviderNetClient struct { + db ClientDbInfo +} + +// NewProviderNetClient returns an instance of the ProviderNetClient +// which implements the Manager +func NewProviderNetClient() *ProviderNetClient { + return &ProviderNetClient{ + db: ClientDbInfo{ + storeName: "cluster", + tagMeta: "networkmetadata", + }, + } +} + +// CreateProviderNet - create a new ProviderNet +func (v *ProviderNetClient) CreateProviderNet(p ProviderNet, clusterProvider, cluster string, exists bool) (ProviderNet, error) { + + //Construct key and tag to select the entry + key := ProviderNetKey{ + ClusterProviderName: clusterProvider, + ClusterName: cluster, + ProviderNetName: p.Metadata.Name, + } + + //Check if cluster exists + _, err := NewClusterClient().GetCluster(clusterProvider, cluster) + if err != nil { + return ProviderNet{}, pkgerrors.New("Unable to find the cluster") + } + + //Check if this ProviderNet already exists + _, err = v.GetProviderNet(p.Metadata.Name, clusterProvider, cluster) + if err == nil && !exists { + return ProviderNet{}, pkgerrors.New("ProviderNet already exists") + } + + err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagMeta, p) + if err != nil { + return ProviderNet{}, pkgerrors.Wrap(err, "Creating DB Entry") + } + + return p, nil +} + +// GetProviderNet returns the ProviderNet for corresponding name +func (v *ProviderNetClient) GetProviderNet(name, clusterProvider, cluster string) (ProviderNet, error) { + + //Construct key and tag to select the entry + key := ProviderNetKey{ + ClusterProviderName: clusterProvider, + ClusterName: cluster, + ProviderNetName: name, + } + + value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) + if err != nil { + return ProviderNet{}, pkgerrors.Wrap(err, "Get ProviderNet") + } + + //value is a byte array + if value != nil { + cp := ProviderNet{} + err = db.DBconn.Unmarshal(value[0], &cp) + if err != nil { + return ProviderNet{}, pkgerrors.Wrap(err, "Unmarshalling Value") + } + return cp, nil + } + + return ProviderNet{}, pkgerrors.New("Error getting ProviderNet") +} + +// GetProviderNetList returns all of the ProviderNet for corresponding name +func (v *ProviderNetClient) GetProviderNets(clusterProvider, cluster string) ([]ProviderNet, error) { + + //Construct key and tag to select the entry + key := ProviderNetKey{ + ClusterProviderName: clusterProvider, + ClusterName: cluster, + ProviderNetName: "", + } + + var resp []ProviderNet + values, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) + if err != nil { + return []ProviderNet{}, pkgerrors.Wrap(err, "Get ProviderNets") + } + + for _, value := range values { + cp := ProviderNet{} + err = db.DBconn.Unmarshal(value, &cp) + if err != nil { + return []ProviderNet{}, pkgerrors.Wrap(err, "Unmarshalling Value") + } + resp = append(resp, cp) + } + + return resp, nil +} + +// Delete the ProviderNet from database +func (v *ProviderNetClient) DeleteProviderNet(name, clusterProvider, cluster string) error { + + //Construct key and tag to select the entry + key := ProviderNetKey{ + ClusterProviderName: clusterProvider, + ClusterName: cluster, + ProviderNetName: name, + } + + err := db.DBconn.Remove(v.db.storeName, key) + if err != nil { + return pkgerrors.Wrap(err, "Delete ProviderNet Entry;") + } + + return nil +} |