blob: 16cd48a72d4118096d2ca5c0af59d3b064a3ee99 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2 * Copyright 2019-present Ciena Corporation
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 */
16package commands
17
18import (
19 "context"
20 "fmt"
Zack Williamse940c7a2019-08-21 14:25:39 -070021 "github.com/fullstorydev/grpcurl"
22 flags "github.com/jessevdk/go-flags"
23 "github.com/jhump/protoreflect/dynamic"
Scott Baker2b0ad652019-08-21 14:57:07 -070024 "github.com/opencord/voltctl/pkg/format"
25 "github.com/opencord/voltctl/pkg/model"
David Bainbridge12f036f2019-10-15 22:09:04 +000026 "log"
Zack Williamse940c7a2019-08-21 14:25:39 -070027 "strings"
28)
29
30const (
31 DEFAULT_LOGICAL_DEVICE_FORMAT = "table{{ .Id }}\t{{.DatapathId}}\t{{.RootDeviceId}}\t{{.SerialNumber}}\t{{.Features.NBuffers}}\t{{.Features.NTables}}\t{{.Features.Capabilities}}"
32 DEFAULT_LOGICAL_DEVICE_PORT_FORMAT = "table{{.Id}}\t{{.DeviceId}}\t{{.DevicePortNo}}\t{{.RootPort}}\t{{.Openflow.PortNo}}\t{{.Openflow.HwAddr}}\t{{.Openflow.Name}}\t{{.Openflow.State}}\t{{.Openflow.Features.Current}}\t{{.Openflow.Bitrate.Current}}"
33 DEFAULT_LOGICAL_DEVICE_INSPECT_FORMAT = `ID: {{.Id}}
34 DATAPATHID: {{.DatapathId}}
35 ROOTDEVICEID: {{.RootDeviceId}}
36 SERIALNUMNER: {{.SerialNumber}}`
37)
38
39type LogicalDeviceId string
40
41type LogicalDeviceList struct {
42 ListOutputOptions
43}
44
45type LogicalDeviceFlowList struct {
46 ListOutputOptions
47 Args struct {
48 Id LogicalDeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
49 } `positional-args:"yes"`
50}
51
52type LogicalDevicePortList struct {
53 ListOutputOptions
54 Args struct {
55 Id LogicalDeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
56 } `positional-args:"yes"`
57}
58
59type LogicalDeviceInspect struct {
60 OutputOptionsJson
61 Args struct {
62 Id LogicalDeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
63 } `positional-args:"yes"`
64}
65
66type LogicalDeviceOpts struct {
67 List LogicalDeviceList `command:"list"`
68 Flows LogicalDeviceFlowList `command:"flows"`
69 Ports LogicalDevicePortList `command:"ports"`
70 Inspect LogicalDeviceInspect `command:"inspect"`
71}
72
73var logicalDeviceOpts = LogicalDeviceOpts{}
74
75func RegisterLogicalDeviceCommands(parser *flags.Parser) {
David Bainbridge12f036f2019-10-15 22:09:04 +000076 if _, err := parser.AddCommand("logicaldevice", "logical device commands", "Commands to query and manipulate VOLTHA logical devices", &logicalDeviceOpts); err != nil {
77 log.Fatalf("Unexpected error while attempting to register logical device commands : %s", err)
78 }
Zack Williamse940c7a2019-08-21 14:25:39 -070079}
80
81func (i *LogicalDeviceId) Complete(match string) []flags.Completion {
82 conn, err := NewConnection()
83 if err != nil {
84 return nil
85 }
86 defer conn.Close()
87
88 descriptor, method, err := GetMethod("logical-device-list")
89 if err != nil {
90 return nil
91 }
92
93 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
94 defer cancel()
95
96 h := &RpcEventHandler{}
97 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
98 if err != nil {
99 return nil
100 }
101
102 if h.Status != nil && h.Status.Err() != nil {
103 return nil
104 }
105
106 d, err := dynamic.AsDynamicMessage(h.Response)
107 if err != nil {
108 return nil
109 }
110
111 items, err := d.TryGetFieldByName("items")
112 if err != nil {
113 return nil
114 }
115
116 list := make([]flags.Completion, 0)
117 for _, item := range items.([]interface{}) {
118 val := item.(*dynamic.Message)
119 id := val.GetFieldByName("id").(string)
120 if strings.HasPrefix(id, match) {
121 list = append(list, flags.Completion{Item: id})
122 }
123 }
124
125 return list
126}
127
128func (options *LogicalDeviceList) Execute(args []string) error {
129
130 conn, err := NewConnection()
131 if err != nil {
132 return err
133 }
134 defer conn.Close()
135
136 descriptor, method, err := GetMethod("logical-device-list")
137 if err != nil {
138 return err
139 }
140
141 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
142 defer cancel()
143
144 h := &RpcEventHandler{}
145 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
146 if err != nil {
147 return err
148 }
149
150 if h.Status != nil && h.Status.Err() != nil {
151 return h.Status.Err()
152 }
153
154 d, err := dynamic.AsDynamicMessage(h.Response)
155 if err != nil {
156 return err
157 }
158
159 items, err := d.TryGetFieldByName("items")
160 if err != nil {
161 return err
162 }
163
164 outputFormat := CharReplacer.Replace(options.Format)
165 if outputFormat == "" {
166 outputFormat = DEFAULT_LOGICAL_DEVICE_FORMAT
167 }
168 if options.Quiet {
169 outputFormat = "{{.Id}}"
170 }
171
172 data := make([]model.LogicalDevice, len(items.([]interface{})))
173 for i, item := range items.([]interface{}) {
174 data[i].PopulateFrom(item.(*dynamic.Message))
175 }
176
177 result := CommandResult{
178 Format: format.Format(outputFormat),
179 Filter: options.Filter,
180 OrderBy: options.OrderBy,
181 OutputAs: toOutputType(options.OutputAs),
182 NameLimit: options.NameLimit,
183 Data: data,
184 }
185
186 GenerateOutput(&result)
187 return nil
188}
189
190func (options *LogicalDevicePortList) Execute(args []string) error {
191
192 conn, err := NewConnection()
193 if err != nil {
194 return err
195 }
196 defer conn.Close()
197
198 descriptor, method, err := GetMethod("logical-device-ports")
199 if err != nil {
200 return err
201 }
202
203 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
204 defer cancel()
205
206 h := &RpcEventHandler{
207 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": options.Args.Id}},
208 }
209 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
210 if err != nil {
211 return err
212 }
213
214 if h.Status != nil && h.Status.Err() != nil {
215 return h.Status.Err()
216 }
217
218 d, err := dynamic.AsDynamicMessage(h.Response)
219 if err != nil {
220 return err
221 }
222
223 items, err := d.TryGetFieldByName("items")
224 if err != nil {
225 return err
226 }
227
228 outputFormat := CharReplacer.Replace(options.Format)
229 if outputFormat == "" {
230 outputFormat = DEFAULT_LOGICAL_DEVICE_PORT_FORMAT
231 }
232 if options.Quiet {
233 outputFormat = "{{.Id}}"
234 }
235
236 data := make([]model.LogicalPort, len(items.([]interface{})))
237 for i, item := range items.([]interface{}) {
238 data[i].PopulateFrom(item.(*dynamic.Message))
239 }
240
241 result := CommandResult{
242 Format: format.Format(outputFormat),
243 Filter: options.Filter,
244 OrderBy: options.OrderBy,
245 OutputAs: toOutputType(options.OutputAs),
246 NameLimit: options.NameLimit,
247 Data: data,
248 }
249
250 GenerateOutput(&result)
251 return nil
252}
253
254func (options *LogicalDeviceFlowList) Execute(args []string) error {
255 fl := &FlowList{}
256 fl.ListOutputOptions = options.ListOutputOptions
257 fl.Args.Id = string(options.Args.Id)
258 fl.Method = "logical-device-flow-list"
259 return fl.Execute(args)
260}
261
262func (options *LogicalDeviceInspect) Execute(args []string) error {
263 if len(args) > 0 {
264 return fmt.Errorf("only a single argument 'DEVICE_ID' can be provided")
265 }
266
267 conn, err := NewConnection()
268 if err != nil {
269 return err
270 }
271 defer conn.Close()
272
273 descriptor, method, err := GetMethod("logical-device-inspect")
274 if err != nil {
275 return err
276 }
277
278 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
279 defer cancel()
280
281 h := &RpcEventHandler{
282 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": options.Args.Id}},
283 }
284 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{"Get-Depth: 2"}, h, h.GetParams)
285 if err != nil {
286 return err
287 } else if h.Status != nil && h.Status.Err() != nil {
288 return h.Status.Err()
289 }
290
291 d, err := dynamic.AsDynamicMessage(h.Response)
292 if err != nil {
293 return err
294 }
295
296 device := &model.LogicalDevice{}
297 device.PopulateFrom(d)
298
299 outputFormat := CharReplacer.Replace(options.Format)
300 if outputFormat == "" {
301 outputFormat = DEFAULT_LOGICAL_DEVICE_INSPECT_FORMAT
302 }
303 if options.Quiet {
304 outputFormat = "{{.Id}}"
305 }
306
307 result := CommandResult{
308 Format: format.Format(outputFormat),
309 OutputAs: toOutputType(options.OutputAs),
310 NameLimit: options.NameLimit,
311 Data: device,
312 }
313 GenerateOutput(&result)
314 return nil
315}