blob: 1e8ec6263c436b35ba5d7668d57d11c3f8ffe7f6 [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"
Scott Baker9173ed82020-05-19 08:30:12 -070023 "github.com/opencord/voltha-protos/v3/go/openflow_13"
24 "github.com/opencord/voltha-protos/v3/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 }
72)
73
74/*
75 * Construct a template format string based on the fields required by the
76 * results.
77 */
78func buildOutputFormat(fieldset model.FlowFieldFlag, ignore model.FlowFieldFlag) string {
79 want := fieldset & ^(ignore)
80 fields := make([]string, want.Count())
81 idx := 0
82 for _, flag := range model.AllFlowFieldFlags {
83 if want.IsSet(flag) {
84 fields[idx] = flag.String()
85 idx += 1
86 }
87 }
88 sort.Slice(fields, func(i, j int) bool {
89 return SORT_ORDER[fields[i]] < SORT_ORDER[fields[j]]
90 })
91 var b strings.Builder
92 b.WriteString("table")
93 first := true
94 for _, k := range fields {
95 if !first {
96 b.WriteString("\t")
97 }
98 first = false
99 b.WriteString("{{.")
100 b.WriteString(k)
101 b.WriteString("}}")
102 }
103 return b.String()
104}
105
Zack Williamse940c7a2019-08-21 14:25:39 -0700106func (options *FlowList) Execute(args []string) error {
107 if len(args) > 0 {
108 return fmt.Errorf("only a single argument 'DEVICE_ID' can be provided")
109 }
110
111 conn, err := NewConnection()
112 if err != nil {
113 return err
114 }
115 defer conn.Close()
116
Scott Baker9173ed82020-05-19 08:30:12 -0700117 client := voltha.NewVolthaServiceClient(conn)
Zack Williamse940c7a2019-08-21 14:25:39 -0700118
119 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
120 defer cancel()
121
Scott Baker9173ed82020-05-19 08:30:12 -0700122 id := voltha.ID{Id: string(options.Args.Id)}
123
124 var flows *openflow_13.Flows
125
126 switch options.Method {
127 case "device-flows":
128 flows, err = client.ListDeviceFlows(ctx, &id)
129 case "logical-device-flows":
130 flows, err = client.ListLogicalDeviceFlows(ctx, &id)
131 default:
132 Error.Fatalf("Unknown method name: '%s'", options.Method)
Zack Williamse940c7a2019-08-21 14:25:39 -0700133 }
134
Zack Williamse940c7a2019-08-21 14:25:39 -0700135 if err != nil {
136 return err
137 }
138
Scott Baker9173ed82020-05-19 08:30:12 -0700139 if toOutputType(options.OutputAs) == OUTPUT_TABLE && (flows == nil || len(flows.Items) == 0) {
Zack Williamse940c7a2019-08-21 14:25:39 -0700140 fmt.Println("*** NO FLOWS ***")
141 return nil
142 }
143
Scott Baker9173ed82020-05-19 08:30:12 -0700144 data := make([]model.Flow, len(flows.Items))
Zack Williamse940c7a2019-08-21 14:25:39 -0700145 var fieldset model.FlowFieldFlag
Scott Baker9173ed82020-05-19 08:30:12 -0700146 for i, item := range flows.Items {
Maninder045921e2020-09-29 16:46:02 +0530147 data[i].PopulateFromProto(item, options.HexId)
Zack Williamse940c7a2019-08-21 14:25:39 -0700148 fieldset |= data[i].Populated()
149 }
150
151 outputFormat := CharReplacer.Replace(options.Format)
152 if options.Quiet {
153 outputFormat = "{{.Id}}"
154 } else if outputFormat == "" {
David Bainbridgea6722342019-10-24 23:55:53 +0000155 outputFormat = GetCommandOptionWithDefault(options.Method, "format", buildOutputFormat(fieldset, model.FLOW_FIELD_STATS))
156 }
157
158 orderBy := options.OrderBy
159 if orderBy == "" {
160 orderBy = GetCommandOptionWithDefault(options.Method, "order", "")
Zack Williamse940c7a2019-08-21 14:25:39 -0700161 }
162
163 result := CommandResult{
164 Format: format.Format(outputFormat),
165 Filter: options.Filter,
David Bainbridgea6722342019-10-24 23:55:53 +0000166 OrderBy: orderBy,
Zack Williamse940c7a2019-08-21 14:25:39 -0700167 OutputAs: toOutputType(options.OutputAs),
168 NameLimit: options.NameLimit,
169 Data: data,
170 }
171 GenerateOutput(&result)
172
173 return nil
174}