blob: 8788e8a67ea6f13bd28940b04a330b6afc6475c1 [file] [log] [blame]
Scott Baker6cf525a2019-05-09 12:25:08 -07001/*
2 * Portions copyright 2019-present Open Networking Foundation
3 * Original copyright 2019-present Ciena Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package commands
18
19import (
20 "context"
21 "errors"
22 "fmt"
23 "github.com/fullstorydev/grpcurl"
24 "github.com/jhump/protoreflect/dynamic"
25 "google.golang.org/grpc"
26 "strings"
27 "time"
28)
29
30const GM_QUIET = 1
31const GM_UNTIL_FOUND = 2
32const GM_UNTIL_ENACTED = 4
33const GM_UNTIL_STATUS = 8
34
35// Return a list of all available model names
36func GetModelNames(source grpcurl.DescriptorSource) (map[string]bool, error) {
37 models := make(map[string]bool)
38 methods, err := grpcurl.ListMethods(source, "xos.xos")
39
40 if err != nil {
41 return nil, err
42 }
43
44 for _, method := range methods {
45 if strings.HasPrefix(method, "xos.xos.Get") {
46 models[method[11:]] = true
47 }
48 }
49
50 return models, nil
51}
52
53// Check to see if a model name is valid
54func CheckModelName(source grpcurl.DescriptorSource, name string) error {
55 models, err := GetModelNames(source)
56 if err != nil {
57 return err
58 }
59 _, present := models[name]
60 if !present {
61 return errors.New("Model " + name + " does not exist. Use `cordctl models available` to get a list of available models")
62 }
63 return nil
64}
65
66// Create a model in XOS given a map of fields
67func CreateModel(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, fields map[string]interface{}) error {
68 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
69 defer cancel()
70
71 headers := GenerateHeaders()
72
73 h := &RpcEventHandler{
74 Fields: map[string]map[string]interface{}{"xos." + modelName: fields},
75 }
76 err := grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.xos.Create"+modelName, headers, h, h.GetParams)
77 if err != nil {
78 return err
79 } else if h.Status != nil && h.Status.Err() != nil {
80 return h.Status.Err()
81 }
82
83 resp, err := dynamic.AsDynamicMessage(h.Response)
84 if err != nil {
85 return err
86 }
87
88 fields["id"] = resp.GetFieldByName("id").(int32)
89
90 if resp.HasFieldName("uuid") {
91 fields["uuid"] = resp.GetFieldByName("uuid").(string)
92 }
93
94 return nil
95}
96
97// Get a model from XOS given its ID
98func GetModel(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, id int32) (*dynamic.Message, error) {
99 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
100 defer cancel()
101
102 headers := GenerateHeaders()
103
104 h := &RpcEventHandler{
105 Fields: map[string]map[string]interface{}{"xos.ID": map[string]interface{}{"id": id}},
106 }
107 err := grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.xos.Get"+modelName, headers, h, h.GetParams)
108 if err != nil {
109 return nil, err
110 }
111
112 if h.Status != nil && h.Status.Err() != nil {
113 return nil, h.Status.Err()
114 }
115
116 d, err := dynamic.AsDynamicMessage(h.Response)
117 if err != nil {
118 return nil, err
119 }
120
121 return d, nil
122}
123
124// Get a model, but retry under a variety of circumstances
125func GetModelWithRetry(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, id int32, flags uint32) (*grpc.ClientConn, *dynamic.Message, error) {
126 quiet := (flags & GM_QUIET) != 0
127 until_found := (flags & GM_UNTIL_FOUND) != 0
128 until_enacted := (flags & GM_UNTIL_ENACTED) != 0
129 until_status := (flags & GM_UNTIL_STATUS) != 0
130
131 for {
132 var err error
133
134 if conn == nil {
135 conn, err = NewConnection()
136 if err != nil {
137 return nil, nil, err
138 }
139 }
140
141 model, err := GetModel(conn, descriptor, modelName, id)
142 if err != nil {
143 if strings.Contains(err.Error(), "rpc error: code = Unavailable") ||
144 strings.Contains(err.Error(), "rpc error: code = Internal desc = stream terminated by RST_STREAM") {
145 if !quiet {
146 fmt.Print(".")
147 }
148 time.Sleep(100 * time.Millisecond)
149 conn.Close()
150 conn = nil
151 continue
152 }
153
154 if until_found && strings.Contains(err.Error(), "rpc error: code = NotFound") {
155 if !quiet {
156 fmt.Print("x")
157 }
158 time.Sleep(100 * time.Millisecond)
159 continue
160 }
161 return nil, nil, err
162 }
163
164 if until_enacted && !IsEnacted(model) {
165 if !quiet {
166 fmt.Print("o")
167 }
168 time.Sleep(100 * time.Millisecond)
169 continue
170 }
171
172 if until_status && model.GetFieldByName("status") == nil {
173 if !quiet {
174 fmt.Print("O")
175 }
176 time.Sleep(100 * time.Millisecond)
177 continue
178 }
179
180 return conn, model, nil
181 }
182}
183
184// Get a model from XOS given a fieldName/fieldValue
185func FindModel(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, fieldName string, fieldValue string) (*dynamic.Message, error) {
186 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
187 defer cancel()
188
189 headers := GenerateHeaders()
190
191 // TODO(smbaker): Implement filter the right way
192
193 h := &RpcEventHandler{}
194 err := grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.xos.List"+modelName, headers, h, h.GetParams)
195 if err != nil {
196 return nil, err
197 }
198
199 if h.Status != nil && h.Status.Err() != nil {
200 return nil, h.Status.Err()
201 }
202
203 d, err := dynamic.AsDynamicMessage(h.Response)
204 if err != nil {
205 return nil, err
206 }
207
208 items, err := d.TryGetFieldByName("items")
209 if err != nil {
210 return nil, err
211 }
212
213 for _, item := range items.([]interface{}) {
214 val := item.(*dynamic.Message)
215
216 if val.GetFieldByName(fieldName).(string) == fieldValue {
217 return val, nil
218 }
219
220 }
221
222 return nil, errors.New("rpc error: code = NotFound")
223}
224
225// Find a model, but retry under a variety of circumstances
226func FindModelWithRetry(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, modelName string, fieldName string, fieldValue string, flags uint32) (*grpc.ClientConn, *dynamic.Message, error) {
227 quiet := (flags & GM_QUIET) != 0
228 until_found := (flags & GM_UNTIL_FOUND) != 0
229 until_enacted := (flags & GM_UNTIL_ENACTED) != 0
230 until_status := (flags & GM_UNTIL_STATUS) != 0
231
232 for {
233 var err error
234
235 if conn == nil {
236 conn, err = NewConnection()
237 if err != nil {
238 return nil, nil, err
239 }
240 }
241
242 model, err := FindModel(conn, descriptor, modelName, fieldName, fieldValue)
243 if err != nil {
244 if strings.Contains(err.Error(), "rpc error: code = Unavailable") ||
245 strings.Contains(err.Error(), "rpc error: code = Internal desc = stream terminated by RST_STREAM") {
246 if !quiet {
247 fmt.Print(".")
248 }
249 time.Sleep(100 * time.Millisecond)
250 conn.Close()
251 conn = nil
252 continue
253 }
254
255 if until_found && strings.Contains(err.Error(), "rpc error: code = NotFound") {
256 if !quiet {
257 fmt.Print("x")
258 }
259 time.Sleep(100 * time.Millisecond)
260 continue
261 }
262 return nil, nil, err
263 }
264
265 if until_enacted && !IsEnacted(model) {
266 if !quiet {
267 fmt.Print("o")
268 }
269 time.Sleep(100 * time.Millisecond)
270 continue
271 }
272
273 if until_status && model.GetFieldByName("status") == nil {
274 if !quiet {
275 fmt.Print("O")
276 }
277 time.Sleep(100 * time.Millisecond)
278 continue
279 }
280
281 return conn, model, nil
282 }
283}
284
285func MessageToMap(d *dynamic.Message) map[string]interface{} {
286 fields := make(map[string]interface{})
287 for _, field_desc := range d.GetKnownFields() {
288 field_name := field_desc.GetName()
289 fields[field_name] = d.GetFieldByName(field_name)
290 }
291 return fields
292}
293
294func IsEnacted(d *dynamic.Message) bool {
295 enacted := d.GetFieldByName("enacted").(float64)
296 updated := d.GetFieldByName("updated").(float64)
297
298 return (enacted >= updated)
299}