da8f9a85efcd131313bd250f07dd12cd7c51df71
[multicloud/k8s.git] / src / ncm / pkg / module / netcontrolintent.go
1 /*
2  * Copyright 2020 Intel Corporation, Inc
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package module
18
19 import (
20         "encoding/json"
21         "strings"
22
23         jyaml "github.com/ghodss/yaml"
24
25         nettypes "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
26         "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext"
27         "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
28         log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils"
29         "k8s.io/apimachinery/pkg/runtime"
30         "k8s.io/client-go/kubernetes/scheme"
31
32         pkgerrors "github.com/pkg/errors"
33 )
34
35 // NetControlIntent contains the parameters needed for dynamic networks
36 type NetControlIntent struct {
37         Metadata Metadata `json:"metadata"`
38 }
39
40 // NetControlIntentKey is the key structure that is used in the database
41 type NetControlIntentKey struct {
42         NetControlIntent    string `json:"netcontrolintent"`
43         Project             string `json:"project"`
44         CompositeApp        string `json:"compositeapp"`
45         CompositeAppVersion string `json:"compositeappversion"`
46 }
47
48 // Manager is an interface exposing the NetControlIntent functionality
49 type NetControlIntentManager interface {
50         CreateNetControlIntent(nci NetControlIntent, project, compositeapp, compositeappversion string, exists bool) (NetControlIntent, error)
51         GetNetControlIntent(name, project, compositeapp, compositeappversion string) (NetControlIntent, error)
52         GetNetControlIntents(project, compositeapp, compositeappversion string) ([]NetControlIntent, error)
53         DeleteNetControlIntent(name, project, compositeapp, compositeappversion string) error
54         ApplyNetControlIntent(name, project, compositeapp, compositeappversion, appContextId string) error
55 }
56
57 // NetControlIntentClient implements the Manager
58 // It will also be used to maintain some localized state
59 type NetControlIntentClient struct {
60         db ClientDbInfo
61 }
62
63 // NewNetControlIntentClient returns an instance of the NetControlIntentClient
64 // which implements the Manager
65 func NewNetControlIntentClient() *NetControlIntentClient {
66         return &NetControlIntentClient{
67                 db: ClientDbInfo{
68                         storeName: "orchestrator",
69                         tagMeta:   "netcontrolintentmetadata",
70                 },
71         }
72 }
73
74 // CreateNetControlIntent - create a new NetControlIntent
75 func (v *NetControlIntentClient) CreateNetControlIntent(nci NetControlIntent, project, compositeapp, compositeappversion string, exists bool) (NetControlIntent, error) {
76
77         //Construct key and tag to select the entry
78         key := NetControlIntentKey{
79                 NetControlIntent:    nci.Metadata.Name,
80                 Project:             project,
81                 CompositeApp:        compositeapp,
82                 CompositeAppVersion: compositeappversion,
83         }
84
85         //Check if this NetControlIntent already exists
86         _, err := v.GetNetControlIntent(nci.Metadata.Name, project, compositeapp, compositeappversion)
87         if err == nil && !exists {
88                 return NetControlIntent{}, pkgerrors.New("NetControlIntent already exists")
89         }
90
91         err = db.DBconn.Insert(v.db.storeName, key, nil, v.db.tagMeta, nci)
92         if err != nil {
93                 return NetControlIntent{}, pkgerrors.Wrap(err, "Creating DB Entry")
94         }
95
96         return nci, nil
97 }
98
99 // GetNetControlIntent returns the NetControlIntent for corresponding name
100 func (v *NetControlIntentClient) GetNetControlIntent(name, project, compositeapp, compositeappversion string) (NetControlIntent, error) {
101
102         //Construct key and tag to select the entry
103         key := NetControlIntentKey{
104                 NetControlIntent:    name,
105                 Project:             project,
106                 CompositeApp:        compositeapp,
107                 CompositeAppVersion: compositeappversion,
108         }
109
110         value, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta)
111         if err != nil {
112                 return NetControlIntent{}, pkgerrors.Wrap(err, "Get NetControlIntent")
113         }
114
115         //value is a byte array
116         if value != nil {
117                 nci := NetControlIntent{}
118                 err = db.DBconn.Unmarshal(value[0], &nci)
119                 if err != nil {
120                         return NetControlIntent{}, pkgerrors.Wrap(err, "Unmarshalling Value")
121                 }
122                 return nci, nil
123         }
124
125         return NetControlIntent{}, pkgerrors.New("Error getting NetControlIntent")
126 }
127
128 // GetNetControlIntentList returns all of the NetControlIntent for corresponding name
129 func (v *NetControlIntentClient) GetNetControlIntents(project, compositeapp, compositeappversion string) ([]NetControlIntent, error) {
130
131         //Construct key and tag to select the entry
132         key := NetControlIntentKey{
133                 NetControlIntent:    "",
134                 Project:             project,
135                 CompositeApp:        compositeapp,
136                 CompositeAppVersion: compositeappversion,
137         }
138
139         var resp []NetControlIntent
140         values, err := db.DBconn.Find(v.db.storeName, key, v.db.tagMeta)
141         if err != nil {
142                 return []NetControlIntent{}, pkgerrors.Wrap(err, "Get NetControlIntents")
143         }
144
145         for _, value := range values {
146                 nci := NetControlIntent{}
147                 err = db.DBconn.Unmarshal(value, &nci)
148                 if err != nil {
149                         return []NetControlIntent{}, pkgerrors.Wrap(err, "Unmarshalling Value")
150                 }
151                 resp = append(resp, nci)
152         }
153
154         return resp, nil
155 }
156
157 // Delete the  NetControlIntent from database
158 func (v *NetControlIntentClient) DeleteNetControlIntent(name, project, compositeapp, compositeappversion string) error {
159
160         //Construct key and tag to select the entry
161         key := NetControlIntentKey{
162                 NetControlIntent:    name,
163                 Project:             project,
164                 CompositeApp:        compositeapp,
165                 CompositeAppVersion: compositeappversion,
166         }
167
168         err := db.DBconn.Remove(v.db.storeName, key)
169         if err != nil {
170                 return pkgerrors.Wrap(err, "Delete NetControlIntent Entry;")
171         }
172
173         return nil
174 }
175
176 // (Test Routine) - Apply network-control-intent
177 func (v *NetControlIntentClient) ApplyNetControlIntent(name, project, compositeapp, compositeappversion, appContextId string) error {
178         // TODO: Handle all Network Chain Intents for the Network Control Intent
179
180         // Handle all Workload Intents for the Network Control Intent
181         wis, err := NewWorkloadIntentClient().GetWorkloadIntents(project, compositeapp, compositeappversion, name)
182         if err != nil {
183                 return pkgerrors.Wrapf(err, "Error getting Workload Intents for Network Control Intent %v for %v/%v%v not found", name, project, compositeapp, compositeappversion)
184         }
185
186         // Setup the AppContext
187         var context appcontext.AppContext
188         _, err = context.LoadAppContext(appContextId)
189         if err != nil {
190                 return pkgerrors.Wrapf(err, "Error getting AppContext with Id: %v for %v/%v%v",
191                         appContextId, project, compositeapp, compositeappversion)
192         }
193
194         // Handle all intents (currently just Interface intents) for each Workload Intent
195         for _, wi := range wis {
196                 // The app/resource identified in the workload intent needs to be updated with two annotations.
197                 // 1 - The "k8s.v1.cni.cncf.io/networks" annotation will have {"name": "ovn-networkobj", "namespace": "default"} added
198                 //     to it (preserving any existing values for this annotation.
199                 // 2 - The "k8s.plugin.opnfv.org/nfn-network" annotation will add any network interfaces that are provided by the
200                 //     workload/interfaces intents.
201
202                 // Prepare the list of interfaces from the workload intent
203                 wifs, err := NewWorkloadIfIntentClient().GetWorkloadIfIntents(project,
204                         compositeapp,
205                         compositeappversion,
206                         name,
207                         wi.Metadata.Name)
208                 if err != nil {
209                         return pkgerrors.Wrapf(err,
210                                 "Error getting Workload Interface Intents for Workload Intent %v under Network Control Intent %v for %v/%v%v not found",
211                                 wi.Metadata.Name, name, project, compositeapp, compositeappversion)
212                 }
213                 if len(wifs) == 0 {
214                         log.Warn("No interface intents provided for workload intent", log.Fields{
215                                 "project":                project,
216                                 "composite app":          compositeapp,
217                                 "composite app version":  compositeappversion,
218                                 "network control intent": name,
219                                 "workload intent":        wi.Metadata.Name,
220                         })
221                         continue
222                 }
223
224                 // Get all clusters for the current App from the AppContext
225                 clusters, err := context.GetClusterNames(wi.Spec.AppName)
226                 for _, c := range clusters {
227                         rh, err := context.GetResourceHandle(wi.Spec.AppName, c,
228                                 strings.Join([]string{wi.Spec.WorkloadResource, wi.Spec.Type}, "+"))
229                         if err != nil {
230                                 log.Warn("App Context resource handle not found", log.Fields{
231                                         "project":                project,
232                                         "composite app":          compositeapp,
233                                         "composite app version":  compositeappversion,
234                                         "network control intent": name,
235                                         "workload name":          wi.Metadata.Name,
236                                         "app":                    wi.Spec.AppName,
237                                         "resource":               wi.Spec.WorkloadResource,
238                                         "resource type":          wi.Spec.Type,
239                                 })
240                                 continue
241                         }
242                         r, err := context.GetValue(rh)
243                         if err != nil {
244                                 log.Error("Error retrieving resource from App Context", log.Fields{
245                                         "error":           err,
246                                         "resource handle": rh,
247                                 })
248                         }
249
250                         // Unmarshal resource to K8S object
251                         robj, err := runtime.Decode(scheme.Codecs.UniversalDeserializer(), []byte(r.(string)))
252
253                         // Add network annotation to object
254                         netAnnot := nettypes.NetworkSelectionElement{
255                                 Name:      "ovn-networkobj",
256                                 Namespace: "default",
257                         }
258                         AddNetworkAnnotation(robj, netAnnot)
259
260                         // Add nfn interface annotations to object
261                         var newNfnIfs []WorkloadIfIntentSpec
262                         for _, i := range wifs {
263                                 newNfnIfs = append(newNfnIfs, i.Spec)
264                         }
265                         AddNfnAnnotation(robj, newNfnIfs)
266
267                         // Marshal object back to yaml format (via json - seems to eliminate most clutter)
268                         j, err := json.Marshal(robj)
269                         if err != nil {
270                                 log.Error("Error marshalling resource to JSON", log.Fields{
271                                         "error": err,
272                                 })
273                                 continue
274                         }
275                         y, err := jyaml.JSONToYAML(j)
276                         if err != nil {
277                                 log.Error("Error marshalling resource to YAML", log.Fields{
278                                         "error": err,
279                                 })
280                                 continue
281                         }
282
283                         // Update resource in AppContext
284                         err = context.UpdateResourceValue(rh, y)
285                         if err != nil {
286                                 log.Error("Network updating app context resource handle", log.Fields{
287                                         "error":           err,
288                                         "resource handle": rh,
289                                 })
290                         }
291                 }
292         }
293
294         return nil
295 }