/* * 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" "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" ) // NetControlIntent contains the parameters needed for dynamic networks type NetControlIntent struct { Metadata Metadata `json:"metadata"` } // NetControlIntentKey is the key structure that is used in the database type NetControlIntentKey struct { NetControlIntent string `json:"netcontrolintent"` Project string `json:"project"` CompositeApp string `json:"compositeapp"` CompositeAppVersion string `json:"compositeappversion"` } // Manager is an interface exposing the NetControlIntent functionality type NetControlIntentManager interface { CreateNetControlIntent(nci NetControlIntent, project, compositeapp, compositeappversion string, exists bool) (NetControlIntent, error) 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 // It will also be used to maintain some localized state type NetControlIntentClient struct { db ClientDbInfo } // NewNetControlIntentClient returns an instance of the NetControlIntentClient // which implements the Manager func NewNetControlIntentClient() *NetControlIntentClient { return &NetControlIntentClient{ db: ClientDbInfo{ storeName: "orchestrator", tagMeta: "netcontrolintentmetadata", }, } } // CreateNetControlIntent - create a new NetControlIntent func (v *NetControlIntentClient) CreateNetControlIntent(nci NetControlIntent, project, compositeapp, compositeappversion string, exists bool) (NetControlIntent, error) { //Construct key and tag to select the entry key := NetControlIntentKey{ NetControlIntent: nci.Metadata.Name, Project: project, CompositeApp: compositeapp, CompositeAppVersion: compositeappversion, } //Check if this NetControlIntent already exists _, err := v.GetNetControlIntent(nci.Metadata.Name, project, compositeapp, compositeappversion) if err == nil && !exists { return NetControlIntent{}, pkgerrors.New("NetControlIntent already exists") } err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagMeta, nci) if err != nil { return NetControlIntent{}, pkgerrors.Wrap(err, "Creating DB Entry") } return nci, nil } // GetNetControlIntent returns the NetControlIntent for corresponding name func (v *NetControlIntentClient) GetNetControlIntent(name, project, compositeapp, compositeappversion string) (NetControlIntent, error) { //Construct key and tag to select the entry key := NetControlIntentKey{ NetControlIntent: name, Project: project, CompositeApp: compositeapp, CompositeAppVersion: compositeappversion, } value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return NetControlIntent{}, pkgerrors.Wrap(err, "Get NetControlIntent") } //value is a byte array if value != nil { nci := NetControlIntent{} err = db.DBconn.Unmarshal(value[0], &nci) if err != nil { return NetControlIntent{}, pkgerrors.Wrap(err, "Unmarshalling Value") } return nci, nil } return NetControlIntent{}, pkgerrors.New("Error getting NetControlIntent") } // GetNetControlIntentList returns all of the NetControlIntent for corresponding name func (v *NetControlIntentClient) GetNetControlIntents(project, compositeapp, compositeappversion string) ([]NetControlIntent, error) { //Construct key and tag to select the entry key := NetControlIntentKey{ NetControlIntent: "", Project: project, CompositeApp: compositeapp, CompositeAppVersion: compositeappversion, } var resp []NetControlIntent values, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta) if err != nil { return []NetControlIntent{}, pkgerrors.Wrap(err, "Get NetControlIntents") } for _, value := range values { nci := NetControlIntent{} err = db.DBconn.Unmarshal(value, &nci) if err != nil { return []NetControlIntent{}, pkgerrors.Wrap(err, "Unmarshalling Value") } resp = append(resp, nci) } return resp, nil } // Delete the NetControlIntent from database func (v *NetControlIntentClient) DeleteNetControlIntent(name, project, compositeapp, compositeappversion string) error { //Construct key and tag to select the entry key := NetControlIntentKey{ NetControlIntent: name, Project: project, CompositeApp: compositeapp, CompositeAppVersion: compositeappversion, } err := db.DBconn.Remove(v.db.storeName, key) if err != nil { return pkgerrors.Wrap(err, "Delete NetControlIntent Entry;") } 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, y) if err != nil { log.Error("Network updating app context resource handle", log.Fields{ "error": err, "resource handle": rh, }) } } } return nil }