diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2023-11-12 21:25:31 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2023-11-12 21:39:54 +0300 |
commit | d28c55ab25cc8cedab8a5f4736c0d66e88b3da95 (patch) | |
tree | 73d373709b74fa2baaa4fe02a40a77c0a5baf6b7 /library | |
parent | 35b17f4f3b6e0ed855e7e47d3f1eb57470388a2c (diff) | |
download | ydb-d28c55ab25cc8cedab8a5f4736c0d66e88b3da95.tar.gz |
Intermediate changes
Diffstat (limited to 'library')
62 files changed, 5107 insertions, 0 deletions
diff --git a/library/cpp/CMakeLists.darwin-x86_64.txt b/library/cpp/CMakeLists.darwin-x86_64.txt index 0f393b2039..22b108ee05 100644 --- a/library/cpp/CMakeLists.darwin-x86_64.txt +++ b/library/cpp/CMakeLists.darwin-x86_64.txt @@ -63,6 +63,7 @@ add_subdirectory(openssl) add_subdirectory(packedtypes) add_subdirectory(packers) add_subdirectory(pop_count) +add_subdirectory(porto) add_subdirectory(presort) add_subdirectory(protobuf) add_subdirectory(random_provider) diff --git a/library/cpp/CMakeLists.linux-aarch64.txt b/library/cpp/CMakeLists.linux-aarch64.txt index cf47314f07..b42033fd82 100644 --- a/library/cpp/CMakeLists.linux-aarch64.txt +++ b/library/cpp/CMakeLists.linux-aarch64.txt @@ -62,6 +62,7 @@ add_subdirectory(openssl) add_subdirectory(packedtypes) add_subdirectory(packers) add_subdirectory(pop_count) +add_subdirectory(porto) add_subdirectory(presort) add_subdirectory(protobuf) add_subdirectory(random_provider) diff --git a/library/cpp/CMakeLists.linux-x86_64.txt b/library/cpp/CMakeLists.linux-x86_64.txt index 0f393b2039..22b108ee05 100644 --- a/library/cpp/CMakeLists.linux-x86_64.txt +++ b/library/cpp/CMakeLists.linux-x86_64.txt @@ -63,6 +63,7 @@ add_subdirectory(openssl) add_subdirectory(packedtypes) add_subdirectory(packers) add_subdirectory(pop_count) +add_subdirectory(porto) add_subdirectory(presort) add_subdirectory(protobuf) add_subdirectory(random_provider) diff --git a/library/cpp/CMakeLists.windows-x86_64.txt b/library/cpp/CMakeLists.windows-x86_64.txt index 772027a342..8925d1f2bf 100644 --- a/library/cpp/CMakeLists.windows-x86_64.txt +++ b/library/cpp/CMakeLists.windows-x86_64.txt @@ -62,6 +62,7 @@ add_subdirectory(openssl) add_subdirectory(packedtypes) add_subdirectory(packers) add_subdirectory(pop_count) +add_subdirectory(porto) add_subdirectory(presort) add_subdirectory(protobuf) add_subdirectory(random_provider) diff --git a/library/cpp/porto/CMakeLists.darwin-x86_64.txt b/library/cpp/porto/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..499930c4b0 --- /dev/null +++ b/library/cpp/porto/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,9 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +add_subdirectory(proto) diff --git a/library/cpp/porto/CMakeLists.linux-aarch64.txt b/library/cpp/porto/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..f61df9eb93 --- /dev/null +++ b/library/cpp/porto/CMakeLists.linux-aarch64.txt @@ -0,0 +1,22 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +add_subdirectory(proto) + +add_library(library-cpp-porto) +target_link_libraries(library-cpp-porto PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + cpp-porto-proto + contrib-libs-protobuf +) +target_sources(library-cpp-porto PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/porto/libporto.cpp + ${CMAKE_SOURCE_DIR}/library/cpp/porto/metrics.cpp +) diff --git a/library/cpp/porto/CMakeLists.linux-x86_64.txt b/library/cpp/porto/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..f61df9eb93 --- /dev/null +++ b/library/cpp/porto/CMakeLists.linux-x86_64.txt @@ -0,0 +1,22 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +add_subdirectory(proto) + +add_library(library-cpp-porto) +target_link_libraries(library-cpp-porto PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + cpp-porto-proto + contrib-libs-protobuf +) +target_sources(library-cpp-porto PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/porto/libporto.cpp + ${CMAKE_SOURCE_DIR}/library/cpp/porto/metrics.cpp +) diff --git a/library/cpp/porto/CMakeLists.txt b/library/cpp/porto/CMakeLists.txt new file mode 100644 index 0000000000..f8b31df0c1 --- /dev/null +++ b/library/cpp/porto/CMakeLists.txt @@ -0,0 +1,17 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/library/cpp/porto/CMakeLists.windows-x86_64.txt b/library/cpp/porto/CMakeLists.windows-x86_64.txt new file mode 100644 index 0000000000..499930c4b0 --- /dev/null +++ b/library/cpp/porto/CMakeLists.windows-x86_64.txt @@ -0,0 +1,9 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +add_subdirectory(proto) diff --git a/library/cpp/porto/libporto.cpp b/library/cpp/porto/libporto.cpp new file mode 100644 index 0000000000..8fd8924300 --- /dev/null +++ b/library/cpp/porto/libporto.cpp @@ -0,0 +1,1547 @@ +#include "libporto.hpp" +#include "metrics.hpp" + +#include <google/protobuf/text_format.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/io/coded_stream.h> + +extern "C" { +#include <errno.h> +#include <time.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> + +#ifndef __linux__ +#include <fcntl.h> +#else +#include <sys/epoll.h> +#endif +} + +namespace Porto { + +TPortoApi::~TPortoApi() { + Disconnect(); +} + +EError TPortoApi::SetError(const TString &prefix, int _errno) { + LastErrorMsg = prefix + ": " + strerror(_errno); + + switch (_errno) { + case ENOENT: + LastError = EError::SocketUnavailable; + break; + case EAGAIN: + LastErrorMsg = prefix + ": Timeout exceeded. Timeout value: " + std::to_string(Timeout); + LastError = EError::SocketTimeout; + break; + case EIO: + case EPIPE: + LastError = EError::SocketError; + break; + default: + LastError = EError::Unknown; + break; + } + + Disconnect(); + return LastError; +} + +TString TPortoApi::GetLastError() const { + return EError_Name(LastError) + ":(" + LastErrorMsg + ")"; +} + +EError TPortoApi::Connect(const char *socket_path) { + struct sockaddr_un peer_addr; + socklen_t peer_addr_size; + + Disconnect(); + +#ifdef __linux__ + Fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (Fd < 0) + return SetError("socket", errno); +#else + Fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (Fd < 0) + return SetError("socket", errno); + if (fcntl(Fd, F_SETFD, FD_CLOEXEC) < 0) + return SetError("fcntl FD_CLOEXEC", errno); +#endif + + if (Timeout > 0 && SetSocketTimeout(3, Timeout)) + return LastError; + + memset(&peer_addr, 0, sizeof(struct sockaddr_un)); + peer_addr.sun_family = AF_UNIX; + strncpy(peer_addr.sun_path, socket_path, strlen(socket_path)); + + peer_addr_size = sizeof(struct sockaddr_un); + if (connect(Fd, (struct sockaddr *) &peer_addr, peer_addr_size) < 0) + return SetError("connect", errno); + + /* Restore async wait state */ + if (!AsyncWaitNames.empty()) { + for (auto &name: AsyncWaitNames) + Req.mutable_asyncwait()->add_name(name); + for (auto &label: AsyncWaitLabels) + Req.mutable_asyncwait()->add_label(label); + if (AsyncWaitTimeout >= 0) + Req.mutable_asyncwait()->set_timeout_ms(AsyncWaitTimeout * 1000); + return Call(); + } + + return EError::Success; +} + +void TPortoApi::Disconnect() { + if (Fd >= 0) + close(Fd); + Fd = -1; +} + +EError TPortoApi::SetSocketTimeout(int direction, int timeout) { + struct timeval tv; + + if (Fd < 0) + return EError::Success; + + tv.tv_sec = timeout > 0 ? timeout : 0; + tv.tv_usec = 0; + + if ((direction & 1) && setsockopt(Fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv)) + return SetError("setsockopt SO_SNDTIMEO", errno); + + if ((direction & 2) && setsockopt(Fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv)) + return SetError("setsockopt SO_RCVTIMEO", errno); + + return EError::Success; +} + +EError TPortoApi::SetTimeout(int timeout) { + Timeout = timeout ? timeout : DEFAULT_TIMEOUT; + return SetSocketTimeout(3, Timeout); +} + +EError TPortoApi::SetDiskTimeout(int timeout) { + DiskTimeout = timeout ? timeout : DEFAULT_DISK_TIMEOUT; + return EError::Success; +} + +EError TPortoApi::Send(const TPortoRequest &req) { + google::protobuf::io::FileOutputStream raw(Fd); + + if (!req.IsInitialized()) { + LastError = EError::InvalidMethod; + LastErrorMsg = "Request is not initialized"; + return EError::InvalidMethod; + } + + { + google::protobuf::io::CodedOutputStream output(&raw); + + output.WriteVarint32(req.ByteSize()); + req.SerializeWithCachedSizes(&output); + } + + raw.Flush(); + + int err = raw.GetErrno(); + if (err) + return SetError("send", err); + + return EError::Success; +} + +EError TPortoApi::Recv(TPortoResponse &rsp) { + google::protobuf::io::FileInputStream raw(Fd); + google::protobuf::io::CodedInputStream input(&raw); + + while (true) { + uint32_t size; + + if (!input.ReadVarint32(&size)) + return SetError("recv", raw.GetErrno() ?: EIO); + + auto prev_limit = input.PushLimit(size); + + rsp.Clear(); + + if (!rsp.ParseFromCodedStream(&input)) + return SetError("recv", raw.GetErrno() ?: EIO); + + input.PopLimit(prev_limit); + + if (rsp.has_asyncwait()) { + if (AsyncWaitCallback) + AsyncWaitCallback(rsp.asyncwait()); + + if (AsyncWaitOneShot) + return EError::Success; + + continue; + } + + return EError::Success; + } +} + +EError TPortoApi::Call(const TPortoRequest &req, + TPortoResponse &rsp, + int extra_timeout) { + bool reconnect = AutoReconnect; + EError err = EError::Success; + + if (Fd < 0) { + if (!reconnect) + return SetError("Not connected", EIO); + err = Connect(); + reconnect = false; + } + + if (!err) { + err = Send(req); + if (err == EError::SocketError && reconnect) { + err = Connect(); + if (!err) + err = Send(req); + } + } + + if (!err && extra_timeout && Timeout > 0) + err = SetSocketTimeout(2, extra_timeout > 0 ? (extra_timeout + Timeout) : -1); + + if (!err) + err = Recv(rsp); + + if (extra_timeout && Timeout > 0) { + EError err = SetSocketTimeout(2, Timeout); + (void)err; + } + + if (!err) { + err = LastError = rsp.error(); + LastErrorMsg = rsp.errormsg(); + } + + return err; +} + +EError TPortoApi::Call(int extra_timeout) { + return Call(Req, Rsp, extra_timeout); +} + +EError TPortoApi::Call(const TString &req, + TString &rsp, + int extra_timeout) { + Req.Clear(); + if (!google::protobuf::TextFormat::ParseFromString(req, &Req)) { + LastError = EError::InvalidMethod; + LastErrorMsg = "Cannot parse request"; + rsp = ""; + return EError::InvalidMethod; + } + + EError err = Call(Req, Rsp, extra_timeout); + + rsp = Rsp.DebugString(); + + return err; +} + +EError TPortoApi::GetVersion(TString &tag, TString &revision) { + Req.Clear(); + Req.mutable_version(); + + if (!Call()) { + tag = Rsp.version().tag(); + revision = Rsp.version().revision(); + } + + return LastError; +} + +const TGetSystemResponse *TPortoApi::GetSystem() { + Req.Clear(); + Req.mutable_getsystem(); + if (!Call()) + return &Rsp.getsystem(); + return nullptr; +} + +EError TPortoApi::SetSystem(const TString &key, const TString &val) { + TString rsp; + return Call("SetSystem {" + key + ":" + val + "}", rsp); +} + +/* Container */ + +EError TPortoApi::Create(const TString &name) { + Req.Clear(); + auto req = Req.mutable_create(); + req->set_name(name); + return Call(); +} + +EError TPortoApi::CreateWeakContainer(const TString &name) { + Req.Clear(); + auto req = Req.mutable_createweak(); + req->set_name(name); + return Call(); +} + +EError TPortoApi::Destroy(const TString &name) { + Req.Clear(); + auto req = Req.mutable_destroy(); + req->set_name(name); + return Call(); +} + +const TListResponse *TPortoApi::List(const TString &mask) { + Req.Clear(); + auto req = Req.mutable_list(); + + if(!mask.empty()) + req->set_mask(mask); + + if (!Call()) + return &Rsp.list(); + + return nullptr; +} + +EError TPortoApi::List(TVector<TString> &list, const TString &mask) { + Req.Clear(); + auto req = Req.mutable_list(); + if(!mask.empty()) + req->set_mask(mask); + if (!Call()) + list = TVector<TString>(std::begin(Rsp.list().name()), + std::end(Rsp.list().name())); + return LastError; +} + +const TListPropertiesResponse *TPortoApi::ListProperties() { + Req.Clear(); + Req.mutable_listproperties(); + + if (Call()) + return nullptr; + + bool has_data = false; + for (const auto &prop: Rsp.listproperties().list()) { + if (prop.read_only()) { + has_data = true; + break; + } + } + + if (!has_data) { + TPortoRequest req; + TPortoResponse rsp; + + req.mutable_listdataproperties(); + if (!Call(req, rsp)) { + for (const auto &data: rsp.listdataproperties().list()) { + auto d = Rsp.mutable_listproperties()->add_list(); + d->set_name(data.name()); + d->set_desc(data.desc()); + d->set_read_only(true); + } + } + } + + return &Rsp.listproperties(); +} + +EError TPortoApi::ListProperties(TVector<TString> &properties) { + properties.clear(); + auto rsp = ListProperties(); + if (rsp) { + for (auto &prop: rsp->list()) + properties.push_back(prop.name()); + } + return LastError; +} + +const TGetResponse *TPortoApi::Get(const TVector<TString> &names, + const TVector<TString> &vars, + int flags) { + Req.Clear(); + auto get = Req.mutable_get(); + + for (const auto &n : names) + get->add_name(n); + + for (const auto &v : vars) + get->add_variable(v); + + if (flags & GET_NONBLOCK) + get->set_nonblock(true); + if (flags & GET_SYNC) + get->set_sync(true); + if (flags & GET_REAL) + get->set_real(true); + + if (!Call()) + return &Rsp.get(); + + return nullptr; +} + +EError TPortoApi::GetContainerSpec(const TString &name, TContainer &container) { + Req.Clear(); + TListContainersRequest req; + auto filter = req.add_filters(); + filter->set_name(name); + + TVector<TContainer> containers; + + auto ret = ListContainersBy(req, containers); + if (containers.empty()) + return EError::ContainerDoesNotExist; + + if (!ret) + container = containers[0]; + + return ret; +} + +EError TPortoApi::ListContainersBy(const TListContainersRequest &listContainersRequest, TVector<TContainer> &containers) { + Req.Clear(); + auto req = Req.mutable_listcontainersby(); + *req = listContainersRequest; + + auto ret = Call(); + if (ret) + return ret; + + for (auto &ct : Rsp.listcontainersby().containers()) + containers.push_back(ct); + + return EError::Success; +} + +EError TPortoApi::CreateFromSpec(const TContainerSpec &container, TVector<TVolumeSpec> volumes, bool start) { + Req.Clear(); + auto req = Req.mutable_createfromspec(); + + auto ct = req->mutable_container(); + *ct = container; + + for (auto &volume : volumes) { + auto v = req->add_volumes(); + *v = volume; + } + + req->set_start(start); + + return Call(); +} + +EError TPortoApi::UpdateFromSpec(const TContainerSpec &container) { + Req.Clear(); + auto req = Req.mutable_updatefromspec(); + + auto ct = req->mutable_container(); + *ct = container; + + return Call(); +} + +EError TPortoApi::GetProperty(const TString &name, + const TString &property, + TString &value, + int flags) { + Req.Clear(); + auto req = Req.mutable_getproperty(); + + req->set_name(name); + req->set_property(property); + if (flags & GET_SYNC) + req->set_sync(true); + if (flags & GET_REAL) + req->set_real(true); + + if (!Call()) + value = Rsp.getproperty().value(); + + return LastError; +} + +EError TPortoApi::SetProperty(const TString &name, + const TString &property, + const TString &value) { + Req.Clear(); + auto req = Req.mutable_setproperty(); + + req->set_name(name); + req->set_property(property); + req->set_value(value); + + return Call(); +} + +EError TPortoApi::GetInt(const TString &name, + const TString &property, + const TString &index, + uint64_t &value) { + TString key = property, str; + if (index.size()) + key = property + "[" + index + "]"; + if (!GetProperty(name, key, str)) { + const char *ptr = str.c_str(); + char *end; + errno = 0; + value = strtoull(ptr, &end, 10); + if (errno || end == ptr || *end) { + LastError = EError::InvalidValue; + LastErrorMsg = " value: " + str; + } + } + return LastError; +} + +EError TPortoApi::SetInt(const TString &name, + const TString &property, + const TString &index, + uint64_t value) { + TString key = property; + if (index.size()) + key = property + "[" + index + "]"; + return SetProperty(name, key, ToString(value)); +} + +EError TPortoApi::GetProcMetric(const TVector<TString> &names, + const TString &metric, + TMap<TString, uint64_t> &values) { + auto it = ProcMetrics.find(metric); + + if (it == ProcMetrics.end()) { + LastError = EError::InvalidValue; + LastErrorMsg = " Unknown metric: " + metric; + return LastError; + } + + LastError = it->second->GetValues(names, values, *this); + + if (LastError) + LastErrorMsg = "Unknown error on Get() method"; + + return LastError; +} + +EError TPortoApi::SetLabel(const TString &name, + const TString &label, + const TString &value, + const TString &prev_value) { + Req.Clear(); + auto req = Req.mutable_setlabel(); + + req->set_name(name); + req->set_label(label); + req->set_value(value); + if (prev_value != " ") + req->set_prev_value(prev_value); + + return Call(); +} + +EError TPortoApi::IncLabel(const TString &name, + const TString &label, + int64_t add, + int64_t &result) { + Req.Clear(); + auto req = Req.mutable_inclabel(); + + req->set_name(name); + req->set_label(label); + req->set_add(add); + + EError err = Call(); + + if (Rsp.has_inclabel()) + result = Rsp.inclabel().result(); + + return err; +} + +EError TPortoApi::Start(const TString &name) { + Req.Clear(); + auto req = Req.mutable_start(); + + req->set_name(name); + + return Call(); +} + +EError TPortoApi::Stop(const TString &name, int stop_timeout) { + Req.Clear(); + auto req = Req.mutable_stop(); + + req->set_name(name); + if (stop_timeout >= 0) + req->set_timeout_ms(stop_timeout * 1000); + + return Call(stop_timeout > 0 ? stop_timeout : 0); +} + +EError TPortoApi::Kill(const TString &name, int sig) { + Req.Clear(); + auto req = Req.mutable_kill(); + + req->set_name(name); + req->set_sig(sig); + + return Call(); +} + +EError TPortoApi::Pause(const TString &name) { + Req.Clear(); + auto req = Req.mutable_pause(); + + req->set_name(name); + + return Call(); +} + +EError TPortoApi::Resume(const TString &name) { + Req.Clear(); + auto req = Req.mutable_resume(); + + req->set_name(name); + + return Call(); +} + +EError TPortoApi::Respawn(const TString &name) { + Req.Clear(); + auto req = Req.mutable_respawn(); + + req->set_name(name); + + return Call(); +} + +EError TPortoApi::CallWait(TString &result_state, int wait_timeout) { + time_t deadline = 0; + time_t last_retry = 0; + + if (wait_timeout >= 0) { + deadline = time(nullptr) + wait_timeout; + Req.mutable_wait()->set_timeout_ms(wait_timeout * 1000); + } + +retry: + if (!Call(wait_timeout)) { + if (Rsp.wait().has_state()) + result_state = Rsp.wait().state(); + else if (Rsp.wait().name() == "") + result_state = "timeout"; + else + result_state = "dead"; + } else if (LastError == EError::SocketError && AutoReconnect) { + time_t now = time(nullptr); + + if (wait_timeout < 0 || now < deadline) { + if (wait_timeout >= 0) { + wait_timeout = deadline - now; + Req.mutable_wait()->set_timeout_ms(wait_timeout * 1000); + } + if (last_retry == now) + sleep(1); + last_retry = now; + goto retry; + } + + result_state = "timeout"; + } else + result_state = "unknown"; + + return LastError; +} + +EError TPortoApi::WaitContainer(const TString &name, + TString &result_state, + int wait_timeout) { + Req.Clear(); + auto req = Req.mutable_wait(); + + req->add_name(name); + + return CallWait(result_state, wait_timeout); +} + +EError TPortoApi::WaitContainers(const TVector<TString> &names, + TString &result_name, + TString &result_state, + int wait_timeout) { + Req.Clear(); + auto req = Req.mutable_wait(); + + for (auto &c : names) + req->add_name(c); + + EError err = CallWait(result_state, wait_timeout); + + result_name = Rsp.wait().name(); + + return err; +} + +const TWaitResponse *TPortoApi::Wait(const TVector<TString> &names, + const TVector<TString> &labels, + int wait_timeout) { + Req.Clear(); + auto req = Req.mutable_wait(); + TString result_state; + + for (auto &c : names) + req->add_name(c); + for (auto &label: labels) + req->add_label(label); + + EError err = CallWait(result_state, wait_timeout); + (void)err; + + if (Rsp.has_wait()) + return &Rsp.wait(); + + return nullptr; +} + +EError TPortoApi::AsyncWait(const TVector<TString> &names, + const TVector<TString> &labels, + TWaitCallback callback, + int wait_timeout, + const TString &targetState) { + Req.Clear(); + auto req = Req.mutable_asyncwait(); + + AsyncWaitNames.clear(); + AsyncWaitLabels.clear(); + AsyncWaitTimeout = wait_timeout; + AsyncWaitCallback = callback; + + for (auto &name: names) + req->add_name(name); + for (auto &label: labels) + req->add_label(label); + if (wait_timeout >= 0) + req->set_timeout_ms(wait_timeout * 1000); + if (!targetState.empty()) { + req->set_target_state(targetState); + AsyncWaitOneShot = true; + } else + AsyncWaitOneShot = false; + + if (Call()) { + AsyncWaitCallback = nullptr; + } else { + AsyncWaitNames = names; + AsyncWaitLabels = labels; + } + + return LastError; +} + +EError TPortoApi::StopAsyncWait(const TVector<TString> &names, + const TVector<TString> &labels, + const TString &targetState) { + Req.Clear(); + auto req = Req.mutable_stopasyncwait(); + + AsyncWaitNames.clear(); + AsyncWaitLabels.clear(); + + for (auto &name: names) + req->add_name(name); + for (auto &label: labels) + req->add_label(label); + if (!targetState.empty()) { + req->set_target_state(targetState); + } + + return Call(); +} + +EError TPortoApi::ConvertPath(const TString &path, + const TString &src, + const TString &dest, + TString &res) { + Req.Clear(); + auto req = Req.mutable_convertpath(); + + req->set_path(path); + req->set_source(src); + req->set_destination(dest); + + if (!Call()) + res = Rsp.convertpath().path(); + + return LastError; +} + +EError TPortoApi::AttachProcess(const TString &name, int pid, + const TString &comm) { + Req.Clear(); + auto req = Req.mutable_attachprocess(); + + req->set_name(name); + req->set_pid(pid); + req->set_comm(comm); + + return Call(); +} + +EError TPortoApi::AttachThread(const TString &name, int pid, + const TString &comm) { + Req.Clear(); + auto req = Req.mutable_attachthread(); + + req->set_name(name); + req->set_pid(pid); + req->set_comm(comm); + + return Call(); +} + +EError TPortoApi::LocateProcess(int pid, const TString &comm, + TString &name) { + Req.Clear(); + auto req = Req.mutable_locateprocess(); + + req->set_pid(pid); + req->set_comm(comm); + + if (!Call()) + name = Rsp.locateprocess().name(); + + return LastError; +} + +/* Volume */ + +const TListVolumePropertiesResponse *TPortoApi::ListVolumeProperties() { + Req.Clear(); + Req.mutable_listvolumeproperties(); + + if (!Call()) + return &Rsp.listvolumeproperties(); + + return nullptr; +} + +EError TPortoApi::ListVolumeProperties(TVector<TString> &properties) { + properties.clear(); + auto rsp = ListVolumeProperties(); + if (rsp) { + for (auto &prop: rsp->list()) + properties.push_back(prop.name()); + } + return LastError; +} + +EError TPortoApi::CreateVolume(TString &path, + const TMap<TString, TString> &config) { + Req.Clear(); + auto req = Req.mutable_createvolume(); + + req->set_path(path); + + *(req->mutable_properties()) = + google::protobuf::Map<TString, TString>(config.begin(), config.end()); + + if (!Call(DiskTimeout) && path.empty()) + path = Rsp.createvolume().path(); + + return LastError; +} + +EError TPortoApi::TuneVolume(const TString &path, + const TMap<TString, TString> &config) { + Req.Clear(); + auto req = Req.mutable_tunevolume(); + + req->set_path(path); + + *(req->mutable_properties()) = + google::protobuf::Map<TString, TString>(config.begin(), config.end()); + + return Call(DiskTimeout); +} + +EError TPortoApi::LinkVolume(const TString &path, + const TString &container, + const TString &target, + bool read_only, + bool required) { + Req.Clear(); + auto req = (target.empty() && !required) ? Req.mutable_linkvolume() : + Req.mutable_linkvolumetarget(); + + req->set_path(path); + if (!container.empty()) + req->set_container(container); + if (target != "") + req->set_target(target); + if (read_only) + req->set_read_only(read_only); + if (required) + req->set_required(required); + + return Call(); +} + +EError TPortoApi::UnlinkVolume(const TString &path, + const TString &container, + const TString &target, + bool strict) { + Req.Clear(); + auto req = (target == "***") ? Req.mutable_unlinkvolume() : + Req.mutable_unlinkvolumetarget(); + + req->set_path(path); + if (!container.empty()) + req->set_container(container); + if (target != "***") + req->set_target(target); + if (strict) + req->set_strict(strict); + + return Call(DiskTimeout); +} + +const TListVolumesResponse * +TPortoApi::ListVolumes(const TString &path, + const TString &container) { + Req.Clear(); + auto req = Req.mutable_listvolumes(); + + if (!path.empty()) + req->set_path(path); + + if (!container.empty()) + req->set_container(container); + + if (Call()) + return nullptr; + + auto list = Rsp.mutable_listvolumes(); + + /* compat */ + for (auto v: *list->mutable_volumes()) { + if (v.links().size()) + break; + for (auto &ct: v.containers()) + v.add_links()->set_container(ct); + } + + return list; +} + +EError TPortoApi::ListVolumes(TVector<TString> &paths) { + Req.Clear(); + auto rsp = ListVolumes(); + paths.clear(); + if (rsp) { + for (auto &v : rsp->volumes()) + paths.push_back(v.path()); + } + return LastError; +} + +const TVolumeDescription *TPortoApi::GetVolumeDesc(const TString &path) { + Req.Clear(); + auto rsp = ListVolumes(path); + + if (rsp && rsp->volumes().size()) + return &rsp->volumes(0); + + return nullptr; +} + +const TVolumeSpec *TPortoApi::GetVolume(const TString &path) { + Req.Clear(); + auto req = Req.mutable_getvolume(); + + req->add_path(path); + + if (!Call() && Rsp.getvolume().volume().size()) + return &Rsp.getvolume().volume(0); + + return nullptr; +} + +const TGetVolumeResponse *TPortoApi::GetVolumes(uint64_t changed_since) { + Req.Clear(); + auto req = Req.mutable_getvolume(); + + if (changed_since) + req->set_changed_since(changed_since); + + if (!Call() && Rsp.has_getvolume()) + return &Rsp.getvolume(); + + return nullptr; +} + + +EError TPortoApi::ListVolumesBy(const TGetVolumeRequest &getVolumeRequest, TVector<TVolumeSpec> &volumes) { + Req.Clear(); + auto req = Req.mutable_getvolume(); + *req = getVolumeRequest; + + auto ret = Call(); + if (ret) + return ret; + + for (auto volume : Rsp.getvolume().volume()) + volumes.push_back(volume); + return EError::Success; +} + +EError TPortoApi::CreateVolumeFromSpec(const TVolumeSpec &volume, TVolumeSpec &resultSpec) { + Req.Clear(); + auto req = Req.mutable_newvolume(); + auto vol = req->mutable_volume(); + *vol = volume; + + auto ret = Call(); + if (ret) + return ret; + + resultSpec = Rsp.newvolume().volume(); + + return ret; +} + +/* Layer */ + +EError TPortoApi::ImportLayer(const TString &layer, + const TString &tarball, + bool merge, + const TString &place, + const TString &private_value, + bool verboseError) { + Req.Clear(); + auto req = Req.mutable_importlayer(); + + req->set_layer(layer); + req->set_tarball(tarball); + req->set_merge(merge); + req->set_verbose_error(verboseError); + if (place.size()) + req->set_place(place); + if (private_value.size()) + req->set_private_value(private_value); + + return Call(DiskTimeout); +} + +EError TPortoApi::ExportLayer(const TString &volume, + const TString &tarball, + const TString &compress) { + Req.Clear(); + auto req = Req.mutable_exportlayer(); + + req->set_volume(volume); + req->set_tarball(tarball); + if (compress.size()) + req->set_compress(compress); + + return Call(DiskTimeout); +} + +EError TPortoApi::ReExportLayer(const TString &layer, + const TString &tarball, + const TString &compress) { + Req.Clear(); + auto req = Req.mutable_exportlayer(); + + req->set_volume(""); + req->set_layer(layer); + req->set_tarball(tarball); + if (compress.size()) + req->set_compress(compress); + + return Call(DiskTimeout); +} + +EError TPortoApi::RemoveLayer(const TString &layer, + const TString &place, + bool async) { + Req.Clear(); + auto req = Req.mutable_removelayer(); + + req->set_layer(layer); + req->set_async(async); + if (place.size()) + req->set_place(place); + + return Call(DiskTimeout); +} + +const TListLayersResponse *TPortoApi::ListLayers(const TString &place, + const TString &mask) { + Req.Clear(); + auto req = Req.mutable_listlayers(); + + if (place.size()) + req->set_place(place); + if (mask.size()) + req->set_mask(mask); + + if (Call()) + return nullptr; + + auto list = Rsp.mutable_listlayers(); + + /* compat conversion */ + if (!list->layers().size() && list->layer().size()) { + for (auto &name: list->layer()) { + auto l = list->add_layers(); + l->set_name(name); + l->set_owner_user(""); + l->set_owner_group(""); + l->set_last_usage(0); + l->set_private_value(""); + } + } + + return list; +} + +EError TPortoApi::ListLayers(TVector<TString> &layers, + const TString &place, + const TString &mask) { + Req.Clear(); + auto req = Req.mutable_listlayers(); + + if (place.size()) + req->set_place(place); + if (mask.size()) + req->set_mask(mask); + + if (!Call()) + layers = TVector<TString>(std::begin(Rsp.listlayers().layer()), + std::end(Rsp.listlayers().layer())); + + return LastError; +} + +EError TPortoApi::GetLayerPrivate(TString &private_value, + const TString &layer, + const TString &place) { + Req.Clear(); + auto req = Req.mutable_getlayerprivate(); + + req->set_layer(layer); + if (place.size()) + req->set_place(place); + + if (!Call()) + private_value = Rsp.getlayerprivate().private_value(); + + return LastError; +} + +EError TPortoApi::SetLayerPrivate(const TString &private_value, + const TString &layer, + const TString &place) { + Req.Clear(); + auto req = Req.mutable_setlayerprivate(); + + req->set_layer(layer); + req->set_private_value(private_value); + if (place.size()) + req->set_place(place); + + return Call(); +} + +/* Docker images */ + +DockerImage::DockerImage(const TDockerImage &i) { + Id = i.id(); + for (const auto &tag: i.tags()) + Tags.emplace_back(tag); + for (const auto &digest: i.digests()) + Digests.emplace_back(digest); + for (const auto &layer: i.layers()) + Layers.emplace_back(layer); + if (i.has_size()) + Size = i.size(); + if (i.has_config()) { + auto &cfg = i.config(); + for (const auto &cmd: cfg.cmd()) + Config.Cmd.emplace_back(cmd); + for (const auto &env: cfg.env()) + Config.Env.emplace_back(env); + } +} + +EError TPortoApi::DockerImageStatus(DockerImage &image, + const TString &name, + const TString &place) { + auto req = Req.mutable_dockerimagestatus(); + req->set_name(name); + if (!place.empty()) + req->set_place(place); + EError ret = Call(); + if (!ret && Rsp.dockerimagestatus().has_image()) + image = DockerImage(Rsp.dockerimagestatus().image()); + return ret; +} + +EError TPortoApi::ListDockerImages(std::vector<DockerImage> &images, + const TString &place, + const TString &mask) { + auto req = Req.mutable_listdockerimages(); + if (place.size()) + req->set_place(place); + if (mask.size()) + req->set_mask(mask); + EError ret = Call(); + if (!ret) { + for (const auto &i: Rsp.listdockerimages().images()) + images.emplace_back(i); + } + return ret; +} + +EError TPortoApi::PullDockerImage(DockerImage &image, + const TString &name, + const TString &place, + const TString &auth_token, + const TString &auth_path, + const TString &auth_service) { + auto req = Req.mutable_pulldockerimage(); + req->set_name(name); + if (place.size()) + req->set_place(place); + if (auth_token.size()) + req->set_auth_token(auth_token); + if (auth_path.size()) + req->set_auth_path(auth_path); + if (auth_service.size()) + req->set_auth_service(auth_service); + EError ret = Call(); + if (!ret && Rsp.pulldockerimage().has_image()) + image = DockerImage(Rsp.pulldockerimage().image()); + return ret; +} + +EError TPortoApi::RemoveDockerImage(const TString &name, + const TString &place) { + auto req = Req.mutable_removedockerimage(); + req->set_name(name); + if (place.size()) + req->set_place(place); + return Call(); +} + +/* Storage */ + +const TListStoragesResponse *TPortoApi::ListStorages(const TString &place, + const TString &mask) { + Req.Clear(); + auto req = Req.mutable_liststorages(); + + if (place.size()) + req->set_place(place); + if (mask.size()) + req->set_mask(mask); + + if (Call()) + return nullptr; + + return &Rsp.liststorages(); +} + +EError TPortoApi::ListStorages(TVector<TString> &storages, + const TString &place, + const TString &mask) { + Req.Clear(); + auto req = Req.mutable_liststorages(); + + if (place.size()) + req->set_place(place); + if (mask.size()) + req->set_mask(mask); + + if (!Call()) { + storages.clear(); + for (auto &storage: Rsp.liststorages().storages()) + storages.push_back(storage.name()); + } + + return LastError; +} + +EError TPortoApi::RemoveStorage(const TString &storage, + const TString &place) { + Req.Clear(); + auto req = Req.mutable_removestorage(); + + req->set_name(storage); + if (place.size()) + req->set_place(place); + + return Call(DiskTimeout); +} + +EError TPortoApi::ImportStorage(const TString &storage, + const TString &archive, + const TString &place, + const TString &compression, + const TString &private_value) { + Req.Clear(); + auto req = Req.mutable_importstorage(); + + req->set_name(storage); + req->set_tarball(archive); + if (place.size()) + req->set_place(place); + if (compression.size()) + req->set_compress(compression); + if (private_value.size()) + req->set_private_value(private_value); + + return Call(DiskTimeout); +} + +EError TPortoApi::ExportStorage(const TString &storage, + const TString &archive, + const TString &place, + const TString &compression) { + Req.Clear(); + auto req = Req.mutable_exportstorage(); + + req->set_name(storage); + req->set_tarball(archive); + if (place.size()) + req->set_place(place); + if (compression.size()) + req->set_compress(compression); + + return Call(DiskTimeout); +} + +#ifdef __linux__ +void TAsyncWaiter::MainCallback(const TWaitResponse &event) { + CallbacksCount++; + + auto it = AsyncCallbacks.find(event.name()); + if (it != AsyncCallbacks.end() && it->second.State == event.state()) { + it->second.Callback(event); + AsyncCallbacks.erase(it); + } +} + +int TAsyncWaiter::Repair() { + for (const auto &it : AsyncCallbacks) { + int ret = Api.AsyncWait({it.first}, {}, GetMainCallback(), -1, it.second.State); + if (ret) + return ret; + } + return 0; +} + +void TAsyncWaiter::WatchDog() { + int ret; + auto apiFd = Api.Fd; + + while (true) { + struct epoll_event events[2]; + int nfds = epoll_wait(EpollFd, events, 2, -1); + + if (nfds < 0) { + if (errno == EINTR) + continue; + + Fatal("Can not make epoll_wait", errno); + return; + } + + for (int n = 0; n < nfds; ++n) { + if (events[n].data.fd == apiFd) { + TPortoResponse rsp; + ret = Api.Recv(rsp); + // portod reloaded - async_wait must be repaired + if (ret == EError::SocketError) { + ret = Api.Connect(); + if (ret) { + Fatal("Can not connect to porto api", ret); + return; + } + + ret = Repair(); + if (ret) { + Fatal("Can not repair", ret); + return; + } + + apiFd = Api.Fd; + + struct epoll_event portoEv; + portoEv.events = EPOLLIN; + portoEv.data.fd = apiFd; + if (epoll_ctl(EpollFd, EPOLL_CTL_ADD, apiFd, &portoEv)) { + Fatal("Can not epoll_ctl", errno); + return; + } + } + } else if (events[n].data.fd == Sock) { + ERequestType requestType = static_cast<ERequestType>(RecvInt(Sock)); + + switch (requestType) { + case ERequestType::Add: + HandleAddRequest(); + break; + case ERequestType::Del: + HandleDelRequest(); + break; + case ERequestType::Stop: + return; + case ERequestType::None: + default: + Fatal("Unknown request", static_cast<int>(requestType)); + } + } + } + } +} + +void TAsyncWaiter::SendInt(int fd, int value) { + int ret = write(fd, &value, sizeof(value)); + if (ret != sizeof(value)) + Fatal("Can not send int", errno); +} + +int TAsyncWaiter::RecvInt(int fd) { + int value; + int ret = read(fd, &value, sizeof(value)); + if (ret != sizeof(value)) + Fatal("Can not recv int", errno); + + return value; +} + +void TAsyncWaiter::HandleAddRequest() { + int ret = 0; + + auto it = AsyncCallbacks.find(ReqCt); + if (it != AsyncCallbacks.end()) { + ret = Api.StopAsyncWait({ReqCt}, {}, it->second.State); + AsyncCallbacks.erase(it); + } + + AsyncCallbacks.insert(std::make_pair(ReqCt, TCallbackData({ReqCallback, ReqState}))); + + ret = Api.AsyncWait({ReqCt}, {}, GetMainCallback(), -1, ReqState); + SendInt(Sock, ret); +} + +void TAsyncWaiter::HandleDelRequest() { + int ret = 0; + + auto it = AsyncCallbacks.find(ReqCt); + if (it != AsyncCallbacks.end()) { + ret = Api.StopAsyncWait({ReqCt}, {}, it->second.State); + AsyncCallbacks.erase(it); + } + + SendInt(Sock, ret); +} + +TAsyncWaiter::TAsyncWaiter(std::function<void(const TString &error, int ret)> fatalCallback) + : CallbacksCount(0ul) + , FatalCallback(fatalCallback) +{ + int socketPair[2]; + int ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, socketPair); + if (ret) { + Fatal("Can not make socketpair", ret); + return; + } + + MasterSock = socketPair[0]; + Sock = socketPair[1]; + + ret = Api.Connect(); + if (ret) { + Fatal("Can not connect to porto api", ret); + return; + } + + auto apiFd = Api.Fd; + + EpollFd = epoll_create1(EPOLL_CLOEXEC); + + if (EpollFd == -1) { + Fatal("Can not epoll_create", errno); + return; + } + + struct epoll_event pairEv; + pairEv.events = EPOLLIN; + pairEv.data.fd = Sock; + + struct epoll_event portoEv; + portoEv.events = EPOLLIN; + portoEv.data.fd = apiFd; + + if (epoll_ctl(EpollFd, EPOLL_CTL_ADD, Sock, &pairEv)) { + Fatal("Can not epoll_ctl", errno); + return; + } + + if (epoll_ctl(EpollFd, EPOLL_CTL_ADD, apiFd, &portoEv)) { + Fatal("Can not epoll_ctl", errno); + return; + } + + WatchDogThread = std::unique_ptr<std::thread>(new std::thread(&TAsyncWaiter::WatchDog, this)); +} + +TAsyncWaiter::~TAsyncWaiter() { + SendInt(MasterSock, static_cast<int>(ERequestType::Stop)); + WatchDogThread->join(); + + // pedantic check, that porto api is watching by epoll + if (epoll_ctl(EpollFd, EPOLL_CTL_DEL, Api.Fd, nullptr) || epoll_ctl(EpollFd, EPOLL_CTL_DEL, Sock, nullptr)) { + Fatal("Can not epoll_ctl_del", errno); + } + + close(EpollFd); + close(Sock); + close(MasterSock); +} + +int TAsyncWaiter::Add(const TString &ct, const TString &state, TWaitCallback callback) { + if (FatalError) + return -1; + + ReqCt = ct; + ReqState = state; + ReqCallback = callback; + + SendInt(MasterSock, static_cast<int>(ERequestType::Add)); + return RecvInt(MasterSock); +} + +int TAsyncWaiter::Remove(const TString &ct) { + if (FatalError) + return -1; + + ReqCt = ct; + + SendInt(MasterSock, static_cast<int>(ERequestType::Del)); + return RecvInt(MasterSock); +} +#endif + +} /* namespace Porto */ diff --git a/library/cpp/porto/libporto.hpp b/library/cpp/porto/libporto.hpp new file mode 100644 index 0000000000..e30f22a41e --- /dev/null +++ b/library/cpp/porto/libporto.hpp @@ -0,0 +1,492 @@ +#pragma once + +#include <atomic> +#include <thread> + +#include <util/string/cast.h> +#include <util/generic/hash.h> +#include <util/generic/map.h> +#include <util/generic/vector.h> + +#include <library/cpp/porto/proto/rpc.pb.h> + +namespace Porto { + +constexpr int INFINITE_TIMEOUT = -1; +constexpr int DEFAULT_TIMEOUT = 300; // 5min +constexpr int DEFAULT_DISK_TIMEOUT = 900; // 15min + +constexpr char SOCKET_PATH[] = "/run/portod.socket"; + +typedef std::function<void(const TWaitResponse &event)> TWaitCallback; + +enum { + GET_NONBLOCK = 1, // try lock container state + GET_SYNC = 2, // refresh cached values, cache ttl 5s + GET_REAL = 4, // no faked or inherited values +}; + +struct DockerImage { + std::string Id; + std::vector<std::string> Tags; + std::vector<std::string> Digests; + std::vector<std::string> Layers; + uint64_t Size; + struct Config { + std::vector<std::string> Cmd; + std::vector<std::string> Env; + } Config; + + DockerImage() = default; + DockerImage(const TDockerImage &i); + + DockerImage(const DockerImage &i) = default; + DockerImage(DockerImage &&i) = default; + + DockerImage& operator=(const DockerImage &i) = default; + DockerImage& operator=(DockerImage &&i) = default; +}; + +class TPortoApi { +#ifdef __linux__ + friend class TAsyncWaiter; +#endif +private: + int Fd = -1; + int Timeout = DEFAULT_TIMEOUT; + int DiskTimeout = DEFAULT_DISK_TIMEOUT; + bool AutoReconnect = true; + + EError LastError = EError::Success; + TString LastErrorMsg; + + /* + * These keep last request and response. Method might return + * pointers to Rsp innards -> pointers valid until next call. + */ + TPortoRequest Req; + TPortoResponse Rsp; + + std::vector<TString> AsyncWaitNames; + std::vector<TString> AsyncWaitLabels; + int AsyncWaitTimeout = INFINITE_TIMEOUT; + TWaitCallback AsyncWaitCallback; + bool AsyncWaitOneShot = false; + + EError SetError(const TString &prefix, int _errno) Y_WARN_UNUSED_RESULT; + + EError SetSocketTimeout(int direction, int timeout) Y_WARN_UNUSED_RESULT; + + EError Send(const TPortoRequest &req) Y_WARN_UNUSED_RESULT; + + EError Recv(TPortoResponse &rsp) Y_WARN_UNUSED_RESULT; + + EError Call(int extra_timeout = 0) Y_WARN_UNUSED_RESULT; + + EError CallWait(TString &result_state, int wait_timeout) Y_WARN_UNUSED_RESULT; + +public: + TPortoApi() { } + ~TPortoApi(); + + int GetFd() const { + return Fd; + } + + bool Connected() const { + return Fd >= 0; + } + + EError Connect(const char *socket_path = SOCKET_PATH) Y_WARN_UNUSED_RESULT; + void Disconnect(); + + /* Requires signal(SIGPIPE, SIG_IGN) */ + void SetAutoReconnect(bool auto_reconnect) { + AutoReconnect = auto_reconnect; + } + + /* Request and response timeout in seconds */ + int GetTimeout() const { + return Timeout; + } + EError SetTimeout(int timeout); + + /* Extra timeout for disk operations in seconds */ + int GetDiskTimeout() const { + return DiskTimeout; + } + EError SetDiskTimeout(int timeout); + + EError Error() const Y_WARN_UNUSED_RESULT { + return LastError; + } + + EError GetLastError(TString &msg) const Y_WARN_UNUSED_RESULT { + msg = LastErrorMsg; + return LastError; + } + + /* Returns "LastError:(LastErrorMsg)" */ + TString GetLastError() const Y_WARN_UNUSED_RESULT; + + /* Returns text protobuf */ + TString GetLastRequest() const { + return Req.DebugString(); + } + TString GetLastResponse() const { + return Rsp.DebugString(); + } + + /* To be used for next changed_since */ + uint64_t ResponseTimestamp() const Y_WARN_UNUSED_RESULT { + return Rsp.timestamp(); + } + + // extra_timeout: 0 - none, -1 - infinite + EError Call(const TPortoRequest &req, + TPortoResponse &rsp, + int extra_timeout = 0) Y_WARN_UNUSED_RESULT; + + EError Call(const TString &req, + TString &rsp, + int extra_timeout = 0) Y_WARN_UNUSED_RESULT; + + /* System */ + + EError GetVersion(TString &tag, TString &revision) Y_WARN_UNUSED_RESULT; + + const TGetSystemResponse *GetSystem(); + + EError SetSystem(const TString &key, const TString &val) Y_WARN_UNUSED_RESULT; + + /* Container */ + + const TListPropertiesResponse *ListProperties(); + + EError ListProperties(TVector<TString> &properties) Y_WARN_UNUSED_RESULT; + + const TListResponse *List(const TString &mask = ""); + + EError List(TVector<TString> &names, const TString &mask = "") Y_WARN_UNUSED_RESULT; + + EError Create(const TString &name) Y_WARN_UNUSED_RESULT; + + EError CreateWeakContainer(const TString &name) Y_WARN_UNUSED_RESULT; + + EError Destroy(const TString &name) Y_WARN_UNUSED_RESULT; + + EError Start(const TString &name)Y_WARN_UNUSED_RESULT; + + // stop_timeout: time between SIGTERM and SIGKILL, -1 - default + EError Stop(const TString &name, int stop_timeout = -1) Y_WARN_UNUSED_RESULT; + + EError Kill(const TString &name, int sig = 9) Y_WARN_UNUSED_RESULT; + + EError Pause(const TString &name) Y_WARN_UNUSED_RESULT; + + EError Resume(const TString &name) Y_WARN_UNUSED_RESULT; + + EError Respawn(const TString &name) Y_WARN_UNUSED_RESULT; + + // wait_timeout: 0 - nonblock, -1 - infinite + EError WaitContainer(const TString &name, + TString &result_state, + int wait_timeout = INFINITE_TIMEOUT) Y_WARN_UNUSED_RESULT; + + EError WaitContainers(const TVector<TString> &names, + TString &result_name, + TString &result_state, + int wait_timeout = INFINITE_TIMEOUT) Y_WARN_UNUSED_RESULT; + + const TWaitResponse *Wait(const TVector<TString> &names, + const TVector<TString> &labels, + int wait_timeout = INFINITE_TIMEOUT) Y_WARN_UNUSED_RESULT; + + EError AsyncWait(const TVector<TString> &names, + const TVector<TString> &labels, + TWaitCallback callbacks, + int wait_timeout = INFINITE_TIMEOUT, + const TString &targetState = "") Y_WARN_UNUSED_RESULT; + + EError StopAsyncWait(const TVector<TString> &names, + const TVector<TString> &labels, + const TString &targetState = "") Y_WARN_UNUSED_RESULT; + + const TGetResponse *Get(const TVector<TString> &names, + const TVector<TString> &properties, + int flags = 0) Y_WARN_UNUSED_RESULT; + + /* Porto v5 api */ + EError GetContainerSpec(const TString &name, TContainer &container) Y_WARN_UNUSED_RESULT ; + EError ListContainersBy(const TListContainersRequest &listContainersRequest, TVector<TContainer> &containers) Y_WARN_UNUSED_RESULT; + EError CreateFromSpec(const TContainerSpec &container, TVector<TVolumeSpec> volumes, bool start = false) Y_WARN_UNUSED_RESULT; + EError UpdateFromSpec(const TContainerSpec &container) Y_WARN_UNUSED_RESULT; + + EError GetProperty(const TString &name, + const TString &property, + TString &value, + int flags = 0) Y_WARN_UNUSED_RESULT; + + EError GetProperty(const TString &name, + const TString &property, + const TString &index, + TString &value, + int flags = 0) Y_WARN_UNUSED_RESULT { + return GetProperty(name, property + "[" + index + "]", value, flags); + } + + EError SetProperty(const TString &name, + const TString &property, + const TString &value) Y_WARN_UNUSED_RESULT; + + EError SetProperty(const TString &name, + const TString &property, + const TString &index, + const TString &value) Y_WARN_UNUSED_RESULT { + return SetProperty(name, property + "[" + index + "]", value); + } + + EError GetInt(const TString &name, + const TString &property, + const TString &index, + uint64_t &value) Y_WARN_UNUSED_RESULT; + + EError GetInt(const TString &name, + const TString &property, + uint64_t &value) Y_WARN_UNUSED_RESULT { + return GetInt(name, property, "", value); + } + + EError SetInt(const TString &name, + const TString &property, + const TString &index, + uint64_t value) Y_WARN_UNUSED_RESULT; + + EError SetInt(const TString &name, + const TString &property, + uint64_t value) Y_WARN_UNUSED_RESULT { + return SetInt(name, property, "", value); + } + + EError GetProcMetric(const TVector<TString> &names, + const TString &metric, + TMap<TString, uint64_t> &values); + + EError GetLabel(const TString &name, + const TString &label, + TString &value) Y_WARN_UNUSED_RESULT { + return GetProperty(name, "labels", label, value); + } + + EError SetLabel(const TString &name, + const TString &label, + const TString &value, + const TString &prev_value = " ") Y_WARN_UNUSED_RESULT; + + EError IncLabel(const TString &name, + const TString &label, + int64_t add, + int64_t &result) Y_WARN_UNUSED_RESULT; + + EError IncLabel(const TString &name, + const TString &label, + int64_t add = 1) Y_WARN_UNUSED_RESULT { + int64_t result; + return IncLabel(name, label, add, result); + } + + EError ConvertPath(const TString &path, + const TString &src_name, + const TString &dst_name, + TString &result_path) Y_WARN_UNUSED_RESULT; + + EError AttachProcess(const TString &name, int pid, + const TString &comm = "") Y_WARN_UNUSED_RESULT; + + EError AttachThread(const TString &name, int pid, + const TString &comm = "") Y_WARN_UNUSED_RESULT; + + EError LocateProcess(int pid, + const TString &comm /* = "" */, + TString &name) Y_WARN_UNUSED_RESULT; + + /* Volume */ + + const TListVolumePropertiesResponse *ListVolumeProperties(); + + EError ListVolumeProperties(TVector<TString> &properties) Y_WARN_UNUSED_RESULT; + + const TListVolumesResponse *ListVolumes(const TString &path = "", + const TString &container = ""); + + EError ListVolumes(TVector<TString> &paths) Y_WARN_UNUSED_RESULT; + + const TVolumeDescription *GetVolumeDesc(const TString &path); + + /* Porto v5 api */ + EError ListVolumesBy(const TGetVolumeRequest &getVolumeRequest, TVector<TVolumeSpec> &volumes) Y_WARN_UNUSED_RESULT; + EError CreateVolumeFromSpec(const TVolumeSpec &volume, TVolumeSpec &resultSpec) Y_WARN_UNUSED_RESULT; + + const TVolumeSpec *GetVolume(const TString &path); + + const TGetVolumeResponse *GetVolumes(uint64_t changed_since = 0); + + EError CreateVolume(TString &path, + const TMap<TString, TString> &config) Y_WARN_UNUSED_RESULT; + + EError LinkVolume(const TString &path, + const TString &container = "", + const TString &target = "", + bool read_only = false, + bool required = false) Y_WARN_UNUSED_RESULT; + + EError UnlinkVolume(const TString &path, + const TString &container = "", + const TString &target = "***", + bool strict = false) Y_WARN_UNUSED_RESULT; + + EError TuneVolume(const TString &path, + const TMap<TString, TString> &config) Y_WARN_UNUSED_RESULT; + + /* Layer */ + + const TListLayersResponse *ListLayers(const TString &place = "", + const TString &mask = ""); + + EError ListLayers(TVector<TString> &layers, + const TString &place = "", + const TString &mask = "") Y_WARN_UNUSED_RESULT; + + EError ImportLayer(const TString &layer, + const TString &tarball, + bool merge = false, + const TString &place = "", + const TString &private_value = "", + bool verboseError = false) Y_WARN_UNUSED_RESULT; + + EError ExportLayer(const TString &volume, + const TString &tarball, + const TString &compress = "") Y_WARN_UNUSED_RESULT; + + EError ReExportLayer(const TString &layer, + const TString &tarball, + const TString &compress = "") Y_WARN_UNUSED_RESULT; + + EError RemoveLayer(const TString &layer, + const TString &place = "", + bool async = false) Y_WARN_UNUSED_RESULT; + + EError GetLayerPrivate(TString &private_value, + const TString &layer, + const TString &place = "") Y_WARN_UNUSED_RESULT; + + EError SetLayerPrivate(const TString &private_value, + const TString &layer, + const TString &place = "") Y_WARN_UNUSED_RESULT; + + /* Docker images */ + + EError DockerImageStatus(DockerImage &image, + const TString &name, + const TString &place = "") Y_WARN_UNUSED_RESULT; + + EError ListDockerImages(std::vector<DockerImage> &images, + const TString &place = "", + const TString &mask = "") Y_WARN_UNUSED_RESULT; + + EError PullDockerImage(DockerImage &image, + const TString &name, + const TString &place = "", + const TString &auth_token = "", + const TString &auth_host = "", + const TString &auth_service = "") Y_WARN_UNUSED_RESULT; + + EError RemoveDockerImage(const TString &name, + const TString &place = ""); + + /* Storage */ + + const TListStoragesResponse *ListStorages(const TString &place = "", + const TString &mask = ""); + + EError ListStorages(TVector<TString> &storages, + const TString &place = "", + const TString &mask = "") Y_WARN_UNUSED_RESULT; + + EError RemoveStorage(const TString &storage, + const TString &place = "") Y_WARN_UNUSED_RESULT; + + EError ImportStorage(const TString &storage, + const TString &archive, + const TString &place = "", + const TString &compression = "", + const TString &private_value = "") Y_WARN_UNUSED_RESULT; + + EError ExportStorage(const TString &storage, + const TString &archive, + const TString &place = "", + const TString &compression = "") Y_WARN_UNUSED_RESULT; +}; + +#ifdef __linux__ +class TAsyncWaiter { + struct TCallbackData { + const TWaitCallback Callback; + const TString State; + }; + + enum class ERequestType { + None, + Add, + Del, + Stop, + }; + + THashMap<TString, TCallbackData> AsyncCallbacks; + std::unique_ptr<std::thread> WatchDogThread; + std::atomic<uint64_t> CallbacksCount; + int EpollFd = -1; + TPortoApi Api; + + int Sock, MasterSock; + TString ReqCt; + TString ReqState; + TWaitCallback ReqCallback; + + std::function<void(const TString &error, int ret)> FatalCallback; + bool FatalError = false; + + void MainCallback(const TWaitResponse &event); + inline TWaitCallback GetMainCallback() { + return [this](const TWaitResponse &event) { + MainCallback(event); + }; + } + + int Repair(); + void WatchDog(); + + void SendInt(int fd, int value); + int RecvInt(int fd); + + void HandleAddRequest(); + void HandleDelRequest(); + + void Fatal(const TString &error, int ret) { + FatalError = true; + FatalCallback(error, ret); + } + + public: + TAsyncWaiter(std::function<void(const TString &error, int ret)> fatalCallback); + ~TAsyncWaiter(); + + int Add(const TString &ct, const TString &state, TWaitCallback callback); + int Remove(const TString &ct); + uint64_t InvocationCount() const { + return CallbacksCount; + } +}; +#endif + +} /* namespace Porto */ diff --git a/library/cpp/porto/libporto_ut.cpp b/library/cpp/porto/libporto_ut.cpp new file mode 100644 index 0000000000..9d78397fb8 --- /dev/null +++ b/library/cpp/porto/libporto_ut.cpp @@ -0,0 +1,226 @@ +#include <library/cpp/testing/unittest/registar.h> +#include <libporto.hpp> + +#include <signal.h> +#include <cassert> + +#define Expect(a) assert(a) +#define ExpectEq(a, b) assert((a) == (b)) +#define ExpectNeq(a, b) assert((a) != (b)) +#define ExpectSuccess(ret) assert((ret) == Porto::EError::Success) + +const TString CT_NAME = "test-a"; + +void test_porto() { + TVector<TString> list; + TString str, path; + + signal(SIGPIPE, SIG_IGN); + + Porto::TPortoApi api; + + Expect(!api.Connected()); + Expect(api.GetFd() < 0); + + // Connect + ExpectSuccess(api.Connect()); + + Expect(api.Connected()); + Expect(api.GetFd() >= 0); + + // Disconnect + api.Disconnect(); + + Expect(!api.Connected()); + Expect(api.GetFd() < 0); + + // Auto connect + ExpectSuccess(api.GetVersion(str, str)); + Expect(api.Connected()); + + // Auto reconnect + api.Disconnect(); + ExpectSuccess(api.GetVersion(str, str)); + Expect(api.Connected()); + + // No auto reconnect + api.Disconnect(); + api.SetAutoReconnect(false); + ExpectEq(api.GetVersion(str, str), Porto::EError::SocketError); + api.SetAutoReconnect(true); + + uint64_t val = api.GetTimeout(); + ExpectNeq(val, 0); + ExpectSuccess(api.SetTimeout(5)); + + ExpectSuccess(api.List(list)); + + ExpectSuccess(api.ListProperties(list)); + + ExpectSuccess(api.ListVolumes(list)); + + ExpectSuccess(api.ListVolumeProperties(list)); + + ExpectSuccess(api.ListLayers(list)); + + ExpectSuccess(api.ListStorages(list)); + + ExpectSuccess(api.Call("Version {}", str)); + + ExpectSuccess(api.GetProperty("/", "state", str)); + ExpectEq(str, "meta"); + + ExpectSuccess(api.GetProperty("/", "controllers", "memory", str)); + ExpectEq(str, "true"); + + ExpectSuccess(api.GetProperty("/", "memory_usage", str)); + ExpectNeq(str, "0"); + + val = 0; + ExpectSuccess(api.GetInt("/", "memory_usage", val)); + ExpectNeq(val, 0); + + Porto::TContainer ct; + ExpectSuccess(api.GetContainerSpec("/", ct)); + ExpectEq(ct.spec().name(), "/"); + + ExpectEq(api.GetInt("/", "__wrong__", val), Porto::EError::InvalidProperty); + ExpectEq(api.Error(), Porto::EError::InvalidProperty); + ExpectEq(api.GetLastError(str), Porto::EError::InvalidProperty); + + ExpectSuccess(api.GetContainerSpec(CT_NAME, ct)); + ExpectEq(ct.error().error(), Porto::EError::ContainerDoesNotExist); + + ExpectSuccess(api.CreateWeakContainer(CT_NAME)); + + ExpectSuccess(api.SetProperty(CT_NAME, "memory_limit", "20M")); + ExpectSuccess(api.GetProperty(CT_NAME, "memory_limit", str)); + ExpectEq(str, "20971520"); + + ExpectSuccess(api.SetInt(CT_NAME, "memory_limit", 10<<20)); + ExpectSuccess(api.GetInt(CT_NAME, "memory_limit", val)); + ExpectEq(val, 10485760); + + ExpectSuccess(api.SetLabel(CT_NAME, "TEST.a", ".")); + + ExpectSuccess(api.GetContainerSpec(CT_NAME, ct)); + ExpectEq(ct.status().state(), "stopped"); + ExpectEq(ct.spec().memory_limit(), 10 << 20); + + ExpectSuccess(api.WaitContainer(CT_NAME, str)); + ExpectEq(str, "stopped"); + + ExpectSuccess(api.CreateVolume(path, { + {"containers", CT_NAME}, + {"backend", "native"}, + {"space_limit", "1G"}})); + ExpectNeq(path, ""); + + [[maybe_unused]] auto vd = api.GetVolumeDesc(path); + Expect(vd != nullptr); + ExpectEq(vd->path(), path); + + [[maybe_unused]] auto vs = api.GetVolume(path); + Expect(vs != nullptr); + ExpectEq(vs->path(), path); + + ExpectSuccess(api.SetProperty(CT_NAME, "command", "sleep 1000")); + ExpectSuccess(api.Start(CT_NAME)); + + ExpectSuccess(api.GetProperty(CT_NAME, "state", str)); + ExpectEq(str, "running"); + + ExpectSuccess(api.Destroy(CT_NAME)); + + TMap<TString, uint64_t> values; + auto CT_NAME_0 = CT_NAME + "abcd"; + auto CT_NAME_CHILD = CT_NAME + "/b"; + + ExpectSuccess(api.Create(CT_NAME_0)); + ExpectSuccess(api.SetProperty(CT_NAME_0, "command", "sleep 15")); + ExpectSuccess(api.Start(CT_NAME_0)); + + ExpectSuccess(api.Create(CT_NAME)); + ExpectSuccess(api.SetProperty(CT_NAME, "command", "sleep 10")); + ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME, CT_NAME_0}, "ctxsw", values)); + ExpectEq(values[CT_NAME], 0); + ExpectNeq(values[CT_NAME_0], 0); + + ExpectSuccess(api.Start(CT_NAME)); + ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME}, "ctxsw", values)); + ExpectNeq(values[CT_NAME], 0); + + ExpectSuccess(api.Create(CT_NAME_CHILD)); + ExpectSuccess(api.SetProperty(CT_NAME_CHILD, "command", "sleep 10")); + ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME_CHILD}, "ctxsw", values)); + ExpectEq(values[CT_NAME_CHILD], 0); + + ExpectSuccess(api.Start(CT_NAME_CHILD)); + ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME, CT_NAME_CHILD}, "ctxsw", values)); + ExpectNeq(values[CT_NAME_CHILD], 0); + Expect(values[CT_NAME] > values[CT_NAME_CHILD]); + + ExpectSuccess(api.Stop(CT_NAME_CHILD)); + ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME_CHILD}, "ctxsw", values)); + ExpectEq(values[CT_NAME_CHILD], 0); + + ExpectSuccess(api.Stop(CT_NAME)); + ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME}, "ctxsw", values)); + ExpectEq(values[CT_NAME], 0); + + ExpectSuccess(api.Destroy(CT_NAME_CHILD)); + ExpectSuccess(api.Destroy(CT_NAME)); + ExpectSuccess(api.Destroy(CT_NAME_0)); + +#ifdef __linux__ + // test TAsyncWaiter + Porto::TAsyncWaiter waiter([](const TString &error, int ret) { + Y_UNUSED(error); + Y_UNUSED(ret); + + Expect(false); + }); + + TString result; + waiter.Add("abc", "starting", [&result](const Porto::TWaitResponse &event) { + result += event.name() + "-" + event.state(); + }); + + TString name = "abcdef"; + ExpectSuccess(api.Create(name)); + ExpectSuccess(api.SetProperty(name, "command", "sleep 1")); + ExpectSuccess(api.Start(name)); + ExpectSuccess(api.Destroy(name)); + ExpectEq(result, ""); + + // callback work only once + for (int i = 0; i < 3; i++) { + name = "abc"; + ExpectSuccess(api.Create(name)); + ExpectSuccess(api.SetProperty(name, "command", "sleep 1")); + ExpectSuccess(api.Start(name)); + ExpectSuccess(api.Destroy(name)); + ExpectEq(result, "abc-starting"); + } + + waiter.Add("abc", "starting", [&result](const Porto::TWaitResponse &event) { + result += event.name() + "-" + event.state(); + }); + waiter.Remove("abc"); + + name = "abc"; + ExpectSuccess(api.Create(name)); + ExpectSuccess(api.SetProperty(name, "command", "sleep 1")); + ExpectSuccess(api.Start(name)); + ExpectSuccess(api.Destroy(name)); + ExpectEq(result, "abc-starting"); +#endif + + api.Disconnect(); +} + +Y_UNIT_TEST_SUITE(Porto) { + Y_UNIT_TEST(All) { + test_porto(); + } +} diff --git a/library/cpp/porto/metrics.cpp b/library/cpp/porto/metrics.cpp new file mode 100644 index 0000000000..7d17d0aee4 --- /dev/null +++ b/library/cpp/porto/metrics.cpp @@ -0,0 +1,183 @@ +#include "metrics.hpp" + +#include <util/folder/path.h> +#include <util/generic/maybe.h> +#include <util/stream/file.h> + +namespace Porto { + +TMap<TString, TMetric*> ProcMetrics; + +TMetric::TMetric(const TString& name, EMetric metric) { + Name = name; + Metric = metric; + ProcMetrics[name] = this; +} + +void TMetric::ClearValues(const TVector<TString>& names, TMap<TString, uint64_t>& values) const { + values.clear(); + + for (const auto&name : names) + values[name] = 0; +} + +EError TMetric::GetValues(const TVector<TString>& names, TMap<TString, uint64_t>& values, TPortoApi& api) const { + ClearValues(names, values); + + int procFd = open("/proc", O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY); + TFileHandle procFdHandle(procFd); + if (procFd == -1) + return EError::Unknown; + + TVector<TString> tids; + TidSnapshot(tids); + + auto getResponse = api.Get(names, TVector<TString>{"cgroups[freezer]"}); + + if (getResponse == nullptr) + return EError::Unknown; + + const auto containersCgroups = GetCtFreezerCgroups(getResponse); + + for (const auto& tid : tids) { + const TString tidCgroup = GetFreezerCgroup(procFd, tid); + if (tidCgroup == "") + continue; + + TMaybe<uint64_t> metricValue; + + for (const auto& keyval : containersCgroups) { + const TString& containerCgroup = keyval.second; + if (MatchCgroups(tidCgroup, containerCgroup)) { + if (!metricValue) + metricValue = GetMetric(procFd, tid); + values[keyval.first] += *metricValue; + } + } + } + + return EError::Success; +} + +uint64_t TMetric::GetTidSchedMetricValue(int procFd, const TString& tid, const TString& metricName) const { + const TString schedPath = tid + "/sched"; + try { + int fd = openat(procFd, schedPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOCTTY, 0); + TFile file(fd); + if (!file.IsOpen()) + return 0ul; + + TIFStream iStream(file); + TString line; + while (iStream.ReadLine(line)) { + auto metricPos = line.find(metricName); + + if (metricPos != TString::npos) { + auto valuePos = metricPos; + + while (valuePos < line.size() && !::isdigit(line[valuePos])) + ++valuePos; + + TString value = line.substr(valuePos); + if (!value.empty() && IsNumber(value)) + return IntFromString<uint64_t, 10>(value); + } + } + } + catch(...) {} + + return 0ul; +} + +void TMetric::GetPidTasks(const TString& pid, TVector<TString>& tids) const { + TFsPath task("/proc/" + pid + "/task"); + TVector<TString> rawTids; + + try { + task.ListNames(rawTids); + } + catch(...) {} + + for (const auto& tid : rawTids) { + tids.push_back(tid); + } +} + +void TMetric::TidSnapshot(TVector<TString>& tids) const { + TFsPath proc("/proc"); + TVector<TString> rawPids; + + try { + proc.ListNames(rawPids); + } + catch(...) {} + + for (const auto& pid : rawPids) { + if (IsNumber(pid)) + GetPidTasks(pid, tids); + } +} + +TString TMetric::GetFreezerCgroup(int procFd, const TString& tid) const { + const TString cgroupPath = tid + "/cgroup"; + try { + int fd = openat(procFd, cgroupPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOCTTY, 0); + TFile file(fd); + if (!file.IsOpen()) + return TString(); + + TIFStream iStream(file); + TString line; + + while (iStream.ReadLine(line)) { + static const TString freezer = ":freezer:"; + auto freezerPos = line.find(freezer); + + if (freezerPos != TString::npos) { + line = line.substr(freezerPos + freezer.size()); + return line; + } + } + } + catch(...){} + + return TString(); +} + +TMap<TString, TString> TMetric::GetCtFreezerCgroups(const TGetResponse* response) const { + TMap<TString, TString> containersProps; + + for (const auto& ctGetListResponse : response->list()) { + for (const auto& keyval : ctGetListResponse.keyval()) { + if (!keyval.error()) { + TString value = keyval.value(); + static const TString freezerPath = "/sys/fs/cgroup/freezer"; + + if (value.find(freezerPath) != TString::npos) + value = value.substr(freezerPath.size()); + + containersProps[ctGetListResponse.name()] = value; + } + } + } + + return containersProps; +} + +bool TMetric::MatchCgroups(const TString& tidCgroup, const TString& ctCgroup) const { + if (tidCgroup.size() <= ctCgroup.size()) + return tidCgroup == ctCgroup; + return ctCgroup == tidCgroup.substr(0, ctCgroup.size()) && tidCgroup[ctCgroup.size()] == '/'; +} + +class TCtxsw : public TMetric { +public: + TCtxsw() : TMetric(M_CTXSW, EMetric::CTXSW) + {} + + uint64_t GetMetric(int procFd, const TString& tid) const override { + return GetTidSchedMetricValue(procFd, tid, "nr_switches"); + } +} static Ctxsw; + +} /* namespace Porto */ diff --git a/library/cpp/porto/metrics.hpp b/library/cpp/porto/metrics.hpp new file mode 100644 index 0000000000..5b2ffde8d9 --- /dev/null +++ b/library/cpp/porto/metrics.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "libporto.hpp" + +#include <util/generic/map.h> +#include <util/generic/vector.h> +#include <util/string/cast.h> +#include <util/string/type.h> + +#include <library/cpp/porto/proto/rpc.pb.h> +namespace Porto { + +constexpr const char *M_CTXSW = "ctxsw"; + +enum class EMetric { + NONE, + CTXSW, +}; + +class TMetric { +public: + TString Name; + EMetric Metric; + + TMetric(const TString& name, EMetric metric); + + void ClearValues(const TVector<TString>& names, TMap<TString, uint64_t>& values) const; + EError GetValues(const TVector<TString>& names, TMap<TString, uint64_t>& values, TPortoApi& api) const; + + // Returns value of metric from /proc/tid/sched for some tid + uint64_t GetTidSchedMetricValue(int procFd, const TString& tid, const TString& metricName) const; + + void TidSnapshot(TVector<TString>& tids) const; + void GetPidTasks(const TString& pid, TVector<TString>& tids) const; + + // Returns freezer cgroup from /proc/tid/cgroup + TString GetFreezerCgroup(int procFd, const TString& tid) const; + + // Resurns clean cgroup[freezer] for containers names + TMap<TString, TString> GetCtFreezerCgroups(const TGetResponse* response) const; + + // Verify inclusion of container cgroup in process cgroup + bool MatchCgroups(const TString& tidCgroup, const TString& ctCgroup) const; + +private: + virtual uint64_t GetMetric(int procFd, const TString& tid) const = 0; +}; + +extern TMap<TString, TMetric*> ProcMetrics; +} /* namespace Porto */ diff --git a/library/cpp/porto/proto/CMakeLists.darwin-x86_64.txt b/library/cpp/porto/proto/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..9b8be22fe6 --- /dev/null +++ b/library/cpp/porto/proto/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,43 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +get_built_tool_path( + TOOL_protoc_bin + TOOL_protoc_dependency + contrib/tools/protoc/bin + protoc +) +get_built_tool_path( + TOOL_cpp_styleguide_bin + TOOL_cpp_styleguide_dependency + contrib/tools/protoc/plugins/cpp_styleguide + cpp_styleguide +) + +add_library(cpp-porto-proto) +target_link_libraries(cpp-porto-proto PUBLIC + contrib-libs-cxxsupp + yutil + contrib-libs-protobuf +) +target_proto_messages(cpp-porto-proto PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/porto/proto/rpc.proto +) +target_proto_addincls(cpp-porto-proto + ./ + ${CMAKE_SOURCE_DIR}/ + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src +) +target_proto_outs(cpp-porto-proto + --cpp_out=${CMAKE_BINARY_DIR}/ + --cpp_styleguide_out=${CMAKE_BINARY_DIR}/ +) diff --git a/library/cpp/porto/proto/CMakeLists.linux-aarch64.txt b/library/cpp/porto/proto/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..ba0aa7060d --- /dev/null +++ b/library/cpp/porto/proto/CMakeLists.linux-aarch64.txt @@ -0,0 +1,44 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +get_built_tool_path( + TOOL_protoc_bin + TOOL_protoc_dependency + contrib/tools/protoc/bin + protoc +) +get_built_tool_path( + TOOL_cpp_styleguide_bin + TOOL_cpp_styleguide_dependency + contrib/tools/protoc/plugins/cpp_styleguide + cpp_styleguide +) + +add_library(cpp-porto-proto) +target_link_libraries(cpp-porto-proto PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + contrib-libs-protobuf +) +target_proto_messages(cpp-porto-proto PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/porto/proto/rpc.proto +) +target_proto_addincls(cpp-porto-proto + ./ + ${CMAKE_SOURCE_DIR}/ + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src +) +target_proto_outs(cpp-porto-proto + --cpp_out=${CMAKE_BINARY_DIR}/ + --cpp_styleguide_out=${CMAKE_BINARY_DIR}/ +) diff --git a/library/cpp/porto/proto/CMakeLists.linux-x86_64.txt b/library/cpp/porto/proto/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..ba0aa7060d --- /dev/null +++ b/library/cpp/porto/proto/CMakeLists.linux-x86_64.txt @@ -0,0 +1,44 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +get_built_tool_path( + TOOL_protoc_bin + TOOL_protoc_dependency + contrib/tools/protoc/bin + protoc +) +get_built_tool_path( + TOOL_cpp_styleguide_bin + TOOL_cpp_styleguide_dependency + contrib/tools/protoc/plugins/cpp_styleguide + cpp_styleguide +) + +add_library(cpp-porto-proto) +target_link_libraries(cpp-porto-proto PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + contrib-libs-protobuf +) +target_proto_messages(cpp-porto-proto PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/porto/proto/rpc.proto +) +target_proto_addincls(cpp-porto-proto + ./ + ${CMAKE_SOURCE_DIR}/ + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src +) +target_proto_outs(cpp-porto-proto + --cpp_out=${CMAKE_BINARY_DIR}/ + --cpp_styleguide_out=${CMAKE_BINARY_DIR}/ +) diff --git a/library/cpp/porto/proto/CMakeLists.txt b/library/cpp/porto/proto/CMakeLists.txt new file mode 100644 index 0000000000..f8b31df0c1 --- /dev/null +++ b/library/cpp/porto/proto/CMakeLists.txt @@ -0,0 +1,17 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/library/cpp/porto/proto/CMakeLists.windows-x86_64.txt b/library/cpp/porto/proto/CMakeLists.windows-x86_64.txt new file mode 100644 index 0000000000..9b8be22fe6 --- /dev/null +++ b/library/cpp/porto/proto/CMakeLists.windows-x86_64.txt @@ -0,0 +1,43 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +get_built_tool_path( + TOOL_protoc_bin + TOOL_protoc_dependency + contrib/tools/protoc/bin + protoc +) +get_built_tool_path( + TOOL_cpp_styleguide_bin + TOOL_cpp_styleguide_dependency + contrib/tools/protoc/plugins/cpp_styleguide + cpp_styleguide +) + +add_library(cpp-porto-proto) +target_link_libraries(cpp-porto-proto PUBLIC + contrib-libs-cxxsupp + yutil + contrib-libs-protobuf +) +target_proto_messages(cpp-porto-proto PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/porto/proto/rpc.proto +) +target_proto_addincls(cpp-porto-proto + ./ + ${CMAKE_SOURCE_DIR}/ + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src +) +target_proto_outs(cpp-porto-proto + --cpp_out=${CMAKE_BINARY_DIR}/ + --cpp_styleguide_out=${CMAKE_BINARY_DIR}/ +) diff --git a/library/cpp/porto/proto/rpc.proto b/library/cpp/porto/proto/rpc.proto new file mode 100644 index 0000000000..abb8c63905 --- /dev/null +++ b/library/cpp/porto/proto/rpc.proto @@ -0,0 +1,1607 @@ +syntax = "proto2"; + +option go_package = "github.com/ydb-platform/ydb/library/cpp/porto/proto;myapi"; + +/* + Portod daemon listens on /run/portod.socket unix socket. + + Request: Varint32 length, TPortoRequest request + Response: Varint32 length, TPortoResponse response + + Command is defined by optional nested message field. + Result will be in nested message with the same name. + + Push notification is send as out of order response. + + Access level depends on client container and uid. + + See defails in porto.md or manpage porto + + TContainer, TVolume and related methods are Porto v5 API. +*/ + +package Porto; + +// List of error codes +enum EError { + // No errors occured. + Success = 0; + + // Unclassified error, usually unexpected syscall fail. + Unknown = 1; + + // Unknown method or bad request. + InvalidMethod = 2; + + // Container with specified name already exists. + ContainerAlreadyExists = 3; + + // Container with specified name doesn't exist. + ContainerDoesNotExist = 4; + + // Unknown property specified. + InvalidProperty = 5; + + // Unknown data specified. + InvalidData = 6; + + // Invalid value of property or data. + InvalidValue = 7; + + // Can't perform specified operation in current container state. + InvalidState = 8; + + // Permanent faulure: old kernel version, missing feature, configuration, etc. + NotSupported = 9; + + // Temporary failure: too much objects, not enough memory, etc. + ResourceNotAvailable = 10; + + // Insufficient rights for performing requested operation. + Permission = 11; + + // Can't create new volume with specified name, because there is already one. + VolumeAlreadyExists = 12; + + // Volume with specified name doesn't exist. + VolumeNotFound = 13; + + // Not enough disk space. + NoSpace = 14; + + // Object in use. + Busy = 15; + + // Volume already linked with container. + VolumeAlreadyLinked = 16; + + // Volume not linked with container. + VolumeNotLinked = 17; + + // Layer with this name already exists. + LayerAlreadyExists = 18; + + // Layer with this name not found. + LayerNotFound = 19; + + // Property has no value, data source permanently not available. + NoValue = 20; + + // Volume under construction or destruction. + VolumeNotReady = 21; + + // Cannot parse or execute command. + InvalidCommand = 22; + + // Error code is lost or came from future. + LostError = 23; + + // Device node not found. + DeviceNotFound = 24; + + // Path does not match restricitons or does not exist. + InvalidPath = 25; + + // Wrong or unuseable ip address. + InvalidNetworkAddress = 26; + + // Porto in system maintenance state. + PortoFrozen = 27; + + // Label with this name is not set. + LabelNotFound = 28; + + // Label name does not meet restrictions. + InvalidLabel = 29; + + // Errors in tar, on archive extraction + HelperError = 30; + HelperFatalError = 31; + + // Generic object not found. + NotFound = 404; + + // Reserved error code for client library. + SocketError = 502; + + // Reserved error code for client library. + SocketUnavailable = 503; + + // Reserved error code for client library. + SocketTimeout = 504; + + // Portod close client connections on reload + PortodReloaded = 505; + + // Reserved error code for taints. + Taint = 666; + + // Reserved error codes 700-800 to docker + Docker = 700; + DockerImageNotFound = 701; + + // Internal error code, not for users. + Queued = 1000; +} + + +message TPortoRequest { + + /* System methods */ + + // Get portod version + optional TVersionRequest Version = 14; + + // Get portod statistics + optional TGetSystemRequest GetSystem = 300; + + // Change portod state (for host root user only) + optional TSetSystemRequest SetSystem = 301; + + /* Container methods */ + + // Create new container + optional TCreateRequest Create = 1; + + // Create new contaienr and auto destroy when client disconnects + optional TCreateRequest CreateWeak = 17; + + // Force kill all and destroy container and nested containers + optional TDestroyRequest Destroy = 2; + + // List container names in current namespace + optional TListRequest List = 3; + + // Start contianer and parents if needed + optional TStartRequest Start = 7; + + // Kill all and stop container + optional TStopRequest Stop = 8; + + // Freeze execution + optional TPauseRequest Pause = 9; + + // Resume execution + optional TResumeRequest Resume = 10; + + // Send signal to main process + optional TKillRequest Kill = 13; + + // Restart dead container + optional TRespawnRequest Respawn = 18; + + // Wait for process finish or change of labels + optional TWaitRequest Wait = 16; + + // Subscribe to push notifictaions + optional TWaitRequest AsyncWait = 19; + optional TWaitRequest StopAsyncWait = 128; + + /* Container properties */ + + // List supported container properties + optional TListPropertiesRequest ListProperties = 11; + + // Get one property + optional TGetPropertyRequest GetProperty = 4; + + // Set one property + optional TSetPropertyRequest SetProperty = 5; + + // Deprecated, now data properties are also read-only properties + optional TListDataPropertiesRequest ListDataProperties = 12; + optional TGetDataPropertyRequest GetDataProperty = 6; + + // Get multiple properties for multiple containers + optional TGetRequest Get = 15; + + /* Container API based on TContainer (Porto v5 API) */ + + // Create, configure and start container with volumes + optional TCreateFromSpecRequest CreateFromSpec = 230; + + // Set multiple container properties + optional TUpdateFromSpecRequest UpdateFromSpec = 231; + + // Get multiple properties for multiple containers + optional TListContainersRequest ListContainersBy = 232; + + // Modify symlink in container + optional TSetSymlinkRequest SetSymlink = 125; + + /* Container labels - user defined key-value */ + + // Find containers with labels + optional TFindLabelRequest FindLabel = 20; + + // Atomic compare and set for label + optional TSetLabelRequest SetLabel = 21; + + // Atomic add and return for counter in label + optional TIncLabelRequest IncLabel = 22; + + /* Volume methods */ + + optional TListVolumePropertiesRequest ListVolumeProperties = 103; + + // List layers and their properties + optional TListVolumesRequest ListVolumes = 107; + + // Create, configure and build volume + optional TCreateVolumeRequest CreateVolume = 104; + + // Change volume properties - for now only resize + optional TTuneVolumeRequest TuneVolume = 108; + + // Volume API based on TVolume (Porto v5 API) + optional TNewVolumeRequest NewVolume = 126; + optional TGetVolumeRequest GetVolume = 127; + + // Add link between container and volume + optional TLinkVolumeRequest LinkVolume = 105; + + // Same as LinkVolume but fails if target is not supported + optional TLinkVolumeRequest LinkVolumeTarget = 120; + + // Del link between container and volume + optional TUnlinkVolumeRequest UnlinkVolume = 106; + + // Same as UnlinkVolume but fails if target is not supported + optional TUnlinkVolumeRequest UnlinkVolumeTarget = 121; + + /* Layer methods */ + + // Import layer from tarball + optional TImportLayerRequest ImportLayer = 110; + + // Remove layer + optional TRemoveLayerRequest RemoveLayer = 111; + + // List layers + optional TListLayersRequest ListLayers = 112; + + // Export volume or layer into tarball + optional TExportLayerRequest ExportLayer = 113; + + // Get/set layer private (user defined string) + optional TGetLayerPrivateRequest GetLayerPrivate = 114; + optional TSetLayerPrivateRequest SetLayerPrivate = 115; + + /* Storage methods */ + + // Volume creation creates required storage if missing + + // List storages and meta storages + optional TListStoragesRequest ListStorages = 116; + + optional TRemoveStorageRequest RemoveStorage = 117; + + // Import storage from tarball + optional TImportStorageRequest ImportStorage = 118; + + // Export storage into tarball + optional TExportStorageRequest ExportStorage = 119; + + // Meta storage (bundle for storages and layers) + + optional TMetaStorage CreateMetaStorage = 122; + optional TMetaStorage ResizeMetaStorage = 123; + optional TMetaStorage RemoveMetaStorage = 124; + + // Convert path between containers + optional TConvertPathRequest ConvertPath = 200; + + /* Process methods */ + + // Attach process to nested container + optional TAttachProcessRequest AttachProcess = 201; + + // Find container for process + optional TLocateProcessRequest LocateProcess = 202; + + // Attach one thread to nexted container + optional TAttachProcessRequest AttachThread = 203; + + /* Docker images API */ + + optional TDockerImageStatusRequest dockerImageStatus = 303; + optional TDockerImageListRequest listDockerImages = 304; + optional TDockerImagePullRequest pullDockerImage = 305; + optional TDockerImageRemoveRequest removeDockerImage = 306; +} + + +message TPortoResponse { + // Actually always set, hack for adding new error codes + optional EError error = 1 [ default = LostError ]; + + // Human readable comment - must be shown to user as is + optional string errorMsg = 2; + + optional uint64 timestamp = 1000; // for next changed_since + + /* System methods */ + + optional TVersionResponse Version = 8; + + optional TGetSystemResponse GetSystem = 300; + optional TSetSystemResponse SetSystem = 301; + + /* Container methods */ + + optional TListResponse List = 3; + + optional TWaitResponse Wait = 11; + + optional TWaitResponse AsyncWait = 19; + + /* Container properties */ + + optional TListPropertiesResponse ListProperties = 6; + + optional TGetPropertyResponse GetProperty = 4; + + + // Deprecated + optional TListDataPropertiesResponse ListDataProperties = 7; + optional TGetDataPropertyResponse GetDataProperty = 5; + + optional TGetResponse Get = 10; + + /* Container API based on TContainer (Porto v5 API) */ + + optional TListContainersResponse ListContainersBy = 232; + + /* Container Labels */ + + optional TFindLabelResponse FindLabel = 20; + optional TSetLabelResponse SetLabel = 21; + optional TIncLabelResponse IncLabel = 22; + + /* Volume methods */ + + optional TListVolumePropertiesResponse ListVolumeProperties = 12; + + optional TListVolumesResponse ListVolumes = 9; + + optional TVolumeDescription CreateVolume = 13; + + optional TNewVolumeResponse NewVolume = 126; + + optional TGetVolumeResponse GetVolume = 127; + + optional TListLayersResponse ListLayers = 14; + + optional TGetLayerPrivateResponse GetLayerPrivate = 16; + + // List storages and meta storages + optional TListStoragesResponse ListStorages = 17; + + optional TConvertPathResponse ConvertPath = 15; + + // Process + optional TLocateProcessResponse LocateProcess = 18; + + /* Docker images API */ + + optional TDockerImageStatusResponse dockerImageStatus = 302; + optional TDockerImageListResponse listDockerImages = 303; + optional TDockerImagePullResponse pullDockerImage = 304; +} + + +// Common objects + + +message TStringMap { + message TStringMapEntry { + optional string key = 1; + optional string val = 2; + } + // TODO replace with map + // map<string, string> map = 1; + repeated TStringMapEntry map = 1; + optional bool merge = 2; // in, default: replace +} + + +message TUintMap { + message TUintMapEntry { + optional string key = 1; + optional uint64 val = 2; + } + // TODO replace with map + // map<string, uint64> map = 1; + repeated TUintMapEntry map = 1; + optional bool merge = 2; // in, default: replace +} + + +message TError { + optional EError error = 1 [ default = LostError ]; + optional string msg = 2; +} + + +message TCred { + optional string user = 1; // requires user or uid or both + optional fixed32 uid = 2; + optional string group = 3; + optional fixed32 gid = 4; + repeated fixed32 grp = 5; // out, supplementary groups +} + + +message TCapabilities { + repeated string cap = 1; + optional string hex = 2; // out +} + + +message TContainerCommandArgv { + repeated string argv = 1; +} + + +// Container + + +message TContainerEnvVar { + optional string name = 1; //required + optional string value = 2; + optional bool unset = 3; // out + optional string salt = 4; + optional string hash = 5; +} + +message TContainerEnv { + repeated TContainerEnvVar var = 1; + optional bool merge = 2; // in, default: replace +} + + +message TContainerUlimit { + optional string type = 1; //required + optional bool unlimited = 2; + optional uint64 soft = 3; + optional uint64 hard = 4; + optional bool inherited = 5; // out +} + +message TContainerUlimits { + repeated TContainerUlimit ulimit = 1; + optional bool merge = 2; // in, default: replace +} + + +message TContainerControllers { + repeated string controller = 1; +} + + +message TContainerCgroup { + optional string controller = 1; //required + optional string path = 2; //required + optional bool inherited = 3; +} + +message TContainerCgroups { + repeated TContainerCgroup cgroup = 1; +} + + +message TContainerCpuSet { + optional string policy = 1; // inherit|set|node|reserve|threads|cores + optional uint32 arg = 2; // for node|reserve|threads|cores + optional string list = 3; // for set + repeated uint32 cpu = 4; // for set (used if list isn't set) + optional uint32 count = 5; // out + optional string mems = 6; +} + + +message TContainerBindMount { + optional string source = 1; //required + optional string target = 2; //required + repeated string flag = 3; +} + +message TContainerBindMounts { + repeated TContainerBindMount bind = 1; +} + + +message TContainerVolumeLink { + optional string volume = 1; //required + optional string target = 2; + optional bool required = 3; + optional bool read_only = 4; +} + +message TContainerVolumeLinks { + repeated TContainerVolumeLink link = 1; +} + + +message TContainerVolumes { + repeated string volume = 1; +} + + +message TContainerPlace { + optional string place = 1; //required + optional string alias = 2; +} + +message TContainerPlaceConfig { + repeated TContainerPlace cfg = 1; +} + + +message TContainerDevice { + optional string device = 1; //required + optional string access = 2; //required + optional string path = 3; + optional string mode = 4; + optional string user = 5; + optional string group = 6; +} + +message TContainerDevices { + repeated TContainerDevice device = 1; + optional bool merge = 2; // in, default: replace +} + + +message TContainerNetOption { + optional string opt = 1; //required + repeated string arg = 2; +} + +message TContainerNetConfig { + repeated TContainerNetOption cfg = 1; + optional bool inherited = 2; // out +} + + +message TContainerIpLimit { + optional string policy = 1; //required any|none|some + repeated string ip = 2; +} + + +message TContainerIpConfig { + message TContainerIp { + optional string dev = 1; //required + optional string ip = 2; //required + } + repeated TContainerIp cfg = 1; +} + + +message TVmStat { + optional uint64 count = 1; + optional uint64 size = 2; + optional uint64 max_size = 3; + optional uint64 used = 4; + optional uint64 max_used = 5; + optional uint64 anon = 6; + optional uint64 file = 7; + optional uint64 shmem = 8; + optional uint64 huge = 9; + optional uint64 swap = 10; + optional uint64 data = 11; + optional uint64 stack = 12; + optional uint64 code = 13; + optional uint64 locked = 14; + optional uint64 table = 15; +} + +message TContainerStatus { + optional string absolute_name = 1; // out, "/porto/..." + optional string state = 2; // out + optional uint64 id = 3; // out + optional uint32 level = 4; // out + optional string parent = 5; // out, "/porto/..." + + optional string absolute_namespace = 6; // out + + optional int32 root_pid = 7; // out + optional int32 exit_status = 8; // out + optional int32 exit_code = 9; // out + optional bool core_dumped = 10; // out + optional TError start_error = 11; // out + optional uint64 time = 12; // out + optional uint64 dead_time = 13; // out + + optional TCapabilities capabilities_allowed = 14; // out + optional TCapabilities capabilities_ambient_allowed = 15; // out + optional string root_path = 16; // out, in client namespace + optional uint64 stdout_offset = 17; // out + optional uint64 stderr_offset = 18; // out + optional string std_err = 69; // out + optional string std_out = 70; // out + + optional uint64 creation_time = 19; // out + optional uint64 start_time = 20; // out + optional uint64 death_time = 21; // out + optional uint64 change_time = 22; // out + optional bool no_changes = 23; // out, change_time < changed_since + optional string extra_properties = 73; + + optional TContainerCgroups cgroups = 24; // out + optional TContainerCpuSet cpu_set_affinity = 25; // out + + optional uint64 cpu_usage = 26; // out + optional uint64 cpu_usage_system = 27; // out + optional uint64 cpu_wait = 28; // out + optional uint64 cpu_throttled = 29; // out + + optional uint64 process_count = 30; // out + optional uint64 thread_count = 31; // out + + optional TUintMap io_read = 32; // out, bytes + optional TUintMap io_write = 33; // out, bytes + optional TUintMap io_ops = 34; // out, ops + optional TUintMap io_read_ops = 341; // out, ops + optional TUintMap io_write_ops = 342; // out, ops + optional TUintMap io_time = 35; // out, ns + optional TUintMap io_pressure = 351; // out + + optional TUintMap place_usage = 36; + optional uint64 memory_usage = 37; // out, bytes + + optional uint64 memory_guarantee_total = 38; // out + + optional uint64 memory_limit_total = 39; // out + + optional uint64 anon_limit_total = 40; + optional uint64 anon_usage = 41; // out, bytes + optional double cpu_guarantee_total = 42; + optional double cpu_guarantee_bound = 421; + optional double cpu_limit_total = 422; + optional double cpu_limit_bound = 423; + + optional uint64 cache_usage = 43; // out, bytes + + optional uint64 hugetlb_usage = 44; // out, bytes + optional uint64 hugetlb_limit = 45; + + optional uint64 minor_faults = 46; // out + optional uint64 major_faults = 47; // out + optional uint64 memory_reclaimed = 48; // out + optional TVmStat virtual_memory = 49; // out + + optional uint64 shmem_usage = 71; // out, bytes + optional uint64 mlock_usage = 72; // out, bytes + + optional uint64 oom_kills = 50; // out + optional uint64 oom_kills_total = 51; // out + optional bool oom_killed = 52; // out + + optional TUintMap net_bytes = 54; // out + optional TUintMap net_packets = 55; // out + optional TUintMap net_drops = 56; // out + optional TUintMap net_overlimits = 57; // out + optional TUintMap net_rx_bytes = 58; // out + optional TUintMap net_rx_packets = 59; // out + optional TUintMap net_rx_drops = 60; // out + optional TUintMap net_tx_bytes = 61; // out + optional TUintMap net_tx_packets = 62; // out + optional TUintMap net_tx_drops = 63; // out + + optional TContainerVolumeLinks volumes_linked = 64; // out + optional TContainerVolumes volumes_owned = 65; + + repeated TError error = 66; // out + repeated TError warning = 67; // out + repeated TError taint = 68; // out +} + +message TContainerSpec { + optional string name = 1; // required / in client namespace + optional bool weak = 2; + optional string private = 3; + optional TStringMap labels = 4; + + optional string command = 5; + optional TContainerCommandArgv command_argv = 76; + optional TContainerEnv env = 6; + optional TContainerEnv env_secret = 90; // in, out hides values + optional TContainerUlimits ulimit = 7; + optional string core_command = 8; + + optional bool isolate = 9; + optional string virt_mode = 10; + optional string enable_porto = 11; + optional string porto_namespace = 12; + optional string cgroupfs = 78; + optional bool userns = 79; + + optional uint64 aging_time = 13; + + optional TCred task_cred = 14; + optional string user = 15; + optional string group = 16; + + optional TCred owner_cred = 17; + optional string owner_user = 18; + optional string owner_group = 19; + optional string owner_containers = 77; + + optional TCapabilities capabilities = 20; + optional TCapabilities capabilities_ambient = 21; + + optional string root = 22; // in parent namespace + optional bool root_readonly = 23; + optional TContainerBindMounts bind = 24; + optional TStringMap symlink = 25; + optional TContainerDevices devices = 26; + optional TContainerPlaceConfig place = 27; + optional TUintMap place_limit = 28; + + optional string cwd = 29; + optional string stdin_path = 30; + optional string stdout_path = 31; + optional string stderr_path = 32; + optional uint64 stdout_limit = 33; + optional uint32 umask = 34; + + optional bool respawn = 35; + optional uint64 respawn_count = 36; + optional int64 max_respawns = 37; + optional uint64 respawn_delay = 38; + + optional TContainerControllers controllers = 39; + + optional string cpu_policy = 40; // normal|idle|batch|high|rt + optional double cpu_weight = 41; // 0.01 .. 100 + + optional double cpu_guarantee = 42; // in cores + optional double cpu_limit = 43; // in cores + optional double cpu_limit_total = 44; // deprecated (value moved to TContainerStatus) + optional uint64 cpu_period = 45; // ns + + optional TContainerCpuSet cpu_set = 46; + + optional uint64 thread_limit = 47; + + optional string io_policy = 48; // none|rt|high|normal|batch|idle + optional double io_weight = 49; // 0.01 .. 100 + + optional TUintMap io_limit = 50; // bps + optional TUintMap io_guarantee = 84; // bps + optional TUintMap io_ops_limit = 51; // iops + optional TUintMap io_ops_guarantee = 85; // iops + + optional uint64 memory_guarantee = 52; // bytes + + optional uint64 memory_limit = 53; // bytes + + optional uint64 anon_limit = 54; + optional uint64 anon_max_usage = 55; + + optional uint64 dirty_limit = 56; + + optional uint64 hugetlb_limit = 57; + + optional bool recharge_on_pgfault = 58; + optional bool pressurize_on_death = 59; + optional bool anon_only = 60; + + optional int32 oom_score_adj = 61; // -1000 .. +1000 + optional bool oom_is_fatal = 62; + + optional TContainerNetConfig net = 63; + optional TContainerIpLimit ip_limit = 64; + optional TContainerIpConfig ip = 65; + optional TContainerIpConfig default_gw = 66; + optional string hostname = 67; + optional string resolv_conf = 68; + optional string etc_hosts = 69; + optional TStringMap sysctl = 70; + optional TUintMap net_guarantee = 71; // bytes per second + optional TUintMap net_limit = 72; // bytes per second + optional TUintMap net_rx_limit = 73; // bytes per second + + optional TContainerVolumes volumes_required = 75; +} + +message TContainer { + optional TContainerSpec spec = 1; //required + optional TContainerStatus status = 2; + optional TError error = 3; +} + + +// Volumes + +message TVolumeDescription { + required string path = 1; // path in client namespace + map<string, string> properties = 2; + repeated string containers = 3; // linked containers (legacy) + repeated TVolumeLink links = 4; // linked containers with details + + optional uint64 change_time = 5; // sec since epoch + optional bool no_changes = 6; // change_time < changed_since +} + + +message TVolumeLink { + optional string container = 1; + optional string target = 2; // absolute path in container, default: anon + optional bool required = 3; // container cannot work without it + optional bool read_only = 4; + optional string host_target = 5; // out, absolute path in host + optional bool container_root = 6; // in, set container root + optional bool container_cwd = 7; // in, set container cwd +} + +message TVolumeResource { + optional uint64 limit = 1; // bytes or inodes + optional uint64 guarantee = 2; // bytes or inodes + optional uint64 usage = 3; // out, bytes or inodes + optional uint64 available = 4; // out, bytes or inodes +} + +message TVolumeDirectory { + optional string path = 1; // relative path in volume + optional TCred cred = 2; // default: volume cred + optional fixed32 permissions = 3; // default: volume permissions +} + +message TVolumeSymlink { + optional string path = 1; // relative path in volume + optional string target_path = 2; +} + +message TVolumeShare { + optional string path = 1; // relative path in volume + optional string origin_path = 2; // absolute path to origin + optional bool cow = 3; // default: mutable share +} + +// Structured Volume description (Porto V5 API) + +message TVolumeSpec { + optional string path = 1; // path in container, default: auto + optional string container = 2; // defines root for paths, default: self (client container) + repeated TVolumeLink links = 3; // initial links, default: anon link to self + + optional string id = 4; // out + optional string state = 5; // out + + optional string private_value = 6; // at most 4096 bytes + + optional string device_name = 7; // out + + optional string backend = 10; // default: auto + optional string place = 11; // path in host or alias, default from client container + optional string storage = 12; // persistent storage, path or name, default: non-persistent + optional string image = 52; + repeated string layers = 13; // name or path + optional bool read_only = 14; + + // defines root directory user, group and permissions + optional TCred cred = 20; // default: self task cred + optional fixed32 permissions = 21; // default: 0775 + + optional TVolumeResource space = 22; + optional TVolumeResource inodes = 23; + + optional TCred owner = 30; // default: self owner + optional string owner_container = 31; // default: self + optional string place_key = 32; // out, key for place_limit + optional string creator = 33; // out + optional bool auto_path = 34; // out + optional uint32 device_index = 35; // out + optional uint64 build_time = 37; // out, sec since epoch + + // customization at creation + repeated TVolumeDirectory directories = 40; // in + repeated TVolumeSymlink symlinks = 41; // in + repeated TVolumeShare shares = 42; // in + + optional uint64 change_time = 50; // out, sec since epoch + optional bool no_changes = 51; // out, change_time < changed_since +} + + +message TLayer { + optional string name = 1; // name or meta/name + optional string owner_user = 2; + optional string owner_group = 3; + optional uint64 last_usage = 4; // out, sec since last usage + optional string private_value = 5; +} + + +message TStorage { + optional string name = 1; // name or meta/name + optional string owner_user = 2; + optional string owner_group = 3; + optional uint64 last_usage = 4; // out, sec since last usage + optional string private_value = 5; +} + + +message TMetaStorage { + optional string name = 1; + optional string place = 2; + optional string private_value = 3; + optional uint64 space_limit = 4; // bytes + optional uint64 inode_limit = 5; // inodes + + optional uint64 space_used = 6; // out, bytes + optional uint64 space_available = 7; // out, bytes + optional uint64 inode_used = 8; // out, inodes + optional uint64 inode_available = 9; // out, inodes + optional string owner_user = 10; // out + optional string owner_group = 11; // out + optional uint64 last_usage = 12; // out, sec since last usage +} + + +// COMMANDS + +// System + +// Get porto version +message TVersionRequest { +} + +message TVersionResponse { + optional string tag = 1; + optional string revision = 2; +} + + +// Get porto statistics +message TGetSystemRequest { +} + +message TGetSystemResponse { + optional string porto_version = 1; + optional string porto_revision = 2; + optional string kernel_version = 3; + + optional fixed64 errors = 4; + optional fixed64 warnings = 5; + optional fixed64 porto_starts = 6; + optional fixed64 porto_uptime = 7; + optional fixed64 master_uptime = 8; + optional fixed64 taints = 9; + + optional bool frozen = 10; + optional bool verbose = 100; + optional bool debug = 101; + optional fixed64 log_lines = 102; + optional fixed64 log_bytes = 103; + + optional fixed64 stream_rotate_bytes = 104; + optional fixed64 stream_rotate_errors = 105; + + optional fixed64 log_lines_lost = 106; + optional fixed64 log_bytes_lost = 107; + optional fixed64 log_open = 108; + + optional fixed64 container_count = 200; + optional fixed64 container_limit = 201; + optional fixed64 container_running = 202; + optional fixed64 container_created = 203; + optional fixed64 container_started = 204; + optional fixed64 container_start_failed = 205; + optional fixed64 container_oom = 206; + optional fixed64 container_buried = 207; + optional fixed64 container_lost = 208; + optional fixed64 container_tainted = 209; + + optional fixed64 volume_count = 300; + optional fixed64 volume_limit = 301; + optional fixed64 volume_created = 303; + optional fixed64 volume_failed = 304; + optional fixed64 volume_links = 305; + optional fixed64 volume_links_mounted = 306; + optional fixed64 volume_lost = 307; + + optional fixed64 layer_import = 390; + optional fixed64 layer_export = 391; + optional fixed64 layer_remove = 392; + + optional fixed64 client_count = 400; + optional fixed64 client_max = 401; + optional fixed64 client_connected = 402; + + optional fixed64 request_queued = 500; + optional fixed64 request_completed = 501; + optional fixed64 request_failed = 502; + optional fixed64 request_threads = 503; + optional fixed64 request_longer_1s = 504; + optional fixed64 request_longer_3s = 505; + optional fixed64 request_longer_30s = 506; + optional fixed64 request_longer_5m = 507; + + optional fixed64 fail_system = 600; + optional fixed64 fail_invalid_value = 601; + optional fixed64 fail_invalid_command = 602; + optional fixed64 fail_memory_guarantee = 603; + optional fixed64 fail_invalid_netaddr = 604; + + optional fixed64 porto_crash = 666; + + optional fixed64 network_count = 700; + optional fixed64 network_created = 701; + optional fixed64 network_problems = 702; + optional fixed64 network_repairs = 703; +} + + +// Change porto state +message TSetSystemRequest { + optional bool frozen = 10; + optional bool verbose = 100; + optional bool debug = 101; +} + +message TSetSystemResponse { +} + +message TCreateFromSpecRequest { + optional TContainerSpec container = 1; //required + repeated TVolumeSpec volumes = 2; + optional bool start = 3; +} + +message TUpdateFromSpecRequest { + optional TContainerSpec container = 1; //required + optional bool start = 2; +} + +message TListContainersFilter { + optional string name = 1; // name or wildcards, default: all + optional TStringMap labels = 2; + optional uint64 changed_since = 3; // change_time >= changed_since +} + +message TStreamDumpOptions { + optional uint64 stdstream_offset = 2; // default: 0 + optional uint64 stdstream_limit = 3; // default: 8Mb +} + +message TListContainersFieldOptions { + repeated string properties = 1; // property names, default: all + optional TStreamDumpOptions stdout_options = 2; // for GetIndexed stdout + optional TStreamDumpOptions stderr_options = 3; // for GetIndexed stderr +} + +message TListContainersRequest { + repeated TListContainersFilter filters = 1; + optional TListContainersFieldOptions field_options = 2; +} + +message TListContainersResponse { + repeated TContainer containers = 1; +} + +// List available properties +message TListPropertiesRequest { +} + +message TListPropertiesResponse { + message TContainerPropertyListEntry { + optional string name = 1; + optional string desc = 2; + optional bool read_only = 3; + optional bool dynamic = 4; + } + repeated TContainerPropertyListEntry list = 1; +} + + +// deprecated, use ListProperties +message TListDataPropertiesRequest { +} + +message TListDataPropertiesResponse { + message TContainerDataListEntry { + optional string name = 1; + optional string desc = 2; + } + repeated TContainerDataListEntry list = 1; +} + + +// Create stopped container +message TCreateRequest { + optional string name = 1; +} + + +// Stop and destroy container +message TDestroyRequest { + optional string name = 1; +} + + +// List container names +message TListRequest { + optional string mask = 1; + optional uint64 changed_since = 2; // change_time >= changed_since +} + +message TListResponse { + repeated string name = 1; + optional string absolute_namespace = 2; +} + + +// Read one property +message TGetPropertyRequest { + optional string name = 1; + optional string property = 2; + // update cached counters + optional bool sync = 3; + optional bool real = 4; +} + +message TGetPropertyResponse { + optional string value = 1; +} + + +// Alias for GetProperty, deprecated +message TGetDataPropertyRequest { + optional string name = 1; + optional string data = 2; + // update cached counters + optional bool sync = 3; + optional bool real = 4; +} + +message TGetDataPropertyResponse { + optional string value = 1; +} + + +// Change one property +message TSetPropertyRequest { + optional string name = 1; + optional string property = 2; + optional string value = 3; +} + + +// Get multiple properties/data of many containers with one request +message TGetRequest { + // list of containers or wildcards, "***" - all + repeated string name = 1; + + // list of properties/data + repeated string variable = 2; + + // do not wait busy containers + optional bool nonblock = 3; + + // update cached counters + optional bool sync = 4; + optional bool real = 5; + + // change_time >= changed_since + optional uint64 changed_since = 6; +} + +message TGetResponse { + message TContainerGetValueResponse { + optional string variable = 1; + optional EError error = 2; + optional string errorMsg = 3; + optional string value = 4; + } + + message TContainerGetListResponse { + optional string name = 1; + repeated TContainerGetValueResponse keyval = 2; + + optional uint64 change_time = 3; + optional bool no_changes = 4; // change_time < changed_since + } + + repeated TContainerGetListResponse list = 1; +} + + +// Start stopped container +message TStartRequest { + optional string name = 1; +} + + +// Restart dead container +message TRespawnRequest { + optional string name = 1; +} + + +// Stop dead or running container +message TStopRequest { + optional string name = 1; + // Timeout in 1/1000 seconds between SIGTERM and SIGKILL, default 30s + optional uint32 timeout_ms = 2; +} + + +// Freeze running container +message TPauseRequest { + optional string name = 1; +} + + +// Unfreeze paused container +message TResumeRequest { + optional string name = 1; +} + + +// Translate filesystem path between containers +message TConvertPathRequest { + optional string path = 1; + optional string source = 2; + optional string destination = 3; +} + +message TConvertPathResponse { + optional string path = 1; +} + + +// Wait while container(s) is/are in running state +message TWaitRequest { + // list of containers or wildcards, "***" - all + repeated string name = 1; + + // timeout in 1/1000 seconds, 0 - nonblock + optional uint32 timeout_ms = 2; + + // list of label names or wildcards + repeated string label = 3; + + // async wait with target_state works only once + optional string target_state = 4; +} + +message TWaitResponse { + optional string name = 1; // container name + optional string state = 2; // container state or "timeout" + optional uint64 when = 3; // unix time stamp in seconds + optional string label = 4; + optional string value = 5; +} + + +// Send signal main process in container +message TKillRequest { + optional string name = 1; + optional int32 sig = 2; +} + + +// Move process into container +message TAttachProcessRequest { + optional string name = 1; + optional uint32 pid = 2; + optional string comm = 3; // ignored if empty +} + + +// Determine container by pid +message TLocateProcessRequest { + optional uint32 pid = 1; + optional string comm = 2; // ignored if empty +} + +message TLocateProcessResponse { + optional string name = 1; +} + + +// Labels + + +message TFindLabelRequest { + optional string mask = 1; // containers name or wildcard + optional string state = 2; // filter by container state + optional string label = 3; // label name or wildcard + optional string value = 4; // filter by label value +} + +message TFindLabelResponse { + message TFindLabelEntry { + optional string name = 1; + optional string state = 2; + optional string label = 3; + optional string value = 4; + } + repeated TFindLabelEntry list = 1; +} + + +message TSetLabelRequest { + optional string name = 1; + optional string label = 2; + optional string value = 3; + optional string prev_value = 4; // fail with Busy if does not match + optional string state = 5; // fail with InvalidState if not match +} + +message TSetLabelResponse { + optional string prev_value = 1; + optional string state = 2; +} + + +message TIncLabelRequest { + optional string name = 1; + optional string label = 2; // missing label starts from 0 + optional int64 add = 3 [ default = 1]; +} + +message TIncLabelResponse { + optional int64 result = 1; +} + + +message TSetSymlinkRequest { + optional string container = 1; + optional string symlink = 2; + optional string target = 3; +} + + +// Volumes + + +message TNewVolumeRequest { + optional TVolumeSpec volume = 1; +} + +message TNewVolumeResponse { + optional TVolumeSpec volume = 1; +} + + +message TGetVolumeRequest { + optional string container = 1; // get paths in container, default: self (client container) + repeated string path = 2; // volume path in container, default: all + optional uint64 changed_since = 3; // change_time >= changed_since + repeated string label = 4; // labels or wildcards +} + +message TGetVolumeResponse { + repeated TVolumeSpec volume = 1; +} + + +// List available volume properties +message TListVolumePropertiesRequest { +} + +message TListVolumePropertiesResponse { + message TVolumePropertyDescription { + optional string name = 1; + optional string desc = 2; + } + repeated TVolumePropertyDescription list = 1; +} + + +// Create new volume +// "createVolume" returns TVolumeDescription in "volume" +message TCreateVolumeRequest { + optional string path = 1; + map<string, string> properties = 2; +} + + +message TLinkVolumeRequest { + optional string path = 1; + optional string container = 2; // default - self (client container) + optional string target = 3; // path in container, "" - anon + optional bool required = 4; // stop container at fail + optional bool read_only = 5; +} + + +message TUnlinkVolumeRequest { + optional string path = 1; + optional string container = 2; // default - self, "***" - all + optional bool strict = 3; // non-lazy umount + optional string target = 4; // path in container, "" - anon, default - "***" - all +} + + +message TListVolumesRequest { + optional string path = 1; // volume path or wildcard + optional string container = 2; + optional uint64 changed_since = 3; // change_time >= changed_since +} + +message TListVolumesResponse { + repeated TVolumeDescription volumes = 1; +} + + +message TTuneVolumeRequest { + optional string path = 1; + map<string, string> properties = 2; +} + +// Layers + + +message TListLayersRequest { + optional string place = 1; // default from client container + optional string mask = 2; +} + +message TListLayersResponse { + repeated string layer = 1; // layer names (legacy) + repeated TLayer layers = 2; // layer with description +} + + +message TImportLayerRequest { + optional string layer = 1; + optional string tarball = 2; + optional bool merge = 3; + optional string place = 4; + optional string private_value = 5; + optional string compress = 6; + optional bool verbose_error = 7; +} + + +message TExportLayerRequest { + optional string volume = 1; + optional string tarball = 2; + optional string layer = 3; + optional string place = 4; + optional string compress = 5; +} + + +message TRemoveLayerRequest { + optional string layer = 1; + optional string place = 2; + optional bool async = 3; +} + + +message TGetLayerPrivateRequest { + optional string layer = 1; + optional string place = 2; +} + +message TGetLayerPrivateResponse { + optional string private_value = 1; +} + + +message TSetLayerPrivateRequest { + optional string layer = 1; + optional string place = 2; + optional string private_value = 3; +} + + +// Storages + + +message TListStoragesRequest { + optional string place = 1; + optional string mask = 2; // "name" - storage, "name/" - meta-storage +} + +message TListStoragesResponse { + repeated TStorage storages = 1; + repeated TMetaStorage meta_storages = 2; +} + + +message TRemoveStorageRequest { + optional string name = 1; + optional string place = 2; +} + + +message TImportStorageRequest { + optional string name = 1; + optional string tarball = 2; + optional string place = 3; + optional string private_value = 5; + optional string compress = 6; +} + + +message TExportStorageRequest { + optional string name = 1; + optional string tarball = 2; + optional string place = 3; + optional string compress = 4; +} + + +// Docker images API + + +message TDockerImageConfig { + repeated string cmd = 1; + repeated string env = 2; +} + +message TDockerImage { + required string id = 1; + repeated string tags = 2; + repeated string digests = 3; + repeated string layers = 4; + optional uint64 size = 5; + optional TDockerImageConfig config = 6; +} + + +message TDockerImageStatusRequest { + required string name = 1; + optional string place = 2; +} + +message TDockerImageStatusResponse { + optional TDockerImage image = 1; +} + + +message TDockerImageListRequest { + optional string place = 1; + optional string mask = 2; +} + +message TDockerImageListResponse { + repeated TDockerImage images = 1; +} + + +message TDockerImagePullRequest { + required string name = 1; + optional string place = 2; + optional string auth_token = 3; + optional string auth_path = 4; + optional string auth_service = 5; +} + +message TDockerImagePullResponse { + optional TDockerImage image = 1; +} + + +message TDockerImageRemoveRequest { + required string name = 1; + optional string place = 2; +} diff --git a/library/cpp/porto/proto/ya.make b/library/cpp/porto/proto/ya.make new file mode 100644 index 0000000000..525a807ee0 --- /dev/null +++ b/library/cpp/porto/proto/ya.make @@ -0,0 +1,5 @@ +PROTO_LIBRARY() +INCLUDE_TAGS(GO_PROTO) +SRCS(rpc.proto) +END() + diff --git a/library/cpp/porto/ut/ya.make b/library/cpp/porto/ut/ya.make new file mode 100644 index 0000000000..766a45eb56 --- /dev/null +++ b/library/cpp/porto/ut/ya.make @@ -0,0 +1,4 @@ +UNITTEST_FOR(library/cpp/porto) +TAG(ya:manual sb:portod) +SRCS(libporto_ut.cpp) +END() diff --git a/library/cpp/porto/ya.make b/library/cpp/porto/ya.make new file mode 100644 index 0000000000..e1ccbac281 --- /dev/null +++ b/library/cpp/porto/ya.make @@ -0,0 +1,17 @@ +LIBRARY() + +BUILD_ONLY_IF(WARNING WARNING LINUX) + +PEERDIR( + library/cpp/porto/proto + contrib/libs/protobuf +) + +SRCS( + libporto.cpp + metrics.cpp +) + +END() + +RECURSE_FOR_TESTS(ut) diff --git a/library/cpp/yt/CMakeLists.txt b/library/cpp/yt/CMakeLists.txt index b1dc1594fc..d05e8fb68e 100644 --- a/library/cpp/yt/CMakeLists.txt +++ b/library/cpp/yt/CMakeLists.txt @@ -16,7 +16,9 @@ add_subdirectory(logging) add_subdirectory(malloc) add_subdirectory(memory) add_subdirectory(misc) +add_subdirectory(mlock) add_subdirectory(small_containers) +add_subdirectory(stockpile) add_subdirectory(string) add_subdirectory(system) add_subdirectory(threading) diff --git a/library/cpp/yt/backtrace/cursors/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/backtrace/cursors/CMakeLists.darwin-x86_64.txt index 6c6f5d1c50..76c3eda332 100644 --- a/library/cpp/yt/backtrace/cursors/CMakeLists.darwin-x86_64.txt +++ b/library/cpp/yt/backtrace/cursors/CMakeLists.darwin-x86_64.txt @@ -6,4 +6,6 @@ # original buildsystem will not be accepted. +add_subdirectory(frame_pointer) +add_subdirectory(interop) add_subdirectory(libunwind) diff --git a/library/cpp/yt/backtrace/cursors/CMakeLists.linux-aarch64.txt b/library/cpp/yt/backtrace/cursors/CMakeLists.linux-aarch64.txt index 6c6f5d1c50..76c3eda332 100644 --- a/library/cpp/yt/backtrace/cursors/CMakeLists.linux-aarch64.txt +++ b/library/cpp/yt/backtrace/cursors/CMakeLists.linux-aarch64.txt @@ -6,4 +6,6 @@ # original buildsystem will not be accepted. +add_subdirectory(frame_pointer) +add_subdirectory(interop) add_subdirectory(libunwind) diff --git a/library/cpp/yt/backtrace/cursors/CMakeLists.linux-x86_64.txt b/library/cpp/yt/backtrace/cursors/CMakeLists.linux-x86_64.txt index 6c6f5d1c50..76c3eda332 100644 --- a/library/cpp/yt/backtrace/cursors/CMakeLists.linux-x86_64.txt +++ b/library/cpp/yt/backtrace/cursors/CMakeLists.linux-x86_64.txt @@ -6,4 +6,6 @@ # original buildsystem will not be accepted. +add_subdirectory(frame_pointer) +add_subdirectory(interop) add_subdirectory(libunwind) diff --git a/library/cpp/yt/backtrace/cursors/CMakeLists.windows-x86_64.txt b/library/cpp/yt/backtrace/cursors/CMakeLists.windows-x86_64.txt index 961a9a908b..27fb2d8417 100644 --- a/library/cpp/yt/backtrace/cursors/CMakeLists.windows-x86_64.txt +++ b/library/cpp/yt/backtrace/cursors/CMakeLists.windows-x86_64.txt @@ -7,3 +7,6 @@ add_subdirectory(dummy) +add_subdirectory(frame_pointer) +add_subdirectory(interop) +add_subdirectory(libunwind) diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..9078cd7245 --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,20 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(backtrace-cursors-frame_pointer) +target_compile_options(backtrace-cursors-frame_pointer PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(backtrace-cursors-frame_pointer PUBLIC + contrib-libs-cxxsupp + yutil +) +target_sources(backtrace-cursors-frame_pointer PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/frame_pointer/frame_pointer_cursor.cpp +) diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-aarch64.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..ce9e059d81 --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-aarch64.txt @@ -0,0 +1,21 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(backtrace-cursors-frame_pointer) +target_compile_options(backtrace-cursors-frame_pointer PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(backtrace-cursors-frame_pointer PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil +) +target_sources(backtrace-cursors-frame_pointer PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/frame_pointer/frame_pointer_cursor.cpp +) diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-x86_64.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..ce9e059d81 --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-x86_64.txt @@ -0,0 +1,21 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(backtrace-cursors-frame_pointer) +target_compile_options(backtrace-cursors-frame_pointer PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(backtrace-cursors-frame_pointer PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil +) +target_sources(backtrace-cursors-frame_pointer PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/frame_pointer/frame_pointer_cursor.cpp +) diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.txt new file mode 100644 index 0000000000..f8b31df0c1 --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.txt @@ -0,0 +1,17 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.windows-x86_64.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.windows-x86_64.txt new file mode 100644 index 0000000000..ebfaf2065e --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.windows-x86_64.txt @@ -0,0 +1,17 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(backtrace-cursors-frame_pointer) +target_link_libraries(backtrace-cursors-frame_pointer PUBLIC + contrib-libs-cxxsupp + yutil +) +target_sources(backtrace-cursors-frame_pointer PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/frame_pointer/frame_pointer_cursor.cpp +) diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..8ddc8397b7 --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,22 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(backtrace-cursors-interop) +target_compile_options(backtrace-cursors-interop PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(backtrace-cursors-interop PUBLIC + contrib-libs-cxxsupp + yutil + backtrace-cursors-frame_pointer + contrib-libs-libunwind +) +target_sources(backtrace-cursors-interop PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/interop/interop.cpp +) diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-aarch64.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..221213fdae --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-aarch64.txt @@ -0,0 +1,23 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(backtrace-cursors-interop) +target_compile_options(backtrace-cursors-interop PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(backtrace-cursors-interop PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + backtrace-cursors-frame_pointer + contrib-libs-libunwind +) +target_sources(backtrace-cursors-interop PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/interop/interop.cpp +) diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-x86_64.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..221213fdae --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-x86_64.txt @@ -0,0 +1,23 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(backtrace-cursors-interop) +target_compile_options(backtrace-cursors-interop PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(backtrace-cursors-interop PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + backtrace-cursors-frame_pointer + contrib-libs-libunwind +) +target_sources(backtrace-cursors-interop PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/interop/interop.cpp +) diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.txt new file mode 100644 index 0000000000..f8b31df0c1 --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.txt @@ -0,0 +1,17 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.windows-x86_64.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.windows-x86_64.txt new file mode 100644 index 0000000000..9a7660f685 --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.windows-x86_64.txt @@ -0,0 +1,19 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(backtrace-cursors-interop) +target_link_libraries(backtrace-cursors-interop PUBLIC + contrib-libs-cxxsupp + yutil + backtrace-cursors-frame_pointer + contrib-libs-libunwind +) +target_sources(backtrace-cursors-interop PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/interop/interop.cpp +) diff --git a/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.txt b/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.txt index 606ff46b4b..f8b31df0c1 100644 --- a/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.txt +++ b/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.txt @@ -10,6 +10,8 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarc include(CMakeLists.linux-aarch64.txt) elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) include(CMakeLists.linux-x86_64.txt) endif() diff --git a/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.windows-x86_64.txt b/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.windows-x86_64.txt new file mode 100644 index 0000000000..bea2a794c1 --- /dev/null +++ b/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.windows-x86_64.txt @@ -0,0 +1,18 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(backtrace-cursors-libunwind) +target_link_libraries(backtrace-cursors-libunwind PUBLIC + contrib-libs-cxxsupp + yutil + contrib-libs-libunwind +) +target_sources(backtrace-cursors-libunwind PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/libunwind/libunwind_cursor.cpp +) diff --git a/library/cpp/yt/mlock/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/mlock/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..ca50021faf --- /dev/null +++ b/library/cpp/yt/mlock/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,20 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yt-mlock) +target_compile_options(cpp-yt-mlock PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(cpp-yt-mlock PUBLIC + contrib-libs-cxxsupp + yutil +) +target_sources(cpp-yt-mlock PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/mlock/mlock_other.cpp +) diff --git a/library/cpp/yt/mlock/CMakeLists.linux-aarch64.txt b/library/cpp/yt/mlock/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..68f500a75a --- /dev/null +++ b/library/cpp/yt/mlock/CMakeLists.linux-aarch64.txt @@ -0,0 +1,21 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yt-mlock) +target_compile_options(cpp-yt-mlock PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(cpp-yt-mlock PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil +) +target_sources(cpp-yt-mlock PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/mlock/mlock_linux.cpp +) diff --git a/library/cpp/yt/mlock/CMakeLists.linux-x86_64.txt b/library/cpp/yt/mlock/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..68f500a75a --- /dev/null +++ b/library/cpp/yt/mlock/CMakeLists.linux-x86_64.txt @@ -0,0 +1,21 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yt-mlock) +target_compile_options(cpp-yt-mlock PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(cpp-yt-mlock PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil +) +target_sources(cpp-yt-mlock PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/mlock/mlock_linux.cpp +) diff --git a/library/cpp/yt/mlock/CMakeLists.txt b/library/cpp/yt/mlock/CMakeLists.txt new file mode 100644 index 0000000000..f8b31df0c1 --- /dev/null +++ b/library/cpp/yt/mlock/CMakeLists.txt @@ -0,0 +1,17 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/library/cpp/yt/mlock/CMakeLists.windows-x86_64.txt b/library/cpp/yt/mlock/CMakeLists.windows-x86_64.txt new file mode 100644 index 0000000000..1537ee764b --- /dev/null +++ b/library/cpp/yt/mlock/CMakeLists.windows-x86_64.txt @@ -0,0 +1,17 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yt-mlock) +target_link_libraries(cpp-yt-mlock PUBLIC + contrib-libs-cxxsupp + yutil +) +target_sources(cpp-yt-mlock PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/mlock/mlock_other.cpp +) diff --git a/library/cpp/yt/mlock/README.md b/library/cpp/yt/mlock/README.md new file mode 100644 index 0000000000..b61b6072c4 --- /dev/null +++ b/library/cpp/yt/mlock/README.md @@ -0,0 +1,11 @@ +# mlock + +MlockFileMappings подгружает и лочит в память все страницы исполняемого файла. + +В отличии от вызова mlockall, функция не лочит другие страницы процесса. +mlockall явно выделяет физическую память под все vma. Типичный процесс сначала +стартует и инициализирует аллокатор, а потом уже вызывает функцию для mlock страниц. +Аллокатор при старте выделяет большие диапазоны через mmap, но реально их не использует. +Поэтому mlockall приводит в повышенному потреблению памяти. + +Также, в отличии от mlockall, функция может подгрузить страницы в память сразу. diff --git a/library/cpp/yt/mlock/mlock.h b/library/cpp/yt/mlock/mlock.h new file mode 100644 index 0000000000..035fc47e37 --- /dev/null +++ b/library/cpp/yt/mlock/mlock.h @@ -0,0 +1,11 @@ +#pragma once + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +bool MlockFileMappings(bool populate = true); + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/mlock/mlock_linux.cpp b/library/cpp/yt/mlock/mlock_linux.cpp new file mode 100644 index 0000000000..8791869f95 --- /dev/null +++ b/library/cpp/yt/mlock/mlock_linux.cpp @@ -0,0 +1,89 @@ +#include "mlock.h" + +#include <stdio.h> +#include <sys/mman.h> +#include <stdint.h> +#include <inttypes.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +void PopulateFile(void* ptr, size_t size) +{ + constexpr size_t PageSize = 4096; + + auto* begin = static_cast<volatile char*>(ptr); + for (auto* current = begin; current < begin + size; current += PageSize) { + *current; + } +} + +bool MlockFileMappings(bool populate) +{ + auto* file = ::fopen("/proc/self/maps", "r"); + if (!file) { + return false; + } + + // Each line of /proc/<pid>/smaps has the following format: + // address perms offset dev inode path + // E.g. + // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm + + bool failed = false; + while (true) { + char line[1024]; + if (!fgets(line, sizeof(line), file)) { + break; + } + + char addressStr[64]; + char permsStr[64]; + char offsetStr[64]; + char devStr[64]; + int inode; + if (sscanf(line, "%s %s %s %s %d", + addressStr, + permsStr, + offsetStr, + devStr, + &inode) != 5) + { + continue; + } + + if (inode == 0) { + continue; + } + + if (permsStr[0] != 'r') { + continue; + } + + uintptr_t startAddress; + uintptr_t endAddress; + if (sscanf(addressStr, "%" PRIx64 "-%" PRIx64, + &startAddress, + &endAddress) != 2) + { + continue; + } + + if (::mlock(reinterpret_cast<const void*>(startAddress), endAddress - startAddress) != 0) { + failed = true; + continue; + } + + if (populate) { + PopulateFile(reinterpret_cast<void*>(startAddress), endAddress - startAddress); + } + } + + ::fclose(file); + return !failed; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/mlock/mlock_other.cpp b/library/cpp/yt/mlock/mlock_other.cpp new file mode 100644 index 0000000000..269c5c3cb9 --- /dev/null +++ b/library/cpp/yt/mlock/mlock_other.cpp @@ -0,0 +1,14 @@ +#include "mlock.h" + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +bool MlockFileMappings(bool /* populate */) +{ + return false; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/mlock/unittests/mlock_ut.cpp b/library/cpp/yt/mlock/unittests/mlock_ut.cpp new file mode 100644 index 0000000000..98386622e8 --- /dev/null +++ b/library/cpp/yt/mlock/unittests/mlock_ut.cpp @@ -0,0 +1,19 @@ +#include <gtest/gtest.h> + +#include <library/cpp/yt/mlock/mlock.h> + +namespace NYT { +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +TEST(TMlockTest, Call) +{ + ASSERT_TRUE(MlockFileMappings(false)); + ASSERT_TRUE(MlockFileMappings(true)); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace +} // namespace NYT
\ No newline at end of file diff --git a/library/cpp/yt/mlock/unittests/ya.make b/library/cpp/yt/mlock/unittests/ya.make new file mode 100644 index 0000000000..f1f956d468 --- /dev/null +++ b/library/cpp/yt/mlock/unittests/ya.make @@ -0,0 +1,13 @@ +GTEST() + +INCLUDE(${ARCADIA_ROOT}/library/cpp/yt/ya_cpp.make.inc) + +SRCS( + mlock_ut.cpp +) + +PEERDIR( + library/cpp/yt/mlock +) + +END() diff --git a/library/cpp/yt/mlock/ya.make b/library/cpp/yt/mlock/ya.make new file mode 100644 index 0000000000..2603d128ed --- /dev/null +++ b/library/cpp/yt/mlock/ya.make @@ -0,0 +1,16 @@ +LIBRARY() + +INCLUDE(${ARCADIA_ROOT}/library/cpp/yt/ya_cpp.make.inc) + +IF (OS_LINUX AND NOT SANITIZER_TYPE) + SRCS(mlock_linux.cpp) +ELSE() + SRCS(mlock_other.cpp) +ENDIF() + +END() + +IF (OS_LINUX AND NOT SANITIZER_TYPE) + RECURSE(unittests) +ENDIF() + diff --git a/library/cpp/yt/stockpile/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/stockpile/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..8036bd5d7e --- /dev/null +++ b/library/cpp/yt/stockpile/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,20 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yt-stockpile) +target_compile_options(cpp-yt-stockpile PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(cpp-yt-stockpile PUBLIC + contrib-libs-cxxsupp + yutil +) +target_sources(cpp-yt-stockpile PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/stockpile/stockpile_other.cpp +) diff --git a/library/cpp/yt/stockpile/CMakeLists.linux-aarch64.txt b/library/cpp/yt/stockpile/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..d023cce4da --- /dev/null +++ b/library/cpp/yt/stockpile/CMakeLists.linux-aarch64.txt @@ -0,0 +1,21 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yt-stockpile) +target_compile_options(cpp-yt-stockpile PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(cpp-yt-stockpile PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil +) +target_sources(cpp-yt-stockpile PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/stockpile/stockpile_linux.cpp +) diff --git a/library/cpp/yt/stockpile/CMakeLists.linux-x86_64.txt b/library/cpp/yt/stockpile/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..d023cce4da --- /dev/null +++ b/library/cpp/yt/stockpile/CMakeLists.linux-x86_64.txt @@ -0,0 +1,21 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yt-stockpile) +target_compile_options(cpp-yt-stockpile PRIVATE + -Wdeprecated-this-capture +) +target_link_libraries(cpp-yt-stockpile PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil +) +target_sources(cpp-yt-stockpile PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/stockpile/stockpile_linux.cpp +) diff --git a/library/cpp/yt/stockpile/CMakeLists.txt b/library/cpp/yt/stockpile/CMakeLists.txt new file mode 100644 index 0000000000..f8b31df0c1 --- /dev/null +++ b/library/cpp/yt/stockpile/CMakeLists.txt @@ -0,0 +1,17 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/library/cpp/yt/stockpile/CMakeLists.windows-x86_64.txt b/library/cpp/yt/stockpile/CMakeLists.windows-x86_64.txt new file mode 100644 index 0000000000..d60191d7fe --- /dev/null +++ b/library/cpp/yt/stockpile/CMakeLists.windows-x86_64.txt @@ -0,0 +1,17 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yt-stockpile) +target_link_libraries(cpp-yt-stockpile PUBLIC + contrib-libs-cxxsupp + yutil +) +target_sources(cpp-yt-stockpile PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yt/stockpile/stockpile_other.cpp +) diff --git a/library/cpp/yt/stockpile/README.md b/library/cpp/yt/stockpile/README.md new file mode 100644 index 0000000000..6ee4cd1b1f --- /dev/null +++ b/library/cpp/yt/stockpile/README.md @@ -0,0 +1,12 @@ +# stockpile + +При приближении к лимиту памяти в memory cgroup, linux запускает механизм direct reclaim, +чтобы освободить свободную память. По опыту YT, direct reclaim очень сильно замедляет работу +всего процесса. + +Проблема возникает не только, когда память занята анонимными страницами. 50% памяти контейнера +может быть занято не dirty страницами page cache, но проблема всёравно будет проявляться. Например, +если процесс активно читает файлы с диска без O_DIRECT, вся память очень быстро будет забита. + +Чтобы бороться с этой проблемой, в яндексовом ядре добавлена ручка `madvise(MADV_STOCKPILE)`. +Больше подробностей в https://st.yandex-team.ru/KERNEL-186
\ No newline at end of file diff --git a/library/cpp/yt/stockpile/stockpile.h b/library/cpp/yt/stockpile/stockpile.h new file mode 100644 index 0000000000..1df9591de4 --- /dev/null +++ b/library/cpp/yt/stockpile/stockpile.h @@ -0,0 +1,29 @@ +#pragma once + +#include <util/system/types.h> + +#include <util/generic/size_literals.h> + +#include <util/datetime/base.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +struct TStockpileOptions +{ + static constexpr i64 DefaultBufferSize = 4_GBs; + i64 BufferSize = DefaultBufferSize; + + static constexpr int DefaultThreadCount = 4; + int ThreadCount = DefaultThreadCount; + + static constexpr TDuration DefaultPeriod = TDuration::MilliSeconds(10); + TDuration Period = DefaultPeriod; +}; + +void ConfigureStockpile(const TStockpileOptions& options); + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/stockpile/stockpile_linux.cpp b/library/cpp/yt/stockpile/stockpile_linux.cpp new file mode 100644 index 0000000000..3ee83d9334 --- /dev/null +++ b/library/cpp/yt/stockpile/stockpile_linux.cpp @@ -0,0 +1,42 @@ +#include "stockpile.h" + +#include <thread> +#include <mutex> + +#include <sys/mman.h> + +#include <util/system/thread.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +namespace { + +void RunStockpile(const TStockpileOptions& options) +{ + TThread::SetCurrentThreadName("Stockpile"); + + constexpr int MADV_STOCKPILE = 0x59410004; + + while (true) { + ::madvise(nullptr, options.BufferSize, MADV_STOCKPILE); + Sleep(options.Period); + } +} + +} // namespace + +void ConfigureStockpile(const TStockpileOptions& options) +{ + static std::once_flag OnceFlag; + std::call_once(OnceFlag, [options] { + for (int i = 0; i < options.ThreadCount; i++) { + std::thread(RunStockpile, options).detach(); + } + }); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/stockpile/stockpile_other.cpp b/library/cpp/yt/stockpile/stockpile_other.cpp new file mode 100644 index 0000000000..3495d9c1cb --- /dev/null +++ b/library/cpp/yt/stockpile/stockpile_other.cpp @@ -0,0 +1,12 @@ +#include "stockpile.h" + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +void ConfigureStockpile(const TStockpileOptions& /*options*/) +{ } + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yt/stockpile/ya.make b/library/cpp/yt/stockpile/ya.make new file mode 100644 index 0000000000..39d51aaf97 --- /dev/null +++ b/library/cpp/yt/stockpile/ya.make @@ -0,0 +1,11 @@ +LIBRARY() + +INCLUDE(${ARCADIA_ROOT}/library/cpp/yt/ya_cpp.make.inc) + +IF (OS_LINUX AND NOT SANITIZER_TYPE) + SRCS(stockpile_linux.cpp) +ELSE() + SRCS(stockpile_other.cpp) +ENDIF() + +END() |