Skip to content

Commit 558cda6

Browse files
authored
Client side code for GNOI.System.SetPackage (#358)
#### Why I did it Supports system.SetPackage, see sonic-net/SONiC#1906 This PR also improve ease of use for client by not using (in this `System.SetPackage` specifically) the generic `-jsonin` flag where we pass a json string, in favor of a more readable and verifiable flags like `-package_filename` and `-package_url` . #### How I did it Client side code for system.SetPackage. #### How to verify it On physical switches: ``` admin@switch:~$ docker exec gnmi gnoi_client -target 127.0.0.1:50052 -logtostderr -insecure -module System -rpc SetPackage System SetPackage Error validating flags: missing -package_filename admin@switch:~$ docker exec gnmi gnoi_client -target 127.0.0.1:50052 -logtostderr -insecure -module System -rpc SetPackage -package_filename=filename System SetPackage Error validating flags: missing -package_version admin@switch:~$ docker exec gnmi gnoi_client -target 127.0.0.1:50052 -logtostderr -insecure -module System -rpc SetPackage -package_filename=filename -package_version=abc -package_activate=true -package_url=abc.com System SetPackage Error receiving response: rpc error: code = Unimplemented desc = ``` Server already have placeholder code so the unimplemented error is expected.
1 parent e06ff6c commit 558cda6

File tree

7 files changed

+646
-5
lines changed

7 files changed

+646
-5
lines changed

Makefile

+4-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ $(GO_DEPS): go.mod $(PATCHES) swsscommon_wrap $(GNOI_YANG)
7070
$(GO) mod download github.com/google/[email protected]
7171
cp -r $(GOPATH)/pkg/mod/github.com/google/[email protected]/* vendor/github.com/google/gnxi/
7272

73-
# Apply patch from sonic-mgmt-common, ignore glog.patch because glog version changed
73+
# Apply patch from sonic-mgmt-common, ignore glog.patch because glog version changed
7474
sed -i 's/patch -d $${DEST_DIR}\/github.com\/golang\/glog/\#patch -d $${DEST_DIR}\/github.com\/golang\/glog/g' $(MGMT_COMMON_DIR)/patches/apply.sh
7575
$(MGMT_COMMON_DIR)/patches/apply.sh vendor
7676
sed -i 's/#patch -d $${DEST_DIR}\/github.com\/golang\/glog/patch -d $${DEST_DIR}\/github.com\/golang\/glog/g' $(MGMT_COMMON_DIR)/patches/apply.sh
@@ -106,7 +106,7 @@ endif
106106
$(GO) install -mod=vendor github.com/sonic-net/sonic-gnmi/gnmi_dump
107107
$(GO) install -mod=vendor github.com/sonic-net/sonic-gnmi/build/gnoi_yang/client/gnoi_openconfig_client
108108
$(GO) install -mod=vendor github.com/sonic-net/sonic-gnmi/build/gnoi_yang/client/gnoi_sonic_client
109-
109+
110110
endif
111111

112112
# download and apply patch for gnmi client, which will break advancetls
@@ -216,11 +216,12 @@ endif
216216
sudo CGO_LDFLAGS="$(CGO_LDFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" $(GO) test -race -coverprofile=coverage-data.txt -covermode=atomic -mod=vendor -v github.com/sonic-net/sonic-gnmi/sonic_data_client
217217
sudo CGO_LDFLAGS="$(CGO_LDFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" $(GO) test -race -coverprofile=coverage-dbus.txt -covermode=atomic -mod=vendor -v github.com/sonic-net/sonic-gnmi/sonic_service_client
218218
sudo CGO_LDFLAGS="$(CGO_LDFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" $(TESTENV) $(GO) test -race -coverprofile=coverage-translutils.txt -covermode=atomic -mod=vendor -v github.com/sonic-net/sonic-gnmi/transl_utils
219+
sudo CGO_LDFLAGS="$(CGO_LDFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" $(TESTENV) $(GO) test -race -coverprofile=coverage-gnoi-client-system.txt -covermode=atomic -mod=vendor -v github.com/sonic-net/sonic-gnmi/gnoi_client/system
219220
$(GO) install github.com/axw/gocov/[email protected]
220221
$(GO) install github.com/AlekSi/gocov-xml@latest
221222
$(GO) mod vendor
222223
gocov convert coverage-*.txt | gocov-xml -source $(shell pwd) > coverage.xml
223-
rm -rf coverage-*.txt
224+
rm -rf coverage-*.txt
224225

225226
check_memleak: $(DBCONFG) $(ENVFILE)
226227
sudo CGO_LDFLAGS="$(MEMCHECK_CGO_LDFLAGS)" CGO_CXXFLAGS="$(MEMCHECK_CGO_CXXFLAGS)" $(GO) test -coverprofile=coverage-telemetry.txt -covermode=atomic -mod=vendor $(MEMCHECK_FLAGS) -v github.com/sonic-net/sonic-gnmi/telemetry

gnoi_client/gnoi_client.go

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ func main() {
4343
system.RebootStatus(conn, ctx)
4444
case "KillProcess":
4545
system.KillProcess(conn, ctx)
46+
case "SetPackage":
47+
system.SetPackage(conn, ctx)
4648
default:
4749
panic("Invalid RPC Name")
4850
}

gnoi_client/system/set_package.go

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package system
2+
3+
import (
4+
"context"
5+
"flag"
6+
"fmt"
7+
8+
"github.com/openconfig/gnoi/common"
9+
"github.com/openconfig/gnoi/system"
10+
"github.com/sonic-net/sonic-gnmi/gnoi_client/utils"
11+
"google.golang.org/grpc"
12+
)
13+
14+
var (
15+
// Flags for System.SetPackage
16+
filename = flag.String("package_filename", "", "Destination path and filename of the package")
17+
version = flag.String("package_version", "", "Version of the package, i.e. vendor internal name")
18+
url = flag.String("package_url", "", "URL to download the package from")
19+
activate = flag.Bool("package_activate", true, "Whether to activate the package after setting it")
20+
)
21+
22+
// newSystemClient is a package-level variable that returns a new system.SystemClient.
23+
// We define it here so that unit tests can replace it with a mock constructor if needed.
24+
var newSystemClient = func(conn *grpc.ClientConn) system.SystemClient {
25+
return system.NewSystemClient(conn)
26+
}
27+
28+
// SetPackage is the main entry point. It validates flags, creates the SystemClient,
29+
// and then calls setPackageClient to perform the actual SetPackage gRPC flow.
30+
func SetPackage(conn *grpc.ClientConn, ctx context.Context) {
31+
fmt.Println("System SetPackage")
32+
33+
// Attach user credentials if needed.
34+
ctx = utils.SetUserCreds(ctx)
35+
36+
// Validate the flags before proceeding.
37+
err := validateFlags()
38+
if err != nil {
39+
fmt.Println("Error validating flags:", err)
40+
return
41+
}
42+
43+
// Create a new gNOI SystemClient using the function defined above.
44+
sc := newSystemClient(conn)
45+
46+
// Call the helper that implements the SetPackage logic (sending requests, closing, etc.).
47+
if err := setPackageClient(sc, ctx); err != nil {
48+
fmt.Println("Error during SetPackage:", err)
49+
}
50+
}
51+
52+
// setPackageClient contains the core gRPC calls to send the package request and
53+
// receive the response. We separate it out for easier testing and mocking.
54+
func setPackageClient(sc system.SystemClient, ctx context.Context) error {
55+
// Prepare the remote download info.
56+
download := &common.RemoteDownload{
57+
Path: *url,
58+
}
59+
60+
// Build the package with flags.
61+
pkg := &system.Package{
62+
Filename: *filename,
63+
Version: *version,
64+
Activate: *activate,
65+
RemoteDownload: download,
66+
}
67+
68+
// The gNOI SetPackageRequest can contain different request types, but we only
69+
// use the "Package" request type here.
70+
req := &system.SetPackageRequest{
71+
Request: &system.SetPackageRequest_Package{
72+
Package: pkg,
73+
},
74+
}
75+
76+
// Open a streaming RPC.
77+
stream, err := sc.SetPackage(ctx)
78+
if err != nil {
79+
return fmt.Errorf("error creating stream: %v", err)
80+
}
81+
82+
// Send the package information.
83+
if err := stream.Send(req); err != nil {
84+
return fmt.Errorf("error sending package request: %v", err)
85+
}
86+
87+
// Close the send direction of the stream. The device should download the
88+
// package itself, so we are not sending direct data or checksums here.
89+
if err := stream.CloseSend(); err != nil {
90+
return fmt.Errorf("error closing send direction: %v", err)
91+
}
92+
93+
// Receive the final response from the device.
94+
resp, err := stream.CloseAndRecv()
95+
if err != nil {
96+
return fmt.Errorf("error receiving response: %v", err)
97+
}
98+
99+
// For demonstration purposes, we simply print the response.
100+
fmt.Println(resp)
101+
return nil
102+
}
103+
104+
// validateFlags ensures all required flags are set correctly before we proceed.
105+
func validateFlags() error {
106+
if *filename == "" {
107+
return fmt.Errorf("missing -package_filename: Destination path and filename of the package is required for the SetPackage operation")
108+
}
109+
if *version == "" {
110+
return fmt.Errorf("missing -package_version: Version of the package is required for the SetPackage operation")
111+
}
112+
if *url == "" {
113+
return fmt.Errorf("missing -package_url: URL to download the package from is required for the SetPackage operation. Direct transfer is not supported yet")
114+
}
115+
if !*activate {
116+
// TODO: Currently, installing the image will always set it to default.
117+
return fmt.Errorf("-package_activate=false is not yet supported: The package will always be activated after setting it")
118+
}
119+
return nil
120+
}

0 commit comments

Comments
 (0)