FOSDEM 2012 talk proposal: Matahari project
by Zane Bitter
Hi folks,
I would like to submit the following talk for consideration for the
Configuration and Systems Management DevRoom.
A few notes for context: The Matahari project is a Free Software
project, licensed under the GPL and developed in the open. Matahari's
sysconfig agent (one of the core agents) supports system configuration
using Puppet, but Matahari is about much more than system configuration.
Title: Systems Management with Matahari
Speaker: Zane Bitter
(Senior Software Engineer at Red Hat and one of the current Matahari
project maintainers)
In this talk I will introduce the Matahari project
(http://matahariproject.org/), an interface for system management APIs
of all kinds. I will discuss the architecture of the Matahari ecosystem
and how the use of AMQP as a transport layer allows it to scale to large
collections of servers in the Cloud. I will also describe the ways in
which developers and sysadmins can get involved in the Matahari
ecosystem: by creating agents to provide access to existing APIs through
Matahari; by creating their own APIs; by writing their own clients to
interact with agents; and by interacting through a command line shell or
script.
Please let me know if I can provide any more information/clarification.
thanks,
Zane.
12 years, 4 months
[PATCH 0/6] Make coverity happy again.
by Russell Bryant
Greetings,
These patches resolve errors found by coverity, as well as one
unused variable warning.
[PATCH 1/6] core: Change return type to be signed.
[PATCH 2/6] broker: remove unused variable.
[PATCH 3/6] sysconfig: Check malloc() return value.
[PATCH 4/6] sysconfig: Fix a memory leak.
[PATCH 5/6] host: Don't deref pointer until after NULL check.
[PATCH 6/6] utilities: Resolve use-after-free errors.
broker/broker_federation.c | 1 -
include/matahari/utilities.h | 2 +-
lib/host_linux.c | 4 ++--
lib/utilities.c | 4 +---
sysconfig/sysconfig-dbus.c | 12 +++++++++---
5 files changed, 13 insertions(+), 10 deletions(-)
Thanks,
--
Russell Bryant
12 years, 5 months
question about API mismatch between console/agent
by Perry Myers
I'm sure this has been covered before, but either I'm always forgetting
or I never clearly understood the explanation.
AgentX has a method foo(A, B)
ConsoleX calls method foo(A, B)
No issues so far, both Agent and Console are using the v1 schema for
AgentX so all is well
Scenario 1:
-----------
Now say that Agent1 has a schema change and does the following:
Adds: foo(A, B, C)
Adds: bar(D, E)
ConsoleX is still running and calling foo(A, B)
What happens? Does ConsoleX blow up?
Scenario 2:
-----------
Let's say that instead of the above, ConsoleX adds the following:
Adds: foo(A, B, C)
Adds: bar(D, E)
But AgentX still has schema with only foo(A, B)
If ConsoleX makes a call to foo(A, B, C) will AgentX process it and just
ignore the new parameter, or will it just fail gracefully, or will it
error and stacktrace the Console
Same question if ConsoleX calls bar(D, E) (new method vs. existing
method with new parameter)
Finally... the above covers Consoles that are calling methods somewhat
statically. But what about the matahari shell, where we'll be
presenting remote Agent schema more dynamically to the user?
Cheers,
Perry
12 years, 5 months
[PATCH v2] Core: Add utilities for managing multiple objects
by Zane Bitter
Signed-off-by: Zane Bitter <zbitter(a)redhat.com>
---
matahari.spec.in | 1
src/CMakeLists.txt | 1
src/include/matahari/object.h | 96 +++++++++++++++++++++++++++++++
src/lib/CMakeLists.txt | 2 -
src/lib/mh_object.cpp | 125 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 224 insertions(+), 1 deletions(-)
create mode 100644 src/include/matahari/object.h
create mode 100644 src/lib/mh_object.cpp
diff --git a/matahari.spec.in b/matahari.spec.in
index b0db60a..c40f9fb 100644
--- a/matahari.spec.in
+++ b/matahari.spec.in
@@ -658,6 +658,7 @@ test "x%{buildroot}" != "x" && rm -rf %{buildroot}
%if %{with qmf}
%{_includedir}/matahari/agent.h
+%{_includedir}/matahari/object.h
%{_includedir}/matahari/mainloop.h
%{_datadir}/cmake/Modules/FindQPID.cmake
%endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e305951..14a4bd5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -206,6 +206,7 @@ install(FILES cmake/modules/MatahariMacros.cmake DESTINATION share/cmake/Modules
if(WITH-QMF)
install(FILES cmake/modules/FindQPID.cmake DESTINATION share/cmake/Modules)
install(FILES include/matahari/agent.h DESTINATION include/matahari)
+ install(FILES include/matahari/object.h DESTINATION include/matahari)
install(FILES include/matahari/mainloop.h DESTINATION include/matahari)
endif(WITH-QMF)
diff --git a/src/include/matahari/object.h b/src/include/matahari/object.h
new file mode 100644
index 0000000..ead6dd6
--- /dev/null
+++ b/src/include/matahari/object.h
@@ -0,0 +1,96 @@
+/* mh_object.cpp - Copyright (C) 2011 Red Hat, Inc.
+ * Written by Zane Bitter <zbitter(a)redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MATAHARI_OBJECT_H
+#define __MATAHARI_OBJECT_H
+
+#include <qmf/AgentEvent.h>
+#include <qmf/AgentSession.h>
+#include <qmf/Schema.h>
+#include <qmf/Data.h>
+#include <qmf/DataAddr.h>
+
+/**
+ * \file
+ * \brief Utilities for managing multiple objects
+ */
+
+
+/**
+ * An object that is managed by an ObjectManager. Create a subclass of this and
+ * override the invoke() method to handle events destined for the object.
+ */
+class MatahariObject
+{
+public:
+ /** Create the object with the supplied schema. */
+ MatahariObject(qmf::Schema schema);
+ virtual ~MatahariObject();
+
+ /** Get the QMF address of the object. */
+ const qmf::DataAddr& getAddr(void) { return _instance.getAddr(); }
+
+ /** Publish the object in the specified session. */
+ void publish(qmf::AgentSession& session);
+ /** Unpublish the object from the specified session. */
+ void withdraw(qmf::AgentSession& session);
+
+ /**
+ * Handle an event that is destined for this object.
+ * \param session the session from which the event originated.
+ * \param event the event
+ * \return true if the event was handled, false otherwise.
+ */
+ virtual bool invoke(qmf::AgentSession& session, qmf::AgentEvent& event)
+ { return false; }
+
+protected:
+ qmf::Data _instance;
+};
+
+
+class ObjectManagerImpl;
+
+/**
+ * A store that manages a collection of QMF objects.
+ */
+class ObjectManager
+{
+public:
+ /** Initialise for the given session. */
+ ObjectManager(qmf::AgentSession& session);
+ ~ObjectManager();
+
+ /** Publish a new object to the session. */
+ ObjectManager& operator+=(MatahariObject* object);
+ /** Unpublish an object from the session. */
+ ObjectManager& operator-=(MatahariObject* object);
+
+ /**
+ * Handle an event by directing it to the correct object.
+ * \param session the session from which the event originated.
+ * \param event the event
+ * \return true if the event was handled, false otherwise.
+ */
+ bool invoke(qmf::AgentSession& session, qmf::AgentEvent& event) const;
+
+private:
+ ObjectManagerImpl *_impl;
+};
+
+#endif
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index b5ab0f7..67cad82 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -43,7 +43,7 @@ else(WIN32)
endif(WIN32)
if(WITH-QMF)
- add_library (mcommon_qmf SHARED mh_agent.cpp)
+ add_library (mcommon_qmf SHARED mh_agent.cpp mh_object.cpp)
set_target_properties(mcommon_qmf PROPERTIES SOVERSION 1.0.0)
target_link_libraries(mcommon_qmf mcommon ${QPIDCOMMON_LIBRARY}
diff --git a/src/lib/mh_object.cpp b/src/lib/mh_object.cpp
new file mode 100644
index 0000000..fcfe5dd
--- /dev/null
+++ b/src/lib/mh_object.cpp
@@ -0,0 +1,125 @@
+/* mh_object.cpp - Copyright (C) 2011 Red Hat, Inc.
+ * Written by Zane Bitter <zbitter(a)redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <map>
+#include <matahari/object.h>
+extern "C" {
+#include <matahari/logging.h>
+}
+
+struct ObjectManagerImpl
+{
+ ObjectManagerImpl(qmf::AgentSession& session): _session(session) { }
+
+ MatahariObject *find(const qmf::DataAddr& addr);
+
+
+ // This is necessary to avoid a bug (#QPID3344) in libqmf2 versions prior
+ // to 0.14, in which DataAddr objects are not compared correctly when they
+ // are const (as all std::map keys are).
+ struct AddrComparator {
+ bool operator()(const qmf::DataAddr& addr1, const qmf::DataAddr& addr2)
+ {
+ return const_cast<qmf::DataAddr&>(addr1) < addr2;
+ }
+ };
+
+ typedef std::map<qmf::DataAddr, MatahariObject*, AddrComparator> ObjectMap;
+
+
+ qmf::AgentSession& _session;
+ ObjectMap _objects;
+};
+
+
+MatahariObject*
+ObjectManagerImpl::find(const qmf::DataAddr& addr)
+{
+ ObjectMap::const_iterator it(_objects.find(addr));
+
+ return it != _objects.end() ? it->second : NULL;
+}
+
+
+ObjectManager::ObjectManager(qmf::AgentSession& session):
+ _impl(new ObjectManagerImpl(session))
+{ }
+
+ObjectManager::~ObjectManager()
+{
+ delete _impl;
+}
+
+ObjectManager& ObjectManager::operator+=(MatahariObject* object)
+{
+ object->publish(_impl->_session);
+ if (!object->getAddr().isValid()) {
+ mh_err("Failed to publish object");
+ return *this;
+ }
+ _impl->_objects[object->getAddr()] = object;
+
+ return *this;
+}
+
+ObjectManager& ObjectManager::operator-=(MatahariObject* object)
+{
+ _impl->_objects.erase(object->getAddr());
+ object->withdraw(_impl->_session);
+
+ return *this;
+}
+
+bool ObjectManager::invoke(qmf::AgentSession& session,
+ qmf::AgentEvent& event) const
+{
+ if (event.hasDataAddr()) {
+ MatahariObject *obj = _impl->find(event.getDataAddr());
+
+ if (obj) {
+ return obj->invoke(session, event);
+ }
+
+ mh_err("Target of method call not found");
+ }
+
+ return false;
+}
+
+
+MatahariObject::MatahariObject(qmf::Schema schema): _instance(schema)
+{ }
+
+MatahariObject::~MatahariObject()
+{ }
+
+void
+MatahariObject::publish(qmf::AgentSession& session)
+{
+ session.addData(_instance);
+ mh_debug("Published object %s", getAddr().getName().c_str());
+}
+
+void
+MatahariObject::withdraw(qmf::AgentSession& session)
+{
+ mh_debug("Withdrawing object %s", getAddr().getName().c_str());
+ session.delData(getAddr());
+}
12 years, 5 months
[PATCH] Make C headers 'extern "C"' in C++
by Zane Bitter
It makes no sense to force users of these header files to specify it every
time.
Signed-off-by: Zane Bitter <zbitter(a)redhat.com>
---
src/host/host-qmf.cpp | 2 --
src/include/matahari/dnssrv.h | 8 ++++++++
src/include/matahari/errors.h | 8 ++++++++
src/include/matahari/host.h | 8 ++++++++
src/include/matahari/logging.h | 9 +++++++++
src/include/matahari/mainloop.h | 8 ++++++++
src/include/matahari/network.h | 8 ++++++++
src/include/matahari/services.h | 8 ++++++++
src/include/matahari/sysconfig.h | 8 ++++++++
src/include/matahari/utilities.h | 9 +++++++++
src/lib/mh_agent.cpp | 5 +++--
src/network/network-qmf.cpp | 2 --
src/service/service-qmf-console.cpp | 2 --
src/service/service-qmf.cpp | 4 ----
src/sysconfig/sysconfig-console.cpp | 2 --
src/sysconfig/sysconfig-qmf.cpp | 2 --
16 files changed, 77 insertions(+), 16 deletions(-)
diff --git a/src/host/host-qmf.cpp b/src/host/host-qmf.cpp
index 4b72d00..ea2c738 100644
--- a/src/host/host-qmf.cpp
+++ b/src/host/host-qmf.cpp
@@ -29,13 +29,11 @@
#include <qmf/Data.h>
#include "qmf/org/matahariproject/QmfPackage.h"
-extern "C" {
#include <string.h>
#include <sigar.h>
#include "matahari/host.h"
#include "matahari/logging.h"
#include "matahari/errors.h"
-}
class HostAgent : public MatahariAgent
{
diff --git a/src/include/matahari/dnssrv.h b/src/include/matahari/dnssrv.h
index 0a39519..da2d2a1 100644
--- a/src/include/matahari/dnssrv.h
+++ b/src/include/matahari/dnssrv.h
@@ -26,6 +26,10 @@
#ifndef __MH_DNSSRV_H__
#define __MH_DNSSRV_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <sys/types.h>
#include <inttypes.h>
#include <glib.h>
@@ -109,4 +113,8 @@ mh_dnssrv_record_get_weight(const struct mh_dnssrv_record *record);
void
mh_dnssrv_record_free(gpointer data);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __MH_DNSSRV_H__ */
diff --git a/src/include/matahari/errors.h b/src/include/matahari/errors.h
index 47aa5db..95fad88 100644
--- a/src/include/matahari/errors.h
+++ b/src/include/matahari/errors.h
@@ -26,6 +26,10 @@
#ifndef __MH_ERRORS_H__
#define __MH_ERRORS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Global enumeration with result codes.
*/
@@ -49,4 +53,8 @@ enum mh_result {
const char *
mh_result_to_str(enum mh_result res);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/include/matahari/host.h b/src/include/matahari/host.h
index 31a5a98..f873da3 100644
--- a/src/include/matahari/host.h
+++ b/src/include/matahari/host.h
@@ -25,6 +25,10 @@
#ifndef __MH_HOST_H__
#define __MH_HOST_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <stdint.h>
#include <stdlib.h>
#include <sigar.h>
@@ -153,4 +157,8 @@ mh_host_get_power_profile(char **profile);
GList *
mh_host_list_power_profiles(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif // __MH_HOST_H__
diff --git a/src/include/matahari/logging.h b/src/include/matahari/logging.h
index 260eccc..9132b81 100644
--- a/src/include/matahari/logging.h
+++ b/src/include/matahari/logging.h
@@ -26,6 +26,10 @@
#ifndef __MH_LOGGING__
#define __MH_LOGGING__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifdef __linux__
# include <syslog.h>
#else
@@ -198,4 +202,9 @@ extern struct _mh_ddebug __stop___verbose[];
fprintf(stderr, fmt ": %s (%d)\n", ##args, err, errno); \
mh_log(level, fmt ": %s (%d)", ##args, err, errno); \
} while(0)
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/include/matahari/mainloop.h b/src/include/matahari/mainloop.h
index 74adce8..62da239 100644
--- a/src/include/matahari/mainloop.h
+++ b/src/include/matahari/mainloop.h
@@ -25,6 +25,10 @@
#ifndef __MH_MAINLOOP__
#define __MH_MAINLOOP__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <glib.h>
#include <sys/types.h>
@@ -101,4 +105,8 @@ mainloop_add_child(pid_t pid, int timeout, const char *desc, void *privatedata,
void (*callback)(mainloop_child_t* p, int status, int signo,
int exitcode));
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/include/matahari/network.h b/src/include/matahari/network.h
index 1249bfd..a0345c6 100644
--- a/src/include/matahari/network.h
+++ b/src/include/matahari/network.h
@@ -25,6 +25,10 @@
#ifndef __NETWORK_H
#define __NETWORK_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <glib.h>
#include <stdint.h>
@@ -139,4 +143,8 @@ mh_network_restart(const char *iface);
int
mh_network_status(const char *iface, uint64_t *flags);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __NETWORK_H */
diff --git a/src/include/matahari/services.h b/src/include/matahari/services.h
index 6e61b4d..ad98e9f 100644
--- a/src/include/matahari/services.h
+++ b/src/include/matahari/services.h
@@ -25,6 +25,10 @@
#ifndef __MH_SERVICES__
#define __MH_SERVICES__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <glib.h>
#include <stdio.h>
#include "matahari/mainloop.h"
@@ -269,4 +273,8 @@ services_get_ocf_exitcode(char *action, int lsb_exitcode)
return (enum ocf_exitcode)lsb_exitcode;
}
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __MH_SERVICES__ */
diff --git a/src/include/matahari/sysconfig.h b/src/include/matahari/sysconfig.h
index 3a74c61..c11a01b 100644
--- a/src/include/matahari/sysconfig.h
+++ b/src/include/matahari/sysconfig.h
@@ -26,6 +26,10 @@
#ifndef __MH_SYSCONFIG_H__
#define __MH_SYSCONFIG_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <stdint.h>
#include <glib.h>
@@ -119,4 +123,8 @@ mh_sysconfig_set_configured(const char *key, const char *contents);
char *
mh_sysconfig_is_configured(const char *key);
+#ifdef __cplusplus
+}
+#endif
+
#endif // __MH_SYSCONFIG_H__
diff --git a/src/include/matahari/utilities.h b/src/include/matahari/utilities.h
index 5084261..941e3a2 100644
--- a/src/include/matahari/utilities.h
+++ b/src/include/matahari/utilities.h
@@ -25,6 +25,10 @@
#ifndef __MH_UTILITIES__
#define __MH_UTILITIES__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "matahari/config.h"
#include <stdlib.h>
@@ -140,4 +144,9 @@ mh_string_copy(char *dst, const char *src, size_t dst_len);
*/
gsize
mh_read_from_fd(int fd, char **data);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/lib/mh_agent.cpp b/src/lib/mh_agent.cpp
index 7c94c7f..34799fe 100644
--- a/src/lib/mh_agent.cpp
+++ b/src/lib/mh_agent.cpp
@@ -42,19 +42,20 @@
#include <qmf/SchemaProperty.h>
#include "matahari/agent.h"
-extern "C" {
#include <sys/types.h>
#include "matahari/logging.h"
#include "matahari/dnssrv.h"
#include "matahari/utilities.h"
+
#ifndef WIN32
+extern "C" {
#include <sys/socket.h>
#include <netdb.h>
#ifdef MH_SSL
#include <secmod.h>
#endif
-#endif
}
+#endif
using namespace qpid::management;
using namespace qpid::client;
diff --git a/src/network/network-qmf.cpp b/src/network/network-qmf.cpp
index a6271c2..ca69f7f 100644
--- a/src/network/network-qmf.cpp
+++ b/src/network/network-qmf.cpp
@@ -24,7 +24,6 @@
#include <qpid/agent/ManagementAgent.h>
-extern "C" {
#include <stdlib.h>
#include <string.h>
#include <glib.h>
@@ -33,7 +32,6 @@ extern "C" {
#include "matahari/network.h"
#include "matahari/host.h"
#include "matahari/errors.h"
-};
class NetAgent : public MatahariAgent
{
diff --git a/src/service/service-qmf-console.cpp b/src/service/service-qmf-console.cpp
index 344773a..850e6f9 100644
--- a/src/service/service-qmf-console.cpp
+++ b/src/service/service-qmf-console.cpp
@@ -32,9 +32,7 @@
#include <iostream>
#include <sstream>
-extern "C" {
#include "matahari/logging.h"
-}
using namespace std;
using namespace qmf;
diff --git a/src/service/service-qmf.cpp b/src/service/service-qmf.cpp
index 6aa4904..74a2600 100644
--- a/src/service/service-qmf.cpp
+++ b/src/service/service-qmf.cpp
@@ -18,10 +18,8 @@
#include "config.h"
-extern "C" {
#include <stdlib.h>
#include <string.h>
-};
#include <string>
#include <qpid/management/Manageable.h>
@@ -30,11 +28,9 @@ extern "C" {
#include "qmf/org/matahariproject/QmfPackage.h"
-extern "C" {
#include "matahari/logging.h"
#include "matahari/services.h"
#include "matahari/errors.h"
-}
#include <iostream>
diff --git a/src/sysconfig/sysconfig-console.cpp b/src/sysconfig/sysconfig-console.cpp
index 865eda2..a6bd1b3 100644
--- a/src/sysconfig/sysconfig-console.cpp
+++ b/src/sysconfig/sysconfig-console.cpp
@@ -34,10 +34,8 @@
#include <iostream>
#include <sstream>
-extern "C" {
#include "matahari/logging.h"
#include "matahari/sysconfig.h"
-}
using namespace std;
using namespace qmf;
diff --git a/src/sysconfig/sysconfig-qmf.cpp b/src/sysconfig/sysconfig-qmf.cpp
index bf81414..d832a7e 100644
--- a/src/sysconfig/sysconfig-qmf.cpp
+++ b/src/sysconfig/sysconfig-qmf.cpp
@@ -27,11 +27,9 @@
#include "qmf/org/matahariproject/QmfPackage.h"
#include "matahari/agent.h"
-extern "C" {
#include "matahari/logging.h"
#include "matahari/host.h"
#include "matahari/sysconfig.h"
-};
class ConfigAgent : public MatahariAgent
{
12 years, 5 months
[PATCH] Broker: Add windows variant for qpidd broker
by Adam Stokes
- Windows variant for qpidd broker and routing
needed for broker federation.
Signed-off-by: Adam Stokes <astokes(a)fedoraproject.org>
---
src/broker/broker_federation.c | 5 +-
src/broker/broker_windows.c | 97 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 99 insertions(+), 3 deletions(-)
create mode 100644 src/broker/broker_windows.c
diff --git a/src/broker/broker_federation.c b/src/broker/broker_federation.c
index 27156a1..20ffc14 100644
--- a/src/broker/broker_federation.c
+++ b/src/broker/broker_federation.c
@@ -120,8 +120,7 @@ broker_federation_configure(void)
const char *proxy_service_dir = getenv("VP_G_HOST_DIR");
char local[16];
char peers[1024];
- char *p = NULL;
- char *peer;
+ char *peer = NULL;
if (g_file_test(proxy_service_dir, G_FILE_TEST_IS_DIR)) {
char remote[16];
@@ -133,7 +132,7 @@ broker_federation_configure(void)
mh_string_copy(peers, brokers, sizeof(peers));
- while ((peer = strtok(peers, ",; ")) != NULL) {
+ while ((peer = strtok(peer ? NULL : peers, ",; ")) != NULL) {
if (IS_DNS_SRV(peer, TCP) || IS_DNS_SRV(peer, TLS)) {
peer = broker_lookup(peer);
}
diff --git a/src/broker/broker_windows.c b/src/broker/broker_windows.c
new file mode 100644
index 0000000..a0d51b5
--- /dev/null
+++ b/src/broker/broker_windows.c
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <wchar.h>
+
+#include <windows.h>
+
+#include "broker.h"
+#include "broker_os.h"
+#include "broker_federation.h"
+
+
+#define CMD_LINE_LENGTH 32768
+
+#define QPID_BROKER L"qpidd"
+#define QPID_ROUTE L"qpid-route"
+
+
+static int start_process(const wchar_t *proc, wchar_t *cmdline)
+{
+ STARTUPINFO startupinfo;
+ PROCESS_INFORMATION procinfo;
+ BOOL result;
+
+ GetStartupInfo(&startupinfo);
+
+ result = CreateProcess(proc, cmdline,
+ NULL, NULL, FALSE, 0, NULL, NULL,
+ &startupinfo, &procinfo);
+
+ CloseHandle(procinfo.hProcess);
+ CloseHandle(procinfo.hThread);
+
+ return result == FALSE;
+}
+
+int broker_os_start_broker(char * const args[])
+{
+ int ret;
+ wchar_t cmdline[CMD_LINE_LENGTH];
+ int i = 0;
+ const char *arg;
+
+ for (arg = *args; arg; arg++) {
+ int c = snwprintf(cmdline + i, CMD_LINE_LENGTH - i, L"%s ", arg);
+ if (c < 0 || (i += c) >= (CMD_LINE_LENGTH - 1)) {
+ return -1;
+ }
+ }
+
+ ret = start_process(QPID_BROKER, cmdline);
+
+ broker_federation_configure();
+
+ return ret ? -1 : 0;
+}
+
+int broker_os_add_qpid_route_link(const char *local, const char *remote)
+{
+ wchar_t cmd[1024];
+
+ snwprintf(cmd, sizeof(cmd) / sizeof(*cmd),
+ L"%ls link add %s untrusted/untrusted@%s PLAIN", QPID_ROUTE,
+ local, remote);
+
+ if (start_process(QPID_ROUTE, cmd)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+int broker_os_add_qpid_route(const struct mh_qpid_route *route)
+{
+ wchar_t cmd[1024];
+
+ if (route->aggregate && route->srclocal) {
+ snwprintf(cmd, sizeof(cmd) / sizeof(*cmd),
+ L"%ls --src-local route add %s %s %s %s", QPID_ROUTE,
+ route->dest, route->src, route->exchange, route->route_key);
+ } else if (route->aggregate) {
+ snwprintf(cmd, sizeof(cmd) / sizeof(*cmd),
+ L"%ls route add %s %s %s %s", QPID_ROUTE,
+ route->dest, route->src, route->exchange, route->route_key);
+ } else {
+ snwprintf(cmd, sizeof(cmd) / sizeof(*cmd),
+ L"%ls --timeout=5 dynamic add %s %s %s", QPID_ROUTE,
+ route->dest, route->src, route->exchange);
+ }
+
+ if (start_process(QPID_ROUTE, cmd)) {
+ return -1;
+ }
+
+ return 0;
+}
+
--
1.7.6.4
12 years, 5 months
[PATCH] Gofer: Prototype of a Gofer agent
by Zane Bitter
At present, this just loads Gofer plugins in the same way that goferd would
and allows their methods to be called over QMF. Additional features of
Gofer (basically all out-of-band data that is normally passed to the
plugin) have not yet been implemented, so the only data that is passed is
the class/method name and the arguments.
---
matahari.spec.in | 58 +++++++
src/CMakeLists.txt | 6 +
src/gofer/.gitignore | 2
src/gofer/CMakeLists.txt | 20 ++
src/gofer/gofer-qmf.cpp | 199 +++++++++++++++++++++++
src/gofer/mhgofer_test.py | 59 +++++++
src/gofer/schema.xml | 15 ++
src/include/matahari/gofer.h | 100 ++++++++++++
src/lib/CMakeLists.txt | 6 +
src/lib/gofer-python.c | 362 ++++++++++++++++++++++++++++++++++++++++++
src/lib/gofer.c | 153 ++++++++++++++++++
src/lib/gofer_private.h | 35 ++++
12 files changed, 1014 insertions(+), 1 deletions(-)
create mode 100644 src/gofer/.gitignore
create mode 100644 src/gofer/CMakeLists.txt
create mode 100644 src/gofer/gofer-qmf.cpp
create mode 100644 src/gofer/mhgofer_test.py
create mode 100644 src/gofer/schema.xml
create mode 100644 src/include/matahari/gofer.h
create mode 100644 src/lib/gofer-python.c
create mode 100644 src/lib/gofer.c
create mode 100644 src/lib/gofer_private.h
diff --git a/matahari.spec.in b/matahari.spec.in
index c40f9fb..2ccd894 100644
--- a/matahari.spec.in
+++ b/matahari.spec.in
@@ -41,6 +41,7 @@ Requires: pcre
Requires: %{name}-core = %{version}-%{release}
Requires: %{name}-consoles = %{version}-%{release}
Requires: %{name}-broker = %{version}-%{release}
+Requires: %{name}-gofer = %{version}-%{release}
Requires: %{name}-host = %{version}-%{release}
Requires: %{name}-network = %{version}-%{release}
Requires: %{name}-service = %{version}-%{release}
@@ -50,6 +51,7 @@ BuildRequires: cmake
BuildRequires: libuuid-devel
BuildRequires: gcc-c++
BuildRequires: pcre-devel
+BuildRequires: python-devel
BuildRequires: glib2-devel
BuildRequires: sigar-devel
BuildRequires: libcurl-devel
@@ -156,6 +158,20 @@ Requires: kstart
%description agent-lib
C++ library containing the base class for Matahari agents
+%package gofer
+License: GPLv2+
+Summary: QMF agent for Gofer plugins
+Group: Applications/System
+Requires: %{name}-lib = %{version}-%{release}
+Requires: %{name}-agent-lib = %{version}-%{release}
+Requires: gofer
+Requires(post): chkconfig
+Requires(preun):chkconfig
+Requires(preun):initscripts
+
+%description gofer
+QMF agent for dispatching RMI calls to Gofer plugins
+
%package host
License: GPLv2+
Summary: QMF agent for remote hosts
@@ -256,6 +272,7 @@ make DESTDIR=%{buildroot} -f Makefile install
%if %{systemd}
rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/matahari-service
rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/matahari-network
+rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/matahari-gofer
rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/matahari-host
rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/matahari-sysconfig
rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/matahari-sysconfig-console
@@ -273,6 +290,29 @@ rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/matahari-vios-proxy-guest
/sbin/ldconfig
%if %{with qmf}
+#== Gofer
+
+%post gofer
+%if %{systemd}
+systemctl --system daemon-reload
+%else
+/sbin/chkconfig --add matahari-gofer
+%endif
+/sbin/service matahari-gofer condrestart
+
+%preun gofer
+if [ $1 = 0 ]; then
+ /sbin/service matahari-gofer stop >/dev/null 2>&1 || :
+%if !%{systemd}
+ chkconfig --del matahari-gofer
+%endif
+fi
+
+%postun gofer
+if [ "$1" -ge "1" ]; then
+ /sbin/service matahari-gofer condrestart >/dev/null 2>&1 || :
+fi
+
#== Host
%post host
@@ -458,6 +498,7 @@ test "x%{buildroot}" != "x" && rm -rf %{buildroot}
%files lib
%defattr(644, root, root, 755)
%{_libdir}/libmcommon.so.*
+%{_libdir}/libmgofer.so.*
%{_libdir}/libmhost.so.*
%{_libdir}/libmnetwork.so.*
%{_libdir}/libmservice.so.*
@@ -496,6 +537,22 @@ test "x%{buildroot}" != "x" && rm -rf %{buildroot}
%{_datadir}/polkit-1/actions/org.matahariproject.Network.policy
%endif
+%files gofer
+%defattr(644, root, root, 755)
+%doc AUTHORS COPYING
+
+%if %{systemd}
+%{_unitdir}/matahari-gofer.service
+%endif
+
+%if %{with qmf}
+%if !%{systemd}
+%attr(755, root, root) %{_initddir}/matahari-gofer
+%endif
+%attr(755, root, root) %{_sbindir}/matahari-qmf-goferd
+%{_mandir}/man8/matahari-qmf-goferd.8*
+%endif
+
%files host
%defattr(644, root, root, 755)
%doc AUTHORS COPYING
@@ -649,6 +706,7 @@ test "x%{buildroot}" != "x" && rm -rf %{buildroot}
%{_includedir}/matahari/utilities.h
%{_includedir}/matahari/errors.h
%{_includedir}/matahari/dnssrv.h
+%{_includedir}/matahari/gofer.h
%{_includedir}/matahari/host.h
%{_includedir}/matahari/network.h
%{_includedir}/matahari/sysconfig.h
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 14a4bd5..9eea413 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -108,6 +108,10 @@ include_directories(${glib_INCLUDE_DIRS})
SET(CMAKE_REQUIRED_LIBRARIES ${glib_LIBRARIES})
check_function_exists (g_list_free_full HAVE_G_LIST_FREE_FULL)
+# Python
+pkg_check_modules(python REQUIRED python)
+include_directories(${python_INCLUDE_DIRS})
+
# cURL
if(NOT WIN32)
find_library(CURL curl)
@@ -182,6 +186,7 @@ endif(WITH-QMF)
### Subdirectories
add_subdirectory(broker)
add_subdirectory(lib)
+add_subdirectory(gofer)
add_subdirectory(host)
add_subdirectory(network)
add_subdirectory(service)
@@ -196,6 +201,7 @@ install(FILES include/matahari/logging.h DESTINATION include/matahari)
install(FILES include/matahari/utilities.h DESTINATION include/matahari)
install(FILES include/matahari/errors.h DESTINATION include/matahari)
install(FILES include/matahari/dnssrv.h DESTINATION include/matahari)
+install(FILES include/matahari/gofer.h DESTINATION include/matahari)
install(FILES include/matahari/host.h DESTINATION include/matahari)
install(FILES include/matahari/network.h DESTINATION include/matahari)
install(FILES include/matahari/sysconfig.h DESTINATION include/matahari)
diff --git a/src/gofer/.gitignore b/src/gofer/.gitignore
new file mode 100644
index 0000000..52e4e61
--- /dev/null
+++ b/src/gofer/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
diff --git a/src/gofer/CMakeLists.txt b/src/gofer/CMakeLists.txt
new file mode 100644
index 0000000..adf953c
--- /dev/null
+++ b/src/gofer/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(BASE "gofer")
+set(BASE_LIB "m${BASE}")
+set(QMF_AGENT "matahari-qmf-${BASE}d")
+
+# QMF daemon
+if(WITH-QMF)
+ set(SCHEMA_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/qmf/org/matahariproject/gofer/QmfPackage.cpp)
+ generate_qmf_schemas(${CMAKE_CURRENT_SOURCE_DIR}/schema.xml ${SCHEMA_SOURCES})
+ include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+ add_executable(${QMF_AGENT} ${BASE}-qmf.cpp ${SCHEMA_SOURCES})
+ target_link_libraries(${QMF_AGENT} ${BASE_LIB} mcommon_qmf)
+
+ create_manpage(${QMF_AGENT} ${AGENT_MAN_SECTION} ${AGENT_MAN_DESC})
+ create_service_scripts(${BASE})
+
+ install(TARGETS ${QMF_AGENT} DESTINATION sbin)
+
+endif(WITH-QMF)
+
diff --git a/src/gofer/gofer-qmf.cpp b/src/gofer/gofer-qmf.cpp
new file mode 100644
index 0000000..82b1489
--- /dev/null
+++ b/src/gofer/gofer-qmf.cpp
@@ -0,0 +1,199 @@
+/* matahari-gofer.cpp - Copyright (C) 2009 Red Hat, Inc.
+ * Written by Zane Bitter <zbitter(a)redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * \file
+ * \brief Gofer QMF Agent
+ */
+
+#include "config.h"
+
+#include <set>
+#include "matahari/agent.h"
+#include "matahari/object.h"
+#include <qmf/Data.h>
+#include "qmf/org/matahariproject/gofer/QmfPackage.h"
+
+extern "C" {
+#include "matahari/gofer.h"
+#include "matahari/logging.h"
+#include "matahari/errors.h"
+}
+
+
+class GoferAgent;
+
+class GoferPlugin: public MatahariObject
+{
+public:
+ GoferPlugin(GoferAgent& agent, mh_gofer_plugin_t& plugin);
+ ~GoferPlugin();
+
+ bool invoke(qmf::AgentSession& session, qmf::AgentEvent& event);
+
+private:
+ GoferAgent& _agent;
+ std::string _name;
+ mh_gofer_plugin_t _plugin;
+};
+
+
+class GoferAgent : public MatahariAgent
+{
+public:
+ virtual ~GoferAgent();
+
+ virtual int setup(qmf::AgentSession session);
+ virtual gboolean invoke(qmf::AgentSession session, qmf::AgentEvent event,
+ gpointer user_data);
+
+private:
+ friend class GoferPlugin;
+ qmf::org::matahariproject::gofer::PackageDefinition _package;
+ ObjectManager *_objectManager;
+
+ typedef std::map<std::string, GoferPlugin*> PluginMap;
+ PluginMap _plugins;
+
+ void addPlugin(mh_gofer_plugin_t& plugin);
+};
+
+
+int
+main(int argc, char **argv)
+{
+ mh_gofer_init(1, &argv[0]);
+
+ GoferAgent *agent = new GoferAgent();
+ int rc = agent->init(argc, argv, "gofer");
+ if (rc == 0) {
+ agent->run();
+ }
+
+ mh_gofer_deinit();
+ return rc;
+}
+
+gboolean
+GoferAgent::invoke(qmf::AgentSession session, qmf::AgentEvent event,
+ gpointer user_data)
+{
+ if (!_objectManager->invoke(session, event)) {
+ session.raiseException(event, mh_result_to_str(MH_RES_NOT_IMPLEMENTED));
+ }
+
+ return TRUE;
+}
+
+void GoferAgent::addPlugin(mh_gofer_plugin_t& plugin)
+{
+ _plugins[std::string(plugin.name)] = new GoferPlugin(*this, plugin);
+}
+
+int
+GoferAgent::setup(qmf::AgentSession session)
+{
+ int num_plugins = 0;
+ mh_gofer_plugin_t *plugins = NULL;
+
+ _package.configure(session);
+ _objectManager = new ObjectManager(session);
+
+ if (mh_gofer_load_plugins(&num_plugins, &plugins)) {
+ return 1;
+ }
+
+ for (int i = 0; i < num_plugins; i++) {
+ addPlugin(plugins[i]);
+ }
+ return 0;
+}
+
+GoferAgent::~GoferAgent()
+{
+ delete _objectManager;
+}
+
+
+GoferPlugin::GoferPlugin(GoferAgent& agent, mh_gofer_plugin_t& plugin):
+ MatahariObject(agent._package.data_Plugin),
+ _agent(agent),
+ _name(plugin.name),
+ _plugin(plugin)
+{
+ mh_info("Creating Plugin %s", _name.c_str());
+
+ _instance.setProperty("name", _name);
+ *_agent._objectManager += this;
+}
+
+GoferPlugin::~GoferPlugin()
+{
+ *_agent._objectManager -= this;
+}
+
+bool
+GoferPlugin::invoke(qmf::AgentSession& session, qmf::AgentEvent& event)
+{
+ if (event.getType() != qmf::AGENT_METHOD) {
+ return true;
+ }
+
+ const std::string& methodName(event.getMethodName());
+ qpid::types::Variant::Map& args = event.getArguments();
+
+ if (methodName == "invoke") {
+ mh_gofer_rmi_t call;
+ mh_gofer_rmi_create(&call,
+ args["classname"].asString().c_str(),
+ args["method"].asString().c_str());
+
+ qpid::types::Variant::List arglist(args["args"].asList());
+ for (qpid::types::Variant::List::iterator iter = arglist.begin();
+ iter != arglist.end();
+ iter++) {
+ mh_gofer_rmi_add_arg(call, iter->asString().c_str());
+ }
+ qpid::types::Variant::Map argmap(args["kwargs"].asMap());
+ for (qpid::types::Variant::Map::iterator iter = argmap.begin();
+ iter != argmap.end();
+ iter++) {
+ mh_gofer_rmi_add_kw_arg(call,
+ iter->first.c_str(),
+ iter->second.asString().c_str());
+ }
+ char *output;
+ enum mh_result result = mh_gofer_invoke(&_plugin, call, &output);
+
+ mh_gofer_rmi_destroy(call);
+
+ if (result != MH_RES_SUCCESS) {
+ session.raiseException(event, mh_result_to_str(result));
+ return true;
+ }
+
+ event.addReturnArgument("result", output);
+ free(output);
+ } else {
+ return false;
+ }
+
+ session.methodSuccess(event);
+ return true;
+}
+
diff --git a/src/gofer/mhgofer_test.py b/src/gofer/mhgofer_test.py
new file mode 100644
index 0000000..ae39ded
--- /dev/null
+++ b/src/gofer/mhgofer_test.py
@@ -0,0 +1,59 @@
+import unittest
+import json
+import qmf.console
+
+class GoferTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls._session = qmf.console.Session()
+ cls._broker = cls._session.addBroker('amqp://localhost:49000')
+
+ @classmethod
+ def tearDownClass(cls):
+ cls._session.delBroker(cls._broker)
+ cls._session.close()
+
+ def get_plugin(self, name):
+ p = self._session.getObjects(_class='Plugin',
+ _package='org.matahariproject.gofer',
+ name=name)
+ self.assertTrue(p)
+ return p[0]
+
+ def test_hello(self):
+ builtin = self.get_plugin('builtin')
+ self.assertIsNotNone(builtin)
+ result = builtin.invoke('Admin', 'hello', [], {})
+ self.assertEqual(result.status, 0)
+ self.assertIn('result', result.outArgs)
+ resJSON = result.outArgs['result']
+ resDict = json.loads(resJSON)
+ self.assertIn('retval', resDict)
+ retval = resDict['retval']
+ self.assertTrue(retval.startswith('Hello'))
+
+ def test_help(self):
+ builtin = self.get_plugin('builtin')
+ self.assertIsNotNone(builtin)
+ result = builtin.invoke('Admin', 'help', [], {})
+ self.assertEqual(result.status, 0)
+ self.assertIn('result', result.outArgs)
+ resJSON = result.outArgs['result']
+ resDict = json.loads(resJSON)
+ self.assertIn('retval', resDict)
+ retval = resDict['retval']
+ self.assertTrue(retval.startswith('Plugins:'))
+
+ def test_args_invalid(self):
+ builtin = self.get_plugin('builtin')
+ self.assertIsNotNone(builtin)
+ result = builtin.invoke('Admin', 'hello', ['1', '2'], {'3': '4'})
+ self.assertEqual(result.status, 0)
+ self.assertIn('result', result.outArgs)
+ resJSON = result.outArgs['result']
+ resDict = json.loads(resJSON)
+ self.assertNotIn('retval', resDict)
+ self.assertIn('exval', resDict)
+ self.assertIn('xclass', resDict)
+ self.assertEqual(resDict['xclass'], 'TypeError')
diff --git a/src/gofer/schema.xml b/src/gofer/schema.xml
new file mode 100644
index 0000000..54fbe3c
--- /dev/null
+++ b/src/gofer/schema.xml
@@ -0,0 +1,15 @@
+<schema package="org.matahariproject.gofer">
+
+ <class name="Plugin">
+ <property name="name" type="sstr" access="RO" />
+
+ <method name="invoke" desc="Make a remote procedure call">
+ <arg name="classname" dir="I" type="sstr" />
+ <arg name="method" dir="I" type="sstr" />
+ <arg name="args" dir="I" type="list" />
+ <arg name="kwargs" dir="I" type="map" />
+ <arg name="result" dir="O" type="lstr" />
+ </method>
+ </class>
+
+</schema>
diff --git a/src/include/matahari/gofer.h b/src/include/matahari/gofer.h
new file mode 100644
index 0000000..e6258b1
--- /dev/null
+++ b/src/include/matahari/gofer.h
@@ -0,0 +1,100 @@
+
+#ifndef __MH_GOFER_H__
+#define __MH_GOFER_H__
+
+#include "matahari/errors.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct mh_gofer_plugin_impl;
+
+typedef struct {
+ char *name;
+ unsigned int type;
+ struct mh_gofer_plugin_impl *impl;
+} mh_gofer_plugin_t;
+
+struct mh_gofer_rmi;
+typedef struct mh_gofer_rmi* mh_gofer_rmi_t;
+
+
+/**
+ * Initialise the Gofer library with the given CLI options to goferd.
+ */
+void
+mh_gofer_init(int argc, char **argv);
+
+/**
+ * De-initialise the Gofer library and clean up.
+ */
+void
+mh_gofer_deinit(void);
+
+/**
+ * Load the available Gofer plugins.
+ * \param count pointer in which to store the plugin count. Should be
+ * initialised to 0.
+ * \param plugins pointer in which to store the list of plugins. Should be
+ * initialised to NULL.
+ * \return zero on success, non-zero otherwise.
+ */
+int
+mh_gofer_load_plugins(int *count, mh_gofer_plugin_t **plugins);
+
+/**
+ * Create a Remote Method Invocation.
+ * This should be freed with mh_gofer_rmi_destroy() when done.
+ * \param pcall pointer in which to store the RMI data.
+ * \param classname the name of the class containing the method to call.
+ * \param method the name of the method to invoke.
+ * \return zero on success, non-zero otherwise.
+ */
+int
+mh_gofer_rmi_create(mh_gofer_rmi_t *pcall,
+ const char *classname,
+ const char *method);
+
+/**
+ * Clean up a Remote Method Invocation.
+ * \param call the data to clean up.
+ */
+int
+mh_gofer_rmi_destroy(mh_gofer_rmi_t call);
+
+/**
+ * Add a positional argument to the argument list for the RMI.
+ * \param call the RMI data.
+ * \param arg the argument to add.
+ */
+int
+mh_gofer_rmi_add_arg(mh_gofer_rmi_t call, const char *arg);
+
+/**
+ * Add a keyword argument to the keyword argument map for the RMI.
+ * \param call the RMI data.
+ * \param key the argument keyword.
+ * \param arg the argument to add.
+ */
+int
+mh_gofer_rmi_add_kw_arg(mh_gofer_rmi_t call, const char *key, const char *arg);
+
+/**
+ * Invoke a remote method call.
+ * \param plugin the plugin to invoke the method from.
+ * \param call the RMI data.
+ * \param output pointer in which to store the result string (in JSON format).
+ * This needs to be freed with free().
+ * \return the return status code.
+ */
+enum mh_result
+mh_gofer_invoke(const mh_gofer_plugin_t *plugin,
+ const struct mh_gofer_rmi *call,
+ char **output);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index 67cad82..9fd38ba 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -1,6 +1,6 @@
# Library
-set(LIBS mcommon mhost mnetwork mservice msysconfig)
+set(LIBS mcommon mgofer mhost mnetwork mservice msysconfig)
include_directories(${pcre_INCLUDE_DIRS})
# See http://sourceware.org/autobook/autobook/autobook_91.html for
@@ -14,6 +14,10 @@ if(HAVE_RESOLV_H)
target_link_libraries(mcommon resolv)
endif(HAVE_RESOLV_H)
+add_library (mgofer SHARED gofer.c gofer-python.c)
+set_target_properties(mgofer PROPERTIES SOVERSION 1.0.0)
+target_link_libraries(mgofer mcommon ${python_LIBRARIES})
+
add_library (mhost SHARED host.c host_${VARIANT}.c)
set_target_properties(mhost PROPERTIES SOVERSION 1.0.0)
target_link_libraries(mhost ${uuid_LIBRARIES} ${pcre_LIBRARIES} mcommon ${SIGAR} ${glib_LIBRARIES})
diff --git a/src/lib/gofer-python.c b/src/lib/gofer-python.c
new file mode 100644
index 0000000..ce81723
--- /dev/null
+++ b/src/lib/gofer-python.c
@@ -0,0 +1,362 @@
+
+#include <Python.h>
+#include <stdio.h>
+
+#include "matahari/gofer.h"
+#include "gofer_private.h"
+#include "matahari/logging.h"
+#include "matahari/errors.h"
+
+
+#define PLUGIN_MODULE "gofer.agent.plugin"
+#define MESSAGING_MODULE "gofer.messaging"
+#define DISPATCHER_MODULE "gofer.rmi.dispatcher"
+
+
+struct mh_gofer_plugin_impl {
+ PyObject *plugin;
+};
+
+
+static void
+init(int argc, char **argv)
+{
+ if (!Py_IsInitialized()) {
+ Py_Initialize();
+ }
+ PySys_SetArgvEx(argc, argv, 0);
+}
+
+static void
+deinit(void)
+{
+ if (Py_IsInitialized()) {
+ Py_Finalize();
+ }
+}
+
+/* Equivalent to the Python code:
+ * def get_py_class_instance(package, classname, args):
+ * module = builtins.__import__(package)
+ * classobject = getattr(module, classname)
+ * instance = classobject(*args)
+ * return instance
+ */
+static PyObject *
+get_py_class_instance(const char *package, const char *classname,
+ PyObject *args)
+{
+ PyObject *module, *classobject, *instance;
+
+ module = PyImport_ImportModule(package);
+ if (!module) {
+ mh_err("Error importing module %s", package);
+ PyErr_Print();
+ return NULL;
+ }
+
+ classobject = PyObject_GetAttrString(module, classname);
+ if (!classobject) {
+ mh_err("Error importing %s class", classname);
+ PyErr_Print();
+ Py_DECREF(module);
+ return NULL;
+ }
+
+ instance = PyObject_CallObject(classobject, args);
+ if (!instance) {
+ mh_err("Error instantiating %s object", classname);
+ PyErr_Print();
+ }
+
+ Py_DECREF(classobject);
+ Py_DECREF(module);
+ return instance;
+}
+
+/* Equivalent to the Python code:
+ * def load_plugin_list():
+ * loader = gofer.agent.plugin.PluginLoader()
+ * plugins = loader.load()
+ * return plugins
+ */
+static PyObject *
+load_plugin_list(void)
+{
+ PyObject *loader, *plugins;
+
+ loader = get_py_class_instance(PLUGIN_MODULE, "PluginLoader", NULL);
+ if (loader == NULL) {
+ mh_err("Failed to create plugin loader");
+ PyErr_Print();
+ return NULL;
+ }
+
+ plugins = PyObject_CallMethod(loader, "load", NULL);
+ if (plugins == NULL) {
+ mh_err("Failed to import Gofer Python module");
+ PyErr_Print();
+ }
+
+ Py_DECREF(loader);
+ return plugins;
+}
+
+static int
+load_plugins(int *count, mh_gofer_plugin_t **plugins)
+{
+ PyObject *pluginList;
+ Py_ssize_t length, i;
+
+ if (!Py_IsInitialized()) {
+ mh_err("Python not initialised");
+ return 1;
+ }
+
+ pluginList = load_plugin_list();
+ if (!pluginList) {
+ return 1;
+ }
+
+ if (!PySequence_Check(pluginList)) {
+ mh_err("Plugin list is not a sequence");
+ return 1;
+ }
+
+ length = PySequence_Length(pluginList);
+ if (length < 0) {
+ mh_err("Error getting length of plugin list");
+ return 1;
+ }
+
+ for (i = 0; i < length; i++) {
+ mh_gofer_plugin_t *tmp_list;
+ mh_gofer_plugin_t new_plugin;
+ struct mh_gofer_plugin_impl *new_impl;
+
+ PyObject *plugin = PySequence_GetItem(pluginList, i);
+ if (!plugin) {
+ mh_err("Error retrieving plugin module");
+ PyErr_Print();
+ continue;
+ }
+
+ // plugin is an instance of the gofer.agent.plugin.Plugin class
+ {
+ char *name_str;
+ PyObject *name = PyObject_GetAttrString(plugin, "name");
+
+ if (!name) {
+ mh_err("Failed to get plugin name");
+ PyErr_Print();
+ Py_DECREF(plugin);
+ continue;
+ }
+
+ name_str = PyString_AsString(name);
+ if (!name_str) {
+ mh_err("Module name is not a string");
+ PyErr_Print();
+ Py_DECREF(name);
+ Py_DECREF(plugin);
+ continue;
+ }
+ new_plugin.name = strdup(name_str);
+ Py_DECREF(name);
+ }
+
+ tmp_list = realloc(*plugins, sizeof(mh_gofer_plugin_t) * (*count + 1));
+ if (!tmp_list) {
+ mh_err("Out of Memory");
+ return 1;
+ }
+ *plugins = tmp_list;
+
+ new_impl = malloc(sizeof(*new_impl));
+ if (!new_impl) {
+ mh_err("Out of Memory");
+ return 1;
+ }
+ memset(new_impl, 0, sizeof(*new_impl));
+ new_impl->plugin = plugin;
+
+ new_plugin.type = MH_GOFER_PYTHON;
+ new_plugin.impl = new_impl;
+
+ memcpy(&tmp_list[(*count)++], &new_plugin, sizeof(new_plugin));
+ }
+
+ Py_DECREF(pluginList);
+
+ return 0;
+}
+
+/* Equivalent to the Python code:
+ * def get_classes(plugin):
+ * dispatcher = plugin.dispatcher
+ * return dispatcher.classes()
+ */
+static PyObject *
+get_classes(PyObject *plugin)
+{
+ PyObject *dispatcher, *classes;
+
+ dispatcher = PyObject_GetAttrString(plugin, "dispatcher");
+ if (!dispatcher) {
+ mh_err("Error getting plugin dispatcher");
+ PyErr_Print();
+ return NULL;
+ }
+
+ classes = PyObject_GetAttrString(dispatcher, "classes");
+ if (!classes) {
+ mh_err("Error getting plugin class list");
+ PyErr_Print();
+ }
+
+ Py_DECREF(dispatcher);
+
+ return classes;
+}
+
+/* Equivalent to the Python code:
+ * def get_rmi(plugin, envelope):
+ * classes = get_classes(plugin)
+ * return gofer.rmi.dispatcher.RMI(envelope, classes)
+ */
+static PyObject *
+get_rmi(PyObject *plugin, PyObject *envelope)
+{
+ PyObject *classes, *args, *rmi;
+
+ classes = get_classes(plugin);
+ if (!classes) {
+ Py_DECREF(envelope);
+ return NULL;
+ }
+
+ args = PyTuple_Pack(2, envelope, classes);
+ if (!args) {
+ mh_err("Error creating argument list");
+ PyErr_Print();
+ Py_DECREF(envelope);
+ Py_DECREF(classes);
+ return NULL;
+ }
+
+ rmi = get_py_class_instance(DISPATCHER_MODULE, "RMI", args);
+ if (!rmi) {
+ mh_err("Error creating RMI object");
+ PyErr_Print();
+ }
+
+ Py_DECREF(envelope);
+ Py_DECREF(classes);
+ Py_DECREF(args);
+
+ return rmi;
+}
+
+static PyObject*
+get_method_call_envelope(const struct mh_gofer_rmi *call)
+{
+ PyObject *envelope, *ret;
+
+ char *json = NULL;
+ asprintf(&json, "{\n"
+ "\"classname\": \"%s\",\n"
+ "\"method\": \"%s\",\n"
+ "\"args\": [%s],\n"
+ "\"kws\": {%s}\n"
+ "}",
+ call->classname, call->method,
+ call->args.str ?: "",
+ call->kwargs.str ?: "");
+
+ if (!json) {
+ return NULL;
+ }
+
+ envelope = get_py_class_instance(MESSAGING_MODULE, "Envelope", NULL);
+ if (!envelope) {
+ return NULL;
+ }
+
+ ret = PyObject_CallMethod(envelope, "load", "(s)", json);
+ free(json);
+ if (!ret) {
+ mh_err("Error loading JSON");
+ PyErr_Print();
+ Py_DECREF(envelope);
+ return NULL;
+ }
+ Py_DECREF(ret);
+
+ return envelope;
+}
+
+static enum mh_result
+invoke(const struct mh_gofer_plugin_impl *plugin,
+ const struct mh_gofer_rmi *call,
+ char **output)
+{
+ PyObject *envelope, *rmi, *result;
+
+ mh_debug("Got method invocation \"%s\"", call->method);
+
+ *output = NULL;
+
+ envelope = get_method_call_envelope(call);
+ if (!envelope) {
+ return MH_RES_INVALID_ARGS;
+ }
+
+ rmi = get_rmi(plugin->plugin, envelope);
+ if (!rmi) {
+ Py_DECREF(envelope);
+ return MH_RES_INVALID_ARGS;
+ }
+
+ // result = get_rmi(plugin, get_method_call_envelope(call))()
+ result = PyObject_CallObject(rmi, NULL);
+ if (!result) {
+ mh_err("Error invoking remote method");
+ PyErr_Print();
+ Py_DECREF(rmi);
+ Py_DECREF(envelope);
+ return MH_RES_BACKEND_ERROR;
+ }
+
+ mh_debug("Remote method invocation successful");
+
+ Py_DECREF(rmi);
+ Py_DECREF(envelope);
+
+ // jsonResult = str(result)
+ {
+ char *result_str;
+ PyObject *jsonResult = PyObject_Str(result);
+ Py_DECREF(result);
+
+ if (!jsonResult) {
+ return MH_RES_BACKEND_ERROR;
+ }
+ result_str = PyString_AsString(jsonResult);
+ if (!result_str) {
+ Py_DECREF(jsonResult);
+ return MH_RES_BACKEND_ERROR;
+ }
+ *output = strdup(result_str);
+ Py_DECREF(jsonResult);
+ }
+
+ return MH_RES_SUCCESS;
+}
+
+
+struct mh_gofer_api mh_gofer_python_api = {
+ init,
+ deinit,
+ load_plugins,
+ invoke,
+};
diff --git a/src/lib/gofer.c b/src/lib/gofer.c
new file mode 100644
index 0000000..69fab46
--- /dev/null
+++ b/src/lib/gofer.c
@@ -0,0 +1,153 @@
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "matahari/gofer.h"
+#include "gofer_private.h"
+
+
+#define API_IN_RANGE(INDEX) ((INDEX) >= 0 && \
+ (INDEX) < (sizeof(apis) / sizeof(*apis)))
+
+#define API_DEFINED(INDEX) (API_IN_RANGE(INDEX) && (apis[INDEX] != NULL))
+
+#define FOREACH_API(P_API, API) \
+ for ((P_API) = &apis[0], (API) = *(P_API); \
+ API_IN_RANGE((P_API) - apis); \
+ (API) = *++(P_API)) \
+ if ((API) != NULL)
+
+
+const struct mh_gofer_api * const apis[] = {
+ [MH_GOFER_PYTHON] = &mh_gofer_python_api,
+};
+
+
+void
+mh_gofer_init(int argc, char **argv)
+{
+ const struct mh_gofer_api * const *p_api, *api;
+
+ FOREACH_API(p_api, api) {
+ api->init(argc, argv);
+ }
+}
+
+void
+mh_gofer_deinit(void)
+{
+ const struct mh_gofer_api * const *p_api, *api;
+
+ FOREACH_API(p_api, api) {
+ api->deinit();
+ }
+}
+
+int
+mh_gofer_load_plugins(int *count, mh_gofer_plugin_t **plugins)
+{
+ const struct mh_gofer_api * const *p_api, *api;
+
+ FOREACH_API(p_api, api) {
+ api->load_plugins(count, plugins);
+ }
+
+ return 0;
+}
+
+int
+mh_gofer_rmi_create(mh_gofer_rmi_t *pcall,
+ const char *classname,
+ const char *method)
+{
+ *pcall = malloc(sizeof(struct mh_gofer_rmi));
+ if (!*pcall) {
+ return -1;
+ }
+
+ memset(*pcall, 0, sizeof(struct mh_gofer_rmi));
+ (*pcall)->classname = strdup(classname);
+ (*pcall)->method = strdup(method);
+
+ return 0;
+}
+
+int
+mh_gofer_rmi_destroy(mh_gofer_rmi_t call)
+{
+ free(call->classname);
+ call->classname = NULL;
+
+ free(call->method);
+ call->method = NULL;
+
+ free(call->args.str);
+ call->args.str = NULL;
+ call->args.len = 0;
+
+ free(call->kwargs.str);
+ call->kwargs.str = NULL;
+ call->kwargs.len = 0;
+
+ free(call);
+ return 0;
+}
+
+#define JSON_SEP ", "
+
+static int
+add_arg(struct argstring *argstring, const char *arg, size_t len)
+{
+ char *s = realloc(argstring->str, argstring->len + len + sizeof(JSON_SEP));
+ if (!s) {
+ return 1;
+ }
+
+ if (argstring->len) {
+ strcpy(&s[argstring->len], JSON_SEP);
+ argstring->len += strlen(JSON_SEP);
+ }
+
+ memcpy(&s[argstring->len], arg, len + 1);
+
+ argstring->str = s;
+ argstring->len += len;
+
+ return 0;
+}
+
+int
+mh_gofer_rmi_add_arg(mh_gofer_rmi_t call, const char *arg)
+{
+ return add_arg(&call->args, arg, strlen(arg));
+}
+
+int
+mh_gofer_rmi_add_kw_arg(mh_gofer_rmi_t call, const char *key, const char *arg)
+{
+ int ret = 1;
+ size_t len = strlen(key) + strlen(arg) + 4;
+ char *entry = malloc(len + 1);
+
+ if (entry) {
+ sprintf(entry, "\"%s\": %s", key, arg);
+ ret = add_arg(&call->kwargs, entry, len);
+ free(entry);
+ }
+
+ return ret;
+}
+
+
+enum mh_result
+mh_gofer_invoke(const mh_gofer_plugin_t *plugin,
+ const struct mh_gofer_rmi *call,
+ char **output)
+{
+ if (!API_DEFINED(plugin->type)) {
+ return MH_RES_BACKEND_ERROR;
+ }
+
+ return apis[plugin->type]->invoke(plugin->impl, call, output);
+}
diff --git a/src/lib/gofer_private.h b/src/lib/gofer_private.h
new file mode 100644
index 0000000..0167789
--- /dev/null
+++ b/src/lib/gofer_private.h
@@ -0,0 +1,35 @@
+
+#ifndef __MH_GOFER_PRIVATE_H__
+#define __MH_GOFER_PRIVATE_H__
+
+#include "matahari/gofer.h"
+
+enum mh_gofer_plugin_type {
+ MH_GOFER_INVALID,
+ MH_GOFER_PYTHON,
+};
+
+struct mh_gofer_api {
+ void (*init)(int, char**);
+ void (*deinit)(void);
+ int (*load_plugins)(int*, mh_gofer_plugin_t**);
+ enum mh_result (*invoke)(const struct mh_gofer_plugin_impl*,
+ const struct mh_gofer_rmi*,
+ char **output);
+};
+
+extern struct mh_gofer_api mh_gofer_python_api;
+
+
+struct argstring {
+ char *str;
+ size_t len;
+};
+
+struct mh_gofer_rmi {
+ char *classname;
+ char *method;
+ struct argstring args, kwargs;
+};
+
+#endif
12 years, 5 months
Matahari-Gofer integration options
by Zane Bitter
Jeff has floated a couple of options for how we could potentially
integrate Gofer with Matahari, which I'm going to attempt to summarise
here for comment:
1) Just use the Matahari broker
Gofer does not use QMF, and porting it to use QMF would be a substantial
amount of work. Currently it runs over unalloyed AMQP. Since the
Matahari broker is just an AMQP broker with support for QMF on top, we
could simply reconfigure Gofer to use the Matahari broker. (It would be
desirable if this was done by reading the same Matahari configuration
files as other agents.)
One downside to this would be that the ACLs we use to configure
privileges in Qpid depend on everything passing through the QMF queues.
If Gofer is to use different queues on the same broker, we would need to
rewrite the ACLs to allow for it. The difficulty of this task is unknown
and would require further investigation.
This approach would also preclude using the Matahari Shell (when it is
completed) to interact with Gofer plugins.
2) Write a C++ agent reusing some of the Gofer code
In this approach, we would create a 'Gofer' agent in C++ like an
ordinary Matahari agent, then cherry-pick the existing plugin and
dispatch code from Gofer and plumb it in.[1]
The big unknowns with this approach revolve around the dynamic
generation of schemata. In particular, how we would handle method
arguments, which are statically typed[2] in QMF. (Programmatic
generation of schemata in C++ is itself not that difficult.)
Planned investigations:
Jeff - Look into how we can reuse code in this scenario
Zane - Look into how we can handle dynamically generated schemata
One thing that remains somewhat unclear is the exact goal of any
integration effort. Some possible requirements are:
* Allow end users (i.e. sysadmins) to create agents to control their
own apps without writing C++.
* Do so using the same infrastructure as Matahari (TCP port, broker,
configuration, security model).
* Make the functionality available through the Matahari Shell.
* Avoid reinventing the wheel by reusing code as much as possible.
Any and all comments/questions welcome.
regards,
Zane.
[1] Further mixed metaphors will follow.
[2] However they don't appear to be *strongly* typed, which may open the
door for us.
12 years, 5 months
Ubuntu packages ready for testing
by Russell Bryant
Greetings,
We now have Ubuntu packages of Matahari and some dependencies in a PPA
(Personal Package Archive). We have builds available for all of the
currently supported versions of Ubuntu. Here is the page for the
Matahari PPA:
https://launchpad.net/~matahari/+archive/ppa
Starting from a fresh install of Ubuntu, here is how you get the
Matahari packages.
# To get add-apt-repository
$ sudo apt-get install python-software-properties
$ sudo add-apt-repository ppa:matahari/ppa
$ sudo apt-get update
At this point you can see the list of available packages from our PPA:
$ apt-cache search matahari
matahari-agent-lib - C++ library containing the base class
matahari-broker - Matahari Broker
matahari-consoles - QMF console for monitoring agents
matahari-core - Matahari Core files
matahari-dev - Headers and shared libraries for developing
matahari-host - Matahari Host Agent
matahari-lib - C libraries used by Matahari agents
matahari-network - Matahari Network Agent
matahari-service - Matahari Services Agent
matahari-sysconfig - Matahari Sysconfig Agent
Now you can install the broker and whichever agents you want.
$ sudo apt-get install matahari-broker matahari-host \
> matahari-service
If you try these packages, please let us know if you have any problems.
Thanks,
--
Russell Bryant
12 years, 5 months