blob: ee780e73394fa9ffc97b154c8223d88fb041d2ac [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"
Scott Baker2b0ad652019-08-21 14:57:07 -070021 "github.com/opencord/voltctl/pkg/format"
22 "github.com/opencord/voltctl/pkg/model"
David K. Bainbridgebd6b2882021-08-26 13:31:02 +000023 "github.com/opencord/voltha-protos/v5/go/openflow_13"
24 "github.com/opencord/voltha-protos/v5/go/voltha"
Zack Williamse940c7a2019-08-21 14:25:39 -070025 "sort"
26 "strings"
27)
28
29type FlowList struct {
30 ListOutputOptions
Maninder045921e2020-09-29 16:46:02 +053031 FlowIdOptions
Zack Williamse940c7a2019-08-21 14:25:39 -070032 Args struct {
33 Id string `positional-arg-name:"DEVICE_ID" required:"yes"`
34 } `positional-args:"yes"`
35
36 Method string
37}
38
39type FlowOpts struct {
40 List FlowList `command:"list"`
41}
42
43var (
Zack Williamse940c7a2019-08-21 14:25:39 -070044 // Used to sort the table colums in a consistent order
45 SORT_ORDER = map[string]uint16{
46 "Id": 0,
47 "TableId": 10,
48 "Priority": 20,
49 "Cookie": 30,
50 "UnsupportedMatch": 35,
51 "InPort": 40,
52 "VlanId": 50,
53 "VlanPcp": 55,
54 "EthType": 60,
55 "IpProto": 70,
56 "UdpSrc": 80,
57 "UdpDst": 90,
58 "Metadata": 100,
59 "TunnelId": 101,
60 "UnsupportedInstruction": 102,
61 "UnsupportedAction": 105,
62 "UnsupportedSetField": 107,
63 "SetVlanId": 110,
64 "PopVlan": 120,
65 "PushVlanId": 130,
66 "Output": 1000,
67 "GotoTable": 1010,
David Bainbridge09c61672019-08-23 19:07:54 +000068 "WriteMetadata": 1015,
Zack Williamse940c7a2019-08-21 14:25:39 -070069 "ClearActions": 1020,
David Bainbridge09c61672019-08-23 19:07:54 +000070 "MeterId": 1030,
Zack Williamse940c7a2019-08-21 14:25:39 -070071 }
Hardik Windlass9361bb82022-03-23 05:58:48 +000072 DEFAULT_FLOWS_ORDER = "Id"
Zack Williamse940c7a2019-08-21 14:25:39 -070073)
74
75/*
76 * Construct a template format string based on the fields required by the
77 * results.
78 */
79func buildOutputFormat(fieldset model.FlowFieldFlag, ignore model.FlowFieldFlag) string {
80 want := fieldset & ^(ignore)
81 fields := make([]string, want.Count())
82 idx := 0
83 for _, flag := range model.AllFlowFieldFlags {
84 if want.IsSet(flag) {
85 fields[idx] = flag.String()
86 idx += 1
87 }
88 }
89 sort.Slice(fields, func(i, j int) bool {
90 return SORT_ORDER[fields[i]] < SORT_ORDER[fields[j]]
91 })
92 var b strings.Builder
93 b.WriteString("table")
94 first := true
95 for _, k := range fields {
96 if !first {
97 b.WriteString("\t")
98 }
99 first = false
100 b.WriteString("{{.")
101 b.WriteString(k)
102 b.WriteString("}}")
103 }
104 return b.String()
105}
106
Zack Williamse940c7a2019-08-21 14:25:39 -0700107func (options *FlowList) Execute(args []string) error {
108 if len(args) > 0 {
109 return fmt.Errorf("only a single argument 'DEVICE_ID' can be provided")
110 }
111
112 conn, err := NewConnection()
113 if err != nil {
114 return err
115 }
116 defer conn.Close()
117
Scott Baker9173ed82020-05-19 08:30:12 -0700118 client := voltha.NewVolthaServiceClient(conn)
Zack Williamse940c7a2019-08-21 14:25:39 -0700119
David K. Bainbridge9189c632021-03-26 21:52:21 +0000120 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().Grpc.Timeout)
Zack Williamse940c7a2019-08-21 14:25:39 -0700121 defer cancel()
122
Scott Baker9173ed82020-05-19 08:30:12 -0700123 id := voltha.ID{Id: string(options.Args.Id)}
124
125 var flows *openflow_13.Flows
126
127 switch options.Method {
128 case "device-flows":
129 flows, err = client.ListDeviceFlows(ctx, &id)
130 case "logical-device-flows":
131 flows, err = client.ListLogicalDeviceFlows(ctx, &id)
132 default:
133 Error.Fatalf("Unknown method name: '%s'", options.Method)
Zack Williamse940c7a2019-08-21 14:25:39 -0700134 }
135
Zack Williamse940c7a2019-08-21 14:25:39 -0700136 if err != nil {
137 return err
138 }
139
Scott Baker9173ed82020-05-19 08:30:12 -0700140 if toOutputType(options.OutputAs) == OUTPUT_TABLE && (flows == nil || len(flows.Items) == 0) {
Zack Williamse940c7a2019-08-21 14:25:39 -0700141 fmt.Println("*** NO FLOWS ***")
142 return nil
143 }
144
Scott Baker9173ed82020-05-19 08:30:12 -0700145 data := make([]model.Flow, len(flows.Items))
Zack Williamse940c7a2019-08-21 14:25:39 -0700146 var fieldset model.FlowFieldFlag
Scott Baker9173ed82020-05-19 08:30:12 -0700147 for i, item := range flows.Items {
Maninder045921e2020-09-29 16:46:02 +0530148 data[i].PopulateFromProto(item, options.HexId)
Zack Williamse940c7a2019-08-21 14:25:39 -0700149 fieldset |= data[i].Populated()
150 }
151
152 outputFormat := CharReplacer.Replace(options.Format)
153 if options.Quiet {
154 outputFormat = "{{.Id}}"
155 } else if outputFormat == "" {
David Bainbridgea6722342019-10-24 23:55:53 +0000156 outputFormat = GetCommandOptionWithDefault(options.Method, "format", buildOutputFormat(fieldset, model.FLOW_FIELD_STATS))
157 }
158
159 orderBy := options.OrderBy
160 if orderBy == "" {
Hardik Windlass9361bb82022-03-23 05:58:48 +0000161 orderBy = GetCommandOptionWithDefault(options.Method, "order", DEFAULT_FLOWS_ORDER)
Zack Williamse940c7a2019-08-21 14:25:39 -0700162 }
163
164 result := CommandResult{
165 Format: format.Format(outputFormat),
166 Filter: options.Filter,
David Bainbridgea6722342019-10-24 23:55:53 +0000167 OrderBy: orderBy,
Zack Williamse940c7a2019-08-21 14:25:39 -0700168 OutputAs: toOutputType(options.OutputAs),
169 NameLimit: options.NameLimit,
170 Data: data,
171 }
172 GenerateOutput(&result)
173
174 return nil
175}