blob: f38f5873d93dcd95dbb8a72446a2f97bdc80e088 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __MODULE_MANAGER_HPP__
#define __MODULE_MANAGER_HPP__
#include <list>
#include <mutex>
#include <string>
#include <vector>
#include <glog/logging.h>
#include <mesos/mesos.hpp>
#include <mesos/module.hpp>
#include <mesos/module/module.hpp>
#include <process/owned.hpp>
#include <stout/check.hpp>
#include <stout/dynamiclibrary.hpp>
#include <stout/hashmap.hpp>
#include <stout/nothing.hpp>
#include <stout/synchronized.hpp>
#include <stout/try.hpp>
#include "messages/messages.hpp"
namespace mesos {
namespace modules {
// Mesos module loading.
//
// Phases:
// 1. Load dynamic libraries that contain modules from the Modules
// instance which may have come from a commandline flag.
// 2. Verify versions and compatibilities.
// a) Library compatibility. (Module API version check)
// b) Module compatibility. (Module Kind version check)
// 3. Instantiate singleton per module. (happens in the library)
// 4. Bind reference to use case. (happens in Mesos)
class ModuleManager
{
public:
// Loads dynamic libraries, and verifies the compatibility of the
// modules in them.
//
// NOTE: If loading fails at a particular library we don't unload
// all of the already loaded libraries.
static Try<Nothing> load(const Modules& modules)
{
return loadManifest(modules);
}
// NOTE: If loading fails at a particular library we don't unload
// all of the already loaded libraries.
static Try<Nothing> load(const std::string& modulesDir);
// create() should be called only after load().
template <typename T>
static Try<T*> create(
const std::string& moduleName,
const Option<Parameters>& params = None())
{
synchronized (mutex) {
if (!moduleBases.contains(moduleName)) {
return Error(
"Module '" + moduleName + "' unknown");
}
Module<T>* module = (Module<T>*) moduleBases[moduleName];
if (module->create == nullptr) {
return Error(
"Error creating module instance for '" + moduleName + "': "
"create() method not found");
}
std::string expectedKind = kind<T>();
if (expectedKind != module->kind) {
return Error(
"Error creating module instance for '" + moduleName + "': "
"module is of kind '" + module->kind + "', but the requested "
"kind is '" + expectedKind + "'");
}
T* instance =
module->create(
params.isSome() ? params.get() : moduleParameters[moduleName]);
if (instance == nullptr) {
return Error("Error creating Module instance for '" + moduleName + "'");
}
return instance;
}
}
template <typename T>
static bool contains(const std::string& moduleName)
{
synchronized (mutex) {
return (moduleBases.contains(moduleName) &&
moduleBases[moduleName]->kind == stringify(kind<T>()));
}
}
// Returns all module names that have been loaded that implement the
// specified interface 'T'. For example:
//
// std::vector<std::string> modules = ModuleManager::find<Hook>();
//
// Will return all of the module names for modules that implement
// the Isolator interface.
template <typename T>
static std::vector<std::string> find()
{
std::vector<std::string> names;
synchronized (mutex) {
foreachpair (const std::string& name, ModuleBase* base, moduleBases) {
if (base->kind == stringify(kind<T>())) {
names.push_back(name);
}
}
}
return names;
}
// Exposed just for testing so that we can unload a given
// module and remove it from the list of ModuleBases.
static Try<Nothing> unload(const std::string& moduleName);
private:
static void initialize();
static Try<Nothing> loadManifest(const Modules& modules);
static Try<Nothing> verifyModule(
const std::string& moduleName,
const ModuleBase* moduleBase);
// Allow multiple calls to `load()` by verifying that the modules with same
// name are indeed identical. Thus, multiple loadings of a module manifest
// are harmless.
static Try<Nothing> verifyIdenticalModule(
const std::string& libraryName,
const Modules::Library::Module& module,
const ModuleBase* base);
static std::mutex mutex;
static hashmap<std::string, std::string> kindToVersion;
// Mapping from "module name" to the actual ModuleBase. If two
// modules from different libraries have the same name then the last
// one specified in the protobuf Modules will be picked.
static hashmap<std::string, ModuleBase*> moduleBases;
// Module-specific command-line parameters.
static hashmap<std::string, Parameters> moduleParameters;
// A list of dynamic libraries to keep the object from getting
// destructed.
// NOTE: We do leak loaded dynamic libraries. This is to allow
// modules to make use of e.g., libprocess which otherwise could
// lead to situations where libprocess (which we depend on ourself)
// is unloaded before the destructor of below `static` is called.
// Unloading the dynamic library could then lead to the linker
// attempting to unload the libprocess loaded from the module which
// is not there anymore.
static hashmap<std::string, DynamicLibrary*> dynamicLibraries;
// Module to library name mapping.
static hashmap<std::string, std::string> moduleLibraries;
};
} // namespace modules {
} // namespace mesos {
#endif // __MODULE_MANAGER_HPP__