diff --git a/resource/readers/resource_reader_jgf.cpp b/resource/readers/resource_reader_jgf.cpp index 2661c0b58..9e5243e78 100644 --- a/resource/readers/resource_reader_jgf.cpp +++ b/resource/readers/resource_reader_jgf.cpp @@ -1351,10 +1351,7 @@ int resource_reader_jgf_t::unpack_at (resource_graph_t &g, const std::string &str, int rank) { - /* This functionality is currently experimental, as resource graph - * growth causes a resize of the boost vecS vertex container type. - * Resizing the vecS results in lost job allocations and reservations - * as there is no copy constructor for planner. + /* This functionality is currently experimental. * vtx_t vtx is not implemented and may be used in the future * for optimization. */ diff --git a/resource/reapi/bindings/c++/reapi.hpp b/resource/reapi/bindings/c++/reapi.hpp index cf5eec3e9..5fd344033 100644 --- a/resource/reapi/bindings/c++/reapi.hpp +++ b/resource/reapi/bindings/c++/reapi.hpp @@ -190,6 +190,36 @@ class reapi_t { return -1; } + /*! Update the resource state with R. + * + * \param h Opaque handle. How it is used is an implementation + * detail. However, when it is used within a Flux's + * service module, it is expected to be a pointer + * to a flux_t object. + * \param R_subgraph R string of subgraph to attach to existing resources + * \return 0 on success; -1 on error. + */ + static int grow (void *h, const std::string &R_subgraph) + { + return -1; + } + + /*! Update the resource state with R. + * + * \param h Opaque handle. How it is used is an implementation + * detail. However, when it is used within a Flux's + * service module, it is expected to be a pointer + * to a flux_t object. + * \param subgraph_path String representing the path from the cluster root + * to the root of the subgraph to be removed from + * the resource graph + * \return 0 on success; -1 on error. + */ + static int shrink (void *h, const std::string &subgraph_path) + { + return -1; + } + /*! Cancel the allocation or reservation corresponding to jobid. * * \param h Opaque handle. How it is used is an implementation diff --git a/resource/reapi/bindings/c++/reapi_cli.hpp b/resource/reapi/bindings/c++/reapi_cli.hpp index 5edb8393c..77e5fcee0 100644 --- a/resource/reapi/bindings/c++/reapi_cli.hpp +++ b/resource/reapi/bindings/c++/reapi_cli.hpp @@ -89,6 +89,8 @@ class resource_query_t { void set_job (const uint64_t jobid, const std::shared_ptr &job); int remove_job (const uint64_t jobid); int remove_job (const uint64_t jobid, const std::string &R, bool &full_removal); + int grow (const std::string &R_subgraph); + int shrink (const std::string &subgraph_path); void incr_job_counter (); /* Run the traverser to match the jobspec */ @@ -148,6 +150,8 @@ class reapi_cli_t : public reapi_t { int64_t &at, double &ov, std::string &R_out); + static int grow (void *h, const std::string &R_subgraph); + static int shrink (void *h, const std::string &subgraph_path); static int cancel (void *h, const uint64_t jobid, bool noent_ok); static int cancel (void *h, const uint64_t jobid, diff --git a/resource/reapi/bindings/c++/reapi_cli_impl.hpp b/resource/reapi/bindings/c++/reapi_cli_impl.hpp index e9fda8306..cf8067bd9 100644 --- a/resource/reapi/bindings/c++/reapi_cli_impl.hpp +++ b/resource/reapi/bindings/c++/reapi_cli_impl.hpp @@ -165,6 +165,32 @@ int reapi_cli_t::update_allocate (void *h, return NOT_YET_IMPLEMENTED; } +int reapi_cli_t::grow (void *h, const std::string &R_subgraph) +{ + resource_query_t *rq = static_cast (h); + int rc = -1; + + if ((rc = rq->grow (R_subgraph)) != 0) { + m_err_msg += __FUNCTION__; + m_err_msg += ": ERROR: grow error: " + std::string (strerror (errno)) + "\n"; + } + + return rc; +} + +int reapi_cli_t::shrink (void *h, const std::string &subgraph_path) +{ + resource_query_t *rq = static_cast (h); + int rc = -1; + + if ((rc = rq->shrink (subgraph_path)) != 0) { + m_err_msg += __FUNCTION__; + m_err_msg += ": ERROR: shrink error: " + std::string (strerror (errno)) + "\n"; + } + + return rc; +} + int reapi_cli_t::match_allocate_multi (void *h, bool orelse_reserve, const char *jobs, @@ -720,6 +746,73 @@ int resource_query_t::remove_job (const uint64_t jobid, const std::string &R, bo return rc; } +int resource_query_t::grow (const std::string &R_subgraph) +{ + int rc = -1; + std::shared_ptr reader; + vtx_t v = boost::graph_traits::null_vertex (); + + if (R_subgraph == "") { + errno = EINVAL; + return rc; + } + if (params.load_format != "jgf") { + m_err_msg = __FUNCTION__; + m_err_msg += ": ERROR: growing a resource graph not "; + m_err_msg += " initialized with JGF is unsupported\n"; + errno = ENOTSUP; + return rc; + } + if ((reader = create_resource_reader ("jgf")) == nullptr) { + m_err_msg = __FUNCTION__; + m_err_msg += ": ERROR: can't create JGF reader\n"; + return rc; + } + if ((rc = reader->unpack_at (db->resource_graph, db->metadata, v, R_subgraph, -1)) != 0) { + m_err_msg = __FUNCTION__; + m_err_msg += ": ERROR: reader returned error: "; + m_err_msg += reader->err_message () + "\n"; + return rc; + } + if ((rc = traverser->initialize (db, matcher)) != 0) { + m_err_msg = __FUNCTION__; + m_err_msg += ": ERROR: reinitialize traverser after grow. "; + m_err_msg += reader->err_message () + "\n"; + } + return rc; +} + +int resource_query_t::shrink (const std::string &subgraph_path) +{ + int rc = -1; + std::shared_ptr reader; + + if (subgraph_path == "") { + errno = EINVAL; + return rc; + } + // No need to check if params.load_format != "jgf" since + // remove_subgraph takes a subgraph path, which is + // supported by any resource graph + if ((reader = create_resource_reader ("jgf")) == nullptr) { + m_err_msg = __FUNCTION__; + m_err_msg += ": ERROR: can't create JGF reader\n"; + return rc; + } + if ((rc = reader->remove_subgraph (db->resource_graph, db->metadata, subgraph_path)) != 0) { + m_err_msg = __FUNCTION__; + m_err_msg += ": ERROR: reader returned error: "; + m_err_msg += reader->err_message () + "\n"; + return rc; + } + if ((rc = traverser->initialize (db, matcher)) != 0) { + m_err_msg = __FUNCTION__; + m_err_msg += ": ERROR: reinitialize traverser after shrink. "; + m_err_msg += reader->err_message () + "\n"; + } + return rc; +} + void resource_query_t::incr_job_counter () { jobid_counter++; diff --git a/resource/reapi/bindings/c/reapi_cli.cpp b/resource/reapi/bindings/c/reapi_cli.cpp index 6bec44ca9..683a79318 100644 --- a/resource/reapi/bindings/c/reapi_cli.cpp +++ b/resource/reapi/bindings/c/reapi_cli.cpp @@ -186,6 +186,24 @@ extern "C" int reapi_cli_update_allocate (reapi_cli_ctx_t *ctx, return rc; } +extern "C" int reapi_cli_grow (reapi_cli_ctx_t *ctx, const char *R_subgraph) +{ + if (!ctx || !ctx->rqt || !R_subgraph) { + errno = EINVAL; + return -1; + } + return reapi_cli_t::grow (ctx->rqt, R_subgraph); +} + +extern "C" int reapi_cli_shrink (reapi_cli_ctx_t *ctx, const char *subgraph_path) +{ + if (!ctx || !ctx->rqt || !subgraph_path) { + errno = EINVAL; + return -1; + } + return reapi_cli_t::shrink (ctx->rqt, subgraph_path); +} + extern "C" int reapi_cli_cancel (reapi_cli_ctx_t *ctx, const uint64_t jobid, bool noent_ok) { if (!ctx || !ctx->rqt) { @@ -259,11 +277,13 @@ extern "C" const char *reapi_cli_get_err_msg (reapi_cli_ctx_t *ctx) { std::string err_buf = ""; - if (ctx->rqt) - err_buf = ctx->rqt->get_resource_query_err_msg () + reapi_cli_t::get_err_message () - + ctx->err_msg; - else - err_buf = reapi_cli_t::get_err_message () + ctx->err_msg; + if (!ctx || !ctx->rqt) { + errno = EINVAL; + return "ERROR: REAPI context and/or rqt null \n"; + } + + err_buf = + ctx->rqt->get_resource_query_err_msg () + reapi_cli_t::get_err_message () + ctx->err_msg; return strdup (err_buf.c_str ()); } diff --git a/resource/reapi/bindings/c/reapi_cli.h b/resource/reapi/bindings/c/reapi_cli.h index eeca639dd..39a257182 100644 --- a/resource/reapi/bindings/c/reapi_cli.h +++ b/resource/reapi/bindings/c/reapi_cli.h @@ -140,6 +140,30 @@ int reapi_cli_update_allocate (reapi_cli_ctx_t *ctx, double *ov, const char **R_out); +/*! Grow the resource graph with R. + * + * \param h Opaque handle. How it is used is an implementation + * detail. However, when it is used within a Flux's + * service module, it is expected to be a pointer + * to a flux_t object. + * \param R_subgraph R string of subgraph to attach to existing resources + * \return 0 on success; -1 on error. + */ +int reapi_cli_grow (reapi_cli_ctx_t *ctx, const char *R_subgraph); + +/*! Shrink the resource graph with a path. + * + * \param h Opaque handle. How it is used is an implementation + * detail. However, when it is used within a Flux's + * service module, it is expected to be a pointer + * to a flux_t object. + * \param subgraph_path String representing the path from the cluster root + * to the root of the subgraph to be removed from + * the resource graph + * \return 0 on success; -1 on error. + */ +int reapi_cli_shrink (reapi_cli_ctx_t *ctx, const char *subgraph_path); + /*! Cancel the allocation or reservation corresponding to jobid. * * \param ctx reapi_cli_ctx_t context object diff --git a/resource/utilities/command.cpp b/resource/utilities/command.cpp index 94e9ecc06..d637358bf 100644 --- a/resource/utilities/command.cpp +++ b/resource/utilities/command.cpp @@ -558,7 +558,10 @@ static int remove (std::shared_ptr &ctx, std::vectorerr_message (); return -1; } - // TODO: reinitialize the traverser, see issue #1075 + if (ctx->traverser->initialize (ctx->db, ctx->matcher) != 0) { + std::cerr << "ERROR: can't reinitialize traverser after attach" << std::endl; + return -1; + } return 0; }