blob: efe09f2117789a885ab7ac22fbe9033677cddc46 [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
31 Args struct {
32 Id string `positional-arg-name:"DEVICE_ID" required:"yes"`
33 } `positional-args:"yes"`
34
35 Method string
36}
37
38type FlowOpts struct {
39 List FlowList `command:"list"`
40}
41
42var (
Zack Williamse940c7a2019-08-21 14:25:39 -070043 // Used to sort the table colums in a consistent order
44 SORT_ORDER = map[string]uint16{
45 "Id": 0,
46 "TableId": 10,
47 "Priority": 20,
48 "Cookie": 30,
49 "UnsupportedMatch": 35,
50 "InPort": 40,
51 "VlanId": 50,
52 "VlanPcp": 55,
53 "EthType": 60,
54 "IpProto": 70,
55 "UdpSrc": 80,
56 "UdpDst": 90,
57 "Metadata": 100,
58 "TunnelId": 101,
59 "UnsupportedInstruction": 102,
60 "UnsupportedAction": 105,
61 "UnsupportedSetField": 107,
62 "SetVlanId": 110,
63 "PopVlan": 120,
64 "PushVlanId": 130,
65 "Output": 1000,
66 "GotoTable": 1010,
David Bainbridge09c61672019-08-23 19:07:54 +000067 "WriteMetadata": 1015,
Zack Williamse940c7a2019-08-21 14:25:39 -070068 "ClearActions": 1020,
David Bainbridge09c61672019-08-23 19:07:54 +000069 "MeterId": 1030,
Zack Williamse940c7a2019-08-21 14:25:39 -070070 }
71)
72
73/*
74 * Construct a template format string based on the fields required by the
75 * results.
76 */
77func buildOutputFormat(fieldset model.FlowFieldFlag, ignore model.FlowFieldFlag) string {
78 want := fieldset & ^(ignore)
79 fields := make([]string, want.Count())
80 idx := 0
81 for _, flag := range model.AllFlowFieldFlags {
82 if want.IsSet(flag) {
83 fields[idx] = flag.String()
84 idx += 1
85 }
86 }
87 sort.Slice(fields, func(i, j int) bool {
88 return SORT_ORDER[fields[i]] < SORT_ORDER[fields[j]]
89 })
90 var b strings.Builder
91 b.WriteString("table")
92 first := true
93 for _, k := range fields {
94 if !first {
95 b.WriteString("\t")
96 }
97 first = false
98 b.WriteString("{{.")
99 b.WriteString(k)
100 b.WriteString("}}")
101 }
102 return b.String()
103}
104
Zack Williamse940c7a2019-08-21 14:25:39 -0700105func (options *FlowList) Execute(args []string) error {
106 if len(args) > 0 {
107 return fmt.Errorf("only a single argument 'DEVICE_ID' can be provided")
108 }
109
110 conn, err := NewConnection()
111 if err != nil {
112 return err
113 }
114 defer conn.Close()
115
Scott Baker9173ed82020-05-19 08:30:12 -0700116 client := voltha.NewVolthaServiceClient(conn)
Zack Williamse940c7a2019-08-21 14:25:39 -0700117
118 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
119 defer cancel()
120
Scott Baker9173ed82020-05-19 08:30:12 -0700121 id := voltha.ID{Id: string(options.Args.Id)}
122
123 var flows *openflow_13.Flows
124
125 switch options.Method {
126 case "device-flows":
127 flows, err = client.ListDeviceFlows(ctx, &id)
128 case "logical-device-flows":
129 flows, err = client.ListLogicalDeviceFlows(ctx, &id)
130 default:
131 Error.Fatalf("Unknown method name: '%s'", options.Method)
Zack Williamse940c7a2019-08-21 14:25:39 -0700132 }
133
Zack Williamse940c7a2019-08-21 14:25:39 -0700134 if err != nil {
135 return err
136 }
137
Scott Baker9173ed82020-05-19 08:30:12 -0700138 if toOutputType(options.OutputAs) == OUTPUT_TABLE && (flows == nil || len(flows.Items) == 0) {
Zack Williamse940c7a2019-08-21 14:25:39 -0700139 fmt.Println("*** NO FLOWS ***")
140 return nil
141 }
142
Scott Baker9173ed82020-05-19 08:30:12 -0700143 data := make([]model.Flow, len(flows.Items))
Zack Williamse940c7a2019-08-21 14:25:39 -0700144 var fieldset model.FlowFieldFlag
Scott Baker9173ed82020-05-19 08:30:12 -0700145 for i, item := range flows.Items {
146 data[i].PopulateFromProto(item)
Zack Williamse940c7a2019-08-21 14:25:39 -0700147 fieldset |= data[i].Populated()
148 }
149
150 outputFormat := CharReplacer.Replace(options.Format)
151 if options.Quiet {
152 outputFormat = "{{.Id}}"
153 } else if outputFormat == "" {
David Bainbridgea6722342019-10-24 23:55:53 +0000154 outputFormat = GetCommandOptionWithDefault(options.Method, "format", buildOutputFormat(fieldset, model.FLOW_FIELD_STATS))
155 }
156
157 orderBy := options.OrderBy
158 if orderBy == "" {
159 orderBy = GetCommandOptionWithDefault(options.Method, "order", "")
Zack Williamse940c7a2019-08-21 14:25:39 -0700160 }
161
162 result := CommandResult{
163 Format: format.Format(outputFormat),
164 Filter: options.Filter,
David Bainbridgea6722342019-10-24 23:55:53 +0000165 OrderBy: orderBy,
Zack Williamse940c7a2019-08-21 14:25:39 -0700166 OutputAs: toOutputType(options.OutputAs),
167 NameLimit: options.NameLimit,
168 Data: data,
169 }
170 GenerateOutput(&result)
171
172 return nil
173}