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