summaryrefslogtreecommitdiffstats
path: root/src/ncm
diff options
context:
space:
mode:
Diffstat (limited to 'src/ncm')
-rw-r--r--src/ncm/api/api.go3
-rw-r--r--src/ncm/api/clusterhandler.go30
-rw-r--r--src/ncm/api/clusterhandler_test.go18
-rw-r--r--src/ncm/api/netcontrolintenthandler.go32
-rw-r--r--src/ncm/go.mod15
-rw-r--r--src/ncm/pkg/module/cluster.go223
-rw-r--r--src/ncm/pkg/module/module_definitions.go30
-rw-r--r--src/ncm/pkg/module/netcontrolintent.go133
-rw-r--r--src/ncm/pkg/module/network.go14
-rw-r--r--src/ncm/pkg/module/providernet.go18
-rw-r--r--src/ncm/pkg/module/resources.go273
11 files changed, 763 insertions, 26 deletions
diff --git a/src/ncm/api/api.go b/src/ncm/api/api.go
index fcef7b43..2b105716 100644
--- a/src/ncm/api/api.go
+++ b/src/ncm/api/api.go
@@ -98,6 +98,8 @@ func NewRouter(testClient interface{}) *mux.Router {
router.HandleFunc("/cluster-providers/{provider-name}/clusters", clusterHandler.getClusterHandler).Queries("label", "{label}")
router.HandleFunc("/cluster-providers/{provider-name}/clusters/{name}", clusterHandler.getClusterHandler).Methods("GET")
router.HandleFunc("/cluster-providers/{provider-name}/clusters/{name}", clusterHandler.deleteClusterHandler).Methods("DELETE")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{name}/apply", clusterHandler.applyClusterHandler).Methods("POST")
+ router.HandleFunc("/cluster-providers/{provider-name}/clusters/{name}/terminate", clusterHandler.terminateClusterHandler).Methods("POST")
router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/labels", clusterHandler.createClusterLabelHandler).Methods("POST")
router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/labels", clusterHandler.getClusterLabelHandler).Methods("GET")
router.HandleFunc("/cluster-providers/{provider-name}/clusters/{cluster-name}/labels/{label}", clusterHandler.getClusterLabelHandler).Methods("GET")
@@ -133,6 +135,7 @@ func NewRouter(testClient interface{}) *mux.Router {
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),
diff --git a/src/ncm/api/clusterhandler.go b/src/ncm/api/clusterhandler.go
index 8c50f720..78453aa8 100644
--- a/src/ncm/api/clusterhandler.go
+++ b/src/ncm/api/clusterhandler.go
@@ -324,6 +324,36 @@ func (h clusterHandler) deleteClusterHandler(w http.ResponseWriter, r *http.Requ
w.WriteHeader(http.StatusNoContent)
}
+// apply network intents associated with the cluster
+func (h clusterHandler) applyClusterHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ provider := vars["provider-name"]
+ name := vars["name"]
+
+ err := h.client.ApplyNetworkIntents(provider, name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// terminate network intents associated with the cluster
+func (h clusterHandler) terminateClusterHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ provider := vars["provider-name"]
+ name := vars["name"]
+
+ err := h.client.TerminateNetworkIntents(provider, name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
// Create handles creation of the ClusterLabel entry in the database
func (h clusterHandler) createClusterLabelHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
diff --git a/src/ncm/api/clusterhandler_test.go b/src/ncm/api/clusterhandler_test.go
index a26c41bd..b32df527 100644
--- a/src/ncm/api/clusterhandler_test.go
+++ b/src/ncm/api/clusterhandler_test.go
@@ -28,6 +28,7 @@ import (
"testing"
moduleLib "github.com/onap/multicloud-k8s/src/ncm/pkg/module"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext"
pkgerrors "github.com/pkg/errors"
)
@@ -41,6 +42,7 @@ type mockClusterManager struct {
ClusterProviderItems []moduleLib.ClusterProvider
ClusterItems []moduleLib.Cluster
ClusterContentItems []moduleLib.ClusterContent
+ ClusterContextItems []appcontext.AppContext
ClusterLabelItems []moduleLib.ClusterLabel
ClusterKvPairsItems []moduleLib.ClusterKvPairs
ClusterList []string
@@ -99,6 +101,14 @@ func (m *mockClusterManager) GetClusterContent(provider, name string) (moduleLib
return m.ClusterContentItems[0], nil
}
+func (m *mockClusterManager) GetClusterContext(provider, name string) (appcontext.AppContext, error) {
+ if m.Err != nil {
+ return appcontext.AppContext{}, m.Err
+ }
+
+ return m.ClusterContextItems[0], nil
+}
+
func (m *mockClusterManager) GetClusters(provider string) ([]moduleLib.Cluster, error) {
if m.Err != nil {
return []moduleLib.Cluster{}, m.Err
@@ -119,6 +129,14 @@ func (m *mockClusterManager) DeleteCluster(provider, name string) error {
return m.Err
}
+func (m *mockClusterManager) ApplyNetworkIntents(provider, name string) error {
+ return m.Err
+}
+
+func (m *mockClusterManager) TerminateNetworkIntents(provider, name string) error {
+ return m.Err
+}
+
func (m *mockClusterManager) CreateClusterLabel(provider, cluster string, inp moduleLib.ClusterLabel) (moduleLib.ClusterLabel, error) {
if m.Err != nil {
return moduleLib.ClusterLabel{}, m.Err
diff --git a/src/ncm/api/netcontrolintenthandler.go b/src/ncm/api/netcontrolintenthandler.go
index c59a389b..48ef1de2 100644
--- a/src/ncm/api/netcontrolintenthandler.go
+++ b/src/ncm/api/netcontrolintenthandler.go
@@ -196,3 +196,35 @@ func (h netcontrolintentHandler) deleteHandler(w http.ResponseWriter, r *http.Re
w.WriteHeader(http.StatusNoContent)
}
+
+// Apply handles POST operations to Apply a particular NetControlIntent to the App Context
+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/ncm/go.mod b/src/ncm/go.mod
index 32ff481a..8b45298f 100644
--- a/src/ncm/go.mod
+++ b/src/ncm/go.mod
@@ -1,21 +1,26 @@
module github.com/onap/multicloud-k8s/src/ncm
require (
- github.com/coreos/etcd v3.3.12+incompatible
- github.com/docker/engine v0.0.0-20190620014054-c513a4c6c298
+ github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/ghodss/yaml v1.0.0
+ github.com/google/gofuzz v1.1.0 // indirect
github.com/gorilla/handlers v1.3.0
github.com/gorilla/mux v1.6.2
- github.com/hashicorp/consul v1.4.0
+ github.com/json-iterator/go v1.1.9 // indirect
+ github.com/k8snetworkplumbingwg/network-attachment-definition-client v0.0.0-20200127152046-0ee521d56061
github.com/pkg/errors v0.8.1
github.com/sirupsen/logrus v1.4.2
go.etcd.io/etcd v3.3.12+incompatible
go.mongodb.org/mongo-driver v1.0.0
- golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
+ golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3
+ gopkg.in/yaml.v2 v2.2.8
k8s.io/api v0.0.0-20190831074750-7364b6bdad65
k8s.io/apimachinery v0.0.0-20190831074630-461753078381
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
- k8s.io/helm v2.14.3+incompatible
+ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a // indirect
+ k8s.io/kubernetes v1.14.1
+ k8s.io/utils v0.0.0-20200414100711-2df71ebbae66 // indirect
+ sigs.k8s.io/yaml v1.2.0 // indirect
)
replace (
diff --git a/src/ncm/pkg/module/cluster.go b/src/ncm/pkg/module/cluster.go
index 4ca4e7c8..2397a091 100644
--- a/src/ncm/pkg/module/cluster.go
+++ b/src/ncm/pkg/module/cluster.go
@@ -17,7 +17,10 @@
package module
import (
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext"
"github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
+ log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils"
+ "gopkg.in/yaml.v2"
pkgerrors "github.com/pkg/errors"
)
@@ -79,6 +82,10 @@ type ClusterKvPairsKey struct {
ClusterKvPairsName string `json:"kvname"`
}
+const SEPARATOR = "+"
+const CONTEXT_CLUSTER_APP = "network-intents"
+const CONTEXT_CLUSTER_RESOURCE = "network-intents"
+
// ClusterManager is an interface exposes the Cluster functionality
type ClusterManager interface {
CreateClusterProvider(pr ClusterProvider) (ClusterProvider, error)
@@ -88,9 +95,12 @@ type ClusterManager interface {
CreateCluster(provider string, pr Cluster, qr ClusterContent) (Cluster, error)
GetCluster(provider, name string) (Cluster, error)
GetClusterContent(provider, name string) (ClusterContent, error)
+ GetClusterContext(provider, name string) (appcontext.AppContext, error)
GetClusters(provider string) ([]Cluster, error)
GetClustersWithLabel(provider, label string) ([]string, error)
DeleteCluster(provider, name string) error
+ ApplyNetworkIntents(provider, name string) error
+ TerminateNetworkIntents(provider, name string) error
CreateClusterLabel(provider, cluster string, pr ClusterLabel) (ClusterLabel, error)
GetClusterLabel(provider, cluster, label string) (ClusterLabel, error)
GetClusterLabels(provider, cluster string) ([]ClusterLabel, error)
@@ -115,6 +125,7 @@ func NewClusterClient() *ClusterClient {
storeName: "cluster",
tagMeta: "clustermetadata",
tagContent: "clustercontent",
+ tagContext: "clustercontext",
},
}
}
@@ -294,6 +305,33 @@ func (v *ClusterClient) GetClusterContent(provider, name string) (ClusterContent
return ClusterContent{}, pkgerrors.New("Error getting Cluster Content")
}
+// GetClusterContext returns the AppContext for corresponding provider and name
+func (v *ClusterClient) GetClusterContext(provider, name string) (appcontext.AppContext, error) {
+ //Construct key and tag to select the entry
+ key := ClusterKey{
+ ClusterProviderName: provider,
+ ClusterName: name,
+ }
+
+ value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagContext)
+ if err != nil {
+ return appcontext.AppContext{}, pkgerrors.Wrap(err, "Get Cluster Context")
+ }
+
+ //value is a byte array
+ if value != nil {
+ ctxVal := string(value[0])
+ var cc appcontext.AppContext
+ _, err = cc.LoadAppContext(ctxVal)
+ if err != nil {
+ return appcontext.AppContext{}, pkgerrors.Wrap(err, "Reinitializing Cluster AppContext")
+ }
+ return cc, nil
+ }
+
+ return appcontext.AppContext{}, pkgerrors.New("Error getting Cluster AppContext")
+}
+
// GetClusters returns all the Clusters for corresponding provider
func (v *ClusterClient) GetClusters(provider string) ([]Cluster, error) {
//Construct key and tag to select the entry
@@ -351,8 +389,12 @@ func (v *ClusterClient) DeleteCluster(provider, name string) error {
ClusterProviderName: provider,
ClusterName: name,
}
+ _, err := v.GetClusterContext(provider, name)
+ if err == nil {
+ return pkgerrors.Errorf("Cannot delete cluster until context is deleted: %v, %v", provider, name)
+ }
- err := db.DBconn.Remove(v.db.storeName, key)
+ err = db.DBconn.Remove(v.db.storeName, key)
if err != nil {
return pkgerrors.Wrap(err, "Delete Cluster Entry;")
}
@@ -360,6 +402,185 @@ func (v *ClusterClient) DeleteCluster(provider, name string) error {
return nil
}
+// Apply Network Intents associated with a cluster
+func (v *ClusterClient) ApplyNetworkIntents(provider, name string) error {
+
+ _, err := v.GetClusterContext(provider, name)
+ if err == nil {
+ return pkgerrors.Errorf("Cluster network intents have already been applied: %v, %v", provider, name)
+ }
+
+ type resource struct {
+ name string
+ value string
+ }
+
+ var resources []resource
+
+ // Find all Network Intents for this cluster
+ networkIntents, err := NewNetworkClient().GetNetworks(provider, name)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Error finding Network Intents")
+ }
+ for _, intent := range networkIntents {
+ var crNetwork = CrNetwork{
+ ApiVersion: NETWORK_APIVERSION,
+ Kind: NETWORK_KIND,
+ }
+ crNetwork.Network = intent
+ // Produce the yaml CR document for each intent
+ y, err := yaml.Marshal(&crNetwork)
+ if err != nil {
+ log.Info("Error marshalling network intent to yaml", log.Fields{
+ "error": err,
+ "intent": intent,
+ })
+ continue
+ }
+ resources = append(resources, resource{
+ name: intent.Metadata.Name + SEPARATOR + NETWORK_KIND,
+ value: string(y),
+ })
+ }
+
+ // Find all Provider Network Intents for this cluster
+ providerNetworkIntents, err := NewProviderNetClient().GetProviderNets(provider, name)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Error finding Provider Network Intents")
+ }
+ for _, intent := range providerNetworkIntents {
+ var crProviderNet = CrProviderNet{
+ ApiVersion: PROVIDER_NETWORK_APIVERSION,
+ Kind: PROVIDER_NETWORK_KIND,
+ }
+ crProviderNet.ProviderNet = intent
+ // Produce the yaml CR document for each intent
+ y, err := yaml.Marshal(&crProviderNet)
+ if err != nil {
+ log.Info("Error marshalling provider network intent to yaml", log.Fields{
+ "error": err,
+ "intent": intent,
+ })
+ continue
+ }
+ resources = append(resources, resource{
+ name: intent.Metadata.Name + SEPARATOR + PROVIDER_NETWORK_KIND,
+ value: string(y),
+ })
+ }
+
+ if len(resources) == 0 {
+ return nil
+ }
+
+ // Make an app context for the network intent resources
+ context := appcontext.AppContext{}
+ ctxVal, err := context.InitAppContext()
+ if err != nil {
+ return pkgerrors.Wrap(err, "Error creating AppContext")
+ }
+ handle, err := context.CreateCompositeApp()
+ if err != nil {
+ return pkgerrors.Wrap(err, "Error creating AppContext CompositeApp")
+ }
+
+ // Add an app (fixed value) to the app context
+ apphandle, err := context.AddApp(handle, CONTEXT_CLUSTER_APP)
+ if err != nil {
+ cleanuperr := context.DeleteCompositeApp()
+ if cleanuperr != nil {
+ log.Warn("Error cleaning AppContext CompositeApp create failure", log.Fields{
+ "cluster-provider": provider,
+ "cluster": name,
+ })
+ }
+ return pkgerrors.Wrap(err, "Error adding App to AppContext")
+ }
+
+ // Add a cluster to the app
+ clusterhandle, err := context.AddCluster(apphandle, provider+SEPARATOR+name)
+ if err != nil {
+ cleanuperr := context.DeleteCompositeApp()
+ if cleanuperr != nil {
+ log.Warn("Error cleaning AppContext after add cluster failure", log.Fields{
+ "cluster-provider": provider,
+ "cluster": name,
+ })
+ }
+ return pkgerrors.Wrap(err, "Error adding Cluster to AppContext")
+ }
+
+ // add the resources to the app context
+ for _, resource := range resources {
+ _, err = context.AddResource(clusterhandle, resource.name, resource.value)
+ if err != nil {
+ cleanuperr := context.DeleteCompositeApp()
+ if cleanuperr != nil {
+ log.Warn("Error cleaning AppContext after add resource failure", log.Fields{
+ "cluster-provider": provider,
+ "cluster": name,
+ "resource": resource.name,
+ })
+ }
+ return pkgerrors.Wrap(err, "Error adding Resource to AppContext")
+ }
+ }
+
+ // save the context in the cluster db record
+ key := ClusterKey{
+ ClusterProviderName: provider,
+ ClusterName: name,
+ }
+ err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagContext, ctxVal)
+ if err != nil {
+ cleanuperr := context.DeleteCompositeApp()
+ if cleanuperr != nil {
+ log.Warn("Error cleaning AppContext after DB insert failure", log.Fields{
+ "cluster-provider": provider,
+ "cluster": name,
+ })
+ }
+ return pkgerrors.Wrap(err, "Error adding AppContext to DB")
+ }
+
+ // TODO: call resource synchronizer to instantiate the CRs in the cluster
+
+ return nil
+}
+
+// Terminate Network Intents associated with a cluster
+func (v *ClusterClient) TerminateNetworkIntents(provider, name string) error {
+ context, err := v.GetClusterContext(provider, name)
+ if err != nil {
+ return pkgerrors.Wrapf(err, "Error finding AppContext for cluster: %v, %v", provider, name)
+ }
+
+ // TODO: call resource synchronizer to terminate the CRs in the cluster
+
+ // remove the app context
+ cleanuperr := context.DeleteCompositeApp()
+ if cleanuperr != nil {
+ log.Warn("Error deleted AppContext", log.Fields{
+ "cluster-provider": provider,
+ "cluster": name,
+ })
+ }
+
+ // remove the app context field from the cluster db record
+ key := ClusterKey{
+ ClusterProviderName: provider,
+ ClusterName: name,
+ }
+ err = db.DBconn.RemoveTag(v.db.storeName, key, v.db.tagContext)
+ if err != nil {
+ log.Warn("Error removing AppContext from Cluster document", log.Fields{
+ "cluster-provider": provider,
+ "cluster": name,
+ })
+ }
+ return nil
+}
+
// CreateClusterLabel - create a new Cluster Label mongo document for a cluster-provider/cluster
func (v *ClusterClient) CreateClusterLabel(provider string, cluster string, p ClusterLabel) (ClusterLabel, error) {
//Construct key and tag to select the entry
diff --git a/src/ncm/pkg/module/module_definitions.go b/src/ncm/pkg/module/module_definitions.go
index 729a9dbd..36c865a5 100644
--- a/src/ncm/pkg/module/module_definitions.go
+++ b/src/ncm/pkg/module/module_definitions.go
@@ -31,28 +31,32 @@ const CNI_TYPE_OVN4NFV string = "ovn4nfv"
var CNI_TYPES = [...]string{CNI_TYPE_OVN4NFV}
+const YAML_START = "---\n"
+const YAML_END = "...\n"
+
// It implements the interface for managing the ClusterProviders
const MAX_DESCRIPTION_LEN int = 1024
const MAX_USERDATA_LEN int = 4096
type Metadata struct {
- Name string `json:"name"`
- Description string `json:"description"`
- UserData1 string `json:"userData1"`
- UserData2 string `json:"userData2"`
+ Name string `json:"name" yaml:"name"`
+ Description string `json:"description" yaml:"-"`
+ UserData1 string `json:"userData1" yaml:"-"`
+ UserData2 string `json:"userData2" yaml:"-"`
}
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
+ tagContext string // attribute key name for context object in App Context
}
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"
+ Subnet string `json:"subnet" yaml:"subnet"` // CIDR notation, e.g. 172.16.33.0/24
+ Name string `json:"name" yaml:"name"`
+ Gateway string `json:"gateway" yaml:"gateway"` // IPv4 addre, e.g. 172.16.33.1/24
+ Exclude string `json:"excludeIps" yaml:"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"
@@ -61,11 +65,11 @@ 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"`
+ VlanId int `json:"vlanID" yaml:"vlanId"`
+ ProviderInterfaceName string `json:"providerInterfaceName" yaml:"providerInterfaceName"`
+ LogicalInterfaceName string `json:"logicalInterfaceName" yaml:"logicalInterfaceName"`
+ VlanNodeSelector string `json:"vlanNodeSelector" yaml:"vlanNodeSelector"`
+ NodeLabelList []string `json:"nodeLabelList" yaml:"nodeLabelList"`
}
// Check for valid format Metadata
diff --git a/src/ncm/pkg/module/netcontrolintent.go b/src/ncm/pkg/module/netcontrolintent.go
index 5ef9dffe..c005a935 100644
--- a/src/ncm/pkg/module/netcontrolintent.go
+++ b/src/ncm/pkg/module/netcontrolintent.go
@@ -17,8 +17,17 @@
package module
import (
- "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
+ "encoding/json"
+ "strings"
+
+ jyaml "github.com/ghodss/yaml"
+ nettypes "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
+ log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/kubernetes/scheme"
pkgerrors "github.com/pkg/errors"
)
@@ -42,6 +51,7 @@ type NetControlIntentManager interface {
GetNetControlIntent(name, project, compositeapp, compositeappversion string) (NetControlIntent, error)
GetNetControlIntents(project, compositeapp, compositeappversion string) ([]NetControlIntent, error)
DeleteNetControlIntent(name, project, compositeapp, compositeappversion string) error
+ ApplyNetControlIntent(name, project, compositeapp, compositeappversion, appContextId string) error
}
// NetControlIntentClient implements the Manager
@@ -162,3 +172,124 @@ func (v *NetControlIntentClient) DeleteNetControlIntent(name, project, composite
return nil
}
+
+// (Test Routine) - Apply network-control-intent
+func (v *NetControlIntentClient) ApplyNetControlIntent(name, project, compositeapp, compositeappversion, appContextId string) error {
+ // TODO: Handle all Network Chain Intents for the Network Control Intent
+
+ // Handle all Workload Intents for the Network Control Intent
+ wis, err := NewWorkloadIntentClient().GetWorkloadIntents(project, compositeapp, compositeappversion, name)
+ if err != nil {
+ return pkgerrors.Wrapf(err, "Error getting Workload Intents for Network Control Intent %v for %v/%v%v not found", name, project, compositeapp, compositeappversion)
+ }
+
+ // Setup the AppContext
+ var context appcontext.AppContext
+ _, err = context.LoadAppContext(appContextId)
+ if err != nil {
+ return pkgerrors.Wrapf(err, "Error getting AppContext with Id: %v for %v/%v%v",
+ appContextId, project, compositeapp, compositeappversion)
+ }
+
+ // Handle all intents (currently just Interface intents) for each Workload Intent
+ for _, wi := range wis {
+ // The app/resource identified in the workload intent needs to be updated with two annotations.
+ // 1 - The "k8s.v1.cni.cncf.io/networks" annotation will have {"name": "ovn-networkobj", "namespace": "default"} added
+ // to it (preserving any existing values for this annotation.
+ // 2 - The "k8s.plugin.opnfv.org/nfn-network" annotation will add any network interfaces that are provided by the
+ // workload/interfaces intents.
+
+ // Prepare the list of interfaces from the workload intent
+ wifs, err := NewWorkloadIfIntentClient().GetWorkloadIfIntents(project,
+ compositeapp,
+ compositeappversion,
+ name,
+ wi.Metadata.Name)
+ if err != nil {
+ return pkgerrors.Wrapf(err,
+ "Error getting Workload Interface Intents for Workload Intent %v under Network Control Intent %v for %v/%v%v not found",
+ wi.Metadata.Name, name, project, compositeapp, compositeappversion)
+ }
+ if len(wifs) == 0 {
+ log.Warn("No interface intents provided for workload intent", log.Fields{
+ "project": project,
+ "composite app": compositeapp,
+ "composite app version": compositeappversion,
+ "network control intent": name,
+ "workload intent": wi.Metadata.Name,
+ })
+ continue
+ }
+
+ // Get all clusters for the current App from the AppContext
+ clusters, err := context.GetClusterNames(wi.Spec.AppName)
+ for _, c := range clusters {
+ rh, err := context.GetResourceHandle(wi.Spec.AppName, c,
+ strings.Join([]string{wi.Spec.WorkloadResource, wi.Spec.Type}, "+"))
+ if err != nil {
+ log.Warn("App Context resource handle not found", log.Fields{
+ "project": project,
+ "composite app": compositeapp,
+ "composite app version": compositeappversion,
+ "network control intent": name,
+ "workload name": wi.Metadata.Name,
+ "app": wi.Spec.AppName,
+ "resource": wi.Spec.WorkloadResource,
+ "resource type": wi.Spec.Type,
+ })
+ continue
+ }
+ r, err := context.GetValue(rh)
+ if err != nil {
+ log.Error("Error retrieving resource from App Context", log.Fields{
+ "error": err,
+ "resource handle": rh,
+ })
+ }
+
+ // Unmarshal resource to K8S object
+ robj, err := runtime.Decode(scheme.Codecs.UniversalDeserializer(), []byte(r.(string)))
+
+ // Add network annotation to object
+ netAnnot := nettypes.NetworkSelectionElement{
+ Name: "ovn-networkobj",
+ Namespace: "default",
+ }
+ AddNetworkAnnotation(robj, netAnnot)
+
+ // Add nfn interface annotations to object
+ var newNfnIfs []WorkloadIfIntentSpec
+ for _, i := range wifs {
+ newNfnIfs = append(newNfnIfs, i.Spec)
+ }
+ AddNfnAnnotation(robj, newNfnIfs)
+
+ // Marshal object back to yaml format (via json - seems to eliminate most clutter)
+ j, err := json.Marshal(robj)
+ if err != nil {
+ log.Error("Error marshalling resource to JSON", log.Fields{
+ "error": err,
+ })
+ continue
+ }
+ y, err := jyaml.JSONToYAML(j)
+ if err != nil {
+ log.Error("Error marshalling resource to YAML", log.Fields{
+ "error": err,
+ })
+ continue
+ }
+
+ // Update resource in AppContext
+ err = context.UpdateResourceValue(rh, string(y))
+ if err != nil {
+ log.Error("Network updating app context resource handle", log.Fields{
+ "error": err,
+ "resource handle": rh,
+ })
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/src/ncm/pkg/module/network.go b/src/ncm/pkg/module/network.go
index 2d4121e9..cfb414c5 100644
--- a/src/ncm/pkg/module/network.go
+++ b/src/ncm/pkg/module/network.go
@@ -24,8 +24,8 @@ import (
// Network contains the parameters needed for dynamic networks
type Network struct {
- Metadata Metadata `json:"metadata"`
- Spec NetworkSpec `json:"spec"`
+ Metadata Metadata `json:"metadata" yaml:"metadata"`
+ Spec NetworkSpec `json:"spec" yaml:"spec"`
}
type NetworkSpec struct {
@@ -40,6 +40,16 @@ type NetworkKey struct {
NetworkName string `json:"network"`
}
+// structure for the Network Custom Resource
+type CrNetwork struct {
+ ApiVersion string `yaml:"apiVersion"`
+ Kind string `yaml:"kind"`
+ Network Network
+}
+
+const NETWORK_APIVERSION = "k8s.plugin.opnfv.org/v1alpha1"
+const NETWORK_KIND = "Network"
+
// Manager is an interface exposing the Network functionality
type NetworkManager interface {
CreateNetwork(pr Network, clusterProvider, cluster string, exists bool) (Network, error)
diff --git a/src/ncm/pkg/module/providernet.go b/src/ncm/pkg/module/providernet.go
index 5e2c0343..0435f2ba 100644
--- a/src/ncm/pkg/module/providernet.go
+++ b/src/ncm/pkg/module/providernet.go
@@ -29,12 +29,22 @@ type ProviderNet struct {
}
type ProviderNetSpec struct {
- CniType string `json:"cniType"`
- Ipv4Subnets []Ipv4Subnet `json:"ipv4Subnets"`
- ProviderNetType string `json:"providerNetType"`
- Vlan Vlan `json:"vlan"`
+ CniType string `json:"cniType" yaml:"cniType"`
+ Ipv4Subnets []Ipv4Subnet `json:"ipv4Subnets" yaml:"ipv4Subnets"`
+ ProviderNetType string `json:"providerNetType" yaml:"providerNetType"`
+ Vlan Vlan `json:"vlan" yaml:"vlan"`
}
+// structure for the Network Custom Resource
+type CrProviderNet struct {
+ ApiVersion string `yaml:"apiVersion"`
+ Kind string `yaml:"kind"`
+ ProviderNet ProviderNet
+}
+
+const PROVIDER_NETWORK_APIVERSION = "k8s.plugin.opnfv.org/v1alpha1"
+const PROVIDER_NETWORK_KIND = "ProviderNetwork"
+
// ProviderNetKey is the key structure that is used in the database
type ProviderNetKey struct {
ClusterProviderName string `json:"provider"`
diff --git a/src/ncm/pkg/module/resources.go b/src/ncm/pkg/module/resources.go
new file mode 100644
index 00000000..24c9833e
--- /dev/null
+++ b/src/ncm/pkg/module/resources.go
@@ -0,0 +1,273 @@
+/*
+ * 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 (
+ "encoding/json"
+ "fmt"
+
+ nettypes "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
+ netutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils"
+ log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils"
+ v1 "k8s.io/api/apps/v1"
+ batch "k8s.io/api/batch/v1"
+ batchv1beta1 "k8s.io/api/batch/v1beta1"
+ v1core "k8s.io/api/core/v1"
+ _ "k8s.io/kubernetes/pkg/apis/apps/install"
+ _ "k8s.io/kubernetes/pkg/apis/batch/install"
+ _ "k8s.io/kubernetes/pkg/apis/core/install"
+ _ "k8s.io/kubernetes/pkg/apis/extensions/install"
+
+ pkgerrors "github.com/pkg/errors"
+)
+
+type NfnAnnotation struct {
+ CniType string
+ Interface []WorkloadIfIntentSpec
+}
+
+const NfnAnnotationKey = "k8s.plugin.opnfv.org/nfn-network"
+
+// ParsePodTemplateNetworkAnnotation parses Pod annotation in PodTemplate
+func ParsePodTemplateNetworkAnnotation(pt *v1core.PodTemplateSpec) ([]*nettypes.NetworkSelectionElement, error) {
+ netAnnot := pt.Annotations[nettypes.NetworkAttachmentAnnot]
+ defaultNamespace := pt.Namespace
+
+ if len(netAnnot) == 0 {
+ return nil, pkgerrors.Errorf("No kubernetes network annotation found")
+ }
+
+ networks, err := netutils.ParseNetworkAnnotation(netAnnot, defaultNamespace)
+ if err != nil {
+ return nil, err
+ }
+ return networks, nil
+}
+
+// GetPodTemplateNetworkAnnotation gets Pod Nfn annotation in PodTemplate
+func GetPodTemplateNfnAnnotation(pt *v1core.PodTemplateSpec) NfnAnnotation {
+ var nfn NfnAnnotation
+
+ nfnAnnot := pt.Annotations[NfnAnnotationKey]
+ if len(nfnAnnot) == 0 {
+ return nfn
+ }
+
+ err := json.Unmarshal([]byte(nfnAnnot), &nfn)
+ if err != nil {
+ log.Warn("Error unmarshalling nfn annotation", log.Fields{
+ "annotation": nfnAnnot,
+ })
+ }
+ return nfn
+}
+
+// GetPodNetworkAnnotation gets Pod Nfn annotation in PodTemplate
+func GetPodNfnAnnotation(p *v1core.Pod) NfnAnnotation {
+ var nfn NfnAnnotation
+
+ nfnAnnot := p.Annotations[NfnAnnotationKey]
+ if len(nfnAnnot) == 0 {
+ return nfn
+ }
+
+ err := json.Unmarshal([]byte(nfnAnnot), &nfn)
+ if err != nil {
+ log.Warn("Error unmarshalling nfn annotation", log.Fields{
+ "annotation": nfnAnnot,
+ })
+ }
+ return nfn
+}
+
+func addNetworkAnnotation(a nettypes.NetworkSelectionElement, as []*nettypes.NetworkSelectionElement) []*nettypes.NetworkSelectionElement {
+ var netElements []*nettypes.NetworkSelectionElement
+
+ found := false
+ for _, e := range as {
+ if e.Name == a.Name {
+ found = true
+ }
+ netElements = append(netElements, e)
+ }
+ if !found {
+ netElements = append(netElements, &a)
+ }
+
+ return netElements
+}
+
+// Add the interfaces in the 'new' parameter to the nfn annotation
+func newNfnIfs(nfn NfnAnnotation, new []WorkloadIfIntentSpec) NfnAnnotation {
+ // Prepare a new interface list - combining the original plus new ones
+ var newNfn NfnAnnotation
+
+ if nfn.CniType != CNI_TYPE_OVN4NFV {
+ if len(nfn.CniType) > 0 {
+ log.Warn("Error existing nfn cnitype is invalid", log.Fields{
+ "existing cnitype": nfn.CniType,
+ "using cnitype": CNI_TYPE_OVN4NFV,
+ })
+ }
+ }
+ newNfn.CniType = CNI_TYPE_OVN4NFV
+
+ // update any interfaces already in the list with the updated interface
+ for _, i := range nfn.Interface {
+ for _, j := range new {
+ if i.NetworkName == j.NetworkName && i.IfName == j.IfName {
+ i.DefaultGateway = j.DefaultGateway
+ i.IpAddr = j.IpAddr
+ i.MacAddr = j.MacAddr
+ break
+ }
+ }
+ newNfn.Interface = append(newNfn.Interface, i)
+ }
+
+ // add new interfaces not present in original list
+ for _, j := range new {
+ found := false
+ for _, i := range nfn.Interface {
+ if i.NetworkName == j.NetworkName && i.IfName == j.IfName {
+ found = true
+ break
+ }
+ }
+ if !found {
+ newNfn.Interface = append(newNfn.Interface, j)
+ }
+ }
+ return newNfn
+}
+
+func updatePodTemplateNetworkAnnotation(pt *v1core.PodTemplateSpec, a nettypes.NetworkSelectionElement) {
+ netAnnotation, _ := ParsePodTemplateNetworkAnnotation(pt)
+ elements := addNetworkAnnotation(a, netAnnotation)
+ j, err := json.Marshal(elements)
+ if err != nil {
+ log.Error("Existing network annotation has invalid format", log.Fields{
+ "error": err,
+ })
+ return
+ }
+ if pt.Annotations == nil {
+ pt.Annotations = make(map[string]string)
+ }
+ pt.Annotations[nettypes.NetworkAttachmentAnnot] = string(j)
+}
+
+// Add a network annotation to the resource
+func AddNetworkAnnotation(r interface{}, a nettypes.NetworkSelectionElement) {
+
+ switch o := r.(type) {
+ case *batch.Job:
+ updatePodTemplateNetworkAnnotation(&o.Spec.Template, a)
+ case *batchv1beta1.CronJob:
+ updatePodTemplateNetworkAnnotation(&o.Spec.JobTemplate.Spec.Template, a)
+ case *v1.DaemonSet:
+ updatePodTemplateNetworkAnnotation(&o.Spec.Template, a)
+ case *v1.Deployment:
+ updatePodTemplateNetworkAnnotation(&o.Spec.Template, a)
+ case *v1.ReplicaSet:
+ updatePodTemplateNetworkAnnotation(&o.Spec.Template, a)
+ case *v1.StatefulSet:
+ updatePodTemplateNetworkAnnotation(&o.Spec.Template, a)
+ case *v1core.Pod:
+ netAnnotation, _ := netutils.ParsePodNetworkAnnotation(o)
+ elements := addNetworkAnnotation(a, netAnnotation)
+ j, err := json.Marshal(elements)
+ if err != nil {
+ log.Error("Existing network annotation has invalid format", log.Fields{
+ "error": err,
+ })
+ break
+ }
+ if o.Annotations == nil {
+ o.Annotations = make(map[string]string)
+ }
+ o.Annotations[nettypes.NetworkAttachmentAnnot] = string(j)
+ return
+ case *v1core.ReplicationController:
+ updatePodTemplateNetworkAnnotation(o.Spec.Template, a)
+ default:
+ typeStr := fmt.Sprintf("%T", o)
+ log.Warn("Network annotations not supported for resource type", log.Fields{
+ "resource type": typeStr,
+ })
+ }
+}
+
+func updatePodTemplateNfnAnnotation(pt *v1core.PodTemplateSpec, new []WorkloadIfIntentSpec) {
+ nfnAnnotation := GetPodTemplateNfnAnnotation(pt)
+ newNfnAnnotation := newNfnIfs(nfnAnnotation, new)
+ j, err := json.Marshal(newNfnAnnotation)
+ if err != nil {
+ log.Error("Network nfn annotation has invalid format", log.Fields{
+ "error": err,
+ })
+ return
+ }
+ if pt.Annotations == nil {
+ pt.Annotations = make(map[string]string)
+ }
+ pt.Annotations[NfnAnnotationKey] = string(j)
+}
+
+// Add an nfn annotation to the resource
+func AddNfnAnnotation(r interface{}, new []WorkloadIfIntentSpec) {
+
+ switch o := r.(type) {
+ case *batch.Job:
+ updatePodTemplateNfnAnnotation(&o.Spec.Template, new)
+ case *batchv1beta1.CronJob:
+ updatePodTemplateNfnAnnotation(&o.Spec.JobTemplate.Spec.Template, new)
+ case *v1.DaemonSet:
+ updatePodTemplateNfnAnnotation(&o.Spec.Template, new)
+ return
+ case *v1.Deployment:
+ updatePodTemplateNfnAnnotation(&o.Spec.Template, new)
+ return
+ case *v1.ReplicaSet:
+ updatePodTemplateNfnAnnotation(&o.Spec.Template, new)
+ case *v1.StatefulSet:
+ updatePodTemplateNfnAnnotation(&o.Spec.Template, new)
+ case *v1core.Pod:
+ nfnAnnotation := GetPodNfnAnnotation(o)
+ newNfnAnnotation := newNfnIfs(nfnAnnotation, new)
+ j, err := json.Marshal(newNfnAnnotation)
+ if err != nil {
+ log.Error("Network nfn annotation has invalid format", log.Fields{
+ "error": err,
+ })
+ break
+ }
+ if o.Annotations == nil {
+ o.Annotations = make(map[string]string)
+ }
+ o.Annotations[NfnAnnotationKey] = string(j)
+ return
+ case *v1core.ReplicationController:
+ updatePodTemplateNfnAnnotation(o.Spec.Template, new)
+ return
+ default:
+ typeStr := fmt.Sprintf("%T", o)
+ log.Warn("Network nfn annotations not supported for resource type", log.Fields{
+ "resource type": typeStr,
+ })
+ }
+}