diff --git a/examples/c/ADM_transfer_datasets_user.c b/examples/c/ADM_transfer_datasets_user.c new file mode 100644 index 0000000000000000000000000000000000000000..303567547c944c587a8357faee629ad0eeab7648 --- /dev/null +++ b/examples/c/ADM_transfer_datasets_user.c @@ -0,0 +1,104 @@ +/****************************************************************************** + * Copyright 2021-2023, Barcelona Supercomputing Center (BSC), Spain + * + * This software was partially supported by the EuroHPC-funded project ADMIRE + * (Project ID: 956748, https://www.admire-eurohpc.eu). + * + * This file is part of scord. + * + * scord is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * scord is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with scord. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + *****************************************************************************/ + +#include +#include +#include "common.h" + +int +main(int argc, char** argv) { + + enum { N = 5 }; + + test_info_t test_info = { + .name = TESTNAME, + .requires_server = true, + .requires_controller = true, + }; + + cli_args_t cli_args; + if(process_args(argc, argv, test_info, &cli_args)) { + exit(EXIT_FAILURE); + } + + const char* paths[N] = {"input00.dat", "inpu01.dat", "input02.dat", + "input03.dat", "input04.dat"}; + + ADM_dataset_t sources[N]; + ADM_dataset_t targets[N]; + + for(size_t i = 0; i < N; ++i) { + sources[i] = ADM_dataset_create(/*"lustre", */ paths[i]); + targets[i] = ADM_dataset_create(/*"gekkofs",*/ paths[i]); + + if(!sources[i] || !targets[i]) { + abort(); + } + } + + ADM_return_t ret = ADM_SUCCESS; + ADM_transfer_t tx; + + // the library will automatically route the request to the `scord` + // server configured in the cluster + if((ret = ADM_transfer_datasets(sources, N, targets, N, &tx)) != + ADM_SUCCESS) { + fprintf(stderr, "ADM_transfer_datasets() failed: %s\n", + ADM_strerror(ret)); + abort(); + } + + ADM_transfer_status_t status; + do { + struct timespec timeout = {.tv_sec = 1, .tv_nsec = 0}; + // Wait for the transfer to complete + ret = ADM_transfer_wait(tx, &status, &timeout); + + if(ret != ADM_SUCCESS && ret != ADM_ETIMEOUT) { + fprintf(stderr, "ADM_transfer_wait() failed: %s\n", + ADM_strerror(ret)); + abort(); + } + + if(ADM_TRANSFER_SUCCEEDED(status)) { + fprintf(stdout, "Transfer completed successfully\n"); + } else if(ADM_TRANSFER_FAILED(status)) { + fprintf(stderr, "Transfer failed: %s\n", + ADM_strerror(ADM_TRANSFER_ERROR(status))); + } else if(ADM_TRANSFER_PENDING(status)) { + fprintf(stdout, "Transfer pending\n"); + continue; + } else if(ADM_TRANSFER_IN_PROGRESS(status)) { + fprintf(stdout, "Transfer in progress\n"); + continue; + } else { + fprintf(stderr, "Transfer status unknown\n"); + abort(); + } + } while(!ADM_TRANSFER_SUCCEEDED(status)); + + free(status); + + return 0; +} diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 22004b2f1fe64876db23e3276b38d3ff81c43751..8e793a7b292bdb3a456afeb35249af6ad193d479 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -31,6 +31,8 @@ list(APPEND c_examples_with_controller # transfers ADM_transfer_datasets ADM_get_transfer_priority ADM_set_transfer_priority ADM_cancel_transfer ADM_get_pending_transfers + # transfers (user) + ADM_transfer_datasets_user # qos ADM_set_qos_constraints ADM_get_qos_constraints # data operations @@ -55,7 +57,15 @@ target_link_libraries(c_examples_common libscord_c_types) foreach(example IN LISTS c_examples_with_controller c_examples_without_controller) add_executable(${example}_c) target_sources(${example}_c PRIVATE ${example}.c) - target_link_libraries(${example}_c PUBLIC libscord c_examples_common) + + if (example MATCHES "^.*_user$") + target_link_libraries(${example}_c PUBLIC libscord-user) + else () + target_link_libraries(${example}_c PUBLIC libscord) + endif () + + target_link_libraries(${example}_c PUBLIC c_examples_common) + set_target_properties(${example}_c PROPERTIES OUTPUT_NAME ${example}) endforeach() @@ -92,6 +102,12 @@ if(SCORD_BUILD_TESTS) ) endforeach() + # Disable until we have more than stubs for the user library + set_tests_properties(run_ADM_transfer_datasets_user_c_test + PROPERTIES DISABLED TRUE) + set_tests_properties(validate_ADM_transfer_datasets_user_c_test + PROPERTIES DISABLED TRUE) + foreach(example IN LISTS c_examples_without_controller) # prepare environment for the RPC test itself and its validation test diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index ce01d8644472b5a27cd4bbdc0aee0a79a280409d..086288087cbfd7ce92d5e00ccc71a52450977e9d 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -79,7 +79,7 @@ target_link_libraries( set_property(TARGET libscord_cxx_types PROPERTY POSITION_INDEPENDENT_CODE ON) ################################################################################ -# Create a target for the actual library that will be used by clients +# Create a target for the actual library that will be used by admin clients ################################################################################ add_library(libscord SHARED) @@ -107,8 +107,41 @@ target_link_libraries( set_target_properties(libscord PROPERTIES OUTPUT_NAME "scord") +################################################################################ +# Create a target for the actual library that will be used by users +################################################################################ +add_library(libscord-user SHARED) + +target_sources( + libscord-user + PUBLIC scord/scord-user.h + PRIVATE libscord-user.c +) + +set(public_headers, "") +list(APPEND public_headers "scord/scord-user.h") + +set_target_properties(libscord-user PROPERTIES PUBLIC_HEADER + "${public_headers}") + +target_include_directories( + libscord-user PUBLIC $ + $ +) + +target_link_libraries( + libscord-user + PRIVATE common::network::rpc_client + PUBLIC libscord_c_types +) + +set_target_properties(libscord-user PROPERTIES OUTPUT_NAME "scord-user") + +################################################################################ +# Install the libraries +################################################################################ install( - TARGETS libscord libscord_c_types libscord_cxx_types + TARGETS libscord libscord-user libscord_c_types libscord_cxx_types LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} diff --git a/src/lib/errors.c b/src/lib/errors.c index e4ca5c8759ab962bd81a63f7ae05a7f54cc7db41..f5a99cc74c2a3a784b1647f7d1b1946caadd7767 100644 --- a/src/lib/errors.c +++ b/src/lib/errors.c @@ -37,6 +37,7 @@ const char* const adm_errlist[ADM_ERR_MAX + 1] = { "Cannot create adhoc storage directory", [ADM_EADHOC_DIR_EXISTS] = "Adhoc storage directory already exists", [ADM_ESUBPROCESS_ERROR] = "Subprocess error", + [ADM_ETIMEOUT] = "Timeout", [ADM_EOTHER] = "Undetermined error", /* fallback */ diff --git a/src/lib/libscord-user.c b/src/lib/libscord-user.c new file mode 100644 index 0000000000000000000000000000000000000000..c2977cf2e320502e893517aae423a4b1cd84dfd8 --- /dev/null +++ b/src/lib/libscord-user.c @@ -0,0 +1,88 @@ +/****************************************************************************** + * Copyright 2021-2023, Barcelona Supercomputing Center (BSC), Spain + * + * This software was partially supported by the EuroHPC-funded project ADMIRE + * (Project ID: 956748, https://www.admire-eurohpc.eu). + * + * This file is part of scord. + * + * scord is free software: you can redistribute it and/or modify + * it under the terms of the Lesser GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * scord is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with scord. If not, see . + * + * SPDX-License-Identifier: LGPL-3.0-or-later + *****************************************************************************/ + +#include +#include "types_private.h" + +ADM_return_t +ADM_transfer_datasets(ADM_dataset_t sources[], size_t sources_len, + ADM_dataset_t targets[], size_t targets_len, + ADM_transfer_t* transfer) { + (void) sources; + (void) sources_len; + (void) targets; + (void) targets_len; + (void) transfer; + + return ADM_SUCCESS; +} + + +ADM_return_t +ADM_transfer_wait(ADM_transfer_t transfer, ADM_transfer_status_t* status, + struct timespec* timeout) { + (void) transfer; + (void) status; + (void) timeout; + + if(transfer == NULL || status == NULL) { + return ADM_EBADARGS; + } + + *status = malloc(sizeof(struct adm_transfer_status)); + + if(*status == NULL) { + return ADM_ENOMEM; + } + + (*status)->s_state = ADM_TRANSFER_FINISHED; + (*status)->s_error = ADM_SUCCESS; + + return ADM_SUCCESS; +} + +inline bool +__adm_transfer_succeeded(ADM_transfer_status_t st) { + return st->s_state == ADM_TRANSFER_FINISHED && st->s_error == ADM_SUCCESS; +} + +inline bool +__adm_transfer_failed(ADM_transfer_status_t st) { + return st->s_state == ADM_TRANSFER_FINISHED && st->s_error != ADM_SUCCESS; +} + +inline bool +__adm_transfer_pending(ADM_transfer_status_t st) { + return st->s_state == ADM_TRANSFER_QUEUED; +} + +inline bool +__adm_transfer_in_progress(ADM_transfer_status_t st) { + return st->s_state == ADM_TRANSFER_RUNNING; +} + +inline ADM_return_t +__adm_transfer_error(ADM_transfer_status_t st) { + return st->s_error; +} diff --git a/src/lib/scord/scord-user.h b/src/lib/scord/scord-user.h new file mode 100644 index 0000000000000000000000000000000000000000..2397200343d3a18e5ff55ce3a244f5f38eb00602 --- /dev/null +++ b/src/lib/scord/scord-user.h @@ -0,0 +1,127 @@ +/****************************************************************************** + * Copyright 2021-2023, Barcelona Supercomputing Center (BSC), Spain + * + * This software was partially supported by the EuroHPC-funded project ADMIRE + * (Project ID: 956748, https://www.admire-eurohpc.eu). + * + * This file is part of scord. + * + * scord is free software: you can redistribute it and/or modify + * it under the terms of the Lesser GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * scord is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with scord. If not, see . + * + * SPDX-License-Identifier: LGPL-3.0-or-later + *****************************************************************************/ + + +#ifndef SCORD_USER_H +#define SCORD_USER_H + +#include +#include + +typedef struct adm_transfer_status* ADM_transfer_status_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Transfer datasets between storage tiers asynchronously. + * + * @param[in] sources The datasets to transfer. + * @param[in] sources_len The number of datasets to transfer. + * @param[in] targets The destination datasets. + * @param[in] targets_len The number of destination datasets. + * @param[out] transfer A transfer handle to query the operation status. + * @return + */ +ADM_return_t +ADM_transfer_datasets(ADM_dataset_t sources[], size_t sources_len, + ADM_dataset_t targets[], size_t targets_len, + ADM_transfer_t* transfer); + + +/** + * @brief Wait for a transfer to complete. + * + * @param[in] transfer The transfer handle. + * @param[out] status A pointer to a dynamically allocated + * `ADM_transfer_status_t` structure where status information should be stored. + * The caller is responsible for freeing the memory allocated for this + * structure. + * @param[out] timeout The maximum time to wait for the transfer to complete. If + * NULL, query the transfer status and return immediately. If not NULL, wait for + * the transfer to complete or the timeout to expire. + * @return ADM_SUCCESS if the transfer completed successfully. ADM_ETIMEOUT if + * the transfer did not complete before the timeout expired. An + * specifc ADM_E* error code otherwise. + */ +ADM_return_t +ADM_transfer_wait(ADM_transfer_t transfer, ADM_transfer_status_t* status, + struct timespec* timeout); + +bool +__adm_transfer_succeeded(ADM_transfer_status_t status); + +bool +__adm_transfer_failed(ADM_transfer_status_t status); + +bool +__adm_transfer_pending(ADM_transfer_status_t status); + +bool +__adm_transfer_in_progress(ADM_transfer_status_t status); + +ADM_return_t +__adm_transfer_error(ADM_transfer_status_t status); + +/** + * @brief Check if a transfer completed successfully. + */ +#define ADM_TRANSFER_SUCCEEDED(st) __adm_transfer_succeeded(st) + +/** + * @brief Check if a transfer failed. + */ +#define ADM_TRANSFER_FAILED(st) __adm_transfer_failed(st) + +/** + * @brief Check if a transfer is pending. + */ +#define ADM_TRANSFER_PENDING(st) __adm_transfer_pending(st) + +/** + * @brief Check if a transfer is in progress. + */ +#define ADM_TRANSFER_IN_PROGRESS(st) __adm_transfer_in_progress(st) + +/** + * @brief Return the error code of a failed transfer. + */ +#define ADM_TRANSFER_ERROR(st) __adm_transfer_error(st) + +/** + * Return a string describing the error number + * + * @param[in] errnum The error number for which a description should be + * returned. + * @return A pointer to a string describing `errnum`. + */ +const char* +ADM_strerror(ADM_return_t errnum); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SCORD_USER_H diff --git a/src/lib/scord/types.h b/src/lib/scord/types.h index 7bfbc11356f349547c86b9e8323d2654bf59c702..d1191a3a3b74e55b93d632ffa1d0bdad824f8644 100644 --- a/src/lib/scord/types.h +++ b/src/lib/scord/types.h @@ -25,15 +25,15 @@ #ifndef SCORD_TYPES_H #define SCORD_TYPES_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include #include -#ifdef __cplusplus -extern "C" { -#endif - /******************************************************************************/ /* Public type and struct definitions */ /******************************************************************************/ @@ -53,6 +53,7 @@ typedef enum { ADM_EADHOC_DIR_CREATE_FAILED, ADM_EADHOC_DIR_EXISTS, ADM_ESUBPROCESS_ERROR, + ADM_ETIMEOUT, ADM_EOTHER, ADM_ERR_MAX = 512 } ADM_return_t; @@ -201,9 +202,21 @@ typedef enum { ADM_MAPPING_N_TO_N } ADM_transfer_mapping_t; +/** A transfer state */ +typedef enum { + ADM_TRANSFER_QUEUED, + ADM_TRANSFER_RUNNING, + ADM_TRANSFER_FINISHED, + ADM_TRANSFER_FAILED, + ADM_TRANSFER_CANCELLED +} ADM_transfer_state_t; + /** A handle to a created transfer */ typedef struct adm_transfer* ADM_transfer_t; +/** A transfer status */ +typedef struct adm_transfer_status* ADM_transfer_status_t; + /** A transfer priority */ typedef int ADM_transfer_priority_t; diff --git a/src/lib/types_private.h b/src/lib/types_private.h index 3fda9e61877a998f3aa8e2fa0d72fb78374582f0..2d5a261d1fc1c7cdf8a03e62b79cbf041ec0adb7 100644 --- a/src/lib/types_private.h +++ b/src/lib/types_private.h @@ -70,6 +70,13 @@ struct adm_transfer { uint64_t t_id; }; +struct adm_transfer_status { + /** The transfer state */ + ADM_transfer_state_t s_state; + /** The transfer error code */ + ADM_return_t s_error; +}; + struct adm_dataset_info { // TODO: undefined for now int32_t placeholder;