198 lines
7.9 KiB
C++
198 lines
7.9 KiB
C++
|
|
//
|
||
|
|
// Copyright (C) 2022 The Android Open Source Project
|
||
|
|
//
|
||
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
// you may not use this file except in compliance with the License.
|
||
|
|
// You may obtain a copy of the License at
|
||
|
|
//
|
||
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
//
|
||
|
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
// See the License for the specific language governing permissions and
|
||
|
|
// limitations under the License.
|
||
|
|
|
||
|
|
#include "host/commands/remote/remote.h"
|
||
|
|
|
||
|
|
#include <iostream>
|
||
|
|
|
||
|
|
namespace cuttlefish {
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
const char* kFieldItems = "items";
|
||
|
|
const char* kFieldName = "name";
|
||
|
|
const char* kFieldDone = "done";
|
||
|
|
const char* kFieldResult = "result";
|
||
|
|
const char* kFieldResponse = "response";
|
||
|
|
|
||
|
|
static std::string JsonToString(const Json::Value& input) {
|
||
|
|
Json::StreamWriterBuilder wbuilder;
|
||
|
|
wbuilder["indentation"] = "";
|
||
|
|
return Json::writeString(wbuilder, input);
|
||
|
|
}
|
||
|
|
|
||
|
|
static std::string CreateHostBody(const CreateHostInstanceRequest& request) {
|
||
|
|
Json::Value gcp;
|
||
|
|
gcp["machine_type"] = request.gcp->machine_type;
|
||
|
|
gcp["min_cpu_platform"] = request.gcp->min_cpu_platform;
|
||
|
|
Json::Value request_json;
|
||
|
|
request_json["create_host_instance_request"]["gcp"] = gcp;
|
||
|
|
return JsonToString(request_json);
|
||
|
|
}
|
||
|
|
|
||
|
|
static std::string ToJson(const CreateCVDRequest& request) {
|
||
|
|
Json::Value build_info;
|
||
|
|
build_info["build_id"] = request.build_info.build_id;
|
||
|
|
build_info["target"] = request.build_info.target;
|
||
|
|
Json::Value root;
|
||
|
|
root["build_info"] = build_info;
|
||
|
|
return JsonToString(root);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
CloudOrchestratorApi::CloudOrchestratorApi(const std::string& service_url,
|
||
|
|
const std::string& zone,
|
||
|
|
HttpClient& http_client)
|
||
|
|
: service_url_(service_url), zone_(zone), http_client_(http_client) {}
|
||
|
|
|
||
|
|
CloudOrchestratorApi::~CloudOrchestratorApi() {}
|
||
|
|
|
||
|
|
Result<std::string> CloudOrchestratorApi::CreateHost(
|
||
|
|
const CreateHostInstanceRequest& request) {
|
||
|
|
std::string url = service_url_ + "/v1/zones/" + zone_ + "/hosts";
|
||
|
|
std::string data = CreateHostBody(request);
|
||
|
|
auto resp =
|
||
|
|
CF_EXPECT(http_client_.PostToString(url, data), "Http client failed");
|
||
|
|
CF_EXPECT(resp.HttpSuccess(), "Http request failed with status code: "
|
||
|
|
<< resp.http_code << ", server response:\n"
|
||
|
|
<< resp.data);
|
||
|
|
auto resp_json =
|
||
|
|
CF_EXPECT(ParseJson(resp.data), "Failed parsing response body");
|
||
|
|
CF_EXPECT(
|
||
|
|
resp_json.isMember(kFieldName),
|
||
|
|
"Invalid create host response, missing field: '" << kFieldName << "'");
|
||
|
|
return resp_json[kFieldName].asString();
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<Operation> CloudOrchestratorApi::WaitCloudOperation(
|
||
|
|
const std::string& name) {
|
||
|
|
std::string url =
|
||
|
|
service_url_ + "/v1/zones/" + zone_ + "/operations/" + name + "/wait";
|
||
|
|
auto resp =
|
||
|
|
CF_EXPECT(http_client_.PostToString(url, ""), "Http client failed");
|
||
|
|
CF_EXPECT(resp.HttpSuccess(), "Http request failed with status code: "
|
||
|
|
<< resp.http_code << ", server response:\n"
|
||
|
|
<< resp.data);
|
||
|
|
auto resp_json =
|
||
|
|
CF_EXPECT(ParseJson(resp.data), "Failed parsing response body");
|
||
|
|
CF_EXPECT(resp_json.isMember(kFieldDone),
|
||
|
|
"Invalid response, missing field: '" << kFieldDone << "'");
|
||
|
|
bool done = resp_json[kFieldDone].asBool();
|
||
|
|
if (!done) {
|
||
|
|
return Operation{done : done};
|
||
|
|
}
|
||
|
|
CF_EXPECT(resp_json.isMember(kFieldResult),
|
||
|
|
"Invalid response, missing field: '" << kFieldResult << "'");
|
||
|
|
CF_EXPECT(resp_json[kFieldResult].isMember(kFieldResponse),
|
||
|
|
"Invalid response, missing field: '" << kFieldResponse << "'");
|
||
|
|
return Operation{
|
||
|
|
done : done,
|
||
|
|
result : OperationResult{
|
||
|
|
response : resp_json[kFieldResult][kFieldResponse],
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<std::vector<std::string>> CloudOrchestratorApi::ListHosts() {
|
||
|
|
std::string url = service_url_ + "/v1/zones/" + zone_ + "/hosts";
|
||
|
|
auto resp = CF_EXPECT(http_client_.GetToString(url), "Http client failed");
|
||
|
|
CF_EXPECT(resp.HttpSuccess(), "Http request failed with status code: "
|
||
|
|
<< resp.http_code << ", server response:\n"
|
||
|
|
<< resp.data);
|
||
|
|
auto root = CF_EXPECT(ParseJson(resp.data), "Failed parsing response body");
|
||
|
|
CF_EXPECT(
|
||
|
|
root.isMember(kFieldItems),
|
||
|
|
"Invalid list hosts response, missing field: '" << kFieldItems << "'");
|
||
|
|
std::vector<std::string> result;
|
||
|
|
for (const Json::Value& item : root[kFieldItems]) {
|
||
|
|
result.push_back(item["name"].asString());
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<void> CloudOrchestratorApi::DeleteHost(const std::string& host) {
|
||
|
|
std::string url = service_url_ + "/v1/zones/" + zone_ + "/hosts/" + host;
|
||
|
|
auto resp = CF_EXPECT(http_client_.DeleteToString(url), "Http client failed");
|
||
|
|
CF_EXPECT(resp.HttpSuccess(), "Http request failed with status code: "
|
||
|
|
<< resp.http_code << ", server response:\n"
|
||
|
|
<< resp.data);
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<std::string> CloudOrchestratorApi::CreateCVD(
|
||
|
|
const std::string& host, const CreateCVDRequest& request) {
|
||
|
|
std::string url =
|
||
|
|
service_url_ + "/v1/zones/" + zone_ + "/hosts/" + host + "/cvds";
|
||
|
|
std::string data = ToJson(request);
|
||
|
|
auto resp =
|
||
|
|
CF_EXPECT(http_client_.PostToString(url, data), "Http client failed");
|
||
|
|
CF_EXPECT(resp.HttpSuccess(), "Http request failed with status code: "
|
||
|
|
<< resp.http_code << ", server response:\n"
|
||
|
|
<< resp.data);
|
||
|
|
auto resp_json =
|
||
|
|
CF_EXPECT(ParseJson(resp.data), "Failed parsing response body");
|
||
|
|
CF_EXPECT(
|
||
|
|
resp_json.isMember(kFieldName),
|
||
|
|
"Invalid create cvd response, missing field: '" << kFieldName << "'");
|
||
|
|
return resp_json[kFieldName].asString();
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<Operation> CloudOrchestratorApi::WaitHostOperation(
|
||
|
|
const std::string& host, const std::string& name) {
|
||
|
|
std::string url = service_url_ + "/v1/zones/" + zone_ + "/hosts/" + host +
|
||
|
|
"/operations/" + name + "/wait";
|
||
|
|
auto resp =
|
||
|
|
CF_EXPECT(http_client_.PostToString(url, ""), "Http client failed");
|
||
|
|
CF_EXPECT(resp.HttpSuccess(), "Http request failed with status code: "
|
||
|
|
<< resp.http_code << ", server response:\n"
|
||
|
|
<< resp.data);
|
||
|
|
auto resp_json =
|
||
|
|
CF_EXPECT(ParseJson(resp.data), "Failed parsing response body");
|
||
|
|
CF_EXPECT(resp_json.isMember(kFieldDone),
|
||
|
|
"Invalid response, missing field: '" << kFieldDone << "'");
|
||
|
|
bool done = resp_json[kFieldDone].asBool();
|
||
|
|
if (!done) {
|
||
|
|
return Operation{done : done};
|
||
|
|
}
|
||
|
|
CF_EXPECT(resp_json.isMember(kFieldResult),
|
||
|
|
"Invalid response, missing field: '" << kFieldResult << "'");
|
||
|
|
CF_EXPECT(resp_json[kFieldResult].isMember(kFieldResponse),
|
||
|
|
"Invalid response, missing field: '" << kFieldResponse << "'");
|
||
|
|
return Operation{
|
||
|
|
done : done,
|
||
|
|
result : OperationResult{
|
||
|
|
response : resp_json[kFieldResult][kFieldResponse],
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<std::vector<std::string>> CloudOrchestratorApi::ListCVDWebRTCStreams(
|
||
|
|
const std::string& host) {
|
||
|
|
std::string url =
|
||
|
|
service_url_ + "/v1/zones/" + zone_ + "/hosts/" + host + "/devices";
|
||
|
|
auto resp = CF_EXPECT(http_client_.GetToString(url), "Http client failed");
|
||
|
|
CF_EXPECT(resp.HttpSuccess(), "Http request failed with status code: "
|
||
|
|
<< resp.http_code << ", server response:\n"
|
||
|
|
<< resp.data);
|
||
|
|
auto root = CF_EXPECT(ParseJson(resp.data), "Failed parsing response body");
|
||
|
|
std::vector<std::string> result;
|
||
|
|
for (int index = 0; index < root.size(); index++) {
|
||
|
|
result.push_back(root[index].asString());
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace cuttlefish
|