diff --git a/CMakeLists.txt b/CMakeLists.txt index d15aef4f0..19518eba1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,6 +147,7 @@ add_subdirectory(doc) add_subdirectory(test) add_subdirectory(pki) add_subdirectory(contrib) +add_subdirectory(python) set(CPACK_PACKAGE_NAME "Icinga2") set(CPACK_PACKAGE_VENDOR "Icinga Development Team") diff --git a/INSTALL b/INSTALL index 3f9b3246c..451e0cec8 100644 --- a/INSTALL +++ b/INSTALL @@ -27,9 +27,9 @@ parentheses): on Debian) * GNU bison (bison) * GNU flex (flex) >= 2.5.35 +* Python (python-devel on RHEL, python-dev on Debian) * recommended: libexecinfo on FreeBSD * optional: MySQL (mysql-devel on RHEL, libmysqlclient-dev on Debian) -* optional: Python (python-devel on RHEL, python-dev on Debian) Note: RHEL5 ships an ancient flex version. Updated packages are available for example from the repoforge buildtools repository. diff --git a/debian/control b/debian/control index e924f6278..7a7c94a7b 100644 --- a/debian/control +++ b/debian/control @@ -19,7 +19,9 @@ Build-Depends: bison, libpq-dev, libssl-dev, make (>= 3.81), - po-debconf + po-debconf, + python-setuptools, + python-all Standards-Version: 3.9.5 Homepage: http://www.icinga.org @@ -51,7 +53,7 @@ Description: host and network monitoring system Package: icinga2-common Architecture: all -Depends: adduser, lsb-release, ${misc:Depends} +Depends: python-icinga2 (= ${source:Version}), adduser, lsb-release, ${misc:Depends} Description: host and network monitoring system - common files Icinga 2 is a general-purpose monitoring application and the next generation after Icinga 1.x - which was a Nagios fork. It should fit the needs of a small @@ -235,3 +237,29 @@ Description: host and network monitoring system - debug symbols * Native support for Livestatus and Graphite . This package provides debug symbols for Icinga 2. + +Package: python-icinga2 +Architecture: all +Section: python +Priority: extra +Depends: ${misc:Depends}, ${python:Depends} +Description: host and network monitoring system - debug symbols + Icinga 2 is a general-purpose monitoring application and the next generation + after Icinga 1.x - which was a Nagios fork. It should fit the needs of a small + environment as well as big installations. + . + Rewritten from scratch in C++, with multi-threading and cluster support. + . + Features: + * All common features of Icinga and Nagios + * Much faster and more scalable than Icinga 1 and Nagios + * New configuration format, more intuitive and template based + * Monitoring several services by executing checks (see nagios-plugins*) + for example ICMP/ping, SMTP, POP3, HTTP, NNTP, or other TCP port + * Any small script can be used as a check plugin, when following the Nagios + plugin API + * Notifications about alerts with any custom script, or shipped examples + * Native support for Livestatus and Graphite + . + This package provides the Python module for Icinga 2. + diff --git a/debian/icinga2-common.install b/debian/icinga2-common.install index a97e0a835..f4f8e2622 100644 --- a/debian/icinga2-common.install +++ b/debian/icinga2-common.install @@ -5,4 +5,5 @@ tools/syntax/* usr/share/icinga2-common/syntax usr/bin/icinga2-build* usr/bin/icinga2-sign-key usr/sbin/icinga2-*-feature +usr/sbin/icinga2-list-objects usr/share/icinga2 diff --git a/debian/python-icinga2.install b/debian/python-icinga2.install new file mode 100644 index 000000000..2453ce7ab --- /dev/null +++ b/debian/python-icinga2.install @@ -0,0 +1 @@ +usr/lib/python*/ diff --git a/debian/rules b/debian/rules index 49dd1bd53..d445ce243 100755 --- a/debian/rules +++ b/debian/rules @@ -3,7 +3,7 @@ export DH_VERBOSE=1 %: - dh $@ + dh $@ --with python2 override_dh_auto_clean: ifeq ($(shell test -d .git && echo "git"),git) # verify we are in a GIT repo diff --git a/doc/6-configuring-icinga-2.md b/doc/6-configuring-icinga-2.md index 97f18a40c..c2840f76c 100644 --- a/doc/6-configuring-icinga-2.md +++ b/doc/6-configuring-icinga-2.md @@ -13,6 +13,7 @@ LocalStateDir |**Read-only.** Contains the path of the local state directo RunDir |**Read-only.** Contains the path of the run directory. Defaults to LocalStateDir + "/run". PkgDataDir |**Read-only.** Contains the path of the package data directory. Defaults to PrefixDir + "/share/icinga2". StatePath |**Read-write.** Contains the path of the Icinga 2 state file. Defaults to LocalStateDir + "/lib/icinga2/icinga2.state". +ObjectsPath |**Read-write.** Contains the path of the Icinga 2 objects file. Defaults to LocalStateDir + "/cache/icinga2/icinga2.debug". PidPath |**Read-write.** Contains the path of the Icinga 2 PID file. Defaults to RunDir + "/icinga2/icinga2.pid". Vars |**Read-write.** Contains a dictionary with global custom attributes. Not set by default. NodeName |**Read-write.** Contains the cluster node name. Set to the local hostname by default. diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index 5cec83f91..7f54d1040 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -82,7 +82,7 @@ static void IncludeNonLocalZone(const String& zonePath) IncludeZoneDirRecursive(zonePath); } -static bool LoadConfigFiles(const String& appType) +static bool LoadConfigFiles(const String& appType, const String& objectsFile = String()) { ConfigCompilerContext::GetInstance()->Reset(); @@ -113,7 +113,7 @@ static bool LoadConfigFiles(const String& appType) ConfigItem::Ptr item = builder->Compile(); item->Register(); - bool result = ConfigItem::ValidateItems(); + bool result = ConfigItem::ValidateItems(objectsFile); int warnings = 0, errors = 0; @@ -382,6 +382,7 @@ int Main(void) } Application::DeclareStatePath(Application::GetLocalStateDir() + "/lib/icinga2/icinga2.state"); + Application::DeclareObjectsPath(Application::GetLocalStateDir() + "/cache/icinga2/icinga2.debug"); Application::DeclarePidPath(Application::GetRunDir() + "/icinga2/icinga2.pid"); #ifndef _WIN32 @@ -543,7 +544,7 @@ int Main(void) } } - if (!LoadConfigFiles(appType)) + if (!LoadConfigFiles(appType, Application::GetObjectsPath())) return EXIT_FAILURE; if (g_AppParams.count("validate")) { diff --git a/icinga2.spec b/icinga2.spec index cb6130cd3..aaa1a326a 100644 --- a/icinga2.spec +++ b/icinga2.spec @@ -44,6 +44,10 @@ %endif %endif +%{!?__python2: %global __python2 /usr/bin/python2} +%{!?python2_sitelib: %global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} +%{!?python2_sitearch: %global python2_sitearch %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")} + %define icinga_user icinga %define icinga_group icinga %define icingacmd_group icingacmd @@ -63,7 +67,6 @@ Group: Applications/System Source: https://github.com/Icinga/%{name}/archive/v%{version}.tar.gz URL: http://www.icinga.org/ BuildRoot: %{_tmppath}/%{name}-%{version}-build - Requires: %{name}-bin = %{version} #Requires: %{name}-ido-mysql = %{version} #Requires: %{icingaweb2name} >= %{icingaweb2version} @@ -75,6 +78,7 @@ Meta package for Icinga 2 Core, DB IDO and Web. Summary: Icinga 2 binaries and libraries Group: Applications/System +Requires: python-%{name} = %{version} %if "%{_vendor}" == "suse" PreReq: permissions %endif @@ -168,6 +172,17 @@ Icinga 1.x Classic UI Standalone configuration with locations for Icinga 2. +%package -n python-icinga2 +Summary: Python module for Icinga 2 +Group: Application/System +BuildRequires: python +BuildRequires: python-devel +BuildRequires: python-setuptools +Requires: python-setuptools + +%description -n python-icinga2 +Python module for Icinga 2. + %prep %setup -q -n %{name}-%{version} @@ -207,7 +222,6 @@ cmake $CMAKE_OPTS . make %{?_smp_mflags} -rm -f components/db_ido_*sql/schema/upgrade/.gitignore %install [ "%{buildroot}" != "/" ] && [ -d "%{buildroot}" ] && rm -rf %{buildroot} @@ -448,6 +462,7 @@ exit 0 %{_bindir}/%{name}-build-ca %{_bindir}/%{name}-build-key %{_bindir}/%{name}-sign-key +%{_sbindir}/%{name}-list-objects %{_sbindir}/%{name}-enable-feature %{_sbindir}/%{name}-disable-feature %{_sbindir}/%{name}-prepare-dirs @@ -506,5 +521,8 @@ exit 0 %config(noreplace) %{apacheconfdir}/icinga.conf %config(noreplace) %attr(0640,root,%{apachegroup}) %{icingaclassicconfdir}/passwd +%files -n python-icinga2 +%{python2_sitelib}/icinga2* + %changelog diff --git a/lib/base/application.cpp b/lib/base/application.cpp index e2a00039d..49d2d39a0 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -478,6 +478,7 @@ void Application::DisplayInfoMessage(bool skipVersion) << " Local state directory: " << GetLocalStateDir() << std::endl << " Package data directory: " << GetPkgDataDir() << std::endl << " State path: " << GetStatePath() << std::endl + << " Objects path: " << GetObjectsPath() << std::endl << " PID path: " << GetPidPath() << std::endl << " Application type: " << GetApplicationType() << std::endl; } @@ -976,6 +977,26 @@ void Application::DeclareStatePath(const String& path) ScriptVariable::Set("StatePath", path, false); } +/** + * Retrieves the path for the objects file. + * + * @returns The path. + */ +String Application::GetObjectsPath(void) +{ + return ScriptVariable::Get("ObjectsPath"); +} + +/** + * Sets the path for the objects file. + * + * @param path The new path. + */ +void Application::DeclareObjectsPath(const String& path) +{ + ScriptVariable::Set("ObjectsPath", path, false); +} + /** * Retrieves the path for the PID file. * @@ -1023,8 +1044,9 @@ void Application::MakeVariablesConstant(void) ScriptVariable::GetByName("LocalStateDir")->SetConstant(true); ScriptVariable::GetByName("RunDir")->SetConstant(true); ScriptVariable::GetByName("PkgDataDir")->SetConstant(true); - ScriptVariable::GetByName("StatePath")->SetConstant(false); - ScriptVariable::GetByName("PidPath")->SetConstant(false); + ScriptVariable::GetByName("StatePath")->SetConstant(true); + ScriptVariable::GetByName("ObjectsPath")->SetConstant(true); + ScriptVariable::GetByName("PidPath")->SetConstant(true); ScriptVariable::GetByName("ApplicationType")->SetConstant(true); } diff --git a/lib/base/application.hpp b/lib/base/application.hpp index 2213fc669..b1d043b6b 100644 --- a/lib/base/application.hpp +++ b/lib/base/application.hpp @@ -104,6 +104,9 @@ public: static String GetStatePath(void); static void DeclareStatePath(const String& path); + static String GetObjectsPath(void); + static void DeclareObjectsPath(const String& path); + static String GetPidPath(void); static void DeclarePidPath(const String& path); diff --git a/lib/config/aexpression.cpp b/lib/config/aexpression.cpp index fad40a3f3..df4b70341 100644 --- a/lib/config/aexpression.cpp +++ b/lib/config/aexpression.cpp @@ -45,7 +45,7 @@ AExpression::AExpression(OpCallback op, const Value& operand1, const Value& oper : m_Operator(op), m_Operand1(operand1), m_Operand2(operand2), m_DebugInfo(di) { } -Value AExpression::Evaluate(const Dictionary::Ptr& locals) const +Value AExpression::Evaluate(const Dictionary::Ptr& locals, DebugHint *dhint) const { try { #ifdef _DEBUG @@ -56,7 +56,7 @@ Value AExpression::Evaluate(const Dictionary::Ptr& locals) const } #endif /* _DEBUG */ - return m_Operator(this, locals); + return m_Operator(this, locals, dhint); } catch (const std::exception& ex) { if (boost::get_error_info(ex)) throw; @@ -98,22 +98,22 @@ void AExpression::Dump(std::ostream& stream, int indent) const DumpOperand(stream, m_Operand2, indent + 1); } -Value AExpression::EvaluateOperand1(const Dictionary::Ptr& locals) const +Value AExpression::EvaluateOperand1(const Dictionary::Ptr& locals, DebugHint *dhint) const { - return static_cast(m_Operand1)->Evaluate(locals); + return static_cast(m_Operand1)->Evaluate(locals, dhint); } -Value AExpression::EvaluateOperand2(const Dictionary::Ptr& locals) const +Value AExpression::EvaluateOperand2(const Dictionary::Ptr& locals, DebugHint *dhint) const { - return static_cast(m_Operand2)->Evaluate(locals); + return static_cast(m_Operand2)->Evaluate(locals, dhint); } -Value AExpression::OpLiteral(const AExpression *expr, const Dictionary::Ptr&) +Value AExpression::OpLiteral(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->m_Operand1; } -Value AExpression::OpVariable(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpVariable(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Dictionary::Ptr scope = locals; @@ -127,87 +127,87 @@ Value AExpression::OpVariable(const AExpression *expr, const Dictionary::Ptr& lo return ScriptVariable::Get(expr->m_Operand1); } -Value AExpression::OpNegate(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpNegate(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return ~(long)expr->EvaluateOperand1(locals); } -Value AExpression::OpLogicalNegate(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpLogicalNegate(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return !expr->EvaluateOperand1(locals).ToBool(); } -Value AExpression::OpAdd(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpAdd(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) + expr->EvaluateOperand2(locals); } -Value AExpression::OpSubtract(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpSubtract(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) - expr->EvaluateOperand2(locals); } -Value AExpression::OpMultiply(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpMultiply(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) * expr->EvaluateOperand2(locals); } -Value AExpression::OpDivide(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpDivide(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) / expr->EvaluateOperand2(locals); } -Value AExpression::OpBinaryAnd(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpBinaryAnd(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) & expr->EvaluateOperand2(locals); } -Value AExpression::OpBinaryOr(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpBinaryOr(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) | expr->EvaluateOperand2(locals); } -Value AExpression::OpShiftLeft(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpShiftLeft(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) << expr->EvaluateOperand2(locals); } -Value AExpression::OpShiftRight(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpShiftRight(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) >> expr->EvaluateOperand2(locals); } -Value AExpression::OpEqual(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpEqual(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) == expr->EvaluateOperand2(locals); } -Value AExpression::OpNotEqual(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpNotEqual(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) != expr->EvaluateOperand2(locals); } -Value AExpression::OpLessThan(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpLessThan(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) < expr->EvaluateOperand2(locals); } -Value AExpression::OpGreaterThan(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpGreaterThan(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) > expr->EvaluateOperand2(locals); } -Value AExpression::OpLessThanOrEqual(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpLessThanOrEqual(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) <= expr->EvaluateOperand2(locals); } -Value AExpression::OpGreaterThanOrEqual(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpGreaterThanOrEqual(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals) >= expr->EvaluateOperand2(locals); } -Value AExpression::OpIn(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpIn(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value right = expr->EvaluateOperand2(locals); @@ -231,22 +231,22 @@ Value AExpression::OpIn(const AExpression *expr, const Dictionary::Ptr& locals) return found; } -Value AExpression::OpNotIn(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpNotIn(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { - return !OpIn(expr, locals); + return !OpIn(expr, locals, dhint); } -Value AExpression::OpLogicalAnd(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpLogicalAnd(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals).ToBool() && expr->EvaluateOperand2(locals).ToBool(); } -Value AExpression::OpLogicalOr(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpLogicalOr(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { return expr->EvaluateOperand1(locals).ToBool() || expr->EvaluateOperand2(locals).ToBool(); } -Value AExpression::OpFunctionCall(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpFunctionCall(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value funcName = expr->EvaluateOperand1(locals); @@ -270,7 +270,7 @@ Value AExpression::OpFunctionCall(const AExpression *expr, const Dictionary::Ptr return func->Invoke(arguments); } -Value AExpression::OpArray(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpArray(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr arr = expr->m_Operand1; Array::Ptr result = make_shared(); @@ -285,7 +285,7 @@ Value AExpression::OpArray(const AExpression *expr, const Dictionary::Ptr& local return result; } -Value AExpression::OpDict(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpDict(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr arr = expr->m_Operand1; bool in_place = expr->m_Operand2; @@ -297,7 +297,7 @@ Value AExpression::OpDict(const AExpression *expr, const Dictionary::Ptr& locals for (Array::SizeType index = 0; index < arr->GetLength(); index++) { const AExpression::Ptr& aexpr = arr->Get(index); Dictionary::Ptr alocals = in_place ? locals : result; - aexpr->Evaluate(alocals); + aexpr->Evaluate(alocals, dhint); if (alocals->Contains("__result")) break; @@ -309,15 +309,17 @@ Value AExpression::OpDict(const AExpression *expr, const Dictionary::Ptr& locals return xresult; } -Value AExpression::OpSet(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpSet(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); - Value right = expr->EvaluateOperand2(locals); + DebugHint *sdhint = dhint->GetChild(index); + Value right = expr->EvaluateOperand2(locals, sdhint); locals->Set(index, right); + sdhint->AddMessage("=", expr->m_DebugInfo); return right; } -Value AExpression::OpSetPlus(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpSetPlus(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); Value left = locals->Get(index); @@ -333,7 +335,8 @@ Value AExpression::OpSetPlus(const AExpression *expr, const Dictionary::Ptr& loc xlocals->Set("__parent", locals); } - Value result = left + expr->EvaluateOperand2(xlocals); + DebugHint *sdhint = dhint->GetChild(index); + Value result = left + expr->EvaluateOperand2(xlocals, sdhint); if (exp_right->m_Operator == &AExpression::OpDict) { Dictionary::Ptr dict = result; @@ -341,10 +344,12 @@ Value AExpression::OpSetPlus(const AExpression *expr, const Dictionary::Ptr& loc } locals->Set(index, result); + sdhint->AddMessage("+=", expr->m_DebugInfo); + return result; } -Value AExpression::OpSetMinus(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpSetMinus(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); Value left = locals->Get(index); @@ -360,7 +365,8 @@ Value AExpression::OpSetMinus(const AExpression *expr, const Dictionary::Ptr& lo xlocals->Set("__parent", locals); } - Value result = left - expr->EvaluateOperand2(xlocals); + DebugHint *sdhint = dhint->GetChild(index); + Value result = left - expr->EvaluateOperand2(xlocals, sdhint); if (exp_right->m_Operator == &AExpression::OpDict) { Dictionary::Ptr dict = result; @@ -368,10 +374,12 @@ Value AExpression::OpSetMinus(const AExpression *expr, const Dictionary::Ptr& lo } locals->Set(index, result); + sdhint->AddMessage("-=", expr->m_DebugInfo); + return result; } -Value AExpression::OpSetMultiply(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpSetMultiply(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); Value left = locals->Get(index); @@ -387,7 +395,8 @@ Value AExpression::OpSetMultiply(const AExpression *expr, const Dictionary::Ptr& xlocals->Set("__parent", locals); } - Value result = left * expr->EvaluateOperand2(xlocals); + DebugHint *sdhint = dhint->GetChild(index); + Value result = left * expr->EvaluateOperand2(xlocals, sdhint); if (exp_right->m_Operator == &AExpression::OpDict) { Dictionary::Ptr dict = result; @@ -395,10 +404,12 @@ Value AExpression::OpSetMultiply(const AExpression *expr, const Dictionary::Ptr& } locals->Set(index, result); + sdhint->AddMessage("*=", expr->m_DebugInfo); + return result; } -Value AExpression::OpSetDivide(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpSetDivide(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value index = expr->EvaluateOperand1(locals); Value left = locals->Get(index); @@ -414,7 +425,8 @@ Value AExpression::OpSetDivide(const AExpression *expr, const Dictionary::Ptr& l xlocals->Set("__parent", locals); } - Value result = left / expr->EvaluateOperand2(xlocals); + DebugHint *sdhint = dhint->GetChild(index); + Value result = left / expr->EvaluateOperand2(xlocals, sdhint); if (exp_right->m_Operator == &AExpression::OpDict) { Dictionary::Ptr dict = result; @@ -422,10 +434,12 @@ Value AExpression::OpSetDivide(const AExpression *expr, const Dictionary::Ptr& l } locals->Set(index, result); + sdhint->AddMessage("/=", expr->m_DebugInfo); + return result; } -Value AExpression::OpIndexer(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpIndexer(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value value = expr->EvaluateOperand1(locals); Value index = expr->EvaluateOperand2(locals); @@ -456,7 +470,7 @@ Value AExpression::OpIndexer(const AExpression *expr, const Dictionary::Ptr& loc } } -Value AExpression::OpImport(const AExpression *expr, const Dictionary::Ptr& locals) +Value AExpression::OpImport(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Value type = expr->EvaluateOperand1(locals); Value name = expr->EvaluateOperand2(locals); @@ -466,7 +480,7 @@ Value AExpression::OpImport(const AExpression *expr, const Dictionary::Ptr& loca if (!item) BOOST_THROW_EXCEPTION(ConfigError("Import references unknown template: '" + name + "'")); - item->GetExpressionList()->Evaluate(locals); + item->GetExpressionList()->Evaluate(locals, dhint); return Empty; } @@ -486,7 +500,7 @@ Value AExpression::FunctionWrapper(const std::vector& arguments, const Ar return locals->Get("__result"); } -Value AExpression::OpFunction(const AExpression* expr, const Dictionary::Ptr& locals) +Value AExpression::OpFunction(const AExpression* expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr left = expr->m_Operand1; AExpression::Ptr aexpr = left->Get(1); @@ -501,7 +515,7 @@ Value AExpression::OpFunction(const AExpression* expr, const Dictionary::Ptr& lo return func; } -Value AExpression::OpApply(const AExpression* expr, const Dictionary::Ptr& locals) +Value AExpression::OpApply(const AExpression* expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr left = expr->m_Operand1; AExpression::Ptr exprl = expr->m_Operand2; @@ -510,14 +524,14 @@ Value AExpression::OpApply(const AExpression* expr, const Dictionary::Ptr& local AExpression::Ptr aname = left->Get(2); AExpression::Ptr filter = left->Get(3); - String name = aname->Evaluate(locals); + String name = aname->Evaluate(locals, dhint); ApplyRule::AddRule(type, target, name, exprl, filter, expr->m_DebugInfo, locals); return Empty; } -Value AExpression::OpObject(const AExpression* expr, const Dictionary::Ptr& locals) +Value AExpression::OpObject(const AExpression* expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr left = expr->m_Operand1; AExpression::Ptr exprl = expr->m_Operand2; @@ -527,7 +541,7 @@ Value AExpression::OpObject(const AExpression* expr, const Dictionary::Ptr& loca AExpression::Ptr filter = left->Get(3); String zone = left->Get(4); - String name = aname->Evaluate(locals); + String name = aname->Evaluate(locals, dhint); ConfigItemBuilder::Ptr item = make_shared(expr->m_DebugInfo); @@ -571,14 +585,14 @@ Value AExpression::OpObject(const AExpression* expr, const Dictionary::Ptr& loca return Empty; } -Value AExpression::OpFor(const AExpression* expr, const Dictionary::Ptr& locals) +Value AExpression::OpFor(const AExpression* expr, const Dictionary::Ptr& locals, DebugHint *dhint) { Array::Ptr left = expr->m_Operand1; String varname = left->Get(0); AExpression::Ptr aexpr = left->Get(1); AExpression::Ptr ascope = expr->m_Operand2; - Array::Ptr arr = aexpr->Evaluate(locals); + Array::Ptr arr = aexpr->Evaluate(locals, dhint); ObjectLock olock(arr); BOOST_FOREACH(const Value& value, arr) { @@ -586,8 +600,40 @@ Value AExpression::OpFor(const AExpression* expr, const Dictionary::Ptr& locals) xlocals->Set("__parent", locals); xlocals->Set(varname, value); - ascope->Evaluate(xlocals); + ascope->Evaluate(xlocals, dhint); } return Empty; } + +Dictionary::Ptr DebugHint::ToDictionary(void) const +{ + Dictionary::Ptr result = make_shared(); + + Array::Ptr messages = make_shared(); + typedef std::pair MessageType; + BOOST_FOREACH(const MessageType& message, Messages) { + Array::Ptr amsg = make_shared(); + amsg->Add(message.first); + amsg->Add(message.second.Path); + amsg->Add(message.second.FirstLine); + amsg->Add(message.second.FirstColumn); + amsg->Add(message.second.LastLine); + amsg->Add(message.second.LastColumn); + messages->Add(amsg); + } + + result->Set("messages", messages); + + Dictionary::Ptr properties = make_shared(); + + typedef std::map::value_type ChildType; + BOOST_FOREACH(const ChildType& kv, Children) { + properties->Set(kv.first, kv.second.ToDictionary()); + } + + result->Set("properties", properties); + + return result; +} + diff --git a/lib/config/aexpression.hpp b/lib/config/aexpression.hpp index 1e659ac39..8a416c4a7 100644 --- a/lib/config/aexpression.hpp +++ b/lib/config/aexpression.hpp @@ -28,6 +28,24 @@ namespace icinga { +struct DebugHint +{ + std::vector > Messages; + std::map Children; + + inline void AddMessage(const String& message, const DebugInfo& di) + { + Messages.push_back(std::make_pair(message, di)); + } + + inline DebugHint *GetChild(const String& name) + { + return &Children[name]; + } + + Dictionary::Ptr ToDictionary(void) const; +}; + /** * @ingroup config */ @@ -36,53 +54,53 @@ class I2_CONFIG_API AExpression : public Object public: DECLARE_PTR_TYPEDEFS(AExpression); - typedef Value (*OpCallback)(const AExpression *, const Dictionary::Ptr&); + typedef Value (*OpCallback)(const AExpression *, const Dictionary::Ptr&, DebugHint *dhint); AExpression(OpCallback op, const Value& operand1, const DebugInfo& di); AExpression(OpCallback op, const Value& operand1, const Value& operand2, const DebugInfo& di); - Value Evaluate(const Dictionary::Ptr& locals) const; + Value Evaluate(const Dictionary::Ptr& locals, DebugHint *dhint = NULL) const; void MakeInline(void); void Dump(std::ostream& stream, int indent = 0) const; - static Value OpLiteral(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpVariable(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpNegate(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpLogicalNegate(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpAdd(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpSubtract(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpMultiply(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpDivide(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpBinaryAnd(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpBinaryOr(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpShiftLeft(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpShiftRight(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpEqual(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpNotEqual(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpLessThan(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpGreaterThan(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpLessThanOrEqual(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpGreaterThanOrEqual(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpIn(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpNotIn(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpLogicalAnd(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpLogicalOr(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpFunctionCall(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpArray(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpDict(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpSet(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpSetPlus(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpSetMinus(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpSetMultiply(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpSetDivide(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpIndexer(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpImport(const AExpression *expr, const Dictionary::Ptr& locals); - static Value OpFunction(const AExpression* expr, const Dictionary::Ptr& locals); - static Value OpApply(const AExpression* expr, const Dictionary::Ptr& locals); - static Value OpObject(const AExpression* expr, const Dictionary::Ptr& locals); - static Value OpFor(const AExpression* expr, const Dictionary::Ptr& locals); + static Value OpLiteral(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpVariable(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpNegate(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpLogicalNegate(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpAdd(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpSubtract(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpMultiply(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpDivide(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpBinaryAnd(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpBinaryOr(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpShiftLeft(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpShiftRight(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpEqual(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpNotEqual(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpLessThan(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpGreaterThan(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpLessThanOrEqual(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpGreaterThanOrEqual(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpIn(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpNotIn(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpLogicalAnd(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpLogicalOr(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpFunctionCall(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpArray(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpDict(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpSet(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpSetPlus(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpSetMinus(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpSetMultiply(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpSetDivide(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpIndexer(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpImport(const AExpression *expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpFunction(const AExpression* expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpApply(const AExpression* expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpObject(const AExpression* expr, const Dictionary::Ptr& locals, DebugHint *dhint); + static Value OpFor(const AExpression* expr, const Dictionary::Ptr& locals, DebugHint *dhint); private: OpCallback m_Operator; @@ -90,8 +108,8 @@ private: Value m_Operand2; DebugInfo m_DebugInfo; - Value EvaluateOperand1(const Dictionary::Ptr& locals) const; - Value EvaluateOperand2(const Dictionary::Ptr& locals) const; + Value EvaluateOperand1(const Dictionary::Ptr& locals, DebugHint *dhint = NULL) const; + Value EvaluateOperand2(const Dictionary::Ptr& locals, DebugHint *dhint = NULL) const; static void DumpOperand(std::ostream& stream, const Value& operand, int indent); diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index 80e9d99f9..61bc1cb0a 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -30,7 +30,10 @@ #include "base/debug.hpp" #include "base/workqueue.hpp" #include "base/exception.hpp" +#include "base/stdiostream.hpp" +#include "base/netstring.hpp" #include +#include #include using namespace icinga; @@ -118,13 +121,15 @@ Dictionary::Ptr ConfigItem::GetProperties(void) ASSERT(OwnsLock()); if (!m_Properties) { + DebugHint dhint; m_Properties = make_shared(); m_Properties->Set("type", m_Type); if (!m_Zone.IsEmpty()) m_Properties->Set("zone", m_Zone); m_Properties->Set("__parent", m_Scope); - GetExpressionList()->Evaluate(m_Properties); + GetExpressionList()->Evaluate(m_Properties, &dhint); m_Properties->Remove("__parent"); + m_DebugHints = dhint.ToDictionary(); String name = m_Name; @@ -150,6 +155,11 @@ Dictionary::Ptr ConfigItem::GetProperties(void) return m_Properties; } +Dictionary::Ptr ConfigItem::GetDebugHints(void) const +{ + return m_DebugHints; +} + /** * Commits the configuration item by creating a DynamicObject * object. @@ -264,7 +274,53 @@ void ConfigItem::ValidateItem(void) m_Validated = true; } -bool ConfigItem::ValidateItems(void) +void ConfigItem::WriteObjectsFile(const String& filename) +{ + Log(LogInformation, "ConfigItem", "Dumping config items to file '" + filename + "'"); + + String tempFilename = filename + ".tmp"; + + std::fstream fp; + fp.open(tempFilename.CStr(), std::ios_base::out); + + if (!fp) + BOOST_THROW_EXCEPTION(std::runtime_error("Could not open '" + tempFilename + "' file")); + + StdioStream::Ptr sfp = make_shared(&fp, false); + + BOOST_FOREACH(const ItemMap::value_type& kv, m_Items) { + ConfigItem::Ptr item = kv.second; + + Dictionary::Ptr persistentItem = make_shared(); + + persistentItem->Set("type", item->GetType()); + persistentItem->Set("name", item->GetName()); + persistentItem->Set("abstract", item->IsAbstract()); + persistentItem->Set("properties", item->GetProperties()); + persistentItem->Set("debug_hints", item->GetDebugHints()); + + String json = JsonSerialize(persistentItem); + + NetString::WriteStringToStream(sfp, json); + } + + sfp->Close(); + + fp.close(); + +#ifdef _WIN32 + _unlink(filename.CStr()); +#endif /* _WIN32 */ + + if (rename(tempFilename.CStr(), filename.CStr()) < 0) { + BOOST_THROW_EXCEPTION(posix_error() + << boost::errinfo_api_function("rename") + << boost::errinfo_errno(errno) + << boost::errinfo_file_name(tempFilename)); + } +} + +bool ConfigItem::ValidateItems(const String& objectsFile) { if (ConfigCompilerContext::GetInstance()->HasErrors()) return false; @@ -323,6 +379,9 @@ bool ConfigItem::ValidateItems(void) upq.Join(); + if (!objectsFile.IsEmpty()) + ConfigItem::WriteObjectsFile(objectsFile); + ConfigItem::DiscardItems(); ConfigType::DiscardTypes(); diff --git a/lib/config/configitem.hpp b/lib/config/configitem.hpp index 7c79cbb03..96dd7b0d7 100644 --- a/lib/config/configitem.hpp +++ b/lib/config/configitem.hpp @@ -49,6 +49,7 @@ public: AExpression::Ptr GetExpressionList(void) const; Dictionary::Ptr GetProperties(void); + Dictionary::Ptr GetDebugHints(void) const; DynamicObject::Ptr Commit(void); void Register(void); @@ -64,10 +65,12 @@ public: void ValidateItem(void); - static bool ValidateItems(void); + static bool ValidateItems(const String& objectsFile = String()); static bool ActivateItems(void); static void DiscardItems(void); + static void WriteObjectsFile(const String& filename); + private: String m_Type; /**< The object type. */ String m_Name; /**< The name. */ @@ -76,6 +79,7 @@ private: AExpression::Ptr m_ExpressionList; Dictionary::Ptr m_Properties; + Dictionary::Ptr m_DebugHints; std::vector m_ParentNames; /**< The names of parent configuration items. */ DebugInfo m_DebugInfo; /**< Debug information. */ diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 000000000..dda939d00 --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,43 @@ +# Icinga 2 +# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) +# +# 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 +# of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the Free Software Foundation +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +find_package(PythonInterp REQUIRED) + +add_subdirectory(icinga2) + +configure_file(setup.py.cmake ${CMAKE_CURRENT_BINARY_DIR}/setup.py @ONLY) + +install(CODE " + execute_process( + COMMAND ${CMAKE_COMMAND} + -DSETUP_PY=${CMAKE_CURRENT_BINARY_DIR}/setup.py + -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} + -DPREFIX=${CMAKE_INSTALL_PREFIX} + -DWDIR=${CMAKE_CURRENT_BINARY_DIR} + -P ${PROJECT_SOURCE_DIR}/third-party/cmake/PythonSetup.cmake + ) + + configure_file( + \$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/icinga2-list-objects + \$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SBINDIR}/icinga2-list-objects COPYONLY} + ) + + file( + REMOVE + \$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/icinga2-list-objects + )" +) diff --git a/python/icinga2/CMakeLists.txt b/python/icinga2/CMakeLists.txt new file mode 100644 index 000000000..846637f37 --- /dev/null +++ b/python/icinga2/CMakeLists.txt @@ -0,0 +1,23 @@ +# Icinga 2 +# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) +# +# 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 +# of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the Free Software Foundation +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +add_subdirectory(commands) +add_subdirectory(utils) + +configure_file(config.py.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.py @ONLY) +configure_file(__init__.py ${CMAKE_CURRENT_BINARY_DIR}/__init__.py COPYONLY) + diff --git a/python/icinga2/__init__.py b/python/icinga2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/icinga2/commands/CMakeLists.txt b/python/icinga2/commands/CMakeLists.txt new file mode 100644 index 000000000..b31292d44 --- /dev/null +++ b/python/icinga2/commands/CMakeLists.txt @@ -0,0 +1,20 @@ +# Icinga 2 +# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) +# +# 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 +# of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the Free Software Foundation +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +configure_file(__init__.py ${CMAKE_CURRENT_BINARY_DIR}/__init__.py COPYONLY) +configure_file(list_objects.py ${CMAKE_CURRENT_BINARY_DIR}/list_objects.py COPYONLY) + diff --git a/python/icinga2/commands/__init__.py b/python/icinga2/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/icinga2/commands/list_objects.py b/python/icinga2/commands/list_objects.py new file mode 100644 index 000000000..ebcaead6c --- /dev/null +++ b/python/icinga2/commands/list_objects.py @@ -0,0 +1,25 @@ +# Icinga 2 +# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) +# +# 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 +# of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the Free Software Foundation +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +from icinga2.utils.debug import ObjectsFile +from icinga2.config import LocalStateDir + +def main(): + fp = open(LocalStateDir + "/cache/icinga2/icinga2.debug") + of = ObjectsFile(fp) + for obj in of: + print obj diff --git a/python/icinga2/config.py.cmake b/python/icinga2/config.py.cmake new file mode 100644 index 000000000..9596450f7 --- /dev/null +++ b/python/icinga2/config.py.cmake @@ -0,0 +1,24 @@ +# Icinga 2 +# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) +# +# 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 +# of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the Free Software Foundation +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +Prefix = '@CMAKE_INSTALL_PREFIX@' +SysconfDir = '@CMAKE_INSTALL_FULL_SYSCONFDIR@' +RunDir = '@ICINGA2_RUNDIR@' +LocalStateDir = '@CMAKE_INSTALL_FULL_LOCALSTATEDIR@' +PkgDataDir = '@CMAKE_INSTALL_FULL_DATADIR@/icinga2' +IncludeConfDir = '@CMAKE_INSTALL_FULL_DATADIR@/icinga2/include' + diff --git a/python/icinga2/utils/CMakeLists.txt b/python/icinga2/utils/CMakeLists.txt new file mode 100644 index 000000000..7aaabe725 --- /dev/null +++ b/python/icinga2/utils/CMakeLists.txt @@ -0,0 +1,21 @@ +# Icinga 2 +# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) +# +# 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 +# of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the Free Software Foundation +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +configure_file(__init__.py ${CMAKE_CURRENT_BINARY_DIR}/__init__.py COPYONLY) +configure_file(debug.py ${CMAKE_CURRENT_BINARY_DIR}/debug.py COPYONLY) +configure_file(netstring.py ${CMAKE_CURRENT_BINARY_DIR}/netstring.py COPYONLY) + diff --git a/python/icinga2/utils/__init__.py b/python/icinga2/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/icinga2/utils/debug.py b/python/icinga2/utils/debug.py new file mode 100644 index 000000000..81b314ee6 --- /dev/null +++ b/python/icinga2/utils/debug.py @@ -0,0 +1,115 @@ +# Icinga 2 +# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) +# +# 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 +# of the License, or (at your option) any later version. +# +# This program 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 program; if not, write to the Free Software Foundation +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +from icinga2.utils import netstring +import subprocess + +try: + import json +except ImportError: + import simplejson as json + +class ConsoleColors(object): + @staticmethod + def _exec(args): + return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0] + + def __init__(self): + self.RESET = ConsoleColors._exec(['tput', 'sgr0']) + self.GREEN = ConsoleColors._exec(['tput', 'setaf', '2']) + self.CYAN = ConsoleColors._exec(['tput', 'setaf', '6']) + +_colors = ConsoleColors() + +class DebugObject(object): + def __init__(self, obj): + self._obj = obj + + def __str__(self): + return self.format_object() + + def format_object(self): + if self._obj["abstract"]: + result = "Template '" + else: + result = "Object '" + result += self._obj["properties"]["__name"] + "' of type '" + self._obj["type"] + "':\n" + result += self.format_properties(2) + return result + + @staticmethod + def format_value(value): + if isinstance(value, list): + result = "" + for avalue in value: + if result != "": + result += ", " + result += DebugObject.format_value(avalue) + return "[%s]" % (result) + elif isinstance(value, basestring): + return "'%s'" % (str(value)) + else: + return str(value) + + def format_properties(self, indent=0, path=[]): + props = self._obj["properties"] + for component in path: + props = props[component] + + result = "" + for key, value in props.items(): + path.append(key) + result += ' ' * indent + "* %s%s%s" % (_colors.GREEN, key, _colors.RESET) + hints = self.format_hints(self, indent + 2, path) + if isinstance(value, dict): + result += "\n" + hints + result += self.format_properties(indent + 2, path) + else: + result += " = %s\n" % (DebugObject.format_value(value)) + result += hints + path.pop() + return result + + def format_hints(self, dhints, indent=0, path=[]): + dhints = self._obj["debug_hints"] + try: + for component in path: + dhints = dhints["properties"][component] + except KeyError: + return "" + + result = "" + for message in dhints["messages"]: + result += ' ' * indent + "%% %smodified in %s, lines %s:%s-%s:%s%s\n" % (_colors.CYAN, + message[1], message[2], message[3], message[4], message[5], _colors.RESET) + return result + +class ObjectsFile(object): + def __init__(self, file): + self._file = file + + def __iter__(self): + fr = netstring.FileReader(self._file) + + while True: + try: + json_data = fr.readskip() + except EOFError: + break + if json_data == "": + break + yield DebugObject(json.loads(json_data)) diff --git a/python/icinga2/utils/netstring.py b/python/icinga2/utils/netstring.py new file mode 100644 index 000000000..704c66f26 --- /dev/null +++ b/python/icinga2/utils/netstring.py @@ -0,0 +1,305 @@ +#!/usr/bin/python +# netstring.py - Netstring encoding/decoding routines. +# Version 1.1 - July 2003 +# http://www.dlitz.net/software/python-netstring/ +# +# Copyright (c) 2003 Dwayne C. Litzenberger +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# HISTORY: +# +# Changes between 1.0 and 1.1: +# - Renamed Reader to BaseReader. Use FileReader and StringReader instead. +# - Added BaseReader.readskip() +# - Switched to saner stream reading semantics. Now the stream is not read +# until information is requested which requires it to be read. +# - Added split() +# + +from __future__ import generators +import StringIO + +"""Conversions to/from netstring format. + +The netstring format is defined in http://cr.yp.to/proto/netstrings.txt +(or http://www.dlitz.net/proto/netstrings-abnf.txt if you prefer ABNF) + +Classes: + + BaseReader (not to be used directly) + FileReader + StringReader + +Functions: + + dump + dumps + load + loads + split + +Misc variables: + + maxintlen - Maximum number of digits when reading integers + +""" + +__all__ = ['BaseReader', 'FileReader', 'StringReader', + 'dump', 'dumps', 'load', 'loads', 'split'] + +maxintlen = 999 # Maximum number of digits when reading integers + # This allows numbers up to 10**1000 - 1, which should + # be large enough for most applications. :-) + + +def dump(s, file): + """dump(s, file) -> None + +Writes the string s as a netstring to file. +""" + file.write(dumps(s)) + + +def dumps(s): + """dumps(s) -> string + +Encodes the string s as a netstring, and returns the result. +""" + return str(len(s)) + ":" + s + "," + + +def load(file, maxlen=None): + """load(file, maxlen=None) -> string + +Read a netstring from a file, and return the extracted netstring. + +If the parsed string would be longer than maxlen, OverflowError is raised. +""" + n = _readlen(file) + if maxlen is not None and n > maxlen: + raise OverflowError + retval = file.read(n) + #assert(len(retval) == n) + ch = file.read(1) + if ch == "": + raise EOFError + elif ch != ",": + raise ValueError + return retval + + +def loads(s, maxlen=None, returnUnparsed=False): + """loads(s, maxlen=None, returnUnparsed=False) -> string or (string, + string) + +Extract a netstring from a string. If returnUnparsed is false, return the +decoded netstring, otherwise return a tuple (parsed, unparsed) containing both +the parsed string and the remaining unparsed part of s. + +If the parsed string would be longer than maxlen, OverflowError is raised. +""" + f = StringIO.StringIO(s) + parsed = load(f, maxlen=maxlen) + if not returnUnparsed: + return parsed + unparsed = f.read() + return parsed, unparsed + + +def _readlen(file): + """_readlen(file) -> integer + +Read the initial "[length]:" of a netstring from file, and return the length. +""" + i = 0 + n = "" + ch = file.read(1) + while ch != ":": + if ch == "": + raise EOFError + elif not ch in "0123456789": + raise ValueError + n += ch + i += 1 + if i > maxintlen: + raise OverflowError + ch = file.read(1) + #assert(ch == ":") + return long(n) + + +def split(s): + """split(s) -> list of strings + +Return a list of the decoded netstrings in s. +""" + if s == "": + raise EOFError + retval = [] + unparsed = s + while unparsed != "": + parsed, unparsed = loads(unparsed, returnUnparsed=True) + retval.append(parsed) + return retval + + +class BaseReader: + """BaseReader(file, maxlen=None, blocksize=1024) -> BaseReader object + +Return a new BaseReader object. BaseReader allows reading a +netstring in blocks, instead of reading an netstring into memory at once. + +If BaseReader encounters a netstring which is larger than maxlen, it will return +OverflowError. BaseReader will also return ValueError if it encounters bad +formatting, or EOFError if an unexpected attempt is made to read beyond the +end of file. + +The state of BaseReader is undefined once any exception other than StopIteration +is raised. + +blocksize is the size of blocks to use when iterating over the BaseReader class. +You should not use BaseReader except when subclassing. Use FileReader or one +of the other *Reader classes instead. +""" + + def __init__(self, file, maxlen=None, blocksize=1024): + self._file = file + self._length = None + self._bytesleft = 0L + self._maxlen = maxlen + self._blocksize = blocksize + + def _readlen(self): + if self._length is None: + self._length = _readlen(self._file) + self._bytesleft = self._length + if self._maxlen is not None and self._length > self._maxlen: + raise OverflowError + # Handle the 0-byte case + if self._length == 0: + ch = self._file.read(1) + if ch == "": + raise EOFError + elif ch != ",": + raise ValueError + + def read(self, size=None): + """x.read([size]) -> string + +Works like .read. +""" + self._readlen() + if size is None or size > self._bytesleft: + size = self._bytesleft + if size == 0: + return "" + retval = self._file.read(size) + self._bytesleft -= len(retval) + if self._bytesleft == 0: + ch = self._file.read(1) + if ch == "": + raise EOFError + elif ch != ",": + raise ValueError + return retval + + def length(self): + """x.length() -> long + +Return the total length of the decoded string. +""" + self._readlen() + return self._length + + def bytesremaining(self): + """x.bytesremaining() -> long + +Return the number of decoded string bytes remaining to be read from the file. +""" + self._readlen() + return self._bytesleft + + def skip(self): + """x.skip() -> None + +Skip to the next netstring. +""" + self._readlen() + if self._bytesleft: + self._file.seek(self._bytesleft, 1) + ch = self._file.read(1) + if ch == "": + raise EOFError + elif ch != ",": + raise ValueError + self._bytesleft = 0L + self._length = None + + def readskip(self, size=None): + """x.readskip([size]) -> string + +Equivalent to x.read([size]); x.skip(). Returns whatever is returned by +x.read(). +""" + retval = self.read(size) + self.skip() + return retval + + def __iter__(self): + """x.__iter__() -> iterator + +Return a block of the decoded netstring. +""" + block = self.read(self._blocksize) + while block != "": + yield block + block = self.read(self._blocksize) + + def __len__(self): + """x.__len__() -> integer + +Return the total length of the decoded string. + +Note that this is limited to the maximum integer value. Use x.length() +wherever possible. +""" + return int(self.length()) + + +class FileReader(BaseReader): + """FileReader(file, ...) -> FileReader object + +Takes a file as input. See BaseReader.__doc__ for more information. +""" + pass + + +class StringReader(BaseReader): + """StringReader(s, ...) -> StringReader object + +Takes a string as input. See BaseReader.__doc__ for more information. +""" + def __init__(self, s, *args, **kwargs): + file = StringIO.StringIO(s) + return BaseReader.__init__(self, file, *args, **kwargs) + + +# vim:set tw=78 sw=4 ts=4 expandtab: diff --git a/python/setup.py.cmake b/python/setup.py.cmake new file mode 100644 index 000000000..ce8e1e04a --- /dev/null +++ b/python/setup.py.cmake @@ -0,0 +1,20 @@ +#!/usr/bin/env python +import os, re +from setuptools import setup, find_packages + +def get_icinga2_version(): + spec = open(os.path.join('@PROJECT_SOURCE_DIR@', 'icinga2.spec')).read() + m = re.search('^Version: (.*)$', spec, re.MULTILINE) + if not m: + return None + return m.group(1) + +setup( + name = 'icinga2', + version = get_icinga2_version(), + packages = find_packages(), + entry_points = { + 'console_scripts': [ 'icinga2-list-objects=icinga2.commands.list_objects:main' ] + } +) + diff --git a/third-party/cmake/PythonSetup.cmake b/third-party/cmake/PythonSetup.cmake new file mode 100644 index 000000000..9042eaec3 --- /dev/null +++ b/third-party/cmake/PythonSetup.cmake @@ -0,0 +1,215 @@ +# Copyright: 2004 Martin F. Krafft +# 2008 Martin Schreiber +# 2012-2013 Sebastian Ramacher +# License: Artistic-2.0 +# +# License: Artistic-2.0 +# Copyright (c) 2000-2006, The Perl Foundation. +# http://www.perlfoundation.org/artistic_license_2_0 +# . +# Everyone is permitted to copy and distribute verbatim copies of this +# license document, but changing it is not allowed. +# . +# Preamble +# . +# This license establishes the terms under which a given free software +# Package may be copied, modified, distributed, and/or redistributed. +# The intent is that the Copyright Holder maintains some artistic +# control over the development of that Package while still keeping the +# Package available as open source and free software. +# . +# You are always permitted to make arrangements wholly outside of this +# license directly with the Copyright Holder of a given Package. If the +# terms of this license do not permit the full use that you propose to +# make of the Package, you should contact the Copyright Holder and seek +# a different licensing arrangement. +# . +# Definitions +# . +# "Copyright Holder" means the individual(s) or organization(s) named in +# the copyright notice for the entire Package. +# . +# "Contributor" means any party that has contributed code or other +# material to the Package, in accordance with the Copyright Holder's +# procedures. +# . +# "You" and "your" means any person who would like to copy, distribute, +# or modify the Package. +# . +# "Package" means the collection of files distributed by the Copyright +# Holder, and derivatives of that collection and/or of those files. A +# given Package may consist of either the Standard Version, or a +# Modified Version. +# . +# "Distribute" means providing a copy of the Package or making it +# accessible to anyone else, or in the case of a company or +# organization, to others outside of your company or organization. +# . +# "Distributor Fee" means any fee that you charge for Distributing this +# Package or providing support for this Package to another party. It +# does not mean licensing fees. +# . +# "Standard Version" refers to the Package if it has not been modified, +# or has been modified only in ways explicitly requested by the +# Copyright Holder. +# . +# "Modified Version" means the Package, if it has been changed, and such +# changes were not explicitly requested by the Copyright Holder. +# . +# "Original License" means this Artistic License as Distributed with the +# Standard Version of the Package, in its current version or as it may +# be modified by The Perl Foundation in the future. +# . +# "Source" form means the source code, documentation source, and +# configuration files for the Package. +# . +# "Compiled" form means the compiled bytecode, object code, binary, or +# any other form resulting from mechanical transformation or translation +# of the Source form. +# . +# Permission for Use and Modification Without Distribution +# . +# (1) You are permitted to use the Standard Version and create and use +# Modified Versions for any purpose without restriction, provided that +# you do not Distribute the Modified Version. +# . +# Permissions for Redistribution of the Standard Version +# . +# (2) You may Distribute verbatim copies of the Source form of the +# Standard Version of this Package in any medium without restriction, +# either gratis or for a Distributor Fee, provided that you duplicate +# all of the original copyright notices and associated disclaimers. At +# your discretion, such verbatim copies may or may not include a +# Compiled form of the Package. +# . +# (3) You may apply any bug fixes, portability changes, and other +# modifications made available from the Copyright Holder. The resulting +# Package will still be considered the Standard Version, and as such +# will be subject to the Original License. +# . +# Distribution of Modified Versions of the Package as Source +# . +# (4) You may Distribute your Modified Version as Source (either gratis +# or for a Distributor Fee, and with or without a Compiled form of the +# Modified Version) provided that you clearly document how it differs +# from the Standard Version, including, but not limited to, documenting +# any non-standard features, executables, or modules, and provided that +# you do at least ONE of the following: +# . +# (a) make the Modified Version available to the Copyright Holder of the +# Standard Version, under the Original License, so that the Copyright +# Holder may include your modifications in the Standard Version. (b) +# ensure that installation of your Modified Version does not prevent the +# user installing or running the Standard Version. In addition, the +# Modified Version must bear a name that is different from the name of +# the Standard Version. (c) allow anyone who receives a copy of the +# Modified Version to make the Source form of the Modified Version +# available to others under (i) the Original License or (ii) a license +# that permits the licensee to freely copy, modify and redistribute the +# Modified Version using the same licensing terms that apply to the copy +# that the licensee received, and requires that the Source form of the +# Modified Version, and of any works derived from it, be made freely +# available in that license fees are prohibited but Distributor Fees are +# allowed. +# . +# Distribution of Compiled Forms of the Standard Version or Modified +# Versions without the Source +# . +# (5) You may Distribute Compiled forms of the Standard Version without +# the Source, provided that you include complete instructions on how to +# get the Source of the Standard Version. Such instructions must be +# valid at the time of your distribution. If these instructions, at any +# time while you are carrying out such distribution, become invalid, you +# must provide new instructions on demand or cease further distribution. +# If you provide valid instructions or cease distribution within thirty +# days after you become aware that the instructions are invalid, then +# you do not forfeit any of your rights under this license. +# . +# (6) You may Distribute a Modified Version in Compiled form without the +# Source, provided that you comply with Section 4 with respect to the +# Source of the Modified Version. +# . +# Aggregating or Linking the Package +# . +# (7) You may aggregate the Package (either the Standard Version or +# Modified Version) with other packages and Distribute the resulting +# aggregation provided that you do not charge a licensing fee for the +# Package. Distributor Fees are permitted, and licensing fees for other +# components in the aggregation are permitted. The terms of this license +# apply to the use and Distribution of the Standard or Modified Versions +# as included in the aggregation. +# . +# (8) You are permitted to link Modified and Standard Versions with +# other works, to embed the Package in a larger work of your own, or to +# build stand-alone binary or bytecode versions of applications that +# include the Package, and Distribute the result without restriction, +# provided the result does not expose a direct interface to the Package. +# . +# Items That are Not Considered Part of a Modified Version +# . +# (9) Works (including, but not limited to, modules and scripts) that +# merely extend or make use of the Package, do not, by themselves, cause +# the Package to be a Modified Version. In addition, such works are not +# considered parts of the Package itself, and are not subject to the +# terms of this license. +# . +# General Provisions +# . +# (10) Any use, modification, and distribution of the Standard or +# Modified Versions is governed by this Artistic License. By using, +# modifying or distributing the Package, you accept this license. Do not +# use, modify, or distribute the Package, if you do not accept this +# license. +# . +# (11) If your Modified Version has been derived from a Modified Version +# made by someone other than you, you are nevertheless required to +# ensure that your Modified Version complies with the requirements of +# this license. +# . +# (12) This license does not grant you the right to use any trademark, +# service mark, tradename, or logo of the Copyright Holder. +# . +# (13) This license includes the non-exclusive, worldwide, +# free-of-charge patent license to make, have made, use, offer to sell, +# sell, import and otherwise transfer the Package with respect to any +# patent claims licensable by the Copyright Holder that are necessarily +# infringed by the Package. If you institute patent litigation +# (including a cross-claim or counterclaim) against any party alleging +# that the Package constitutes direct or contributory patent +# infringement, then this Artistic License to you shall terminate on the +# date that such litigation is filed. +# . +# (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT +# HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT +# PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT +# HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE +# OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This is a ugly workaround to be able to pass DESTDIR as --root to setup.py. +# cmake expands any occurence of $ENV{foo} with the value of foo while runing +# cmake and not while running make afterwards. + +SET(PYTHON_EXECUTABLE "" CACHE FORCE "Python executable") +SET(PREFIX ${CMAKE_INSTALL_PREFIX} CACHE FORCE "cmake install prefix") +SET(WDIR "" CACHE FORCE "working directory") +SET(SETUP_PY "" CACHE FORCE "setup.py path") +SET(EXTRA_ARGS $ENV{SETUP_PY_EXTRA_ARGS} CACHE FORCE "extra arguments for setup.py") + +SET(INSTALL_ROOT $ENV{DESTDIR}) +IF(INSTALL_ROOT) + SET(INSTALL_ROOT_ARGS "--root=$ENV{DESTDIR}") +ELSE(INSTALL_ROOT) + SET(INSTALL_ROOT_ARGS "") +ENDIF(INSTALL_ROOT) + +EXECUTE_PROCESS( + COMMAND ${PYTHON_EXECUTABLE} + ${SETUP_PY} + install + --prefix=${PREFIX} + ${INSTALL_ROOT_ARGS} + ${EXTRA_ARGS} + WORKING_DIRECTORY ${WDIR})