1 // Copyright 2022 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Frontend client
16 #include "frontend/frontend_client.h"
17
18 #include <google/protobuf/util/json_util.h>
19 #include <grpcpp/support/status.h>
20
21 #include <chrono>
22 #include <cstdint>
23 #include <memory>
24 #include <string>
25
26 #include "google/protobuf/empty.pb.h"
27 #include "grpcpp/create_channel.h"
28 #include "grpcpp/security/credentials.h"
29 #include "grpcpp/support/status_code_enum.h"
30 #include "netsim-cli/src/ffi.rs.h"
31 #include "netsim/frontend.grpc.pb.h"
32 #include "netsim/frontend.pb.h"
33 #include "netsim/model.pb.h"
34 #include "util/log.h"
35 #include "util/os_utils.h"
36
37 namespace netsim {
38 namespace frontend {
39 namespace {
40 const std::chrono::duration kConnectionDeadline = std::chrono::seconds(1);
41
NewFrontendStub(std::string server)42 std::unique_ptr<frontend::FrontendService::Stub> NewFrontendStub(
43 std::string server) {
44 if (server == "") {
45 return {};
46 }
47 std::shared_ptr<grpc::Channel> channel =
48 grpc::CreateChannel(server, grpc::InsecureChannelCredentials());
49
50 auto deadline = std::chrono::system_clock::now() + kConnectionDeadline;
51 if (!channel->WaitForConnected(deadline)) {
52 BtsLogWarn("Frontend gRPC channel not connected");
53 return nullptr;
54 }
55
56 return frontend::FrontendService::NewStub(channel);
57 }
58
59 // A synchronous client for the netsim frontend service.
60 class FrontendClientImpl : public FrontendClient {
61 public:
FrontendClientImpl(std::unique_ptr<frontend::FrontendService::Stub> stub)62 FrontendClientImpl(std::unique_ptr<frontend::FrontendService::Stub> stub)
63 : stub_(std::move(stub)) {}
64
make_result(const grpc::Status & status,const google::protobuf::Message & message) const65 std::unique_ptr<ClientResult> make_result(
66 const grpc::Status &status,
67 const google::protobuf::Message &message) const {
68 std::vector<unsigned char> message_vec(message.ByteSizeLong());
69 message.SerializeToArray(message_vec.data(), message_vec.size());
70 if (!status.ok()) {
71 return std::make_unique<ClientResult>(false, status.error_message(),
72 message_vec);
73 }
74 return std::make_unique<ClientResult>(true, "", message_vec);
75 }
76
77 // Gets the version of the network simulator service.
GetVersion() const78 std::unique_ptr<ClientResult> GetVersion() const override {
79 frontend::VersionResponse response;
80 grpc::ClientContext context_;
81 auto status = stub_->GetVersion(&context_, {}, &response);
82 return make_result(status, response);
83 }
84
85 // Gets the list of device information
ListDevice() const86 std::unique_ptr<ClientResult> ListDevice() const override {
87 frontend::ListDeviceResponse response;
88 grpc::ClientContext context_;
89 auto status = stub_->ListDevice(&context_, {}, &response);
90 return make_result(status, response);
91 }
92
Reset() const93 std::unique_ptr<ClientResult> Reset() const override {
94 grpc::ClientContext context_;
95 google::protobuf::Empty response;
96 auto status = stub_->Reset(&context_, {}, &response);
97 return make_result(status, response);
98 }
99
CreateDevice(rust::Vec<::rust::u8> const & request_byte_vec) const100 std::unique_ptr<ClientResult> CreateDevice(
101 rust::Vec<::rust::u8> const &request_byte_vec) const {
102 frontend::CreateDeviceResponse response;
103 grpc::ClientContext context_;
104 frontend::CreateDeviceRequest request;
105 if (!request.ParseFromArray(request_byte_vec.data(),
106 request_byte_vec.size())) {
107 return make_result(
108 grpc::Status(
109 grpc::StatusCode::INVALID_ARGUMENT,
110 "Error parsing CreateDevice request protobuf. request size:" +
111 std::to_string(request_byte_vec.size())),
112 response);
113 }
114 auto status = stub_->CreateDevice(&context_, request, &response);
115 return make_result(status, response);
116 }
117
118 // Patchs the information of the device
PatchDevice(rust::Vec<::rust::u8> const & request_byte_vec) const119 std::unique_ptr<ClientResult> PatchDevice(
120 rust::Vec<::rust::u8> const &request_byte_vec) const override {
121 google::protobuf::Empty response;
122 grpc::ClientContext context_;
123 frontend::PatchDeviceRequest request;
124 if (!request.ParseFromArray(request_byte_vec.data(),
125 request_byte_vec.size())) {
126 return make_result(
127 grpc::Status(
128 grpc::StatusCode::INVALID_ARGUMENT,
129 "Error parsing PatchDevice request protobuf. request size:" +
130 std::to_string(request_byte_vec.size())),
131 response);
132 };
133 auto status = stub_->PatchDevice(&context_, request, &response);
134 return make_result(status, response);
135 }
136
DeleteChip(rust::Vec<::rust::u8> const & request_byte_vec) const137 std::unique_ptr<ClientResult> DeleteChip(
138 rust::Vec<::rust::u8> const &request_byte_vec) const {
139 google::protobuf::Empty response;
140 grpc::ClientContext context_;
141 frontend::DeleteChipRequest request;
142 if (!request.ParseFromArray(request_byte_vec.data(),
143 request_byte_vec.size())) {
144 return make_result(
145 grpc::Status(
146 grpc::StatusCode::INVALID_ARGUMENT,
147 "Error parsing DeleteChip request protobuf. request size:" +
148 std::to_string(request_byte_vec.size())),
149 response);
150 }
151 auto status = stub_->DeleteChip(&context_, request, &response);
152 return make_result(status, response);
153 }
154
155 // Get the list of Capture information
ListCapture() const156 std::unique_ptr<ClientResult> ListCapture() const override {
157 frontend::ListCaptureResponse response;
158 grpc::ClientContext context_;
159 auto status = stub_->ListCapture(&context_, {}, &response);
160 return make_result(status, response);
161 }
162
163 // Patch the Capture
PatchCapture(rust::Vec<::rust::u8> const & request_byte_vec) const164 std::unique_ptr<ClientResult> PatchCapture(
165 rust::Vec<::rust::u8> const &request_byte_vec) const override {
166 google::protobuf::Empty response;
167 grpc::ClientContext context_;
168 frontend::PatchCaptureRequest request;
169 if (!request.ParseFromArray(request_byte_vec.data(),
170 request_byte_vec.size())) {
171 return make_result(
172 grpc::Status(
173 grpc::StatusCode::INVALID_ARGUMENT,
174 "Error parsing PatchCapture request protobuf. request size:" +
175 std::to_string(request_byte_vec.size())),
176 response);
177 };
178 auto status = stub_->PatchCapture(&context_, request, &response);
179 return make_result(status, response);
180 }
181
182 // Download capture file by using ClientResponseReader to handle streaming
183 // grpc
GetCapture(rust::Vec<::rust::u8> const & request_byte_vec,ClientResponseReader const & client_reader) const184 std::unique_ptr<ClientResult> GetCapture(
185 rust::Vec<::rust::u8> const &request_byte_vec,
186 ClientResponseReader const &client_reader) const override {
187 grpc::ClientContext context_;
188 frontend::GetCaptureRequest request;
189 if (!request.ParseFromArray(request_byte_vec.data(),
190 request_byte_vec.size())) {
191 return make_result(
192 grpc::Status(
193 grpc::StatusCode::INVALID_ARGUMENT,
194 "Error parsing GetCapture request protobuf. request size:" +
195 std::to_string(request_byte_vec.size())),
196 google::protobuf::Empty());
197 };
198 auto reader = stub_->GetCapture(&context_, request);
199 frontend::GetCaptureResponse chunk;
200 // Read every available chunks from grpc reader
201 while (reader->Read(&chunk)) {
202 // Using a mutable protobuf here so the move iterator can move
203 // the capture stream without copying.
204 auto mut_stream = chunk.mutable_capture_stream();
205 auto bytes =
206 std::vector<uint8_t>(std::make_move_iterator(mut_stream->begin()),
207 std::make_move_iterator(mut_stream->end()));
208 client_reader.handle_chunk(
209 rust::Slice<const uint8_t>{bytes.data(), bytes.size()});
210 }
211 auto status = reader->Finish();
212 return make_result(status, google::protobuf::Empty());
213 }
214
215 // Helper function to redirect to the correct Grpc call
SendGrpc(frontend::GrpcMethod const & grpc_method,rust::Vec<::rust::u8> const & request_byte_vec) const216 std::unique_ptr<ClientResult> SendGrpc(
217 frontend::GrpcMethod const &grpc_method,
218 rust::Vec<::rust::u8> const &request_byte_vec) const override {
219 switch (grpc_method) {
220 case frontend::GrpcMethod::GetVersion:
221 return GetVersion();
222 case frontend::GrpcMethod::CreateDevice:
223 return CreateDevice(request_byte_vec);
224 case frontend::GrpcMethod::DeleteChip:
225 return DeleteChip(request_byte_vec);
226 case frontend::GrpcMethod::PatchDevice:
227 return PatchDevice(request_byte_vec);
228 case frontend::GrpcMethod::ListDevice:
229 return ListDevice();
230 case frontend::GrpcMethod::Reset:
231 return Reset();
232 case frontend::GrpcMethod::ListCapture:
233 return ListCapture();
234 case frontend::GrpcMethod::PatchCapture:
235 return PatchCapture(request_byte_vec);
236 default:
237 return make_result(grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
238 "Unknown GrpcMethod found."),
239 google::protobuf::Empty());
240 }
241 }
242
243 private:
244 std::unique_ptr<frontend::FrontendService::Stub> stub_;
245
CheckStatus(const grpc::Status & status,const std::string & message)246 static bool CheckStatus(const grpc::Status &status,
247 const std::string &message) {
248 if (status.ok()) return true;
249 if (status.error_code() == grpc::StatusCode::UNAVAILABLE)
250 BtsLogError(
251 "netsim frontend service is unavailable, "
252 "please restart.");
253 else
254 BtsLogError("request to frontend service failed (%d) - %s",
255 status.error_code(), status.error_message().c_str());
256 return false;
257 }
258 };
259
260 } // namespace
261
NewFrontendClient(const std::string & server)262 std::unique_ptr<FrontendClient> NewFrontendClient(const std::string &server) {
263 auto stub = NewFrontendStub(server);
264 return (stub == nullptr
265 ? nullptr
266 : std::make_unique<FrontendClientImpl>(std::move(stub)));
267 }
268
269 } // namespace frontend
270 } // namespace netsim