Commit 2aaebb1b authored by Ciarán Ó Rourke's avatar Ciarán Ó Rourke
Browse files

Squashed 'external/phobos/' changes from 087c71a3..e07d0af48

e07d0af48 Update changelog for release 1.90 (Hi'iaka)
55202f892 pkg: make RPM spec file compatible with rhel8
89adec779 fix: avoid calling dss_set_unlock() if no devices were locked
e017eefc4 fix: SQL request error when at least one device is not selected
7537ab591 admin: clarify function and variable names to list layouts
ab0f32e04 cfg template: add LTO7 and LTO8 compatibility rules
62739bfbe build: fix parallel make
d4209ae8a phobosd: fix environment file path in service file
f2698b8a7 API: make object list available to phobos_store API
dbe0c6e81 scheduler: fix deadlock during device selection
01140526d refactor: move dss "drive add" code to the admin module
b0db8f19b cli: add medium option to 'phobos extent list'
1dfb1f1df cli: list extents
ba66bf1bc scheduler: ensure that an added device is unlocked
6b520bfd1 cli: fix bad directory addition
6681443a cli: add metadata option to 'phobos object list'
c04d184b cli: pattern-matching for 'phobos object list'
2cd5426b design: fix spelling in adapters.txt
1b9889b3 cli: list objects
986afb27 refactor: move dss "drive lock" code to the admin module
0b4e6a63 phobosd: lock operation notification
ee639c71 admin: fix incomplete data copy
eafebcf1 refactor: move dss "drive unlock" code to the admin module
5c8bacb4 refactor: change test_resource_availability.sh, using -f/-l options
5a0fc9bf dss: lock the device/medium when updating its admin status
7ddae2dd refactor: remove const to admin prototypes when passing by value
9c65b62e refactor: reorganize admin source file
0707ba20 phobosd: unlock operation notification
2c9d2217 store: fix family retrieval from Xfer data structure
86f28ce3 cli: add context manager methods to AdminClient
15cbe254 tests: fix missing test in test_store_retry
d7fe03ca admin: refactor _admin_notify() to receive a pho_id
a3794938 cli: add daemon notify when adding a dir (dummy device)
644e18ef cli: remove target file if get request fails
54a29ae5 tests: add 'return 0' to test_resource_availability.sh

git-subtree-dir: external/phobos
git-subtree-split: e07d0af484bdffbcb1e1a86ee1234a7413e8309f
parent d073d06c
......@@ -215,6 +215,63 @@ $ phobos getmd obj0123
cksum=md5:7c28aec5441644094064fcf651ab5e3e,user=foo
```
##### Listing objects
To list objects, use `phobos object list`:
```
$ phobos object list -o oid,user_md
| oid | user_md |
|-------|-----------------|
| obj01 | {} |
| obj02 | {"user": "foo"} |
```
You can add a pattern to match object oids:
```
phobos object list "obj.*"
```
The accepted patterns are Basic Regular Expressions (BRE) and Extended
Regular Expressions (ERE). As defined in [PostgreSQL manual](https://www.postgresql.org/docs/9.3/functions-matching.html#POSIX-SYNTAX-DETAILS),
PSQL also accepts Advanced Regular Expressions (ARE), but we will not maintain
this feature as ARE is not a POSIX standard.
The option '-m' applies a filter on user metadata
```
phobos object list -m user=foo,test=abd
```
##### Listing extents
To list extents, use `phobos extent list`:
```
$ phobos extent list -o oid,ext_count,layout,media_name
| oid | ext_count | layout | media_name |
|------|-----------|--------|------------------------|
| obj1 | 1 | simple | ['/tmp/d1'] |
| obj2 | 2 | raid1 | ['/tmp/d1', '/tmp/d2'] |
```
The option `--degroup` outputs an extent per row instead of one object per row:
```
$ phobos extent list --degroup -o oid,layout,media_name
| oid | layout | media_name |
|------|--------|-------------|
| obj1 | simple | ['/tmp/d1'] |
| obj2 | raid1 | ['/tmp/d1'] |
| obj2 | raid1 | ['/tmp/d2'] |
```
The option `--name` applies a filter on a medium's name. Here, the name does not
accept a pattern.
```
phobos extent list --name tape01
```
You can add a POSIX pattern to match oids, as described in "Listing objects"
section:
```
phobos extent list "obj.*"
```
## Device and media management
### Listing resources
Any device or media can be listed using the 'list' operation. For instance,
......
......@@ -50,28 +50,36 @@ default_family = tape
# drives).
#############################################################
# List of drive models implementing each generation of drive
# (comma-separated lists without any space)
[drive_type "LTO5_drive"]
# List of drive models implementing LTO gen 5
# (comma-separated list without any space)
models = ULTRIUM-TD5,ULT3580-TD5,ULTRIUM-HH5,ULT3580-HH5,HH LTO Gen 5
[drive_type "LTO6_drive"]
# List of drive models implementing LTO gen 6
# (comma-separated list without any space)
models = ULTRIUM-TD6,ULT3580-TD6,ULTRIUM-HH6,ULT3580-HH6,HH LTO Gen 6
[drive_type "LTO7_drive"]
models = ULTRIUM-TD7,ULT3580-TD7,ULTRIUM-HH7,ULT3580-HH7,HH LTO Gen 7
[drive_type "LTO8_drive"]
models = ULTRIUM-TD8,ULT3580-TD8,ULTRIUM-HH8,ULT3580-HH8,HH LTO Gen 8
[tape_model]
# List of supported tape models (checked by "phobos tape add ...")
# comma separated list of tape models, without any space
# default: LTO5,LTO6,LTO7,LTO8,T10KB,T10KC,T10KD
#supported_list = LTO5,LTO6,LTO7,LTO8,T10KB,T10KC,T10KD
# List of drive types that can write each tape generation
# (comma-separated lists without any space)
[tape_type "LTO5"]
# List of drive types that can write LTO5 tapes
# (comma-separated list without any space)
drive_rw = LTO5_drive,LTO6_drive
[tape_type "LTO6"]
# List of drive types that can write LTO6 tapes
# (comma-separated list without any space)
drive_rw = LTO6_drive
drive_rw = LTO6_drive,LTO7_drive
[tape_type "LTO7"]
drive_rw = LTO7_drive,LTO8_drive
[tape_type "LTO8"]
drive_rw = LTO8_drive
......@@ -39,7 +39,7 @@ entries are addressed, organized, and accessed in this backend.
(e.g. as extended attributes, or a metadata_blob). This information is critical
to rebuild phobos database in case of accidental loss.
- Adapater should provide an object-base interface: put/get/del
- Adapters should provide an object-based interface: put/get/del
- If possible, it must provide a feature to manage media transactions,
that could be performed after writing an extent or after writing a set of
......
......@@ -33,7 +33,9 @@ Other operators are available. They are used as such:
- $LT: less than (<)
- $LTE: less or equal than (<=)
- $LIKE: pattern matching, wildcard is '%'
- $REGEXP: posix regexp matching
- $INJSON: json inclusion test
- $KVINJSON: json key-value inclusion test
- $XJSON: json existence test
== Boolean combinators ==
......
......@@ -9,11 +9,20 @@ URL: http://www-hpc.cea.fr
Source0: %{name}-%{version}.tar.gz
%define phobosdocdir %{_docdir}/%{name}
%define __python /usr/bin/python2
BuildRequires: postgresql94-devel
%if 0%{?rhel} >= 8
%global python_prefix python2
%global postgres_prefix postgresql
%else
%global python_prefix python
%global postgres_prefix postgresql94
%endif
BuildRequires: %{postgres_prefix}-devel
BuildRequires: glib2-devel >= 2.28
BuildRequires: python-devel
BuildRequires: %{python_prefix}-devel
BuildRequires: jansson-devel >= 2.5
BuildRequires: libini_config-devel
BuildRequires: openssl-devel
......@@ -22,19 +31,22 @@ BuildRequires: sg3_utils-devel
BuildRequires: protobuf-c-devel
BuildRequires: systemd
Requires: postgresql94-server
Requires: postgresql94-contrib
Requires: %{postgres_prefix}-server
Requires: %{postgres_prefix}-contrib
Requires: glib2 >= 2.28
Requires: jansson
Requires: libini_config
Requires: openssl
Requires: attr
Requires: libattr
Requires: python
Requires: %{python_prefix}
%if 0%{?rhel} < 8
Requires: python-argparse
Requires: python-yaml
Requires: clustershell
Requires: python-psycopg2
Requires: python2-tabulate
%endif
Requires: %{python_prefix}-yaml
Requires: %{python_prefix}-clustershell
Requires: %{python_prefix}-psycopg2
Requires: %{python_prefix}-tabulate
Requires: protobuf
Requires: protobuf-c
......@@ -55,7 +67,9 @@ object store from a C program.
%build
%if 0%{?rhel} < 8
export PKG_CONFIG_PATH=/usr/pgsql-9.4/lib/pkgconfig
%endif
%configure
make %{?_smp_mflags}
......@@ -99,6 +113,26 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/phobos/libpho_layout_*.la
%{_libdir}/libphobos_admin.la
%changelog
* Thu Sep 10 2020 - Thomas Leibovici <thomas.leibovici@cea.fr> - 1.90.0-1
- Release 1.90 (Hi'iaka): 1.9x is the release cycle towards 2.0
- Phobos now runs a daemon (phobosd) on each IO node to manage devices and
schedule IOs
- Object and extent listing and filtering commands
- Layout and family selection from (m)put command line
- Split too large extents on multiple media
- Port to RHEL/CentOS8
- Various fixes and refactoring
* Fri Jan 10 2020 - Thomas Leibovici <thomas.leibovici@cea.fr> - 1.3.1-1
- Pass 'scsi generic' devices to LTFS instead of 'scsi tape'.
* Tue Nov 26 2019 - Thomas Leibovici <thomas.leibovici@cea.fr> - 1.3.0-1
- Release 1.3 (Vala)
- Now uses LTFS 2.4. No longer requires lin_tape.
- Media list filter by tag, e.g. "phobos tape list -T foo,bar"
- Drive list filter by model, e.g. "phobos drive list -m ULTRIUM-TD6"
- Fix various memory leaks and wrong behaviors
* Thu Jul 18 2019 - Thomas Leibovici <thomas.leibovici@cea.fr> - 1.2.0-1
- Final 1.2 release (Echo)
- New features:
......
......@@ -28,12 +28,17 @@
#include "phobos_admin.h"
#include <errno.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "pho_cfg.h"
#include "pho_comm.h"
#include "pho_common.h"
#include "pho_dss.h"
#include "pho_ldm.h"
#include "pho_srl_lrs.h"
#include "pho_type_utils.h"
enum pho_cfg_params_admin {
/* Actual admin parameters */
......@@ -52,6 +57,10 @@ const struct pho_config_item cfg_admin[] = {
},
};
/* ****************************************************************************/
/* Static Communication-related Functions *************************************/
/* ****************************************************************************/
static int _send_and_receive(struct admin_handle *adm, pho_req_t *req,
pho_resp_t **resp)
{
......@@ -91,6 +100,263 @@ static int _send_and_receive(struct admin_handle *adm, pho_req_t *req,
return 0;
}
static int _admin_notify(struct admin_handle *adm, struct pho_id *id,
enum notify_op op)
{
pho_resp_t *resp;
pho_req_t req;
int rid = 1;
int rc;
if (op <= PHO_NTFY_OP_INVAL || op >= PHO_NTFY_OP_LAST)
LOG_RETURN(-ENOTSUP, "Operation not supported");
rc = pho_srl_request_notify_alloc(&req);
if (rc)
LOG_RETURN(rc, "Cannot create notify request");
req.id = rid;
req.notify->op = op;
req.notify->rsrc_id->family = id->family;
req.notify->rsrc_id->name = strdup(id->name);
rc = _send_and_receive(adm, &req, &resp);
if (rc)
LOG_RETURN(rc, "Error with LRS communication");
if (pho_response_is_notify(resp)) {
if (resp->req_id == rid &&
(int) id->family == (int) resp->notify->rsrc_id->family &&
!strcmp(resp->notify->rsrc_id->name, id->name)) {
pho_debug("Notify request succeeded");
goto out;
}
LOG_GOTO(out, rc = -EINVAL, "Received response does not "
"answer emitted request");
}
if (pho_response_is_error(resp)) {
rc = resp->error->rc;
LOG_GOTO(out, rc, "Received error response");
}
pho_error(rc = -EINVAL, "Received invalid response");
out:
pho_srl_response_free(resp, true);
return rc;
}
/* ****************************************************************************/
/* Static Database-related Functions ******************************************/
/* ****************************************************************************/
static int _add_device_in_dss(struct admin_handle *adm, struct pho_id *dev_ids,
unsigned int num_dev, bool keep_locked)
{
char host_name[HOST_NAME_MAX + 1];
enum rsc_adm_status status;
struct ldm_dev_state lds = {};
struct dev_info *devices;
struct dev_adapter deva;
char *real_path;
int rc;
int i;
rc = get_dev_adapter(dev_ids[0].family, &deva);
if (rc)
LOG_RETURN(rc, "Cannot get device adapter");
rc = gethostname(host_name, HOST_NAME_MAX);
if (rc)
LOG_RETURN(rc, "Cannot get host name");
if (strchr(host_name, '.'))
*strchr(host_name, '.') = '\0';
devices = calloc(num_dev, sizeof(*devices));
if (!devices)
LOG_RETURN(errno, "Device info allocation failed");
for (i = 0; i < num_dev; ++i) {
struct dev_info *devi = devices + i;
real_path = realpath(dev_ids[i].name, NULL);
if (!real_path)
LOG_GOTO(out_free, rc = -errno,
"Cannot get the real path of device '%s'",
dev_ids[i].name);
rc = ldm_dev_query(&deva, real_path, &lds);
free(real_path);
if (rc)
LOG_GOTO(out_free, rc, "Failed to query device '%s'",
dev_ids[i].name);
status = keep_locked ? PHO_RSC_ADM_ST_LOCKED : PHO_RSC_ADM_ST_UNLOCKED;
devi->rsc.id.family = dev_ids[i].family;
pho_id_name_set(&devi->rsc.id, lds.lds_serial);
devi->rsc.adm_status = status;
devi->host = host_name;
if (lds.lds_model) {
devi->rsc.model = strdup(lds.lds_model);
if (!devi->rsc.model)
LOG_GOTO(out_free, rc = -errno, "Allocation failed");
}
devi->path = strdup(dev_ids[i].name);
if (!devi->path)
LOG_GOTO(out_free, rc = -errno, "Allocation failed");
ldm_dev_state_fini(&lds);
pho_info("Will add device '%s:%s' to the database: "
"model=%s serial=%s (%s)", rsc_family2str(devi->rsc.id.family),
dev_ids[i].name, devi->rsc.model, devi->rsc.id.name,
rsc_adm_status2str(devi->rsc.adm_status));
/* in case the name given by the user is not the device ID name */
if (strcmp(dev_ids[i].name, devi->rsc.id.name))
strcpy(dev_ids[i].name, devi->rsc.id.name);
}
rc = dss_device_set(&adm->dss, devices, num_dev, DSS_SET_INSERT);
if (rc)
LOG_GOTO(out_free, rc, "Cannot add devices");
out_free:
for (i = 0; i < num_dev; ++i) {
free(devices[i].rsc.model);
free(devices[i].path);
}
free(devices);
return rc;
}
static int _get_device_by_id(struct admin_handle *adm, struct pho_id *dev_id,
struct dev_info **dev_res)
{
struct dss_filter filter;
int dcnt;
int rc;
rc = dss_filter_build(&filter,
"{\"$AND\": ["
" {\"DSS::DEV::family\": \"%s\"},"
" {\"$OR\": ["
" {\"DSS::DEV::serial\": \"%s\"},"
" {\"DSS::DEV::path\": \"%s\"}"
" ]}"
"]}",
rsc_family2str(dev_id->family),
dev_id->name, dev_id->name);
if (rc)
return rc;
rc = dss_device_get(&adm->dss, &filter, dev_res, &dcnt);
dss_filter_free(&filter);
if (rc)
return rc;
if (dcnt == 0)
LOG_RETURN(-ENXIO, "Device '%s' not found", dev_id->name);
assert(dcnt == 1);
return 0;
}
static int _device_update_adm_status(struct admin_handle *adm,
struct pho_id *dev_ids, int num_dev,
enum rsc_adm_status status, bool is_forced)
{
struct dev_info *devices;
int avail_devices = 0;
int rc;
int i;
devices = calloc(num_dev, sizeof(*devices));
if (!devices)
LOG_RETURN(-ENOMEM, "Device info allocation failed");
for (i = 0; i < num_dev; ++i) {
struct dev_info *dev_res;
rc = _get_device_by_id(adm, dev_ids + i, &dev_res);
if (rc)
goto out_free;
rc = dss_device_lock(&adm->dss, dev_res, 1, adm->lock_owner);
if (rc) {
pho_error(-EBUSY, "Device '%s' is in use by '%s'", dev_ids[i].name,
dev_res->lock.lock);
dss_res_free(dev_res, 1);
continue;
}
dss_res_free(dev_res, 1);
rc = _get_device_by_id(adm, dev_ids + i, &dev_res);
if (rc)
goto out_free;
/* TODO: to uncomment once the dss_device_lock() is removed ie. when
* the update of a single database field will be implemented
*/
/* if (strcmp(dev_res->lock.lock, "")) {
* if (!is_forced) {
* pho_error(-EBUSY, "Device '%s' is in use by '%s'",
* dev_ids[i].name, dev_res->lock.lock);
* dss_res_free(dev_res, 1);
* continue;
* } else if (status == PHO_RSC_ADM_ST_LOCKED) {
* pho_warn("Device '%s' is in use. Administrative locking will "
* "not be effective immediately", dev_res->rsc.id.name);
* }
* }
*/
if (dev_res->rsc.adm_status == status)
pho_warn("Device '%s' is already in the desired state",
dev_ids[i].name);
dev_res->rsc.adm_status = status;
dev_info_cpy(&devices[avail_devices++], dev_res);
dss_res_free(dev_res, 1);
}
if (avail_devices != num_dev)
LOG_GOTO(out_free, rc = -EBUSY,
"At least one device is in use, use --force");
rc = dss_device_set(&adm->dss, devices, num_dev, DSS_SET_UPDATE);
if (rc)
goto out_free;
// in case the name given by the user is not the device ID name
for (i = 0; i < num_dev; ++i)
if (strcmp(dev_ids[i].name, devices[i].rsc.id.name))
strcpy(dev_ids[i].name, devices[i].rsc.id.name);
out_free:
if (avail_devices)
dss_device_unlock(&adm->dss, devices, avail_devices, adm->lock_owner);
for (i = 0; i < num_dev; ++i)
dev_info_free(devices + i, false);
free(devices);
return rc;
}
/* ****************************************************************************/
/* API Functions **************************************************************/
/* ****************************************************************************/
void phobos_admin_fini(struct admin_handle *adm)
{
int rc;
......@@ -99,10 +365,14 @@ void phobos_admin_fini(struct admin_handle *adm)
if (rc)
pho_error(rc, "Cannot close the communication socket");
free(adm->lock_owner);
dss_fini(&adm->dss);
}
int phobos_admin_init(struct admin_handle *adm, const bool lrs_required)
static __thread uint64_t adm_lock_number;
int phobos_admin_init(struct admin_handle *adm, bool lrs_required)
{
const char *sock_path;
int rc;
......@@ -110,6 +380,15 @@ int phobos_admin_init(struct admin_handle *adm, const bool lrs_required)
memset(adm, 0, sizeof(*adm));
adm->comm = pho_comm_info_init();
rc = asprintf(&adm->lock_owner, "%.213s:%.8lx:%.16lx:%.16lx",
get_hostname(), syscall(SYS_gettid), time(NULL),
adm_lock_number);
if (rc == -1)
LOG_GOTO(out, rc, "Cannot allocate lock_owner");
++adm_lock_number;
rc = pho_cfg_init_local(NULL);
if (rc && rc != -EALREADY)
return rc;
......@@ -139,71 +418,92 @@ out:
return rc;
}
static int _admin_notify(struct admin_handle *adm, enum rsc_family family,
const char *name, enum notify_op op)
int phobos_admin_device_add(struct admin_handle *adm, struct pho_id *dev_ids,
unsigned int num_dev, bool keep_locked)
{
pho_resp_t *resp;
pho_req_t req;
int rid = 1;
int rc;
int i;
if (op <= PHO_NTFY_OP_INVAL || op >= PHO_NTFY_OP_LAST)
LOG_RETURN(-ENOTSUP, "Operation not supported");
if (!num_dev)
LOG_RETURN(-EINVAL, "No device were given");
rc = pho_srl_request_notify_alloc(&req);
rc = _add_device_in_dss(adm, dev_ids, num_dev, keep_locked);
if (rc)
LOG_RETURN(rc, "Cannot create notify request");
return rc;
req.id = rid;
req.notify->op = op;
req.notify->rsrc_id->family = family;
req.notify->rsrc_id->name = strdup(name);
if (keep_locked)
// do not need to inform the daemon because it does not
// consider locked devices
return 0;
rc = _send_and_receive(adm, &req, &resp);
if (rc)
LOG_RETURN(rc, "Error with LRS communication");
if (!adm->daemon_is_online)
return 0;
if (pho_response_is_notify(resp)) {
if (resp->req_id == rid &&
(int) family == (int) resp->notify->rsrc_id->family &&
!strcmp(resp->notify->rsrc_id->name, name)) {
pho_debug("Notify request succeeded");
goto out;
}
for (i = 0; i < num_dev; ++i) {
int rc2;
LOG_GOTO(out, rc = -EINVAL, "Received response does not "
"answer emitted request");
rc2 = _admin_notify(adm, dev_ids + i, PHO_NTFY_OP_DEVICE_ADD);
if (rc2)
pho_error(rc2, "Failure during daemon notification for '%s'",
dev_ids[i].name);
rc = rc ? : rc2;
}
if (pho_response_is_error(resp)) {
rc = resp->error->rc;
LOG_GOTO(out, rc, "Received error response");
}
return rc;
}
pho_error(rc = -EINVAL, "Received invalid response");
int phobos_admin_device_lock(struct admin_handle *adm, struct pho_id *dev_ids,
int num_dev, bool is_forced)
{
int rc;
int i;
rc = _device_update_adm_stat