blob: 91bd7e1d2ee69878f246e13b6e8567d81c371cf6 [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"
21 "github.com/ciena/voltctl/pkg/format"
22 "github.com/ciena/voltctl/pkg/model"
23 "github.com/fullstorydev/grpcurl"
24 flags "github.com/jessevdk/go-flags"
25 "github.com/jhump/protoreflect/dynamic"
26 "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 {
66 List LogicalDeviceList `command:"list"`
67 Flows LogicalDeviceFlowList `command:"flows"`
68 Ports LogicalDevicePortList `command:"ports"`
69 Inspect LogicalDeviceInspect `command:"inspect"`
70}
71
72var logicalDeviceOpts = LogicalDeviceOpts{}
73
74func RegisterLogicalDeviceCommands(parser *flags.Parser) {
75 parser.AddCommand("logicaldevice", "logical device commands", "Commands to query and manipulate VOLTHA logical devices", &logicalDeviceOpts)
76}
77
78func (i *LogicalDeviceId) Complete(match string) []flags.Completion {
79 conn, err := NewConnection()
80 if err != nil {
81 return nil
82 }
83 defer conn.Close()
84
85 descriptor, method, err := GetMethod("logical-device-list")
86 if err != nil {
87 return nil
88 }
89
90 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
91 defer cancel()
92
93 h := &RpcEventHandler{}
94 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
95 if err != nil {
96 return nil
97 }
98
99 if h.Status != nil && h.Status.Err() != nil {
100 return nil
101 }
102
103 d, err := dynamic.AsDynamicMessage(h.Response)
104 if err != nil {
105 return nil
106 }
107
108 items, err := d.TryGetFieldByName("items")
109 if err != nil {
110 return nil
111 }
112
113 list := make([]flags.Completion, 0)
114 for _, item := range items.([]interface{}) {
115 val := item.(*dynamic.Message)
116 id := val.GetFieldByName("id").(string)
117 if strings.HasPrefix(id, match) {
118 list = append(list, flags.Completion{Item: id})
119 }
120 }
121
122 return list
123}
124
125func (options *LogicalDeviceList) Execute(args []string) error {
126
127 conn, err := NewConnection()
128 if err != nil {
129 return err
130 }
131 defer conn.Close()
132
133 descriptor, method, err := GetMethod("logical-device-list")
134 if err != nil {
135 return err
136 }
137
138 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
139 defer cancel()
140
141 h := &RpcEventHandler{}
142 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
143 if err != nil {
144 return err
145 }
146
147 if h.Status != nil && h.Status.Err() != nil {
148 return h.Status.Err()
149 }
150
151 d, err := dynamic.AsDynamicMessage(h.Response)
152 if err != nil {
153 return err
154 }
155
156 items, err := d.TryGetFieldByName("items")
157 if err != nil {
158 return err
159 }
160
161 outputFormat := CharReplacer.Replace(options.Format)
162 if outputFormat == "" {
163 outputFormat = DEFAULT_LOGICAL_DEVICE_FORMAT
164 }
165 if options.Quiet {
166 outputFormat = "{{.Id}}"
167 }
168
169 data := make([]model.LogicalDevice, len(items.([]interface{})))
170 for i, item := range items.([]interface{}) {
171 data[i].PopulateFrom(item.(*dynamic.Message))
172 }
173
174 result := CommandResult{
175 Format: format.Format(outputFormat),
176 Filter: options.Filter,
177 OrderBy: options.OrderBy,
178 OutputAs: toOutputType(options.OutputAs),
179 NameLimit: options.NameLimit,
180 Data: data,
181 }
182
183 GenerateOutput(&result)
184 return nil
185}
186
187func (options *LogicalDevicePortList) Execute(args []string) error {
188
189 conn, err := NewConnection()
190 if err != nil {
191 return err
192 }
193 defer conn.Close()
194
195 descriptor, method, err := GetMethod("logical-device-ports")
196 if err != nil {
197 return err
198 }
199
200 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
201 defer cancel()
202
203 h := &RpcEventHandler{
204 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": options.Args.Id}},
205 }
206 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
207 if err != nil {
208 return err
209 }
210
211 if h.Status != nil && h.Status.Err() != nil {
212 return h.Status.Err()
213 }
214
215 d, err := dynamic.AsDynamicMessage(h.Response)
216 if err != nil {
217 return err
218 }
219
220 items, err := d.TryGetFieldByName("items")
221 if err != nil {
222 return err
223 }
224
225 outputFormat := CharReplacer.Replace(options.Format)
226 if outputFormat == "" {
227 outputFormat = DEFAULT_LOGICAL_DEVICE_PORT_FORMAT
228 }
229 if options.Quiet {
230 outputFormat = "{{.Id}}"
231 }
232
233 data := make([]model.LogicalPort, len(items.([]interface{})))
234 for i, item := range items.([]interface{}) {
235 data[i].PopulateFrom(item.(*dynamic.Message))
236 }
237
238 result := CommandResult{
239 Format: format.Format(outputFormat),
240 Filter: options.Filter,
241 OrderBy: options.OrderBy,
242 OutputAs: toOutputType(options.OutputAs),
243 NameLimit: options.NameLimit,
244 Data: data,
245 }
246
247 GenerateOutput(&result)
248 return nil
249}
250
251func (options *LogicalDeviceFlowList) Execute(args []string) error {
252 fl := &FlowList{}
253 fl.ListOutputOptions = options.ListOutputOptions
254 fl.Args.Id = string(options.Args.Id)
255 fl.Method = "logical-device-flow-list"
256 return fl.Execute(args)
257}
258
259func (options *LogicalDeviceInspect) Execute(args []string) error {
260 if len(args) > 0 {
261 return fmt.Errorf("only a single argument 'DEVICE_ID' can be provided")
262 }
263
264 conn, err := NewConnection()
265 if err != nil {
266 return err
267 }
268 defer conn.Close()
269
270 descriptor, method, err := GetMethod("logical-device-inspect")
271 if err != nil {
272 return err
273 }
274
275 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
276 defer cancel()
277
278 h := &RpcEventHandler{
279 Fields: map[string]map[string]interface{}{ParamNames[GlobalConfig.ApiVersion]["ID"]: {"id": options.Args.Id}},
280 }
281 err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{"Get-Depth: 2"}, h, h.GetParams)
282 if err != nil {
283 return err
284 } else if h.Status != nil && h.Status.Err() != nil {
285 return h.Status.Err()
286 }
287
288 d, err := dynamic.AsDynamicMessage(h.Response)
289 if err != nil {
290 return err
291 }
292
293 device := &model.LogicalDevice{}
294 device.PopulateFrom(d)
295
296 outputFormat := CharReplacer.Replace(options.Format)
297 if outputFormat == "" {
298 outputFormat = DEFAULT_LOGICAL_DEVICE_INSPECT_FORMAT
299 }
300 if options.Quiet {
301 outputFormat = "{{.Id}}"
302 }
303
304 result := CommandResult{
305 Format: format.Format(outputFormat),
306 OutputAs: toOutputType(options.OutputAs),
307 NameLimit: options.NameLimit,
308 Data: device,
309 }
310 GenerateOutput(&result)
311 return nil
312}