Add maxCallRecvMsgSize parameter and read it from the configuration
GRPC client max msg receive size
file. It allows to set if the incoming message is bigger than the
default 4K size.
Change-Id: I5b5113264066b84b5b055faf7d4bc02a3f11ec89
diff --git a/README.md b/README.md
index 81a9257..1c195ff 100644
--- a/README.md
+++ b/README.md
@@ -67,6 +67,7 @@
-8, --k8sconfig=FILE Location of Kubernetes config file [$KUBECONFIG]
--kvstoretimeout=DURATION timeout for calls to KV store [$KVSTORE_TIMEOUT]
-o, --command-options=FILE Location of command options default configuration file [$VOLTCTL_COMMAND_OPTIONS]
+ -m, --maxcallrecvmsgsize=SIZE Max GRPC Client request size limit in bytes (eg: 4MB)
Help Options:
-h, --help Show this help message
@@ -226,3 +227,19 @@
adapter-open-olt github.com/opencord/voltha-openolt-adapter/internal/pkg/resourcemanager
adapter-open-olt main
```
+
+### Configuring the message size
+
+When you run VOLTHA with a high number of OLTs/ONUs is possible that the gRPC response exceeds the default 4MB in size.
+This will be addressed on the server side in a future release, but you have the option to exceed the allowed response size
+using the `-m, --maxcallrecvmsgsize=SIZE` option.
+
+The common error message when this happens is:
+```shell
+ERROR: RESOURCEEXHAUSTED: grpc: received message larger than max (4241141 vs. 4194304)
+```
+
+If that happens, retry the command passing the -m option, eg:
+```shell
+$ voltctl device list -m 8M
+```
\ No newline at end of file
diff --git a/go.sum b/go.sum
index 88fcc0d..2729387 100644
--- a/go.sum
+++ b/go.sum
@@ -12,6 +12,7 @@
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
diff --git a/internal/pkg/commands/command.go b/internal/pkg/commands/command.go
index 9b2afd0..d5a2785 100644
--- a/internal/pkg/commands/command.go
+++ b/internal/pkg/commands/command.go
@@ -19,6 +19,17 @@
"encoding/json"
"errors"
"fmt"
+ "io/ioutil"
+ "log"
+ "net"
+ "os"
+ "path/filepath"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"github.com/opencord/voltctl/pkg/filter"
@@ -26,15 +37,6 @@
"github.com/opencord/voltctl/pkg/order"
"google.golang.org/grpc"
"gopkg.in/yaml.v2"
- "io/ioutil"
- "log"
- "net"
- "os"
- "path/filepath"
- "reflect"
- "strconv"
- "strings"
- "time"
)
type OutputType uint8
@@ -55,11 +57,13 @@
defaultKvPort = 2379
defaultKvTimeout = time.Second * 5
- defaultGrpcTimeout = time.Minute * 5
+ defaultGrpcTimeout = time.Minute * 5
+ defaultGrpcMaxCallRecvMsgSize = "4MB"
)
type GrpcConfigSpec struct {
- Timeout time.Duration `yaml:"timeout"`
+ Timeout time.Duration `yaml:"timeout"`
+ MaxCallRecvMsgSize string `yaml:"maxCallRecvMsgSize"`
}
type KvStoreConfigSpec struct {
@@ -109,7 +113,8 @@
UseTls: false,
},
Grpc: GrpcConfigSpec{
- Timeout: defaultGrpcTimeout,
+ Timeout: defaultGrpcTimeout,
+ MaxCallRecvMsgSize: defaultGrpcMaxCallRecvMsgSize,
},
KvStoreConfig: KvStoreConfigSpec{
Timeout: defaultKvTimeout,
@@ -125,16 +130,17 @@
KvStore string `short:"e" long:"kvstore" env:"KVSTORE" value-name:"SERVER:PORT" description:"IP/Host and port of KV store (etcd)"`
// nolint: staticcheck
- Debug bool `short:"d" long:"debug" description:"Enable debug mode"`
- Timeout string `short:"t" long:"timeout" description:"API call timeout duration" value-name:"DURATION" default:""`
- UseTLS bool `long:"tls" description:"Use TLS"`
- CACert string `long:"tlscacert" value-name:"CA_CERT_FILE" description:"Trust certs signed only by this CA"`
- Cert string `long:"tlscert" value-name:"CERT_FILE" description:"Path to TLS vertificate file"`
- Key string `long:"tlskey" value-name:"KEY_FILE" description:"Path to TLS key file"`
- Verify bool `long:"tlsverify" description:"Use TLS and verify the remote"`
- K8sConfig string `short:"8" long:"k8sconfig" env:"KUBECONFIG" value-name:"FILE" default:"" description:"Location of Kubernetes config file"`
- KvStoreTimeout string `long:"kvstoretimeout" env:"KVSTORE_TIMEOUT" value-name:"DURATION" default:"" description:"timeout for calls to KV store"`
- CommandOptions string `short:"o" long:"command-options" env:"VOLTCTL_COMMAND_OPTIONS" value-name:"FILE" default:"" description:"Location of command options default configuration file"`
+ Debug bool `short:"d" long:"debug" description:"Enable debug mode"`
+ Timeout string `short:"t" long:"timeout" description:"API call timeout duration" value-name:"DURATION" default:""`
+ UseTLS bool `long:"tls" description:"Use TLS"`
+ CACert string `long:"tlscacert" value-name:"CA_CERT_FILE" description:"Trust certs signed only by this CA"`
+ Cert string `long:"tlscert" value-name:"CERT_FILE" description:"Path to TLS vertificate file"`
+ Key string `long:"tlskey" value-name:"KEY_FILE" description:"Path to TLS key file"`
+ Verify bool `long:"tlsverify" description:"Use TLS and verify the remote"`
+ K8sConfig string `short:"8" long:"k8sconfig" env:"KUBECONFIG" value-name:"FILE" default:"" description:"Location of Kubernetes config file"`
+ KvStoreTimeout string `long:"kvstoretimeout" env:"KVSTORE_TIMEOUT" value-name:"DURATION" default:"" description:"timeout for calls to KV store"`
+ CommandOptions string `short:"o" long:"command-options" env:"VOLTCTL_COMMAND_OPTIONS" value-name:"FILE" default:"" description:"Location of command options default configuration file"`
+ MaxCallRecvMsgSize string `short:"m" long:"maxcallrecvmsgsize" description:"Max GRPC Client request size limit in bytes (eg: 4MB)" value-name:"SIZE" default:"4M"`
}
Debug = log.New(os.Stdout, "DEBUG: ", 0)
@@ -227,6 +233,36 @@
return defaultValue
}
+var sizeParser = regexp.MustCompile(`^([0-9]+)([PETGMK]?)I?B?$`)
+
+func parseSize(size string) (uint64, error) {
+
+ parts := sizeParser.FindAllStringSubmatch(strings.ToUpper(size), -1)
+ if len(parts) == 0 {
+ return 0, fmt.Errorf("size: invalid size '%s'", size)
+ }
+ value, err := strconv.ParseUint(parts[0][1], 10, 64)
+ if err != nil {
+ return 0, fmt.Errorf("size: invalid size '%s'", size)
+ }
+ switch parts[0][2] {
+ case "E":
+ value = value * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
+ case "P":
+ value = value * 1024 * 1024 * 1024 * 1024 * 1024
+ case "T":
+ value = value * 1024 * 1024 * 1024 * 1024
+ case "G":
+ value = value * 1024 * 1024 * 1024
+ case "M":
+ value = value * 1024 * 1024
+ case "K":
+ value = value * 1024
+ default:
+ }
+ return value, nil
+}
+
func ProcessGlobalOptions() {
if len(GlobalOptions.Config) == 0 {
home, err := os.UserHomeDir()
@@ -298,6 +334,10 @@
GlobalConfig.Grpc.Timeout = timeout
}
+ if GlobalOptions.MaxCallRecvMsgSize != "" {
+ GlobalConfig.Grpc.MaxCallRecvMsgSize = GlobalOptions.MaxCallRecvMsgSize
+ }
+
// If a k8s cert/key were not specified, then attempt to read it from
// any $HOME/.kube/config if it exists
if len(GlobalOptions.K8sConfig) == 0 {
@@ -333,7 +373,14 @@
func NewConnection() (*grpc.ClientConn, error) {
ProcessGlobalOptions()
- return grpc.Dial(GlobalConfig.Server, grpc.WithInsecure())
+
+ // convert grpc.msgSize into bytes
+ n, err := parseSize(GlobalConfig.Grpc.MaxCallRecvMsgSize)
+ if err != nil {
+ Error.Fatalf("Cannot convert msgSize %s to bytes", GlobalConfig.Grpc.MaxCallRecvMsgSize)
+ }
+
+ return grpc.Dial(GlobalConfig.Server, grpc.WithInsecure(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(n))))
}
func ConvertJsonProtobufArray(data_in interface{}) (string, error) {
diff --git a/internal/pkg/commands/command_test.go b/internal/pkg/commands/command_test.go
index 8dd449b..68dd0a4 100644
--- a/internal/pkg/commands/command_test.go
+++ b/internal/pkg/commands/command_test.go
@@ -90,3 +90,24 @@
})
}
}
+
+func TestParseSize(t *testing.T) {
+ var res uint64
+ var err error
+
+ res, err = parseSize("8M")
+ assert.Nil(t, err)
+ assert.Equal(t, uint64(8388608), res)
+
+ res, err = parseSize("8MB")
+ assert.Nil(t, err)
+ assert.Equal(t, uint64(8388608), res)
+
+ res, err = parseSize("8MiB")
+ assert.Nil(t, err)
+ assert.Equal(t, uint64(8388608), res)
+
+ res, err = parseSize("foobar")
+ assert.NotNil(t, err)
+ assert.Equal(t, uint64(0), res)
+}
diff --git a/voltctl.config b/voltctl.config
index f07a75b..857219a 100644
--- a/voltctl.config
+++ b/voltctl.config
@@ -9,5 +9,6 @@
verify: ""
grpc:
timeout: 5m0s
+ maxCallRecvMsgSize: 4MB
kvstoreconfig:
timeout: 5s