diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bbd57c77ce6bb8dd18268ebcd79fb126b8a845a..129a04a0efbe1a7ca8c1c2b626935b8bc160764a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,10 @@ include(FetchContent) # that are substituted when generating defaults.cpp below include(GNUInstallDirs) +# CMakeDependentOption defines cmake_dependent_option() which is used to +# define options that depend on other options +include(CMakeDependentOption) + # Make sure that CMake can find our internal modules list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -149,6 +153,21 @@ message(STATUS "[${PROJECT_NAME}] server bind port: ${CARGO_BIND_PORT}") option(CARGO_BUILD_TESTS "Build tests (disabled by default)" OFF) +### MPI options that should be passed to ${MPIEXEC_EXECUTABLE} when starting +### the server via the `cargoctl` script +set(CARGOCTL_MPIEXEC_OPTIONS "--map-by node --oversubscribe" CACHE STRING + "Options passed to `${MPIEXEC_EXECUTABLE}` by `cargoctl` when starting the server") + +### systemd support +option(CARGO_SYSTEMD_SUPPORT "Enable systemd support (enabled by default)" ON) + +cmake_dependent_option( + CARGO_SYSTEMD_INSTALL_UNIT_FILES + "Install systemd unit files (disabled by default)" + OFF + "CARGO_SYSTEMD_SUPPORT" + OFF) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # ############################################################################## @@ -320,9 +339,13 @@ else () endif () add_subdirectory(etc) +add_subdirectory(cli) add_subdirectory(lib) add_subdirectory(src) -add_subdirectory(util) + +if(CARGO_SYSTEMD_SUPPORT) + add_subdirectory(systemd) +endif() if(CARGO_BUILD_TESTS) add_subdirectory(tests) @@ -334,6 +357,11 @@ endif() # using find_package() # ############################################################################## +set(BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}" + CACHE PATH "Path where ${PROJECT_NAME} binaries will be installed") +set(DATA_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}" + CACHE PATH "Path where ${PROJECT_NAME} data files will be installed") + include(CMakePackageConfigHelpers) configure_package_config_file( @@ -341,6 +369,7 @@ configure_package_config_file( "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION}" + PATH_VARS BIN_INSTALL_DIR DATA_INSTALL_DIR ) write_basic_package_version_file( diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d349e97143aa3d08f2d19f490edcdfda15be5f1b --- /dev/null +++ b/cli/CMakeLists.txt @@ -0,0 +1,92 @@ +################################################################################ +# Copyright 2022-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 Cargo. # +# # +# Cargo 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. # +# # +# Cargo 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 Cargo. If not, see . # +# # +# SPDX-License-Identifier: GPL-3.0-or-later # +################################################################################ + +################################################################################ +## cargoctl: A CLI tool to interact with a Cargo server +# TODO: This is a hack: `cargoctl` needs to know the full path to the +# installed `cargo` and `cargo_shutdown` programs but CMake doesn't seem to +# provide a way to get this information at this stage. Thus, we set it manually +# here :( +set(CARGO_PROGRAM ${CMAKE_INSTALL_FULL_BINDIR}/cargo) +set(CARGO_SHUTDOWN_PROGRAM ${CMAKE_INSTALL_FULL_BINDIR}/cargo_shutdown) +configure_file(cargoctl.in cargoctl @ONLY) + + +################################################################################ +## cargo_ping: A CLI tool to check if a Cargo server is running +add_executable(cargo_ping) + +target_sources(cargo_ping + PRIVATE + ping.cpp +) + +target_link_libraries(cargo_ping + PUBLIC + fmt::fmt + CLI11::CLI11 + net::rpc_client + cargo +) + +################################################################################ +## cargo_shutdown: A CLI tool to shutdown a Cargo server +add_executable(cargo_shutdown) + +target_sources(cargo_shutdown + PRIVATE + shutdown.cpp +) + +target_link_libraries(cargo_shutdown + PUBLIC + fmt::fmt + CLI11::CLI11 + net::rpc_client + cargo +) + +################################################################################ +## ccp: A CLI tool to request a Cargo server to copy files between storage tiers +add_executable(ccp) + +target_sources(ccp + PRIVATE + copy.cpp +) + +target_link_libraries(ccp + PUBLIC + fmt::fmt + CLI11::CLI11 + net::rpc_client + cargo +) + +install(TARGETS cargo_ping cargo_shutdown ccp + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/cargoctl + DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/cli/cargoctl.in b/cli/cargoctl.in new file mode 100755 index 0000000000000000000000000000000000000000..ed81d0bde96c5139843350b16d9117ede97ca338 --- /dev/null +++ b/cli/cargoctl.in @@ -0,0 +1,172 @@ +#!/bin/bash +################################################################################ +# Copyright 2022-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 Cargo. # +# # +# Cargo 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. # +# # +# Cargo 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 Cargo. If not, see . # +# # +# SPDX-License-Identifier: GPL-3.0-or-later # +################################################################################ + +progname=$(basename "$0") + +usage() { + echo "Usage: $progname COMMAND " + echo "" + echo " -h, --help: Show this help message and exit" + echo "" + echo "Valid commands:" + echo "" + echo "Start a Cargo server listening on a address , with workers " + echo "distributed over a pool of hosts :" + echo " $progname start -s -H -n " + echo "" + echo "NOTE: ADDR must be a valid Mercury address, and host1, host2, ..., " + echo "hostN must be valid hostnames. Also, ADDR should refer to one of the " + echo "defined hosts in the host pool." + echo "" + echo "Stop a Cargo server listening on a given address :" + echo " $progname stop -s " + exit 1 +} + +start() { + if [ $# -eq 0 ]; then + echo "$progname: ERROR: No options provided" >&2 + usage + fi + + local OPTIND opt workers address + + while getopts ":s:H:n:" opt; do + case $opt in + s) + address="$OPTARG" + ;; + H) + hosts="$OPTARG" + ;; + n) + workers="$OPTARG" + ;; + \?) + echo "$progname: Invalid option: '-$OPTARG'" >&2 + usage + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + usage + ;; + esac + done + + if [ -z "$workers" ]; then + echo "$progname: ERROR: Number of workers not provided" >&2 + usage + fi + + if [ -z "$address" ]; then + echo "$progname: ERROR: Bind address not provided" >&2 + usage + fi + + echo "Starting the Cargo server" + echo " Hosts: $hosts" + echo " Workers: $workers" + echo " Master address: $address" + + if ! @MPIEXEC_EXECUTABLE@ \ + -H "$hosts" \ + -np "$workers" \ + @CARGOCTL_MPIEXEC_OPTIONS@ \ + @CARGO_PROGRAM@ --listen "$address"; then + echo "Failed to start the Cargo server" + exit 1 + fi +} + +stop() { + + if [ $# -eq 0 ]; then + echo "$progname: ERROR: No options provided" >&2 + usage + fi + + local OPTIND opt address + + while getopts ":s:" opt; do + case $opt in + s) + address="$OPTARG" + ;; + \?) + echo "$progname: Invalid option: '-$OPTARG'" >&2 + usage + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + usage + ;; + esac + done + + if [ -z "$address" ]; then + echo "$progname: ERROR: Server address not provided" >&2 + usage + fi + + echo "Stopping the Cargo server at $address" + @CARGO_SHUTDOWN_PROGRAM@ -s "$address" +} + +main() { + if [ $# -lt 1 ]; then + usage + fi + + echo "Running $progname $*" + + local cmd + + for arg in "$@"; do + case $arg in + "-h" | "--help") + usage + ;; + *) ;; + + esac + done + + while [ $# -gt 0 ]; do + case $1 in + "start" | "stop") + cmd="$1" + shift + "$cmd" "$@" + exit 0 + ;; + *) + echo "$progname: Invalid option: $1" >&2 + usage + ;; + esac + done +} + +main "$@" diff --git a/util/copy.cpp b/cli/copy.cpp similarity index 100% rename from util/copy.cpp rename to cli/copy.cpp diff --git a/util/ping.cpp b/cli/ping.cpp similarity index 100% rename from util/ping.cpp rename to cli/ping.cpp diff --git a/util/shutdown.cpp b/cli/shutdown.cpp similarity index 100% rename from util/shutdown.cpp rename to cli/shutdown.cpp diff --git a/cmake/cargo-config.cmake.in b/cmake/cargo-config.cmake.in index 3e4fe702ed65d91bc54f09f3063f5ddc9b8802ba..8cb239be0bb42b394d590c6f0e36df793e7ec9a9 100644 --- a/cmake/cargo-config.cmake.in +++ b/cmake/cargo-config.cmake.in @@ -28,4 +28,7 @@ check_required_components("@PROJECT_NAME@") include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Exports.cmake") -check_required_components("@PROJECT_NAME@") \ No newline at end of file +set_and_check(CARGO_BIN_INSTALL_DIR "@PACKAGE_BIN_INSTALL_DIR@") +set_and_check(CARGO_DATA_INSTALL_DIR "@PACKAGE_DATA_INSTALL_DIR@") + +check_required_components("@PROJECT_NAME@") diff --git a/cmake/systemd.cmake b/cmake/systemd.cmake new file mode 100644 index 0000000000000000000000000000000000000000..f6a51fe8be974fd239b8a799350ad2dec81e3188 --- /dev/null +++ b/cmake/systemd.cmake @@ -0,0 +1,79 @@ +################################################################################ +# Copyright 2022-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 Cargo. # +# # +# cargo 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. # +# # +# cargo 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 cargo. If not, see . # +# # +# SPDX-License-Identifier: GPL-3.0-or-later # +################################################################################ + + +include(CMakeParseArguments) + +#[=======================================================================[.rst: + + get_systemd_unit_directory(OUTPUT_VARIABLE [USER]) + +Initialize ``OUTPUT_VARIABLE`` to the directory where systemd unit files +are installed. This function will use ``pkg-config`` to find the information. +If ``USER`` is specified, the user unit directory will be returned instead. +#]=======================================================================] +function(get_systemd_unit_directory OUTPUT_VARIABLE) + + set(OPTIONS USER) + set(SINGLE_VALUE) + set(MULTI_VALUE) + + cmake_parse_arguments( + ARGS "${OPTIONS}" "${SINGLE_VALUE}" "${MULTI_VALUE}" ${ARGN} + ) + + if(ARGS_UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments in get_systemd_unit_directory(): " + "this often indicates typos!\n" + "Unparsed arguments: ${ARGS_UNPARSED_ARGUMENTS}" + ) + endif() + + find_package(PkgConfig REQUIRED) + + # Check for the systemd application, so that we can find out where to + # install the service unit files + pkg_check_modules(SYSTEMD_PROGRAM QUIET systemd) + + if (ARGS_USER) + set(_pc_var systemduserunitdir) + else () + set(_pc_var systemdsystemunitdir) + endif () + + if (SYSTEMD_PROGRAM_FOUND) + # Use pkg-config to look up the systemd unit install directory + execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} + --variable=${_pc_var} systemd + OUTPUT_VARIABLE _systemd_unit_dir) + string(REGEX REPLACE "[ \t\n]+" "" _systemd_unit_dir "${_systemd_unit_dir}") + + message(STATUS "systemd services install dir: ${_systemd_unit_dir}") + + set(${OUTPUT_VARIABLE} + ${_systemd_unit_dir} + PARENT_SCOPE + ) + endif () +endfunction() diff --git a/util/CMakeLists.txt b/systemd/CMakeLists.txt similarity index 72% rename from util/CMakeLists.txt rename to systemd/CMakeLists.txt index 9b47c0046be7be64b8c0a9a3bd5e11532c074df4..16646188a5f178d7eb04167f80dcda2ac43268ee 100644 --- a/util/CMakeLists.txt +++ b/systemd/CMakeLists.txt @@ -22,51 +22,24 @@ # SPDX-License-Identifier: GPL-3.0-or-later # ################################################################################ -add_executable(cargo_ping) - -target_sources(cargo_ping - PRIVATE - ping.cpp -) - -target_link_libraries(cargo_ping - PUBLIC - fmt::fmt - CLI11::CLI11 - net::rpc_client - cargo -) - -add_executable(cargo_shutdown) - -target_sources(cargo_shutdown - PRIVATE - shutdown.cpp -) - -target_link_libraries(cargo_shutdown - PUBLIC - fmt::fmt - CLI11::CLI11 - net::rpc_client - cargo -) - -add_executable(ccp) - -target_sources(ccp - PRIVATE - copy.cpp -) - -target_link_libraries(ccp - PUBLIC - fmt::fmt - CLI11::CLI11 - net::rpc_client - cargo -) - -install(TARGETS cargo_ping cargo_shutdown ccp - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) +configure_file(cargo@.service.in cargo@.service @ONLY) + +# If requested, install the systemd unit files to the system directory +# for user-defined services. Otherwise, install them to the configured +# `datadir` directory (usually `/usr/share`). +if(CARGO_SYSTEMD_INSTALL_UNIT_FILES) + include(systemd) + get_systemd_unit_directory(SYSTEMD_UNIT_DIRECTORY USER) + + if(NOT SYSTEMD_UNIT_DIRECTORY) + message(FATAL_ERROR "Could not find systemd unit directory") + endif() + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cargo@.service + DESTINATION ${SYSTEMD_UNIT_DIRECTORY} + ) +else() + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cargo@.service + DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME} + ) +endif() diff --git a/systemd/cargo@.service.in b/systemd/cargo@.service.in new file mode 100644 index 0000000000000000000000000000000000000000..6e1300670b462639b50bb24bce3ddaa01277e2fd --- /dev/null +++ b/systemd/cargo@.service.in @@ -0,0 +1,11 @@ +[Unit] +Description=Cargo parallel data stager + +[Service] +Type=simple +EnvironmentFile=%S/cargo/%I.cfg +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/cargoctl start -s ${CARGO_ADDRESS} -H ${CARGO_HOSTS} -n ${CARGO_NUM_NODES} +ExecStop=@CMAKE_INSTALL_FULL_BINDIR@/cargoctl stop -s ${CARGO_ADDRESS} +Restart=no +PrivateTmp=true +NoNewPrivileges=true