Discussion:
[Libstoragemgmt-devel] [PATCH 0/7] Allow plugin run with root privilege
Gris Ge
2015-01-28 14:27:48 UTC
Permalink
* This patch set contains four parts:
0. Patch 1/7 and 2/7 is just bug fix.
1. Update lsmd for allowing plugin run as root user.
2. New plugin: MegaRAID plugin (megaraid://).
# Require root privilege
# Using LSI binary tool -- 'storcli'
# 'storcli' provides 'J' command line switch/option which change
# output to json format, it's pretty handy for coding with it.
3. New plugin: Linux plugin (linux://).
# Require root privilege
# Using udev, sysfs and mdadm.
# The name of this plugin is confusing. Any suggest?
# We might add LVM(it has RAID also) later.

* There are some duplicate codes in utils.py of linux plugin and MegaRAID
plugin. But considering plugin was supposed to be standalone, let's live
with it.

* For missing HP SmartArray plugin, HP has two tools for RHEL 6 and 7:

* RHEL 7 -- Only has hpssacli (new one used in Openstack cinder and
VMWare)
* RHEL 6 -- Only has hpacucli (old one, marked as obsolete[1])

Hence, I would like to postpone it for a while.


Thank you very much in advance.

[1] http://h20565.www2.hp.com/hpsc/doc/public/display?sp4ts.oid=254934&docId=mmr_kc-0110258&docLocale=en_US

Gris Ge (6):
lsmd: Remove incorrect log in child_cleanup()
lsmcli: Add missing Disk.TYPE_SCSI
lsmd Daemon: New feature: allowing plugin run as root.
lsmenv: Support new feature of lsmd for root privilege.
New Plugin: MegaRAID plugin using storcli binary tool.
New Plugin: Linux Plugin for linux storage management

Tony Asleson (1):
lsmd: Clean up logging

Makefile.am | 2 +-
config/Makefile.am | 15 ++
config/lsmd.conf | 1 +
config/pluginconf.d/linux.conf | 1 +
config/pluginconf.d/megaraid.conf | 1 +
config/pluginconf.d/sim.conf | 1 +
configure.ac | 14 ++
daemon/Makefile.am | 4 +-
daemon/lsm_daemon.c | 278 ++++++++++++++++++++++++------
doc/man/Makefile.am | 4 +-
doc/man/linux_lsmplugin.1.in | 54 ++++++
doc/man/lsmd.conf.5.in | 58 +++++++
doc/man/megaraid_lsmplugin.1.in | 50 ++++++
packaging/daemon/libstoragemgmt.service | 2 +-
packaging/libstoragemgmt.spec.in | 63 +++++++
plugin/Makefile.am | 2 +-
plugin/linux/Makefile.am | 6 +
plugin/linux/__init__.py | 1 +
plugin/linux/base_os.py | 134 +++++++++++++++
plugin/linux/linux.py | 122 +++++++++++++
plugin/linux/linux_lsmplugin | 37 ++++
plugin/linux/md_raid.py | 98 +++++++++++
plugin/linux/utils.py | 53 ++++++
plugin/megaraid/Makefile.am | 6 +
plugin/megaraid/__init__.py | 1 +
plugin/megaraid/megaraid.py | 292 ++++++++++++++++++++++++++++++++
plugin/megaraid/megaraid_lsmplugin | 37 ++++
plugin/megaraid/utils.py | 47 +++++
tools/lsmcli/data_display.py | 1 +
tools/lsmenv | 61 ++++++-
30 files changed, 1382 insertions(+), 64 deletions(-)
create mode 100644 config/Makefile.am
create mode 100644 config/lsmd.conf
create mode 100644 config/pluginconf.d/linux.conf
create mode 100644 config/pluginconf.d/megaraid.conf
create mode 100644 config/pluginconf.d/sim.conf
create mode 100644 doc/man/linux_lsmplugin.1.in
create mode 100644 doc/man/lsmd.conf.5.in
create mode 100644 doc/man/megaraid_lsmplugin.1.in
create mode 100644 plugin/linux/Makefile.am
create mode 100644 plugin/linux/__init__.py
create mode 100644 plugin/linux/base_os.py
create mode 100644 plugin/linux/linux.py
create mode 100755 plugin/linux/linux_lsmplugin
create mode 100644 plugin/linux/md_raid.py
create mode 100644 plugin/linux/utils.py
create mode 100644 plugin/megaraid/Makefile.am
create mode 100644 plugin/megaraid/__init__.py
create mode 100644 plugin/megaraid/megaraid.py
create mode 100755 plugin/megaraid/megaraid_lsmplugin
create mode 100644 plugin/megaraid/utils.py
--
1.8.3.1
Gris Ge
2015-01-28 14:27:49 UTC
Permalink
From: Tony Asleson <***@redhat.com>

- Renamed loud() to log_and_exit()
- Changes the logger function to use severity in syslog
call

Signed-off-by: Tony Asleson <***@redhat.com>

Changes in V2:

- Rebase to current master(b933bccad3ee29538f6200ae1b26e3644e9358fe).

Signed-off-by: Gris Ge <***@redhat.com>
---
daemon/lsm_daemon.c | 61 +++++++++++++++++++++++++++++------------------------
1 file changed, 34 insertions(+), 27 deletions(-)

diff --git a/daemon/lsm_daemon.c b/daemon/lsm_daemon.c
index acc8ef5..a60296d 100644
--- a/daemon/lsm_daemon.c
+++ b/daemon/lsm_daemon.c
@@ -96,15 +96,18 @@ void logger(int severity, const char *fmt, ...)
{
char buf[2048];

- if( (LOG_INFO == severity && verbose_flag) || LOG_ERR == LOG_WARNING
- || LOG_ERR == severity) {
+ if( verbose_flag || LOG_WARNING == severity || LOG_ERR == severity) {
va_list arg;
va_start(arg, fmt);
vsnprintf(buf, sizeof(buf), fmt, arg);
va_end(arg);

if( !systemd ) {
- syslog(LOG_ERR, "%s", buf);
+ if( verbose_flag ) {
+ syslog(LOG_ERR, "%s", buf);
+ } else {
+ syslog(severity, "%s", buf);
+ }
} else {
fprintf(stdout, "%s", buf);
fflush(stdout);
@@ -115,7 +118,7 @@ void logger(int severity, const char *fmt, ...)
}
}
}
-#define loud(fmt, ...) logger(LOG_ERR, fmt, ##__VA_ARGS__)
+#define log_and_exit(fmt, ...) logger(LOG_ERR, fmt, ##__VA_ARGS__)
#define warn(fmt, ...) logger(LOG_WARNING, fmt, ##__VA_ARGS__)
#define info(fmt, ...) logger(LOG_INFO, fmt, ##__VA_ARGS__)

@@ -157,17 +160,20 @@ void drop_privileges(void)

if( -1 == setgid(pw->pw_gid) ) {
err = errno;
- loud("Unexpected error on setgid(errno %d)\n", err);
+ log_and_exit(
+ "Unexpected error on setgid(errno %d)\n", err);
}

if( -1 == setgroups(1, &pw->pw_gid) ) {
err = errno;
- loud("Unexpected error on setgroups(errno %d)\n", err);
+ log_and_exit(
+ "Unexpected error on setgroups(errno %d)\n", err);
}

if( -1 == setuid(pw->pw_uid) ) {
err = errno;
- loud("Unexpected error on setuid(errno %d)\n", err);
+ log_and_exit(
+ "Unexpected error on setuid(errno %d)\n", err);
}
} else if ( pw->pw_uid != getuid() ) {
warn("Daemon not running as correct user\n");
@@ -187,14 +193,14 @@ void flight_check(void)
int err = 0;
if( -1 == access(socket_dir, R_OK|W_OK )) {
err = errno;
- loud("Unable to access socket directory %s, errno= %d\n",
- socket_dir, err);
+ log_and_exit("Unable to access socket directory %s, errno= %d\n",
+ socket_dir, err);
}

if( -1 == access(plugin_dir, R_OK|X_OK)) {
err = errno;
- loud("Unable to access plug-in directory %s, errno= %d\n",
- plugin_dir, err);
+ log_and_exit("Unable to access plug-in directory %s, errno= %d\n",
+ plugin_dir, err);
}
}

@@ -225,7 +231,7 @@ char *path_form(const char* path, const char *name)
if( full ) {
snprintf(full, s, "%s/%s", path, name);
} else {
- loud("malloc failure while trying to allocate %d bytes\n", s);
+ log_and_exit("malloc failure while trying to allocate %d bytes\n", s);
}
return full;
}
@@ -277,12 +283,13 @@ void process_directory( char *dir, void *p, file_op call_back)

if( closedir(dp) ) {
err = errno;
- loud("Error on closing dir %s: %s\n", dir, strerror(err));
+ log_and_exit("Error on closing dir %s: %s\n", dir,
+ strerror(err));
}
} else {
err = errno;
- loud("Error on processing directory %s: %s\n", dir,
- strerror(err));
+ log_and_exit("Error on processing directory %s: %s\n", dir,
+ strerror(err));
}
}
}
@@ -304,7 +311,7 @@ int delete_socket(void *p, char *full_name)
if( S_ISSOCK(statbuf.st_mode)) {
if( unlink(full_name) ) {
err = errno;
- loud("Error on unlinking file %s: %s\n",
+ log_and_exit("Error on unlinking file %s: %s\n",
full_name, strerror(err));
}
}
@@ -351,23 +358,23 @@ int setup_socket(char *full_name)

if( -1 == bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un))) {
err = errno;
- loud("Error on binding socket %s: %s\n", socket_file, strerror(err));
+ log_and_exit("Error on binding socket %s: %s\n", socket_file, strerror(err));
}

if( -1 == chmod(socket_file, S_IREAD|S_IWRITE|S_IRGRP|S_IWGRP
|S_IROTH|S_IWOTH)) {
err = errno;
- loud("Error on chmod socket file %s: %s\n", socket_file, strerror(err));
+ log_and_exit("Error on chmod socket file %s: %s\n", socket_file, strerror(err));
}

if( -1 == listen(fd, 5)) {
err = errno;
- loud("Error on listening %s: %s\n", socket_file, strerror(err));
+ log_and_exit("Error on listening %s: %s\n", socket_file, strerror(err));
}

} else {
err = errno;
- loud("Error on socket create %s: %s\n",
+ log_and_exit("Error on socket create %s: %s\n",
socket_file, strerror(err));
}

@@ -429,10 +436,10 @@ int process_plugin(void *p, char *full_name)
setup_socket will exit on error. */
free(item);
item = NULL;
- loud("strdup failed %s\n", full_name);
+ log_and_exit("strdup failed %s\n", full_name);
}
} else {
- loud("Memory allocation failure!\n");
+ log_and_exit("Memory allocation failure!\n");
}
}
}
@@ -456,7 +463,7 @@ void child_cleanup(void)

if( -1 == rc ) {
err = errno;
- warn("Error: waitid %d - %s\n", err, strerror(err));
+ info("waitid %d - %s\n", err, strerror(err));
break;
} else {
if( 0 == rc && si.si_pid == 0 ) {
@@ -566,7 +573,7 @@ void exec_plugin( char *plugin, int client_fd )

if( -1 == exec_rc ) {
int err = errno;
- loud("Error on exec'ing Plugin %s: %s\n",
+ log_and_exit("Error on exec'ing Plugin %s: %s\n",
p_copy, strerror(err));
}
}
@@ -598,7 +605,7 @@ void _serving(void)
}

if( !nfds ) {
- loud("No plugins found in directory %s\n", plugin_dir);
+ log_and_exit("No plugins found in directory %s\n", plugin_dir);
}

nfds += 1;
@@ -609,7 +616,7 @@ void _serving(void)
return;
} else {
err = errno;
- loud("Error on selecting Plugin: %s", strerror(err));
+ log_and_exit("Error on selecting Plugin: %s", strerror(err));
}
} else if( ready > 0 ) {
int fd = 0;
@@ -729,7 +736,7 @@ int main(int argc, char *argv[])
if( !systemd ) {
if ( -1 == daemon(0, 0) ) {
int err = errno;
- loud("Error on calling daemon: %s\n", strerror(err));
+ log_and_exit("Error on calling daemon: %s\n", strerror(err));
}
}
--
1.8.3.1
Gris Ge
2015-01-28 14:27:50 UTC
Permalink
Problem:
lsmd will keep printing out(info level) message below every 15 seconds:
'waitid 10 - No child processes'

Root cause:
In _serving() method, the select() call will timeout at tmo(15 seconds).
When timeout, actually not child process has been created.
This cause waitid() complain about '10 - No child processes'.

Fix:
Don't print out this message if the error of waitid() is ECHILD.

Signed-off-by: Gris Ge <***@redhat.com>
---
daemon/lsm_daemon.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/daemon/lsm_daemon.c b/daemon/lsm_daemon.c
index a60296d..1b8a769 100644
--- a/daemon/lsm_daemon.c
+++ b/daemon/lsm_daemon.c
@@ -463,7 +463,9 @@ void child_cleanup(void)

if( -1 == rc ) {
err = errno;
- info("waitid %d - %s\n", err, strerror(err));
+ if (err != ECHILD){
+ info("waitid %d - %s\n", err, strerror(err));
+ }
break;
} else {
if( 0 == rc && si.si_pid == 0 ) {
--
1.8.3.1
Gris Ge
2015-01-28 14:27:51 UTC
Permalink
* Show Disk.TYPE_SCSI as 'SCSI' instead of 'Unknown(8)'

Signed-off-by: Gris Ge <***@redhat.com>
---
tools/lsmcli/data_display.py | 1 +
1 file changed, 1 insertion(+)

diff --git a/tools/lsmcli/data_display.py b/tools/lsmcli/data_display.py
index 1d8f61e..285a14f 100644
--- a/tools/lsmcli/data_display.py
+++ b/tools/lsmcli/data_display.py
@@ -174,6 +174,7 @@ _DISK_TYPE_CONV = {
Disk.TYPE_SAS: 'SAS',
Disk.TYPE_FC: 'FC',
Disk.TYPE_SOP: 'SCSI Over PCI-E(SSD)',
+ Disk.TYPE_SCSI: 'SCSI',
Disk.TYPE_NL_SAS: 'NL_SAS',
Disk.TYPE_HDD: 'HDD',
Disk.TYPE_SSD: 'SSD',
--
1.8.3.1
Gris Ge
2015-01-28 14:27:52 UTC
Permalink
* This patch allow plugins running as root privilege if these conditions
are all meet:
1. "allow-plugin-root-privilege = true;" in /etc/lsm/lsmd.conf
# This allows admin to disable root privilege globally.

2. "require-root-privilege = true;" in plugin config:
/etc/lsm/pluginconf.d/<uri_scheme>.conf
# This allows plugin level config.

3. API connection(or lsmcli) is ran by root user.
# This is for security concern.

* Warning message will be printed or redirect to syslog(depend on lsmd
command line option '-d') by lsmd if any of above condition failed.
It's plugin's responsibility to provide proper error message to API client.

* If no plugin installed has 'require-root-privilege = true;' config line,
lsmd will drop the non-root privilege.

* Implementation:
1. Use libconfig 1.3.2+ to read config files.
(might works on older version, but only tested on RHEL 6)
2. Use getsockopt() to get API client uid.

* Autotools config file updated.

* RPM SPEC file updated.

* The default installation will install lsmd.conf with
"allow-plugin-root-privilege = true;".
With this, plugins could work out of box by installing correct config
into /etc/lsm/pluginconf.d/ folder.

* New manpage 'lsmd.conf(5)'.

* Sample plugin config "pluginconf.d/sim.conf" created with
"require-root-privilege = false;" as a demonstration.

* Updated systemd service file 'libstoragemgmt.service' by
changing running user from 'libstoragemgmt' to 'root'.

* Known bug:
1. Rebuild the source rpm will fail at test stage when run as root user
with libstoragemgmt user account.
Root cause:
As lsmd will try to drop privilege to libstoragemgmt user which
has no read access to the plugin directory.
Workaround:
A: Don't compile as root as everyone(except Slackware) suggests.
B: Remove libstoragemgmt user account.

Signed-off-by: Gris Ge <***@redhat.com>
---
Makefile.am | 2 +-
config/Makefile.am | 8 ++
config/lsmd.conf | 1 +
config/pluginconf.d/sim.conf | 1 +
configure.ac | 10 ++
daemon/Makefile.am | 4 +-
daemon/lsm_daemon.c | 229 +++++++++++++++++++++++++++-----
doc/man/Makefile.am | 1 +
doc/man/lsmd.conf.5.in | 58 ++++++++
packaging/daemon/libstoragemgmt.service | 2 +-
packaging/libstoragemgmt.spec.in | 5 +
11 files changed, 283 insertions(+), 38 deletions(-)
create mode 100644 config/Makefile.am
create mode 100644 config/lsmd.conf
create mode 100644 config/pluginconf.d/sim.conf
create mode 100644 doc/man/lsmd.conf.5.in

diff --git a/Makefile.am b/Makefile.am
index 99e7578..2452931 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@ ACLOCAL_AMFLAGS = -I m4

DISTCHECK_CONFIGURE_FLAGS = --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)

-SUBDIRS= c_binding python_binding plugin doc tools daemon packaging
+SUBDIRS= c_binding python_binding plugin doc tools daemon packaging config

if BUILD_C_UNIT
SUBDIRS += test
diff --git a/config/Makefile.am b/config/Makefile.am
new file mode 100644
index 0000000..71291ad
--- /dev/null
+++ b/config/Makefile.am
@@ -0,0 +1,8 @@
+lsmconfdir=$(sysconfdir)/lsm
+lsmconf_DATA=lsmd.conf
+
+EXTRA_DIST= lsmd.conf pluginconf.d/sim.conf
+
+pluginconfdir=$(sysconfdir)/lsm/pluginconf.d
+
+pluginconf_DATA=pluginconf.d/sim.conf
diff --git a/config/lsmd.conf b/config/lsmd.conf
new file mode 100644
index 0000000..7eb4335
--- /dev/null
+++ b/config/lsmd.conf
@@ -0,0 +1 @@
+allow-plugin-root-privilege = true;
diff --git a/config/pluginconf.d/sim.conf b/config/pluginconf.d/sim.conf
new file mode 100644
index 0000000..da915bf
--- /dev/null
+++ b/config/pluginconf.d/sim.conf
@@ -0,0 +1 @@
+require-root-privilege = false;
diff --git a/configure.ac b/configure.ac
index 1c27fa0..eeaa350 100644
--- a/configure.ac
+++ b/configure.ac
@@ -180,6 +180,14 @@ if test "x$with_rest_api" = "xyes"; then
fi
AM_CONDITIONAL([WITH_REST_API], [test "x$with_rest_api" = "xyes"])

+dnl ==========================================================================
+dnl Check for libconfig as it is needed for lsmd daemon
+dnl ==========================================================================
+PKG_CHECK_MODULES(
+ [LIBCONFIG], [libconfig >= 1.3.2],,
+ AC_MSG_ERROR([libconfig 1.3.2 or newer not found.])
+)
+
#Setup the unit directory for systemd stuff
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
@@ -201,10 +209,12 @@ AC_OUTPUT(libstoragemgmt.pc \
plugin/Makefile \
plugin/simc/Makefile \
daemon/Makefile \
+ config/Makefile \
doc/Makefile \
doc/man/lsmcli.1 \
doc/man/lsmd.1 \
doc/doxygen.conf \
+ doc/man/lsmd.conf.5 \
tools/Makefile \
tools/udev/Makefile \
tools/lsmcli/Makefile \
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 521055f..a9ef569 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -2,8 +2,8 @@ bin_PROGRAMS = lsmd

EXTRA_DIST=lsm_rest.c lsm_rest.h

-lsmd_LDFLAGS=-Wl,-z,relro,-z,now -pie
-lsmd_CFLAGS=-fPIE -DPIE
+lsmd_LDFLAGS=-Wl,-z,relro,-z,now -pie $(LIBCONFIG_LIBS)
+lsmd_CFLAGS=-fPIE -DPIE $(LIBCONFIG_CFLAGS)

lsmd_SOURCES = lsm_daemon.c

diff --git a/daemon/lsm_daemon.c b/daemon/lsm_daemon.c
index 1b8a769..8a9e8db 100644
--- a/daemon/lsm_daemon.c
+++ b/daemon/lsm_daemon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2014 Red Hat, Inc.
+ * Copyright (C) 2011-2015 Red Hat, Inc.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -17,10 +17,10 @@
* Author: tasleson
*/

+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
-#define _GNU_SOURCE
#include <string.h>
#include <ctype.h>
#include <signal.h>
@@ -43,11 +43,17 @@
#include <assert.h>
#include <grp.h>
#include <limits.h>
+#include <libconfig.h>

#define BASE_DIR "/var/run/lsm"
#define SOCKET_DIR BASE_DIR"/ipc"
#define PLUGIN_DIR "/usr/bin"
#define LSM_USER "libstoragemgmt"
+#define LSM_CONF_DIR "/etc/lsm/"
+#define LSM_PLUGIN_CONF_DIR_NAME "pluginconf.d"
+#define LSMD_CONF_FILE "lsmd.conf"
+#define LSM_CONF_ALLOW_ROOT_OPT_NAME "allow-plugin-root-privilege"
+#define LSM_CONF_REQUIRE_ROOT_OPT_NAME "require-root-privilege"

#define min(a,b) \
({ __typeof__ (a) _a = (a); \
@@ -64,19 +70,26 @@ int systemd = 0;

char *socket_dir = SOCKET_DIR;
char *plugin_dir = PLUGIN_DIR;
+char *conf_dir = LSM_CONF_DIR;

char plugin_extension[] = "_lsmplugin";

+char plugin_conf_extension[] = ".conf";
+
typedef enum { RUNNING, RESTART, EXIT } serve_type;
serve_type serve_state = RUNNING;

int plugin_mem_debug = 0;

+int allow_root_plugin = 0;
+int has_root_plugin = 0;
+
/**
* Each item in plugin list contains this information
*/
struct plugin {
char *file_path;
+ int require_root;
int fd;
LIST_ENTRY(plugin) pointers;
};
@@ -153,35 +166,33 @@ void drop_privileges(void)
int err = 0;
struct passwd *pw = NULL;

- if( !systemd ) {
- pw = getpwnam(LSM_USER);
- if( pw ) {
- if ( !geteuid() ) {
-
- if( -1 == setgid(pw->pw_gid) ) {
- err = errno;
- log_and_exit(
- "Unexpected error on setgid(errno %d)\n", err);
- }
+ pw = getpwnam(LSM_USER);
+ if( pw ) {
+ if ( !geteuid() ) {

- if( -1 == setgroups(1, &pw->pw_gid) ) {
- err = errno;
- log_and_exit(
- "Unexpected error on setgroups(errno %d)\n", err);
- }
+ if( -1 == setgid(pw->pw_gid) ) {
+ err = errno;
+ log_and_exit(
+ "Unexpected error on setgid(errno %d)\n", err);
+ }

- if( -1 == setuid(pw->pw_uid) ) {
- err = errno;
- log_and_exit(
- "Unexpected error on setuid(errno %d)\n", err);
- }
- } else if ( pw->pw_uid != getuid() ) {
- warn("Daemon not running as correct user\n");
+ if( -1 == setgroups(1, &pw->pw_gid) ) {
+ err = errno;
+ log_and_exit(
+ "Unexpected error on setgroups(errno %d)\n", err);
}
- } else {
- info("Warn: Missing %s user, running as existing user!\n",
- LSM_USER);
+
+ if( -1 == setuid(pw->pw_uid) ) {
+ err = errno;
+ log_and_exit(
+ "Unexpected error on setuid(errno %d)\n", err);
+ }
+ } else if ( pw->pw_uid != getuid() ) {
+ warn("Daemon not running as correct user\n");
}
+ } else {
+ info("Warn: Missing %s user, running as existing user!\n",
+ LSM_USER);
}
}

@@ -214,6 +225,8 @@ void usage(void)
printf(" --plugindir = The directory where the plugins are located\n");
printf(" --socketdir = The directory where the Unix domain sockets will "
"be created\n");
+ printf(" --confdir = The directory where the config files are "
+ "located\n");
printf(" -v = Verbose logging\n");
printf(" -d = new style daemon (systemd)\n");
}
@@ -410,6 +423,94 @@ void empty_plugin_list(struct plugin_list *list)
}

/**
+ * Parse config and seeking provided key name bool
+ * 1. Keep value untouched if file not exist
+ * 2. If file is not readable, abort via log_and_exit()
+ * 3. Keep value untouched if provided key not found
+ * 4. Abort via log_and_exit() if no enough memory.
+ * @param conf_path config file path
+ * @param key_name string, searching key
+ * @param value int, output, value of this config key
+ */
+
+void parse_conf_bool(char * conf_path, char *key_name, int *value)
+{
+ if( access(conf_path, F_OK) == -1 ) {
+ /* file not exist. */
+ return;
+ }
+ config_t *cfg = (config_t *)malloc(sizeof(config_t));
+ if (cfg){
+ config_init(cfg);
+ if (CONFIG_TRUE == config_read_file(cfg, conf_path)){
+ config_lookup_bool(cfg, key_name, value);
+ }else{
+ log_and_exit(
+ "configure %s parsing failed: %s at line %d\n",
+ conf_path, config_error_text(cfg), config_error_line(cfg));
+ }
+ } else{
+ log_and_exit(
+ "malloc failure while trying to allocate memory for config_t\n");
+ }
+
+ config_destroy(cfg);
+}
+
+/**
+ * Load plugin config for root privilege setting.
+ * If config not found, return 0 for no root privilege required.
+ * @param plugin_path Full path of plugin
+ * @return 1 for require root privilege, 0 or not.
+ */
+
+int chk_pconf_root_pri(char *plugin_path)
+{
+ int require_root = 0;
+ char *base_name = basename(plugin_path);
+ ssize_t plugin_name_len =
+ strlen(base_name) - strlen(plugin_extension);
+ if ( plugin_name_len <= 0 ){
+ log_and_exit(
+ "Got invalid plugin full path %s\n", plugin_path);
+ }
+ ssize_t conf_file_name_len = plugin_name_len +
+ strlen(plugin_conf_extension) + 1;
+ char *plugin_conf_filename =
+ (char *)malloc(conf_file_name_len);
+ if (plugin_conf_filename){
+ strncpy(
+ plugin_conf_filename, base_name, plugin_name_len);
+ strcpy(
+ plugin_conf_filename+plugin_name_len,
+ plugin_conf_extension);
+ plugin_conf_filename[conf_file_name_len] = '\0';
+ }else{
+ log_and_exit(
+ "malloc failure while trying to allocate %d "
+ "bytes\n", conf_file_name_len);
+ }
+ char *plugin_conf_dir_path =
+ path_form(conf_dir, LSM_PLUGIN_CONF_DIR_NAME);
+
+ char *plugin_conf_path = path_form(
+ plugin_conf_dir_path, plugin_conf_filename);
+ parse_conf_bool(
+ plugin_conf_path, LSM_CONF_REQUIRE_ROOT_OPT_NAME,
+ &require_root);
+
+ if (require_root == 1 && allow_root_plugin == 0){
+ warn(
+ "Plugin %s require root privilege while %s disable globally\n",
+ base_name, LSMD_CONF_FILE);
+ }
+ free(plugin_conf_dir_path);
+ free(plugin_conf_filename);
+ free(plugin_conf_path);
+ return require_root;
+}
+
+/**
* Call back for plug-in processing.
* @param p Private data
* @param full_name Full path and file name
@@ -427,6 +528,8 @@ int process_plugin(void *p, char *full_name)
if( item ) {
item->file_path = strdup(full_name);
item->fd = setup_socket(full_name);
+ item->require_root = chk_pconf_root_pri(full_name);
+ has_root_plugin |= item->require_root;

if( item->file_path && item->fd >= 0 ) {
LIST_INSERT_HEAD((struct plugin_list*)p, item, pointers);
@@ -498,20 +601,25 @@ int process_plugins(void)
clean_up();
info("Scanning plug-in directory %s\n", plugin_dir);
process_directory(plugin_dir, &head, process_plugin);
+ if ( allow_root_plugin == 1 && has_root_plugin == 0 ){
+ info("No plugin requires root privilege, dropping root privilege\n");
+ flight_check();
+ drop_privileges();
+ }
return 0;
}

/**
* Given a socket descriptor looks it up and returns the plug-in
* @param fd Socket descriptor to lookup
- * @return Character string
+ * @return struct plugin
*/
-char *plugin_lookup(int fd)
+struct plugin *plugin_lookup(int fd)
{
struct plugin *plug = NULL;
LIST_FOREACH(plug, &head, pointers) {
if( plug->fd == fd ) {
- return plug->file_path;
+ return plug;
}
}
return NULL;
@@ -521,8 +629,10 @@ char *plugin_lookup(int fd)
* Does the actual fork and exec of the plug-in
* @param plugin Full filename and path of plug-in to exec.
* @param client_fd Client connected file descriptor
+ * @param require_root int, indicate whether this plugin require root
+ * privilege or not
*/
-void exec_plugin( char *plugin, int client_fd )
+void exec_plugin( char *plugin, int client_fd, int require_root)
{
int err = 0;

@@ -544,6 +654,45 @@ void exec_plugin( char *plugin, int client_fd )
char fd_str[12];
char *plugin_argv[7];
extern char **environ;
+ struct ucred cli_user_cred;
+ socklen_t cli_user_cred_len = sizeof(cli_user_cred);
+
+ /*
+ * The plugin will still run no matter with root privilege or not.
+ * so that client could get detailed error message.
+ */
+ if ( require_root == 0 ){
+ drop_privileges();
+ }else{
+ if (getuid()){
+ warn("Plugin %s require root privilege, but lsmd daemon "
+ "is not run as root user\n", plugin);
+ }else if(allow_root_plugin == 0){
+ warn("Plugin %s require root privilege, but %s disabled "
+ "it globally\n", LSMD_CONF_FILE);
+ drop_privileges();
+ }else{
+ /* Check socket client uid */
+ int rc_get_cli_uid = getsockopt(
+ client_fd, SOL_SOCKET, SO_PEERCRED,
+ &cli_user_cred, &cli_user_cred_len);
+ if( 0 == rc_get_cli_uid){
+ if (cli_user_cred.uid != 0){
+ warn("Plugin %s require root privilege, but "
+ "client is not run as root user\n", plugin);
+ drop_privileges();
+ }else{
+ info("Plugin %s is running as root privilege\n",
+ plugin);
+ }
+ }else{
+ warn("Failed to get client socket uid, getsockopt() "
+ "error: %d\n", errno);
+ drop_privileges();
+ }
+ }
+
+ }

/* Make copy of plug-in string as once we call empty_plugin_list it
* will be deleted :-) */
@@ -626,8 +775,8 @@ void _serving(void)
if( FD_ISSET(fd, &readfds) ) {
int cfd = accept(fd, NULL, NULL);
if( -1 != cfd ) {
- char *p = plugin_lookup(fd);
- exec_plugin(p, cfd);
+ struct plugin *p = plugin_lookup(fd);
+ exec_plugin(p->file_path, cfd, p->require_root);
} else {
err = errno;
info("Error on accepting request: %s", strerror(err));
@@ -668,6 +817,7 @@ int main(int argc, char *argv[])
{"help", no_argument, 0, 'h'}, //Index 0
{"plugindir", required_argument, 0, 0}, //Index 1
{"socketdir", required_argument, 0, 0}, //Index 2
+ {"confdir", required_argument, 0, 0}, //Index 3
{0, 0, 0, 0}
};

@@ -687,6 +837,9 @@ int main(int argc, char *argv[])
case 2:
socket_dir = optarg;
break;
+ case 3:
+ conf_dir = optarg;
+ break;
}
break;

@@ -725,13 +878,21 @@ int main(int argc, char *argv[])
openlog("lsmd", LOG_ODELAY, LOG_USER);
}

+ /* Check lsmd.conf */
+ char *lsmd_conf_path = path_form(conf_dir, LSMD_CONF_FILE);
+ parse_conf_bool(
+ lsmd_conf_path, LSM_CONF_ALLOW_ROOT_OPT_NAME, &allow_root_plugin);
+ free(lsmd_conf_path);
+
/* Check to see if we want to check plugin for memory errors */
if( getenv("LSM_VALGRIND") ) {
plugin_mem_debug = 1;
}

install_sh();
- drop_privileges();
+ if (allow_root_plugin == 0){
+ drop_privileges();
+ }
flight_check();

/* Become a daemon if told we are not using systemd */
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 17abcee..18d8663 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -1,2 +1,3 @@
notrans_dist_man1_MANS = lsmcli.1 lsmd.1

+notrans_dist_man5_MANS = lsmd.conf.5.in
diff --git a/doc/man/lsmd.conf.5.in b/doc/man/lsmd.conf.5.in
new file mode 100644
index 0000000..e01c329
--- /dev/null
+++ b/doc/man/lsmd.conf.5.in
@@ -0,0 +1,58 @@
+.TH lsmd.conf "5" "January 2015" "lsmd.conf @VERSION@" "libStorageMgmt daemon config"
+.SH NAME
+lsmd.conf - libStorageMgmt daemon lsmd configuration file.
+
+.SH DESCRIPTION
+The libStorageMgmt plugin-in daemon(\fBlsmd\fR) will read \fBlsmd.conf\fR
+file in the folder defined via \fB--confdir\fR(default is \fB/etc/lsm/\fR).
+
+The \fBlsmd\fR also read plugin configuration files from sub-folder
+\fBpluginconf.d\fR. The plugin configuration file should be named as
+<uri_scheme>.conf. For example:
+
+ * Linux plugin(\fBlinux\fR://): \fBlinux.conf\fR
+ * MegaRAID plugin(\fBmegaraid\fR://): \fBmegaraid.conf\fR
+
+The \fBlsmd.conf\fR controls the global settings for \fBlsmd\fR while
+plugin configuration file controls plugin behaviour.
+
+Each option line of configuration file should contain tailing
+semicolon(\fB;\fR).
+
+.SH lsmd.conf OPTIONS
+.TP
+\fBallow-plugin-root-privilege = true;\fR
+
+Indicate whether \fBlsmd\fR daemon should keep running in root mode and invoke
+plugin as root user when needed.
+
+Without this option or set as \fBfalse\fR means never run lsmd and its plugins
+as root user.
+
+Only when all the following requirements meet, will the \fBlsmd\fR run certain
+plugin as root user:
+
+ 1. "allow-plugin-root-privilege = true;" in lsmd.conf
+ 2. "require-root-privilege = true;" in plugin config.
+ 3. API connection(or lsmcli) is ran by root user.
+
+.SH Plugin OPTIONS
+.TP
+\fBrequire-root-privilege = true;\fR
+
+Indicate plugin requires root privilege.
+Without this line or set as \fBfalse\fR, the plugin will never invoked as root
+user by \fBlsmd\fR.
+
+Please check \fBlsmd.conf\fR option \fBallow-plugin-root-privilege\fR for
+detail.
+
+.SH SEE ALSO
+\fIlsmd (1)\fR
+
+.SH BUGS
+Please report bugs to
+<libstoragemgmt\-***@lists.sourceforge.net>
+.SH AUTHOR
+Gris Ge <***@redhat.com>
+
diff --git a/packaging/daemon/libstoragemgmt.service b/packaging/daemon/libstoragemgmt.service
index e0dcf98..81a69ae 100644
--- a/packaging/daemon/libstoragemgmt.service
+++ b/packaging/daemon/libstoragemgmt.service
@@ -6,7 +6,7 @@ After=syslog.target
ExecStart=/usr/bin/lsmd -d
ExecReload=/bin/kill -HUP $MAINPID
StandardError=syslog
-User=libstoragemgmt
+User=root

[Install]
WantedBy=multi-user.target
diff --git a/packaging/libstoragemgmt.spec.in b/packaging/libstoragemgmt.spec.in
index d787e72..a79bff5 100644
--- a/packaging/libstoragemgmt.spec.in
+++ b/packaging/libstoragemgmt.spec.in
@@ -50,6 +50,7 @@ BuildRequires: python-argparse
BuildRequires: glib2-devel
# Explicitly require gcc-c++ is for OBS
BuildRequires: gcc-c++
+BuildRequires: libconfig-devel
%if 0%{?suse_version}
BuildRequires: libyajl-devel
%else
@@ -417,10 +418,13 @@ fi
%doc README COPYING.LIB
%{_mandir}/man1/lsmcli.1*
%{_mandir}/man1/lsmd.1*
+%{_mandir}/man5/lsmd.conf.5*
%{_libdir}/*.so.*
%{_bindir}/lsmcli
%{_bindir}/lsmd
%{_bindir}/simc_lsmplugin
+%{_sysconfdir}/lsm/lsmd.conf
+%dir %{_sysconfdir}/lsm/pluginconf.d

%if 0%{?with_systemd}
%{_unitdir}/libstoragemgmt.service
@@ -463,6 +467,7 @@ fi
%{python_sitelib}/lsm/lsmcli/data_display.*
%{python_sitelib}/lsm/lsmcli/cmdline.*
%{_bindir}/sim_lsmplugin
+%{_sysconfdir}/lsm/pluginconf.d/sim.conf

%files -n %{libstoragemgmt}-smis-plugin
%defattr(-,root,root,-)
--
1.8.3.1
Gris Ge
2015-01-28 14:27:53 UTC
Permalink
* 'sudo' is required to run 'lsmenv lsmd' command for these reason:
1. Support plugin run as root privilege feature.
2. Create libstoragemgmt user and grant it with read and execute access
to current code tree.

* With this change, please link your '.lsm_uri' file to '/root' folder.
So that 'sudo lsmenv mega lsmcli ls' can read out URI settings.
Try this command:

sudo ln -s ~/.lsm_uri /root/

Signed-off-by: Gris Ge <***@redhat.com>
---
tools/lsmenv | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/tools/lsmenv b/tools/lsmenv
index da14eed..568b87f 100755
--- a/tools/lsmenv
+++ b/tools/lsmenv
@@ -20,13 +20,16 @@ use strict;
use Config::IniFiles;
use File::Basename;
use Cwd 'abs_path';
+use Fcntl ':mode';

$|++;

-my $LSM_UDS_PATH = '/tmp/lsm/ipc/';
-my $BASE_DIR; # Used to store the path of libstoragemgmt-code path.
+my $LSM_UDS_PATH = '/var/run/lsm/ipc/';
+my $BASE_DIR = dirname( dirname( abs_path($0) ) );
my $LSM_URI_FILE = "$ENV{HOME}/.lsm_uri";
my $URI_CFG = Config::IniFiles->new( -file => $LSM_URI_FILE );
+my $LSM_USER = 'libstoragemgmt';
+my $LSM_GROUP = 'libstoragemgmt';

my $REF_PRE_BUILD_URI = {
"sim" => "sim://",
@@ -77,8 +80,51 @@ sub is_in_array($$) {
return undef;
}

+sub chk_lsm_user(){
+ my $code_gid = (stat $BASE_DIR)[5];
+ my $code_group_name = getgrgid($code_gid);
+ my $lsm_gid = (getpwnam("libstoragemgmt"))[3];
+ if ($lsm_gid and $lsm_gid != $code_gid){
+ print "FAIL: Existing $LSM_USER account is not sharing gid of " .
+ "curren code tree which might cause plugin permission problem" .
+ "\n";
+ print " Please delete that user $LSM_USER " .
+ "and rerun lsmenv as root user\n";
+ exit 1;
+ }
+ unless($lsm_gid){
+ # LSM_USER not exist.
+ if ($> != 0){
+ print "FAIL: Please run lsmenv as root user to create a proper " .
+ "$LSM_USER account\n";
+ exit 1;
+ }
+ print "WARN: Creating libstoragemgmt account and add it into " .
+ "the group '$code_group_name($code_gid)' of code tree owner. \n";
+
+ system("useradd -r -g $code_gid -d $LSM_UDS_PATH " .
+ "-s /sbin/nologin " .
+ "-c 'daemon account for libstoragemgmt' libstoragemgmt");
+ # Check whether code tree $BASE_DIR is read and executable by LSM_USER
+ # User are on their own if they changed file permission inside git
+ # tree.
+ my $cur_path = "$BASE_DIR/a";
+ while(1){
+ $cur_path = dirname($cur_path);
+ print $cur_path, "\n";
+ my $mod = (stat $cur_path)[2];
+ unless(( $mod & S_IRGRP) and ( $mod & S_IXGRP )){
+ print "WARN: Adding $cur_path with group read and " .
+ "excute permision\n";
+ system("chmod g+rx $cur_path");
+ }
+ last if ($cur_path eq '/');
+ }
+ }
+ return 1;
+}
+
sub lsm_env_setup() {
- $BASE_DIR = dirname( dirname( abs_path($0) ) );
my $py_binding_dir = "$BASE_DIR/python_binding";
my $c_binding_dir = "$BASE_DIR/c_binding";
unless ( ( -e $py_binding_dir ) && ( -e $c_binding_dir ) ) {
@@ -98,6 +144,7 @@ sub lsm_env_setup() {
unless ( -d "$LSM_UDS_PATH" ) {
print "INFO: Creating socket folder $LSM_UDS_PATH\n";
system("mkdir -pv $LSM_UDS_PATH");
+ system("chown $LSM_USER $LSM_UDS_PATH");
}

$ENV{LSM_UDS_PATH} = $LSM_UDS_PATH;
@@ -171,6 +218,10 @@ sub call_out($) {
}

sub start_lsmd() {
+ if ($> != 0){
+ print "FAIL: Please run lsmenv as root to start lsmd\n";
+ exit 1;
+ }
my $lsmd_exec_file = "$BASE_DIR/daemon/lsmd";
unless ( -f $lsmd_exec_file ) {
print "FAIL: $lsmd_exec_file not exist, "
@@ -183,8 +234,9 @@ sub start_lsmd() {
exit 1;
}
my $plugin_dir = "$BASE_DIR/plugin";
+ my $conf_dir = "$BASE_DIR/config";
system( "$lsmd_exec_file --plugindir $plugin_dir "
- . "--socketdir $LSM_UDS_PATH -v -d" );
+ . "--socketdir $LSM_UDS_PATH -v -d --confdir $conf_dir" );
}

sub main() {
@@ -192,6 +244,7 @@ sub main() {
help();
}

+ chk_lsm_user();
lsm_env_setup();
if ( $ARGV[0] eq 'lsmd' ) {
start_lsmd();
--
1.8.3.1
Gris Ge
2015-01-28 14:27:54 UTC
Permalink
* Using storcli(LSI binary tool).

* Currently only got 'systems()' and 'disks()' supported.

* This plugin require root privilege, plugin config file included.

* Autotools files updated.

* RPM SPEC updated with new sub package:
libstoragemgmt-megaraid-plugin (RHEL/Fedora/Centos)
or
libstoragemgmt1-megaraid-plugin (OpenSuSE)
* The URI for this plugin is:
megaraid://
or
megaraid://?storcli=<path_of_storcli>

* If SELinux enabled, please check SELinux logs to grant permission for this.
For exmaple:
grep storcli64 /var/log/audit/audit.log | audit2allow -M mypol
semodule -i mypol.pp

* Added new manpage: megaraid_lsmplugin(1).

* Tested on RHEL6 and RHEL7 for SAS and SATA disks.

Signed-off-by: Gris Ge <***@redhat.com>
---
config/Makefile.am | 7 +-
config/pluginconf.d/megaraid.conf | 1 +
configure.ac | 2 +
doc/man/Makefile.am | 2 +-
doc/man/megaraid_lsmplugin.1.in | 50 +++++++
packaging/libstoragemgmt.spec.in | 27 ++++
plugin/Makefile.am | 2 +-
plugin/megaraid/Makefile.am | 6 +
plugin/megaraid/__init__.py | 1 +
plugin/megaraid/megaraid.py | 292 +++++++++++++++++++++++++++++++++++++
plugin/megaraid/megaraid_lsmplugin | 37 +++++
plugin/megaraid/utils.py | 47 ++++++
12 files changed, 470 insertions(+), 4 deletions(-)
create mode 100644 config/pluginconf.d/megaraid.conf
create mode 100644 doc/man/megaraid_lsmplugin.1.in
create mode 100644 plugin/megaraid/Makefile.am
create mode 100644 plugin/megaraid/__init__.py
create mode 100644 plugin/megaraid/megaraid.py
create mode 100755 plugin/megaraid/megaraid_lsmplugin
create mode 100644 plugin/megaraid/utils.py

diff --git a/config/Makefile.am b/config/Makefile.am
index 71291ad..ff0481b 100644
--- a/config/Makefile.am
+++ b/config/Makefile.am
@@ -1,8 +1,11 @@
lsmconfdir=$(sysconfdir)/lsm
lsmconf_DATA=lsmd.conf

-EXTRA_DIST= lsmd.conf pluginconf.d/sim.conf
+EXTRA_DIST= lsmd.conf pluginconf.d/sim.conf pluginconf.d/megaraid.conf

pluginconfdir=$(sysconfdir)/lsm/pluginconf.d

-pluginconf_DATA=pluginconf.d/sim.conf
+pluginconf_DATA= \
+ pluginconf.d/sim.conf \
+ pluginconf.d/megaraid.conf
+
diff --git a/config/pluginconf.d/megaraid.conf b/config/pluginconf.d/megaraid.conf
new file mode 100644
index 0000000..35b52d4
--- /dev/null
+++ b/config/pluginconf.d/megaraid.conf
@@ -0,0 +1 @@
+require-root-privilege = true;
diff --git a/configure.ac b/configure.ac
index eeaa350..9ea7b79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -208,6 +208,7 @@ AC_OUTPUT(libstoragemgmt.pc \
python_binding/lsm/version.py \
plugin/Makefile \
plugin/simc/Makefile \
+ plugin/megaraid/Makefile \
daemon/Makefile \
config/Makefile \
doc/Makefile \
@@ -215,6 +216,7 @@ AC_OUTPUT(libstoragemgmt.pc \
doc/man/lsmd.1 \
doc/doxygen.conf \
doc/man/lsmd.conf.5 \
+ doc/man/megaraid_lsmplugin.1 \
tools/Makefile \
tools/udev/Makefile \
tools/lsmcli/Makefile \
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 18d8663..92d1398 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -1,3 +1,3 @@
-notrans_dist_man1_MANS = lsmcli.1 lsmd.1
+notrans_dist_man1_MANS = lsmcli.1 lsmd.1 megaraid_lsmplugin.1

notrans_dist_man5_MANS = lsmd.conf.5.in
diff --git a/doc/man/megaraid_lsmplugin.1.in b/doc/man/megaraid_lsmplugin.1.in
new file mode 100644
index 0000000..ae28391
--- /dev/null
+++ b/doc/man/megaraid_lsmplugin.1.in
@@ -0,0 +1,50 @@
+.TH megaraid_lsmplugin "1" "Januray 2015" "megaraid_lsmplugin @VERSION@" "libStorageMgmt"
+.SH NAME
+megaraid_lsmplugin -- LibstorageMgmt MegaRAID plugin
+
+.SH DESCRIPTION
+LibstorageMgmt megaraid plugin allows user to manage LSI MegaRAID via vendor
+tool \fBstorcli\fR[1].
+The 'megaraid_lsmplugin' executable file is for libStorageMgmt
+daemon to execute when client user specifies megaraid plugin in the URI.
+
+.SH URI
+To use this plugin, users should set their URI to this format:
+.nf
+
+ \fBmegaraid://\fR
+ or
+ \fBmegaraid://?storcli=<path_of_storcli>\fR
+
+.fi
+
+.TP storcli
+The 'storcli' URI paramter is used to specified the path of storcli tool.
+By default, this plugin will try the paths used in storcli rpm:
+\fB/opt/MegaRAID/storcli/storcli64\fR and \fB/opt/MegaRAID/storcli/storcli\fR.
+
+.SH ROOT PRIVILEGE
+This plugin requires both \fBlsmd\fR daemon and API client running as root
+user. Please check manpage \fIlsmd.conf (5)\fR for detail.
+
+.SH SUPPORTED HARDWARES
+Please refer to LSI website for hardware support status of storcli.
+Detailed support status can be queried via:
+
+ * \fBlsm.Client.capabilities()\fR (Python API)
+ * \fBlsm_capabilities()\fR (C API)
+ * \fBlsmcli capabilities\fR (lsmcli command line).
+
+.SH FIREWALL RULES
+This plugin only execute \fBstorcli\fR on localhost. No network connection
+required.
+
+.SH SEE ALSO
+\fIlsmcli\fR(1), \fIlsmd\fR(1), [1] http://www.lsi.com
+
+.SH BUGS
+Please report bugs to
+\fI<libstoragemgmt-***@lists.sourceforge.net>\fR
+
+.SH AUTHOR
+Gris Ge \fI<***@redhat.com>\fR
diff --git a/packaging/libstoragemgmt.spec.in b/packaging/libstoragemgmt.spec.in
index a79bff5..17add9f 100644
--- a/packaging/libstoragemgmt.spec.in
+++ b/packaging/libstoragemgmt.spec.in
@@ -207,6 +207,16 @@ the %{libstoragemgmt}-rest package contains the http daemon for
%{libstoragemgmt} rest api.
%endif

+%package -n %{libstoragemgmt}-megaraid-plugin
+Summary: Files for LSI MegaRAID support for %{libstoragemgmt}
+Group: System Environment/Libraries
+Requires: %{libstoragemgmt}%{?_isa} = %{version}-%{release}
+
+%description -n %{libstoragemgmt}-megaraid-plugin
+The %{libstoragemgmt}-megaraid-plugin package contains the plugin for LSI
+MegaRAID storage management via storcli.
+
+
%prep
%setup -q

@@ -413,6 +423,13 @@ if [ $1 -eq 0 ]; then
fi

+%postun -n %{libstoragemgmt}-megaraid-plugin
+if [ $1 -eq 0 ]; then
+ # Remove
+ /usr/bin/systemctl try-restart libstoragemgmt.service \
+ >/dev/null 2>&1 || :
+fi
+
%files -n %{libstoragemgmt}
%defattr(-,root,root,-)
%doc README COPYING.LIB
@@ -507,6 +524,16 @@ fi
%{python_sitelib}/lsm/plugin/nstor/nstor.*
%{_bindir}/nstor_lsmplugin

+%files -n %{libstoragemgmt}-megaraid-plugin
+%defattr(-,root,root,-)
+%dir %{python_sitelib}/lsm/plugin/megaraid
+%{python_sitelib}/lsm/plugin/megaraid/__init__.*
+%{python_sitelib}/lsm/plugin/megaraid/megaraid.*
+%{python_sitelib}/lsm/plugin/megaraid/utils.*
+%{_bindir}/megaraid_lsmplugin
+%{_sysconfdir}/lsm/pluginconf.d/megaraid.conf
+%{_mandir}/man1/megaraid_lsmplugin.1*
+
%files udev
%defattr(-,root,root,-)
%{udev_dir}/udev/scan-scsi-target
diff --git a/plugin/Makefile.am b/plugin/Makefile.am
index 78ceb36..f9146a8 100644
--- a/plugin/Makefile.am
+++ b/plugin/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS=simc
+SUBDIRS=simc megaraid

plugindir = $(pythondir)/lsm/plugin

diff --git a/plugin/megaraid/Makefile.am b/plugin/megaraid/Makefile.am
new file mode 100644
index 0000000..680cb83
--- /dev/null
+++ b/plugin/megaraid/Makefile.am
@@ -0,0 +1,6 @@
+plugindir = $(pythondir)/lsm/plugin
+megaraiddir = $(plugindir)/megaraid
+
+megaraid_PYTHON = __init__.py megaraid.py utils.py
+
+dist_bin_SCRIPTS= megaraid_lsmplugin
diff --git a/plugin/megaraid/__init__.py b/plugin/megaraid/__init__.py
new file mode 100644
index 0000000..8f4602c
--- /dev/null
+++ b/plugin/megaraid/__init__.py
@@ -0,0 +1 @@
+from lsm.plugin.megaraid.megaraid import MegaRAID
diff --git a/plugin/megaraid/megaraid.py b/plugin/megaraid/megaraid.py
new file mode 100644
index 0000000..8e61775
--- /dev/null
+++ b/plugin/megaraid/megaraid.py
@@ -0,0 +1,292 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <***@redhat.com>
+
+import os
+import json
+from string import strip
+import re
+import errno
+
+from lsm import (uri_parse, search_property, size_human_2_size_bytes,
+ Capabilities, LsmError, ErrorNumber, System, Client,
+ Disk, VERSION, search_property)
+
+from lsm.plugin.megaraid.utils import cmd_exec, ExecError
+
+_FLAG_RSVD = Client.FLAG_RSVD
+
+
+# Naming scheme
+# mega_sys_path /c0
+# mega_disk_path /c0/e64/s0
+
+
+def _handle_errors(method):
+ def _wrapper(*args, **kwargs):
+ try:
+ return method(*args, **kwargs)
+ except LsmError:
+ raise
+ except KeyError as key_error:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Expected key missing from MegaRAID storcli output:%s" %
+ key_error)
+ except ExecError as exec_error:
+ raise LsmError(ErrorNumber.PLUGIN_BUG, str(exec_error))
+ except Exception as common_error:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Got unexpected error %s" % common_error)
+
+ return _wrapper
+
+
+def _blk_count_of(mega_disk_size):
+ blk_count_regex = re.compile("(0x[0-9a-f]+) Sectors")
+ blk_count_search = blk_count_regex.search(mega_disk_size)
+ if blk_count_search:
+ return int(blk_count_search.group(1), 16)
+ return Disk.BLOCK_COUNT_NOT_FOUND
+
+
+def _disk_type_of(disk_show_basic_dict):
+ """
+ Return the 'Drive /c0/e64/s0' entry of '/c0/e64/s0 show all'
+ """
+ disk_media = disk_show_basic_dict['Med']
+ disk_interface = disk_show_basic_dict['Intf']
+ if disk_media == 'HDD':
+ if disk_interface == 'SATA':
+ return Disk.TYPE_SATA
+ elif disk_interface == 'SAS':
+ return Disk.TYPE_SAS
+ else:
+ return Disk.TYPE_HDD
+ # TODO(Gris Ge): No document found. Just need more test on real hardware.
+ elif disk_media == 'SSD':
+ return Disk.TYPE_SSD
+
+ return Disk.TYPE_UNKNOWN
+
+_DISK_STATE_MAP = {
+ 'Onln': Disk.STATUS_OK,
+ 'Offln': Disk.STATUS_ERROR,
+ 'GHS': Disk.STATUS_SPARE_DISK | Disk.STATUS_OK,
+ 'DHS': Disk.STATUS_SPARE_DISK | Disk.STATUS_OK,
+ 'UGood': Disk.STATUS_STOPPED | Disk.STATUS_OK,
+ 'UBad': Disk.STATUS_STOPPED | Disk.STATUS_ERROR,
+}
+
+
+def _disk_status_of(disk_show_basic_dict, disk_show_stat_dict):
+ if disk_show_stat_dict['Media Error Count'] or \
+ disk_show_stat_dict['Other Error Count'] or \
+ disk_show_stat_dict['S.M.A.R.T alert flagged by drive'] != 'No':
+ return Disk.STATUS_ERROR
+
+ if disk_show_stat_dict['Predictive Failure Count']:
+ return Disk.STATUS_PREDICTIVE_FAILURE
+
+ if disk_show_basic_dict['Sp'] == 'D':
+ return Disk.STATUS_STOPPED
+
+ if disk_show_basic_dict['Sp'] == 'F':
+ return Disk.STATUS_OTHER
+
+ return _DISK_STATE_MAP.get(
+ disk_show_basic_dict['State'], Disk.STATUS_UNKNOWN)
+
+
+class MegaRAID(object):
+ _DEFAULT_MDADM_BIN_PATHS = [
+ "/opt/MegaRAID/storcli/storcli64", "/opt/MegaRAID/storcli/storcli"]
+ _CMD_JSON_OUTPUT_SWITCH = 'J'
+
+ def __init__(self):
+ self._storcli_bin = None
+
+ def _find_storcli(self):
+ """
+ Try _DEFAULT_MDADM_BIN_PATHS
+ """
+ for cur_path in MegaRAID._DEFAULT_MDADM_BIN_PATHS:
+ if os.path.lexists(cur_path):
+ self._storcli_bin = cur_path
+
+ if not self._storcli_bin:
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "MegaRAID storcli is not installed correctly")
+
+ @_handle_errors
+ def plugin_register(self, uri, password, timeout, flags=Client.FLAG_RSVD):
+ uri_parsed = uri_parse(uri)
+ self._storcli_bin = uri_parsed.get('parameters', {}).get('storcli')
+ if not self._storcli_bin:
+ self._find_storcli()
+
+ # change working dir to "/tmp" as storcli will create a log file
+ # named as 'MegaSAS.log'.
+ os.chdir("/tmp")
+ self._storcli_exec(['-v'], flag_json=False)
+
+ def plugin_unregister(self, flags=0):
+ pass
+
+ def plugin_info(self, flags=Client.FLAG_RSVD):
+ return "LSI MegaRAID Plugin", VERSION
+
+ def capabilities(self, system, flags=_FLAG_RSVD):
+ cur_lsm_syss = self.systems()
+ if system.id not in list(s.id for s in cur_lsm_syss):
+ raise LsmError(
+ ErrorNumber.NOT_FOUND_SYSTEM,
+ "System not found")
+ cap = Capabilities()
+ cap.set(Capabilities.DISKS)
+ return cap
+
+ def _storcli_exec(self, storcli_cmds, flag_json=True):
+ storcli_cmds.insert(0, self._storcli_bin)
+ if flag_json:
+ storcli_cmds.append(MegaRAID._CMD_JSON_OUTPUT_SWITCH)
+ try:
+ output = cmd_exec(storcli_cmds)
+ except OSError as os_error:
+ if os_error.errno == errno.ENOENT:
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "storcli binary '%s' is not exist or excutable." %
+ self._storcli_bin)
+ else:
+ raise
+
+ if flag_json:
+ output_dict = json.loads(output)
+ ctrl_output = output_dict.get('Controllers')
+ if len(ctrl_output) != 1:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "_storcli_exec(): Unexpected output from MegaRAID "
+ "storcli: %s" % output_dict)
+
+ rc_status = ctrl_output[0].get('Command Status')
+ if rc_status.get('Status') != 'Success':
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "MegaRAID storcli failed with error %d: %s" %
+ (rc_status['Status Code'], rc_status['Description']))
+ return ctrl_output[0].get('Response Data')
+ else:
+ return output
+
+ def _ctrl_count(self):
+ return self._storcli_exec(
+ ["show", "ctrlcount"]).get("Controller Count")
+
+ def _lsm_status_of_ctrl(self, ctrl_num):
+ ctrl_health_output = self._storcli_exec(
+ ["/c%d" % ctrl_num, 'show', 'health', 'all'])
+ health_info = ctrl_health_output['Controller Health Info']
+ # TODO(Gris Ge): Try pull a disk off to check whether this change.
+ if health_info['Overall Health'] == 'GOOD':
+ return System.STATUS_OK, ''
+
+ return System.STATUS_UNKNOWN, "%s reason code %s" % (
+ health_info['Overall Health'],
+ health_info['Reason Code'])
+
+ def _sys_id_of_ctrl_num(self, ctrl_num, ctrl_show_output=None):
+ if ctrl_show_output is None:
+ ctrl_show_output = self._storcli_exec(["/c%d" % ctrl_num, "show"])
+ sys_id = ctrl_show_output['Serial Number']
+ if not sys_id:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "_sys_id_of_ctrl_num(): Fail to get system id: %s" %
+ ctrl_show_output.items())
+ else:
+ return sys_id
+
+ @_handle_errors
+ def systems(self, flags=0):
+ rc_lsm_syss = []
+ for ctrl_num in range(self._ctrl_count()):
+ ctrl_show_output = self._storcli_exec(["/c%d" % ctrl_num, "show"])
+ sys_id = self._sys_id_of_ctrl_num(ctrl_num, ctrl_show_output)
+ sys_name = "%s %s %s:%s:%s" % (
+ ctrl_show_output['Product Name'],
+ ctrl_show_output['Host Interface'],
+ format(ctrl_show_output['Bus Number'], '02x'),
+ format(ctrl_show_output['Device Number'], '02x'),
+ format(ctrl_show_output['Function Number'], '02x'))
+ (status, status_info) = self._lsm_status_of_ctrl(ctrl_num)
+ plugin_data = "/c%d"
+ # Since PCI slot sequence might change.
+ # This string just stored for quick system verification.
+
+ rc_lsm_syss.append(
+ System(sys_id, sys_name, status, status_info, plugin_data))
+
+ return rc_lsm_syss
+
+ @_handle_errors
+ def disks(self, search_key=None, search_value=None, flags=_FLAG_RSVD):
+ rc_lsm_disks = []
+ mega_disk_path_regex = re.compile(
+ r"^Drive (\/c[0-9]+\/e[0-9]+\/s[0-9]+) - Detailed Information$")
+
+ for ctrl_num in range(self._ctrl_count()):
+ sys_id = self._sys_id_of_ctrl_num(ctrl_num)
+
+ disk_show_output = self._storcli_exec(
+ ["/c%d/eall/sall" % ctrl_num, "show", "all"])
+ for drive_name in disk_show_output.keys():
+ re_match = mega_disk_path_regex.match(drive_name)
+ if not re_match:
+ continue
+
+ mega_disk_path = re_match.group(1)
+ # Assuming only 1 disk attached to each slot.
+ disk_show_basic_dict = disk_show_output[
+ "Drive %s" % mega_disk_path][0]
+ disk_show_attr_dict = disk_show_output[drive_name][
+ 'Drive %s Device attributes' % mega_disk_path]
+ disk_show_stat_dict = disk_show_output[drive_name][
+ 'Drive %s State' % mega_disk_path]
+
+ disk_id = disk_show_attr_dict['SN'].strip()
+ disk_name = "%s %s %s" % (
+ mega_disk_path,
+ disk_show_attr_dict['Manufacturer Id'].strip(),
+ disk_show_attr_dict['Model Number'])
+ disk_type = _disk_type_of(disk_show_basic_dict)
+ blk_size = size_human_2_size_bytes(
+ disk_show_basic_dict['SeSz'])
+ blk_count = _blk_count_of(disk_show_attr_dict['Coerced size'])
+ status = _disk_status_of(
+ disk_show_basic_dict, disk_show_stat_dict)
+
+ plugin_data = mega_disk_path
+
+ rc_lsm_disks.append(
+ Disk(
+ disk_id, disk_name, disk_type, blk_size, blk_count,
+ status, sys_id, plugin_data))
+
+ return search_property(rc_lsm_disks, search_key, search_value)
diff --git a/plugin/megaraid/megaraid_lsmplugin b/plugin/megaraid/megaraid_lsmplugin
new file mode 100755
index 0000000..5aaa0e8
--- /dev/null
+++ b/plugin/megaraid/megaraid_lsmplugin
@@ -0,0 +1,37 @@
+#!/usr/bin/env python2
+
+# Copyright (C) 2011-2013 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: tasleson
+
+import sys
+import syslog
+import traceback
+
+try:
+ from lsm import PluginRunner
+ from lsm.plugin.megaraid import MegaRAID
+
+ if __name__ == '__main__':
+ PluginRunner(MegaRAID, sys.argv).run()
+except Exception:
+ #This should be quite rare, but when it does happen this is pretty
+ #key in understanding what happened, especially when it happens when
+ #running from the daemon.
+ msg = str(traceback.format_exc())
+ syslog.syslog(syslog.LOG_ERR, msg)
+ sys.stderr.write(msg)
+ sys.exit(1)
diff --git a/plugin/megaraid/utils.py b/plugin/megaraid/utils.py
new file mode 100644
index 0000000..4290b39
--- /dev/null
+++ b/plugin/megaraid/utils.py
@@ -0,0 +1,47 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <***@redhat.com>
+
+import subprocess
+import os
+
+def cmd_exec(cmds):
+ """
+ Execute provided command and return the STDOUT as string.
+ Raise ExecError if command return code is not zero
+ """
+ cmd_popen = subprocess.Popen(
+ cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env={"PATH": os.getenv("PATH")})
+ str_stdout = "".join(list(cmd_popen.stdout)).strip()
+ str_stderr = "".join(list(cmd_popen.stderr)).strip()
+ errno = cmd_popen.wait()
+ if errno != 0:
+ raise ExecError(" ".join(cmds), errno, str_stdout, str_stderr)
+ return str_stdout
+
+
+class ExecError(Exception):
+ def __init__(self, cmd, errno, stdout, stderr, *args, **kwargs):
+ Exception.__init__(self, *args, **kwargs)
+ self.cmd = cmd
+ self.errno = errno
+ self.stdout = stdout
+ self.stderr = stderr
+
+ def __str__(self):
+ return "cmd: '%s', errno: %d, stdout: '%s', stderr: '%s'" % \
+ (self.cmd, self.errno, self.stdout, self.stderr)
--
1.8.3.1
Tony Asleson
2015-02-05 09:36:07 UTC
Permalink
Comment below

Thanks,
Tony
Post by Gris Ge
* Using storcli(LSI binary tool).
* Currently only got 'systems()' and 'disks()' supported.
* This plugin require root privilege, plugin config file included.
* Autotools files updated.
libstoragemgmt-megaraid-plugin (RHEL/Fedora/Centos)
or
libstoragemgmt1-megaraid-plugin (OpenSuSE)
megaraid://
or
megaraid://?storcli=<path_of_storcli>
* If SELinux enabled, please check SELinux logs to grant permission for this.
grep storcli64 /var/log/audit/audit.log | audit2allow -M mypol
semodule -i mypol.pp
* Added new manpage: megaraid_lsmplugin(1).
* Tested on RHEL6 and RHEL7 for SAS and SATA disks.
---
config/Makefile.am | 7 +-
config/pluginconf.d/megaraid.conf | 1 +
configure.ac | 2 +
doc/man/Makefile.am | 2 +-
doc/man/megaraid_lsmplugin.1.in | 50 +++++++
packaging/libstoragemgmt.spec.in | 27 ++++
plugin/Makefile.am | 2 +-
plugin/megaraid/Makefile.am | 6 +
plugin/megaraid/__init__.py | 1 +
plugin/megaraid/megaraid.py | 292 +++++++++++++++++++++++++++++++++++++
plugin/megaraid/megaraid_lsmplugin | 37 +++++
plugin/megaraid/utils.py | 47 ++++++
12 files changed, 470 insertions(+), 4 deletions(-)
create mode 100644 config/pluginconf.d/megaraid.conf
create mode 100644 doc/man/megaraid_lsmplugin.1.in
create mode 100644 plugin/megaraid/Makefile.am
create mode 100644 plugin/megaraid/__init__.py
create mode 100644 plugin/megaraid/megaraid.py
create mode 100755 plugin/megaraid/megaraid_lsmplugin
create mode 100644 plugin/megaraid/utils.py
diff --git a/config/Makefile.am b/config/Makefile.am
index 71291ad..ff0481b 100644
--- a/config/Makefile.am
+++ b/config/Makefile.am
@@ -1,8 +1,11 @@
lsmconfdir=$(sysconfdir)/lsm
lsmconf_DATA=lsmd.conf
-EXTRA_DIST= lsmd.conf pluginconf.d/sim.conf
+EXTRA_DIST= lsmd.conf pluginconf.d/sim.conf pluginconf.d/megaraid.conf
pluginconfdir=$(sysconfdir)/lsm/pluginconf.d
-pluginconf_DATA=pluginconf.d/sim.conf
+pluginconf_DATA= \
+ pluginconf.d/sim.conf \
+ pluginconf.d/megaraid.conf
+
diff --git a/config/pluginconf.d/megaraid.conf b/config/pluginconf.d/megaraid.conf
new file mode 100644
index 0000000..35b52d4
--- /dev/null
+++ b/config/pluginconf.d/megaraid.conf
@@ -0,0 +1 @@
+require-root-privilege = true;
diff --git a/configure.ac b/configure.ac
index eeaa350..9ea7b79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -208,6 +208,7 @@ AC_OUTPUT(libstoragemgmt.pc \
python_binding/lsm/version.py \
plugin/Makefile \
plugin/simc/Makefile \
+ plugin/megaraid/Makefile \
daemon/Makefile \
config/Makefile \
doc/Makefile \
@@ -215,6 +216,7 @@ AC_OUTPUT(libstoragemgmt.pc \
doc/man/lsmd.1 \
doc/doxygen.conf \
doc/man/lsmd.conf.5 \
+ doc/man/megaraid_lsmplugin.1 \
tools/Makefile \
tools/udev/Makefile \
tools/lsmcli/Makefile \
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 18d8663..92d1398 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -1,3 +1,3 @@
-notrans_dist_man1_MANS = lsmcli.1 lsmd.1
+notrans_dist_man1_MANS = lsmcli.1 lsmd.1 megaraid_lsmplugin.1
notrans_dist_man5_MANS = lsmd.conf.5.in
diff --git a/doc/man/megaraid_lsmplugin.1.in b/doc/man/megaraid_lsmplugin.1.in
new file mode 100644
index 0000000..ae28391
--- /dev/null
+++ b/doc/man/megaraid_lsmplugin.1.in
@@ -0,0 +1,50 @@
+.SH NAME
+megaraid_lsmplugin -- LibstorageMgmt MegaRAID plugin
+
+.SH DESCRIPTION
+LibstorageMgmt megaraid plugin allows user to manage LSI MegaRAID via vendor
+tool \fBstorcli\fR[1].
+The 'megaraid_lsmplugin' executable file is for libStorageMgmt
+daemon to execute when client user specifies megaraid plugin in the URI.
+
+.SH URI
+.nf
+
+ \fBmegaraid://\fR
+ or
+ \fBmegaraid://?storcli=<path_of_storcli>\fR
+
+.fi
+
+.TP storcli
+The 'storcli' URI paramter is used to specified the path of storcli tool.
+\fB/opt/MegaRAID/storcli/storcli64\fR and \fB/opt/MegaRAID/storcli/storcli\fR.
+
+.SH ROOT PRIVILEGE
+This plugin requires both \fBlsmd\fR daemon and API client running as root
+user. Please check manpage \fIlsmd.conf (5)\fR for detail.
+
+.SH SUPPORTED HARDWARES
+Please refer to LSI website for hardware support status of storcli.
+
+ * \fBlsm.Client.capabilities()\fR (Python API)
+ * \fBlsm_capabilities()\fR (C API)
+ * \fBlsmcli capabilities\fR (lsmcli command line).
+
+.SH FIREWALL RULES
+This plugin only execute \fBstorcli\fR on localhost. No network connection
+required.
+
+.SH SEE ALSO
+\fIlsmcli\fR(1), \fIlsmd\fR(1), [1] http://www.lsi.com
+
+.SH BUGS
+Please report bugs to
+
+.SH AUTHOR
diff --git a/packaging/libstoragemgmt.spec.in b/packaging/libstoragemgmt.spec.in
index a79bff5..17add9f 100644
--- a/packaging/libstoragemgmt.spec.in
+++ b/packaging/libstoragemgmt.spec.in
@@ -207,6 +207,16 @@ the %{libstoragemgmt}-rest package contains the http daemon for
%{libstoragemgmt} rest api.
%endif
+%package -n %{libstoragemgmt}-megaraid-plugin
+Summary: Files for LSI MegaRAID support for %{libstoragemgmt}
+Group: System Environment/Libraries
+Requires: %{libstoragemgmt}%{?_isa} = %{version}-%{release}
+
+%description -n %{libstoragemgmt}-megaraid-plugin
+The %{libstoragemgmt}-megaraid-plugin package contains the plugin for LSI
+MegaRAID storage management via storcli.
+
+
%prep
%setup -q
@@ -413,6 +423,13 @@ if [ $1 -eq 0 ]; then
fi
+%postun -n %{libstoragemgmt}-megaraid-plugin
+if [ $1 -eq 0 ]; then
+ # Remove
+ /usr/bin/systemctl try-restart libstoragemgmt.service \
+fi
+
%files -n %{libstoragemgmt}
%defattr(-,root,root,-)
%doc README COPYING.LIB
@@ -507,6 +524,16 @@ fi
%{python_sitelib}/lsm/plugin/nstor/nstor.*
%{_bindir}/nstor_lsmplugin
+%files -n %{libstoragemgmt}-megaraid-plugin
+%defattr(-,root,root,-)
+%dir %{python_sitelib}/lsm/plugin/megaraid
+%{python_sitelib}/lsm/plugin/megaraid/__init__.*
+%{python_sitelib}/lsm/plugin/megaraid/megaraid.*
+%{python_sitelib}/lsm/plugin/megaraid/utils.*
+%{_bindir}/megaraid_lsmplugin
+%{_sysconfdir}/lsm/pluginconf.d/megaraid.conf
+%{_mandir}/man1/megaraid_lsmplugin.1*
+
%files udev
%defattr(-,root,root,-)
%{udev_dir}/udev/scan-scsi-target
diff --git a/plugin/Makefile.am b/plugin/Makefile.am
index 78ceb36..f9146a8 100644
--- a/plugin/Makefile.am
+++ b/plugin/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS=simc
+SUBDIRS=simc megaraid
plugindir = $(pythondir)/lsm/plugin
diff --git a/plugin/megaraid/Makefile.am b/plugin/megaraid/Makefile.am
new file mode 100644
index 0000000..680cb83
--- /dev/null
+++ b/plugin/megaraid/Makefile.am
@@ -0,0 +1,6 @@
+plugindir = $(pythondir)/lsm/plugin
+megaraiddir = $(plugindir)/megaraid
+
+megaraid_PYTHON = __init__.py megaraid.py utils.py
+
+dist_bin_SCRIPTS= megaraid_lsmplugin
diff --git a/plugin/megaraid/__init__.py b/plugin/megaraid/__init__.py
new file mode 100644
index 0000000..8f4602c
--- /dev/null
+++ b/plugin/megaraid/__init__.py
@@ -0,0 +1 @@
+from lsm.plugin.megaraid.megaraid import MegaRAID
diff --git a/plugin/megaraid/megaraid.py b/plugin/megaraid/megaraid.py
new file mode 100644
index 0000000..8e61775
--- /dev/null
+++ b/plugin/megaraid/megaraid.py
@@ -0,0 +1,292 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+import os
+import json
+from string import strip
+import re
+import errno
+
+from lsm import (uri_parse, search_property, size_human_2_size_bytes,
+ Capabilities, LsmError, ErrorNumber, System, Client,
+ Disk, VERSION, search_property)
+
+from lsm.plugin.megaraid.utils import cmd_exec, ExecError
+
+_FLAG_RSVD = Client.FLAG_RSVD
+
+
+# Naming scheme
+# mega_sys_path /c0
+# mega_disk_path /c0/e64/s0
+
+
+ return method(*args, **kwargs)
+ raise
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Expected key missing from MegaRAID storcli output:%s" %
+ key_error)
+ raise LsmError(ErrorNumber.PLUGIN_BUG, str(exec_error))
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Got unexpected error %s" % common_error)
+
+ return _wrapper
+
+
+ blk_count_regex = re.compile("(0x[0-9a-f]+) Sectors")
+ blk_count_search = blk_count_regex.search(mega_disk_size)
+ return int(blk_count_search.group(1), 16)
+ return Disk.BLOCK_COUNT_NOT_FOUND
+
+
+ """
+ Return the 'Drive /c0/e64/s0' entry of '/c0/e64/s0 show all'
+ """
+ disk_media = disk_show_basic_dict['Med']
+ disk_interface = disk_show_basic_dict['Intf']
+ return Disk.TYPE_SATA
+ return Disk.TYPE_SAS
+ return Disk.TYPE_HDD
+ # TODO(Gris Ge): No document found. Just need more test on real hardware.
+ return Disk.TYPE_SSD
+
+ return Disk.TYPE_UNKNOWN
+
+_DISK_STATE_MAP = {
+ 'Onln': Disk.STATUS_OK,
+ 'Offln': Disk.STATUS_ERROR,
+ 'GHS': Disk.STATUS_SPARE_DISK | Disk.STATUS_OK,
+ 'DHS': Disk.STATUS_SPARE_DISK | Disk.STATUS_OK,
+ 'UGood': Disk.STATUS_STOPPED | Disk.STATUS_OK,
+ 'UBad': Disk.STATUS_STOPPED | Disk.STATUS_ERROR,
+}
+
+
+ if disk_show_stat_dict['Media Error Count'] or \
+ disk_show_stat_dict['Other Error Count'] or \
+ return Disk.STATUS_ERROR
+
+ return Disk.STATUS_PREDICTIVE_FAILURE
+
+ return Disk.STATUS_STOPPED
+
+ return Disk.STATUS_OTHER
+
+ return _DISK_STATE_MAP.get(
+ disk_show_basic_dict['State'], Disk.STATUS_UNKNOWN)
+
+
This class should be derived from the appropriate existing defined
interface(s) or a newly defined one for local hba.
Post by Gris Ge
+ _DEFAULT_MDADM_BIN_PATHS = [
+ "/opt/MegaRAID/storcli/storcli64", "/opt/MegaRAID/storcli/storcli"]
+ _CMD_JSON_OUTPUT_SWITCH = 'J'
+
+ self._storcli_bin = None
+
+ """
+ Try _DEFAULT_MDADM_BIN_PATHS
+ """
+ self._storcli_bin = cur_path
+
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "MegaRAID storcli is not installed correctly")
+
+ uri_parsed = uri_parse(uri)
+ self._storcli_bin = uri_parsed.get('parameters', {}).get('storcli')
+ self._find_storcli()
+
+ # change working dir to "/tmp" as storcli will create a log file
+ # named as 'MegaSAS.log'.
+ os.chdir("/tmp")
+ self._storcli_exec(['-v'], flag_json=False)
+
+ pass
+
+ return "LSI MegaRAID Plugin", VERSION
+
+ cur_lsm_syss = self.systems()
+ raise LsmError(
+ ErrorNumber.NOT_FOUND_SYSTEM,
+ "System not found")
+ cap = Capabilities()
+ cap.set(Capabilities.DISKS)
+ return cap
+
+ storcli_cmds.insert(0, self._storcli_bin)
+ storcli_cmds.append(MegaRAID._CMD_JSON_OUTPUT_SWITCH)
+ output = cmd_exec(storcli_cmds)
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "storcli binary '%s' is not exist or excutable." %
+ self._storcli_bin)
+ raise
+
+ output_dict = json.loads(output)
+ ctrl_output = output_dict.get('Controllers')
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "_storcli_exec(): Unexpected output from MegaRAID "
+ "storcli: %s" % output_dict)
+
+ rc_status = ctrl_output[0].get('Command Status')
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "MegaRAID storcli failed with error %d: %s" %
+ (rc_status['Status Code'], rc_status['Description']))
+ return ctrl_output[0].get('Response Data')
+ return output
+
+ return self._storcli_exec(
+ ["show", "ctrlcount"]).get("Controller Count")
+
+ ctrl_health_output = self._storcli_exec(
+ ["/c%d" % ctrl_num, 'show', 'health', 'all'])
+ health_info = ctrl_health_output['Controller Health Info']
+ # TODO(Gris Ge): Try pull a disk off to check whether this change.
+ return System.STATUS_OK, ''
+
+ return System.STATUS_UNKNOWN, "%s reason code %s" % (
+ health_info['Overall Health'],
+ health_info['Reason Code'])
+
+ ctrl_show_output = self._storcli_exec(["/c%d" % ctrl_num, "show"])
+ sys_id = ctrl_show_output['Serial Number']
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "_sys_id_of_ctrl_num(): Fail to get system id: %s" %
+ ctrl_show_output.items())
+ return sys_id
+
+ rc_lsm_syss = []
+ ctrl_show_output = self._storcli_exec(["/c%d" % ctrl_num, "show"])
+ sys_id = self._sys_id_of_ctrl_num(ctrl_num, ctrl_show_output)
+ sys_name = "%s %s %s:%s:%s" % (
+ ctrl_show_output['Product Name'],
+ ctrl_show_output['Host Interface'],
+ format(ctrl_show_output['Bus Number'], '02x'),
+ format(ctrl_show_output['Device Number'], '02x'),
+ format(ctrl_show_output['Function Number'], '02x'))
+ (status, status_info) = self._lsm_status_of_ctrl(ctrl_num)
+ plugin_data = "/c%d"
+ # Since PCI slot sequence might change.
+ # This string just stored for quick system verification.
+
+ rc_lsm_syss.append(
+ System(sys_id, sys_name, status, status_info, plugin_data))
+
+ return rc_lsm_syss
+
+ rc_lsm_disks = []
+ mega_disk_path_regex = re.compile(
+ r"^Drive (\/c[0-9]+\/e[0-9]+\/s[0-9]+) - Detailed Information$")
+
+ sys_id = self._sys_id_of_ctrl_num(ctrl_num)
+
+ disk_show_output = self._storcli_exec(
+ ["/c%d/eall/sall" % ctrl_num, "show", "all"])
+ re_match = mega_disk_path_regex.match(drive_name)
+ continue
+
+ mega_disk_path = re_match.group(1)
+ # Assuming only 1 disk attached to each slot.
+ disk_show_basic_dict = disk_show_output[
+ "Drive %s" % mega_disk_path][0]
+ disk_show_attr_dict = disk_show_output[drive_name][
+ 'Drive %s Device attributes' % mega_disk_path]
+ disk_show_stat_dict = disk_show_output[drive_name][
+ 'Drive %s State' % mega_disk_path]
+
+ disk_id = disk_show_attr_dict['SN'].strip()
+ disk_name = "%s %s %s" % (
+ mega_disk_path,
+ disk_show_attr_dict['Manufacturer Id'].strip(),
+ disk_show_attr_dict['Model Number'])
+ disk_type = _disk_type_of(disk_show_basic_dict)
+ blk_size = size_human_2_size_bytes(
+ disk_show_basic_dict['SeSz'])
+ blk_count = _blk_count_of(disk_show_attr_dict['Coerced size'])
+ status = _disk_status_of(
+ disk_show_basic_dict, disk_show_stat_dict)
+
+ plugin_data = mega_disk_path
+
+ rc_lsm_disks.append(
+ Disk(
+ disk_id, disk_name, disk_type, blk_size, blk_count,
+ status, sys_id, plugin_data))
+
+ return search_property(rc_lsm_disks, search_key, search_value)
diff --git a/plugin/megaraid/megaraid_lsmplugin b/plugin/megaraid/megaraid_lsmplugin
new file mode 100755
index 0000000..5aaa0e8
--- /dev/null
+++ b/plugin/megaraid/megaraid_lsmplugin
@@ -0,0 +1,37 @@
+#!/usr/bin/env python2
+
+# Copyright (C) 2011-2013 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: tasleson
+
+import sys
+import syslog
+import traceback
+
+ from lsm import PluginRunner
+ from lsm.plugin.megaraid import MegaRAID
+
+ PluginRunner(MegaRAID, sys.argv).run()
+ #This should be quite rare, but when it does happen this is pretty
+ #key in understanding what happened, especially when it happens when
+ #running from the daemon.
+ msg = str(traceback.format_exc())
+ syslog.syslog(syslog.LOG_ERR, msg)
+ sys.stderr.write(msg)
+ sys.exit(1)
diff --git a/plugin/megaraid/utils.py b/plugin/megaraid/utils.py
new file mode 100644
index 0000000..4290b39
--- /dev/null
+++ b/plugin/megaraid/utils.py
@@ -0,0 +1,47 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+import subprocess
+import os
+
+ """
+ Execute provided command and return the STDOUT as string.
+ Raise ExecError if command return code is not zero
+ """
+ cmd_popen = subprocess.Popen(
+ cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env={"PATH": os.getenv("PATH")})
+ str_stdout = "".join(list(cmd_popen.stdout)).strip()
+ str_stderr = "".join(list(cmd_popen.stderr)).strip()
+ errno = cmd_popen.wait()
+ raise ExecError(" ".join(cmds), errno, str_stdout, str_stderr)
+ return str_stdout
+
+
+ Exception.__init__(self, *args, **kwargs)
+ self.cmd = cmd
+ self.errno = errno
+ self.stdout = stdout
+ self.stderr = stderr
+
+ return "cmd: '%s', errno: %d, stdout: '%s', stderr: '%s'" % \
+ (self.cmd, self.errno, self.stdout, self.stderr)
Gris Ge
2015-01-28 14:27:55 UTC
Permalink
* Linux plugin is designed to handle Linux local soft RAID(md RAID) by now.
It only provides two systems now:
1. base_os: Holding hardware resources like disks using udev and sysfs.
2. mdadm: Provide soft RAID(md) operations via mdadm command.

* This plugin requires root privilege. Plugin config file included.

* This plugin only support 'systems()' and 'disks()' methods.

* SELinux might stop this plugin from executing mdadm.
Please SELinux log for workaround.

* Tested on RHEL 6 and RHEL 7 via lsmcli.

Signed-off-by: Gris Ge <***@redhat.com>
---
config/Makefile.am | 10 ++-
config/pluginconf.d/linux.conf | 1 +
configure.ac | 2 +
doc/man/Makefile.am | 3 +-
doc/man/linux_lsmplugin.1.in | 54 ++++++++++++++++
packaging/libstoragemgmt.spec.in | 31 +++++++++
plugin/Makefile.am | 2 +-
plugin/linux/Makefile.am | 6 ++
plugin/linux/__init__.py | 1 +
plugin/linux/base_os.py | 134 +++++++++++++++++++++++++++++++++++++++
plugin/linux/linux.py | 122 +++++++++++++++++++++++++++++++++++
plugin/linux/linux_lsmplugin | 37 +++++++++++
plugin/linux/md_raid.py | 98 ++++++++++++++++++++++++++++
plugin/linux/utils.py | 53 ++++++++++++++++
14 files changed, 549 insertions(+), 5 deletions(-)
create mode 100644 config/pluginconf.d/linux.conf
create mode 100644 doc/man/linux_lsmplugin.1.in
create mode 100644 plugin/linux/Makefile.am
create mode 100644 plugin/linux/__init__.py
create mode 100644 plugin/linux/base_os.py
create mode 100644 plugin/linux/linux.py
create mode 100755 plugin/linux/linux_lsmplugin
create mode 100644 plugin/linux/md_raid.py
create mode 100644 plugin/linux/utils.py

diff --git a/config/Makefile.am b/config/Makefile.am
index ff0481b..d8a8203 100644
--- a/config/Makefile.am
+++ b/config/Makefile.am
@@ -1,11 +1,15 @@
lsmconfdir=$(sysconfdir)/lsm
lsmconf_DATA=lsmd.conf

-EXTRA_DIST= lsmd.conf pluginconf.d/sim.conf pluginconf.d/megaraid.conf
+EXTRA_DIST= \
+ lsmd.conf \
+ pluginconf.d/sim.conf \
+ pluginconf.d/megaraid.conf \
+ pluginconf.d/linux.conf

pluginconfdir=$(sysconfdir)/lsm/pluginconf.d

pluginconf_DATA= \
pluginconf.d/sim.conf \
- pluginconf.d/megaraid.conf
-
+ pluginconf.d/megaraid.conf \
+ pluginconf.d/linux.conf
diff --git a/config/pluginconf.d/linux.conf b/config/pluginconf.d/linux.conf
new file mode 100644
index 0000000..35b52d4
--- /dev/null
+++ b/config/pluginconf.d/linux.conf
@@ -0,0 +1 @@
+require-root-privilege = true;
diff --git a/configure.ac b/configure.ac
index 9ea7b79..4367155 100644
--- a/configure.ac
+++ b/configure.ac
@@ -209,6 +209,7 @@ AC_OUTPUT(libstoragemgmt.pc \
plugin/Makefile \
plugin/simc/Makefile \
plugin/megaraid/Makefile \
+ plugin/linux/Makefile \
daemon/Makefile \
config/Makefile \
doc/Makefile \
@@ -217,6 +218,7 @@ AC_OUTPUT(libstoragemgmt.pc \
doc/doxygen.conf \
doc/man/lsmd.conf.5 \
doc/man/megaraid_lsmplugin.1 \
+ doc/man/linux_lsmplugin.1 \
tools/Makefile \
tools/udev/Makefile \
tools/lsmcli/Makefile \
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 92d1398..a09368f 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -1,3 +1,4 @@
-notrans_dist_man1_MANS = lsmcli.1 lsmd.1 megaraid_lsmplugin.1
+notrans_dist_man1_MANS = \
+ lsmcli.1 lsmd.1 megaraid_lsmplugin.1 linux_lsmplugin.1

notrans_dist_man5_MANS = lsmd.conf.5.in
diff --git a/doc/man/linux_lsmplugin.1.in b/doc/man/linux_lsmplugin.1.in
new file mode 100644
index 0000000..7e1f364
--- /dev/null
+++ b/doc/man/linux_lsmplugin.1.in
@@ -0,0 +1,54 @@
+.TH linux_lsmplugin "1" "Januray 2015" "linux_lsmplugin @VERSION@" "libStorageMgmt"
+.SH NAME
+linux_lsmplugin -- LibstorageMgmt MegaRAID plugin
+
+.SH DESCRIPTION
+LibstorageMgmt linux plugin allows user to manage Linux local storage via
+linux open source tools(udev, mdadm).
+The 'linux_lsmplugin' executable file is for libStorageMgmt
+daemon to execute when client user specifies linux plugin in the URI.
+
+Currently, this plugin provides two systems:
+ 1. base_os
+ Providing disks seen by kernel.
+ 2. mdadm
+ Providing linux soft RAID(md) related operations.
+
+.SH URI
+To use this plugin, users should set their URI to this format:
+.nf
+
+ \fBlinux://\fR
+ or
+ \fBlinux://?mdadm=<path_of_mdadm>\fR
+
+.fi
+
+.TP mdadm
+The 'mdadm' URI paramter is used to specified the path of mdadm tool if not
+in $PATH. Normally, you don't need to specify this URI paramter.
+
+.SH ROOT PRIVILEGE
+This plugin requires both \fBlsmd\fR daemon and API client running as root
+user. Please check manpage \fIlsmd.conf (5)\fR for detail.
+
+.SH SUPPORTED HARDWARES
+Detailed support status can be queried via:
+
+ * \fBlsm.Client.capabilities()\fR (Python API)
+ * \fBlsm_capabilities()\fR (C API)
+ * \fBlsmcli capabilities\fR (lsmcli command line).
+
+.SH FIREWALL RULES
+This plugin only execute linux open source tools on localhost. No network
+connection required.
+
+.SH SEE ALSO
+\fIlsmcli\fR(1), \fIlsmd\fR(1)
+
+.SH BUGS
+Please report bugs to
+\fI<libstoragemgmt-***@lists.sourceforge.net>\fR
+
+.SH AUTHOR
+Gris Ge \fI<***@redhat.com>\fR
diff --git a/packaging/libstoragemgmt.spec.in b/packaging/libstoragemgmt.spec.in
index 17add9f..8073db4 100644
--- a/packaging/libstoragemgmt.spec.in
+++ b/packaging/libstoragemgmt.spec.in
@@ -217,6 +217,18 @@ The %{libstoragemgmt}-megaraid-plugin package contains the plugin for LSI
MegaRAID storage management via storcli.


+%package -n %{libstoragemgmt}-linux-plugin
+Summary: Files for linux storage support for %{libstoragemgmt}
+Group: System Environment/Libraries
+Requires: %{libstoragemgmt}%{?_isa} = %{version}-%{release}
+Requires: mdadm
+Requires: udev
+
+%description -n %{libstoragemgmt}-linux-plugin
+The %{libstoragemgmt}-linux-plugin package contains the plugin for linux
+storage management via udev and mdadm.
+
+
%prep
%setup -q

@@ -430,6 +442,13 @@ if [ $1 -eq 0 ]; then
fi

+%postun -n %{libstoragemgmt}-linux-plugin
+if [ $1 -eq 0 ]; then
+ # Remove
+ /usr/bin/systemctl try-restart libstoragemgmt.service \
+ >/dev/null 2>&1 || :
+fi
+
%files -n %{libstoragemgmt}
%defattr(-,root,root,-)
%doc README COPYING.LIB
@@ -534,6 +553,18 @@ fi
%{_sysconfdir}/lsm/pluginconf.d/megaraid.conf
%{_mandir}/man1/megaraid_lsmplugin.1*

+%files -n %{libstoragemgmt}-linux-plugin
+%defattr(-,root,root,-)
+%dir %{python_sitelib}/lsm/plugin/linux
+%{python_sitelib}/lsm/plugin/linux/__init__.*
+%{python_sitelib}/lsm/plugin/linux/linux.*
+%{python_sitelib}/lsm/plugin/linux/base_os.*
+%{python_sitelib}/lsm/plugin/linux/md_raid.*
+%{python_sitelib}/lsm/plugin/linux/utils.*
+%{_bindir}/linux_lsmplugin
+%{_sysconfdir}/lsm/pluginconf.d/linux.conf
+%{_mandir}/man1/linux_lsmplugin.1*
+
%files udev
%defattr(-,root,root,-)
%{udev_dir}/udev/scan-scsi-target
diff --git a/plugin/Makefile.am b/plugin/Makefile.am
index f9146a8..5043c10 100644
--- a/plugin/Makefile.am
+++ b/plugin/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS=simc megaraid
+SUBDIRS=simc megaraid linux

plugindir = $(pythondir)/lsm/plugin

diff --git a/plugin/linux/Makefile.am b/plugin/linux/Makefile.am
new file mode 100644
index 0000000..ab2a0c0
--- /dev/null
+++ b/plugin/linux/Makefile.am
@@ -0,0 +1,6 @@
+plugindir = $(pythondir)/lsm/plugin
+linuxdir = $(plugindir)/linux
+
+linux_PYTHON = base_os.py __init__.py linux.py md_raid.py utils.py
+
+dist_bin_SCRIPTS= linux_lsmplugin
diff --git a/plugin/linux/__init__.py b/plugin/linux/__init__.py
new file mode 100644
index 0000000..dfa8b16
--- /dev/null
+++ b/plugin/linux/__init__.py
@@ -0,0 +1 @@
+from lsm.plugin.linux.linux import LinuxST
diff --git a/plugin/linux/base_os.py b/plugin/linux/base_os.py
new file mode 100644
index 0000000..196fbf3
--- /dev/null
+++ b/plugin/linux/base_os.py
@@ -0,0 +1,134 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <***@redhat.com>
+
+
+import platform
+import os
+import re
+
+from lsm import (
+ Client, LsmError, ErrorNumber, System, Disk, Capabilities, md5)
+from lsm.plugin.linux.utils import cmd_exec, read_file
+
+
+def _udev_out_to_dict(udev_out):
+ """
+ Convert "udevadm info" output string to a dictionary
+ """
+ rc_dict = {}
+ info_regex = re.compile("^(?P<key_name>[A-Z_]+)=(?P<value>.+)$")
+ for line in udev_out.split("\n"):
+ line_regex_match = info_regex.match(line)
+ if line_regex_match:
+ key_name = line_regex_match.group('key_name')
+ if key_name:
+ rc_dict[key_name] = line_regex_match.group('value')
+ return rc_dict
+
+def _disk_type_of(sd_name, udev_dict):
+ if udev_dict.get('ID_ATA_ROTATION_RATE_RPM') == 0:
+ return Disk.TYPE_SSD
+ if udev_dict.get('ID_ATA_SATA'):
+ return Disk.TYPE_SATA
+ elif udev_dict.get('ID_ATA'):
+ return Disk.TYPE_ATA
+ elif udev_dict.get('ID_SCSI'):
+ return Disk.TYPE_SCSI
+
+ return Disk.TYPE_UNKNOWN
+
+def _disk_status_of(sd_name):
+ # TODO(Gris Ge):
+ # 1. Use smartctl
+ # 2. Skip multipath owned disk.(or we might trigger failover)
+ # 3. Use multipath check path status if multipath owned disk.
+ return Disk.STATUS_UNKNOWN
+
+def _sd_name_to_lsm_disk(sd_name):
+ udev_dict = _udev_out_to_dict(
+ cmd_exec(
+ ['udevadm', 'info', '--name=%s' % sd_name, '--query=property']))
+
+ if 'ID_SERIAL' in udev_dict.keys():
+ if 'ID_PATH' in udev_dict.keys():
+ # Multipath device
+ disk_id = "%s:%s" % (
+ udev_dict['ID_SERIAL'], md5(udev_dict['ID_PATH']))
+ else:
+ disk_id = udev_dict['ID_SERIAL']
+ else:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Failed to get ID_BUS and ID_SERIAL property of disk %s: %s" %
+ (sd_name, udev_dict.items()))
+ disk_name = sd_name
+ if 'ID_VENDOR' in udev_dict.keys():
+ disk_name += " %s" % udev_dict['ID_VENDOR'].strip()
+ if 'ID_MODEL' in udev_dict.keys():
+ disk_name += " %s" % udev_dict['ID_MODEL'].strip()
+
+ disk_type = _disk_type_of(sd_name, udev_dict)
+ sys_block_size_file = "/sys/block/%s/queue/logical_block_size" % sd_name
+ block_size = 512
+ if os.path.exists(sys_block_size_file):
+ block_size = int(read_file(sys_block_size_file).strip())
+ block_count = -1
+ sys_block_count_file = "/sys/block/%s/size" % sd_name
+ if os.path.exists(sys_block_count_file):
+ block_count = int(read_file(sys_block_count_file).strip())
+ disk_status = _disk_status_of(sd_name)
+ plugin_data = udev_dict.get('DEVLINKS')
+ return Disk(
+ disk_id, disk_name, disk_type, block_size, block_count, disk_status,
+ BaseOS.SYS_ID, plugin_data)
+
+
+class BaseOS(object):
+ SYS_ID = 'base_os'
+
+ def __init__(self):
+ pass
+
+ def plugin_register(self, uri, password, timeout, flags=Client.FLAG_RSVD):
+ pass
+
+ def plugin_unregister(self, flags=Client.FLAG_RSVD):
+ pass
+
+ def capabilities(self, system, flags=Client.FLAG_RSVD):
+ cap = Capabilities()
+ cap.set(Capabilities.DISKS)
+ return cap
+
+ def systems(self, flags):
+ sys_name = " ".join(platform.linux_distribution())
+ return [System(BaseOS.SYS_ID, sys_name, System.STATUS_OK, "")]
+
+ def disks(self, search_key=None, search_value=None,
+ flags=Client.FLAG_RSVD):
+ """
+ Workflow:
+ 1. Search /sys/block/ for all sd[a-z]+ device name
+ 2. Use "udevadm info --name=sdX --query=property" to check
+ information.
+ """
+ rc_lsm_disks = []
+ sd_name_regex = re.compile("^sd[a-z]+$")
+ for blk_name in os.listdir("/sys/block/"):
+ if sd_name_regex.match(blk_name):
+ rc_lsm_disks.append(_sd_name_to_lsm_disk(blk_name))
+ return rc_lsm_disks
diff --git a/plugin/linux/linux.py b/plugin/linux/linux.py
new file mode 100644
index 0000000..0782b24
--- /dev/null
+++ b/plugin/linux/linux.py
@@ -0,0 +1,122 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <***@redhat.com>
+
+# Code Layout:
+# LinuxST -- linux_st.py
+# | # Handle lsm API and methods. But LsmError will be
+# | # raised by BaseOS, MdRAID and etc sub classes.
+# |
+# +--- BaseOS -- base_os.py
+# | # Provide disks and other hardwares seeing by Linux
+# | # kernel.
+# |
+# +--- MdRAID -- md_raid.py
+# | # Handling mdadm related methods.
+
+import os
+from string import split
+
+from lsm import (uri_parse, search_property, Capabilities, LsmError, Client,
+ ErrorNumber, System, Pool, VERSION)
+
+from lsm.plugin.linux.base_os import BaseOS
+from lsm.plugin.linux.md_raid import MdRAID
+from lsm.plugin.linux.utils import ExecError
+
+
+def _handle_errors(method):
+ def _wrapper(*args, **kwargs):
+ try:
+ return method(*args, **kwargs)
+ except LsmError:
+ raise
+ except ExecError as exec_error:
+ raise LsmError(ErrorNumber.PLUGIN_BUG, str(exec_error))
+ except Exception as common_error:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Got unexpected error %s" % str(common_error))
+
+ return _wrapper
+
+
+class LinuxST(object):
+ def __init__(self):
+ self._sub_obj_dict = {}
+
+ @_handle_errors
+ def plugin_register(self, uri, password, timeout, flags=Client.FLAG_RSVD):
+ if os.geteuid() != 0:
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "This plugin requires root privilege both daemon and client")
+ uri_parsed = uri_parse(uri)
+
+ # Raise error if user requested certain system via URI.
+ # If not, ignore failure.
+ flag_ignore_error = True
+ sys_ids = []
+
+ self._sub_obj_dict[BaseOS.SYS_ID] = BaseOS()
+ self._sub_obj_dict[BaseOS.SYS_ID].plugin_register(
+ uri, password, timeout, flags)
+
+ self._sub_obj_dict[MdRAID.SYS_ID] = MdRAID()
+ self._sub_obj_dict[MdRAID.SYS_ID].plugin_register(
+ uri, password, timeout, flags)
+
+ @_handle_errors
+ def plugin_unregister(self, flags=Client.FLAG_RSVD):
+ for sub_obj in self._sub_obj_dict.values():
+ sub_obj.plugin_unregister(flags)
+
+ def plugin_info(self, flags=Client.FLAG_RSVD):
+ return "Linux Storage Plugin", VERSION
+
+ def job_status(self, job_id, flags=Client.FLAG_RSVD):
+ raise LsmError(
+ ErrorNumber.NO_SUPPORT,
+ "job_status() is not supported yet")
+
+ def job_free(self, job_id, flags=Client.FLAG_RSVD):
+ raise LsmError(
+ ErrorNumber.NO_SUPPORT,
+ "job_free() is not supported yet")
+
+ @_handle_errors
+ def capabilities(self, system, flags=Client.FLAG_RSVD):
+ if system.plugin_data not in self._sub_obj_dict.keys():
+ raise LsmError(
+ ErrorNumber.NOT_FOUND_SYSTEM,
+ "System '%s' not found" % system.id)
+
+ return self._sub_obj_dict[system.id].capabilities()
+
+ @_handle_errors
+ def systems(self, flags=Client.FLAG_RSVD):
+ rc_lsm_syss = []
+ for sub_obj in self._sub_obj_dict.values():
+ rc_lsm_syss.extend(sub_obj.systems(flags))
+ return rc_lsm_syss
+
+ @_handle_errors
+ def disks(self, search_key=None, search_value=None,
+ flags=Client.FLAG_RSVD):
+ rc_lsm_disks = []
+ for sub_obj in self._sub_obj_dict.values():
+ rc_lsm_disks.extend(sub_obj.disks(flags))
+ return rc_lsm_disks
diff --git a/plugin/linux/linux_lsmplugin b/plugin/linux/linux_lsmplugin
new file mode 100755
index 0000000..b2c0c49
--- /dev/null
+++ b/plugin/linux/linux_lsmplugin
@@ -0,0 +1,37 @@
+#!/usr/bin/env python2
+
+# Copyright (C) 2011-2013 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: tasleson
+
+import sys
+import syslog
+import traceback
+
+try:
+ from lsm import PluginRunner
+ from lsm.plugin.linux import LinuxST
+
+ if __name__ == '__main__':
+ PluginRunner(LinuxST, sys.argv).run()
+except Exception:
+ #This should be quite rare, but when it does happen this is pretty
+ #key in understanding what happened, especially when it happens when
+ #running from the daemon.
+ msg = str(traceback.format_exc())
+ syslog.syslog(syslog.LOG_ERR, msg)
+ sys.stderr.write(msg)
+ sys.exit(1)
diff --git a/plugin/linux/md_raid.py b/plugin/linux/md_raid.py
new file mode 100644
index 0000000..8c42d7c
--- /dev/null
+++ b/plugin/linux/md_raid.py
@@ -0,0 +1,98 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <***@redhat.com>
+
+import os
+import subprocess
+import errno
+
+from lsm import (uri_parse, search_property, Capabilities, LsmError,
+ ErrorNumber, System, Pool, Client)
+from lsm.plugin.linux.utils import cmd_exec, ExecError
+
+
+def _md_size_to_lsm(md_size):
+ """
+ mdadm is using number in Kibibyte, here we convert it to number in bytes.
+ """
+ return md_size * 1024
+
+def _str_stdout_to_dict(str_stdout):
+ rc_dict = {}
+ for line in str_stdout.split("\n"):
+ tmp_ar = line.split(" : ")
+ if len(tmp_ar) == 2:
+ rc_dict[tmp_ar[0]] = tmp_ar[1]
+
+ return rc_dict
+
+
+class MdRAID(object):
+ SYS_ID = 'mdadm'
+ _DEFAULT_MDADM_BIN_PATH = "mdadm"
+
+ def __init__(self):
+ self._mdadm_bin = MdRAID._DEFAULT_MDADM_BIN_PATH
+
+ def plugin_register(self, uri, password, timeout, flags=Client.FLAG_RSVD):
+ uri_parsed = uri_parse(uri)
+ self._mdadm_bin = uri_parsed.get('parameters', {}).get(
+ 'mdadm', MdRAID._DEFAULT_MDADM_BIN_PATH)
+ self._mdadm_exec(['--version'])
+
+ def plugin_unregister(self, flags=Client.FLAG_RSVD):
+ pass
+
+ def capabilities(self, system, flags=Client.FLAG_RSVD):
+ return Capabilities()
+
+ def _mdadm_exec(self, mdadm_cmds):
+ mdadm_cmds.insert(0, self._mdadm_bin)
+ try:
+ return cmd_exec(mdadm_cmds)
+ except OSError as oe:
+ if oe.errno == errno.ENOENT:
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "mdadm binary '%s' is not exist or excutable." %
+ self._mdadm_bin)
+ else:
+ raise
+
+ def systems(self, flags=Client.FLAG_RSVD):
+ # Try rpm first
+ sys_name = ''
+ try:
+ sys_name = cmd_exec(['rpm', '-q', 'mdadm'])
+ except ExecError:
+ pass
+
+ try:
+ if not sys_name:
+ sys_name = self._mdadm_exec(['--version'])
+ except ExecError:
+ raise LsmError(
+ ErrorNumber.NO_SUPPORT,
+ "Installed mdadm '%s' is not supported" % self._mdadm_bin)
+
+ return [System(MdRAID.SYS_ID, sys_name, System.STATUS_OK, "")]
+
+ def disks(self, search_key=None, search_value=None,
+ flags=Client.FLAG_RSVD):
+ """
+ Disk list is managed by BaseOS
+ """
+ return []
diff --git a/plugin/linux/utils.py b/plugin/linux/utils.py
new file mode 100644
index 0000000..667f687
--- /dev/null
+++ b/plugin/linux/utils.py
@@ -0,0 +1,53 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <***@redhat.com>
+
+import subprocess
+import os
+
+def cmd_exec(cmds):
+ """
+ Execute provided command and return the STDOUT as string.
+ Raise ExecError if command return code is not zero
+ """
+ cmd_popen = subprocess.Popen(
+ cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env={"PATH": os.getenv("PATH")})
+ str_stdout = "".join(list(cmd_popen.stdout)).strip()
+ str_stderr = "".join(list(cmd_popen.stderr)).strip()
+ errno = cmd_popen.wait()
+ if errno != 0:
+ raise ExecError(" ".join(cmds), errno, str_stdout, str_stderr)
+
+ return str_stdout
+
+def read_file(file_path):
+ """
+ Return a string with content of provided file
+ """
+ return "\n".join(open(file_path, 'r').readlines())
+
+class ExecError(Exception):
+ def __init__(self, cmd, errno, stdout, stderr, *args, **kwargs):
+ Exception.__init__(self, *args, **kwargs)
+ self.cmd = cmd
+ self.errno = errno
+ self.stdout = stdout
+ self.stderr = stderr
+
+ def __str__(self):
+ return "cmd: '%s', errno: %d, stdout: '%s', stderr: '%s'" % \
+ (self.cmd, self.errno, self.stdout, self.stderr)
--
1.8.3.1
Tony Asleson
2015-02-05 09:36:28 UTC
Permalink
Comment below
Post by Gris Ge
* Linux plugin is designed to handle Linux local soft RAID(md RAID) by now.
1. base_os: Holding hardware resources like disks using udev and sysfs.
2. mdadm: Provide soft RAID(md) operations via mdadm command.
* This plugin requires root privilege. Plugin config file included.
* This plugin only support 'systems()' and 'disks()' methods.
* SELinux might stop this plugin from executing mdadm.
Please SELinux log for workaround.
* Tested on RHEL 6 and RHEL 7 via lsmcli.
---
config/Makefile.am | 10 ++-
config/pluginconf.d/linux.conf | 1 +
configure.ac | 2 +
doc/man/Makefile.am | 3 +-
doc/man/linux_lsmplugin.1.in | 54 ++++++++++++++++
packaging/libstoragemgmt.spec.in | 31 +++++++++
plugin/Makefile.am | 2 +-
plugin/linux/Makefile.am | 6 ++
plugin/linux/__init__.py | 1 +
plugin/linux/base_os.py | 134 +++++++++++++++++++++++++++++++++++++++
plugin/linux/linux.py | 122 +++++++++++++++++++++++++++++++++++
plugin/linux/linux_lsmplugin | 37 +++++++++++
plugin/linux/md_raid.py | 98 ++++++++++++++++++++++++++++
plugin/linux/utils.py | 53 ++++++++++++++++
14 files changed, 549 insertions(+), 5 deletions(-)
create mode 100644 config/pluginconf.d/linux.conf
create mode 100644 doc/man/linux_lsmplugin.1.in
create mode 100644 plugin/linux/Makefile.am
create mode 100644 plugin/linux/__init__.py
create mode 100644 plugin/linux/base_os.py
create mode 100644 plugin/linux/linux.py
create mode 100755 plugin/linux/linux_lsmplugin
create mode 100644 plugin/linux/md_raid.py
create mode 100644 plugin/linux/utils.py
diff --git a/config/Makefile.am b/config/Makefile.am
index ff0481b..d8a8203 100644
--- a/config/Makefile.am
+++ b/config/Makefile.am
@@ -1,11 +1,15 @@
lsmconfdir=$(sysconfdir)/lsm
lsmconf_DATA=lsmd.conf
-EXTRA_DIST= lsmd.conf pluginconf.d/sim.conf pluginconf.d/megaraid.conf
+EXTRA_DIST= \
+ lsmd.conf \
+ pluginconf.d/sim.conf \
+ pluginconf.d/megaraid.conf \
+ pluginconf.d/linux.conf
pluginconfdir=$(sysconfdir)/lsm/pluginconf.d
pluginconf_DATA= \
pluginconf.d/sim.conf \
- pluginconf.d/megaraid.conf
-
+ pluginconf.d/megaraid.conf \
+ pluginconf.d/linux.conf
diff --git a/config/pluginconf.d/linux.conf b/config/pluginconf.d/linux.conf
new file mode 100644
index 0000000..35b52d4
--- /dev/null
+++ b/config/pluginconf.d/linux.conf
@@ -0,0 +1 @@
+require-root-privilege = true;
diff --git a/configure.ac b/configure.ac
index 9ea7b79..4367155 100644
--- a/configure.ac
+++ b/configure.ac
@@ -209,6 +209,7 @@ AC_OUTPUT(libstoragemgmt.pc \
plugin/Makefile \
plugin/simc/Makefile \
plugin/megaraid/Makefile \
+ plugin/linux/Makefile \
daemon/Makefile \
config/Makefile \
doc/Makefile \
@@ -217,6 +218,7 @@ AC_OUTPUT(libstoragemgmt.pc \
doc/doxygen.conf \
doc/man/lsmd.conf.5 \
doc/man/megaraid_lsmplugin.1 \
+ doc/man/linux_lsmplugin.1 \
tools/Makefile \
tools/udev/Makefile \
tools/lsmcli/Makefile \
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 92d1398..a09368f 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -1,3 +1,4 @@
-notrans_dist_man1_MANS = lsmcli.1 lsmd.1 megaraid_lsmplugin.1
+notrans_dist_man1_MANS = \
+ lsmcli.1 lsmd.1 megaraid_lsmplugin.1 linux_lsmplugin.1
notrans_dist_man5_MANS = lsmd.conf.5.in
diff --git a/doc/man/linux_lsmplugin.1.in b/doc/man/linux_lsmplugin.1.in
new file mode 100644
index 0000000..7e1f364
--- /dev/null
+++ b/doc/man/linux_lsmplugin.1.in
@@ -0,0 +1,54 @@
+.SH NAME
+linux_lsmplugin -- LibstorageMgmt MegaRAID plugin
+
+.SH DESCRIPTION
+LibstorageMgmt linux plugin allows user to manage Linux local storage via
+linux open source tools(udev, mdadm).
+The 'linux_lsmplugin' executable file is for libStorageMgmt
+daemon to execute when client user specifies linux plugin in the URI.
+
+ 1. base_os
+ Providing disks seen by kernel.
+ 2. mdadm
+ Providing linux soft RAID(md) related operations.
+
+.SH URI
+.nf
+
+ \fBlinux://\fR
+ or
+ \fBlinux://?mdadm=<path_of_mdadm>\fR
+
+.fi
+
+.TP mdadm
+The 'mdadm' URI paramter is used to specified the path of mdadm tool if not
+in $PATH. Normally, you don't need to specify this URI paramter.
+
+.SH ROOT PRIVILEGE
+This plugin requires both \fBlsmd\fR daemon and API client running as root
+user. Please check manpage \fIlsmd.conf (5)\fR for detail.
+
+.SH SUPPORTED HARDWARES
+
+ * \fBlsm.Client.capabilities()\fR (Python API)
+ * \fBlsm_capabilities()\fR (C API)
+ * \fBlsmcli capabilities\fR (lsmcli command line).
+
+.SH FIREWALL RULES
+This plugin only execute linux open source tools on localhost. No network
+connection required.
+
+.SH SEE ALSO
+\fIlsmcli\fR(1), \fIlsmd\fR(1)
+
+.SH BUGS
+Please report bugs to
+
+.SH AUTHOR
diff --git a/packaging/libstoragemgmt.spec.in b/packaging/libstoragemgmt.spec.in
index 17add9f..8073db4 100644
--- a/packaging/libstoragemgmt.spec.in
+++ b/packaging/libstoragemgmt.spec.in
@@ -217,6 +217,18 @@ The %{libstoragemgmt}-megaraid-plugin package contains the plugin for LSI
MegaRAID storage management via storcli.
+%package -n %{libstoragemgmt}-linux-plugin
+Summary: Files for linux storage support for %{libstoragemgmt}
+Group: System Environment/Libraries
+Requires: %{libstoragemgmt}%{?_isa} = %{version}-%{release}
+Requires: mdadm
+Requires: udev
+
+%description -n %{libstoragemgmt}-linux-plugin
+The %{libstoragemgmt}-linux-plugin package contains the plugin for linux
+storage management via udev and mdadm.
+
+
%prep
%setup -q
@@ -430,6 +442,13 @@ if [ $1 -eq 0 ]; then
fi
+%postun -n %{libstoragemgmt}-linux-plugin
+if [ $1 -eq 0 ]; then
+ # Remove
+ /usr/bin/systemctl try-restart libstoragemgmt.service \
+fi
+
%files -n %{libstoragemgmt}
%defattr(-,root,root,-)
%doc README COPYING.LIB
@@ -534,6 +553,18 @@ fi
%{_sysconfdir}/lsm/pluginconf.d/megaraid.conf
%{_mandir}/man1/megaraid_lsmplugin.1*
+%files -n %{libstoragemgmt}-linux-plugin
+%defattr(-,root,root,-)
+%dir %{python_sitelib}/lsm/plugin/linux
+%{python_sitelib}/lsm/plugin/linux/__init__.*
+%{python_sitelib}/lsm/plugin/linux/linux.*
+%{python_sitelib}/lsm/plugin/linux/base_os.*
+%{python_sitelib}/lsm/plugin/linux/md_raid.*
+%{python_sitelib}/lsm/plugin/linux/utils.*
+%{_bindir}/linux_lsmplugin
+%{_sysconfdir}/lsm/pluginconf.d/linux.conf
+%{_mandir}/man1/linux_lsmplugin.1*
+
%files udev
%defattr(-,root,root,-)
%{udev_dir}/udev/scan-scsi-target
diff --git a/plugin/Makefile.am b/plugin/Makefile.am
index f9146a8..5043c10 100644
--- a/plugin/Makefile.am
+++ b/plugin/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS=simc megaraid
+SUBDIRS=simc megaraid linux
plugindir = $(pythondir)/lsm/plugin
diff --git a/plugin/linux/Makefile.am b/plugin/linux/Makefile.am
new file mode 100644
index 0000000..ab2a0c0
--- /dev/null
+++ b/plugin/linux/Makefile.am
@@ -0,0 +1,6 @@
+plugindir = $(pythondir)/lsm/plugin
+linuxdir = $(plugindir)/linux
+
+linux_PYTHON = base_os.py __init__.py linux.py md_raid.py utils.py
+
+dist_bin_SCRIPTS= linux_lsmplugin
diff --git a/plugin/linux/__init__.py b/plugin/linux/__init__.py
new file mode 100644
index 0000000..dfa8b16
--- /dev/null
+++ b/plugin/linux/__init__.py
@@ -0,0 +1 @@
+from lsm.plugin.linux.linux import LinuxST
diff --git a/plugin/linux/base_os.py b/plugin/linux/base_os.py
new file mode 100644
index 0000000..196fbf3
--- /dev/null
+++ b/plugin/linux/base_os.py
@@ -0,0 +1,134 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+
+import platform
+import os
+import re
+
+from lsm import (
+ Client, LsmError, ErrorNumber, System, Disk, Capabilities, md5)
+from lsm.plugin.linux.utils import cmd_exec, read_file
+
+
+ """
+ Convert "udevadm info" output string to a dictionary
+ """
+ rc_dict = {}
+ info_regex = re.compile("^(?P<key_name>[A-Z_]+)=(?P<value>.+)$")
+ line_regex_match = info_regex.match(line)
+ key_name = line_regex_match.group('key_name')
+ rc_dict[key_name] = line_regex_match.group('value')
+ return rc_dict
+
+ return Disk.TYPE_SSD
+ return Disk.TYPE_SATA
+ return Disk.TYPE_ATA
+ return Disk.TYPE_SCSI
+
+ return Disk.TYPE_UNKNOWN
+
+ # 1. Use smartctl
+ # 2. Skip multipath owned disk.(or we might trigger failover)
+ # 3. Use multipath check path status if multipath owned disk.
+ return Disk.STATUS_UNKNOWN
+
+ udev_dict = _udev_out_to_dict(
+ cmd_exec(
+ ['udevadm', 'info', '--name=%s' % sd_name, '--query=property']))
+
+ # Multipath device
+ disk_id = "%s:%s" % (
+ udev_dict['ID_SERIAL'], md5(udev_dict['ID_PATH']))
+ disk_id = udev_dict['ID_SERIAL']
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Failed to get ID_BUS and ID_SERIAL property of disk %s: %s" %
+ (sd_name, udev_dict.items()))
+ disk_name = sd_name
+ disk_name += " %s" % udev_dict['ID_VENDOR'].strip()
+ disk_name += " %s" % udev_dict['ID_MODEL'].strip()
+
+ disk_type = _disk_type_of(sd_name, udev_dict)
+ sys_block_size_file = "/sys/block/%s/queue/logical_block_size" % sd_name
+ block_size = 512
+ block_size = int(read_file(sys_block_size_file).strip())
+ block_count = -1
+ sys_block_count_file = "/sys/block/%s/size" % sd_name
+ block_count = int(read_file(sys_block_count_file).strip())
+ disk_status = _disk_status_of(sd_name)
+ plugin_data = udev_dict.get('DEVLINKS')
+ return Disk(
+ disk_id, disk_name, disk_type, block_size, block_count, disk_status,
+ BaseOS.SYS_ID, plugin_data)
+
+
Need to inherit from existing interface class or define a new one for
local hba/system storage.
Post by Gris Ge
+ SYS_ID = 'base_os'
+
+ pass
+
+ pass
+
+ pass
+
+ cap = Capabilities()
+ cap.set(Capabilities.DISKS)
+ return cap
+
+ sys_name = " ".join(platform.linux_distribution())
+ return [System(BaseOS.SYS_ID, sys_name, System.STATUS_OK, "")]
+
+ def disks(self, search_key=None, search_value=None,
+ """
+ 1. Search /sys/block/ for all sd[a-z]+ device name
+ 2. Use "udevadm info --name=sdX --query=property" to check
+ information.
+ """
+ rc_lsm_disks = []
+ sd_name_regex = re.compile("^sd[a-z]+$")
+ rc_lsm_disks.append(_sd_name_to_lsm_disk(blk_name))
+ return rc_lsm_disks
diff --git a/plugin/linux/linux.py b/plugin/linux/linux.py
new file mode 100644
index 0000000..0782b24
--- /dev/null
+++ b/plugin/linux/linux.py
@@ -0,0 +1,122 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+# LinuxST -- linux_st.py
+# | # Handle lsm API and methods. But LsmError will be
+# | # raised by BaseOS, MdRAID and etc sub classes.
+# |
+# +--- BaseOS -- base_os.py
+# | # Provide disks and other hardwares seeing by Linux
+# | # kernel.
+# |
+# +--- MdRAID -- md_raid.py
+# | # Handling mdadm related methods.
+
+import os
+from string import split
+
+from lsm import (uri_parse, search_property, Capabilities, LsmError, Client,
+ ErrorNumber, System, Pool, VERSION)
+
+from lsm.plugin.linux.base_os import BaseOS
+from lsm.plugin.linux.md_raid import MdRAID
+from lsm.plugin.linux.utils import ExecError
+
+
+ return method(*args, **kwargs)
+ raise
+ raise LsmError(ErrorNumber.PLUGIN_BUG, str(exec_error))
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Got unexpected error %s" % str(common_error))
+
+ return _wrapper
+
+
+ self._sub_obj_dict = {}
+
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "This plugin requires root privilege both daemon and client")
+ uri_parsed = uri_parse(uri)
+
+ # Raise error if user requested certain system via URI.
+ # If not, ignore failure.
+ flag_ignore_error = True
+ sys_ids = []
+
+ self._sub_obj_dict[BaseOS.SYS_ID] = BaseOS()
+ self._sub_obj_dict[BaseOS.SYS_ID].plugin_register(
+ uri, password, timeout, flags)
+
+ self._sub_obj_dict[MdRAID.SYS_ID] = MdRAID()
+ self._sub_obj_dict[MdRAID.SYS_ID].plugin_register(
+ uri, password, timeout, flags)
+
+ sub_obj.plugin_unregister(flags)
+
+ return "Linux Storage Plugin", VERSION
+
+ raise LsmError(
+ ErrorNumber.NO_SUPPORT,
+ "job_status() is not supported yet")
+
+ raise LsmError(
+ ErrorNumber.NO_SUPPORT,
+ "job_free() is not supported yet")
+
+ raise LsmError(
+ ErrorNumber.NOT_FOUND_SYSTEM,
+ "System '%s' not found" % system.id)
+
+ return self._sub_obj_dict[system.id].capabilities()
+
+ rc_lsm_syss = []
+ rc_lsm_syss.extend(sub_obj.systems(flags))
+ return rc_lsm_syss
+
+ def disks(self, search_key=None, search_value=None,
+ rc_lsm_disks = []
+ rc_lsm_disks.extend(sub_obj.disks(flags))
+ return rc_lsm_disks
diff --git a/plugin/linux/linux_lsmplugin b/plugin/linux/linux_lsmplugin
new file mode 100755
index 0000000..b2c0c49
--- /dev/null
+++ b/plugin/linux/linux_lsmplugin
@@ -0,0 +1,37 @@
+#!/usr/bin/env python2
+
+# Copyright (C) 2011-2013 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: tasleson
+
+import sys
+import syslog
+import traceback
+
+ from lsm import PluginRunner
+ from lsm.plugin.linux import LinuxST
+
+ PluginRunner(LinuxST, sys.argv).run()
+ #This should be quite rare, but when it does happen this is pretty
+ #key in understanding what happened, especially when it happens when
+ #running from the daemon.
+ msg = str(traceback.format_exc())
+ syslog.syslog(syslog.LOG_ERR, msg)
+ sys.stderr.write(msg)
+ sys.exit(1)
diff --git a/plugin/linux/md_raid.py b/plugin/linux/md_raid.py
new file mode 100644
index 0000000..8c42d7c
--- /dev/null
+++ b/plugin/linux/md_raid.py
@@ -0,0 +1,98 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+import os
+import subprocess
+import errno
+
+from lsm import (uri_parse, search_property, Capabilities, LsmError,
+ ErrorNumber, System, Pool, Client)
+from lsm.plugin.linux.utils import cmd_exec, ExecError
+
+
+ """
+ mdadm is using number in Kibibyte, here we convert it to number in bytes.
+ """
+ return md_size * 1024
+
+ rc_dict = {}
+ tmp_ar = line.split(" : ")
+ rc_dict[tmp_ar[0]] = tmp_ar[1]
+
+ return rc_dict
+
+
+ SYS_ID = 'mdadm'
+ _DEFAULT_MDADM_BIN_PATH = "mdadm"
+
+ self._mdadm_bin = MdRAID._DEFAULT_MDADM_BIN_PATH
+
+ uri_parsed = uri_parse(uri)
+ self._mdadm_bin = uri_parsed.get('parameters', {}).get(
+ 'mdadm', MdRAID._DEFAULT_MDADM_BIN_PATH)
+ self._mdadm_exec(['--version'])
+
+ pass
+
+ return Capabilities()
+
+ mdadm_cmds.insert(0, self._mdadm_bin)
+ return cmd_exec(mdadm_cmds)
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "mdadm binary '%s' is not exist or excutable." %
+ self._mdadm_bin)
+ raise
+
+ # Try rpm first
+ sys_name = ''
+ sys_name = cmd_exec(['rpm', '-q', 'mdadm'])
+ pass
+
+ sys_name = self._mdadm_exec(['--version'])
+ raise LsmError(
+ ErrorNumber.NO_SUPPORT,
+ "Installed mdadm '%s' is not supported" % self._mdadm_bin)
+
+ return [System(MdRAID.SYS_ID, sys_name, System.STATUS_OK, "")]
+
+ def disks(self, search_key=None, search_value=None,
+ """
+ Disk list is managed by BaseOS
+ """
+ return []
diff --git a/plugin/linux/utils.py b/plugin/linux/utils.py
new file mode 100644
index 0000000..667f687
--- /dev/null
+++ b/plugin/linux/utils.py
@@ -0,0 +1,53 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+import subprocess
+import os
+
+ """
+ Execute provided command and return the STDOUT as string.
+ Raise ExecError if command return code is not zero
+ """
+ cmd_popen = subprocess.Popen(
+ cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env={"PATH": os.getenv("PATH")})
+ str_stdout = "".join(list(cmd_popen.stdout)).strip()
+ str_stderr = "".join(list(cmd_popen.stderr)).strip()
+ errno = cmd_popen.wait()
+ raise ExecError(" ".join(cmds), errno, str_stdout, str_stderr)
+
+ return str_stdout
+
+ """
+ Return a string with content of provided file
+ """
+ return "\n".join(open(file_path, 'r').readlines())
+
+ Exception.__init__(self, *args, **kwargs)
+ self.cmd = cmd
+ self.errno = errno
+ self.stdout = stdout
+ self.stderr = stderr
+
+ return "cmd: '%s', errno: %d, stdout: '%s', stderr: '%s'" % \
+ (self.cmd, self.errno, self.stdout, self.stderr)
Tony Asleson
2015-02-05 09:34:57 UTC
Permalink
Hi Gris,

Please add options to configure.ac to optionally include or exclude
MegaRAID/linux from the build/install.

Note: make check fails

Thanks,
Tony
Post by Gris Ge
0. Patch 1/7 and 2/7 is just bug fix.
1. Update lsmd for allowing plugin run as root user.
2. New plugin: MegaRAID plugin (megaraid://).
# Require root privilege
# Using LSI binary tool -- 'storcli'
# 'storcli' provides 'J' command line switch/option which change
# output to json format, it's pretty handy for coding with it.
3. New plugin: Linux plugin (linux://).
# Require root privilege
# Using udev, sysfs and mdadm.
# The name of this plugin is confusing. Any suggest?
# We might add LVM(it has RAID also) later.
* There are some duplicate codes in utils.py of linux plugin and MegaRAID
plugin. But considering plugin was supposed to be standalone, let's live
with it.
* RHEL 7 -- Only has hpssacli (new one used in Openstack cinder and
VMWare)
* RHEL 6 -- Only has hpacucli (old one, marked as obsolete[1])
Hence, I would like to postpone it for a while.
Thank you very much in advance.
[1] http://h20565.www2.hp.com/hpsc/doc/public/display?sp4ts.oid=254934&docId=mmr_kc-0110258&docLocale=en_US
lsmd: Remove incorrect log in child_cleanup()
lsmcli: Add missing Disk.TYPE_SCSI
lsmd Daemon: New feature: allowing plugin run as root.
lsmenv: Support new feature of lsmd for root privilege.
New Plugin: MegaRAID plugin using storcli binary tool.
New Plugin: Linux Plugin for linux storage management
lsmd: Clean up logging
Makefile.am | 2 +-
config/Makefile.am | 15 ++
config/lsmd.conf | 1 +
config/pluginconf.d/linux.conf | 1 +
config/pluginconf.d/megaraid.conf | 1 +
config/pluginconf.d/sim.conf | 1 +
configure.ac | 14 ++
daemon/Makefile.am | 4 +-
daemon/lsm_daemon.c | 278 ++++++++++++++++++++++++------
doc/man/Makefile.am | 4 +-
doc/man/linux_lsmplugin.1.in | 54 ++++++
doc/man/lsmd.conf.5.in | 58 +++++++
doc/man/megaraid_lsmplugin.1.in | 50 ++++++
packaging/daemon/libstoragemgmt.service | 2 +-
packaging/libstoragemgmt.spec.in | 63 +++++++
plugin/Makefile.am | 2 +-
plugin/linux/Makefile.am | 6 +
plugin/linux/__init__.py | 1 +
plugin/linux/base_os.py | 134 +++++++++++++++
plugin/linux/linux.py | 122 +++++++++++++
plugin/linux/linux_lsmplugin | 37 ++++
plugin/linux/md_raid.py | 98 +++++++++++
plugin/linux/utils.py | 53 ++++++
plugin/megaraid/Makefile.am | 6 +
plugin/megaraid/__init__.py | 1 +
plugin/megaraid/megaraid.py | 292 ++++++++++++++++++++++++++++++++
plugin/megaraid/megaraid_lsmplugin | 37 ++++
plugin/megaraid/utils.py | 47 +++++
tools/lsmcli/data_display.py | 1 +
tools/lsmenv | 61 ++++++-
30 files changed, 1382 insertions(+), 64 deletions(-)
create mode 100644 config/Makefile.am
create mode 100644 config/lsmd.conf
create mode 100644 config/pluginconf.d/linux.conf
create mode 100644 config/pluginconf.d/megaraid.conf
create mode 100644 config/pluginconf.d/sim.conf
create mode 100644 doc/man/linux_lsmplugin.1.in
create mode 100644 doc/man/lsmd.conf.5.in
create mode 100644 doc/man/megaraid_lsmplugin.1.in
create mode 100644 plugin/linux/Makefile.am
create mode 100644 plugin/linux/__init__.py
create mode 100644 plugin/linux/base_os.py
create mode 100644 plugin/linux/linux.py
create mode 100755 plugin/linux/linux_lsmplugin
create mode 100644 plugin/linux/md_raid.py
create mode 100644 plugin/linux/utils.py
create mode 100644 plugin/megaraid/Makefile.am
create mode 100644 plugin/megaraid/__init__.py
create mode 100644 plugin/megaraid/megaraid.py
create mode 100755 plugin/megaraid/megaraid_lsmplugin
create mode 100644 plugin/megaraid/utils.py
Gris Ge
2015-02-05 10:40:36 UTC
Permalink
Post by Tony Asleson
Hi Gris,
Please add options to configure.ac to optionally include or exclude
MegaRAID/linux from the build/install.
Note: make check fails
Thanks,
Tony
Hi Tony,

Thanks for the comments.
Will work on these issues.

Best regards.
--
Gris Ge
Gris Ge
2015-02-09 11:22:02 UTC
Permalink
* This patch set contains four parts:
0. Patch 1/7 and 2/7 is just bug fix.
1. Update lsmd for allowing plugin run as root user.
2. New plugin: MegaRAID plugin (megaraid://).
# Require root privilege
# Using LSI binary tool -- 'storcli'
# 'storcli' provides 'J' command line switch/option which change
# output to json format, it's pretty handy for coding with it.
3. New plugin: Linux plugin (linux://).
# Require root privilege
# Using udev, sysfs and mdadm.
# The name of this plugin is confusing. Any suggest?
# We might add LVM(it has RAID also) later.

* There are some duplicate codes in utils.py of linux plugin and MegaRAID
plugin. But considering plugin was supposed to be standalone, let's live
with it.

* For missing HP SmartArray plugin, HP has two tools for RHEL 6 and 7:

* RHEL 7 -- Only has hpssacli (new one used in Openstack cinder and
VMWare)
* RHEL 6 -- Only has hpacucli (old one, marked as obsolete[1])

Hence, I would like to postpone it for a while.


Thank you very much in advance.

[1] http://h20565.www2.hp.com/hpsc/doc/public/display?sp4ts.oid=254934&docId=mmr_kc-0110258&docLocale=en_US

Changes in V2:

1. Drop the 'Linux plugin' for mdadm support. User should use blievt or
other tools instead.

2. With Tony's lsmd.conf(5) manpage update.

3. Change MegaRAID disk name format to contain disk ID which could be use
in smartctl command.

4. Fix memory leak in root privilege codes of lsmd.

5. Update MegaRAID plugin class to inherit from IPlugin base class.

6. Tested on RHEL6 and RHEL7.

Gris Ge (5):
lsmd: Remove incorrect log in child_cleanup()
lsmcli: Add missing Disk.TYPE_SCSI
lsmd Daemon: New feature: allowing plugin run as root.
lsmenv: Support new feature of lsmd for root privilege.
New Plugin: MegaRAID plugin using storcli binary tool.

Tony Asleson (2):
lsmd: Clean up logging
Man page updates for lsmd.conf

Makefile.am | 2 +-
config/Makefile.am | 13 ++
config/lsmd.conf | 1 +
config/pluginconf.d/megaraid.conf | 1 +
config/pluginconf.d/sim.conf | 1 +
configure.ac | 24 +++
daemon/Makefile.am | 4 +-
daemon/lsm_daemon.c | 279 +++++++++++++++++++++------
doc/man/Makefile.am | 5 +
doc/man/lsmd.conf.5.in | 56 ++++++
doc/man/megaraid_lsmplugin.1.in | 52 +++++
packaging/daemon/libstoragemgmt.service | 2 +-
packaging/libstoragemgmt.spec.in | 47 +++++
plugin/Makefile.am | 2 +-
plugin/megaraid/Makefile.am | 8 +
plugin/megaraid/__init__.py | 1 +
plugin/megaraid/megaraid.py | 323 ++++++++++++++++++++++++++++++++
plugin/megaraid/megaraid_lsmplugin | 37 ++++
plugin/megaraid/utils.py | 47 +++++
tools/lsmcli/data_display.py | 1 +
tools/lsmenv | 79 +++++++-
21 files changed, 919 insertions(+), 66 deletions(-)
create mode 100644 config/Makefile.am
create mode 100644 config/lsmd.conf
create mode 100644 config/pluginconf.d/megaraid.conf
create mode 100644 config/pluginconf.d/sim.conf
create mode 100644 doc/man/lsmd.conf.5.in
create mode 100644 doc/man/megaraid_lsmplugin.1.in
create mode 100644 plugin/megaraid/Makefile.am
create mode 100644 plugin/megaraid/__init__.py
create mode 100644 plugin/megaraid/megaraid.py
create mode 100755 plugin/megaraid/megaraid_lsmplugin
create mode 100644 plugin/megaraid/utils.py
--
1.8.3.1
Gris Ge
2015-02-09 11:22:03 UTC
Permalink
From: Tony Asleson <***@redhat.com>

- Renamed loud() to log_and_exit()
- Changes the logger function to use severity in syslog
call

Signed-off-by: Tony Asleson <***@redhat.com>

Changes in V2:

- Rebase to current master(b933bccad3ee29538f6200ae1b26e3644e9358fe).

Signed-off-by: Gris Ge <***@redhat.com>
---
daemon/lsm_daemon.c | 61 +++++++++++++++++++++++++++++------------------------
1 file changed, 34 insertions(+), 27 deletions(-)

diff --git a/daemon/lsm_daemon.c b/daemon/lsm_daemon.c
index acc8ef5..a60296d 100644
--- a/daemon/lsm_daemon.c
+++ b/daemon/lsm_daemon.c
@@ -96,15 +96,18 @@ void logger(int severity, const char *fmt, ...)
{
char buf[2048];

- if( (LOG_INFO == severity && verbose_flag) || LOG_ERR == LOG_WARNING
- || LOG_ERR == severity) {
+ if( verbose_flag || LOG_WARNING == severity || LOG_ERR == severity) {
va_list arg;
va_start(arg, fmt);
vsnprintf(buf, sizeof(buf), fmt, arg);
va_end(arg);

if( !systemd ) {
- syslog(LOG_ERR, "%s", buf);
+ if( verbose_flag ) {
+ syslog(LOG_ERR, "%s", buf);
+ } else {
+ syslog(severity, "%s", buf);
+ }
} else {
fprintf(stdout, "%s", buf);
fflush(stdout);
@@ -115,7 +118,7 @@ void logger(int severity, const char *fmt, ...)
}
}
}
-#define loud(fmt, ...) logger(LOG_ERR, fmt, ##__VA_ARGS__)
+#define log_and_exit(fmt, ...) logger(LOG_ERR, fmt, ##__VA_ARGS__)
#define warn(fmt, ...) logger(LOG_WARNING, fmt, ##__VA_ARGS__)
#define info(fmt, ...) logger(LOG_INFO, fmt, ##__VA_ARGS__)

@@ -157,17 +160,20 @@ void drop_privileges(void)

if( -1 == setgid(pw->pw_gid) ) {
err = errno;
- loud("Unexpected error on setgid(errno %d)\n", err);
+ log_and_exit(
+ "Unexpected error on setgid(errno %d)\n", err);
}

if( -1 == setgroups(1, &pw->pw_gid) ) {
err = errno;
- loud("Unexpected error on setgroups(errno %d)\n", err);
+ log_and_exit(
+ "Unexpected error on setgroups(errno %d)\n", err);
}

if( -1 == setuid(pw->pw_uid) ) {
err = errno;
- loud("Unexpected error on setuid(errno %d)\n", err);
+ log_and_exit(
+ "Unexpected error on setuid(errno %d)\n", err);
}
} else if ( pw->pw_uid != getuid() ) {
warn("Daemon not running as correct user\n");
@@ -187,14 +193,14 @@ void flight_check(void)
int err = 0;
if( -1 == access(socket_dir, R_OK|W_OK )) {
err = errno;
- loud("Unable to access socket directory %s, errno= %d\n",
- socket_dir, err);
+ log_and_exit("Unable to access socket directory %s, errno= %d\n",
+ socket_dir, err);
}

if( -1 == access(plugin_dir, R_OK|X_OK)) {
err = errno;
- loud("Unable to access plug-in directory %s, errno= %d\n",
- plugin_dir, err);
+ log_and_exit("Unable to access plug-in directory %s, errno= %d\n",
+ plugin_dir, err);
}
}

@@ -225,7 +231,7 @@ char *path_form(const char* path, const char *name)
if( full ) {
snprintf(full, s, "%s/%s", path, name);
} else {
- loud("malloc failure while trying to allocate %d bytes\n", s);
+ log_and_exit("malloc failure while trying to allocate %d bytes\n", s);
}
return full;
}
@@ -277,12 +283,13 @@ void process_directory( char *dir, void *p, file_op call_back)

if( closedir(dp) ) {
err = errno;
- loud("Error on closing dir %s: %s\n", dir, strerror(err));
+ log_and_exit("Error on closing dir %s: %s\n", dir,
+ strerror(err));
}
} else {
err = errno;
- loud("Error on processing directory %s: %s\n", dir,
- strerror(err));
+ log_and_exit("Error on processing directory %s: %s\n", dir,
+ strerror(err));
}
}
}
@@ -304,7 +311,7 @@ int delete_socket(void *p, char *full_name)
if( S_ISSOCK(statbuf.st_mode)) {
if( unlink(full_name) ) {
err = errno;
- loud("Error on unlinking file %s: %s\n",
+ log_and_exit("Error on unlinking file %s: %s\n",
full_name, strerror(err));
}
}
@@ -351,23 +358,23 @@ int setup_socket(char *full_name)

if( -1 == bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un))) {
err = errno;
- loud("Error on binding socket %s: %s\n", socket_file, strerror(err));
+ log_and_exit("Error on binding socket %s: %s\n", socket_file, strerror(err));
}

if( -1 == chmod(socket_file, S_IREAD|S_IWRITE|S_IRGRP|S_IWGRP
|S_IROTH|S_IWOTH)) {
err = errno;
- loud("Error on chmod socket file %s: %s\n", socket_file, strerror(err));
+ log_and_exit("Error on chmod socket file %s: %s\n", socket_file, strerror(err));
}

if( -1 == listen(fd, 5)) {
err = errno;
- loud("Error on listening %s: %s\n", socket_file, strerror(err));
+ log_and_exit("Error on listening %s: %s\n", socket_file, strerror(err));
}

} else {
err = errno;
- loud("Error on socket create %s: %s\n",
+ log_and_exit("Error on socket create %s: %s\n",
socket_file, strerror(err));
}

@@ -429,10 +436,10 @@ int process_plugin(void *p, char *full_name)
setup_socket will exit on error. */
free(item);
item = NULL;
- loud("strdup failed %s\n", full_name);
+ log_and_exit("strdup failed %s\n", full_name);
}
} else {
- loud("Memory allocation failure!\n");
+ log_and_exit("Memory allocation failure!\n");
}
}
}
@@ -456,7 +463,7 @@ void child_cleanup(void)

if( -1 == rc ) {
err = errno;
- warn("Error: waitid %d - %s\n", err, strerror(err));
+ info("waitid %d - %s\n", err, strerror(err));
break;
} else {
if( 0 == rc && si.si_pid == 0 ) {
@@ -566,7 +573,7 @@ void exec_plugin( char *plugin, int client_fd )

if( -1 == exec_rc ) {
int err = errno;
- loud("Error on exec'ing Plugin %s: %s\n",
+ log_and_exit("Error on exec'ing Plugin %s: %s\n",
p_copy, strerror(err));
}
}
@@ -598,7 +605,7 @@ void _serving(void)
}

if( !nfds ) {
- loud("No plugins found in directory %s\n", plugin_dir);
+ log_and_exit("No plugins found in directory %s\n", plugin_dir);
}

nfds += 1;
@@ -609,7 +616,7 @@ void _serving(void)
return;
} else {
err = errno;
- loud("Error on selecting Plugin: %s", strerror(err));
+ log_and_exit("Error on selecting Plugin: %s", strerror(err));
}
} else if( ready > 0 ) {
int fd = 0;
@@ -729,7 +736,7 @@ int main(int argc, char *argv[])
if( !systemd ) {
if ( -1 == daemon(0, 0) ) {
int err = errno;
- loud("Error on calling daemon: %s\n", strerror(err));
+ log_and_exit("Error on calling daemon: %s\n", strerror(err));
}
}
--
1.8.3.1
Tony Asleson
2015-02-10 21:43:59 UTC
Permalink
Hi Gris,

Amended patch set ready to be committed, I added Ma Shimiao's patches
first and needed to amend the log clean-up patch to change the loud to
log_and_exit.

Will commit as soon as sf.net is fully back up.

Thanks!

Regards,
Tony
Post by Gris Ge
- Renamed loud() to log_and_exit()
- Changes the logger function to use severity in syslog
call
- Rebase to current master(b933bccad3ee29538f6200ae1b26e3644e9358fe).
---
daemon/lsm_daemon.c | 61 +++++++++++++++++++++++++++++------------------------
1 file changed, 34 insertions(+), 27 deletions(-)
diff --git a/daemon/lsm_daemon.c b/daemon/lsm_daemon.c
index acc8ef5..a60296d 100644
--- a/daemon/lsm_daemon.c
+++ b/daemon/lsm_daemon.c
@@ -96,15 +96,18 @@ void logger(int severity, const char *fmt, ...)
{
char buf[2048];
- if( (LOG_INFO == severity && verbose_flag) || LOG_ERR == LOG_WARNING
- || LOG_ERR == severity) {
+ if( verbose_flag || LOG_WARNING == severity || LOG_ERR == severity) {
va_list arg;
va_start(arg, fmt);
vsnprintf(buf, sizeof(buf), fmt, arg);
va_end(arg);
if( !systemd ) {
- syslog(LOG_ERR, "%s", buf);
+ if( verbose_flag ) {
+ syslog(LOG_ERR, "%s", buf);
+ } else {
+ syslog(severity, "%s", buf);
+ }
} else {
fprintf(stdout, "%s", buf);
fflush(stdout);
@@ -115,7 +118,7 @@ void logger(int severity, const char *fmt, ...)
}
}
}
-#define loud(fmt, ...) logger(LOG_ERR, fmt, ##__VA_ARGS__)
+#define log_and_exit(fmt, ...) logger(LOG_ERR, fmt, ##__VA_ARGS__)
#define warn(fmt, ...) logger(LOG_WARNING, fmt, ##__VA_ARGS__)
#define info(fmt, ...) logger(LOG_INFO, fmt, ##__VA_ARGS__)
@@ -157,17 +160,20 @@ void drop_privileges(void)
if( -1 == setgid(pw->pw_gid) ) {
err = errno;
- loud("Unexpected error on setgid(errno %d)\n", err);
+ log_and_exit(
+ "Unexpected error on setgid(errno %d)\n", err);
}
if( -1 == setgroups(1, &pw->pw_gid) ) {
err = errno;
- loud("Unexpected error on setgroups(errno %d)\n", err);
+ log_and_exit(
+ "Unexpected error on setgroups(errno %d)\n", err);
}
if( -1 == setuid(pw->pw_uid) ) {
err = errno;
- loud("Unexpected error on setuid(errno %d)\n", err);
+ log_and_exit(
+ "Unexpected error on setuid(errno %d)\n", err);
}
} else if ( pw->pw_uid != getuid() ) {
warn("Daemon not running as correct user\n");
@@ -187,14 +193,14 @@ void flight_check(void)
int err = 0;
if( -1 == access(socket_dir, R_OK|W_OK )) {
err = errno;
- loud("Unable to access socket directory %s, errno= %d\n",
- socket_dir, err);
+ log_and_exit("Unable to access socket directory %s, errno= %d\n",
+ socket_dir, err);
}
if( -1 == access(plugin_dir, R_OK|X_OK)) {
err = errno;
- loud("Unable to access plug-in directory %s, errno= %d\n",
- plugin_dir, err);
+ log_and_exit("Unable to access plug-in directory %s, errno= %d\n",
+ plugin_dir, err);
}
}
@@ -225,7 +231,7 @@ char *path_form(const char* path, const char *name)
if( full ) {
snprintf(full, s, "%s/%s", path, name);
} else {
- loud("malloc failure while trying to allocate %d bytes\n", s);
+ log_and_exit("malloc failure while trying to allocate %d bytes\n", s);
}
return full;
}
@@ -277,12 +283,13 @@ void process_directory( char *dir, void *p, file_op call_back)
if( closedir(dp) ) {
err = errno;
- loud("Error on closing dir %s: %s\n", dir, strerror(err));
+ log_and_exit("Error on closing dir %s: %s\n", dir,
+ strerror(err));
}
} else {
err = errno;
- loud("Error on processing directory %s: %s\n", dir,
- strerror(err));
+ log_and_exit("Error on processing directory %s: %s\n", dir,
+ strerror(err));
}
}
}
@@ -304,7 +311,7 @@ int delete_socket(void *p, char *full_name)
if( S_ISSOCK(statbuf.st_mode)) {
if( unlink(full_name) ) {
err = errno;
- loud("Error on unlinking file %s: %s\n",
+ log_and_exit("Error on unlinking file %s: %s\n",
full_name, strerror(err));
}
}
@@ -351,23 +358,23 @@ int setup_socket(char *full_name)
if( -1 == bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un))) {
err = errno;
- loud("Error on binding socket %s: %s\n", socket_file, strerror(err));
+ log_and_exit("Error on binding socket %s: %s\n", socket_file, strerror(err));
}
if( -1 == chmod(socket_file, S_IREAD|S_IWRITE|S_IRGRP|S_IWGRP
|S_IROTH|S_IWOTH)) {
err = errno;
- loud("Error on chmod socket file %s: %s\n", socket_file, strerror(err));
+ log_and_exit("Error on chmod socket file %s: %s\n", socket_file, strerror(err));
}
if( -1 == listen(fd, 5)) {
err = errno;
- loud("Error on listening %s: %s\n", socket_file, strerror(err));
+ log_and_exit("Error on listening %s: %s\n", socket_file, strerror(err));
}
} else {
err = errno;
- loud("Error on socket create %s: %s\n",
+ log_and_exit("Error on socket create %s: %s\n",
socket_file, strerror(err));
}
@@ -429,10 +436,10 @@ int process_plugin(void *p, char *full_name)
setup_socket will exit on error. */
free(item);
item = NULL;
- loud("strdup failed %s\n", full_name);
+ log_and_exit("strdup failed %s\n", full_name);
}
} else {
- loud("Memory allocation failure!\n");
+ log_and_exit("Memory allocation failure!\n");
}
}
}
@@ -456,7 +463,7 @@ void child_cleanup(void)
if( -1 == rc ) {
err = errno;
- warn("Error: waitid %d - %s\n", err, strerror(err));
+ info("waitid %d - %s\n", err, strerror(err));
break;
} else {
if( 0 == rc && si.si_pid == 0 ) {
@@ -566,7 +573,7 @@ void exec_plugin( char *plugin, int client_fd )
if( -1 == exec_rc ) {
int err = errno;
- loud("Error on exec'ing Plugin %s: %s\n",
+ log_and_exit("Error on exec'ing Plugin %s: %s\n",
p_copy, strerror(err));
}
}
@@ -598,7 +605,7 @@ void _serving(void)
}
if( !nfds ) {
- loud("No plugins found in directory %s\n", plugin_dir);
+ log_and_exit("No plugins found in directory %s\n", plugin_dir);
}
nfds += 1;
@@ -609,7 +616,7 @@ void _serving(void)
return;
} else {
err = errno;
- loud("Error on selecting Plugin: %s", strerror(err));
+ log_and_exit("Error on selecting Plugin: %s", strerror(err));
}
} else if( ready > 0 ) {
int fd = 0;
@@ -729,7 +736,7 @@ int main(int argc, char *argv[])
if( !systemd ) {
if ( -1 == daemon(0, 0) ) {
int err = errno;
- loud("Error on calling daemon: %s\n", strerror(err));
+ log_and_exit("Error on calling daemon: %s\n", strerror(err));
}
}
Gris Ge
2015-02-09 11:22:04 UTC
Permalink
Problem:
lsmd will keep printing out(info level) message below every 15 seconds:
'waitid 10 - No child processes'

Root cause:
In _serving() method, the select() call will timeout at tmo(15 seconds).
When timeout, actually not child process has been created.
This cause waitid() complain about '10 - No child processes'.

Fix:
Don't print out this message if the error of waitid() is ECHILD.

Signed-off-by: Gris Ge <***@redhat.com>
---
daemon/lsm_daemon.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/daemon/lsm_daemon.c b/daemon/lsm_daemon.c
index a60296d..1b8a769 100644
--- a/daemon/lsm_daemon.c
+++ b/daemon/lsm_daemon.c
@@ -463,7 +463,9 @@ void child_cleanup(void)

if( -1 == rc ) {
err = errno;
- info("waitid %d - %s\n", err, strerror(err));
+ if (err != ECHILD){
+ info("waitid %d - %s\n", err, strerror(err));
+ }
break;
} else {
if( 0 == rc && si.si_pid == 0 ) {
--
1.8.3.1
Gris Ge
2015-02-09 11:22:05 UTC
Permalink
* Show Disk.TYPE_SCSI as 'SCSI' instead of 'Unknown(8)'

Signed-off-by: Gris Ge <***@redhat.com>
---
tools/lsmcli/data_display.py | 1 +
1 file changed, 1 insertion(+)

diff --git a/tools/lsmcli/data_display.py b/tools/lsmcli/data_display.py
index 1d8f61e..285a14f 100644
--- a/tools/lsmcli/data_display.py
+++ b/tools/lsmcli/data_display.py
@@ -174,6 +174,7 @@ _DISK_TYPE_CONV = {
Disk.TYPE_SAS: 'SAS',
Disk.TYPE_FC: 'FC',
Disk.TYPE_SOP: 'SCSI Over PCI-E(SSD)',
+ Disk.TYPE_SCSI: 'SCSI',
Disk.TYPE_NL_SAS: 'NL_SAS',
Disk.TYPE_HDD: 'HDD',
Disk.TYPE_SSD: 'SSD',
--
1.8.3.1
Gris Ge
2015-02-09 11:22:06 UTC
Permalink
* This patch allow plugins running as root privilege if these conditions
are all meet:
1. "allow-plugin-root-privilege = true;" in /etc/lsm/lsmd.conf
# This allows admin to disable root privilege globally.

2. "require-root-privilege = true;" in plugin config:
/etc/lsm/pluginconf.d/<uri_scheme>.conf
# This allows plugin level config.

3. API connection(or lsmcli) is ran by root user.
# This is for security concern.

* Warning message will be printed or redirect to syslog(depend on lsmd
command line option '-d') by lsmd if any of above condition failed.
It's plugin's responsibility to provide proper error message to API client.

* If no plugin installed has 'require-root-privilege = true;' config line,
lsmd will drop the non-root privilege.

* Implementation:
1. Use libconfig 1.3.2+ to read config files.
(might works on older version, but only tested on RHEL 6)
2. Use getsockopt() to get API client uid.

* Autotools config file updated.

* RPM SPEC file updated.

* The default installation will install lsmd.conf with
"allow-plugin-root-privilege = true;".
With this, plugins could work out of box by installing correct config
into /etc/lsm/pluginconf.d/ folder.

* New manpage 'lsmd.conf(5)'.

* Sample plugin config "pluginconf.d/sim.conf" created with
"require-root-privilege = false;" as a demonstration.

* Updated systemd service file 'libstoragemgmt.service' by
changing running user from 'libstoragemgmt' to 'root'.

* Known bug:
1. Rebuild the source rpm will fail at test stage when run as root user
with libstoragemgmt user account.
Root cause:
As lsmd will try to drop privilege to libstoragemgmt user which
has no read access to the plugin directory.
Workaround:
A: Don't compile as root as everyone(except Slackware) suggests.
B: Remove libstoragemgmt user account.

Changes in V2:

* Fix memory leak in new method parse_conf_bool().
Since config_destroy() does not deallocate the config_t structure itself,
we have to do that free.
* Fix buffer outrun in chk_pconf_root_pri().

Signed-off-by: Gris Ge <***@redhat.com>
---
Makefile.am | 2 +-
config/Makefile.am | 8 ++
config/lsmd.conf | 1 +
config/pluginconf.d/sim.conf | 1 +
configure.ac | 10 ++
daemon/Makefile.am | 4 +-
daemon/lsm_daemon.c | 230 +++++++++++++++++++++++++++-----
doc/man/Makefile.am | 1 +
doc/man/lsmd.conf.5.in | 57 ++++++++
packaging/daemon/libstoragemgmt.service | 2 +-
packaging/libstoragemgmt.spec.in | 5 +
11 files changed, 283 insertions(+), 38 deletions(-)
create mode 100644 config/Makefile.am
create mode 100644 config/lsmd.conf
create mode 100644 config/pluginconf.d/sim.conf
create mode 100644 doc/man/lsmd.conf.5.in

diff --git a/Makefile.am b/Makefile.am
index 99e7578..2452931 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@ ACLOCAL_AMFLAGS = -I m4

DISTCHECK_CONFIGURE_FLAGS = --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)

-SUBDIRS= c_binding python_binding plugin doc tools daemon packaging
+SUBDIRS= c_binding python_binding plugin doc tools daemon packaging config

if BUILD_C_UNIT
SUBDIRS += test
diff --git a/config/Makefile.am b/config/Makefile.am
new file mode 100644
index 0000000..71291ad
--- /dev/null
+++ b/config/Makefile.am
@@ -0,0 +1,8 @@
+lsmconfdir=$(sysconfdir)/lsm
+lsmconf_DATA=lsmd.conf
+
+EXTRA_DIST= lsmd.conf pluginconf.d/sim.conf
+
+pluginconfdir=$(sysconfdir)/lsm/pluginconf.d
+
+pluginconf_DATA=pluginconf.d/sim.conf
diff --git a/config/lsmd.conf b/config/lsmd.conf
new file mode 100644
index 0000000..7eb4335
--- /dev/null
+++ b/config/lsmd.conf
@@ -0,0 +1 @@
+allow-plugin-root-privilege = true;
diff --git a/config/pluginconf.d/sim.conf b/config/pluginconf.d/sim.conf
new file mode 100644
index 0000000..da915bf
--- /dev/null
+++ b/config/pluginconf.d/sim.conf
@@ -0,0 +1 @@
+require-root-privilege = false;
diff --git a/configure.ac b/configure.ac
index 1c27fa0..eeaa350 100644
--- a/configure.ac
+++ b/configure.ac
@@ -180,6 +180,14 @@ if test "x$with_rest_api" = "xyes"; then
fi
AM_CONDITIONAL([WITH_REST_API], [test "x$with_rest_api" = "xyes"])

+dnl ==========================================================================
+dnl Check for libconfig as it is needed for lsmd daemon
+dnl ==========================================================================
+PKG_CHECK_MODULES(
+ [LIBCONFIG], [libconfig >= 1.3.2],,
+ AC_MSG_ERROR([libconfig 1.3.2 or newer not found.])
+)
+
#Setup the unit directory for systemd stuff
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
@@ -201,10 +209,12 @@ AC_OUTPUT(libstoragemgmt.pc \
plugin/Makefile \
plugin/simc/Makefile \
daemon/Makefile \
+ config/Makefile \
doc/Makefile \
doc/man/lsmcli.1 \
doc/man/lsmd.1 \
doc/doxygen.conf \
+ doc/man/lsmd.conf.5 \
tools/Makefile \
tools/udev/Makefile \
tools/lsmcli/Makefile \
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 521055f..a9ef569 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -2,8 +2,8 @@ bin_PROGRAMS = lsmd

EXTRA_DIST=lsm_rest.c lsm_rest.h

-lsmd_LDFLAGS=-Wl,-z,relro,-z,now -pie
-lsmd_CFLAGS=-fPIE -DPIE
+lsmd_LDFLAGS=-Wl,-z,relro,-z,now -pie $(LIBCONFIG_LIBS)
+lsmd_CFLAGS=-fPIE -DPIE $(LIBCONFIG_CFLAGS)

lsmd_SOURCES = lsm_daemon.c

diff --git a/daemon/lsm_daemon.c b/daemon/lsm_daemon.c
index 1b8a769..7b737c4 100644
--- a/daemon/lsm_daemon.c
+++ b/daemon/lsm_daemon.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2014 Red Hat, Inc.
+ * Copyright (C) 2011-2015 Red Hat, Inc.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -17,10 +17,10 @@
* Author: tasleson
*/

+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
-#define _GNU_SOURCE
#include <string.h>
#include <ctype.h>
#include <signal.h>
@@ -43,11 +43,17 @@
#include <assert.h>
#include <grp.h>
#include <limits.h>
+#include <libconfig.h>

#define BASE_DIR "/var/run/lsm"
#define SOCKET_DIR BASE_DIR"/ipc"
#define PLUGIN_DIR "/usr/bin"
#define LSM_USER "libstoragemgmt"
+#define LSM_CONF_DIR "/etc/lsm/"
+#define LSM_PLUGIN_CONF_DIR_NAME "pluginconf.d"
+#define LSMD_CONF_FILE "lsmd.conf"
+#define LSM_CONF_ALLOW_ROOT_OPT_NAME "allow-plugin-root-privilege"
+#define LSM_CONF_REQUIRE_ROOT_OPT_NAME "require-root-privilege"

#define min(a,b) \
({ __typeof__ (a) _a = (a); \
@@ -64,19 +70,26 @@ int systemd = 0;

char *socket_dir = SOCKET_DIR;
char *plugin_dir = PLUGIN_DIR;
+char *conf_dir = LSM_CONF_DIR;

char plugin_extension[] = "_lsmplugin";

+char plugin_conf_extension[] = ".conf";
+
typedef enum { RUNNING, RESTART, EXIT } serve_type;
serve_type serve_state = RUNNING;

int plugin_mem_debug = 0;

+int allow_root_plugin = 0;
+int has_root_plugin = 0;
+
/**
* Each item in plugin list contains this information
*/
struct plugin {
char *file_path;
+ int require_root;
int fd;
LIST_ENTRY(plugin) pointers;
};
@@ -153,35 +166,33 @@ void drop_privileges(void)
int err = 0;
struct passwd *pw = NULL;

- if( !systemd ) {
- pw = getpwnam(LSM_USER);
- if( pw ) {
- if ( !geteuid() ) {
-
- if( -1 == setgid(pw->pw_gid) ) {
- err = errno;
- log_and_exit(
- "Unexpected error on setgid(errno %d)\n", err);
- }
+ pw = getpwnam(LSM_USER);
+ if( pw ) {
+ if ( !geteuid() ) {

- if( -1 == setgroups(1, &pw->pw_gid) ) {
- err = errno;
- log_and_exit(
- "Unexpected error on setgroups(errno %d)\n", err);
- }
+ if( -1 == setgid(pw->pw_gid) ) {
+ err = errno;
+ log_and_exit(
+ "Unexpected error on setgid(errno %d)\n", err);
+ }

- if( -1 == setuid(pw->pw_uid) ) {
- err = errno;
- log_and_exit(
- "Unexpected error on setuid(errno %d)\n", err);
- }
- } else if ( pw->pw_uid != getuid() ) {
- warn("Daemon not running as correct user\n");
+ if( -1 == setgroups(1, &pw->pw_gid) ) {
+ err = errno;
+ log_and_exit(
+ "Unexpected error on setgroups(errno %d)\n", err);
}
- } else {
- info("Warn: Missing %s user, running as existing user!\n",
- LSM_USER);
+
+ if( -1 == setuid(pw->pw_uid) ) {
+ err = errno;
+ log_and_exit(
+ "Unexpected error on setuid(errno %d)\n", err);
+ }
+ } else if ( pw->pw_uid != getuid() ) {
+ warn("Daemon not running as correct user\n");
}
+ } else {
+ info("Warn: Missing %s user, running as existing user!\n",
+ LSM_USER);
}
}

@@ -214,6 +225,8 @@ void usage(void)
printf(" --plugindir = The directory where the plugins are located\n");
printf(" --socketdir = The directory where the Unix domain sockets will "
"be created\n");
+ printf(" --confdir = The directory where the config files are "
+ "located\n");
printf(" -v = Verbose logging\n");
printf(" -d = new style daemon (systemd)\n");
}
@@ -410,6 +423,95 @@ void empty_plugin_list(struct plugin_list *list)
}

/**
+ * Parse config and seeking provided key name bool
+ * 1. Keep value untouched if file not exist
+ * 2. If file is not readable, abort via log_and_exit()
+ * 3. Keep value untouched if provided key not found
+ * 4. Abort via log_and_exit() if no enough memory.
+ * @param conf_path config file path
+ * @param key_name string, searching key
+ * @param value int, output, value of this config key
+ */
+
+void parse_conf_bool(char * conf_path, char *key_name, int *value)
+{
+ if( access(conf_path, F_OK) == -1 ) {
+ /* file not exist. */
+ return;
+ }
+ config_t *cfg = (config_t *)malloc(sizeof(config_t));
+ if (cfg){
+ config_init(cfg);
+ if (CONFIG_TRUE == config_read_file(cfg, conf_path)){
+ config_lookup_bool(cfg, key_name, value);
+ }else{
+ log_and_exit(
+ "configure %s parsing failed: %s at line %d\n",
+ conf_path, config_error_text(cfg), config_error_line(cfg));
+ }
+ } else{
+ log_and_exit(
+ "malloc failure while trying to allocate memory for config_t\n");
+ }
+
+ config_destroy(cfg);
+ free(cfg);
+}
+
+/**
+ * Load plugin config for root privilege setting.
+ * If config not found, return 0 for no root privilege required.
+ * @param plugin_path Full path of plugin
+ * @return 1 for require root privilege, 0 or not.
+ */
+
+int chk_pconf_root_pri(char *plugin_path)
+{
+ int require_root = 0;
+ char *base_name = basename(plugin_path);
+ ssize_t plugin_name_len =
+ strlen(base_name) - strlen(plugin_extension);
+ if ( plugin_name_len <= 0 ){
+ log_and_exit(
+ "Got invalid plugin full path %s\n", plugin_path);
+ }
+ ssize_t conf_file_name_len = plugin_name_len +
+ strlen(plugin_conf_extension) + 1;
+ char *plugin_conf_filename =
+ (char *)malloc(conf_file_name_len);
+ if (plugin_conf_filename){
+ strncpy(
+ plugin_conf_filename, base_name, plugin_name_len);
+ strncpy(
+ plugin_conf_filename+plugin_name_len,
+ plugin_conf_extension, strlen(plugin_conf_extension));
+ plugin_conf_filename[conf_file_name_len-1] = '\0';
+ }else{
+ log_and_exit(
+ "malloc failure while trying to allocate %d "
+ "bytes\n", conf_file_name_len);
+ }
+ char *plugin_conf_dir_path =
+ path_form(conf_dir, LSM_PLUGIN_CONF_DIR_NAME);
+
+ char *plugin_conf_path = path_form(
+ plugin_conf_dir_path, plugin_conf_filename);
+ parse_conf_bool(
+ plugin_conf_path, LSM_CONF_REQUIRE_ROOT_OPT_NAME,
+ &require_root);
+
+ if (require_root == 1 && allow_root_plugin == 0){
+ warn(
+ "Plugin %s require root privilege while %s disable globally\n",
+ base_name, LSMD_CONF_FILE);
+ }
+ free(plugin_conf_dir_path);
+ free(plugin_conf_filename);
+ free(plugin_conf_path);
+ return require_root;
+}
+
+/**
* Call back for plug-in processing.
* @param p Private data
* @param full_name Full path and file name
@@ -427,6 +529,8 @@ int process_plugin(void *p, char *full_name)
if( item ) {
item->file_path = strdup(full_name);
item->fd = setup_socket(full_name);
+ item->require_root = chk_pconf_root_pri(full_name);
+ has_root_plugin |= item->require_root;

if( item->file_path && item->fd >= 0 ) {
LIST_INSERT_HEAD((struct plugin_list*)p, item, pointers);
@@ -498,20 +602,25 @@ int process_plugins(void)
clean_up();
info("Scanning plug-in directory %s\n", plugin_dir);
process_directory(plugin_dir, &head, process_plugin);
+ if ( allow_root_plugin == 1 && has_root_plugin == 0 ){
+ info("No plugin requires root privilege, dropping root privilege\n");
+ flight_check();
+ drop_privileges();
+ }
return 0;
}

/**
* Given a socket descriptor looks it up and returns the plug-in
* @param fd Socket descriptor to lookup
- * @return Character string
+ * @return struct plugin
*/
-char *plugin_lookup(int fd)
+struct plugin *plugin_lookup(int fd)
{
struct plugin *plug = NULL;
LIST_FOREACH(plug, &head, pointers) {
if( plug->fd == fd ) {
- return plug->file_path;
+ return plug;
}
}
return NULL;
@@ -521,8 +630,10 @@ char *plugin_lookup(int fd)
* Does the actual fork and exec of the plug-in
* @param plugin Full filename and path of plug-in to exec.
* @param client_fd Client connected file descriptor
+ * @param require_root int, indicate whether this plugin require root
+ * privilege or not
*/
-void exec_plugin( char *plugin, int client_fd )
+void exec_plugin( char *plugin, int client_fd, int require_root)
{
int err = 0;

@@ -544,6 +655,45 @@ void exec_plugin( char *plugin, int client_fd )
char fd_str[12];
char *plugin_argv[7];
extern char **environ;
+ struct ucred cli_user_cred;
+ socklen_t cli_user_cred_len = sizeof(cli_user_cred);
+
+ /*
+ * The plugin will still run no matter with root privilege or not.
+ * so that client could get detailed error message.
+ */
+ if ( require_root == 0 ){
+ drop_privileges();
+ }else{
+ if (getuid()){
+ warn("Plugin %s require root privilege, but lsmd daemon "
+ "is not run as root user\n", plugin);
+ }else if(allow_root_plugin == 0){
+ warn("Plugin %s require root privilege, but %s disabled "
+ "it globally\n", LSMD_CONF_FILE);
+ drop_privileges();
+ }else{
+ /* Check socket client uid */
+ int rc_get_cli_uid = getsockopt(
+ client_fd, SOL_SOCKET, SO_PEERCRED,
+ &cli_user_cred, &cli_user_cred_len);
+ if( 0 == rc_get_cli_uid){
+ if (cli_user_cred.uid != 0){
+ warn("Plugin %s require root privilege, but "
+ "client is not run as root user\n", plugin);
+ drop_privileges();
+ }else{
+ info("Plugin %s is running as root privilege\n",
+ plugin);
+ }
+ }else{
+ warn("Failed to get client socket uid, getsockopt() "
+ "error: %d\n", errno);
+ drop_privileges();
+ }
+ }
+
+ }

/* Make copy of plug-in string as once we call empty_plugin_list it
* will be deleted :-) */
@@ -626,8 +776,8 @@ void _serving(void)
if( FD_ISSET(fd, &readfds) ) {
int cfd = accept(fd, NULL, NULL);
if( -1 != cfd ) {
- char *p = plugin_lookup(fd);
- exec_plugin(p, cfd);
+ struct plugin *p = plugin_lookup(fd);
+ exec_plugin(p->file_path, cfd, p->require_root);
} else {
err = errno;
info("Error on accepting request: %s", strerror(err));
@@ -668,6 +818,7 @@ int main(int argc, char *argv[])
{"help", no_argument, 0, 'h'}, //Index 0
{"plugindir", required_argument, 0, 0}, //Index 1
{"socketdir", required_argument, 0, 0}, //Index 2
+ {"confdir", required_argument, 0, 0}, //Index 3
{0, 0, 0, 0}
};

@@ -687,6 +838,9 @@ int main(int argc, char *argv[])
case 2:
socket_dir = optarg;
break;
+ case 3:
+ conf_dir = optarg;
+ break;
}
break;

@@ -725,13 +879,21 @@ int main(int argc, char *argv[])
openlog("lsmd", LOG_ODELAY, LOG_USER);
}

+ /* Check lsmd.conf */
+ char *lsmd_conf_path = path_form(conf_dir, LSMD_CONF_FILE);
+ parse_conf_bool(
+ lsmd_conf_path, LSM_CONF_ALLOW_ROOT_OPT_NAME, &allow_root_plugin);
+ free(lsmd_conf_path);
+
/* Check to see if we want to check plugin for memory errors */
if( getenv("LSM_VALGRIND") ) {
plugin_mem_debug = 1;
}

install_sh();
- drop_privileges();
+ if (allow_root_plugin == 0){
+ drop_privileges();
+ }
flight_check();

/* Become a daemon if told we are not using systemd */
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 17abcee..18d8663 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -1,2 +1,3 @@
notrans_dist_man1_MANS = lsmcli.1 lsmd.1

+notrans_dist_man5_MANS = lsmd.conf.5.in
diff --git a/doc/man/lsmd.conf.5.in b/doc/man/lsmd.conf.5.in
new file mode 100644
index 0000000..7b1d6f4
--- /dev/null
+++ b/doc/man/lsmd.conf.5.in
@@ -0,0 +1,57 @@
+.TH lsmd.conf "5" "January 2015" "lsmd.conf @VERSION@" "libStorageMgmt daemon config"
+.SH NAME
+lsmd.conf - libStorageMgmt daemon lsmd configuration file.
+
+.SH DESCRIPTION
+The libStorageMgmt plugin-in daemon(\fBlsmd\fR) will read \fBlsmd.conf\fR
+file in the folder defined via \fB--confdir\fR(default is \fB/etc/lsm/\fR).
+
+The \fBlsmd\fR also read plugin configuration files from sub-folder
+\fBpluginconf.d\fR. The plugin configuration file should be named as
+<uri_scheme>.conf. For example:
+
+ * Linux plugin(\fBlinux\fR://): \fBlinux.conf\fR
+ * MegaRAID plugin(\fBmegaraid\fR://): \fBmegaraid.conf\fR
+
+The \fBlsmd.conf\fR controls the global settings for \fBlsmd\fR while
+plugin configuration file controls plugin behaviour.
+
+Each option line of configuration file should contain tailing
+semicolon(\fB;\fR).
+
+.SH lsmd.conf OPTIONS
+.TP
+\fBallow-plugin-root-privilege = true;\fR
+
+Indicate whether \fBlsmd\fR daemon should keep running in root mode and invoke
+plugin as root user when needed.
+
+Without this option or set as \fBfalse\fR means never run lsmd and its plugins
+as root user.
+
+Only when all the following requirements meet, will the \fBlsmd\fR run certain
+plugin as root user:
+
+ 1. "allow-plugin-root-privilege = true;" in lsmd.conf
+ 2. "require-root-privilege = true;" in plugin config.
+ 3. API connection(or lsmcli) is ran by root user.
+
+.SH Plugin OPTIONS
+.TP
+\fBrequire-root-privilege = true;\fR
+
+Indicate plugin requires root privilege.
+Without this line or set as \fBfalse\fR, the plugin will never invoked as root
+user by \fBlsmd\fR.
+
+Please check \fBlsmd.conf\fR option \fBallow-plugin-root-privilege\fR for
+detail.
+
+.SH SEE ALSO
+\fIlsmd (1)\fR
+
+.SH BUGS
+Please report bugs to
+<libstoragemgmt\-***@lists.sourceforge.net>
+.SH AUTHOR
+Gris Ge <***@redhat.com>
diff --git a/packaging/daemon/libstoragemgmt.service b/packaging/daemon/libstoragemgmt.service
index e0dcf98..81a69ae 100644
--- a/packaging/daemon/libstoragemgmt.service
+++ b/packaging/daemon/libstoragemgmt.service
@@ -6,7 +6,7 @@ After=syslog.target
ExecStart=/usr/bin/lsmd -d
ExecReload=/bin/kill -HUP $MAINPID
StandardError=syslog
-User=libstoragemgmt
+User=root

[Install]
WantedBy=multi-user.target
diff --git a/packaging/libstoragemgmt.spec.in b/packaging/libstoragemgmt.spec.in
index d787e72..a79bff5 100644
--- a/packaging/libstoragemgmt.spec.in
+++ b/packaging/libstoragemgmt.spec.in
@@ -50,6 +50,7 @@ BuildRequires: python-argparse
BuildRequires: glib2-devel
# Explicitly require gcc-c++ is for OBS
BuildRequires: gcc-c++
+BuildRequires: libconfig-devel
%if 0%{?suse_version}
BuildRequires: libyajl-devel
%else
@@ -417,10 +418,13 @@ fi
%doc README COPYING.LIB
%{_mandir}/man1/lsmcli.1*
%{_mandir}/man1/lsmd.1*
+%{_mandir}/man5/lsmd.conf.5*
%{_libdir}/*.so.*
%{_bindir}/lsmcli
%{_bindir}/lsmd
%{_bindir}/simc_lsmplugin
+%{_sysconfdir}/lsm/lsmd.conf
+%dir %{_sysconfdir}/lsm/pluginconf.d

%if 0%{?with_systemd}
%{_unitdir}/libstoragemgmt.service
@@ -463,6 +467,7 @@ fi
%{python_sitelib}/lsm/lsmcli/data_display.*
%{python_sitelib}/lsm/lsmcli/cmdline.*
%{_bindir}/sim_lsmplugin
+%{_sysconfdir}/lsm/pluginconf.d/sim.conf

%files -n %{libstoragemgmt}-smis-plugin
%defattr(-,root,root,-)
--
1.8.3.1
Gris Ge
2015-02-09 11:22:07 UTC
Permalink
* 'sudo' is required to run 'lsmenv lsmd' command for these reason:
1. Support plugin run as root privilege feature.
2. Create libstoragemgmt user and grant it with read and execute access
to current code tree.

* With this change, please link your '.lsm_uri' file to '/root' folder.
So that 'sudo lsmenv mega lsmcli ls' can read out URI settings.
Try this command:

sudo ln -s ~/.lsm_uri /root/

Changes in V2:

* Allowing 'lsmenv lsmdv' to start lsmd with valgrind and ouput valgrind
report to "/tmp/lsmd_leaking_<pid_id>".

Signed-off-by: Gris Ge <***@redhat.com>
---
tools/lsmenv | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 72 insertions(+), 7 deletions(-)

diff --git a/tools/lsmenv b/tools/lsmenv
index da14eed..54bf0da 100755
--- a/tools/lsmenv
+++ b/tools/lsmenv
@@ -20,13 +20,16 @@ use strict;
use Config::IniFiles;
use File::Basename;
use Cwd 'abs_path';
+use Fcntl ':mode';

$|++;

-my $LSM_UDS_PATH = '/tmp/lsm/ipc/';
-my $BASE_DIR; # Used to store the path of libstoragemgmt-code path.
+my $LSM_UDS_PATH = '/var/run/lsm/ipc/';
+my $BASE_DIR = dirname( dirname( abs_path($0) ) );
my $LSM_URI_FILE = "$ENV{HOME}/.lsm_uri";
my $URI_CFG = Config::IniFiles->new( -file => $LSM_URI_FILE );
+my $LSM_USER = 'libstoragemgmt';
+my $LSM_GROUP = 'libstoragemgmt';

my $REF_PRE_BUILD_URI = {
"sim" => "sim://",
@@ -77,8 +80,51 @@ sub is_in_array($$) {
return undef;
}

+sub chk_lsm_user(){
+ my $code_gid = (stat $BASE_DIR)[5];
+ my $code_group_name = getgrgid($code_gid);
+ my $lsm_gid = (getpwnam("libstoragemgmt"))[3];
+ if ($lsm_gid and $lsm_gid != $code_gid){
+ print "FAIL: Existing $LSM_USER account is not sharing gid of " .
+ "curren code tree which might cause plugin permission problem" .
+ "\n";
+ print " Please delete that user $LSM_USER " .
+ "and rerun lsmenv as root user\n";
+ exit 1;
+ }
+ unless($lsm_gid){
+ # LSM_USER not exist.
+ if ($> != 0){
+ print "FAIL: Please run lsmenv as root user to create a proper " .
+ "$LSM_USER account\n";
+ exit 1;
+ }
+ print "WARN: Creating libstoragemgmt account and add it into " .
+ "the group '$code_group_name($code_gid)' of code tree owner. \n";
+
+ system("useradd -r -g $code_gid -d $LSM_UDS_PATH " .
+ "-s /sbin/nologin " .
+ "-c 'daemon account for libstoragemgmt' libstoragemgmt");
+ # Check whether code tree $BASE_DIR is read and executable by LSM_USER
+ # User are on their own if they changed file permission inside git
+ # tree.
+ my $cur_path = "$BASE_DIR/a";
+ while(1){
+ $cur_path = dirname($cur_path);
+ print $cur_path, "\n";
+ my $mod = (stat $cur_path)[2];
+ unless(( $mod & S_IRGRP) and ( $mod & S_IXGRP )){
+ print "WARN: Adding $cur_path with group read and " .
+ "excute permision\n";
+ system("chmod g+rx $cur_path");
+ }
+ last if ($cur_path eq '/');
+ }
+ }
+ return 1;
+}
+
sub lsm_env_setup() {
- $BASE_DIR = dirname( dirname( abs_path($0) ) );
my $py_binding_dir = "$BASE_DIR/python_binding";
my $c_binding_dir = "$BASE_DIR/c_binding";
unless ( ( -e $py_binding_dir ) && ( -e $c_binding_dir ) ) {
@@ -98,6 +144,7 @@ sub lsm_env_setup() {
unless ( -d "$LSM_UDS_PATH" ) {
print "INFO: Creating socket folder $LSM_UDS_PATH\n";
system("mkdir -pv $LSM_UDS_PATH");
+ system("chown $LSM_USER $LSM_UDS_PATH");
}

$ENV{LSM_UDS_PATH} = $LSM_UDS_PATH;
@@ -170,7 +217,12 @@ sub call_out($) {
}
}

-sub start_lsmd() {
+sub start_lsmd($) {
+ my $flag_valgrind = shift;
+ if ($> != 0){
+ print "FAIL: Please run lsmenv as root to start lsmd\n";
+ exit 1;
+ }
my $lsmd_exec_file = "$BASE_DIR/daemon/lsmd";
unless ( -f $lsmd_exec_file ) {
print "FAIL: $lsmd_exec_file not exist, "
@@ -182,9 +234,17 @@ sub start_lsmd() {
. "please run 'make' in '$BASE_DIR' folder again\n";
exit 1;
}
+ my $cmd = '';
+ if ($flag_valgrind){
+ $cmd .= "valgrind --trace-children=no ";
+ $cmd .= "--leak-check=full --show-reachable=no ";
+ $cmd .= '--log-file=/tmp/lsmd_leaking_%p ';
+ }
+ $cmd .= $lsmd_exec_file;
my $plugin_dir = "$BASE_DIR/plugin";
- system( "$lsmd_exec_file --plugindir $plugin_dir "
- . "--socketdir $LSM_UDS_PATH -v -d" );
+ my $conf_dir = "$BASE_DIR/config";
+ system( "$cmd --plugindir $plugin_dir "
+ . "--socketdir $LSM_UDS_PATH -v -d --confdir $conf_dir" );
}

sub main() {
@@ -192,9 +252,14 @@ sub main() {
help();
}

+ chk_lsm_user();
lsm_env_setup();
if ( $ARGV[0] eq 'lsmd' ) {
- start_lsmd();
+ start_lsmd(0);
+ exit 0;
+ }
+ elsif ( $ARGV[0] eq 'lsmdv' ) {
+ start_lsmd(1);
exit 0;
}
elsif ( $ARGV[0] eq '-l' ) {
--
1.8.3.1
Gris Ge
2015-02-09 11:22:08 UTC
Permalink
* Using storcli(LSI binary tool).

* Currently only got 'systems()' and 'disks()' supported.

* This plugin require root privilege, plugin config file included.

* Autotools files updated.

* RPM SPEC updated with new sub package:
libstoragemgmt-megaraid-plugin (RHEL/Fedora/Centos)
or
libstoragemgmt1-megaraid-plugin (OpenSuSE)
* The URI for this plugin is:
megaraid://
or
megaraid://?storcli=<path_of_storcli>

* If SELinux enabled, please check SELinux logs to grant permission for this.
For exmaple:
grep storcli64 /var/log/audit/audit.log | audit2allow -M mypol
semodule -i mypol.pp

* Added new manpage: megaraid_lsmplugin(1).

* Tested on RHEL6 and RHEL7 for SAS and SATA disks.

Changes in V2:

* Update manpage megaraid_lsmplugin(1):
1. Fix incorrect spells.
2. Indicate SELinux might stop plugin accessing the hardware.

* Include disk ID in Disk.name. The disk ID is required to query SMART
information via command:
smartctl -a -d megaraid,10 /dev/sda
^^--> disk ID

* Improve disk type detecting base LSI engineer's feedback.

* Fix typo excutable -> executable.

* Add 'INVALID_ARGUMENT' error when plugin was not run as root.

* Change base class to inherit from IPlugin.

* Implement(as not supported) the abstract method defined by IPlugin:
* job_free
* job_status
* pools
* time_out_get
* time_out_set

* Add '--without-megaraid' option to configure to skip installing the
MegaRAID plugin.

* Add '--without megaraid' option to rpm SPEC to skip building the
MegaRAID plugin rpm.

* Change rpm package for megaraid plugin as noarch.

Signed-off-by: Gris Ge <***@redhat.com>
---
config/Makefile.am | 5 +
config/pluginconf.d/megaraid.conf | 1 +
configure.ac | 14 ++
doc/man/Makefile.am | 4 +
doc/man/megaraid_lsmplugin.1.in | 52 ++++++
packaging/libstoragemgmt.spec.in | 42 +++++
plugin/Makefile.am | 2 +-
plugin/megaraid/Makefile.am | 8 +
plugin/megaraid/__init__.py | 1 +
plugin/megaraid/megaraid.py | 323 +++++++++++++++++++++++++++++++++++++
plugin/megaraid/megaraid_lsmplugin | 37 +++++
plugin/megaraid/utils.py | 47 ++++++
12 files changed, 535 insertions(+), 1 deletion(-)
create mode 100644 config/pluginconf.d/megaraid.conf
create mode 100644 doc/man/megaraid_lsmplugin.1.in
create mode 100644 plugin/megaraid/Makefile.am
create mode 100644 plugin/megaraid/__init__.py
create mode 100644 plugin/megaraid/megaraid.py
create mode 100755 plugin/megaraid/megaraid_lsmplugin
create mode 100644 plugin/megaraid/utils.py

diff --git a/config/Makefile.am b/config/Makefile.am
index 71291ad..70ab97b 100644
--- a/config/Makefile.am
+++ b/config/Makefile.am
@@ -6,3 +6,8 @@ EXTRA_DIST= lsmd.conf pluginconf.d/sim.conf
pluginconfdir=$(sysconfdir)/lsm/pluginconf.d

pluginconf_DATA=pluginconf.d/sim.conf
+
+if WITH_MEGARAID
+pluginconf_DATA += pluginconf.d/megaraid.conf
+EXTRA_DIST += pluginconf.d/megaraid.conf
+endif
diff --git a/config/pluginconf.d/megaraid.conf b/config/pluginconf.d/megaraid.conf
new file mode 100644
index 0000000..35b52d4
--- /dev/null
+++ b/config/pluginconf.d/megaraid.conf
@@ -0,0 +1 @@
+require-root-privilege = true;
diff --git a/configure.ac b/configure.ac
index eeaa350..74e6811 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,6 +181,18 @@ fi
AM_CONDITIONAL([WITH_REST_API], [test "x$with_rest_api" = "xyes"])

dnl ==========================================================================
+dnl Add option '--without-megaraid' to exclude megaraid plugin.
+dnl ==========================================================================
+
+AC_ARG_WITH([megaraid],
+ [AS_HELP_STRING([--without-megaraid],
+ [Do not build the MegaRAID plugin])],
+ [],
+ [with_megaraid=yes])
+
+AM_CONDITIONAL([WITH_MEGARAID], [test "x$with_megaraid" = "xyes"])
+
+dnl ==========================================================================
dnl Check for libconfig as it is needed for lsmd daemon
dnl ==========================================================================
PKG_CHECK_MODULES(
@@ -208,6 +220,7 @@ AC_OUTPUT(libstoragemgmt.pc \
python_binding/lsm/version.py \
plugin/Makefile \
plugin/simc/Makefile \
+ plugin/megaraid/Makefile \
daemon/Makefile \
config/Makefile \
doc/Makefile \
@@ -215,6 +228,7 @@ AC_OUTPUT(libstoragemgmt.pc \
doc/man/lsmd.1 \
doc/doxygen.conf \
doc/man/lsmd.conf.5 \
+ doc/man/megaraid_lsmplugin.1 \
tools/Makefile \
tools/udev/Makefile \
tools/lsmcli/Makefile \
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 18d8663..ccbcb38 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -1,3 +1,7 @@
notrans_dist_man1_MANS = lsmcli.1 lsmd.1

+if WITH_MEGARAID
+notrans_dist_man1_MANS += megaraid_lsmplugin.1
+endif
+
notrans_dist_man5_MANS = lsmd.conf.5.in
diff --git a/doc/man/megaraid_lsmplugin.1.in b/doc/man/megaraid_lsmplugin.1.in
new file mode 100644
index 0000000..27749b6
--- /dev/null
+++ b/doc/man/megaraid_lsmplugin.1.in
@@ -0,0 +1,52 @@
+.TH megaraid_lsmplugin "1" "Januray 2015" "megaraid_lsmplugin @VERSION@" "libStorageMgmt"
+.SH NAME
+megaraid_lsmplugin -- LibstorageMgmt MegaRAID plugin
+
+.SH DESCRIPTION
+LibstorageMgmt megaraid plugin allows user to manage LSI MegaRAID via vendor
+tool \fBstorcli\fR[1].
+The 'megaraid_lsmplugin' executable file is for libStorageMgmt
+daemon to execute when client user specifies megaraid plugin in the URI.
+
+Extra SELinux actions are required to allowing plugin access the hardware.
+
+.SH URI
+To use this plugin, users should set their URI to this format:
+.nf
+
+ \fBmegaraid://\fR
+ or
+ \fBmegaraid://?storcli=<path_of_storcli>\fR
+
+.fi
+
+.TP storcli
+The 'storcli' URI parameter is used to specified the path of storcli tool.
+By default, this plugin will try these paths used by storcli rpm:
+\fB/opt/MegaRAID/storcli/storcli64\fR and \fB/opt/MegaRAID/storcli/storcli\fR.
+
+.SH ROOT PRIVILEGE
+This plugin requires both \fBlsmd\fR daemon and API client running as root
+user. Please check manpage \fIlsmd.conf (5)\fR for detail.
+
+.SH SUPPORTED HARDWARES
+Please refer to LSI website for hardware support status of storcli.
+Detailed support status can be queried via:
+
+ * \fBlsm.Client.capabilities()\fR (Python API)
+ * \fBlsm_capabilities()\fR (C API)
+ * \fBlsmcli capabilities\fR (lsmcli command line).
+
+.SH FIREWALL RULES
+This plugin only execute \fBstorcli\fR on localhost. No network connection
+required.
+
+.SH SEE ALSO
+\fIlsmcli\fR(1), \fIlsmd\fR(1), [1] http://www.lsi.com
+
+.SH BUGS
+Please report bugs to
+\fI<libstoragemgmt-***@lists.sourceforge.net>\fR
+
+.SH AUTHOR
+Gris Ge \fI<***@redhat.com>\fR
diff --git a/packaging/libstoragemgmt.spec.in b/packaging/libstoragemgmt.spec.in
index a79bff5..34ef3f2 100644
--- a/packaging/libstoragemgmt.spec.in
+++ b/packaging/libstoragemgmt.spec.in
@@ -1,10 +1,14 @@
%bcond_with rest_api
+%bcond_without megaraid

# Use one-line macro for OBS workaround:
# https://bugzilla.novell.com/show_bug.cgi?id=864323
%{?_with_rest_api: %global with_rest_api 1 }
%{?_without_rest_api: %global with_rest_api 0 }

+%{?_with_megaraid: %global with_megaraid 1 }
+%{?_without_megaraid: %global with_megaraid 0 }
+
%define libsoname libstoragemgmt

%if 0%{?suse_version} || 0%{?fedora} >= 15 || 0%{?rhel} >= 7
@@ -207,6 +211,20 @@ the %{libstoragemgmt}-rest package contains the http daemon for
%{libstoragemgmt} rest api.
%endif

+
+%if 0%{?with_megaraid}
+%package -n %{libstoragemgmt}-megaraid-plugin
+Summary: Files for LSI MegaRAID support for %{libstoragemgmt}
+Group: System Environment/Libraries
+Requires: %{libstoragemgmt}%{?_isa} = %{version}-%{release}
+BuildArch: noarch
+
+%description -n %{libstoragemgmt}-megaraid-plugin
+The %{libstoragemgmt}-megaraid-plugin package contains the plugin for LSI
+MegaRAID storage management via storcli.
+%endif
+
+
%prep
%setup -q

@@ -217,6 +235,9 @@ the %{libstoragemgmt}-rest package contains the http daemon for
%if 0%{?with_rest_api} != 1
--without-rest-api \
%endif
+%if 0%{?with_megaraid} != 1
+ --without-megaraid \
+%endif
--disable-static

V=1 make %{?_smp_mflags}
@@ -413,6 +434,15 @@ if [ $1 -eq 0 ]; then
fi

+%if 0%{?with_megaraid}
+%postun -n %{libstoragemgmt}-megaraid-plugin
+if [ $1 -eq 0 ]; then
+ # Remove
+ /usr/bin/systemctl try-restart libstoragemgmt.service \
+ >/dev/null 2>&1 || :
+fi
+%endif
+
%files -n %{libstoragemgmt}
%defattr(-,root,root,-)
%doc README COPYING.LIB
@@ -507,6 +537,18 @@ fi
%{python_sitelib}/lsm/plugin/nstor/nstor.*
%{_bindir}/nstor_lsmplugin

+%if 0%{?with_megaraid}
+%files -n %{libstoragemgmt}-megaraid-plugin
+%defattr(-,root,root,-)
+%dir %{python_sitelib}/lsm/plugin/megaraid
+%{python_sitelib}/lsm/plugin/megaraid/__init__.*
+%{python_sitelib}/lsm/plugin/megaraid/megaraid.*
+%{python_sitelib}/lsm/plugin/megaraid/utils.*
+%{_bindir}/megaraid_lsmplugin
+%{_sysconfdir}/lsm/pluginconf.d/megaraid.conf
+%{_mandir}/man1/megaraid_lsmplugin.1*
+%endif
+
%files udev
%defattr(-,root,root,-)
%{udev_dir}/udev/scan-scsi-target
diff --git a/plugin/Makefile.am b/plugin/Makefile.am
index 78ceb36..f9146a8 100644
--- a/plugin/Makefile.am
+++ b/plugin/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS=simc
+SUBDIRS=simc megaraid

plugindir = $(pythondir)/lsm/plugin

diff --git a/plugin/megaraid/Makefile.am b/plugin/megaraid/Makefile.am
new file mode 100644
index 0000000..a4b6b1c
--- /dev/null
+++ b/plugin/megaraid/Makefile.am
@@ -0,0 +1,8 @@
+if WITH_MEGARAID
+plugindir = $(pythondir)/lsm/plugin
+megaraiddir = $(plugindir)/megaraid
+
+megaraid_PYTHON = __init__.py megaraid.py utils.py
+
+dist_bin_SCRIPTS= megaraid_lsmplugin
+endif
diff --git a/plugin/megaraid/__init__.py b/plugin/megaraid/__init__.py
new file mode 100644
index 0000000..8f4602c
--- /dev/null
+++ b/plugin/megaraid/__init__.py
@@ -0,0 +1 @@
+from lsm.plugin.megaraid.megaraid import MegaRAID
diff --git a/plugin/megaraid/megaraid.py b/plugin/megaraid/megaraid.py
new file mode 100644
index 0000000..e1e7e8d
--- /dev/null
+++ b/plugin/megaraid/megaraid.py
@@ -0,0 +1,323 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <***@redhat.com>
+
+import os
+import json
+from string import strip
+import re
+import errno
+
+from lsm import (uri_parse, search_property, size_human_2_size_bytes,
+ Capabilities, LsmError, ErrorNumber, System, Client,
+ Disk, VERSION, search_property, IPlugin)
+
+from lsm.plugin.megaraid.utils import cmd_exec, ExecError
+
+_FLAG_RSVD = Client.FLAG_RSVD
+
+
+# Naming scheme
+# mega_sys_path /c0
+# mega_disk_path /c0/e64/s0
+
+
+def _handle_errors(method):
+ def _wrapper(*args, **kwargs):
+ try:
+ return method(*args, **kwargs)
+ except LsmError:
+ raise
+ except KeyError as key_error:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Expected key missing from MegaRAID storcli output:%s" %
+ key_error)
+ except ExecError as exec_error:
+ raise LsmError(ErrorNumber.PLUGIN_BUG, str(exec_error))
+ except Exception as common_error:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "Got unexpected error %s" % common_error)
+
+ return _wrapper
+
+
+def _blk_count_of(mega_disk_size):
+ blk_count_regex = re.compile("(0x[0-9a-f]+) Sectors")
+ blk_count_search = blk_count_regex.search(mega_disk_size)
+ if blk_count_search:
+ return int(blk_count_search.group(1), 16)
+ return Disk.BLOCK_COUNT_NOT_FOUND
+
+
+def _disk_type_of(disk_show_basic_dict):
+ """
+ Return the 'Drive /c0/e64/s0' entry of '/c0/e64/s0 show all'
+ """
+ disk_media = disk_show_basic_dict['Med']
+ disk_interface = disk_show_basic_dict['Intf']
+ if disk_media == 'HDD':
+ if disk_interface == 'SATA':
+ return Disk.TYPE_SATA
+ elif disk_interface == 'SAS':
+ return Disk.TYPE_SAS
+ elif disk_interface == 'Parallel SCSI':
+ return Disk.TYPE_SCSI
+ elif disk_interface == 'FC':
+ return Disk.TYPE_FC
+ else:
+ return Disk.TYPE_HDD
+ elif disk_media == 'SSD':
+ return Disk.TYPE_SSD
+
+ return Disk.TYPE_UNKNOWN
+
+_DISK_STATE_MAP = {
+ 'Onln': Disk.STATUS_OK,
+ 'Offln': Disk.STATUS_ERROR,
+ 'GHS': Disk.STATUS_SPARE_DISK | Disk.STATUS_OK,
+ 'DHS': Disk.STATUS_SPARE_DISK | Disk.STATUS_OK,
+ 'UGood': Disk.STATUS_STOPPED | Disk.STATUS_OK,
+ 'UBad': Disk.STATUS_STOPPED | Disk.STATUS_ERROR,
+}
+
+
+def _disk_status_of(disk_show_basic_dict, disk_show_stat_dict):
+ if disk_show_stat_dict['Media Error Count'] or \
+ disk_show_stat_dict['Other Error Count'] or \
+ disk_show_stat_dict['S.M.A.R.T alert flagged by drive'] != 'No':
+ return Disk.STATUS_ERROR
+
+ if disk_show_stat_dict['Predictive Failure Count']:
+ return Disk.STATUS_PREDICTIVE_FAILURE
+
+ if disk_show_basic_dict['Sp'] == 'D':
+ return Disk.STATUS_STOPPED
+
+ if disk_show_basic_dict['Sp'] == 'F':
+ return Disk.STATUS_OTHER
+
+ return _DISK_STATE_MAP.get(
+ disk_show_basic_dict['State'], Disk.STATUS_UNKNOWN)
+
+
+class MegaRAID(IPlugin):
+ _DEFAULT_MDADM_BIN_PATHS = [
+ "/opt/MegaRAID/storcli/storcli64", "/opt/MegaRAID/storcli/storcli"]
+ _CMD_JSON_OUTPUT_SWITCH = 'J'
+
+ def __init__(self):
+ self._storcli_bin = None
+
+ def _find_storcli(self):
+ """
+ Try _DEFAULT_MDADM_BIN_PATHS
+ """
+ for cur_path in MegaRAID._DEFAULT_MDADM_BIN_PATHS:
+ if os.path.lexists(cur_path):
+ self._storcli_bin = cur_path
+
+ if not self._storcli_bin:
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "MegaRAID storcli is not installed correctly")
+
+ @_handle_errors
+ def plugin_register(self, uri, password, timeout, flags=Client.FLAG_RSVD):
+ if os.geteuid() != 0:
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "This plugin requires root privilege both daemon and client")
+ uri_parsed = uri_parse(uri)
+ self._storcli_bin = uri_parsed.get('parameters', {}).get('storcli')
+ if not self._storcli_bin:
+ self._find_storcli()
+
+ # change working dir to "/tmp" as storcli will create a log file
+ # named as 'MegaSAS.log'.
+ os.chdir("/tmp")
+ self._storcli_exec(['-v'], flag_json=False)
+
+ @_handle_errors
+ def plugin_unregister(self, flags=Client.FLAG_RSVD):
+ pass
+
+ @_handle_errors
+ def job_status(self, job_id, flags=Client.FLAG_RSVD):
+ raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported yet")
+
+ @_handle_errors
+ def job_free(self, job_id, flags=Client.FLAG_RSVD):
+ pass
+
+ @_handle_errors
+ def plugin_info(self, flags=Client.FLAG_RSVD):
+ return "LSI MegaRAID Plugin", VERSION
+
+ @_handle_errors
+ def time_out_set(self, ms, flags=Client.FLAG_RSVD):
+ raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported yet")
+
+ @_handle_errors
+ def time_out_get(self, flags=Client.FLAG_RSVD):
+ raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported yet")
+
+ @_handle_errors
+ def capabilities(self, system, flags=_FLAG_RSVD):
+ cur_lsm_syss = self.systems()
+ if system.id not in list(s.id for s in cur_lsm_syss):
+ raise LsmError(
+ ErrorNumber.NOT_FOUND_SYSTEM,
+ "System not found")
+ cap = Capabilities()
+ cap.set(Capabilities.DISKS)
+ return cap
+
+ def _storcli_exec(self, storcli_cmds, flag_json=True):
+ storcli_cmds.insert(0, self._storcli_bin)
+ if flag_json:
+ storcli_cmds.append(MegaRAID._CMD_JSON_OUTPUT_SWITCH)
+ try:
+ output = cmd_exec(storcli_cmds)
+ except OSError as os_error:
+ if os_error.errno == errno.ENOENT:
+ raise LsmError(
+ ErrorNumber.INVALID_ARGUMENT,
+ "storcli binary '%s' is not exist or executable." %
+ self._storcli_bin)
+ else:
+ raise
+
+ if flag_json:
+ output_dict = json.loads(output)
+ ctrl_output = output_dict.get('Controllers')
+ if len(ctrl_output) != 1:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "_storcli_exec(): Unexpected output from MegaRAID "
+ "storcli: %s" % output_dict)
+
+ rc_status = ctrl_output[0].get('Command Status')
+ if rc_status.get('Status') != 'Success':
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "MegaRAID storcli failed with error %d: %s" %
+ (rc_status['Status Code'], rc_status['Description']))
+ return ctrl_output[0].get('Response Data')
+ else:
+ return output
+
+ def _ctrl_count(self):
+ return self._storcli_exec(
+ ["show", "ctrlcount"]).get("Controller Count")
+
+ def _lsm_status_of_ctrl(self, ctrl_num):
+ ctrl_health_output = self._storcli_exec(
+ ["/c%d" % ctrl_num, 'show', 'health', 'all'])
+ health_info = ctrl_health_output['Controller Health Info']
+ # TODO(Gris Ge): Try pull a disk off to check whether this change.
+ if health_info['Overall Health'] == 'GOOD':
+ return System.STATUS_OK, ''
+
+ return System.STATUS_UNKNOWN, "%s reason code %s" % (
+ health_info['Overall Health'],
+ health_info['Reason Code'])
+
+ def _sys_id_of_ctrl_num(self, ctrl_num, ctrl_show_output=None):
+ if ctrl_show_output is None:
+ ctrl_show_output = self._storcli_exec(["/c%d" % ctrl_num, "show"])
+ sys_id = ctrl_show_output['Serial Number']
+ if not sys_id:
+ raise LsmError(
+ ErrorNumber.PLUGIN_BUG,
+ "_sys_id_of_ctrl_num(): Fail to get system id: %s" %
+ ctrl_show_output.items())
+ else:
+ return sys_id
+
+ @_handle_errors
+ def systems(self, flags=0):
+ rc_lsm_syss = []
+ for ctrl_num in range(self._ctrl_count()):
+ ctrl_show_output = self._storcli_exec(["/c%d" % ctrl_num, "show"])
+ sys_id = self._sys_id_of_ctrl_num(ctrl_num, ctrl_show_output)
+ sys_name = "%s %s %s:%s:%s" % (
+ ctrl_show_output['Product Name'],
+ ctrl_show_output['Host Interface'],
+ format(ctrl_show_output['Bus Number'], '02x'),
+ format(ctrl_show_output['Device Number'], '02x'),
+ format(ctrl_show_output['Function Number'], '02x'))
+ (status, status_info) = self._lsm_status_of_ctrl(ctrl_num)
+ plugin_data = "/c%d"
+ # Since PCI slot sequence might change.
+ # This string just stored for quick system verification.
+
+ rc_lsm_syss.append(
+ System(sys_id, sys_name, status, status_info, plugin_data))
+
+ return rc_lsm_syss
+
+ @_handle_errors
+ def disks(self, search_key=None, search_value=None, flags=_FLAG_RSVD):
+ rc_lsm_disks = []
+ mega_disk_path_regex = re.compile(
+ r"^Drive (\/c[0-9]+\/e[0-9]+\/s[0-9]+) - Detailed Information$")
+
+ for ctrl_num in range(self._ctrl_count()):
+ sys_id = self._sys_id_of_ctrl_num(ctrl_num)
+
+ disk_show_output = self._storcli_exec(
+ ["/c%d/eall/sall" % ctrl_num, "show", "all"])
+ for drive_name in disk_show_output.keys():
+ re_match = mega_disk_path_regex.match(drive_name)
+ if not re_match:
+ continue
+
+ mega_disk_path = re_match.group(1)
+ # Assuming only 1 disk attached to each slot.
+ disk_show_basic_dict = disk_show_output[
+ "Drive %s" % mega_disk_path][0]
+ disk_show_attr_dict = disk_show_output[drive_name][
+ 'Drive %s Device attributes' % mega_disk_path]
+ disk_show_stat_dict = disk_show_output[drive_name][
+ 'Drive %s State' % mega_disk_path]
+
+ disk_id = disk_show_attr_dict['SN'].strip()
+ disk_name = "Disk %s %s %s" % (
+ disk_show_basic_dict['DID'],
+ disk_show_attr_dict['Manufacturer Id'].strip(),
+ disk_show_attr_dict['Model Number'])
+ disk_type = _disk_type_of(disk_show_basic_dict)
+ blk_size = size_human_2_size_bytes(
+ disk_show_basic_dict['SeSz'])
+ blk_count = _blk_count_of(disk_show_attr_dict['Coerced size'])
+ status = _disk_status_of(
+ disk_show_basic_dict, disk_show_stat_dict)
+
+ plugin_data = mega_disk_path
+
+ rc_lsm_disks.append(
+ Disk(
+ disk_id, disk_name, disk_type, blk_size, blk_count,
+ status, sys_id, plugin_data))
+
+ return search_property(rc_lsm_disks, search_key, search_value)
+
+ @_handle_errors
+ def pools(self, search_key=None, search_value=None,
+ flags=Client.FLAG_RSVD):
+ raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported yet")
diff --git a/plugin/megaraid/megaraid_lsmplugin b/plugin/megaraid/megaraid_lsmplugin
new file mode 100755
index 0000000..5aaa0e8
--- /dev/null
+++ b/plugin/megaraid/megaraid_lsmplugin
@@ -0,0 +1,37 @@
+#!/usr/bin/env python2
+
+# Copyright (C) 2011-2013 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: tasleson
+
+import sys
+import syslog
+import traceback
+
+try:
+ from lsm import PluginRunner
+ from lsm.plugin.megaraid import MegaRAID
+
+ if __name__ == '__main__':
+ PluginRunner(MegaRAID, sys.argv).run()
+except Exception:
+ #This should be quite rare, but when it does happen this is pretty
+ #key in understanding what happened, especially when it happens when
+ #running from the daemon.
+ msg = str(traceback.format_exc())
+ syslog.syslog(syslog.LOG_ERR, msg)
+ sys.stderr.write(msg)
+ sys.exit(1)
diff --git a/plugin/megaraid/utils.py b/plugin/megaraid/utils.py
new file mode 100644
index 0000000..4290b39
--- /dev/null
+++ b/plugin/megaraid/utils.py
@@ -0,0 +1,47 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <***@redhat.com>
+
+import subprocess
+import os
+
+def cmd_exec(cmds):
+ """
+ Execute provided command and return the STDOUT as string.
+ Raise ExecError if command return code is not zero
+ """
+ cmd_popen = subprocess.Popen(
+ cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ env={"PATH": os.getenv("PATH")})
+ str_stdout = "".join(list(cmd_popen.stdout)).strip()
+ str_stderr = "".join(list(cmd_popen.stderr)).strip()
+ errno = cmd_popen.wait()
+ if errno != 0:
+ raise ExecError(" ".join(cmds), errno, str_stdout, str_stderr)
+ return str_stdout
+
+
+class ExecError(Exception):
+ def __init__(self, cmd, errno, stdout, stderr, *args, **kwargs):
+ Exception.__init__(self, *args, **kwargs)
+ self.cmd = cmd
+ self.errno = errno
+ self.stdout = stdout
+ self.stderr = stderr
+
+ def __str__(self):
+ return "cmd: '%s', errno: %d, stdout: '%s', stderr: '%s'" % \
+ (self.cmd, self.errno, self.stdout, self.stderr)
--
1.8.3.1
Gris Ge
2015-02-09 11:22:09 UTC
Permalink
From: Tony Asleson <***@redhat.com>

Signed-off-by: Tony Asleson <***@redhat.com>

Change in V2 by Gris Ge:

* Remove a line mentioned 'linux plugin' which has been dropped from release.

Signed-off-by: Gris Ge <***@redhat.com>
---
doc/man/lsmd.conf.5.in | 35 +++++++++++++++++------------------
1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/doc/man/lsmd.conf.5.in b/doc/man/lsmd.conf.5.in
index 7b1d6f4..894789b 100644
--- a/doc/man/lsmd.conf.5.in
+++ b/doc/man/lsmd.conf.5.in
@@ -3,45 +3,44 @@
lsmd.conf - libStorageMgmt daemon lsmd configuration file.

.SH DESCRIPTION
-The libStorageMgmt plugin-in daemon(\fBlsmd\fR) will read \fBlsmd.conf\fR
-file in the folder defined via \fB--confdir\fR(default is \fB/etc/lsm/\fR).
+The libStorageMgmt plugin daemon (\fBlsmd\fR) will read the \fBlsmd.conf\fR
+file in the folder defined via \fB--confdir\fR (default is \fB/etc/lsm/\fR).

-The \fBlsmd\fR also read plugin configuration files from sub-folder
+The daemon reads the plugin configuration files from the sub-folder
\fBpluginconf.d\fR. The plugin configuration file should be named as
-<uri_scheme>.conf. For example:
+<uri_scheme>.conf, for example:

- * Linux plugin(\fBlinux\fR://): \fBlinux.conf\fR
- * MegaRAID plugin(\fBmegaraid\fR://): \fBmegaraid.conf\fR
+ * MegaRAID plugin (\fBmegaraid\fR://): \fBmegaraid.conf\fR

-The \fBlsmd.conf\fR controls the global settings for \fBlsmd\fR while
-plugin configuration file controls plugin behaviour.
+The \fBlsmd.conf\fR file controls the global settings for \fBlsmd\fR while
+the plugin configuration file for each plugin controls individual plugin behavior.

-Each option line of configuration file should contain tailing
+Each option line of the configuration file should contain a trailing
semicolon(\fB;\fR).

.SH lsmd.conf OPTIONS
.TP
\fBallow-plugin-root-privilege = true;\fR

-Indicate whether \fBlsmd\fR daemon should keep running in root mode and invoke
+Indicates whether the \fBlsmd\fR daemon should keep running as root mode and invoke
plugin as root user when needed.

-Without this option or set as \fBfalse\fR means never run lsmd and its plugins
-as root user.
+Without this option or with option set as \fBfalse\fR means that the daemon and
+the plugins will never run as root.

-Only when all the following requirements meet, will the \fBlsmd\fR run certain
-plugin as root user:
+Only when all the following requirements are met, will \fBlsmd\fR run specified
+plugins as root user:

1. "allow-plugin-root-privilege = true;" in lsmd.conf
- 2. "require-root-privilege = true;" in plugin config.
- 3. API connection(or lsmcli) is ran by root user.
+ 2. "require-root-privilege = true;" in plugin config
+ 3. API connection (or lsmcli) has root privileges

.SH Plugin OPTIONS
.TP
\fBrequire-root-privilege = true;\fR

-Indicate plugin requires root privilege.
-Without this line or set as \fBfalse\fR, the plugin will never invoked as root
+Indicates plugin requires root privilege.
+Without this line or set as \fBfalse\fR, the plugin will never be invoked as root
user by \fBlsmd\fR.

Please check \fBlsmd.conf\fR option \fBallow-plugin-root-privilege\fR for
--
1.8.3.1
Tony Asleson
2015-02-05 09:43:54 UTC
Permalink
Man page comments:

- Will submit a small patch with updates for lsmd.conf
- The linux_lsmplugin has references to MegaRAID (copy & paste errors)
and some spelling errors, please run a spell check on it
- Please run spell check on megaraid_lsmplugin.1.in

Thanks!

Regards,
Tony
Post by Gris Ge
0. Patch 1/7 and 2/7 is just bug fix.
1. Update lsmd for allowing plugin run as root user.
2. New plugin: MegaRAID plugin (megaraid://).
# Require root privilege
# Using LSI binary tool -- 'storcli'
# 'storcli' provides 'J' command line switch/option which change
# output to json format, it's pretty handy for coding with it.
3. New plugin: Linux plugin (linux://).
# Require root privilege
# Using udev, sysfs and mdadm.
# The name of this plugin is confusing. Any suggest?
# We might add LVM(it has RAID also) later.
* There are some duplicate codes in utils.py of linux plugin and MegaRAID
plugin. But considering plugin was supposed to be standalone, let's live
with it.
* RHEL 7 -- Only has hpssacli (new one used in Openstack cinder and
VMWare)
* RHEL 6 -- Only has hpacucli (old one, marked as obsolete[1])
Hence, I would like to postpone it for a while.
Thank you very much in advance.
[1] http://h20565.www2.hp.com/hpsc/doc/public/display?sp4ts.oid=254934&docId=mmr_kc-0110258&docLocale=en_US
lsmd: Remove incorrect log in child_cleanup()
lsmcli: Add missing Disk.TYPE_SCSI
lsmd Daemon: New feature: allowing plugin run as root.
lsmenv: Support new feature of lsmd for root privilege.
New Plugin: MegaRAID plugin using storcli binary tool.
New Plugin: Linux Plugin for linux storage management
lsmd: Clean up logging
Makefile.am | 2 +-
config/Makefile.am | 15 ++
config/lsmd.conf | 1 +
config/pluginconf.d/linux.conf | 1 +
config/pluginconf.d/megaraid.conf | 1 +
config/pluginconf.d/sim.conf | 1 +
configure.ac | 14 ++
daemon/Makefile.am | 4 +-
daemon/lsm_daemon.c | 278 ++++++++++++++++++++++++------
doc/man/Makefile.am | 4 +-
doc/man/linux_lsmplugin.1.in | 54 ++++++
doc/man/lsmd.conf.5.in | 58 +++++++
doc/man/megaraid_lsmplugin.1.in | 50 ++++++
packaging/daemon/libstoragemgmt.service | 2 +-
packaging/libstoragemgmt.spec.in | 63 +++++++
plugin/Makefile.am | 2 +-
plugin/linux/Makefile.am | 6 +
plugin/linux/__init__.py | 1 +
plugin/linux/base_os.py | 134 +++++++++++++++
plugin/linux/linux.py | 122 +++++++++++++
plugin/linux/linux_lsmplugin | 37 ++++
plugin/linux/md_raid.py | 98 +++++++++++
plugin/linux/utils.py | 53 ++++++
plugin/megaraid/Makefile.am | 6 +
plugin/megaraid/__init__.py | 1 +
plugin/megaraid/megaraid.py | 292 ++++++++++++++++++++++++++++++++
plugin/megaraid/megaraid_lsmplugin | 37 ++++
plugin/megaraid/utils.py | 47 +++++
tools/lsmcli/data_display.py | 1 +
tools/lsmenv | 61 ++++++-
30 files changed, 1382 insertions(+), 64 deletions(-)
create mode 100644 config/Makefile.am
create mode 100644 config/lsmd.conf
create mode 100644 config/pluginconf.d/linux.conf
create mode 100644 config/pluginconf.d/megaraid.conf
create mode 100644 config/pluginconf.d/sim.conf
create mode 100644 doc/man/linux_lsmplugin.1.in
create mode 100644 doc/man/lsmd.conf.5.in
create mode 100644 doc/man/megaraid_lsmplugin.1.in
create mode 100644 plugin/linux/Makefile.am
create mode 100644 plugin/linux/__init__.py
create mode 100644 plugin/linux/base_os.py
create mode 100644 plugin/linux/linux.py
create mode 100755 plugin/linux/linux_lsmplugin
create mode 100644 plugin/linux/md_raid.py
create mode 100644 plugin/linux/utils.py
create mode 100644 plugin/megaraid/Makefile.am
create mode 100644 plugin/megaraid/__init__.py
create mode 100644 plugin/megaraid/megaraid.py
create mode 100755 plugin/megaraid/megaraid_lsmplugin
create mode 100644 plugin/megaraid/utils.py
Loading...