This patch creates a condor classad plugin that allows us to check
quotas as a final step in the condor matching process.

In order to enable quotas, you will have to add the following to your
/var/lib/condor/condor_local.config:

CLASSAD_USER_LIBS = 
/usr/share/deltacloud-aggregator/classad_plugin/deltacloud_classad_plugin.so

You must also set up the plugin correctly depending on how you plan to
run the aggregator.  The easiest thing to do is just use 'make rpms' and
install the resulting rpms and run the whole thing installed.

If you wish to run it from the src dir as most developers do, you will
have to run configure with:

./configure --enable-local --prefix=/usr

and then do a 'make install'.  This will install the
deltacloud_classad_plugin.so in
/usr/share/deltacloud-aggregator/classad_plugin directory, but it will
continue to use the database and model files from the local directory.

Signed-off-by: Ian Main <[email protected]>
---
 Makefile.am                                      |    4 +
 autogen.sh                                       |    4 +-
 configure.ac                                     |   14 ++-
 deltacloud-aggregator.spec.in                    |   17 +++-
 src/app/util/condormatic.rb                      |    4 +
 src/classad_plugin/Makefile.am                   |   12 ++
 src/classad_plugin/classad_plugin.rb             |   19 +++
 src/classad_plugin/deltacloud_classad_plugin.cpp |  158 ++++++++++++++++++++++
 8 files changed, 228 insertions(+), 4 deletions(-)
 create mode 100644 src/classad_plugin/Makefile.am
 create mode 100644 src/classad_plugin/classad_plugin.rb
 create mode 100644 src/classad_plugin/deltacloud_classad_plugin.cpp

diff --git a/Makefile.am b/Makefile.am
index 7984035..b9de310 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,6 +38,10 @@ RPMDIR               = $$(rpm --eval '%{_rpmdir}')
 RPM_FLAGS      = --define "deltacloud_cache_dir $(DELTACLOUD_CACHE_DIR)"
 RPM_FLAGS      += $(if $(_deltacloud_dev),--define "extra_release 
.$(GIT_RELEASE)")
 
+SUBDIRS = src/classad_plugin
+
+ACLOCAL_AMFLAGS = -I m4
+
 rpms: dist
        rpmbuild $(RPM_FLAGS) -ta $(distdir).tar.gz
 
diff --git a/autogen.sh b/autogen.sh
index 72f9114..bce073a 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -50,13 +50,15 @@ THEDIR=`pwd`
          echo "to pass any to it, please specify them on the $0 command line."
   fi
 
+  libtoolize --force
   aclocal
 
   # Run autoheader only if needed
   grep '^[[:blank:]]*AC_CONFIG_HEADERS' configure.ac >/dev/null && autoheader
 
-  automake --add-missing
+  autoheader
   autoconf
+  automake --add-missing
   ./configure "$@"
 )
 
diff --git a/configure.ac b/configure.ac
index e11158b..1f1c212 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,21 @@
 AC_INIT([deltacloud-aggregator], [0.0.2], 
[[email protected]])
 AM_INIT_AUTOMAKE([-Wall -Werror foreign -Wno-portability tar-pax])
-AC_PROG_CC
 AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4])
+
+AC_PROG_CXX
+AC_PROG_CC
+AC_PROG_LIBTOOL
+AC_LANG([C++])
+
+AC_ARG_ENABLE(local,
+  [  --enable-local   Set this flag if you are planning to run the aggregator 
without installing it.],
+  [  DELTACLOUD_INSTALL_DIR=`pwd`/src ], [  DELTACLOUD_INSTALL_DIR=$pkgdatadir 
])
+AC_SUBST(DELTACLOUD_INSTALL_DIR)
 
 # If using gcc and default CFLAGS, enable some warnings.
 test x"$ac_ct_CC:$CFLAGS" = 'xgcc:-g -O2' \
   && CFLAGS="$CFLAGS -Wshadow -Wall -Werror"
 
 AC_CONFIG_FILES([Makefile deltacloud-aggregator.spec])
-AC_OUTPUT
+AC_OUTPUT(src/classad_plugin/Makefile)
diff --git a/deltacloud-aggregator.spec.in b/deltacloud-aggregator.spec.in
index ca0db90..de45b34 100644
--- a/deltacloud-aggregator.spec.in
+++ b/deltacloud-aggregator.spec.in
@@ -15,7 +15,6 @@ URL:      http://deltacloud.org
 # tar czvf deltacloud-aggregator-0.0.2.tar.gz deltacloud-aggregator-0.0.2
 Source0:    deltacloud-aggregator-%{version}.tar.gz
 BuildRoot:  %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
-BuildArch:  noarch
 
 Requires: ruby >= 1.8.1
 Requires: ruby(abi) = 1.8
@@ -29,6 +28,9 @@ Requires: postgresql-server
 Requires: ruby-postgres
 Requires: gnuplot >= 4.2.6
 Requires: ruby-RMagick
+Requires: classads >= 1.0
+
+BuildRequires: classads-devel >= 1.0
 
 %package daemons
 Summary:   Deltacloud Aggregator daemons config
@@ -60,9 +62,12 @@ Documentation and tests for the Deltacloud Aggregator
 %setup -q
 
 %build
+%configure
+make
 
 %install
 %{__rm} -rf %{buildroot}
+
 %{__mkdir} -p %{buildroot}
 %{__mkdir} -p %{buildroot}%{app_root}
 %{__mkdir} -p %{buildroot}%{doc_root}
@@ -79,6 +84,11 @@ Documentation and tests for the Deltacloud Aggregator
 # copy over all of the src directory...
 %{__cp} -R src/* %{buildroot}/%{app_root}
 
+# Remove this directory as the make install below will install the correct 
files to this dir.
+%{__rm} -rf %{buildroot}%{app_root}/classad_plugin
+
+%makeinstall
+
 # move documentation to the correct place
 mv %{buildroot}/%{app_root}/doc %{buildroot}/%{app_root}/test 
%{buildroot}/%{doc_root}
 
@@ -115,6 +125,11 @@ touch %{buildroot}%{_localstatedir}/log/%{name}/rails.log
 %{__mkdir} %{buildroot}%{_localstatedir}/lib/%{name}/tmp
 %{__ln_s} %{_localstatedir}/lib/%{name}/tmp %{buildroot}%{app_root}/tmp
 
+%{__rm} -rf %{buildroot}%{app_root}/tmp
+
+# Delete unused build stuff.
+%{__rm} -rf %{buildroot}%{app_root}/classad_plugin/.libs
+%{__rm} -rf %{buildroot}%{app_root}/classad_plugin/.deps
 
 %clean
 %{__rm} -rf %{buildroot}
diff --git a/src/app/util/condormatic.rb b/src/app/util/condormatic.rb
index d93a4b1..f6c2ba8 100644
--- a/src/app/util/condormatic.rb
+++ b/src/app/util/condormatic.rb
@@ -51,6 +51,9 @@ def condormatic_instance_create(task)
 
     requirements = "requirements = hardwareprofile == 
\"#{instance.hardware_profile.id}\" && image == \"#{instance.image.id}\""
     requirements += " && realm == \"#{realm.name}\"" if realm != nil
+    # We may need to add some stuff to the provider classads like pool id, 
provider id etc.  This is mostly just
+    # to test and make sure this works for now.
+    requirements += " && deltacloud_quota_check(\"#{job_name}\", 
other.cloud_account_id)"
     requirements += "\n"
 
     pipe.puts requirements
@@ -198,6 +201,7 @@ def condormatic_classads_sync
             pipe.puts "provider_url=\"#{account.provider.url}\""
             pipe.puts "username=\"#{account.username}\""
             pipe.puts "password=\"#{account.password}\""
+            pipe.puts "cloud_account_id=\"#{account.id}\""
             pipe.close_write
 
             out = pipe.read
diff --git a/src/classad_plugin/Makefile.am b/src/classad_plugin/Makefile.am
new file mode 100644
index 0000000..9334f5f
--- /dev/null
+++ b/src/classad_plugin/Makefile.am
@@ -0,0 +1,12 @@
+AM_CPPFLAGS=-I`ruby -e "require 'rbconfig' ; puts Config::CONFIG['archdir']"` \
+            -DDELTACLOUD_INSTALL_DIR=\"$(DELTACLOUD_INSTALL_DIR)\" 
-DLOGFILE=\"/var/log/condor/deltacloud_condor_plugin.log\"
+
+classadplugindir=$(pkgdatadir)/classad_plugin
+
+classadplugin_LTLIBRARIES = deltacloud_classad_plugin.la
+
+deltacloud_classad_plugin_la_SOURCES = deltacloud_classad_plugin.cpp
+deltacloud_classad_plugin_la_LIBADD = -lclassad -lruby
+deltacloud_classad_plugin_la_LDFLAGS = -module -avoid-version -shared
+
+classadplugin_DATA = classad_plugin.rb
diff --git a/src/classad_plugin/classad_plugin.rb 
b/src/classad_plugin/classad_plugin.rb
new file mode 100644
index 0000000..fce7d2c
--- /dev/null
+++ b/src/classad_plugin/classad_plugin.rb
@@ -0,0 +1,19 @@
+
+require 'dutils'
+
+def classad_plugin(instance_key, account_id)
+
+    # I left the puts in here because you can run condor_negotiator -f from 
the command
+    # line and it will print this stuff out.  Very nice for debugging.
+    #puts "getting instance from key #{instance_key}"
+    instance = Instance.find(:first, :conditions => [ "condor_job_id = ?", 
instance_key ])
+    #puts "getting cloud account from id #{account_id}"
+    cloud_account = CloudAccount.find(:first, :conditions => [ "id = ?", 
account_id ])
+
+    #puts "instance is: #{instance}, cloud account is #{cloud_account}"
+
+    return false if instance.nil?
+    return false if cloud_account.nil?
+    #puts "checking quota.."
+    return Quota.can_start_instance?(instance, cloud_account)
+end
diff --git a/src/classad_plugin/deltacloud_classad_plugin.cpp 
b/src/classad_plugin/deltacloud_classad_plugin.cpp
new file mode 100644
index 0000000..af35bc0
--- /dev/null
+++ b/src/classad_plugin/deltacloud_classad_plugin.cpp
@@ -0,0 +1,158 @@
+#include <ruby.h>
+#include <stdio.h>
+
+#include <iostream>
+#include <sstream>
+
+#include "classad/classad_distribution.h"
+#include "classad/fnCall.h"
+
+using namespace std;
+#ifdef WANT_CLASSAD_NAMESPACE
+using namespace classad;
+#endif
+
+void print_value(FILE *fp, Value val, const char *name)
+{
+    /* AFAICT the only way to get at the string in a 'Value' object is to use
+     * the C++ stream operator <<,  so we set up a stringstream and write
+     * stuff we want into that..
+     */
+    std::stringstream sstr;
+
+    sstr << name << " is " << val;
+    fprintf(fp, "%s\n", sstr.str().c_str());
+}
+
+/*
+ * Perform our quota check against the deltacloud aggregator database.
+ * This function expects:
+ *
+ * - Instance executable name as handed to condor so that we can map to the
+ *   instance found in the database.
+ * - The username
+ * - The provider url so we can map back to the provider.
+ * - The realm key so we know what realm this is in.
+ *
+ * ... at least for now.  Need to better analyze what is required to do all
+ * the quota matching but that's the idea.
+ */
+bool
+deltacloud_quota_check(const char *name, const ArgumentList &arglist,
+                      EvalState &state, Value &result)
+{
+    Value instance_key;
+    Value account_id;
+    FILE *fp;
+    char msg[1024];
+    VALUE res;
+    bool val = false;
+    char *ruby_string;
+    std::stringstream method_args;
+    static int ruby_initialized = 0;
+
+    result.SetBooleanValue(false);
+
+    fp = fopen(LOGFILE, "a");
+
+    if (arglist.size() != 2) {
+      result.SetErrorValue();
+      fprintf(fp, "Expected 2 arguments, saw %d\n", arglist.size());
+      goto do_ret;
+    }
+
+    if (!arglist[0]->Evaluate(state, instance_key)) {
+      result.SetErrorValue();
+      fprintf(fp, "Could not evaluate argument 0 to instance key\n");
+      goto do_ret;
+    }
+    if (!arglist[1]->Evaluate(state, account_id)) {
+      result.SetErrorValue();
+      fprintf(fp, "Could not evaluate argument 1 to account_id\n");
+      goto do_ret;
+    }
+
+    if (instance_key.GetType() != Value::STRING_VALUE) {
+      result.SetErrorValue();
+      fprintf(fp, "Instance type was not a string\n");
+      goto do_ret;
+    }
+    //print_value(fp, instance_key, "instance_key");
+
+    if (account_id.GetType() != Value::STRING_VALUE) {
+      result.SetErrorValue();
+      fprintf(fp, "Account ID type was not a string\n");
+      goto do_ret;
+    }
+    //print_value(fp, account_id, "account_id");
+
+    if (!ruby_initialized) {
+        ruby_init();
+        ruby_init_loadpath();
+    }
+
+    method_args << "'" << instance_key << "', " << account_id;
+
+    asprintf(&ruby_string,
+             "$: << '%s/classad_plugin'\n"
+             "$: << '%s/dutils'\n"
+             "$: << '%s/models'\n"
+             "begin\n"
+             "   require 'classad_plugin.rb'\n"
+             "   classad_plugin(%s)\n"
+             "rescue Exception => ex\n"
+             "   f = File.new('%s', 'a')\n"
+             "   f.write \"Error running classad plugin: #{ex.message}\"\n"
+             "   f.write ex.backtrace\n"
+             "   f.close\n"
+             "   false\n"
+             "end\n",
+             DELTACLOUD_INSTALL_DIR,
+             DELTACLOUD_INSTALL_DIR,
+             DELTACLOUD_INSTALL_DIR,
+             method_args.str().c_str(),
+             LOGFILE);
+
+    fflush(fp);
+
+    res = rb_eval_string(ruby_string);
+    free(ruby_string);
+
+    if (res == Qtrue) {
+        result.SetBooleanValue(true);
+        val = true;
+    } else {
+        fprintf(fp, "Returned result from ruby code was %s\n", (res == Qtrue) 
? "true" : "false");
+    }
+
+ do_ret:
+    fclose(fp);
+
+    return val;
+}
+
+/*
+ * Struct containing the names of the functions provided here and a pointer
+ * to the function that implements them.  Third arg is an int flags that 
appears
+ * to be unused.
+ *
+ * This is found in fnCall.h
+ */
+static ClassAdFunctionMapping classad_functions[] =
+{
+    { "deltacloud_quota_check", (void *) deltacloud_quota_check, 0 },
+    { "", NULL, 0 }
+};
+
+/*
+ * This is the 'Init' function that is called after the library is dlopen()'d.
+ * This just returns the struct defined above.
+ *
+ */
+extern "C"
+{
+    ClassAdFunctionMapping *
+    Init(void) {
+        return classad_functions;
+    }
+}
-- 
1.7.1.1

_______________________________________________
deltacloud-devel mailing list
[email protected]
https://fedorahosted.org/mailman/listinfo/deltacloud-devel

Reply via email to