Add scripts to create myops-getqueryview:
Stephen Soltesz [Thu, 30 Jun 2011 05:29:55 +0000 (05:29 +0000)]
195 files changed:
Makefile
myops.spec
web/collect/automate/install_on_node.sh [new file with mode: 0644]
web/collect/automate/run.sh [new file with mode: 0755]
web/collect/client/bootstrap.sh [new file with mode: 0644]
web/collect/client/collect.sh [new file with mode: 0644]
web/collect/client/get_bootcd_version.sh [new file with mode: 0755]
web/collect/client/sysinfo/Exceptions.py [new file with mode: 0644]
web/collect/client/sysinfo/Exceptions.pyc [new file with mode: 0644]
web/collect/client/sysinfo/ModelOptions.py [new file with mode: 0644]
web/collect/client/sysinfo/ModelOptions.pyc [new file with mode: 0644]
web/collect/client/sysinfo/pypci.py [new file with mode: 0644]
web/collect/client/sysinfo/pypci.pyc [new file with mode: 0644]
web/collect/client/sysinfo/pypcimap.py [new file with mode: 0644]
web/collect/client/sysinfo/pypcimap.pyc [new file with mode: 0644]
web/collect/client/sysinfo/systeminfo.py [new file with mode: 0755]
web/collect/client/timeout3.sh [new file with mode: 0755]
web/collect/client/upload.sh [new file with mode: 0644]
web/collect/cron.d/sysstat.cron [new file with mode: 0644]
web/collect/sar2graphite.py [new file with mode: 0755]
web/collect/server/build.sh [new file with mode: 0755]
web/collect/server/cron.hourly/load_all_couchdb.sh [new file with mode: 0755]
web/collect/server/input.cfg [new file with mode: 0644]
web/collect/server/install_on_server.sh [new file with mode: 0755]
web/collect/server/load-graphite.py [new file with mode: 0755]
web/collect/server/load_couch.py [new file with mode: 0755]
web/collect/server/php.conf [new file with mode: 0644]
web/collect/server/stats.py [new file with mode: 0755]
web/collect/server/summarize_logs.sh [new file with mode: 0755]
web/collect/server/summarize_rpms.sh [new file with mode: 0755]
web/collect/server/summarize_stats.sh [new file with mode: 0755]
web/collect/server/txt2dict.py [new file with mode: 0644]
web/collect/server/upload.php [new file with mode: 0755]
web/db-config.d/030-conf_files_myops [new file with mode: 0644]
web/query/README.md [new file with mode: 0644]
web/query/THANKS.txt [new file with mode: 0644]
web/query/_attachments/LICENSE.txt [new file with mode: 0644]
web/query/_attachments/THANKS.txt [new file with mode: 0644]
web/query/_attachments/default.css [new file with mode: 0644]
web/query/_attachments/files/input.cfg [new file with mode: 0644]
web/query/_attachments/images/bg.jpg [new file with mode: 0644]
web/query/_attachments/images/bottom.png [new file with mode: 0644]
web/query/_attachments/images/content.png [new file with mode: 0644]
web/query/_attachments/images/icon.png [new file with mode: 0644]
web/query/_attachments/images/pl-logo.png [new file with mode: 0644]
web/query/_attachments/images/top.png [new file with mode: 0644]
web/query/_attachments/img/bg.gif [new file with mode: 0644]
web/query/_attachments/img/bgcode.gif [new file with mode: 0644]
web/query/_attachments/img/bgfooter.gif [new file with mode: 0644]
web/query/_attachments/img/bgmain.gif [new file with mode: 0644]
web/query/_attachments/img/li.gif [new file with mode: 0644]
web/query/_attachments/img/nav_li.gif [new file with mode: 0644]
web/query/_attachments/img/quote.gif [new file with mode: 0644]
web/query/_attachments/img/topleft.gif [new file with mode: 0644]
web/query/_attachments/index.html [new file with mode: 0644]
web/query/_attachments/index.html.indigo [new file with mode: 0644]
web/query/_attachments/index.html.mono [new file with mode: 0644]
web/query/_attachments/index2.html [new file with mode: 0644]
web/query/_attachments/indigo.html [new file with mode: 0644]
web/query/_attachments/script/app.js [new file with mode: 0644]
web/query/_attachments/script/jquery.scrollTo.js [new file with mode: 0644]
web/query/_attachments/script/md5.js [new file with mode: 0644]
web/query/_attachments/style/.screen.css.swp [new file with mode: 0644]
web/query/_attachments/style/basic.css [new file with mode: 0644]
web/query/_attachments/style/default.css [new file with mode: 0644]
web/query/_attachments/style/images/bg.jpg [new file with mode: 0644]
web/query/_attachments/style/images/bottom.png [new file with mode: 0644]
web/query/_attachments/style/images/content.png [new file with mode: 0644]
web/query/_attachments/style/images/icon.png [new file with mode: 0644]
web/query/_attachments/style/images/top.png [new file with mode: 0644]
web/query/_attachments/style/img/bg.gif [new file with mode: 0644]
web/query/_attachments/style/img/bgcode.gif [new file with mode: 0644]
web/query/_attachments/style/img/bgfooter.gif [new file with mode: 0644]
web/query/_attachments/style/img/bgmain.gif [new file with mode: 0644]
web/query/_attachments/style/img/li.gif [new file with mode: 0644]
web/query/_attachments/style/img/nav_li.gif [new file with mode: 0644]
web/query/_attachments/style/img/quote.gif [new file with mode: 0644]
web/query/_attachments/style/img/topleft.gif [new file with mode: 0644]
web/query/_attachments/style/pygments.css [new file with mode: 0644]
web/query/_attachments/style/screen.css [new file with mode: 0644]
web/query/_attachments/style/sphinxdoc.css [new file with mode: 0644]
web/query/_attachments/style/style.css [new file with mode: 0755]
web/query/_id [new file with mode: 0644]
web/query/blog.json [new file with mode: 0644]
web/query/couchapp.json [new file with mode: 0644]
web/query/evently/account/loggedIn/data.js [new file with mode: 0644]
web/query/evently/account/loggedIn/mustache.html [new file with mode: 0644]
web/query/evently/profile/loggedOut/mustache.html [new file with mode: 0644]
web/query/evently/profile/profileReady/mustache.html [new file with mode: 0644]
web/query/evently/profile/profileReady/selectors/#preview/click.js [new file with mode: 0644]
web/query/evently/profile/profileReady/selectors/form/submit.js [new file with mode: 0644]
web/query/evently/tagcloud/_init/data.js [new file with mode: 0644]
web/query/evently/tagcloud/_init/mustache.html [new file with mode: 0644]
web/query/evently/tagcloud/_init/query.json [new file with mode: 0644]
web/query/helpers/md5.js [new file with mode: 0644]
web/query/lib/blog.js [new file with mode: 0644]
web/query/lib/mustache.js [new file with mode: 0644]
web/query/lib/validate.js [new file with mode: 0644]
web/query/lists/.hosts.js.swp [new file with mode: 0644]
web/query/lists/.rpms.js.swo [new file with mode: 0644]
web/query/lists/.rpms.js.swp [new file with mode: 0644]
web/query/lists/1 [new file with mode: 0644]
web/query/lists/comments.js [new file with mode: 0644]
web/query/lists/index.js [new file with mode: 0644]
web/query/lists/newlists.js [new file with mode: 0644]
web/query/lists/nodelist.js [new file with mode: 0644]
web/query/lists/nodelist.js.backup [new file with mode: 0644]
web/query/lists/post.js [new file with mode: 0644]
web/query/lists/rpms.js [new file with mode: 0644]
web/query/out.hardware [new file with mode: 0644]
web/query/out2 [new file with mode: 0644]
web/query/rewrites.json [new file with mode: 0644]
web/query/rewrites.json.backup [new file with mode: 0644]
web/query/shows/edit.js [new file with mode: 0644]
web/query/shows/post.js [new file with mode: 0644]
web/query/sofa2.txt [new file with mode: 0644]
web/query/templates/.rpms.html.swp [new file with mode: 0644]
web/query/templates/edit.html [new file with mode: 0644]
web/query/templates/index.html [new file with mode: 0644]
web/query/templates/nodelist.html [new file with mode: 0644]
web/query/templates/partials/comment.html [new file with mode: 0644]
web/query/templates/partials/header.html [new file with mode: 0644]
web/query/templates/partials/scripts.html [new file with mode: 0644]
web/query/templates/post.html [new file with mode: 0644]
web/query/templates/rpms.html [new file with mode: 0644]
web/query/validate_doc_update.js [new file with mode: 0644]
web/query/vendor/couchapp/_attachments/jquery.couch.app.js [new file with mode: 0644]
web/query/vendor/couchapp/_attachments/jquery.couch.app.util.js [new file with mode: 0644]
web/query/vendor/couchapp/_attachments/jquery.evently.js [new file with mode: 0644]
web/query/vendor/couchapp/_attachments/jquery.mustache.js [new file with mode: 0644]
web/query/vendor/couchapp/_attachments/jquery.pathbinder.js [new file with mode: 0644]
web/query/vendor/couchapp/_attachments/loader.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/README.md [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/_init.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/adminParty/mustache.html [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/doLogin.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/doLogout.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/doSignup.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loggedIn/after.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loggedIn/data.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loggedIn/mustache.html [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loggedIn/selectors.json [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loggedOut/mustache.html [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loggedOut/selectors.json [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loginForm/after.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loginForm/mustache.html [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loginForm/selectors/a[href=#signup].json [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/loginForm/selectors/form/submit.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/signupForm/after.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/signupForm/mustache.html [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/signupForm/selectors/a[href=#login].json [new file with mode: 0644]
web/query/vendor/couchapp/evently/account/signupForm/selectors/form/submit.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/profile/loggedIn.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/profile/loggedOut/after.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/profile/loggedOut/mustache.html [new file with mode: 0644]
web/query/vendor/couchapp/evently/profile/noProfile/data.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/profile/noProfile/mustache.html [new file with mode: 0644]
web/query/vendor/couchapp/evently/profile/noProfile/selectors/form/submit.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/profile/profileReady/after.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/profile/profileReady/data.js [new file with mode: 0644]
web/query/vendor/couchapp/evently/profile/profileReady/mustache.html [new file with mode: 0644]
web/query/vendor/couchapp/lib/atom.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/cache.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/code.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/docform.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/linkup.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/list.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/markdown.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/md5.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/mustache.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/path.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/redirect.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/utils.js [new file with mode: 0644]
web/query/vendor/couchapp/lib/validate.js [new file with mode: 0644]
web/query/vendor/couchapp/metadata.json [new file with mode: 0644]
web/query/vendor/textile/textile.js [new file with mode: 0644]
web/query/views/comments/map.js [new file with mode: 0644]
web/query/views/host-to-rpm/map.js [new file with mode: 0644]
web/query/views/host-to-rpm/reduce.js [new file with mode: 0644]
web/query/views/node-page/map.js [new file with mode: 0644]
web/query/views/node-status/map.js [new file with mode: 0644]
web/query/views/post-page/map.js [new file with mode: 0644]
web/query/views/recent-posts/map.js [new file with mode: 0644]
web/query/views/rpm-list/map.js [new file with mode: 0644]
web/query/views/rpm-list/reduce.js [new file with mode: 0644]
web/query/views/rpm-to-host/.map.js.swp [new file with mode: 0644]
web/query/views/rpm-to-host/map.js [new file with mode: 0644]
web/query/views/rpm-to-host/reduce.js [new file with mode: 0644]
web/query/views/tags/map.js [new file with mode: 0644]
web/query/views/tags/reduce.js [new file with mode: 0644]
web/setup.sh [new file with mode: 0644]
web/view/.bash_eternal_history [new file with mode: 0644]
web/view/index.php [new file with mode: 0644]
web/view/storage-schemas.conf [new file with mode: 0644]
web/view/urllist.txt [new file with mode: 0644]

index d8bc8ad..d05a8c1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ $(main.FILE): #FORCE
        rm -f myops-1.0.tar.bz2
        ( test -d $(PWD)/../myops-1.0 &&  unlink $(PWD)/../myops-1.0/ ) || echo "none"
        ln -s $(PWD) $(PWD)/../myops-1.0
-       tar -jchvf myops-1.0.tar.bz2 -C ../  myops-1.0
+       tar -jchvf myops-1.0.tar.bz2 --exclude .git --exclude *.bz2 -C ../  myops-1.0
        @echo "hello"
 
 sources: $(SOURCEFILES)
index 28b623a..c25cb4c 100644 (file)
@@ -33,18 +33,18 @@ Group: Applications/System
 MyOps is a collection of support services and scripts for operating a MyPLC
 deployment.
 
-%package client
+%package puppet-client
 Summary: MyOps for client
 Group: Applications/System
 
 Requires: puppet
 Requires: coreutils
 
-%description client
+%description puppet-client
 Client side configuration.
 
 ######################################## Server
-%package server
+%package puppet-server
 Summary: MyOps the server side
 Group: Applications/System
 
@@ -52,9 +52,45 @@ Requires: puppet-server
 Requires: puppet
 Requires: coreutils
 
-%description server
+%description puppet-server
 Server side services
 
+
+######################################## get/query/view
+%package getqueryview
+Summary: MyOps components for collection, view and query.
+Group: Applications/System
+
+# view
+Requires: bzr
+Requires: nc
+Requires: coreutils
+Requires: twisted
+Requires: pycairo
+Requires: mod_python
+Requires: python-ldap
+Requires: python-memcached
+Requires: mod_wsgi
+Requires: Django 
+Requires: mlocate
+Requires: sysstat
+# NOTE: some default fonts incase no others are present.
+Requires: bitstream-vera-fonts-common
+Requires: bitstream-vera-sans-fonts 
+Requires: bitstream-vera-sans-mono-fonts
+
+# query
+Requires: couchdb
+Requires: python-couchdb
+Requires: python-setuptools-devel
+
+#collect
+Requires: php
+
+
+%description getqueryview
+The combination of collection, query and view servers.
+
 %prep
 %setup -q
 
@@ -62,29 +98,62 @@ Server side services
 rm -rf $RPM_BUILD_ROOT
 
 
-# setup directories
+#################### SERVER
 install -d $RPM_BUILD_ROOT/usr/share/%{name}
+install -d $RPM_BUILD_ROOT/usr/bin
 install -d $RPM_BUILD_ROOT/etc/puppet/manifests
 install -d $RPM_BUILD_ROOT/etc/puppet/bin
-install -d $RPM_BUILD_ROOT/etc/cron.hourly
 install -d $RPM_BUILD_ROOT/var/lib/puppet
+install -d $RPM_BUILD_ROOT/%{_sysconfdir}/cron.hourly/
+install -d $RPM_BUILD_ROOT/%{_sysconfdir}/cron.d/
+
+install -d $RPM_BUILD_ROOT/etc/planetlab/db-config.d/
+install -d $RPM_BUILD_ROOT/var/www/html/PlanetLabConf/
+install -d $RPM_BUILD_ROOT/var/www/html/view
 
-# server
 rsync -a ./puppet  $RPM_BUILD_ROOT/usr/share/myops/
+rsync -a ./web $RPM_BUILD_ROOT/usr/share/myops/
+
+# Generate an autosign list from plc node hostnames
 install -D -m 755 puppet/cron.d/autosign.plcsh $RPM_BUILD_ROOT/%{_sysconfdir}/cron.hourly/
 install -D -m 755 puppet/server/bin/node_classifier $RPM_BUILD_ROOT/%{_sysconfdir}/puppet/bin/
 install -D -m 644 puppet/server/manifests/site.pp $RPM_BUILD_ROOT/%{_sysconfdir}/puppet/manifests/
 install -D -m 644 puppet/server/puppetmaster.conf $RPM_BUILD_ROOT/%{_sysconfdir}/puppet/
+install -D -m 644 puppet/client/puppet.conf $RPM_BUILD_ROOT/%{_sysconfdir}/puppet
 
-# client
-install -D -m 644 puppet/client/hardcoded.conf $RPM_BUILD_ROOT/%{_sysconfdir}/puppet
 rsync -ar ./puppet/server/modules $RPM_BUILD_ROOT/%{_sysconfdir}/puppet/
 
+ls web/collect
+ls $RPM_BUILD_ROOT
+
+install -D -m 644 web/db-config.d/030-conf_files_myops $RPM_BUILD_ROOT/etc/planetlab/db-config.d
+install -D -m 755 web/collect/sar2graphite.py $RPM_BUILD_ROOT/usr/bin/
+install -D -m 755 web/collect/sar2graphite.py $RPM_BUILD_ROOT/var/www/html/PlanetLabConf/
+install -D -m 644 web/collect/cron.d/sysstat.cron $RPM_BUILD_ROOT/var/www/html/PlanetLabConf/
+install -D -m 644 web/collect/cron.d/sysstat.cron $RPM_BUILD_ROOT/%{_sysconfdir}/cron.d/
+install -D -m 755 web/collect/server/upload.php $RPM_BUILD_ROOT/var/www/html/
+install -D -m 755 web/collect/server/cron.hourly/load_all_couchdb.sh $RPM_BUILD_ROOT/%{_sysconfdir}/cron.hourly
+
+install -D -m 644 web/view/urllist.txt $RPM_BUILD_ROOT/var/www/html/view/
+install -D -m 644 web/view/index.php $RPM_BUILD_ROOT/var/www/html/view/
 
 %clean
 rm -rf $RPM_BUILD_ROOT
 
-%files server
+%files getqueryview
+%defattr(-,root,root)
+/usr/share/%{name}/web
+/%{_sysconfdir}/cron.d/sysstat.cron
+/var/www/html/PlanetLabConf/sysstat.cron
+/var/www/html/PlanetLabConf/sar2graphite.py*
+/usr/bin/sar2graphite.py
+/etc/planetlab/db-config.d
+/var/www/html/upload.php
+%{_sysconfdir}/cron.hourly
+/var/www/html/view/urllist.txt
+/var/www/html/view/index.php
+
+%files puppet-server
 %defattr(-,root,root)
 /%{_sysconfdir}/cron.hourly/autosign.plcsh
 /%{_sysconfdir}/puppet/bin/node_classifier
@@ -92,22 +161,20 @@ rm -rf $RPM_BUILD_ROOT
 /%{_sysconfdir}/puppet/puppetmaster.conf
 /%{_sysconfdir}/puppet/modules
 
-%files client
+%files puppet-client
 %defattr(-,root,root)
-/%{_sysconfdir}/puppet/hardcoded.conf
-/usr/share/%{name}
+/%{_sysconfdir}/puppet/puppet.conf
+/usr/share/%{name}/puppet
 
-%post server
-echo "Reminder: open ports 8139,8140,8141 in iptables"
+%post puppet-server
+echo "Reminder: open ports for puppet: 8139,8140,8141 in iptables"
 chkconfig --add puppetmaster
 chkconfig puppetmaster on
 
-%post client
+%post puppet-client
 chkconfig --add puppet
 chkconfig puppet on
 
-cat /etc/puppet/hardcoded.conf >> /etc/puppet/puppet.conf
-
 
 %changelog
 %define module_current_branch 1.0
diff --git a/web/collect/automate/install_on_node.sh b/web/collect/automate/install_on_node.sh
new file mode 100644 (file)
index 0000000..eabc844
--- /dev/null
@@ -0,0 +1,8 @@
+(
+    mkdir -p /home/pl_monitor
+    cd /home/pl_monitor
+    curl --silent --insecure https://128.112.139.113/multiops/bootstrap.tar | tar -xvf -
+    chmod 755 ./*.sh
+    chmod 755 ./lshw
+    ./bootstrap.sh
+)
diff --git a/web/collect/automate/run.sh b/web/collect/automate/run.sh
new file mode 100755 (executable)
index 0000000..521fb24
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+export PATH=$PATH:/usr/bin:/bin:/usr/sbin
+cd /usr/share/monitor-trunk/multiops/
+/usr/bin/curl -s --insecure 'https://128.112.139.113/monitor/query?tg_format=plain&object=nodes&nodehistory_hostname=&observed_status=on&uptime=on&hostname=on&rpmvalue=' | grep BOOT | tr ' ' ','  | awk -F, '{print $1}' > out.nodes
+
+T=`date --rfc-3339='seconds'| tr ' ' 'T'`
+/usr/share/monitor-trunk/tools/automate/fetch.py --nodelist out.nodes --timeout 180 --cmd install_on_node.sh --outdir install_on_node.sh-$T
+
+
diff --git a/web/collect/client/bootstrap.sh b/web/collect/client/bootstrap.sh
new file mode 100644 (file)
index 0000000..50af68e
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# NOTE: use unique value to get minute
+if [[ -f /etc/planetlab/node_id ]] ; then
+    V=`cat /etc/planetlab/node_id`
+else
+    V=$RANDOM
+fi
+
+if [[ -f /etc/planetlab/plc_config ]] ; then
+       source /etc/planetlab/plc_config
+else
+       PLC_SLICE_PREFIX='pl'
+fi
+
+min=$(( $V % 60 ))
+min2=$(( ($min + 10) % 60 ))
+# Run every three hours using a fixed point in time.
+cat <<EOF > collect_and_upload.cron
+PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/home/${PLC_SLICE_PREFIX}_myops/
+$min */1 * * * /home/${PLC_SLICE_PREFIX}_myops/collect.sh
+$min2 */1 * * * /home/${PLC_SLICE_PREFIX}_myops/upload.sh
+EOF
+
+yum install -y lshw
+
+chmod 755 /home/${PLC_SLICE_PREFIX}_myops/collect.sh
+chmod 755 /home/${PLC_SLICE_PREFIX}_myops/upload.sh
+crontab -u root collect_and_upload.cron
+
+
diff --git a/web/collect/client/collect.sh b/web/collect/client/collect.sh
new file mode 100644 (file)
index 0000000..5bbc108
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/bash 
+
+if [ -f /etc/planetlab/plc_config ]; then
+       source /etc/planetlab/plc_config
+else
+       PLC_SLICE_PREFIX='pl'
+fi
+LOGFILE=/home/${PLC_SLICE_PREFIX}_myops/myops.log
+
+cd /home/${PLC_SLICE_PREFIX}_myops
+
+# TODO: add a timeout to eval, that kills children after X seconds
+function write_key_command ()
+{
+    key=$1
+    command=$2
+    value=`eval $command`
+    write_key_value "$key" "$value"
+}
+function write_key_value ()
+{
+    key=$1
+    value=$2
+    (
+        flock -s 200
+        echo $key : $value >> $LOGFILE
+    ) 200>/var/lock/myops
+}
+
+rm -f $LOGFILE
+count=0
+
+while read key colon command ; do
+        if [[ $colon = ":" ]] ; then
+                write_key_command "$key" "$command" &
+                count=$(( $count + 1 ))
+        else
+                if [[ $colon = ":=" ]] ; then
+                        eval $key=`eval $command`
+                else if [[ $colon = "()" ]] ; then
+                        eval "function $key () $command"
+                     fi
+                fi
+        fi
+done < <( curl --silent http://IPADDR/PlanetLabConf/input.cfg )
+
+write_key_value "count" "$count"
+
+
diff --git a/web/collect/client/get_bootcd_version.sh b/web/collect/client/get_bootcd_version.sh
new file mode 100755 (executable)
index 0000000..5ab3465
--- /dev/null
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+kernel=$1
+
+function get_version () {
+
+   DEVICE=$1
+   cd /tmp
+   if [ -f /mnt/boot/pl_version ]; then
+         cat /mnt/boot/pl_version 
+   fi
+}
+function get_kernel_version () {
+
+   DEVICE=$1
+   cd /tmp
+   if [ -f /mnt/boot/kernel ]; then
+         strings /mnt/boot/kernel | grep planetlab
+   else
+         echo "No kernel at /mnt/boot/kernel"
+   fi
+}
+
+function mount_works () {
+   DEVICE=$1
+   msg="mount: you must specify the filesystem type"
+   r=`mount $DEVICE /mnt/boot 2>&1`
+   if [ "$r" == "$msg" ] ; then 
+       return 1;
+   else
+       return 0;
+   fi
+}
+
+mkdir -p /mnt/boot
+DL=`./sysinfo/systeminfo.py | sort -n | awk '{print $2}' | head -1`
+if [ -z "$kernel" ] ; then
+    V="No BootImage Found"
+else
+    V="Unable to find kernel image"
+fi
+(
+    flock -x 400
+    for dev in /dev/cdrom /dev/cdrom1 $DL ${DL}4 ${DL}1; do
+        if [ -b $dev ] ; then
+            if mount_works $dev ; then
+                if [ -z "$kernel" ] ; then
+                    V=$( get_version $dev )
+                else
+                    V=$( get_kernel_version $dev )
+                fi
+                r=`umount /mnt/boot 2>&1`
+                msg2="umount: /mnt/boot: device is busy"
+                if [ "$r" == "$msg2" ] ; then 
+                    r=`umount /mnt/boot 2>&1`
+                fi
+                break
+            fi
+        fi
+    done
+
+    if [ -z "$kernel" ] ; then
+        if [ -z "$V" ] ; then
+            wc=$( dmesg | grep "Buffer I/O error"  | wc -l )
+            if [ $wc -gt 0 ] ; then
+                echo "Multiple IO errors prevent reading: $wc"
+            else 
+                echo "No BootImage Found"
+            fi
+        else
+            echo $V
+        fi
+    else
+        echo $V
+    fi
+) 400>/var/lock/myops.mount
diff --git a/web/collect/client/sysinfo/Exceptions.py b/web/collect/client/sysinfo/Exceptions.py
new file mode 100644 (file)
index 0000000..be4587e
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+# Copyright (c) 2003 Intel Corporation
+# All rights reserved.
+#
+# Copyright (c) 2004-2006 The Trustees of Princeton University
+# All rights reserved.
+
+class BootManagerException(Exception):
+    def __init__( self, err ):
+        self.__fault= err
+
+    def __str__( self ):
+        return self.__fault
+    
+class BootManagerAuthenticationException(Exception):
+    def __init__( self, err ):
+        self.__fault= err
+
+    def __str__( self ):
+        return self.__fault
diff --git a/web/collect/client/sysinfo/Exceptions.pyc b/web/collect/client/sysinfo/Exceptions.pyc
new file mode 100644 (file)
index 0000000..7f3a5dc
Binary files /dev/null and b/web/collect/client/sysinfo/Exceptions.pyc differ
diff --git a/web/collect/client/sysinfo/ModelOptions.py b/web/collect/client/sysinfo/ModelOptions.py
new file mode 100644 (file)
index 0000000..8c8e979
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+
+# Copyright (c) 2003 Intel Corporation
+# All rights reserved.
+#
+# Copyright (c) 2004-2006 The Trustees of Princeton University
+# All rights reserved.
+
+
+import string
+
+MINHW   = 0x001
+SMP     = 0x002
+X86_64  = 0x004
+INTEL   = 0x008
+AMD     = 0x010
+NUMA    = 0x020
+GEODE   = 0x040
+BADHD   = 0x080
+LAST    = 0x100
+RAWDISK = 0x200
+
+modeloptions = {'smp':SMP,
+                'x64':X86_64,
+                'i64':X86_64|INTEL,
+                'a64':X86_64|AMD,
+                'i32':INTEL,
+                'a32':AMD,
+                'numa':NUMA,
+                'geode':GEODE,
+                'badhd':BADHD,
+                'minhw':MINHW,
+                'rawdisk':RAWDISK}
+
+def Get(model):
+    modelinfo = string.split(model,'/')
+    options= 0
+    for mi in modelinfo:
+        info = string.strip(mi)
+        info = info.lower()
+        options = options | modeloptions.get(info,0)
+
+    return options
+
diff --git a/web/collect/client/sysinfo/ModelOptions.pyc b/web/collect/client/sysinfo/ModelOptions.pyc
new file mode 100644 (file)
index 0000000..1a13428
Binary files /dev/null and b/web/collect/client/sysinfo/ModelOptions.pyc differ
diff --git a/web/collect/client/sysinfo/pypci.py b/web/collect/client/sysinfo/pypci.py
new file mode 100644 (file)
index 0000000..32e8ac4
--- /dev/null
@@ -0,0 +1,83 @@
+# Copyright 2008 The Trustees of Princeton University
+# Author: Daniel Hokka Zakrisson
+# $Id$
+# vim:ts=4:expandtab
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met: 
+# 
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#       
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#       
+# * Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#       
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRINCETON
+# UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE. 
+
+PCI_BASE_CLASS_NETWORK=0x02L
+PCI_BASE_CLASS_STORAGE=0x01L
+PCI_ANY=0xffffffffL
+
+def get_devices():
+    """ This is a replacement to the pypciscan library."""
+    import os
+    pci_cmd = os.popen("""/sbin/lspci -nvm | sed -e 's/\t/ /g' -e 's/ Class / /' -e 's/^/"/' -e 's/$/"/' -e 's/$/,/' -e 's/^"",$/],[/'""", 'r')
+    pci_str = "[" + pci_cmd.read() + "]"
+    pci_list = eval(pci_str)
+
+    pci_devlist = []
+    # convert each entry into a dict. and convert strings to ints.
+    for dev in pci_list:
+        rec = {}
+        for field in dev:
+            s = field.split(":")
+            if len(s) > 2:
+                # There are two 'device' fields in the output. Append
+                # 'addr' for the bus address, identified by the extra ':'.
+                end=":".join(s[1:])
+                value = end.strip()
+                key = s[0].lower() + "addr"
+            else:
+                value = int(s[1].strip(), 16)
+                key = s[0].lower()
+
+            rec[key] = value
+
+        pci_devlist.append(rec)
+
+    ret = {}
+    # convert this list of devices into the format expected by the
+    # consumer of get_devices()
+    for dev in pci_devlist:
+        if 'deviceaddr' not in dev:
+            continue
+
+        subdev = dev.get('sdevice',PCI_ANY)
+        subvend = dev.get('svendor',PCI_ANY)
+        progif = dev.get('progif',0)
+
+        value = (dev['vendor'], dev['device'], subvend, subdev, dev['class'] << 8 | progif)
+        ret[dev['deviceaddr']] = value
+
+    return  ret
+
+# for convenience for the clients of pypci
+import pypcimap
diff --git a/web/collect/client/sysinfo/pypci.pyc b/web/collect/client/sysinfo/pypci.pyc
new file mode 100644 (file)
index 0000000..17c8702
Binary files /dev/null and b/web/collect/client/sysinfo/pypci.pyc differ
diff --git a/web/collect/client/sysinfo/pypcimap.py b/web/collect/client/sysinfo/pypcimap.py
new file mode 100644 (file)
index 0000000..81720b2
--- /dev/null
@@ -0,0 +1,84 @@
+#!/usr/bin/python -tt
+# Copyright 2007 The Trustees of Princeton University
+# Author: Daniel Hokka Zakrisson
+# $Id$
+# vim:ts=4:expandtab
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met: 
+# 
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#       
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#       
+# * Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#       
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRINCETON
+# UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE. 
+
+import os
+import re
+
+# These are modules which are only returned if no other driver is available
+greylist = ["ata_generic", "eepro100", "8139cp"]
+
+class PCIMap:
+    """Encapsulates modules.pcimap"""
+    def __init__(self, filename):
+        self.list = []
+        self.read(filename)
+    def get(self, tuple):
+        """Returns a list of candidate modules for the PCI device specified in tuple"""
+        ret = []
+        for i in self.list:
+            if ((i[1] == tuple[0] or i[1] == 0xffffffffL) and
+                (i[2] == tuple[1] or i[2] == 0xffffffffL) and
+                (i[3] == tuple[2] or i[3] == 0xffffffffL) and
+                (i[4] == tuple[3] or i[4] == 0xffffffffL) and
+                (i[5] == (tuple[4] & i[6]))):
+                ret.append(i[0])
+        for i in greylist:
+            if i in ret and len(ret) > 1:
+                ret.remove(i)
+        return ret
+    def add(self, list):
+        # FIXME: check values
+        self.list.append(list)
+    def read(self, filename):
+        f = file(filename)
+        pattern = re.compile("(\\S+)\\s+0x([0-9A-Fa-f]+)\\s0x([0-9A-Fa-f]+)\\s0x([0-9A-Fa-f]+)\\s0x([0-9A-Fa-f]+)\\s0x([0-9A-Fa-f]+)\\s0x([0-9A-Fa-f]+)\\s0x([0-9A-Fa-f]+)\\n")
+        while True:
+            line = f.readline()
+            if line == "":
+                break
+            if line[0] == '#' or line[0] == '\n':
+                continue
+            match = pattern.match(line)
+            if not match:
+                continue
+            self.add([match.group(1),
+                int(match.group(2), 16),
+                int(match.group(3), 16),
+                int(match.group(4), 16),
+                int(match.group(5), 16),
+                int(match.group(6), 16),
+                int(match.group(7), 16),
+                int(match.group(8), 16)])
+        f.close()
diff --git a/web/collect/client/sysinfo/pypcimap.pyc b/web/collect/client/sysinfo/pypcimap.pyc
new file mode 100644 (file)
index 0000000..d20cfb2
Binary files /dev/null and b/web/collect/client/sysinfo/pypcimap.pyc differ
diff --git a/web/collect/client/sysinfo/systeminfo.py b/web/collect/client/sysinfo/systeminfo.py
new file mode 100755 (executable)
index 0000000..1e6caa7
--- /dev/null
@@ -0,0 +1,380 @@
+#!/usr/bin/python
+
+# Copyright (c) 2003 Intel Corporation
+# All rights reserved.
+#
+# Copyright (c) 2004-2006 The Trustees of Princeton University
+# All rights reserved.
+# expected /proc/partitions format
+
+
+#----------------------------------------------------
+#major minor  #blocks  name
+#
+#3     0   40017915 hda
+#3     1     208813 hda1
+#3     2   20482875 hda2
+#3     3     522112 hda3
+#3     4   18804082 hda4
+#----------------------------------------------------
+
+
+import string
+import sys
+import os
+import popen2
+import re
+import errno
+import ModelOptions
+from pypci import *
+from Exceptions import *
+
+"""
+a utility class for finding and returning information about
+block devices, memory, and other hardware on the system
+"""
+
+PROC_MEMINFO_PATH= "/proc/meminfo"
+PROC_PARTITIONS_PATH= "/proc/partitions"
+
+# set when the sfdisk -l <dev> trick has been done to make
+# all devices show up
+DEVICES_SCANNED_FLAG= "/tmp/devices_scanned"
+
+# a /proc/partitions block is 1024 bytes
+# a GB to a HDD manufacturer is 10^9 bytes
+BLOCKS_PER_GB = pow(10, 9) / 1024.0;
+
+
+MODULE_CLASS_NETWORK= "network"
+MODULE_CLASS_SCSI= "scsi"
+
+#PCI_* is now defined in the pypci modules
+#PCI_BASE_CLASS_NETWORK=0x02L
+#PCI_BASE_CLASS_STORAGE=0x01L
+
+def get_total_phsyical_mem(vars = {}, log = sys.stderr):
+    """
+    return the total physical memory of the machine, in kilobytes.
+
+    Return None if /proc/meminfo not readable.
+    """
+
+    try:
+        meminfo_file= file(PROC_MEMINFO_PATH,"r")
+    except IOError, e:
+        return
+
+    total_memory= None
+
+    for line in meminfo_file:
+
+        try:
+            (fieldname,value)= string.split(line,":")
+        except ValueError, e:
+            # this will happen for lines that don't have two values
+            # (like the first line on 2.4 kernels)
+            continue
+
+        fieldname= string.strip(fieldname)
+        value= string.strip(value)
+        
+        if fieldname == "MemTotal":
+            try:
+                (total_memory,units)= string.split(value)
+            except ValueError, e:
+                return
+
+            if total_memory == "" or total_memory == None or \
+                   units == "" or units == None:
+                return
+
+            if string.lower(units) != "kb":
+                return
+
+            try:
+                total_memory= int(total_memory)
+            except ValueError, e:
+                return
+
+            break
+
+    meminfo_file.close()
+    return total_memory
+
+def get_block_device_list(vars = {}, log = sys.stderr):
+    """
+    get a list of block devices from this system.
+    return an associative array, where the device name
+    (full /dev/device path) is the key, and the value
+    is a tuple of (major,minor,numblocks,gb_size,readonly)
+    """
+
+    # make sure we can access to the files/directories in /proc
+    if not os.access(PROC_PARTITIONS_PATH, os.F_OK):
+        return None
+
+    # table with valid scsi/sata/ide/raid block device names
+    valid_blk_names = {}
+    # add in valid sd and hd block device names
+    for blk_prefix in ('sd','hd'):
+        for blk_num in map ( \
+            lambda x: chr(x), range(ord('a'),ord('z')+1)):
+            devicename="%s%c" % (blk_prefix, blk_num)
+            valid_blk_names[devicename]=None
+
+    # add in valid scsi raid block device names
+    for M in range(0,1+1):
+        for N in range(0,7+1):
+            devicename = "cciss/c%dd%d" % (M,N)
+            valid_blk_names[devicename]=None
+
+    for devicename in valid_blk_names.keys():
+        # devfs under 2.4 (old boot cds) used to list partitions
+        # in a format such as scsi/host0/bus0/target0/lun0/disc
+        # and /dev/sda, etc. were just symlinks
+        try:
+            devfsname= os.readlink( "/dev/%s" % devicename )
+            valid_blk_names[devfsname]=None
+        except OSError:
+            pass
+
+    # only do this once every system boot
+    if not os.access(DEVICES_SCANNED_FLAG, os.R_OK):
+
+        # this is ugly. under devfs, device
+        # entries in /dev/scsi/.. and /dev/ide/...
+        # don't show up until you attempt to read
+        # from the associated device at /dev (/dev/sda).
+        # so, lets run sfdisk -l (list partitions) against
+        # most possible block devices, that way they show
+        # up when it comes time to do the install.
+        devicenames = valid_blk_names.keys()
+        devicenames.sort()
+        for devicename in devicenames:
+            os.system( "sfdisk -l /dev/%s > /dev/null 2>&1" % devicename )
+
+        # touch file
+        fb = open(DEVICES_SCANNED_FLAG,"w")
+        fb.close()
+
+    devicelist= {}
+
+    partitions_file= file(PROC_PARTITIONS_PATH,"r")
+    line_count= 0
+    for line in partitions_file:
+        line_count= line_count + 1
+
+        # skip the first two lines always
+        if line_count < 2:
+            continue
+
+        parts= string.split(line)
+
+        if len(parts) < 4:
+            continue
+
+        device= parts[3]
+
+        # skip and ignore any partitions
+        if not valid_blk_names.has_key(device):
+            continue
+
+        try:
+            major= int(parts[0])
+            minor= int(parts[1])
+            blocks= int(parts[2])
+        except ValueError, err:
+            continue
+
+        gb_size= blocks/BLOCKS_PER_GB
+
+        # check to see if the blk device is readonly
+        try:
+            # can we write to it?
+            dev_name= "/dev/%s" % device
+            fb = open(dev_name,"w")
+            fb.close()
+            readonly=False
+        except IOError, e:
+            # check if EROFS errno
+            if errno.errorcode.get(e.errno,None) == 'EROFS':
+                readonly=True
+            else:
+                # got some other errno, pretend device is readonly
+                readonly=True
+
+        devicelist[dev_name]= (major,minor,blocks,gb_size,readonly)
+
+    return devicelist
+
+
+def get_system_modules( vars = {}, log = sys.stderr):
+    """
+    Return a list of kernel modules that this system requires.
+    This requires access to the installed system's root
+    directory, as the following file must exist and is used:
+    <install_root>/lib/modules/(first entry if kernel_version unspecified)/modules.pcimap
+
+    If there are more than one kernels installed, and the kernel
+    version is not specified, then only the first one in
+    /lib/modules is used.
+
+    Returns a dictionary, keys being the type of module:
+        - scsi       MODULE_CLASS_SCSI
+        - network    MODULE_CLASS_NETWORK
+    The value being the kernel module name to load.
+
+    Some sata devices show up under an IDE device class,
+    hence the reason for checking for ide devices as well.
+    If there actually is a match in the pci -> module lookup
+    table, and its an ide device, its most likely sata,
+    as ide modules are built in to the kernel.
+    """
+
+    if not vars.has_key("SYSIMG_PATH"):
+        vars["SYSIMG_PATH"]="/"
+    SYSIMG_PATH=vars["SYSIMG_PATH"]
+
+    if not vars.has_key("NODE_MODEL_OPTIONS"):
+        vars["NODE_MODEL_OPTIONS"] = 0;
+
+    initrd, kernel_version = getKernelVersion(vars, log)
+
+    # get the kernel version we are assuming
+    if kernel_version is None:
+        try:
+            kernel_version= os.listdir( "%s/lib/modules/" % SYSIMG_PATH )
+        except OSError, e:
+            return
+
+        if len(kernel_version) == 0:
+            return
+
+        if len(kernel_version) > 1:
+            print( "WARNING: We may be returning modules for the wrong kernel." )
+
+        kernel_version= kernel_version[0]
+
+    print( "Using kernel version %s" % kernel_version )
+
+    # test to make sure the file we need is present
+    modules_pcimap_path = "%s/lib/modules/%s/modules.pcimap" % \
+                          (SYSIMG_PATH,kernel_version)
+    if not os.access(modules_pcimap_path,os.R_OK):
+        print( "WARNING: Unable to read %s" % modules_pcimap_path )
+        return
+
+    pcimap = pypcimap.PCIMap(modules_pcimap_path)
+
+    # this is the actual data structure we return
+    system_mods= {}
+
+    # these are the lists that will be in system_mods
+    network_mods= []
+    scsi_mods= []
+
+    # XXX: this is really similar to what BootCD/conf_files/pl_hwinit does. merge?
+    pcidevs = get_devices()
+
+    devlist=pcidevs.keys()
+    devlist.sort()
+    for slot in devlist:
+        dev = pcidevs[slot]
+        base = (dev[4] & 0xff0000) >> 16
+        modules = pcimap.get(dev)
+        if base not in (PCI_BASE_CLASS_STORAGE,
+                        PCI_BASE_CLASS_NETWORK):
+            # special exception for forcedeth NICs whose base id
+            # claims to be a Bridge, even though it is clearly a
+            # network device
+            if "forcedeth" in modules: 
+                base=PCI_BASE_CLASS_NETWORK
+            else:
+                continue
+
+        if len(modules) > 0:
+            if base == PCI_BASE_CLASS_NETWORK:
+                network_mods += modules
+            elif base == PCI_BASE_CLASS_STORAGE:
+                scsi_mods += modules
+
+    system_mods[MODULE_CLASS_SCSI]= scsi_mods
+    system_mods[MODULE_CLASS_NETWORK]= network_mods
+
+    return system_mods
+
+
+def getKernelVersion( vars = {} , log = sys.stderr):
+    # make sure we have the variables we need
+    try:
+        SYSIMG_PATH= vars["SYSIMG_PATH"]
+        if SYSIMG_PATH == "":
+            raise ValueError, "SYSIMG_PATH"
+
+        NODE_MODEL_OPTIONS=vars["NODE_MODEL_OPTIONS"]
+    except KeyError, var:
+        raise BootManagerException, "Missing variable in vars: %s\n" % var
+    except ValueError, var:
+        raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
+
+    option = ''
+    if NODE_MODEL_OPTIONS & ModelOptions.SMP:
+        option = 'smp'
+        try:
+            os.stat("%s/boot/kernel-boot%s" % (SYSIMG_PATH,option))
+            os.stat("%s/boot/initrd-boot%s" % (SYSIMG_PATH,option))
+        except OSError, e:
+            # smp kernel is not there; remove option from modeloptions
+            # such that the rest of the code base thinks we are just
+            # using the base kernel.
+            NODE_MODEL_OPTIONS = NODE_MODEL_OPTIONS & ~ModelOptions.SMP
+            vars["NODE_MODEL_OPTIONS"] = NODE_MODEL_OPTIONS
+            log.write( "WARNING: Couldn't locate smp kernel.\n")
+            option = ''
+    try:
+        initrd= os.readlink( "%s/boot/initrd-boot%s" % (SYSIMG_PATH,option) )
+        kernel_version= initrd.replace("initrd-", "").replace(".img", "")    
+    except OSError, e:
+        initrd = None
+        kernel_version = None
+        
+    return (initrd, kernel_version)
+
+
+if __name__ == "__main__":
+    devices= get_block_device_list()
+    #print "block devices detected:"
+    if not devices:
+        print "no devices found!"
+    else:
+        for dev in devices.keys():
+            #print "%s %s" % (dev, repr(devices[dev]))
+            print "%s %s" % (repr(devices[dev][3]), dev)
+            
+
+    #print ""
+    #memory= get_total_phsyical_mem()
+    #if not memory:
+    #    print "unable to read /proc/meminfo for memory"
+    #else:
+    #    print "total physical memory: %d kb" % memory
+        
+
+    #print ""
+
+    #kernel_version = None
+    #if len(sys.argv) > 2:
+    #    kernel_version = sys.argv[1]
+    #    
+    #modules= get_system_modules()
+    #if not modules:
+    #    print "unable to list system modules"
+    #else:
+    #    for module_class in (MODULE_CLASS_SCSI,MODULE_CLASS_NETWORK):
+    #        if len(modules[module_class]) > 0:
+    #            module_list = ""
+    #            for a_mod in modules[module_class]:
+    #                module_list = module_list + "%s " % a_mod
+    #            print "all %s modules: %s" % (module_class, module_list)
+                
diff --git a/web/collect/client/timeout3.sh b/web/collect/client/timeout3.sh
new file mode 100755 (executable)
index 0000000..5c19d2e
--- /dev/null
@@ -0,0 +1,91 @@
+#!/bin/bash
+#
+# The Bash shell script executes a command with a time-out.
+# Upon time-out expiration SIGTERM (15) is sent to the process. If the signal
+# is blocked, then the subsequent SIGKILL (9) terminates it.
+#
+# Based on the Bash documentation example.
+
+# Hello Chet,
+# please find attached a "little easier"  :-)  to comprehend
+# time-out example.  If you find it suitable, feel free to include
+# anywhere: the very same logic as in the original examples/scripts, a
+# little more transparent implementation to my taste.
+#
+# Dmitry V Golovashkin <Dmitry.Golovashkin@sas.com>
+
+scriptName="${0##*/}"
+
+declare -i DEFAULT_TIMEOUT=9
+declare -i DEFAULT_INTERVAL=1
+declare -i DEFAULT_DELAY=1
+
+# Timeout.
+declare -i timeout=DEFAULT_TIMEOUT
+# Interval between checks if the process is still alive.
+declare -i interval=DEFAULT_INTERVAL
+# Delay between posting the SIGTERM signal and destroying the process by SIGKILL.
+declare -i delay=DEFAULT_DELAY
+
+function printUsage() {
+    cat <<EOF
+
+Synopsis
+    $scriptName [-t timeout] [-i interval] [-d delay] command
+    Execute a command with a time-out.
+    Upon time-out expiration SIGTERM (15) is sent to the process. If SIGTERM
+    signal is blocked, then the subsequent SIGKILL (9) terminates it.
+
+    -t timeout
+        Number of seconds to wait for command completion.
+        Default value: $DEFAULT_TIMEOUT seconds.
+
+    -i interval
+        Interval between checks if the process is still alive.
+        Positive integer, default value: $DEFAULT_INTERVAL seconds.
+
+    -d delay
+        Delay between posting the SIGTERM signal and destroying the
+        process by SIGKILL. Default value: $DEFAULT_DELAY seconds.
+
+As of today, Bash does not support floating point arithmetic (sleep does),
+therefore all delay/time values must be integers.
+EOF
+}
+
+# Options.
+while getopts ":t:i:d:" option; do
+    case "$option" in
+        t) timeout=$OPTARG ;;
+        i) interval=$OPTARG ;;
+        d) delay=$OPTARG ;;
+        *) printUsage; exit 1 ;;
+    esac
+done
+shift $((OPTIND - 1))
+
+# $# should be at least 1 (the command to execute), however it may be strictly
+# greater than 1 if the command itself has options.
+if (($# == 0 || interval <= 0)); then
+    printUsage
+    exit 1
+fi
+
+# kill -0 pid   Exit code indicates if a signal may be sent to $pid process.
+(
+    ((t = timeout))
+
+    while ((t > 0)); do
+        sleep $interval
+        kill -0 $$ || exit 0
+        ((t -= interval))
+    done
+
+    # Be nice, post SIGTERM first.
+    # The 'exit 0' below will be executed if any preceeding command fails.
+    kill -s SIGTERM $$ && kill -0 $$ || exit 0
+    sleep $delay
+    kill -s SIGKILL $$
+) 2> /dev/null &
+
+exec "$@"
diff --git a/web/collect/client/upload.sh b/web/collect/client/upload.sh
new file mode 100644 (file)
index 0000000..02b7c5e
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+#if [ -f /tmp/source/configuration ] ; then
+#    source /tmp/source/configuration
+#elif [ -f /etc/planetlab/plc_config ] ; then
+#    source /etc/planetlab/plc_config
+#else 
+#      PLC_MONITOR_HOST=IPADDR
+#fi
+#if [[ -z "$PLC_MONITOR_HOST" || "$PLC_MONITOR_HOST" = "localhost.localdomain" ]] ; then
+#    PLC_MONITOR_HOST=monitor.planet-lab.org
+#fi
+if [ -f /etc/planetlab/plc_config ]; then
+       source /etc/planetlab/plc_config
+else
+       PLC_SLICE_PREFIX='pl'
+fi
+
+export MYOPS_SERVER=IPADDR
+
+function upload_log ()
+{
+    file=$1
+    path=$2
+    base=$( basename $file )
+    old=/tmp/${base}.old
+    new=/tmp/${base}.new
+    log=/tmp/${base}.log
+    if [ ! -f $file ] ; then
+        return
+    fi
+    if [ -f $new ] ; then
+        cp $new $old
+    else
+        touch $old
+    fi
+    cp $file $new
+    #comm -1 -3 $old $new > $log
+    cp $new $log
+    if [ $( stat -c %s $log ) -ne 0 ] ; then
+        curl --max-time 60 --silent http://${MYOPS_SERVER}/upload.php --form "log=@$log"
+        if [ $? -ne 0 ] ; then
+            # the upload has failed, so remove new file so no data is lost
+            rm -f /tmp/$( basename $file ).new
+        fi
+    fi
+}
+
+function upload_logs ()
+{
+    upload_log $1 multiops
+}
+
+upload_logs /home/${PLC_SLICE_PREFIX}_myops/myops.log
+
diff --git a/web/collect/cron.d/sysstat.cron b/web/collect/cron.d/sysstat.cron
new file mode 100644 (file)
index 0000000..c566fb6
--- /dev/null
@@ -0,0 +1,2 @@
+*/1 * * * * root /usr/bin/sar2graphite.py --ghost HOSTNAME --prefix sar. --sarargs "-q" > /dev/null
+53 23 * * * root /usr/lib64/sa/sa2 -A
diff --git a/web/collect/sar2graphite.py b/web/collect/sar2graphite.py
new file mode 100755 (executable)
index 0000000..cb679de
--- /dev/null
@@ -0,0 +1,101 @@
+#!/usr/bin/python
+
+import os
+import time
+import sys
+
+if 'LANG' in os.environ:
+    del os.environ['LANG']
+os.environ['PATH'] = "/sbin:/bin:/usr/sbin:/usr/bin"
+
+def date2ts(d, fmt="%m/%d/%y %H:%M:%S"):
+    return int(time.mktime(time.strptime(d, fmt)))
+
+def subfix(h):
+    f = h.split('_')
+    sf = f[::-1]
+    print sf
+    return sf[2] + "."
+
+def main():
+    from optparse import OptionParser
+    parser = OptionParser()
+
+    parser.set_defaults(prefix="sar.",
+                        sarargs="-q",
+                        seconds=60,
+                        subfix=False,
+                        debug=False,
+                        ghost=None,
+                        filter=None,
+                        )
+
+    parser.add_option("", "--prefix",  dest="prefix", help="")
+    parser.add_option("", "--sarargs",  dest="sarargs", help="")
+    parser.add_option("-s", "--seconds",  dest="seconds", help="")
+    parser.add_option("", "--subfix",  dest="subfix", action="store_true", help="")
+    parser.add_option("", "--debug",  dest="debug", action="store_true", help="")
+    parser.add_option("", "--filter",  dest="filter", help="")
+    parser.add_option("", "--ghost",  dest="ghost", help="")
+
+    (config, args) = parser.parse_args()
+    if len(sys.argv) == 1:
+        parser.print_help()
+        sys.exit(1)
+
+    if config.ghost is None:
+        # send to current host by default.
+        ghost_input = os.popen("/bin/hostname", 'r')
+        config.ghost = ghost_input.read().strip()
+
+    hostname_input = os.popen("/bin/hostname | sed -e 's/\./_/g' ", 'r')
+    sar_input = os.popen("/usr/bin/sar %s %s 1" % (config.sarargs, config.seconds), 'r')
+    nc_output = os.popen("/usr/bin/nc %s 2003" % config.ghost, 'w')
+
+    hostname = hostname_input.read().strip()
+    hostname_input.close()
+
+    first_date = ""
+    headers = []
+
+    if config.subfix:
+        sf = subfix(hostname)
+    else:
+        sf = ""
+    prefix = config.prefix + sf + hostname + "."
+
+    skip_keys = [ 'CPU', 'IFACE' ]
+
+    for line in sar_input:
+        fields = line.split()
+
+        if first_date == "":
+            first_date = fields[3]
+            continue
+
+        if headers == []:
+            headers = fields[1:]
+            continue
+
+        if fields == []:
+            headers = []
+
+        if len(fields) > 0 and fields[0] != "Average:":
+            ts = date2ts("%s %s" % (first_date, fields[0]) )
+            key_fix = ""
+            for k,v in zip(headers, fields[1:]):
+                if config.filter and k in config.filter:
+                    if v not in config.filter: break
+                    else: key_fix = v + "."
+                if k in skip_keys: continue
+
+                k = k.replace("/","_")
+                if not config.debug:
+                    print >>nc_output, prefix + key_fix + k, v, ts
+                print prefix + key_fix + k, v, ts
+                nc_output.flush()
+                
+    nc_output.close()
+
+if __name__ == "__main__":
+    main()
diff --git a/web/collect/server/build.sh b/web/collect/server/build.sh
new file mode 100755 (executable)
index 0000000..18e3871
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+tar -cvf /var/www/html/PlanetLabConf/bootstrap.tar -C /usr/share/myops/web/collect/client/ . 
+if [ ! -f /var/www/html/PlanetLabConf/input.cfg ] ; then
+    ln input.cfg /var/www/html/PlanetLabConf/input.cfg
+fi
diff --git a/web/collect/server/cron.hourly/load_all_couchdb.sh b/web/collect/server/cron.hourly/load_all_couchdb.sh
new file mode 100755 (executable)
index 0000000..40d76dd
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+PREFIX=/var/www/html/uploadlogs/raw
+EXEC_PREFIX=/usr/share/myops/web/collect/server
+
+lasthour=`date +%Y-%m-%dT%H -d "1 hour ago"`
+mkdir -p $PREFIX/old/$lasthour
+for file in `ls $PREFIX/$lasthour*--upload`; do 
+       $EXEC_PREFIX/load_couch.py $file && mv $file $PREFIX/old/$lasthour
+done
+
+#NOTE: temporary; catch any stragglers still being sent to monitor and copied here
+for file in `ls /root/$lasthour*--upload`; do 
+       $EXEC_PREFIX/load_couch.py $file && mv $file $PREFIX/old/$lasthour
+done
+
+$EXEC_PREFIX/summarize_logs.sh || :
+$EXEC_PREFIX/summarize_rpms.sh || :
+$EXEC_PREFIX/summarize_stats.sh || :
+
+thishour=`date +%Y-%m-%dT%H`
+mkdir -p $PREFIX/old/$thishour
+# NOTE: do it twice for all the files that were uploaded while the above was running.
+for file in `ls $PREFIX/$thishour*--upload`; do 
+       $EXEC_PREFIX/load_couch.py $file && mv $file $PREFIX/old/$thishour
+done
+
+curl -s 'http://HOSTNAME:5984/myops/_design/myops/_list/nodelist/node-status?fields=hostname&skip_header' | wc -l &> /dev/null
diff --git a/web/collect/server/input.cfg b/web/collect/server/input.cfg
new file mode 100644 (file)
index 0000000..dd98398
--- /dev/null
@@ -0,0 +1,45 @@
+type : echo "node-status-v3"
+date : date --rfc-3339='seconds' | tr ' ' 'T'
+ts : date +%s
+hostname : hostname
+boot_state : if [ -d /vservers ] ; then echo 'BOOT' ; else echo 'DEBUG' ; fi
+ip_internal : ifconfig eth0 | grep "inet addr:" | sed -e 's/addr://' | awk '{print $2}'
+diskspace_root : python -c 'import sys, os; f="/"; v=os.statvfs(f); sys.stdout.write("%s %.3f %.3f\\n" % (f, v[4]/float(v[2]), v[6]/float(v[5])));' 2>/dev/null  
+diskspace_vservers : python -c 'import sys, os; f="/vservers/"; v=os.statvfs(f); sys.stdout.write("%s %.3f %.3f\\n" % (f, v[4]/float(v[2]), v[6]/float(v[5])));' 2>/dev/null  
+free_disk_root : python -c 'import sys, os; f="/"; v=os.statvfs(f); sys.stdout.write("%.3f\\n" % (v[4]/float(v[2]) ));' 2>/dev/null  
+free_inodes_root : python -c 'import sys, os; f="/"; v=os.statvfs(f); sys.stdout.write("%.3f\\n" % (v[6]/float(v[5]) ));' 2>/dev/null  
+free_disk_vservers : python -c 'import sys, os; f="/vservers/"; v=os.statvfs(f); sys.stdout.write("%.3f\\n" % (v[4]/float(v[2]) ));' 2>/dev/null  
+free_inodes_vservers : python -c 'import sys, os; f="/vservers/"; v=os.statvfs(f); sys.stdout.write("%.3f\\n" % (v[6]/float(v[5]) ));' 2>/dev/null  
+f := echo "/var/local/fprobe/"`ls -rt /var/local/fprobe | tail -1`
+fs_status : grep "planetlab-vservers.*ro," /proc/mounts ; touch /var/log/monitor 2>&1 ; if [ -d /vservers/ ] ; then touch /vservers/monitor.log 2>&1  ; fi 
+fs_status_ok : grep -q "planetlab-vservers.*ro," /proc/mounts || echo "ok" ;  grep -q "planetlab-vservers.*ro," /proc/mounts && echo "ko" ; 
+install_date : python -c "import os,time,stat; print time.strftime('%s %Y-%m-%dT%H:%M:%S',time.localtime(os.stat('/usr/boot/cacert.pem')[stat.ST_CTIME]))"
+iptables_status : iptables -t mangle -nL | awk '$1~/^[A-Z]+$/ {modules[$1]=1;}END{for (k in modules) {if (k) printf "%s ",k;}}'
+kernel_version : uname -r -v
+netflow : perl -e '@s=stat($ARGV[0]);$hours=(time()-$s[9])/3600;(($hours < 4) && print "Ok") || print("Bad");' $f
+netflow_live : touch /var/local/fprobe/.myopscheck;vserver pl_netflow exec bash -c 'if [ -f "/pf/.myopscheck" ]; then echo "OK"; else echo "KO"; fi;';rm -f /var/local/fprobe/.myopscheck
+nm_status : ps ax | grep nodemanager.py | grep -v grep | tail -1
+plc_config : wc -l /etc/planetlab/plc_config | awk '{if ($1<5) {print "KO";} else {print "OK";}}'
+princeton_comon_dir : ls -d /vservers/princeton_comon
+princeton_comon_procs : vps ax | grep `grep princeton_comon /etc/passwd | awk -F : '{if ( $3 > 500 ) { print $3}}'` | grep -v grep | wc -l
+princeton_comon_running : ls -d /proc/virtual/`grep princeton_comon /etc/passwd | awk -F : '{if ( $3 > 500 ) { print $3}}'`
+redundant_procs : sleep 4 && ps xo args | sort | uniq -c | sort -n | grep -v " [1-5] " | grep -vE "bash|flock|sshd|collect|grep" | tail -1 | tr ':' ';' 
+rpmprocess_count : pgrep "rpm|yum" | wc -l
+running_slices : vps ax | awk '{print $3}' | grep -vE 'ALL_PROC|MAIN|TTY|\\?' | sort | uniq
+uptime : cat /proc/uptime | awk '{print $1}'
+uptime_idle : cat /proc/uptime | awk '{print $2}'
+boot_server : cat /mnt/cdrom/bootme/BOOTSERVER
+bootcd_version : cat /mnt/cdrom/bootme/ID || cat /usr/bootme/ID
+real_bootcd_version : ./get_bootcd_version.sh 2>&1 
+real_bootcd_kernel_version : ./get_bootcd_version.sh -k 2>&1 
+rpm_versions :  sleep 6; if [ -f ./timeout3.sh ] ; then ./timeout3.sh -t 60 rpm -q -a ; else rpm -q -a ; fi
+traceroute_from_host : traceroute -n 128.112.139.91 | tr '\\n' '|'
+traceroute_to_host : curl -s --insecure 'https://128.112.139.113/monitor/traceroute'
+running () { pgrep -f $1 | wc -l | awk '{if ($1 > 0){ print "yes"} else { print "no" } }'; }
+zabbix_running : running zabbix
+nodemanager_running : running nodemanager
+nm_running : running nm.py
+func_running : running funcd
+codemux_running : running codemux
+fprobe_size : du -s /var/local/fprobe/ | awk '{print $1}'
+cpu_flags : grep flags /proc/cpuinfo | uniq | awk -F: '{print $2}'
diff --git a/web/collect/server/install_on_server.sh b/web/collect/server/install_on_server.sh
new file mode 100755 (executable)
index 0000000..a70ee56
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+tar -cvf /var/www/html/multiops/bootstrap.tar bootstrap.sh collect.sh upload.sh lshw timeout3.sh
diff --git a/web/collect/server/load-graphite.py b/web/collect/server/load-graphite.py
new file mode 100755 (executable)
index 0000000..ef6231f
--- /dev/null
@@ -0,0 +1,107 @@
+#!/usr/bin/python
+"""Copyright 2008 Orbitz WorldWide
+
+Licensed 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."""
+
+import sys
+import time
+import os
+import platform 
+import subprocess
+from socket import socket
+import time
+import csv
+
+def str_to_ts(date_str):
+       format_list = ["%m/%d/%Y %H:%M:%S", "%m/%d/%Y", "%Y/%m/%d %H:%M", "%Y/%b/%d %H:%M", "%Y-%m-%dT%H"]
+       ts = -1
+
+       for format in format_list:
+               try:
+                       if date_str.find('.') == -1:
+                               ts = time.mktime(time.strptime(date_str, format))
+                       else:
+                               ts = time.mktime(time.strptime(date_str[:date_str.find('.')], format))
+                       break
+               except:
+                       continue
+
+       if ts == -1:
+               raise Exception("No time format to convert date_str: %s" % date_str)
+
+       return ts
+
+def connect_to_carbon(server, port):
+       sock = socket()
+       try:
+               sock.connect( (server,port) )
+       except:
+               print "Couldn't connect to %(server)s on port %(port)d, is carbon-agent.py running?" % { 'server':server, 'port':port}
+               sys.exit(1)
+       return sock
+
+def main():
+
+       from optparse import OptionParser
+       parser = OptionParser()
+       
+       parser.set_defaults(target="",
+                                               date=None,
+                                               value=None,
+                                               datefield=0,
+                                               valuefield=1,
+                                               file=None)
+
+       parser.add_option("", "--target",   dest="target", help="")
+       parser.add_option("", "--date",  dest="date", help="")
+       parser.add_option("", "--value",        dest="value", help="")
+       parser.add_option("", "--datefield", dest="datefield", help="")
+       parser.add_option("", "--valuefield", dest="valuefield", help="")
+       parser.add_option("", "--file",  dest="file", )
+
+       (config, args) = parser.parse_args()
+       if len(sys.argv) == 1:
+               parser.print_help()
+               sys.exit(1)
+
+
+       #print "connecting..."
+       CARBON_SERVER = 'HOSTNAME'
+       CARBON_PORT = 2003
+       sock = connect_to_carbon(CARBON_SERVER, CARBON_PORT)
+
+       if config.file:
+               csvfile = csv.reader(open(config.file, 'r'), delimiter=',', quotechar='|')
+               first_line=True
+       else:
+               csvfile = [(config.date, config.value)]
+               first_line=False
+
+       #print csvfile
+       for line in csvfile:
+               if first_line:
+                       first_line = False
+               else:
+                       t = line[int(config.datefield)]
+                       v = line[int(config.valuefield)]
+
+                       #print t
+                       #print v
+                       ts = str_to_ts(t)
+
+                       message = "%s %s %d\n" % (config.target, v,ts)
+                       print "\t" + message[:-1]
+                       sock.sendall(message)
+
+if __name__ == "__main__":
+       main()
diff --git a/web/collect/server/load_couch.py b/web/collect/server/load_couch.py
new file mode 100755 (executable)
index 0000000..85969fe
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+import couchdb
+import sys
+from txt2dict import *
+import os
+import traceback
+import time
+
+couch = couchdb.Server("http://IPADDR:5984/")
+db = couch['myops']
+#db.resource.http.add_credentials('admin', 'ssorcmor')
+
+def set_or_update(id, data):
+    doc = db.get(id) 
+    if doc is None: 
+        db[id] = data
+    else:
+        # NOTE: how to update type?
+        del data['type']
+        doc.update(data)
+        print db.update([doc])
+
+def get_hist_id(data):
+    return data['ts'] + "-" + data['hostname']
+
+def get_single_id(data):
+    return data['hostname'] + "-" + data['type']
+
+for filename in sys.argv[1:]:
+    print filename
+    try:
+        data_hist = file_to_dict(filename)
+        data_single = data_hist.copy()
+    except:
+        traceback.print_exc()
+        continue
+
+    # history document
+    if 'ts' not in data_hist or 'hostname' not in data_hist or 'type' not in data_hist:
+        print "data missing fields", data_hist
+        continue
+
+    #hist_id = get_hist_id(data_hist)
+    #set_or_update(hist_id, data_hist)
+
+    # single document
+    single_id = get_single_id(data_single) 
+    data_single['type'] += "-single"
+    set_or_update(single_id, data_single)
+
+    os.system("echo %s >> /root/load.log" % filename)
+
+
diff --git a/web/collect/server/php.conf b/web/collect/server/php.conf
new file mode 100644 (file)
index 0000000..bb0f180
--- /dev/null
@@ -0,0 +1,28 @@
+#
+# PHP is an HTML-embedded scripting language which attempts to make it
+# easy for developers to write dynamically generated webpages.
+#
+<IfModule prefork.c>
+  LoadModule php5_module modules/libphp5.so
+</IfModule>
+<IfModule worker.c>
+  LoadModule php5_module modules/libphp5-zts.so
+</IfModule>
+
+#
+# Cause the PHP interpreter to handle files with a .php extension.
+#
+AddHandler php5-script .php
+AddType text/html .php
+
+#
+# Add index.php to the list of files that will be served as directory
+# indexes.
+#
+DirectoryIndex index.php
+
+#
+# Uncomment the following line to allow PHP to pretty-print .phps
+# files as PHP source code:
+#
+#AddType application/x-httpd-php-source .phps
diff --git a/web/collect/server/stats.py b/web/collect/server/stats.py
new file mode 100755 (executable)
index 0000000..8afd03a
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+import sys
+
+def getListFromFile(f):
+       list = []
+       for line in f:
+               line = line.strip()
+               list += [line]
+       return list
+
+l = getListFromFile(sys.stdin)
+l = [ float(x) for x in l ]
+# mean and standard deviation
+ls = len(l)
+print "05th", l[int(ls*0.05)]
+print "25th", l[int(ls*0.25)]
+print "50th", l[int(ls*0.50)]
+print "75th", l[int(ls*0.75)]
+print "95th", l[int(ls*0.95)]
+
diff --git a/web/collect/server/summarize_logs.sh b/web/collect/server/summarize_logs.sh
new file mode 100755 (executable)
index 0000000..c18b1a6
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+PREFIX=/var/www/html/uploadlogs/raw/old
+
+# get keys
+key_list="bootcd_version
+fs_status
+iptables_status
+kernel_version
+netflow
+netflow_live
+nm_running
+nodemanager_running
+plc_config
+real_bootcd_kernel_version
+real_bootcd_version
+"
+
+DATE=$( date --date "1 hour ago" +%Y-%m-%dT%H )
+#DATE=$1
+
+# get count of unique values from keys
+echo $START Hours ago == $DATE
+c=`ls $PREFIX/$DATE/*--upload | wc -l`
+if [[ $c -gt 2 ]] ; then
+    for key in $key_list ; do 
+        echo $key
+        c=`grep -E -h "^$key " $PREFIX/$DATE/*--upload | wc -l`
+               if [[ $c -gt 2 ]] ; then
+                       grep -E -h "^$key " $PREFIX/$DATE/*--upload | \
+                               awk '{if ( NF == 2 ) { print $0, "unknown" } else { print $0 } }' | \
+                               awk '{if ( $3 == "Linux" ) { print $1, $2, $5 } else { print $0 } }'  | \
+                               sed -e 's/^.* : //g' -e 's/ /_/g' -e 's/.*running_:/no/' -e 's/.*status_:/none/g' | \
+                               awk -F. '{ if ( $2 == "" ) { print $1 } else if ( $3 == "" ) { printf("%s.%s\n", $1, $2) } else { printf("%s.%s.%s\n", $1, $2, $3)} }' | \
+                               sed -e 's/Multiple_IO_errors.*/error/' \
+                                       -e 's/\/home\/pl_monitor.*/error/' \
+                                       -e 's/Unable_to_find.*/error/'  \
+                                       -e 's/.*cannot_touch.*/error/'  \
+                                       -e 's/No_kernel_at_.*/error/' | \
+                               sort | uniq -c | \
+                               sed -e 's/\./_/g' -e 's/\//_/g' | awk 'BEGIN{total=0} { total += $1 ; print $0 } END { print total, "total"}' | \
+                               while read value value_name ; do 
+                                       #echo -e "\tmyops.$key.$value_name $value $DATE" 
+                                       #echo -e "\t"/root/load-graphite.py --target "myops.$key.$value_name" --date $DATE  --value $value
+                                       /usr/share/myops/web/collect/server/load-graphite.py --target "myops.$key.$value_name" --date $DATE  --value $value
+                                       sleep .2
+                               done
+               fi
+    done
+fi
+
diff --git a/web/collect/server/summarize_rpms.sh b/web/collect/server/summarize_rpms.sh
new file mode 100755 (executable)
index 0000000..b3a13e9
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+PREFIX=/var/www/html/uploadlogs/raw/old
+
+# get keys
+key_list="NodeManager
+"
+extra="
+NodeUpdate
+fprobe-ulog
+iproute
+iptables
+kernel-2
+util-vserver-pl-0
+vserver-planetlab-f8-i386
+vsys
+vsys-scripts
+"
+
+#grep -E "^rpm_versions " * | \
+#      awk '{  for(o=1;o<=NF;o++){ if ( $o ~ /planetlab/){ print $(o) ; } } }' | \
+#      awk -F. '{printf("%s.%s.%s\n", $1,$2,$3) }' | sort | uniq -c
+
+
+DATE=$( date --date "1 hour ago" +%Y-%m-%dT%H )
+#DATE=$1
+
+# get count of unique values from keys
+echo $START Hours ago == $DATE
+c=`ls $PREFIX/$DATE/*--upload | wc -l`
+if [[ $c -gt 2 ]] ; then
+    for rpm in $key_list ; do 
+        echo $rpm
+        c=`grep -E -h "^rpm_versions .*$rpm" $PREFIX/$DATE/*--upload | wc -l`
+               if [[ $c -gt 2 ]] ; then
+                   grep -h -E "^rpm_versions .*$rpm" $PREFIX/$DATE/*--upload | \
+                           awk '{  for(o=1;o<=NF;o++){ if ( $o ~ /planetlab/){ print $(o) ; } } }' | \
+                                   awk -F. '{printf("%s.%s.%s\n", $1,$2,$3) }' | grep $rpm | sort | uniq -c | 
+                               sed -e 's/\./_/g' -e 's/\//_/g' | awk 'BEGIN{total=0} { total += $1 ; print $0 } END { print total, "total"}' | \
+                               while read value value_name ; do 
+                                       /root/load-graphite.py --target "myops.rpms.$rpm.$value_name" --date $DATE  --value $value
+                                       sleep .2
+                               done
+                   not_found=`grep -h -E "^rpm_versions .*" $PREFIX/$DATE/*--upload | grep -v "$rpm" | wc -l`
+                   /usr/share/myops/web/collect/server/load-graphite.py --target "myops.rpms.$rpm.notfound" --date $DATE  --value $not_found
+               fi
+    done
+fi
+
diff --git a/web/collect/server/summarize_stats.sh b/web/collect/server/summarize_stats.sh
new file mode 100755 (executable)
index 0000000..7406a08
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+PREFIX=/var/www/html/uploadlogs/raw/old
+
+# get keys
+key_list="uptime free_disk_root free_disk_vservers fprobe_size"
+extra="
+"
+
+#grep -E "^rpm_versions " * | \
+#      awk '{  for(o=1;o<=NF;o++){ if ( $o ~ /planetlab/){ print $(o) ; } } }' | \
+#      awk -F. '{printf("%s.%s.%s\n", $1,$2,$3) }' | sort | uniq -c
+
+
+if [[ -z "$1" ]] ; then
+    DATE=$( date --date "1 hour ago" +%Y-%m-%dT%H )
+else
+       DATE=$1
+fi
+
+# get count of unique values from keys
+echo $START Hours ago == $DATE
+c=`ls $PREFIX/$DATE/*--upload | wc -l`
+if [[ $c -gt 2 ]] ; then
+    for val in $key_list ; do 
+        echo $val
+        c=`grep -E -h "^$val :" $PREFIX/$DATE/*--upload | wc -l`
+               if [[ $c -gt 2 ]] ; then
+                   grep -h -E "^$val :" $PREFIX/$DATE/*--upload | \
+                               awk '{print $3}' | sort -n | \
+                               /usr/share/myops/web/collect/server/stats.py | \
+                               while read value_name value ; do 
+                                       /usr/share/myops/web/collect/server/load-graphite.py --target "myops.stats.$val.$value_name" --date $DATE  --value $value
+                                       sleep .2
+                               done
+               fi
+    done
+fi
+
diff --git a/web/collect/server/txt2dict.py b/web/collect/server/txt2dict.py
new file mode 100644 (file)
index 0000000..7a1a7d3
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+import sys
+
+def file_to_dict(file):
+    convert = {
+        'iptables_status' : "list",
+        'rpm_versions' : "list",
+        'running_slices' : "list",
+    }
+    f = open(file, 'r')
+    d = {}
+    for line in f:
+        l = line[:-1]
+        s = l.split()
+        key = s[0]
+        val = " ".join(s[2:])
+        d[key] = val
+        if key in convert:
+            if convert[key] == "list":
+                d[key] = d[key].split()
+    if 'type' not in d:
+        d['type'] = "node-status-v0"
+    return d
+
+
diff --git a/web/collect/server/upload.php b/web/collect/server/upload.php
new file mode 100755 (executable)
index 0000000..5cf172b
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+
+// $$
+
+// this needs be created with proper permissions at package install time
+$logdir="/var/www/html/uploadlogs";
+
+// limit: applies to uploads coming from an unrecognized IP
+$limit_bytes=8*1024;
+
+$default_hostname="unknown";
+
+function mkdir_if_needed ($dirname) {
+  if (is_dir ($dirname))
+    return;
+  mkdir ($dirname) or die ("Cannot create dir " . $dirname);
+}
+  
+// Get admin API handle
+//require_once 'plc_api.php';
+//global $adm;
+
+// find the node that these longs should belong to by looking for a node_id
+// with an ip the same as the http requestor ip
+$ip = $_SERVER['REMOTE_ADDR'];
+
+// $hostname=$default_hostname;
+// locate hostname from DB based on this IP
+// $interfaces=$adm->GetInterfaces(array("ip"=>$ip));
+// if (! empty($interfaces) ) {
+//   $interface=$interfaces[0];
+//   $node_id=$interface['node_id'];
+//   $nodes=$adm->GetNodes($node_id,array("hostname"));
+//   if (!empty($nodes)) {
+//     $hostname=$nodes[0]['hostname'];
+//   }
+//  }
+
+date_default_timezone_set("UTC");
+
+$rawdir=$logdir . "/raw";
+$date=strftime("%Y-%m-%dT%H:%M:%S");
+$log_name=$date . "--" . $ip . "--upload";
+$log_path=$rawdir . "/" . $log_name;
+$month=strftime("%Y-%m");
+$time=strftime("%d-%H-%M");
+
+mkdir_if_needed ($rawdir);
+
+////////////////////////////////////////
+
+$log=fopen($log_path,"w") or die ("Cannot open logfile "+$log_path);
+
+$uploaded_name= $_FILES['log']['tmp_name'];
+$uploaded_size=filesize($uploaded_name);
+
+#fprintf ($log, "BootManager log created on: %s-%s\n",$month,$time);
+fprintf( $log, "ip_external : %s\n",$ip);
+#fprintf( $log, "hostname: %s\n",$hostname);
+#fprintf ( $log, "uploaded file: %s (%d bytes)\n",$uploaded_name,$uploaded_size);
+#if ( ( strcmp($hostname,$default_hostname)==0) && ( $uploaded_size >= $limit_bytes) ) {
+#  fprintf ( $log, "contents from an unrecognized IP address was truncated to %d bytes\n",$limit_bytes);
+#  $truncated=TRUE;
+#  $uploaded_size=$limit_bytes;
+# } else {
+#  $truncated=FALSE;
+# }
+
+#fprintf( $log, "-----------------\n\n" );
+if ( isset($uploaded_name) && $uploaded_name ) {
+    $uploaded = fopen($uploaded_name,'r');
+       $contents = fread($uploaded, $uploaded_size);
+       fclose($uploaded);
+       fwrite($log,$contents);
+       fclose($log);
+} else {
+       $errlog = fopen("/var/www/html/upload.error.log", 'a+');
+       fwrite($errlog, "$ip failed upload\n");
+}
+
+////////////////////////////////////////
+
+// create symlinks for easy browsing
+
+// /var/log/bm/per-month/2008-11/onelab1.inria.fr/31-20-02.bmlog
+#$linkdir=$logdir;
+#$linkdir=$linkdir . "/per-month";
+#mkdir_if_needed ($linkdir);
+#$linkdir=$linkdir . "/" . $month;
+#mkdir_if_needed ($linkdir);
+#$linkdir = $linkdir . "/" . $hostname;
+#mkdir_if_needed ($linkdir);
+#$link = $linkdir . "/" . $time ;
+#symlink ("../../../raw/".$log_name,$link);
+
+## /var/log/bm/per-hostname/onelab1.inria.fr/2008-11-31-20-02.bmlog
+#$linkdir=$logdir;
+#$linkdir=$linkdir . "/per-hostname";
+#mkdir_if_needed ($linkdir);
+#$linkdir=$linkdir . "/" . $hostname;
+#mkdir_if_needed ($linkdir);
+#$link = $linkdir . "/" . $month . "-" . $time ;
+#symlink ("../../raw/".$log_name,$link);
+#
+## /var/log/bm/per-ip/138.96.250.141/2008-11-31-20-02.bmlog
+#$linkdir=$logdir;
+#$linkdir=$linkdir . "/per-ip";
+#mkdir_if_needed ($linkdir);
+#$linkdir=$linkdir . "/" . $ip;
+#mkdir_if_needed ($linkdir);
+#$link = $linkdir . "/" . $month . "-" . $time ;
+#symlink ("../../raw/".$log_name,$link);
+
+?>
diff --git a/web/db-config.d/030-conf_files_myops b/web/db-config.d/030-conf_files_myops
new file mode 100644 (file)
index 0000000..b83eed7
--- /dev/null
@@ -0,0 +1,36 @@
+# -*-python-*-
+# $Id: 030-conf_files$
+#################### conf files
+
+conf_files = [
+
+{'dest': u'/usr/bin/sar2graphite.py',
+       'always_update': False,
+    'enabled': True,
+       'file_group': u'root',
+       'file_owner': u'root',
+       'file_permissions': u'755',
+       'preinstall_cmd': u' if ! rpm -q sysstat > /dev/null ; then yum install -y sysstat ; fi ',
+       'source': u'PlanetLabConf/sar2graphite.py'},
+{'dest': u'/etc/cron.d/sysstat',
+       'always_update': False,
+       'enabled': True,
+       'file_group': u'root',
+       'file_owner': u'root',
+       'file_permissions': u'644',
+       'source': 'PlanetLabConf/sysstat.cron'},
+
+{'dest': "/home/%s_myops/bootstrap.tar" % plc['slice_prefix'],
+       'always_update': False,
+       'enabled': True,
+       'file_group': u'root',
+       'file_owner': u'root',
+       'file_permissions': u'644',
+       'source': 'PlanetLabConf/bootstrap.tar',
+       'preinstall_cmd': ' mkdir -p /home/%s_myops/ ' % plc['slice_prefix'],
+       'postinstall_cmd': ' cd /home/%s_myops/ ; tar -xvf bootstrap.tar ; chmod 755 ./*.sh ./lshw ; ./bootstrap.sh ' % plc['slice_prefix']},
+
+    ]
+
+for conf_file in conf_files:
+       SetConfFile(conf_file)
diff --git a/web/query/README.md b/web/query/README.md
new file mode 100644 (file)
index 0000000..d8e3664
--- /dev/null
@@ -0,0 +1,44 @@
+# Sofa: Standalone CouchDB Blog
+
+Sofa showcases the [potential of pure CouchDB applications](http://jchris.mfdz.com/code/2008/10/standalone_applications_with_co). It should provide an easy way for people to put thier thoughts online, anywhere there's a running Couch. It's just HTML, JavaScript and the magic of CouchDB.
+
+Currently supports authoring by anyone with the proper roles, and comments from anyone with a user account.
+
+## Current News
+
+Things are moving crazy fast around here right now as I bring this stuff up to ship-shape for the [CouchDB book](http://books.couchdb.org). I'll be renaming methods and stuff (if I find the time), any API feedback will be appreciated.
+
+## Install CouchDB
+
+You'll also need CouchDB (verion 0.11 or newer). Once you have that installed and the tests passing, you can install CouchApp
+and the blog software. 
+
+## Install CouchApp
+
+CouchApp makes it easy to edit application that are hosted in CouchDB, by keeping a correspondence between a set of files, and a CouchDB design document. You'll use CouchApp to install Sofa in your CouchDB instance.
+
+    sudo easy_install couchapp
+
+CouchApp is a set of utilities for developing standalone CouchDB applications You can [learn more about the CouchApp project here](http://github.com/couchapp/couchapp/). Also, [`easy_install` has an unpleasant bug on OSX](http://mail.python.org/pipermail/pythonmac-sig/2008-October/020567.html), so you might end up having to work from git source.
+
+
+### Setup Admin Access
+
+If you are going to put your blog in public, you'll want to [set up an Admin account (screencast)](http://www.youtube.com/watch?v=oHKvV3Nh-CI).
+
+
+## Install Sofa
+
+    git clone git://github.com/jchris/sofa.git
+    cd sofa
+    couchapp push . http://user:pass@127.0.0.1:5984/myblogdb 
+  
+You'll want to edit the HTML and CSS to personalize your site. Don't worry, the markup is pretty basic, so it's easy to rework. Adding new features is just a few lines of JavaScript away.
+
+Anytime you make edits to the on-disk version of Sofa, and want to see them in your browser, just run `couchapp push . http://127.0.0.1:5984/blogdb` again. **You probably want to setup your `.couchapprc` file.** You should read the CouchApp readme to learn about that.
+
+You can customize the blog title and other stuff in the `blog.json` file.
+
+# Relax
+
+[Visit your new blog.](http://127.0.0.1:5984/blogdb/_design/sofa/_list/index/recent-posts?descending=true&limit=5)
\ No newline at end of file
diff --git a/web/query/THANKS.txt b/web/query/THANKS.txt
new file mode 100644 (file)
index 0000000..90b46a2
--- /dev/null
@@ -0,0 +1,11 @@
+Sofa THANKS
+=====================
+
+A number of people have contributed to Sofa by reporting problems,
+suggesting improvements, submitting changes or asking hard questions.
+
+Some of these people are:
+
+* Andy Wenk <andy.wenk@googlemail.com>
+* Jan Lehnardt
+* You
\ No newline at end of file
diff --git a/web/query/_attachments/LICENSE.txt b/web/query/_attachments/LICENSE.txt
new file mode 100644 (file)
index 0000000..a7e77cb
--- /dev/null
@@ -0,0 +1,176 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/web/query/_attachments/THANKS.txt b/web/query/_attachments/THANKS.txt
new file mode 100644 (file)
index 0000000..b0ab7ae
--- /dev/null
@@ -0,0 +1,9 @@
+Sofa THANKS
+=====================
+
+A number of people have contributed to Sofa by reporting problems,
+suggesting improvements, submitting changes or asking hard question
+
+Some of these people are:
+
+* Andy Wenk <andy.wenk@googlemail.com>
\ No newline at end of file
diff --git a/web/query/_attachments/default.css b/web/query/_attachments/default.css
new file mode 100644 (file)
index 0000000..f7ae819
--- /dev/null
@@ -0,0 +1,190 @@
+/*#############################################################\r
+Name: Indigo\r
+Description: A lightweight (7kb images), simple and professional design.\r
+Date: 2006-07-27\r
+Author: Viktor Persson\r
+URL: http://arcsin.se\r
+\r
+Feel free to use and modify but please provide credits.\r
+#############################################################*/\r
+\r
+/* standard elements */\r
+* {\r
+       margin: 0;\r
+       padding: 0;\r
+}\r
+a {\r
+       color: #F70;\r
+}\r
+a:hover {\r
+       color: #C60;\r
+}\r
+body {\r
+       background: #0094D6 url(img/bg.gif) repeat-x;\r
+       color: #466;\r
+       font: normal 62.5% "Lucida Sans Unicode",sans-serif;\r
+       margin: 0;\r
+}\r
+input {\r
+       color: #555;\r
+       font: normal 1.1em "Lucida Sans Unicode",sans-serif;\r
+}\r
+p,cite,code,ul {\r
+       font-size: 1.2em;\r
+       padding-bottom: 1.2em;\r
+}\r
+h1 {\r
+       font-size: 1.4em;\r
+       margin-bottom: 4px;\r
+}\r
+code {\r
+       background: url(img/bgcode.gif);\r
+       border: 1px solid #F0F0F0;\r
+       border-left: 6px solid #39F;\r
+       color: #555;\r
+       display: block;\r
+       font: normal 1.1em "Lucida Sans Unicode",serif;\r
+       margin-bottom: 12px;\r
+       padding: 8px 10px;\r
+       white-space: pre;\r
+}\r
+cite {\r
+       background: url(img/quote.gif) no-repeat;\r
+       color: #666;\r
+       display: block;\r
+       font: normal 1.3em "Lucida Sans Unicode",serif;\r
+       padding-left: 28px;\r
+}\r
+h1,h2,h3 {\r
+       color: #06C;\r
+       padding-top: 6px;\r
+}\r
+/* misc */\r
+.clearer {\r
+       clear: both;\r
+}\r
+\r
+/* structure */\r
+.container {\r
+       background: url(img/topleft.gif) no-repeat;\r
+}\r
+\r
+.header {\r
+       height: 92px;\r
+}\r
+\r
+/* title */\r
+.title {\r
+       float: left;\r
+       padding: 28px 0 0 76px;\r
+}\r
+.title h1 {\r
+       color: #FFF;\r
+       font: normal 2em Verdana,sans-serif;\r
+}\r
+.title h4 {\r
+       color: #FFF;\r
+}\r
+/* navigation */\r
+.navigation {\r
+       float: left;\r
+       height: 92px;\r
+       margin-left: 24px;\r
+       padding: 0 16px;\r
+}\r
+.navigation a {\r
+       color: #FFF;\r
+       float: left;\r
+       font: bold 1.2em "Trebuchet MS",sans-serif;\r
+       margin-top: 56px;\r
+       padding: 8px 18px;\r
+       text-align: center;\r
+       text-decoration: none;\r
+}\r
+.navigation a:hover {\r
+       background-color: #4A91C3;\r
+       color: #FFF;\r
+}\r
+\r
+/* main */\r
+.main {\r
+       background: #FFF url(img/bgmain.gif) no-repeat;\r
+       clear: both;\r
+       padding: 12px 12px 0 52px;\r
+}\r
+\r
+/* main left */\r
+.sidenav {\r
+       float: left;\r
+       width: 24%;\r
+}\r
+.sidenav h1 {\r
+       border-bottom: 1px dashed #DDD;\r
+       color: #E73;\r
+       font-size: 1.2em;\r
+       height: 20px;\r
+       margin-top: 1.2em;\r
+}\r
+.sidenav ul {\r
+       margin: 0;\r
+       padding: 0;\r
+}\r
+.sidenav li { \r
+       border-bottom: 1px dashed #EEE;\r
+       list-style: none;\r
+       margin: 0;\r
+}\r
+.sidenav li a {\r
+       color: #777;\r
+       display: block;\r
+       font-size: 0.9em;\r
+       padding: 3px 6px 3px 18px;\r
+       text-decoration: none;\r
+}\r
+.sidenav li a:hover {\r
+       color: #111;\r
+       background: url(img/nav_li.gif) no-repeat;\r
+}\r
+\r
+/* content */\r
+.content {\r
+       float: left;\r
+       margin-right: 4%;\r
+       width: 69%;\r
+}\r
+.content .descr {\r
+       color: #C60;\r
+       margin-bottom: 6px;\r
+}\r
+.content li {\r
+       list-style: url(img/li.gif);\r
+       margin-left: 18px;\r
+}\r
+\r
+/* search form */\r
+.styled {\r
+       border: 3px double #E5E5E5;\r
+       padding: 2px 3px;\r
+}\r
+.button {\r
+       border: 1px solid #AAA;\r
+       margin-left: 5px;\r
+       padding: 2px 3px;\r
+}\r
+\r
+/* footer */\r
+.footer {\r
+       background: #0094D6 url(img/bgfooter.gif) repeat-x;\r
+       color: #C1DEF0;\r
+       font-size: 1.1em;\r
+       line-height: 40px;\r
+       text-align: center;\r
+}\r
+.footer a {\r
+       color: #FFF;\r
+       text-decoration: none;\r
+}\r
+.footer a:hover {\r
+       color: #FFF;\r
+       text-decoration: underline;\r
+}\r
diff --git a/web/query/_attachments/files/input.cfg b/web/query/_attachments/files/input.cfg
new file mode 100644 (file)
index 0000000..f8dcd00
--- /dev/null
@@ -0,0 +1,40 @@
+type : echo "node-status-v3"
+date : date --rfc-3339='seconds' | tr ' ' 'T'
+ts : date +%s
+hostname : hostname
+boot_state : if [ -d /vservers ] ; then echo 'BOOT' ; else echo 'DEBUG' ; fi
+ip_internal : ifconfig eth0 | grep "inet addr:" | sed -e 's/addr://' | awk '{print $2}'
+diskspace_root : python -c 'import sys, os; f="/"; v=os.statvfs(f); sys.stdout.write("%s %.3f %.3f\\n" % (f, v[4]/float(v[2]), v[6]/float(v[5])));' 2>/dev/null  
+diskspace_vservers : python -c 'import sys, os; f="/vservers/"; v=os.statvfs(f); sys.stdout.write("%s %.3f %.3f\\n" % (f, v[4]/float(v[2]), v[6]/float(v[5])));' 2>/dev/null  
+free_disk_root : python -c 'import sys, os; f="/"; v=os.statvfs(f); sys.stdout.write("%.3f\\n" % (v[4]/float(v[2]) ));' 2>/dev/null  
+free_inodes_root : python -c 'import sys, os; f="/"; v=os.statvfs(f); sys.stdout.write("%.3f\\n" % (v[6]/float(v[5]) ));' 2>/dev/null  
+free_disk_vservers : python -c 'import sys, os; f="/vservers/"; v=os.statvfs(f); sys.stdout.write("%.3f\\n" % (v[4]/float(v[2]) ));' 2>/dev/null  
+free_inodes_vservers : python -c 'import sys, os; f="/vservers/"; v=os.statvfs(f); sys.stdout.write("%.3f\\n" % (v[6]/float(v[5]) ));' 2>/dev/null  
+f := echo "/var/local/fprobe/"`ls -rt /var/local/fprobe | tail -1`
+fs_status : grep "planetlab-vservers.*ro," /proc/mounts ; touch /var/log/monitor 2>&1 ; if [ -d /vservers/ ] ; then touch /vservers/monitor.log 2>&1  ; fi 
+install_date : python -c "import os,time,stat; print time.strftime('%s %Y-%m-%dT%H:%M:%S',time.localtime(os.stat('/usr/boot/cacert.pem')[stat.ST_CTIME]))"
+iptables_status : iptables -t mangle -nL | awk '$1~/^[A-Z]+$/ {modules[$1]=1;}END{for (k in modules) {if (k) printf "%s ",k;}}'
+kernel_version : uname -r -v
+netflow : perl -e '@s=stat($ARGV[0]);$hours=(time()-$s[9])/3600;(($hours < 4) && print "Ok") || print("Bad");' $f
+netflow_live : touch /var/local/fprobe/.myopscheck;vserver pl_netflow exec bash -c 'if [ -f "/pf/.myopscheck" ]; then echo "OK"; else echo "KO"; fi;';rm -f /var/local/fprobe/.myopscheck
+nm_status : ps ax | grep nodemanager.py | grep -v grep | tail -1
+plc_config : wc -l /etc/planetlab/plc_config | awk '{if ($1<5) {print "KO";} else {print "OK";}}'
+princeton_comon_dir : ls -d /vservers/princeton_comon
+princeton_comon_procs : vps ax | grep `grep princeton_comon /etc/passwd | awk -F : '{if ( $3 > 500 ) { print $3}}'` | grep -v grep | wc -l
+princeton_comon_running : ls -d /proc/virtual/`grep princeton_comon /etc/passwd | awk -F : '{if ( $3 > 500 ) { print $3}}'`
+redundant_procs : sleep 10 && ps xo args | sort | uniq -c | sort -n | grep -v " [1-5] " | grep -vE "bash|flock|sshd|collect|grep" | tail -1 | tr ':' ';' 
+rpmprocess_count : pgrep "rpm|yum" | wc -l
+running_slices : vps ax | awk '{print $3}' | grep -vE 'ALL_PROC|MAIN|TTY|\\?' | sort | uniq
+uptime : cat /proc/uptime | awk '{print $1}'
+uptime_idle : cat /proc/uptime | awk '{print $2}'
+boot_server : cat /mnt/cdrom/bootme/BOOTSERVER
+bootcd_version : cat /mnt/cdrom/bootme/ID || cat /usr/bootme/ID
+rpm_versions :  sleep 10; if [ -f /home/pl_monitor/timeout3.sh ] ; then /home/pl_monitor/timeout3.sh -t 60 rpm -q -a ; else rpm -q -a ; fi
+traceroute_from_host : traceroute -n 128.112.139.91 | tr '\\n' '|'
+traceroute_to_host : curl -s --insecure 'https://128.112.139.113/monitor/traceroute'
+hardware_system : /home/pl_monitor/lshw -short -class system | grep -E "^\\W+system" | sed -e 's/.*system *//g'
+hardware_processor : /home/pl_monitor/lshw -short -class processor | grep -E "processor.*(Intel|AMD|Pentium)" | sed -e 's/.*processor *//g'
+hardware_memory : /home/pl_monitor/lshw -short -class memory | grep -E "memory.*[[:digit:]].*System Memory" | sed -e 's/.*memory *//g'
+hardware_disk : /home/pl_monitor/lshw -short -class disk | grep -E "sd.*disk" | sed -e 's/.*disk *//g' | tr '\\n' ';'
+hardware_network : /home/pl_monitor/lshw -short -class network | grep -E "eth[[:digit:]].*network" | sed -e 's/.*network *//g' | tr '\\n' ';'
+hardware_pcu : /home/pl_monitor/lshw -short -class system,generic | grep -E "Lights.*Controller|DRAC" | sed -e 's/.*system *//g;s/.*generic *//g'
diff --git a/web/query/_attachments/images/bg.jpg b/web/query/_attachments/images/bg.jpg
new file mode 100644 (file)
index 0000000..d8c2b34
Binary files /dev/null and b/web/query/_attachments/images/bg.jpg differ
diff --git a/web/query/_attachments/images/bottom.png b/web/query/_attachments/images/bottom.png
new file mode 100644 (file)
index 0000000..a594b8d
Binary files /dev/null and b/web/query/_attachments/images/bottom.png differ
diff --git a/web/query/_attachments/images/content.png b/web/query/_attachments/images/content.png
new file mode 100644 (file)
index 0000000..efc4d8d
Binary files /dev/null and b/web/query/_attachments/images/content.png differ
diff --git a/web/query/_attachments/images/icon.png b/web/query/_attachments/images/icon.png
new file mode 100644 (file)
index 0000000..1d8e04b
Binary files /dev/null and b/web/query/_attachments/images/icon.png differ
diff --git a/web/query/_attachments/images/pl-logo.png b/web/query/_attachments/images/pl-logo.png
new file mode 100644 (file)
index 0000000..bac66ce
Binary files /dev/null and b/web/query/_attachments/images/pl-logo.png differ
diff --git a/web/query/_attachments/images/top.png b/web/query/_attachments/images/top.png
new file mode 100644 (file)
index 0000000..db5af63
Binary files /dev/null and b/web/query/_attachments/images/top.png differ
diff --git a/web/query/_attachments/img/bg.gif b/web/query/_attachments/img/bg.gif
new file mode 100644 (file)
index 0000000..227f991
Binary files /dev/null and b/web/query/_attachments/img/bg.gif differ
diff --git a/web/query/_attachments/img/bgcode.gif b/web/query/_attachments/img/bgcode.gif
new file mode 100644 (file)
index 0000000..6f99682
Binary files /dev/null and b/web/query/_attachments/img/bgcode.gif differ
diff --git a/web/query/_attachments/img/bgfooter.gif b/web/query/_attachments/img/bgfooter.gif
new file mode 100644 (file)
index 0000000..4e383dc
Binary files /dev/null and b/web/query/_attachments/img/bgfooter.gif differ
diff --git a/web/query/_attachments/img/bgmain.gif b/web/query/_attachments/img/bgmain.gif
new file mode 100644 (file)
index 0000000..b7fe91f
Binary files /dev/null and b/web/query/_attachments/img/bgmain.gif differ
diff --git a/web/query/_attachments/img/li.gif b/web/query/_attachments/img/li.gif
new file mode 100644 (file)
index 0000000..4493196
Binary files /dev/null and b/web/query/_attachments/img/li.gif differ
diff --git a/web/query/_attachments/img/nav_li.gif b/web/query/_attachments/img/nav_li.gif
new file mode 100644 (file)
index 0000000..a189dcc
Binary files /dev/null and b/web/query/_attachments/img/nav_li.gif differ
diff --git a/web/query/_attachments/img/quote.gif b/web/query/_attachments/img/quote.gif
new file mode 100644 (file)
index 0000000..fa145bb
Binary files /dev/null and b/web/query/_attachments/img/quote.gif differ
diff --git a/web/query/_attachments/img/topleft.gif b/web/query/_attachments/img/topleft.gif
new file mode 100644 (file)
index 0000000..4d9aad7
Binary files /dev/null and b/web/query/_attachments/img/topleft.gif differ
diff --git a/web/query/_attachments/index.html b/web/query/_attachments/index.html
new file mode 100644 (file)
index 0000000..818d80c
--- /dev/null
@@ -0,0 +1,158 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
+"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html>\r
+\r
+<head>\r
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>\r
+<meta name="description" content="description"/>\r
+<meta name="keywords" content="keywords"/> \r
+<meta name="author" content="author"/> \r
+<!--link rel="stylesheet" type="text/css" href="../../screen.css" media="screen"/-->\r
+<!--link rel="stylesheet" href="../../style/screen.css" type="text/css"/-->\r
+<link rel="stylesheet" href="style/sphinxdoc.css" type="text/css" />\r
+<link rel="stylesheet" href="style/pygments.css" type="text/css" />\r
+         \r
+<title>MyOpsQuery</title>\r
+</head>\r
+\r
+<body>\r
+\r
+       <div class="header" style="background-color: white; text-align: left; padding: 10px 10px 15px 15px">\r
+               <img align='right' src="images/pl-logo.png"/>\r
+               <div class="title">\r
+                       <h1>Welcome to MyOps-Query:</h1>\r
+                       <h4>A simple collection & query interface for MyPLC deployments.</h4>\r
+               </div>\r
+               <div class="related">\r
+                       <ul>\r
+                       <li> <a href="index.html">Home</a> | </li>\r
+                       <!--li> <a href="index.html">About</a> </li-->\r
+                       </ul>\r
+               </div>\r
+       </div>\r
+\r
+               <div class="sphinxsidebar">\r
+                       <div class="sphinxsidebarwrapper">\r
+\r
+                       <h1>Query</h1>\r
+                       <form action="query">\r
+                       <div>\r
+                               <input type="text" name="filter" class="styled" /> \r
+                               <input type="hidden" name="fields" value="date,hostname" /> \r
+                               <input type="submit" value="Run Query" class="button" />\r
+                       </div>\r
+                       </form>\r
+\r
+                       <h1>Collected Attributes</h1>\r
+                               <ul>\r
+                                       <li><a href="/PlanetLabConf/input.cfg">input.cfg</a></li>\r
+                                       <li>type </li>\r
+                                       <li>date </li>\r
+                                       <li>ts </li>\r
+                                       <li>hostname </li>\r
+                                       <li>boot_state </li>\r
+                                       <li>ip_internal </li>\r
+                                       <li>diskspace_root </li>\r
+                                       <li>diskspace_vservers </li>\r
+                                       <li>free_disk_root </li>\r
+                                       <li>free_inodes_root </li>\r
+                                       <li>free_disk_vservers </li>\r
+                                       <li>free_inodes_vservers </li>\r
+                                       <li>fs_status </li>\r
+                                       <li>install_date </li>\r
+                                       <li>iptables_status </li>\r
+                                       <li>kernel_version </li>\r
+                                       <li>netflow </li>\r
+                                       <li>netflow_live </li>\r
+                                       <li>nm_status </li>\r
+                                       <li>plc_config </li>\r
+                                       <li>princeton_comon_dir </li>\r
+                                       <li>princeton_comon_procs </li>\r
+                                       <li>princeton_comon_running </li>\r
+                                       <li>redundant_procs </li>\r
+                                       <li>rpmprocess_count </li>\r
+                                       <li>running_slices </li>\r
+                                       <li>uptime </li>\r
+                                       <li>uptime_idle </li>\r
+                                       <li>boot_server </li>\r
+                                       <li>bootcd_version </li>\r
+                                       <li>rpm_versions </li>\r
+                                       <li>traceroute_from_host </li>\r
+                                       <li>traceroute_to_host </li>\r
+                                       <li>hardware_system </li>\r
+                                       <li>hardware_processor </li>\r
+                                       <li>hardware_memory </li>\r
+                                       <li>hardware_disk </li>\r
+                                       <li>hardware_network </li>\r
+                                       <li>hardware_pcu </li>\r
+                               </ul>\r
+               </div>\r
+               </div>\r
+\r
+    <div class="document">\r
+           <div class="documentwrapper">\r
+                       <div class="bodywrapper">\r
+                               <div class="body">\r
+                       <h1>Purpose</h1>\r
+                               <p>MyOps-Query allows an operator to <em>very easily</em> define and collect new attributes of a running system, and it provides a simple <em>query interface</em> to report and select these observations.  MyOps-Query <em>complements</em> the existing and established mechanisms already running on PlanetLab, such as <a href="http://comon.cs.princeton.edu">CoMon</a> and Func().</p>\r
+                                                                           \r
+                       <h1>Example Queries</h1>\r
+                               <p>The following are example queries that you can perform with MyOps.</p>\r
+                               <p>Many other queries are possible by manipulating the query URL.</p>\r
+\r
+                                 <ul>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname,real_bootcd_version&filter=real_bootcd_version==PlanetLab+BootCD+3.3">filter=real_bootcd_version==PlanetLab BootCD 3.3</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=real_bootcd_version,real_bootcd_kernel_version,hostname&filter=real_bootcd_version=~PlanetLab+BootCD+3%26%26real_bootcd_kernel_version=~2.6.1">filter=real_bootcd_version=~PlanetLab+BootCD+3&amp;&amp;real_bootcd_kernel_version=~2.6.1</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname,free_disk_root&filter=fprobe_size%3E=5000000">filter=fprobe_size&gt;=5000000</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=date,hostname,netflow&filter=netflow==Bad">filter=netflow==Bad</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname,plc_config&filter=plc_config==KO">filter=plc_config==KO</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname,redundant_procs&filter=redundant_procs!=">filter=redundant_procs!=</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname,rpmprocess_count&filter=rpmprocess_count>=1">filter=rpmprocess_count&gt;=1</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname,free_disk_root&filter=free_disk_root<=0.2">filter=free_disk_root&lt;=0.2</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname,free_disk_root&filter=free_disk_root<=0.4%26%26free_disk_root>=0.2">filter=free_disk_root&lt;=0.4&&free_disk_root&gt;=0.2</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname,kernel_version&filter=kernel_version=~2.6.32">filter=kernel_version=~2.6.32</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname&filter=running_slices=~princeton_comon">filter=running_slices=~princeton_comon</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname&filter=rpm_versions=~fprobe-ulog-1.1.2-6.planetlab">filter=rpm_versions=~fprobe-ulog-1.1.2-6.planetlab</a> </li>\r
+                                         <li> <a href="_list/nodelist/node-status?fields=hostname,rpm_versions&rpmpattern=vsys-scripts.*">rpmpattern=vsys-scripts.*</a> </li>\r
+                                         <li> <a href="_list/rpms/rpm-to-host?group=true&startkey=[%22rpm%22]&endkey=[%22rpm%22,{}]">List RPMS per host</a> </li>\r
+                               </ul>\r
+\r
+\r
+                       <h1>Supported Operators</h1>\r
+                                 <ul>\r
+                                       <li><strong>==</strong> String comparison for equivalence.</li>\r
+                                       <li><strong>!=</strong> String comparison for non-equivalence</li>\r
+                                       <li><strong>&lt;=</strong> Given a number, &lt;= converts the parameter into a number and compares it to the specified value. i.e. "root_free_disk&lt;=0.2"</li>\r
+                                       <li><strong>&gt;=</strong> Given a number, &gt;= converts the parameter into a number and compares it to the specified value. i.e. "root_free_disk&gt;=0.2"</li>\r
+                                       <li><strong>=~</strong> Given a regular expression, =~ will return rows when the attribute matches this pattern.</li>\r
+                                       <li><strong>!~</strong> Given a regular expression, !~ will return rows when the attribute does not match this pattern.</li>\r
+                                 </ul>\r
+                       <h1>Supported Conjunctions</h1>\r
+                                 <ul>\r
+                                       <li><strong>&&</strong> Logical 'and' for combining two or more simple operators.  Also, can be entered on the url as %26%26, AND, or %%.</li>\r
+                                       <li><strong>||</strong> Logical 'or'. Also can be entered\r
+                                       on the url as OR.</li>\r
+                             </ul>\r
+                       \r
+                 <h1>Supported Parameters</h1>\r
+                                 <ul>\r
+                                       <li><strong>fields</strong> a comma separated list of attribute names to report.</li>\r
+                                       <li><strong>filter </strong> a simple logic expressions for filtering which values are returned.</li> \r
+                                       <li><strong>within </strong> only return node records that have been updated in less than <strong>within</strong> seconds. (default is 3 hours (10800 sec)) </li> \r
+                                       <li><strong>skip_header </strong> do not print the attribute names as the first line.</li> \r
+                                       <li><strong>rpmpattern </strong> a pattern to match against the rpm_versions attribute. Useful for checking a single package version.</li> \r
+                                 </ul>\r
+               </div>\r
+       </div>\r
+       </div>\r
+       </div>\r
+       \r
+\r
+\r
+\r
+<div class="footer">&copy; 2006 <a href="index.html">Website.com</a>. Valid <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> &amp; <a href="http://validator.w3.org/check?uri=referer">XHTML</a>. Template design by <a href="http://arcsin.se">Arcsin</a>\r
+</div>\r
+\r
+</body>\r
+\r
+</html>\r
diff --git a/web/query/_attachments/index.html.indigo b/web/query/_attachments/index.html.indigo
new file mode 100644 (file)
index 0000000..c1be5d8
--- /dev/null
@@ -0,0 +1,103 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
+"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html>\r
+\r
+<head>\r
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>\r
+<meta name="description" content="description"/>\r
+<meta name="keywords" content="keywords"/> \r
+<meta name="author" content="author"/> \r
+<!--link rel="stylesheet" type="text/css" href="../../screen.css" media="screen"/-->\r
+<!--link rel="stylesheet" href="../../style/screen.css" type="text/css"/-->\r
+<link rel="stylesheet" href="_static/sphinxdoc.css" type="text/css" />\r
+<link rel="stylesheet" href="_static/pygments.css" type="text/css" />\r
+         \r
+<title>MyOpsQuery</title>\r
+</head>\r
+\r
+<body>\r
+\r
+<div class="container">\r
+\r
+       <div class="header">\r
+               \r
+               <div class="title">\r
+                       <h1>Welcome to MyOps-Query:</h1>\r
+                       <h4>A simple collection & query interface for MyPLC deployments.</h4>\r
+               </div>\r
+\r
+               <div class="navigation">\r
+                       <a href="index.html">Home</a>\r
+                       <a href="index.html">About</a>\r
+                       <div class="clearer"><span></span></div>\r
+               </div>\r
+\r
+       </div>\r
+\r
+       <div class="main">\r
+               \r
+               <div class="content">\r
+\r
+                       <h1>Example Queries</h1>\r
+                               <p> This is a list of various queries you can perform with MyOps.</p>\r
+                               <p>Many others can be performed by manipulating the query URL.</p>\r
+\r
+                                 <ul>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=date,hostname,netflow&filter=netflow==Bad">filter=netflow==Bad</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,redundant_procs&filter=redundant_procs!=">filter=redundant_procs!=</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,free_disk_root&filter=free_disk_root<=0.2">filter=free_disk_root&lt;=0.2</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,kernel_version&filter=kernel_version=~2.6.32">filter=kernel_version=~2.6.32</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname&filter=running_slices=~princeton_comon">filter=running_slices=~princeton_comon</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname&filter=rpm_versions=~fprobe-ulog-1.1.2-6.planetlab">filter=rpm_versions=~fprobe-ulog-1.1.2-6.planetlab</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,rpm_versions&rpmpattern=vsys-scripts.*">rpmpattern=vsys-scripts.*</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/_list/rpms/rpm-to-host?group=true&startkey=[%22rpm%22]&endkey=[%22rpm%22,{}]">List RPMS per host</a> </li>\r
+                               </ul>\r
+\r
+\r
+                       <h1>Supported Operators</h1>\r
+                                 <ul>\r
+                                       <li><strong>==</strong> String comparison for equivalence.</li>\r
+                                       <li><strong>!=</strong> String comparison for non-equivalence</li>\r
+                                       <li><strong>&lt;=</strong> Given a number, &lt;= converts the parameter into a number and compares it to the specified value. i.e. "root_free_disk&lt;=0.2"</li>\r
+                                       <li><strong>=~</strong> Given a regular expression, =~ will return rows who's attribute matches this pattern.</li>\r
+                                 </ul>\r
+                       \r
+                 <h1>Supported Parameters</h1>\r
+                                 <ul>\r
+                                       <li><strong>fields</strong> a comma separated list of attribute names to report.</li>\r
+                                       <li><strong>filter </strong> a simple logic expressions for filtering which values are returned.</li> \r
+                                       <li><strong>within </strong> only return node records that have been updated in less than <strong>within</strong> seconds. (default is 1 day (86400)) </li> \r
+                                       <li><strong>skip_header </strong> do not print the attribute names as the first ddne.</li> \r
+                                       <li><strong>rpmpattern </strong> a pattern to match against the rpm_versions attribute. Useful for checking a single package version.</li> \r
+                                 </ul>\r
+               </div>\r
+               <div class="sidenav">\r
+\r
+                       <h1>Search</h1>\r
+                       <form action="query">\r
+                       <div>\r
+                               <input type="text" name="filter" class="styled" /> \r
+                               <input type="hidden" name="fields" value="date,hostname" /> \r
+                               <input type="submit" value="submit" class="button" />\r
+                       </div>\r
+                       </form>\r
+\r
+                       <h1>Collected Attributes</h1>\r
+                               <ul>\r
+                               <li><a href="http://myops.planet-lab.org:5984/files/input.cfg">input.cfg</a></li>\r
+                               </ul>\r
+\r
+               </div>\r
+       \r
+               <div class="clearer"><span></span></div>\r
+\r
+       </div>\r
+\r
+</div>\r
+\r
+<div class="footer">&copy; 2006 <a href="index.html">Website.com</a>. Valid <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> &amp; <a href="http://validator.w3.org/check?uri=referer">XHTML</a>. Template design by <a href="http://arcsin.se">Arcsin</a>\r
+</div>\r
+\r
+</body>\r
+\r
+</html>\r
diff --git a/web/query/_attachments/index.html.mono b/web/query/_attachments/index.html.mono
new file mode 100644 (file)
index 0000000..76f8017
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\r
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+       <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+       <link rel="stylesheet" type="text/css" href="../../style/style.css" />
+    <title>MyOpsQuery</title>
+  </head>
+<body>
+       <div id="wrap">
+       <div id="header">
+               <h1>Welcome to MyOps-Query:</h1>
+               <h2>A simple collection & query interface for MyPLC deployments.</h2>
+       </div>
+       <div id="menu">
+               <ul>
+               <li><a href="#">Home</a></li>
+               <li><a href="#">About</a></li>
+               </ul>
+       </div>
+
+<div id="top"> </div>
+
+<div id="contentwrap"> 
+
+       <div id="content">
+
+               <h2>Examples:</h2>
+               <p> This is a list of various queries you can perform with MyOps.</p>
+               <p>Many others can be performed by manipulating the query URL.</p>
+      <ol id="posts">
+          <li> <a href="http://myops.planet-lab.org:5984/query?fields=date,hostname,netflow&filter=netflow==Bad">filter=netflow==Bad</a> </li>
+          <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,redundant_procs&filter=redundant_procs!=">filter=redundant_procs!=</a> </li>
+          <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,free_disk_root&filter=free_disk_root<=0.2">filter=free_disk_root&lt;=0.2</a> </li>
+          <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,kernel_version&filter=kernel_version=~2.6.32">filter=kernel_version=~2.6.32</a> </li>
+          <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname&filter=running_slices=~princeton_comon">filter=running_slices=~princeton_comon</a> </li>
+          <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname&filter=rpm_versions=~fprobe-ulog-1.1.2-6.planetlab">filter=rpm_versions=~fprobe-ulog-1.1.2-6.planetlab</a> </li>
+          <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,rpm_versions&rpmpattern=vsys-scripts.*">rpmpattern=vsys-scripts.*</a> </li>
+          <li> <a href="http://myops.planet-lab.org:5984/_list/rpms/rpm-to-host?group=true&startkey=[%22rpm%22]&endkey=[%22rpm%22,{}]">List RPMS per host</a> </li>
+    </ol>
+       </div>
+
+       <div id="sidebar">
+  <h3>Supported Operators</h3>
+  <dl>
+       <dt>==<dt> <dd>String comparison for equivalence.</dd>
+       <dt>!=<dt> <dd>String comparison for non-equivalence</dd>
+       <dt>&lt;=<dt> <dd>Given a number, &lt;= converts the parameter into a number and compares it to the specified value. i.e. "root_free_disk&lt;=0.2"</dd>
+       <dt>=~<dt> <dd>Given a regular expression, =~ will return rows who's attribute matches this pattern.</dd>
+  <h3>Supported Parameters</h3>
+  <dl>
+       <dt>fields<dt> <dd>a comma separated list of attribute names to report.</dd>
+       <dt>filter </dt><dd> a simple logic expressions for filtering which values are returned.</dd> 
+       <dt>within </dt><dd> only return node records that have been updated in less than <em>within</em> seconds. (default is 1 day (86400)) </dd> 
+       <dt>skip_header </dt><dd> do not print the attribute names as the first ddne.</dd> 
+       <dt>rpmpattern </dt><dd> a pattern to match against the rpm_versions attribute. Useful for checking a single package version.</dd> 
+  </dl>
+  <h3>Collected Attributes</h3>
+  <a href="http://myops.planet-lab.org:5984/files/input.cfg">input.cfg</a>
+
+</div>
+
+<div style="clear: both;"> </div>
+
+</div>
+
+<div id="bottom"> </div>
+
+<div id="footer">
+<p>&copy; Copyright 2010 You | Template by <a href="http://www.starcraft2revealed.com/">Starcraft 2 Guides</a></p>
+</div>
+
+</div>
+
+</body>
+</html>
diff --git a/web/query/_attachments/index2.html b/web/query/_attachments/index2.html
new file mode 100644 (file)
index 0000000..e0679b3
--- /dev/null
@@ -0,0 +1,97 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
+"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html>\r
+\r
+<head>\r
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>\r
+<meta name="description" content="description"/>\r
+<meta name="keywords" content="keywords"/> \r
+<meta name="author" content="author"/> \r
+<link rel="stylesheet" type="text/css" href="default.css" media="screen"/>\r
+<title>MyOpsQuery</title>\r
+</head>\r
+\r
+<body>\r
+\r
+<div class="container">\r
+\r
+       <div class="header">\r
+               \r
+               <div class="title">\r
+                       <h1>Welcome to MyOps-Query:</h1>\r
+                       <h4>A simple collection & query interface for MyPLC deployments.</h4>\r
+               </div>\r
+\r
+               <div class="navigation">\r
+                       <a href="index.html">Home</a>\r
+                       <a href="index.html">About</a>\r
+                       <div class="clearer"><span></span></div>\r
+               </div>\r
+\r
+       </div>\r
+\r
+       <div class="main">\r
+               \r
+               <div class="content">\r
+\r
+                       <h1>Example Queries</h1>\r
+                               <p> This is a list of various queries you can perform with MyOps.</p>\r
+                               <p>Many others can be performed by manipulating the query URL.</p>\r
+\r
+                                 <ul>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=date,hostname,netflow&filter=netflow==Bad">filter=netflow==Bad</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,redundant_procs&filter=redundant_procs!=">filter=redundant_procs!=</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,free_disk_root&filter=free_disk_root<=0.2">filter=free_disk_root&lt;=0.2</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,kernel_version&filter=kernel_version=~2.6.32">filter=kernel_version=~2.6.32</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname&filter=running_slices=~princeton_comon">filter=running_slices=~princeton_comon</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname&filter=rpm_versions=~fprobe-ulog-1.1.2-6.planetlab">filter=rpm_versions=~fprobe-ulog-1.1.2-6.planetlab</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/query?fields=hostname,rpm_versions&rpmpattern=vsys-scripts.*">rpmpattern=vsys-scripts.*</a> </li>\r
+                                         <li> <a href="http://myops.planet-lab.org:5984/_list/rpms/rpm-to-host?group=true&startkey=[%22rpm%22]&endkey=[%22rpm%22,{}]">List RPMS per host</a> </li>\r
+                               </ul>\r
+\r
+\r
+                       <h1>Supported Operators</h1>\r
+                                 <ul>\r
+                                       <li><strong>==</strong> String comparison for equivalence.</li>\r
+                                       <li><strong>!=</strong> String comparison for non-equivalence</li>\r
+                                       <li><strong>&lt;=</strong> Given a number, &lt;= converts the parameter into a number and compares it to the specified value. i.e. "root_free_disk&lt;=0.2"</li>\r
+                                       <li><strong>=~</strong> Given a regular expression, =~ will return rows who's attribute matches this pattern.</li>\r
+                                 </ul>\r
+                       \r
+                 <h1>Supported Parameters</h1>\r
+                                 <ul>\r
+                                       <li><strong>fields</strong> a comma separated list of attribute names to report.</li>\r
+                                       <li><strong>filter </strong> a simple logic expressions for filtering which values are returned.</li> \r
+                                       <li><strong>within </strong> only return node records that have been updated in less than <strong>within</strong> seconds. (default is 1 day (86400)) </li> \r
+                                       <li><strong>skip_header </strong> do not print the attribute names as the first ddne.</li> \r
+                                       <li><strong>rpmpattern </strong> a pattern to match against the rpm_versions attribute. Useful for checking a single package version.</li> \r
+                                 </ul>\r
+               </div>\r
+               <div class="sidenav">\r
+\r
+                       <h1>Search</h1>\r
+                       <form action="index.html">\r
+                       <div>\r
+                               <input type="text" name="search" class="styled" /> <input type="submit" value="submit" class="button" />\r
+                       </div>\r
+                       </form>\r
+\r
+                       <h1>Collected Attributes</h1>\r
+                               <ul>\r
+                               <li><a href="http://myops.planet-lab.org:5984/files/input.cfg">input.cfg</a></li>\r
+                               </ul>\r
+\r
+               </div>\r
+       \r
+               <div class="clearer"><span></span></div>\r
+\r
+       </div>\r
+\r
+</div>\r
+\r
+<div class="footer">&copy; 2006 <a href="index.html">Website.com</a>. Valid <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> &amp; <a href="http://validator.w3.org/check?uri=referer">XHTML</a>. Template design by <a href="http://arcsin.se">Arcsin</a>\r
+</div>\r
+\r
+</body>\r
+\r
+</html>\r
diff --git a/web/query/_attachments/indigo.html b/web/query/_attachments/indigo.html
new file mode 100644 (file)
index 0000000..e50c79e
--- /dev/null
@@ -0,0 +1,118 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
+"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html>\r
+\r
+<head>\r
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>\r
+<meta name="description" content="description"/>\r
+<meta name="keywords" content="keywords"/> \r
+<meta name="author" content="author"/> \r
+<link rel="stylesheet" type="text/css" href="default.css" media="screen"/>\r
+<title>Indigo</title>\r
+</head>\r
+\r
+<body>\r
+\r
+<div class="container">\r
+\r
+       <div class="header">\r
+               \r
+               <div class="title">\r
+                       <h1>Indigo template</h1>\r
+               </div>\r
+\r
+               <div class="navigation">\r
+                       <a href="index.html">Vestibulum</a>\r
+                       <a href="index.html">Suspendisse</a>\r
+                       <a href="index.html">Elemen</a>\r
+                       <a href="index.html">Maecenas</a>\r
+                       <a href="index.html">Sodales</a>\r
+                       <div class="clearer"><span></span></div>\r
+               </div>\r
+\r
+       </div>\r
+\r
+       <div class="main">\r
+               \r
+               <div class="content">\r
+\r
+                       <h1>Porttitor posuere</h1>\r
+                       <div class="descr">Jun 13, 2006 by Vulputate</div>\r
+\r
+                       <p>In hac habitasse platea dictumst. Duis porttitor. Sed vulputate elementum nisl. Vivamus et mi at arcu mattis iaculis. Nullam posuere tristique tortor. In bibendum. Aenean ornare, <a href="index.html">nunc eget pretium</a> porttitor, sem est pretium leo, non euismod nulla dui non diam. Pellentesque dictum faucibus leo. Vestibulum ac ante. Sed in est.</p>\r
+\r
+                       <cite>Sed sodales nisl sit amet augue. Donec ultrices, augue ullamcorper posuere laoreet, turpis massa tristique justo, sed egestas metus magna sed purus.</cite>\r
+                       \r
+                       <p>Aliquam risus justo, mollis in, laoreet a, consectetuer nec, risus. Nunc blandit sodales lacus. Nam luctus semper mi. In eu diam.</p>\r
+\r
+                       <p>Fusce porta pede nec eros. Maecenas ipsum sem, interdum non, aliquam vitae, interdum nec, metus. Maecenas ornare lobortis risus. Etiam placerat varius mauris. Maecenas viverra. Sed feugiat. Donec mattis <a href="index.html">quam aliquam</a> risus. Nulla non felis sollicitudin urna blandit egestas. Integer et libero varius pede tristique ultricies. Cras nisl. Proin quis massa semper felis euismod ultricies.\r
+                       </p>\r
+\r
+                       <h1>Adipiscing</h1>\r
+                       <div class="descr">Jun 11, 2006 by Laoreet</div>\r
+\r
+                       <p>Aliquam risus justo, mollis in, laoreet a, consectetuer nec, risus. Nunc blandit sodales lacus. Nam luctus semper mi. In eu diam. Phasellus rutrum elit vel nisi. Cras mauris nulla, egestas quis, cursus at, venenatis ac, ante. Fusce accumsan enim et arcu. Duis sagittis libero at lacus. Suspendisse lacinia nulla eget urna.</p>\r
+                       <ul>\r
+                               <li>Tristique</li>\r
+                               <li>Aenean</li>\r
+                               <li>Pretium</li>\r
+                       </ul>\r
+                       <p>In hac habitasse platea dictumst. Duis porttitor. Sed vulputate elementum nisl. Vivamus et mi at arcu mattis iaculis. Nullam posuere tristique tortor. In bibendum. Aenean ornare, nunc eget pretium porttitor, sem est pretium leo, non euismod nulla dui non diam. Pellentesque dictum faucibus leo. Vestibulum ac ante. Sed in est. Sed sodales nisl sit amet augue. Donec ultrices, augue ullamcorper posuere laoreet, turpis massa tristique justo, sed egestas metus magna sed purus. Fusce eleifend, dui ut posuere auctor, justo elit posuere sapien, at blandit enim quam fringilla mi.</p>\r
+\r
+                       <h1>Interdum</h1>\r
+                       <div class="descr">May 24, 2006 by Lectus</div>\r
+\r
+                       <p>Praesent nisi sem, bibendum in, ultrices sit amet, euismod sit amet, dui. Donec varius tincidunt nisi. Ut ut sapien. Integer porta. Fusce nibh. Curabitur pellentesque, lectus at <a href="index.html">volutpat interdum</a>, sem justo placerat elit, eget feugiat est leo tempor quam. Ut quis neque convallis magna consequat molestie. Nullam semper massa eget ligula. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque a nibh quis nunc volutpat aliquam</p>\r
+                       <code>margin-bottom: 12px;\r
+font: normal 1.1em "Lucida Sans Unicode",serif;\r
+background: url(img/quote.gif) no-repeat;\r
+padding-left: 28px;\r
+color: #555;</code>\r
+                       <p>Eget feugiat est leo tempor quam. Ut quis neque convallis magna consequat molestie.</p>\r
+\r
+               </div>\r
+\r
+               <div class="sidenav">\r
+\r
+                       <h1>Search</h1>\r
+                       <form action="index.html">\r
+                       <div>\r
+                               <input type="text" name="search" class="styled" /> <input type="submit" value="submit" class="button" />\r
+                       </div>\r
+                       </form>\r
+\r
+                       <h1>Something</h1>\r
+                       <ul>\r
+                               <li><a href="index.html">pellentesque</a></li>\r
+                               <li><a href="index.html">sociis natoque</a></li>\r
+                               <li><a href="index.html">convallis</a></li>\r
+                       </ul>\r
+\r
+                       <h1>Another thing</h1>\r
+                       <ul>\r
+                               <li><a href="index.html">consequat molestie</a></li>\r
+                               <li><a href="index.html">sem justo</a></li>\r
+                               <li><a href="index.html">semper</a></li>\r
+                       </ul>\r
+\r
+                       <h1>Third and last</h1>\r
+                       <ul>\r
+                               <li><a href="index.html">sociis natoque</a></li>\r
+                               <li><a href="index.html">magna sed purus</a></li>\r
+                               <li><a href="index.html">tincidunt</a></li>\r
+                       </ul>\r
+\r
+               </div>\r
+       \r
+               <div class="clearer"><span></span></div>\r
+\r
+       </div>\r
+\r
+</div>\r
+\r
+<div class="footer">&copy; 2006 <a href="index.html">Website.com</a>. Valid <a href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> &amp; <a href="http://validator.w3.org/check?uri=referer">XHTML</a>. Template design by <a href="http://arcsin.se">Arcsin</a>\r
+</div>\r
+\r
+</body>\r
+\r
+</html>\r
diff --git a/web/query/_attachments/script/app.js b/web/query/_attachments/script/app.js
new file mode 100644 (file)
index 0000000..d4780ee
--- /dev/null
@@ -0,0 +1,7 @@
+$.couch.app(function(app) {
+  $('.date').prettyDate();
+
+  $("#account").evently($.extend(true, 
+    app.ddoc.vendor.couchapp.evently.account, 
+    app.ddoc.evently.account), app);
+});
\ No newline at end of file
diff --git a/web/query/_attachments/script/jquery.scrollTo.js b/web/query/_attachments/script/jquery.scrollTo.js
new file mode 100644 (file)
index 0000000..453382d
--- /dev/null
@@ -0,0 +1,11 @@
+/**\r
+ * jQuery.ScrollTo - Easy element scrolling using jQuery.\r
+ * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com\r
+ * Dual licensed under MIT and GPL.\r
+ * Date: 9/11/2008\r
+ * @author Ariel Flesler\r
+ * @version 1.4\r
+ *\r
+ * http://flesler.blogspot.com/2007/10/jqueryscrollto.html\r
+ */\r
+;(function(h){var m=h.scrollTo=function(b,c,g){h(window).scrollTo(b,c,g)};m.defaults={axis:'y',duration:1};m.window=function(b){return h(window).scrollable()};h.fn.scrollable=function(){return this.map(function(){var b=this.parentWindow||this.defaultView,c=this.nodeName=='#document'?b.frameElement||b:this,g=c.contentDocument||(c.contentWindow||c).document,i=c.setInterval;return c.nodeName=='IFRAME'||i&&h.browser.safari?g.body:i?g.documentElement:this})};h.fn.scrollTo=function(r,j,a){if(typeof j=='object'){a=j;j=0}if(typeof a=='function')a={onAfter:a};a=h.extend({},m.defaults,a);j=j||a.speed||a.duration;a.queue=a.queue&&a.axis.length>1;if(a.queue)j/=2;a.offset=n(a.offset);a.over=n(a.over);return this.scrollable().each(function(){var k=this,o=h(k),d=r,l,e={},p=o.is('html,body');switch(typeof d){case'number':case'string':if(/^([+-]=)?\d+(px)?$/.test(d)){d=n(d);break}d=h(d,this);case'object':if(d.is||d.style)l=(d=h(d)).offset()}h.each(a.axis.split(''),function(b,c){var g=c=='x'?'Left':'Top',i=g.toLowerCase(),f='scroll'+g,s=k[f],t=c=='x'?'Width':'Height',v=t.toLowerCase();if(l){e[f]=l[i]+(p?0:s-o.offset()[i]);if(a.margin){e[f]-=parseInt(d.css('margin'+g))||0;e[f]-=parseInt(d.css('border'+g+'Width'))||0}e[f]+=a.offset[i]||0;if(a.over[i])e[f]+=d[v]()*a.over[i]}else e[f]=d[i];if(/^\d+$/.test(e[f]))e[f]=e[f]<=0?0:Math.min(e[f],u(t));if(!b&&a.queue){if(s!=e[f])q(a.onAfterFirst);delete e[f]}});q(a.onAfter);function q(b){o.animate(e,j,a.easing,b&&function(){b.call(this,r,a)})};function u(b){var c='scroll'+b,g=k.ownerDocument;return p?Math.max(g.documentElement[c],g.body[c]):k[c]}}).end()};function n(b){return typeof b=='object'?b:{top:b,left:b}}})(jQuery);
\ No newline at end of file
diff --git a/web/query/_attachments/script/md5.js b/web/query/_attachments/script/md5.js
new file mode 100644 (file)
index 0000000..46d2aab
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
+var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
+var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+function core_md5(x, len)
+{
+  /* append padding */
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i < x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
+    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
+    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
+    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
+    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+
+    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
+    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
+    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
+    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
+    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
+    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
+    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
+    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
+    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
+    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
+    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
+    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
+    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
+    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
+    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data)
+{
+  var bkey = str2binl(key);
+  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i < 16; i++)
+  {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+  return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ */
+function str2binl(str)
+{
+  var bin = Array();
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < str.length * chrsz; i += chrsz)
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+  return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+  var str = "";
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < bin.length * 32; i += chrsz)
+    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+function binl2hex(binarray)
+{
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
+  }
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i += 3)
+  {
+    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
+                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+    for(var j = 0; j < 4; j++)
+    {
+      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+    }
+  }
+  return str;
+}
diff --git a/web/query/_attachments/style/.screen.css.swp b/web/query/_attachments/style/.screen.css.swp
new file mode 100644 (file)
index 0000000..d684b2f
Binary files /dev/null and b/web/query/_attachments/style/.screen.css.swp differ
diff --git a/web/query/_attachments/style/basic.css b/web/query/_attachments/style/basic.css
new file mode 100644 (file)
index 0000000..32630d5
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * basic.css
+ * ~~~~~~~~~
+ *
+ * Sphinx stylesheet -- basic theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/* -- main layout ----------------------------------------------------------- */
+
+div.clearer {
+    clear: both;
+}
+
+/* -- relbar ---------------------------------------------------------------- */
+
+div.related {
+    width: 100%;
+    font-size: 90%;
+}
+
+div.related h3 {
+    display: none;
+}
+
+div.related ul {
+    margin: 0;
+    padding: 0 0 0 10px;
+    list-style: none;
+}
+
+div.related li {
+    display: inline;
+}
+
+div.related li.right {
+    float: right;
+    margin-right: 5px;
+}
+
+/* -- sidebar --------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+    padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+    float: left;
+    width: 230px;
+    margin-left: -100%;
+    font-size: 90%;
+}
+
+div.sphinxsidebar ul {
+    list-style: none;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+    margin-left: 20px;
+    list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+    margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+    border: 1px solid #98dbcc;
+    font-family: sans-serif;
+    font-size: 1em;
+}
+
+img {
+    border: 0;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+    margin: 10px 0 0 20px;
+    padding: 0;
+}
+
+ul.search li {
+    padding: 5px 0 5px 20px;
+    background-image: url(file.png);
+    background-repeat: no-repeat;
+    background-position: 0 7px;
+}
+
+ul.search li a {
+    font-weight: bold;
+}
+
+ul.search li div.context {
+    color: #888;
+    margin: 2px 0 0 30px;
+    text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+    font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+    width: 90%;
+}
+
+table.contentstable p.biglink {
+    line-height: 150%;
+}
+
+a.biglink {
+    font-size: 1.3em;
+}
+
+span.linkdescr {
+    font-style: italic;
+    padding-top: 5px;
+    font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable {
+    width: 100%;
+}
+
+table.indextable td {
+    text-align: left;
+    vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+    height: 10px;
+}
+
+table.indextable tr.cap {
+    margin-top: 10px;
+    background-color: #f2f2f2;
+}
+
+img.toggler {
+    margin-right: 3px;
+    margin-top: 3px;
+    cursor: pointer;
+}
+
+div.modindex-jumpbox {
+    border-top: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+    margin: 1em 0 1em 0;
+    padding: 0.4em;
+}
+
+div.genindex-jumpbox {
+    border-top: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+    margin: 1em 0 1em 0;
+    padding: 0.4em;
+}
+
+/* -- general body styles --------------------------------------------------- */
+
+a.headerlink {
+    visibility: hidden;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+    visibility: visible;
+}
+
+div.body p.caption {
+    text-align: inherit;
+}
+
+div.body td {
+    text-align: left;
+}
+
+.field-list ul {
+    padding-left: 1em;
+}
+
+.first {
+    margin-top: 0 !important;
+}
+
+p.rubric {
+    margin-top: 30px;
+    font-weight: bold;
+}
+
+img.align-left, .figure.align-left, object.align-left {
+    clear: left;
+    float: left;
+    margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+    clear: right;
+    float: right;
+    margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.align-left {
+    text-align: left;
+}
+
+.align-center {
+    clear: both;
+    text-align: center;
+}
+
+.align-right {
+    text-align: right;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar {
+    margin: 0 0 0.5em 1em;
+    border: 1px solid #ddb;
+    padding: 7px 7px 0 7px;
+    background-color: #ffe;
+    width: 40%;
+    float: right;
+}
+
+p.sidebar-title {
+    font-weight: bold;
+}
+
+/* -- topics ---------------------------------------------------------------- */
+
+div.topic {
+    border: 1px solid #ccc;
+    padding: 7px 7px 0 7px;
+    margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+    font-size: 1.1em;
+    font-weight: bold;
+    margin-top: 10px;
+}
+
+/* -- admonitions ----------------------------------------------------------- */
+
+div.admonition {
+    margin-top: 10px;
+    margin-bottom: 10px;
+    padding: 7px;
+}
+
+div.admonition dt {
+    font-weight: bold;
+}
+
+div.admonition dl {
+    margin-bottom: 0;
+}
+
+p.admonition-title {
+    margin: 0px 10px 5px 0px;
+    font-weight: bold;
+}
+
+div.body p.centered {
+    text-align: center;
+    margin-top: 25px;
+}
+
+/* -- tables ---------------------------------------------------------------- */
+
+table.docutils {
+    border: 0;
+    border-collapse: collapse;
+}
+
+table.docutils td, table.docutils th {
+    padding: 1px 8px 1px 5px;
+    border-top: 0;
+    border-left: 0;
+    border-right: 0;
+    border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+    border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+    border: 0 !important;
+}
+
+th {
+    text-align: left;
+    padding-right: 5px;
+}
+
+table.citation {
+    border-left: solid 1px gray;
+    margin-left: 1px;
+}
+
+table.citation td {
+    border-bottom: none;
+}
+
+/* -- other body styles ----------------------------------------------------- */
+
+ol.arabic {
+    list-style: decimal;
+}
+
+ol.loweralpha {
+    list-style: lower-alpha;
+}
+
+ol.upperalpha {
+    list-style: upper-alpha;
+}
+
+ol.lowerroman {
+    list-style: lower-roman;
+}
+
+ol.upperroman {
+    list-style: upper-roman;
+}
+
+dl {
+    margin-bottom: 15px;
+}
+
+dd p {
+    margin-top: 0px;
+}
+
+dd ul, dd table {
+    margin-bottom: 10px;
+}
+
+dd {
+    margin-top: 3px;
+    margin-bottom: 10px;
+    margin-left: 30px;
+}
+
+dt:target, .highlighted {
+    background-color: #fbe54e;
+}
+
+dl.glossary dt {
+    font-weight: bold;
+    font-size: 1.1em;
+}
+
+.field-list ul {
+    margin: 0;
+    padding-left: 1em;
+}
+
+.field-list p {
+    margin: 0;
+}
+
+.refcount {
+    color: #060;
+}
+
+.optional {
+    font-size: 1.3em;
+}
+
+.versionmodified {
+    font-style: italic;
+}
+
+.system-message {
+    background-color: #fda;
+    padding: 5px;
+    border: 3px solid red;
+}
+
+.footnote:target  {
+    background-color: #ffa;
+}
+
+.line-block {
+    display: block;
+    margin-top: 1em;
+    margin-bottom: 1em;
+}
+
+.line-block .line-block {
+    margin-top: 0;
+    margin-bottom: 0;
+    margin-left: 1.5em;
+}
+
+.guilabel, .menuselection {
+    font-family: sans-serif;
+}
+
+.accelerator {
+    text-decoration: underline;
+}
+
+.classifier {
+    font-style: oblique;
+}
+
+/* -- code displays --------------------------------------------------------- */
+
+pre {
+    overflow: auto;
+    overflow-y: hidden;  /* fixes display issues on Chrome browsers */
+}
+
+td.linenos pre {
+    padding: 5px 0px;
+    border: 0;
+    background-color: transparent;
+    color: #aaa;
+}
+
+table.highlighttable {
+    margin-left: 0.5em;
+}
+
+table.highlighttable td {
+    padding: 0 0.5em 0 0.5em;
+}
+
+tt.descname {
+    background-color: transparent;
+    font-weight: bold;
+    font-size: 1.2em;
+}
+
+tt.descclassname {
+    background-color: transparent;
+}
+
+tt.xref, a tt {
+    background-color: transparent;
+    font-weight: bold;
+}
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+    background-color: transparent;
+}
+
+.viewcode-link {
+    float: right;
+}
+
+.viewcode-back {
+    float: right;
+    font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+    margin: -1px -10px;
+    padding: 0 10px;
+}
+
+/* -- math display ---------------------------------------------------------- */
+
+img.math {
+    vertical-align: middle;
+}
+
+div.body div.math p {
+    text-align: center;
+}
+
+span.eqno {
+    float: right;
+}
+
+/* -- printout stylesheet --------------------------------------------------- */
+
+@media print {
+    div.document,
+    div.documentwrapper,
+    div.bodywrapper {
+        margin: 0 !important;
+        width: 100%;
+    }
+
+    div.sphinxsidebar,
+    div.related,
+    div.footer,
+    #top-link {
+        display: none;
+    }
+}
diff --git a/web/query/_attachments/style/default.css b/web/query/_attachments/style/default.css
new file mode 100644 (file)
index 0000000..90c89b2
--- /dev/null
@@ -0,0 +1,187 @@
+/*#############################################################\r
+Name: Indigo\r
+Description: A lightweight (7kb images), simple and professional design.\r
+Date: 2006-07-27\r
+Author: Viktor Persson\r
+URL: http://arcsin.se\r
+\r
+Feel free to use and modify but please provide credits.\r
+#############################################################*/\r
+\r
+/* standard elements */\r
+* {\r
+       margin: 0;\r
+       padding: 0;\r
+}\r
+a {\r
+       color: #F70;\r
+}\r
+a:hover {\r
+       color: #C60;\r
+}\r
+body {\r
+       background: #0094D6 url(img/bg.gif) repeat-x;\r
+       color: #466;\r
+       font: normal 62.5% "Lucida Sans Unicode",sans-serif;\r
+       margin: 0;\r
+}\r
+input {\r
+       color: #555;\r
+       font: normal 1.1em "Lucida Sans Unicode",sans-serif;\r
+}\r
+p,cite,code,ul {\r
+       font-size: 1.2em;\r
+       padding-bottom: 1.2em;\r
+}\r
+h1 {\r
+       font-size: 1.4em;\r
+       margin-bottom: 4px;\r
+}\r
+code {\r
+       background: url(img/bgcode.gif);\r
+       border: 1px solid #F0F0F0;\r
+       border-left: 6px solid #39F;\r
+       color: #555;\r
+       display: block;\r
+       font: normal 1.1em "Lucida Sans Unicode",serif;\r
+       margin-bottom: 12px;\r
+       padding: 8px 10px;\r
+       white-space: pre;\r
+}\r
+cite {\r
+       background: url(img/quote.gif) no-repeat;\r
+       color: #666;\r
+       display: block;\r
+       font: normal 1.3em "Lucida Sans Unicode",serif;\r
+       padding-left: 28px;\r
+}\r
+h1,h2,h3 {\r
+       color: #06C;\r
+       padding-top: 6px;\r
+}\r
+/* misc */\r
+.clearer {\r
+       clear: both;\r
+}\r
+\r
+/* structure */\r
+.container {\r
+       background: url(img/topleft.gif) no-repeat;\r
+}\r
+\r
+.header {\r
+       height: 92px;\r
+}\r
+\r
+/* title */\r
+.title {\r
+       float: left;\r
+       padding: 28px 0 0 76px;\r
+}\r
+.title h1 {\r
+       color: #FFF;\r
+       font: normal 2em Verdana,sans-serif;\r
+}\r
+/* navigation */\r
+.navigation {\r
+       float: left;\r
+       height: 92px;\r
+       margin-left: 24px;\r
+       padding: 0 16px;\r
+}\r
+.navigation a {\r
+       color: #FFF;\r
+       float: left;\r
+       font: bold 1.2em "Trebuchet MS",sans-serif;\r
+       margin-top: 56px;\r
+       padding: 8px 18px;\r
+       text-align: center;\r
+       text-decoration: none;\r
+}\r
+.navigation a:hover {\r
+       background-color: #4A91C3;\r
+       color: #FFF;\r
+}\r
+\r
+/* main */\r
+.main {\r
+       background: #FFF url(img/bgmain.gif) no-repeat;\r
+       clear: both;\r
+       padding: 12px 12px 0 52px;\r
+}\r
+\r
+/* main left */\r
+.sidenav {\r
+       float: left;\r
+       width: 24%;\r
+}\r
+.sidenav h1 {\r
+       border-bottom: 1px dashed #DDD;\r
+       color: #E73;\r
+       font-size: 1.2em;\r
+       height: 20px;\r
+       margin-top: 1.2em;\r
+}\r
+.sidenav ul {\r
+       margin: 0;\r
+       padding: 0;\r
+}\r
+.sidenav li { \r
+       border-bottom: 1px dashed #EEE;\r
+       list-style: none;\r
+       margin: 0;\r
+}\r
+.sidenav li a {\r
+       color: #777;\r
+       display: block;\r
+       font-size: 0.9em;\r
+       padding: 3px 6px 3px 18px;\r
+       text-decoration: none;\r
+}\r
+.sidenav li a:hover {\r
+       color: #111;\r
+       background: url(img/nav_li.gif) no-repeat;\r
+}\r
+\r
+/* content */\r
+.content {\r
+       float: left;\r
+       margin-right: 4%;\r
+       width: 69%;\r
+}\r
+.content .descr {\r
+       color: #C60;\r
+       margin-bottom: 6px;\r
+}\r
+.content li {\r
+       list-style: url(img/li.gif);\r
+       margin-left: 18px;\r
+}\r
+\r
+/* search form */\r
+.styled {\r
+       border: 3px double #E5E5E5;\r
+       padding: 2px 3px;\r
+}\r
+.button {\r
+       border: 1px solid #AAA;\r
+       margin-left: 5px;\r
+       padding: 2px 3px;\r
+}\r
+\r
+/* footer */\r
+.footer {\r
+       background: #0094D6 url(img/bgfooter.gif) repeat-x;\r
+       color: #C1DEF0;\r
+       font-size: 1.1em;\r
+       line-height: 40px;\r
+       text-align: center;\r
+}\r
+.footer a {\r
+       color: #FFF;\r
+       text-decoration: none;\r
+}\r
+.footer a:hover {\r
+       color: #FFF;\r
+       text-decoration: underline;\r
+}
\ No newline at end of file
diff --git a/web/query/_attachments/style/images/bg.jpg b/web/query/_attachments/style/images/bg.jpg
new file mode 100644 (file)
index 0000000..d8c2b34
Binary files /dev/null and b/web/query/_attachments/style/images/bg.jpg differ
diff --git a/web/query/_attachments/style/images/bottom.png b/web/query/_attachments/style/images/bottom.png
new file mode 100644 (file)
index 0000000..a594b8d
Binary files /dev/null and b/web/query/_attachments/style/images/bottom.png differ
diff --git a/web/query/_attachments/style/images/content.png b/web/query/_attachments/style/images/content.png
new file mode 100644 (file)
index 0000000..efc4d8d
Binary files /dev/null and b/web/query/_attachments/style/images/content.png differ
diff --git a/web/query/_attachments/style/images/icon.png b/web/query/_attachments/style/images/icon.png
new file mode 100644 (file)
index 0000000..1d8e04b
Binary files /dev/null and b/web/query/_attachments/style/images/icon.png differ
diff --git a/web/query/_attachments/style/images/top.png b/web/query/_attachments/style/images/top.png
new file mode 100644 (file)
index 0000000..db5af63
Binary files /dev/null and b/web/query/_attachments/style/images/top.png differ
diff --git a/web/query/_attachments/style/img/bg.gif b/web/query/_attachments/style/img/bg.gif
new file mode 100644 (file)
index 0000000..227f991
Binary files /dev/null and b/web/query/_attachments/style/img/bg.gif differ
diff --git a/web/query/_attachments/style/img/bgcode.gif b/web/query/_attachments/style/img/bgcode.gif
new file mode 100644 (file)
index 0000000..6f99682
Binary files /dev/null and b/web/query/_attachments/style/img/bgcode.gif differ
diff --git a/web/query/_attachments/style/img/bgfooter.gif b/web/query/_attachments/style/img/bgfooter.gif
new file mode 100644 (file)
index 0000000..4e383dc
Binary files /dev/null and b/web/query/_attachments/style/img/bgfooter.gif differ
diff --git a/web/query/_attachments/style/img/bgmain.gif b/web/query/_attachments/style/img/bgmain.gif
new file mode 100644 (file)
index 0000000..b7fe91f
Binary files /dev/null and b/web/query/_attachments/style/img/bgmain.gif differ
diff --git a/web/query/_attachments/style/img/li.gif b/web/query/_attachments/style/img/li.gif
new file mode 100644 (file)
index 0000000..4493196
Binary files /dev/null and b/web/query/_attachments/style/img/li.gif differ
diff --git a/web/query/_attachments/style/img/nav_li.gif b/web/query/_attachments/style/img/nav_li.gif
new file mode 100644 (file)
index 0000000..a189dcc
Binary files /dev/null and b/web/query/_attachments/style/img/nav_li.gif differ
diff --git a/web/query/_attachments/style/img/quote.gif b/web/query/_attachments/style/img/quote.gif
new file mode 100644 (file)
index 0000000..fa145bb
Binary files /dev/null and b/web/query/_attachments/style/img/quote.gif differ
diff --git a/web/query/_attachments/style/img/topleft.gif b/web/query/_attachments/style/img/topleft.gif
new file mode 100644 (file)
index 0000000..4d9aad7
Binary files /dev/null and b/web/query/_attachments/style/img/topleft.gif differ
diff --git a/web/query/_attachments/style/pygments.css b/web/query/_attachments/style/pygments.css
new file mode 100644 (file)
index 0000000..a55a324
--- /dev/null
@@ -0,0 +1,59 @@
+.c { color: #408090; font-style: italic } /* Comment */
+.err { border: 1px solid #FF0000 } /* Error */
+.k { color: #007020; font-weight: bold } /* Keyword */
+.o { color: #666666 } /* Operator */
+.cm { color: #408090; font-style: italic } /* Comment.Multiline */
+.cp { color: #007020 } /* Comment.Preproc */
+.c1 { color: #408090; font-style: italic } /* Comment.Single */
+.cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
+.gd { color: #A00000 } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #FF0000 } /* Generic.Error */
+.gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.gi { color: #00A000 } /* Generic.Inserted */
+.go { color: #303030 } /* Generic.Output */
+.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.gt { color: #0040D0 } /* Generic.Traceback */
+.kc { color: #007020; font-weight: bold } /* Keyword.Constant */
+.kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
+.kp { color: #007020 } /* Keyword.Pseudo */
+.kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
+.kt { color: #902000 } /* Keyword.Type */
+.m { color: #208050 } /* Literal.Number */
+.s { color: #4070a0 } /* Literal.String */
+.na { color: #4070a0 } /* Name.Attribute */
+.nb { color: #007020 } /* Name.Builtin */
+.nc { color: #0e84b5; font-weight: bold } /* Name.Class */
+.no { color: #60add5 } /* Name.Constant */
+.nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.ni { color: #d55537; font-weight: bold } /* Name.Entity */
+.ne { color: #007020 } /* Name.Exception */
+.nf { color: #06287e } /* Name.Function */
+.nl { color: #002070; font-weight: bold } /* Name.Label */
+.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.nt { color: #062873; font-weight: bold } /* Name.Tag */
+.nv { color: #bb60d5 } /* Name.Variable */
+.ow { color: #007020; font-weight: bold } /* Operator.Word */
+.w { color: #bbbbbb } /* Text.Whitespace */
+.mf { color: #208050 } /* Literal.Number.Float */
+.mh { color: #208050 } /* Literal.Number.Hex */
+.mi { color: #208050 } /* Literal.Number.Integer */
+.mo { color: #208050 } /* Literal.Number.Oct */
+.sb { color: #4070a0 } /* Literal.String.Backtick */
+.sc { color: #4070a0 } /* Literal.String.Char */
+.sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
+.s2 { color: #4070a0 } /* Literal.String.Double */
+.se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
+.sh { color: #4070a0 } /* Literal.String.Heredoc */
+.si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
+.sx { color: #c65d09 } /* Literal.String.Other */
+.sr { color: #235388 } /* Literal.String.Regex */
+.s1 { color: #4070a0 } /* Literal.String.Single */
+.ss { color: #517918 } /* Literal.String.Symbol */
+.bp { color: #007020 } /* Name.Builtin.Pseudo */
+.vc { color: #bb60d5 } /* Name.Variable.Class */
+.vg { color: #bb60d5 } /* Name.Variable.Global */
+.vi { color: #bb60d5 } /* Name.Variable.Instance */
+.il { color: #208050 } /* Literal.Number.Integer.Long */
\ No newline at end of file
diff --git a/web/query/_attachments/style/screen.css b/web/query/_attachments/style/screen.css
new file mode 100644 (file)
index 0000000..f57a81b
--- /dev/null
@@ -0,0 +1,198 @@
+body {
+  font: 1.0em Arial, serif;
+  line-height:1.2em;
+  margin:0;
+  padding:0;
+  
+}
+
+#profile {
+/*  margin:2em;*/
+/*  padding:.5em;*/
+}
+
+#account {
+  font-size:0.8em;
+  text-shadow: #fff 0 0 4px;
+  float:right;
+}
+
+#feeds {
+  font-size:0.6em;
+  text-shadow: #fff 0 0 4px;
+}
+
+a {
+  text-decoration:none;
+}
+
+h1 {
+  padding:6px;
+  margin:0;
+  line-height:1.2em;
+}
+
+h2 {
+  padding:0;
+  margin:0;
+  line-height:1em;
+  font-size:112%;
+}
+
+#header h2 {
+  text-shadow: 0 0 4px #000;    
+}
+
+#header h2 a {
+  color:#fff;
+}
+
+#header #login, #header #edit {
+  float:right;
+}
+
+h3 {
+  margin-bottom:0;
+  margin-top:4px;
+}
+
+h4 {
+  margin:4px;
+}
+
+#header {
+  background:#9a9;
+  padding:8px;
+  border-bottom: 1em solid #b9dab9;
+}
+
+#content {
+  width:750px;
+  padding:10px;
+  background:#f9f9f9;
+/*  float:left;*/
+  margin-top:0;
+  margin-left:240px;
+}
+
+#tagcloud {
+  float:left;
+  width:220px;
+  margin:2ex 1em;
+}
+
+.tags a, #tagcloud a {
+  color:#bbb;
+}
+.tags a:hover, #tagcloud a:hover {
+  color:#9b9;
+}
+
+.tags, .by {
+  font-size:80%;
+  color:#666;
+  margin-top:0;
+}
+
+
+label {
+  display:block;
+}
+
+#post .body {
+  padding:5px 15px;
+}
+
+#posts .body {
+  padding:5px 10px;
+}
+.body img {
+  border:2px solid #fff;
+  margin-left:133px;
+}
+
+#posts li {
+  margin-bottom:16px;
+}
+
+p.post {
+  font-family: serif;
+  margin:10px;
+}
+
+#comments {
+  font-size:80%;
+  background:#fdfdfd;
+  padding:12px;
+  margin:8px;
+  border:4px #fff;
+}
+
+#comments ul {
+  padding:0;
+}
+
+#comments li {
+  border:4px solid #ebebeb;
+  background:#fbfbfb;
+  margin:20px 0;
+  padding:3px 3px 3px 3px;
+}
+
+#comments h4 {
+  background:#ebebeb;
+  padding:1px 0 0 5px;
+}
+
+img.gravatar {
+  width:40px;
+  height:40px;
+  float:left;
+  margin:2px 8px;
+  border:2px solid #fff;
+}
+
+ul {
+  margin:0;
+  list-style: none;
+  padding:0 20px;
+}
+
+.body ul {
+  list-style: disc;
+}
+
+.paginate {
+  text-align:center;
+  display:block;
+  width:100%;
+  font-size:80%;
+}
+
+form {
+  font-size:0.8em;
+  padding:8px;
+  background:#ddd;
+}
+
+
+
+fieldset {
+  background:#ebebeb;
+}
+
+fieldset#author {
+  float:left;
+  width:20%;
+}
+
+textarea {
+  padding:2px;
+}
+
+
+blockquote {
+  padding:8px;
+  background:#444;
+  color:#fff;
+}
diff --git a/web/query/_attachments/style/sphinxdoc.css b/web/query/_attachments/style/sphinxdoc.css
new file mode 100644 (file)
index 0000000..0a42807
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * sphinxdoc.css_t
+ * ~~~~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- sphinxdoc theme.  Originally created by
+ * Armin Ronacher for Werkzeug.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+    font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+                 'Verdana', sans-serif;
+    font-size: 14px;
+    letter-spacing: -0.01em;
+    line-height: 150%;
+    text-align: center;
+    background-color: #BFD1D4;
+    color: black;
+    padding: 0;
+    border: 1px solid #aaa;
+
+    margin: 0px 80px 0px 80px;
+    min-width: 740px;
+}
+
+div.document {
+    background-color: white;
+    text-align: left;
+    background-image: url(contents.png);
+    background-repeat: repeat-x;
+}
+
+div.bodywrapper {
+    margin: 0 240px 0 0;
+    border-right: 1px solid #ccc;
+}
+
+div.body {
+    margin: 0;
+    padding: 0.5em 20px 20px 20px;
+}
+
+div.related {
+    font-size: 1em;
+}
+
+div.related ul {
+    background-image: url(navigation.png);
+    height: 2em;
+    border-top: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+}
+
+div.related ul li {
+    margin: 0;
+    padding: 0;
+    height: 2em;
+    float: left;
+}
+
+div.related ul li.right {
+    float: right;
+    margin-right: 5px;
+}
+
+div.related ul li a {
+    margin: 0;
+    padding: 0 5px 0 5px;
+    line-height: 1.75em;
+    color: #EE9816;
+}
+
+div.related ul li a:hover {
+    color: #3CA8E7;
+}
+
+div.sphinxsidebarwrapper {
+    padding: 0;
+}
+
+div.sphinxsidebar {
+    margin: 0;
+    padding: 0.5em 15px 15px 0;
+    width: 210px;
+    float: right;
+    font-size: 1em;
+    text-align: left;
+}
+
+div.sphinxsidebar h3, div.sphinxsidebar h4 {
+    margin: 1em 0 0.5em 0;
+    font-size: 1em;
+    padding: 0.1em 0 0.1em 0.5em;
+    color: white;
+    border: 1px solid #86989B;
+    background-color: #AFC1C4;
+}
+
+div.sphinxsidebar h3 a {
+    color: white;
+}
+
+div.sphinxsidebar ul {
+    padding-left: 1.5em;
+    margin-top: 7px;
+    padding: 0;
+    line-height: 130%;
+}
+
+div.sphinxsidebar ul ul {
+    margin-left: 20px;
+}
+
+div.footer {
+    background-color: #E3EFF1;
+    color: #86989B;
+    padding: 3px 8px 3px 0;
+    clear: both;
+    font-size: 0.8em;
+    text-align: right;
+}
+
+div.footer a {
+    color: #86989B;
+    text-decoration: underline;
+}
+
+/* -- body styles ----------------------------------------------------------- */
+
+p {    
+    margin: 0.8em 0 0.5em 0;
+}
+
+a {
+    color: #CA7900;
+    text-decoration: none;
+}
+
+a:hover {
+    color: #2491CF;
+}
+
+div.body a {
+    text-decoration: underline;
+}
+
+h1 {
+    margin: 0;
+    padding: 0.7em 0 0.3em 0;
+    font-size: 1.5em;
+    color: #11557C;
+}
+
+h2 {
+    margin: 1.3em 0 0.2em 0;
+    font-size: 1.35em;
+    padding: 0;
+}
+
+h3 {
+    margin: 1em 0 -0.3em 0;
+    font-size: 1.2em;
+}
+
+div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a {
+    color: black!important;
+}
+
+h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
+    display: none;
+    margin: 0 0 0 0.3em;
+    padding: 0 0.2em 0 0.2em;
+    color: #aaa!important;
+}
+
+h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
+h5:hover a.anchor, h6:hover a.anchor {
+    display: inline;
+}
+
+h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
+h5 a.anchor:hover, h6 a.anchor:hover {
+    color: #777;
+    background-color: #eee;
+}
+
+a.headerlink {
+    color: #c60f0f!important;
+    font-size: 1em;
+    margin-left: 6px;
+    padding: 0 4px 0 4px;
+    text-decoration: none!important;
+}
+
+a.headerlink:hover {
+    background-color: #ccc;
+    color: white!important;
+}
+
+cite, code, tt {
+    font-family: 'Consolas', 'Deja Vu Sans Mono',
+                 'Bitstream Vera Sans Mono', monospace;
+    font-size: 0.95em;
+    letter-spacing: 0.01em;
+}
+
+tt {
+    background-color: #f2f2f2;
+    border-bottom: 1px solid #ddd;
+    color: #333;
+}
+
+tt.descname, tt.descclassname, tt.xref {
+    border: 0;
+}
+
+hr {
+    border: 1px solid #abc;
+    margin: 2em;
+}
+
+a tt {
+    border: 0;
+    color: #CA7900;
+}
+
+a tt:hover {
+    color: #2491CF;
+}
+
+pre {
+    font-family: 'Consolas', 'Deja Vu Sans Mono',
+                 'Bitstream Vera Sans Mono', monospace;
+    font-size: 0.95em;
+    letter-spacing: 0.015em;
+    line-height: 120%;
+    padding: 0.5em;
+    border: 1px solid #ccc;
+    background-color: #f8f8f8;
+}
+
+pre a {
+    color: inherit;
+    text-decoration: underline;
+}
+
+td.linenos pre {
+    padding: 0.5em 0;
+}
+
+div.quotebar {
+    background-color: #f8f8f8;
+    max-width: 250px;
+    float: right;
+    padding: 2px 7px;
+    border: 1px solid #ccc;
+}
+
+div.topic {
+    background-color: #f8f8f8;
+}
+
+table {
+    border-collapse: collapse;
+    margin: 0 -0.5em 0 -0.5em;
+}
+
+table td, table th {
+    padding: 0.2em 0.5em 0.2em 0.5em;
+}
+
+div.admonition, div.warning {
+    font-size: 0.9em;
+    margin: 1em 0 1em 0;
+    border: 1px solid #86989B;
+    background-color: #f7f7f7;
+    padding: 0;
+}
+
+div.admonition p, div.warning p {
+    margin: 0.5em 1em 0.5em 1em;
+    padding: 0;
+}
+
+div.admonition pre, div.warning pre {
+    margin: 0.4em 1em 0.4em 1em;
+}
+
+div.admonition p.admonition-title,
+div.warning p.admonition-title {
+    margin: 0;
+    padding: 0.1em 0 0.1em 0.5em;
+    color: white;
+    border-bottom: 1px solid #86989B;
+    font-weight: bold;
+    background-color: #AFC1C4;
+}
+
+div.warning {
+    border: 1px solid #940000;
+}
+
+div.warning p.admonition-title {
+    background-color: #CF0000;
+    border-bottom-color: #940000;
+}
+
+div.admonition ul, div.admonition ol,
+div.warning ul, div.warning ol {
+    margin: 0.1em 0.5em 0.5em 3em;
+    padding: 0;
+}
+
+div.versioninfo {
+    margin: 1em 0 0 0;
+    border: 1px solid #ccc;
+    background-color: #DDEAF0;
+    padding: 8px;
+    line-height: 1.3em;
+    font-size: 0.9em;
+}
+
+.viewcode-back {
+    font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+                 'Verdana', sans-serif;
+}
+
+div.viewcode-block:target {
+    background-color: #f4debf;
+    border-top: 1px solid #ac9;
+    border-bottom: 1px solid #ac9;
+}
diff --git a/web/query/_attachments/style/style.css b/web/query/_attachments/style/style.css
new file mode 100755 (executable)
index 0000000..0f5666f
--- /dev/null
@@ -0,0 +1,128 @@
+body { 
+font-family: Georgia; 
+line-height: 1.4;
+font-size: 12px;
+background: #fbfcfb url(images/bg.jpg) repeat-x;
+margin: 0;
+padding: 0;
+}
+
+a { color: #121212; text-decoration: none;}
+a:hover { text-decoration: underline; }
+h1 { margin: 0; padding: 0; }
+h2 { margin: 0; padding: 0; }
+
+#wrap { 
+width: 800px;
+margin: 0 auto;
+}
+
+#header {
+height: 100px;
+}
+#header h1 {
+margin: 0;
+padding: 10px 0 0 20px;
+font-weight: 100;
+font-size: 30px;
+letter-spacing: -2px;
+}
+#header h1 a { text-decoration: none; color: #111; }
+#header h1 a:hover { text-decoration: none; border-bottom: 1px solid #111; }
+
+#header h2 { padding: 0 0 0 20px; margin: 0; color: #1F160F; font-weight: 100; font-size: 17px; }
+
+#menu {
+margin-top: 10px;
+height: 40px;
+line-height: 40px;
+}
+
+#menu ul {
+list-style-type: none;
+margin: 0;
+padding-left: 10px;
+}
+#menu ul li {
+display: block;
+float: left;
+}
+#menu ul li a {
+display: block;
+padding: 0 10px 0 10px;
+text-decoration: none;
+color: #fff;
+font-size: 14px;
+font-weight: 100;
+}
+#menu ul li a:hover {
+color: #111;
+background: #fff;
+}
+
+#top { background: url(images/top.png) no-repeat; padding: 10px 0; }
+#bottom { background: url(images/bottom.png) no-repeat; padding: 10px 0; }
+
+#contentwrap {
+padding: 0 20px;
+background: url(images/content.png) repeat-y;
+}
+
+#content {
+float: left;
+width: 500px;
+padding-top: 10px;
+}
+#content h2 {
+margin: 0;
+color: #121212;
+line-height: 30px;
+font-size: 20px;
+font-weight: 100;
+}
+
+#content p { padding: 0 0 10px 0; }
+#content ul { padding: 0 20px 10px 30px; }
+
+#sidebar {
+float: right;
+width: 220px;
+background: #fff;
+}
+
+#sidebar h3 {
+height: 40px;
+line-height: 40px;
+font-weight: 600;
+font-size: 12px;
+margin: 0; padding: 0 0 0 10px; 
+color: #121212;
+}
+
+#sidebar p { padding: 0 10px 10px 10px; color: #232323; }
+
+#sidebar ul {
+padding: 10px 0 25px 30px;
+margin: 0;
+color: #121212;
+list-style-type: circle;
+}
+
+#sidebar ul li {
+padding: 0;
+margin: 0;
+}
+
+#sidebar ul ul { padding: 2px 0 2px 20px; }
+
+#footer { 
+text-align: center;
+background: url(images/footer.jpg) repeat-x;
+height: 40px;
+line-height: 40px;
+color: #111;
+font-size: 12px;
+}
+#footer p { padding: 0; margin: 0; }
+#footer a { color: #111; }
+#footer a:hover { text-decoration: underline; }
diff --git a/web/query/_id b/web/query/_id
new file mode 100644 (file)
index 0000000..8fa2b73
--- /dev/null
@@ -0,0 +1 @@
+_design/myops
diff --git a/web/query/blog.json b/web/query/blog.json
new file mode 100644 (file)
index 0000000..7f56e08
--- /dev/null
@@ -0,0 +1 @@
+{"title": "Sample CouchApp: Engine For MyOps"}
diff --git a/web/query/couchapp.json b/web/query/couchapp.json
new file mode 100644 (file)
index 0000000..d70a6be
--- /dev/null
@@ -0,0 +1 @@
+{"index": "index.html"}
diff --git a/web/query/evently/account/loggedIn/data.js b/web/query/evently/account/loggedIn/data.js
new file mode 100644 (file)
index 0000000..3392de0
--- /dev/null
@@ -0,0 +1,19 @@
+function(e, r) {
+  var app = $$(this).app;
+  var path = app.require("vendor/couchapp/lib/path").init(app.req);
+  var data = {
+    name : r.userCtx.name,
+    uri_name : encodeURIComponent(r.userCtx.name),
+    auth_db : encodeURIComponent(r.info.authentication_db) 
+  };
+  if (r.userCtx.roles.indexOf("_admin") != -1 || r.userCtx.roles.indexOf("author") != -1) {
+    if (app.req.path.indexOf("post-page") == -1) {
+      data.postPath = path.show("edit")+"/";
+      data.postMessage = "New post.";
+    } else {
+      data.postPath = path.show("edit", app.req.query.startkey[0]);
+      data.postMessage = "Edit this post.";
+    }    
+  }
+  return data;
+}
\ No newline at end of file
diff --git a/web/query/evently/account/loggedIn/mustache.html b/web/query/evently/account/loggedIn/mustache.html
new file mode 100644 (file)
index 0000000..cd6521e
--- /dev/null
@@ -0,0 +1,7 @@
+<span>Welcome 
+<a target="_new" href="/_utils/document.html?{{auth_db}}/org.couchdb.user%3A{{uri_name}}">{{name}}</a>! 
+<a href="#logout">Logout?</a>
+{{#postPath}}
+  <a href="{{postPath}}">{{postMessage}}</a>
+{{/postPath}}
+</span>
\ No newline at end of file
diff --git a/web/query/evently/profile/loggedOut/mustache.html b/web/query/evently/profile/loggedOut/mustache.html
new file mode 100644 (file)
index 0000000..1a54173
--- /dev/null
@@ -0,0 +1,3 @@
+<form>
+  <p>Please login (above) to leave comments.</p>  
+</form>
\ No newline at end of file
diff --git a/web/query/evently/profile/profileReady/mustache.html b/web/query/evently/profile/profileReady/mustache.html
new file mode 100644 (file)
index 0000000..891b9d4
--- /dev/null
@@ -0,0 +1,13 @@
+<!-- form to create a comment -->
+<form id="new-comment" action="post.html" method="post">
+  <div class="avatar">
+    {{#avatar_url}}<img src="{{avatar_url}}"/>{{/avatar_url}}
+  </div>
+  <h3>Comment on this post, {{nickname}}:</h3>
+  <p><label>Comment <em>(with <a href="http://warpedvisions.org/projects/markdown-cheat-sheet/">Markdown</a>)</em></label>
+  <textarea name="comment" rows="8" cols="40"></textarea></p>
+  <p>
+    <input type="button" id="preview" value="Preview">
+    <input type="submit" value="Save &rarr;">
+  </p>
+</form>
\ No newline at end of file
diff --git a/web/query/evently/profile/profileReady/selectors/#preview/click.js b/web/query/evently/profile/profileReady/selectors/#preview/click.js
new file mode 100644 (file)
index 0000000..7517c93
--- /dev/null
@@ -0,0 +1,15 @@
+function() {
+  var f = $(this), app = $$(this).app;
+  var Mustache = app.require("lib/mustache");
+  var markdown = app.require("vendor/couchapp/lib/markdown");
+
+  var c = {
+    name : $$("#profile").profile.nickname,
+    url : $$("#profile").profile.url,
+    avatar : $$("#profile").profile.gravatar_url,
+    html : markdown.encode(Mustache.escape($("[name=comment]", f).val())),
+    created_at : JSON.parse(JSON.stringify(new Date()))
+  };
+  
+  $("#comment-preview").html(Mustache.to_html(app.ddoc.templates.partials.comment, c));
+};
\ No newline at end of file
diff --git a/web/query/evently/profile/profileReady/selectors/form/submit.js b/web/query/evently/profile/profileReady/selectors/form/submit.js
new file mode 100644 (file)
index 0000000..9a10e3a
--- /dev/null
@@ -0,0 +1,18 @@
+function() {
+  var f = $(this), app = $$(this).app;
+  var newComment = {
+    type : "comment",
+    post_id : app.post_id,
+    format : "markdown",
+    comment : $("[name=comment]", f).val(),
+    commenter : $$("#profile").profile,
+    created_at : new Date()
+  };
+  app.db.saveDoc(newComment, {
+    success : function(resp) {
+      $("#new-comment").html('<h2>Success! Your comment has posted.</h2><p>Refresh the page to see it.</p>');
+    }
+  });
+  
+  return false;
+};
\ No newline at end of file
diff --git a/web/query/evently/tagcloud/_init/data.js b/web/query/evently/tagcloud/_init/data.js
new file mode 100644 (file)
index 0000000..06dddf1
--- /dev/null
@@ -0,0 +1,25 @@
+function(resp) {
+  var app = $$(this).app;
+  var path = app.require("vendor/couchapp/lib/path").init(app.req);
+  var tags = [];
+  resp.rows.forEach(function(r) {
+    var tag = r.key[0];
+    // todo remove duplication of link definitions
+    var link = path.list("index","tags",{
+      descending : true, 
+      reduce : false,
+      limit : 10,
+      startkey : [tag, {}], 
+      endkey : [tag]});
+    tags.push({
+      tag : tag,
+      link : link,
+      size : (r.value * 2) + 10
+    });
+  });
+  return {
+    tags : tags.sort(function(a, b) {
+      return a.size < b.size;
+    })
+  };
+}
\ No newline at end of file
diff --git a/web/query/evently/tagcloud/_init/mustache.html b/web/query/evently/tagcloud/_init/mustache.html
new file mode 100644 (file)
index 0000000..aa55f30
--- /dev/null
@@ -0,0 +1,3 @@
+{{#tags}}
+  <a style="font-size:{{size}}px;" href="{{link}}">{{tag}}</a>
+{{/tags}}
\ No newline at end of file
diff --git a/web/query/evently/tagcloud/_init/query.json b/web/query/evently/tagcloud/_init/query.json
new file mode 100644 (file)
index 0000000..98f806b
--- /dev/null
@@ -0,0 +1 @@
+{"group_level": 1, "view": "tags"}
\ No newline at end of file
diff --git a/web/query/helpers/md5.js b/web/query/helpers/md5.js
new file mode 100644 (file)
index 0000000..0837f32
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
+var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
+var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ keep
+ */
+function core_md5(x, len)
+{
+  /* append padding */
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i < x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
+    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
+    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
+    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
+    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+
+    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
+    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
+    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
+    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
+    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
+    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
+    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
+    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
+    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
+    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
+    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
+    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
+    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
+    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
+    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data)
+{
+  var bkey = str2binl(key);
+  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i < 16; i++)
+  {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+  return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ keep
+ */
+function str2binl(str)
+{
+  var bin = Array();
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < str.length * chrsz; i += chrsz)
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+  return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+  var str = "";
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < bin.length * 32; i += chrsz)
+    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ keep
+ */
+function binl2hex(binarray)
+{
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
+  }
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i += 3)
+  {
+    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
+                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+    for(var j = 0; j < 4; j++)
+    {
+      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+    }
+  }
+  return str;
+}
\ No newline at end of file
diff --git a/web/query/lib/blog.js b/web/query/lib/blog.js
new file mode 100644 (file)
index 0000000..3295412
--- /dev/null
@@ -0,0 +1,5 @@
+exports.slugifyString = function(string) {
+  return string.replace(/\W/g,'-').
+    replace(/\-*$/,'').replace(/^\-*/,'').
+    replace(/\-{2,}/,'-');
+}
\ No newline at end of file
diff --git a/web/query/lib/mustache.js b/web/query/lib/mustache.js
new file mode 100644 (file)
index 0000000..9c06276
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+  Shameless port of http://github.com/defunkt/mustache
+  by Jan Lehnardt <jan@apache.org>, 
+     Alexander Lang <alex@upstream-berlin.com>,
+     Sebastian Cohnen <sebastian.cohnen@googlemail.com>
+
+  Thanks @defunkt for the awesome code.
+
+  See http://github.com/defunkt/mustache for more info.
+*/
+
+var Mustache = function() {
+  var Renderer = function() {};
+
+  Renderer.prototype = {
+    otag: "{{",
+    ctag: "}}",
+    pragmas: {},
+    buffer: [],
+    pragmas_parsed: false,
+
+    render: function(template, context, partials, in_recursion) {
+      // fail fast
+      if(template.indexOf(this.otag) == -1) {
+        if(in_recursion) {
+          return template;
+        } else {
+          this.send(template);
+        }
+      }
+
+      if(!in_recursion) {
+        this.buffer = [];
+      }
+
+      if(!this.pragmas_parsed) {
+        template = this.render_pragmas(template);
+      }
+      var html = this.render_section(template, context, partials);
+      if(in_recursion) {
+        return this.render_tags(html, context, partials, in_recursion);
+      }
+
+      this.render_tags(html, context, partials, in_recursion);
+    },
+
+    /*
+      Sends parsed lines
+    */
+    send: function(line) {
+      if(line != "") {
+        this.buffer.push(line);
+      }
+    },
+
+    /*
+      Looks for %PRAGMAS
+    */
+    render_pragmas: function(template) {
+      this.pragmas_parsed = true;
+      // no pragmas
+      if(template.indexOf(this.otag + "%") == -1) {
+        return template;
+      }
+
+      var that = this;
+      var regex = new RegExp(this.otag + "%([\\w_-]+) ?([\\w]+=[\\w]+)?"
+        + this.ctag);
+      return template.replace(regex, function(match, pragma, options) {
+        that.pragmas[pragma] = {};
+        if(options) {
+          var opts = options.split("=");
+          that.pragmas[pragma][opts[0]] = opts[1];
+        }
+        return "";
+        // ignore unknown pragmas silently
+      });
+    },
+
+    /*
+      Tries to find a partial in the global scope and render it
+    */
+    render_partial: function(name, context, partials) {
+      if(typeof(context[name]) != "object") {
+        throw({message: "subcontext for '" + name + "' is not an object"});
+      }
+      if(!partials || !partials[name]) {
+        throw({message: "unknown_partial '" + name + "'"});
+      }
+      return this.render(partials[name], context[name], partials, true);
+    },
+
+    /*
+      Renders boolean and enumerable sections
+    */
+    render_section: function(template, context, partials) {
+      if(template.indexOf(this.otag + "#") == -1) {
+        return template;
+      }
+      var that = this;
+      // CSW - Added "+?" so it finds the tighest bound, not the widest
+      var regex = new RegExp(this.otag + "\\#(.+)" + this.ctag +
+              "\\s*([\\s\\S]+?)" + this.otag + "\\/\\1" + this.ctag + "\\s*", "mg");
+
+      // for each {{#foo}}{{/foo}} section do...
+      return template.replace(regex, function(match, name, content) {
+        var value = that.find(name, context);
+        if(that.is_array(value)) { // Enumerable, Let's loop!
+          return that.map(value, function(row) {
+            return that.render(content, that.merge(context,
+                    that.create_context(row)), partials, true);
+          }).join("");
+        } else if (that.is_iterator(value)) {
+          var result = [];
+          var row;
+          while (row = value()) {
+            result.push(that.render(content, that.merge(context,
+                          that.create_context(row)), partials, true));
+          } // fuck buffering, works for now though.
+          return result.join('');
+        } else if(value) { // boolean section
+          return that.render(content, context, partials, true);
+        } else {
+          return "";
+        }
+      });
+    },
+
+    /*
+      Replace {{foo}} and friends with values from our view
+    */
+    render_tags: function(template, context, partials, in_recursion) {
+      // tit for tat
+      var that = this;
+
+      var new_regex = function() {
+        return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\/#]+?)\\1?" +
+          that.ctag + "+", "g");
+      };
+
+      var regex = new_regex();
+      var lines = template.split("\n");
+       for (var i=0; i < lines.length; i++) {
+         lines[i] = lines[i].replace(regex, function(match, operator, name) {
+           switch(operator) {
+             case "!": // ignore comments
+               return match;
+             case "=": // set new delimiters, rebuild the replace regexp
+               that.set_delimiters(name);
+               regex = new_regex();
+               return "";
+             case ">": // render partial
+               return that.render_partial(name, context, partials);
+             case "{": // the triple mustache is unescaped
+               return that.find(name, context);
+             default: // escape the value
+               return that.escape(that.find(name, context));
+           }
+         }, this);
+         if(!in_recursion) {
+           this.send(lines[i]);
+         }
+       }
+       return lines.join("\n");
+    },
+
+    set_delimiters: function(delimiters) {
+      var dels = delimiters.split(" ");
+      this.otag = this.escape_regex(dels[0]);
+      this.ctag = this.escape_regex(dels[1]);
+    },
+
+    escape_regex: function(text) {
+      // thank you Simon Willison
+      if(!arguments.callee.sRE) {
+        var specials = [
+          '/', '.', '*', '+', '?', '|',
+          '(', ')', '[', ']', '{', '}', '\\'
+        ];
+        arguments.callee.sRE = new RegExp(
+          '(\\' + specials.join('|\\') + ')', 'g'
+        );
+      }
+    return text.replace(arguments.callee.sRE, '\\$1');
+    },
+
+    /*
+      find `name` in current `context`. That is find me a value
+      from the view object
+    */
+    find: function(name, context) {
+      name = this.trim(name);
+      if(typeof context[name] === "function" && !context[name].iterator) {
+        return context[name].apply(context);
+      }
+      if(context[name] !== undefined) {
+        return context[name];
+      }
+      // silently ignore unkown variables
+      return "";
+    },
+
+    // Utility methods
+
+    /*
+      Does away with nasty characters
+    */
+    escape: function(s) {
+      return ((s == null) ? "" : s).toString().replace(/[&"<>\\]/g, function(s) {
+        switch(s) {
+          case "&": return "&amp;";
+          case "\\": return "\\\\";;
+          case '"': return '\"';;
+          case "<": return "&lt;";
+          case ">": return "&gt;";
+          default: return s;
+        }
+      });
+    },
+
+    /*
+      Merges all properties of object `b` into object `a`.
+      `b.property` overwrites a.property`
+    */
+    merge: function(a, b) {
+      var _new = {};
+      for(var name in a) {
+        if(a.hasOwnProperty(name)) {
+          _new[name] = a[name];
+        }
+      };
+      for(var name in b) {
+        if(b.hasOwnProperty(name)) {
+          _new[name] = b[name];
+        }
+      };
+      return _new;
+    },
+
+    // by @langalex, support for arrays of strings
+    create_context: function(_context) {
+      if(this.is_object(_context)) {
+        return _context;
+      } else if(this.pragmas["IMPLICIT-ITERATOR"]) {
+        var iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator || ".";
+        var ctx = {};
+        ctx[iterator] = _context
+        return ctx;
+      }
+    },
+
+    is_object: function(a) {
+      return a && typeof a == "object";
+    },
+
+    is_array: function(a) {
+      return Object.prototype.toString.call(a) === '[object Array]';
+    },
+
+    is_iterator : function(f) {
+      return (typeof f === 'function' && f.iterator);
+    },
+
+    /*
+      Gets rid of leading and trailing whitespace
+    */
+    trim: function(s) {
+      return s.replace(/^\s*|\s*$/g, "");
+    },
+
+    /*
+      Why, why, why? Because IE. Cry, cry cry.
+    */
+    map: function(array, fn) {
+      if (typeof array.map == "function") {
+        return array.map(fn)
+      } else {
+        var r = [];
+        var l = array.length;
+        for(i=0;i<l;i++) {
+          r.push(fn(array[i]));
+        }
+        return r;
+      }
+    }
+  };
+
+  return({
+    name: "mustache.js",
+    version: "0.2.3-dev",
+
+    /*
+      Turns a template and view into HTML
+    */
+    to_html: function(template, view, partials, send_fun) {
+      var renderer = new Renderer();
+      if(send_fun) {
+        renderer.send = send_fun;
+      }
+      renderer.render(template, view, partials);
+      if(!send_fun) {
+        return renderer.buffer.join("\n");
+      }
+    },
+    escape : function(string) {
+      var renderer = new Renderer();
+      return renderer.escape(string);
+    }
+  });
+}();
+// for CommonJS
+if (exports) {
+  exports.to_html = Mustache.to_html;
+  exports.escape = Mustache.escape;
+}
\ No newline at end of file
diff --git a/web/query/lib/validate.js b/web/query/lib/validate.js
new file mode 100644 (file)
index 0000000..a2a5699
--- /dev/null
@@ -0,0 +1,49 @@
+// a library for validations
+// over time we expect to extract more helpers and move them here.
+exports.init = function(newDoc, oldDoc, userCtx, secObj) {
+  var v = {};
+  
+  v.forbidden = function(message) {    
+    throw({forbidden : message});
+  };
+
+  v.unauthorized = function(message) {
+    throw({unauthorized : message});
+  };
+
+  v.assert = function(should, message) {
+    if (!should) v.forbidden(message);
+  }
+  
+  v.isAdmin = function() {
+    return userCtx.roles.indexOf('_admin') != -1
+  };
+
+  v.require = function() {
+    for (var i=0; i < arguments.length; i++) {
+      var field = arguments[i];
+      message = "The '"+field+"' field is required.";
+      if (typeof newDoc[field] == "undefined") v.forbidden(message);
+    };
+  };
+
+  v.unchanged = function(field) {
+    if (oldDoc && oldDoc[field] != newDoc[field]) 
+      v.forbidden("You may not change the '"+field+"' field.");
+  };
+
+  v.matches = function(field, regex, message) {
+    if (!newDoc[field].match(regex)) {
+      message = message || "Format of '"+field+"' field is invalid.";
+      v.forbidden(message);    
+    }
+  };
+
+  // this ensures that the date will be UTC, parseable, and collate correctly
+  v.dateFormat = function(field) {
+    message = "Sorry, '"+field+"' is not a valid date format. Try: 2010-02-24T17:00:03.432Z";
+    v.matches(field, /\d{4}\-\d{2}\-\d{2}T\d{2}:\d{2}:\d{2}(\.\d*)?Z/, message);
+  }
+  
+  return v;
+};
\ No newline at end of file
diff --git a/web/query/lists/.hosts.js.swp b/web/query/lists/.hosts.js.swp
new file mode 100644 (file)
index 0000000..e04954e
Binary files /dev/null and b/web/query/lists/.hosts.js.swp differ
diff --git a/web/query/lists/.rpms.js.swo b/web/query/lists/.rpms.js.swo
new file mode 100644 (file)
index 0000000..9bccbed
Binary files /dev/null and b/web/query/lists/.rpms.js.swo differ
diff --git a/web/query/lists/.rpms.js.swp b/web/query/lists/.rpms.js.swp
new file mode 100644 (file)
index 0000000..69af2cb
Binary files /dev/null and b/web/query/lists/.rpms.js.swp differ
diff --git a/web/query/lists/1 b/web/query/lists/1
new file mode 100644 (file)
index 0000000..b19ac9a
--- /dev/null
@@ -0,0 +1,130 @@
+function(head, req) {
+  var ddoc = this;
+  var Mustache = require("lib/mustache");
+  var List = require("vendor/couchapp/lib/list");
+  var path = require("vendor/couchapp/lib/path").init(req);
+  var Atom = require("vendor/couchapp/lib/atom");
+
+  var within = 0;
+       if ( 'within' in req.query ) {
+               within = Number(req.query['within']);
+               log("within: " + within);
+       } else {
+               within = 60 * 60 * 24;  // within the last day.
+       }
+  var ts = (new Date()).getTime()/1000 - within; // convert milliseconds to seconds
+
+  var indexPath = path.list('rpms','rpm-to-host',{descending:true, limit:10});
+  var feedPath = path.list('rpms','rpm-to-host',{descending:true, limit:10, format:"atom"});
+  var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
+
+  function check_field(query, field, def) { 
+       var ret = def;
+       if ( field in query ) {
+               ret = query[field];
+               log(field + ": " + ret);
+       }
+       return ret;
+  }
+
+  var path_parts = req.path;
+
+  // The first matching format is sent, so reordering functions changes 
+  // thier priority. In this case HTML is the preferred format, so it comes first.
+  provides("text", function() {
+               var row, first_row = true;
+               var more_props = true;
+               var header;
+               var hostname = check_field(req.query, 'hostname', false);
+               var rpm = check_field(req.query, 'rpm', false);
+               var show_hosts = check_field(req.query, 'show_hosts', false);
+               var show_rpms = check_field(req.query, 'show_rpms', false);
+               while (row = getRow()) {
+                       if ( row.key[0] < ts ) { continue; }
+               } 
+
+               return '\n';
+  });
+  provides("html", function() {
+    var key = "";
+    // render the html head using a template
+       //
+    var stash = {
+      header : {
+           title : 'blah blah blah',
+        index : indexPath,
+        blogName : ddoc.blog.title,
+        feedPath : feedPath,
+        commentsFeed : commentsFeed
+      },
+      scripts : {},
+      db : req.path[0],
+      design : req.path[2],
+      feedPath : feedPath,
+      newPostPath : path.show("edit"),
+      assets : path.asset(),
+         rpmversion : check_field(req.query, 'rpmversion', 'List all'),
+
+      nodes : List.withRows(function(row) {
+        var node = row.value;
+        key = row.key;
+        return {
+          name : key[1],
+          val  : row.value,
+          link : path.list('rpmlist',{rpmversion : key[1], group : true, startkey : [key[1]], endkey : [key[1],{}]}),
+        };
+      }),
+    };
+    return Mustache.to_html(ddoc.templates.rpms, stash, ddoc.templates.partials, List.send);
+  });
+         // link : path.list('rpms','rpm-to-host', {rpmversion : key[1], group : true, startkey : [key[1]], endkey : [key[1],{}]}),
+
+  // if the client requests an atom feed and not html, 
+  // we run this function to generate the feed.
+  provides("atom", function() {    
+    var path = require("vendor/couchapp/lib/path").init(req);
+    var markdown = require("vendor/couchapp/lib/markdown");
+    var textile = require("vendor/textile/textile");
+
+    // we load the first row to find the most recent change date
+    var row = getRow();
+    
+    // generate the feed header
+    var feedHeader = Atom.header({
+      updated : (row ? new Date(row.value.created_at) : new Date()),
+      title : ddoc.blog.title,
+      feed_id : path.absolute(indexPath),
+      feed_link : path.absolute(feedPath),
+    });
+    
+    // send the header to the client
+    send(feedHeader);
+
+    // loop over all rows
+    if (row) {
+      do {
+        if (row.value.format == "markdown") {
+          var html = markdown.encode(row.value.body);
+        } else if (row.value.format == "textile") {
+          var html = textile.encode(row.value.body);
+        } else {
+          var html = Mustache.escape(row.value.html);
+        }
+        // generate the entry for this row
+        var feedEntry = Atom.entry({
+          entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),
+          title : row.value.title,
+          content : html,
+          updated : new Date(row.value.created_at),
+          author : row.value.author,
+          alternate : path.absolute(path.show('post', row.id))
+        });
+        // send the entry to client
+        send(feedEntry);
+      } while (row = getRow());
+    }
+
+    // close the loop after all rows are rendered
+    return "</feed>";
+  });
+};
diff --git a/web/query/lists/comments.js b/web/query/lists/comments.js
new file mode 100644 (file)
index 0000000..dffcd4f
--- /dev/null
@@ -0,0 +1,55 @@
+function(head, req) {
+  var ddoc = this;
+  var Mustache = require("lib/mustache");
+  var List = require("vendor/couchapp/lib/list");
+  var path = require("vendor/couchapp/lib/path").init(req);
+  var Atom = require("vendor/couchapp/lib/atom");
+
+  var indexPath = path.list('index','recent-posts',{descending:true, limit:10});
+  var feedPath = path.list('index','recent-posts',{descending:true, limit:10, format:"atom"});
+  var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
+
+  // if the client requests an atom feed and not html, 
+  // we run this function to generate the feed.
+  provides("atom", function() {    
+    var path = require("vendor/couchapp/lib/path").init(req);
+    var markdown = require("vendor/couchapp/lib/markdown");
+    var textile = require("vendor/textile/textile");
+
+    // we load the first row to find the most recent change date
+    var row = getRow();
+    
+    // generate the feed header
+    var feedHeader = Atom.header({
+      updated : (row ? new Date(row.value.created_at) : new Date()),
+      title : ddoc.blog.title + " comments",
+      feed_id : path.absolute(indexPath),
+      feed_link : path.absolute(commentsFeed)
+    });
+    
+    // send the header to the client
+    send(feedHeader);
+
+    // loop over all rows
+    if (row) {
+      do {
+        var v = row.value;
+        
+        // generate the entry for this row
+        var feedEntry = Atom.entry({
+          entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),
+          title : "comment on "+v.post_id,
+          content : markdown.encode(Mustache.escape(v.comment)),
+          updated : new Date(v.created_at),
+          author : v.commenter.nickname || v.commenter.name,
+          alternate : path.absolute(path.list('post','post-page', {startkey:[v.post_id]}))
+        });
+        // send the entry to client
+        send(feedEntry);
+      } while (row = getRow());
+    }
+
+    // close the loop after all rows are rendered
+    return "</feed>";
+  });
+}
\ No newline at end of file
diff --git a/web/query/lists/index.js b/web/query/lists/index.js
new file mode 100644 (file)
index 0000000..531300b
--- /dev/null
@@ -0,0 +1,113 @@
+function(head, req) {
+  var ddoc = this;
+  var Mustache = require("lib/mustache");
+  var List = require("vendor/couchapp/lib/list");
+  var path = require("vendor/couchapp/lib/path").init(req);
+  var Atom = require("vendor/couchapp/lib/atom");
+
+  var indexPath = path.list('index','recent-posts',{descending:true, limit:10});
+  var feedPath = path.list('index','recent-posts',{descending:true, limit:10, format:"atom"});
+  var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
+
+  var path_parts = req.path;
+  // The provides function serves the format the client requests.
+  // The first matching format is sent, so reordering functions changes 
+  // thier priority. In this case HTML is the preferred format, so it comes first.
+  provides("html", function() {
+    var key = "";
+    // render the html head using a template
+    var stash = {
+      header : {
+        index : indexPath,
+        blogName : ddoc.blog.title,
+        feedPath : feedPath,
+        commentsFeed : commentsFeed
+      },
+      scripts : {},
+      db : req.path[0],
+      design : req.path[2],
+      feedPath : feedPath,
+      newPostPath : path.show("edit"),
+      assets : path.asset(),
+      posts : List.withRows(function(row) {
+        var post = row.value;
+        key = row.key;
+        return {
+          title : post.title,
+          author : post.author,
+          date : post.created_at,
+          link : path.list('post','post-page', {startkey : [row.id]}),
+          has_tags : post.tags ? true : false,
+          tags : post.tags && post.tags.map ? post.tags.map(function(tag) {
+            var t = tag.toLowerCase();
+            return {
+              tag : tag,
+              link : path.list("index", "tags", {
+                descending : true, 
+                reduce : false, 
+                startkey : [t, {}], 
+                endkey : [t]
+              })
+            }
+          }) : []
+        };
+      }),
+      older : function() {
+        return path.older(key);
+      },
+      "5" : path.limit(5),
+      "10" : path.limit(10),
+      "25" : path.limit(25)
+    };
+    return Mustache.to_html(ddoc.templates.index, stash, ddoc.templates.partials, List.send);
+  });
+
+  // if the client requests an atom feed and not html, 
+  // we run this function to generate the feed.
+  provides("atom", function() {    
+    var path = require("vendor/couchapp/lib/path").init(req);
+    var markdown = require("vendor/couchapp/lib/markdown");
+    var textile = require("vendor/textile/textile");
+
+    // we load the first row to find the most recent change date
+    var row = getRow();
+    
+    // generate the feed header
+    var feedHeader = Atom.header({
+      updated : (row ? new Date(row.value.created_at) : new Date()),
+      title : ddoc.blog.title,
+      feed_id : path.absolute(indexPath),
+      feed_link : path.absolute(feedPath),
+    });
+    
+    // send the header to the client
+    send(feedHeader);
+
+    // loop over all rows
+    if (row) {
+      do {
+        if (row.value.format == "markdown") {
+          var html = markdown.encode(row.value.body);
+        } else if (row.value.format == "textile") {
+          var html = textile.encode(row.value.body);
+        } else {
+          var html = Mustache.escape(row.value.html);
+        }
+        // generate the entry for this row
+        var feedEntry = Atom.entry({
+          entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),
+          title : row.value.title,
+          content : html,
+          updated : new Date(row.value.created_at),
+          author : row.value.author,
+          alternate : path.absolute(path.show('post', row.id))
+        });
+        // send the entry to client
+        send(feedEntry);
+      } while (row = getRow());
+    }
+
+    // close the loop after all rows are rendered
+    return "</feed>";
+  });
+};
\ No newline at end of file
diff --git a/web/query/lists/newlists.js b/web/query/lists/newlists.js
new file mode 100644 (file)
index 0000000..5ae5cb1
--- /dev/null
@@ -0,0 +1,377 @@
+function(head, req) {
+  var ddoc = this;
+  var Mustache = require("lib/mustache");
+  var List = require("vendor/couchapp/lib/list");
+  var path = require("vendor/couchapp/lib/path").init(req);
+  var Atom = require("vendor/couchapp/lib/atom");
+
+  var within = 0;
+       if ( 'within' in req.query ) {
+               within = Number(req.query['within']);
+               log("within: " + within);
+       } else {
+               within = 60 * 60 * 3;   // within three hours;
+       }
+  var ts = (new Date()).getTime()/1000 - within; // convert milliseconds to seconds
+
+  var indexPath = path.list('nodelist','node-status',{descending:true, limit:10});
+  var feedPath = path.list('nodelist','node-status',{descending:true, limit:10, format:"atom"});
+  var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
+
+
+  var path_parts = req.path;
+  var EQ=1;
+  var NE=2;
+  var LE=3;
+  var GE=4;
+  var MATCH=5;
+  var AND=6;
+  var OR=7;
+
+  function check_field(query, field, def) { 
+       var ret = def;
+       if ( field in query ) {
+               ret = query[field];
+               log(field + ": " + ret);
+       }
+       return ret;
+  }
+
+  function tokenize(parameter, value, type) {
+       return { 'param' : parameter, 'value' : value, 'type' : type };
+  }
+  function token_get(sf) {
+               var ret = null;
+               var fields = null;
+               var f_eq = /==/;
+               var f_ne = /!=/;
+               var f_le = /<=|%3C=|%3c=/;
+               var f_ge = />=|%3E=|%3e=/;
+               var f_match = /=~/;
+
+               if ( sf.match(f_eq) ) {
+                       fields = sf.split("==");
+                       ret = [fields[0], fields[1], EQ];
+               } else if ( sf.match(f_ne) ) {
+                       fields = sf.split("!=");
+                       ret = [fields[0], fields[1], NE] ;
+               } else if ( sf.match(f_le) ) {
+                       fields = sf.split("<=");
+                       ret = [fields[0], Number(fields[1]), LE] ;
+               } else if ( sf.match(f_ge) ) {
+                       fields = sf.split(">=");
+                       ret = [fields[0], Number(fields[1]), GE] ;
+               } else if ( sf.match(f_match) ) {
+                       fields = sf.split("=~");
+                       ret = [fields[0], new RegExp(fields[1]), MATCH] ;
+               }
+               return ret;
+  }
+  function token_conjunction_get(sf) {
+               var f_and = /AND|%%|&&/;
+               var f_or = /OR|\|\|/;
+
+               var l = [];
+               var type=AND;
+               var fields = null;
+
+               log("sfilter: " + sf);
+               if ( sf.match(f_and) ) {
+                       fields = sf.split(f_and);
+                       type = AND;
+
+               } else if ( sf.match(f_or) ) {
+                       fields = sf.split(f_or);
+                       type = OR;
+
+               } else {
+                       type = AND;
+                       fields = [ sf ];
+               }
+
+               for ( var i=0; i < fields.length ; i++)
+               {
+                       l.push(token_get(fields[i]));
+               }
+               return {'list' : l, 'type' : type};
+  }
+
+  function token_test(token, row)
+  {
+       var     prop = token[0];
+       var val = token[1];
+       var op = token[2];
+       var result_minor = true;
+
+               if ( op == EQ ) {
+                       result_minor &= val == row[prop];
+               } else if ( op == NE ) {
+                       result_minor &= val != row[prop];
+               } else if ( op == LE && prop in row ) {
+                       result_minor &= Number(row[prop]) <= val;
+               } else if ( op == GE && prop in row ) {
+                       result_minor &= Number(row[prop]) >= val;
+               } else if ( op == MATCH && prop in row ) {
+                       if ( typeof(row[prop]) == "object" ) {
+                               var found = false;
+                               for (var i; i < row[prop].length ; i++ ) {
+                                       if ( val.test(row[prop][i]) ) { 
+                                               found = true;
+                                               break; 
+                                       }
+                               }
+                               result_minor &= found;
+                       } else {
+                               result_minor &= val.test(row[prop]);
+                       }
+               } else {
+                       result_minor = false;
+               }
+       return result_minor;
+  }
+
+  function parse_filter (sfilter) { 
+
+               log("sfilter: " + sfilter);
+               var ret = token_conjunction_get(sfilter);
+               return ret;
+  } 
+
+  function filter_matches (row, pfilter) { 
+               var result_minor = true;
+               var result = true;
+               if ( pfilter != null ) {
+
+                       // set defaults based on conjunction type
+                       if ( pfilter['type'] == AND ) {
+                               result = true;
+                       } else if ( pfilter['type'] == OR ) {
+                               result = false;
+                       }
+
+                       for ( var i=0; i < pfilter['list'].length ; i++ )
+                       {
+                               tok = pfilter['list'][i];
+                               result_minor = token_test(tok, row);
+
+                               if (pfilter['type'] == AND ) {
+                                       result &= result_minor;
+                               } else if ( pfilter['type'] == OR ) {
+                                       result |= result_minor;
+                               }
+                       }
+                       //log("filter:" + JSON.stringify(pfilter) + " result: " +  result);
+
+               }
+               return result; 
+       } 
+  // The provides function serves the format the client requests.
+  // The first matching format is sent, so reordering functions changes 
+  // thier priority. In this case HTML is the preferred format, so it comes first.
+  provides("text", function() {
+               var row, first_row = true;
+               var skip_header = false;
+               var skip_props = {'_id' : "", '_rev' : "", '_deleted_conflicts' : '',}; // 'hardware' : '', };
+
+               //send(JSON.stringify(req)+'\n');
+               var more_props = true;
+               var header;
+               var count=0;
+               var rowstring;
+               var pfilter = null;
+               var rpmpattern = null;
+               var prop_start = [ "hostname", 'ts' ];
+               var prop_list = {'hostname' : '', 'ts' : ''};
+
+               var show = check_field(req.query, 'show', null);
+               if ( show != null ) { show = Number(show); }
+
+               log("req: ");
+               log(req);
+               log(head);
+
+               if ( 'filter' in req.query ) {
+                       pfilter = parse_filter(req.query['filter']);
+                       log(pfilter);
+               }
+
+               if ( 'rpmpattern' in req.query ) {
+                       rpmpattern = req.query['rpmpattern'];
+                       log("found rpmpattern");
+                       log(rpmpattern);
+               }
+
+
+               if ( 'prop_list' in req.query || 'fields' in req.query ) {
+                       if ( 'prop_list' in req.query ) { 
+                               prop_start = req.query['prop_list'].split(',');
+                       } else {
+                               prop_start = req.query['fields'].split(',');
+                       }
+                       prop_list = {'hostname' : '', 'ts' : ''};
+                       for ( var i=0; i < prop_start.length ; i++) {
+                               prop_list[prop_start[i]] = "";
+                       }
+                       more_props = false;
+               }
+               if ( 'skip_header' in req.query ) { 
+                       skip_header = true;
+               }
+
+               while (row = getRow()) {
+                       if ( row.key[0] < ts ) { continue; }
+                       if ( show != null && count >= show ) { break; }
+
+                       if ( first_row == true )
+                       {
+                               header = "";
+                               // get a list of all properties
+                               for (var prop in row.value ) {
+                                       if ( ! (prop in prop_list) && more_props ) {
+                                               prop_start.push(prop);
+                                               prop_list[prop] = "";
+                                       }
+                               }
+                               if ( ! skip_header ) {
+                                       // create a header for all the data to follow
+                                       for (var i=0 ; i < prop_start.length ; i++) {
+                                               prop = prop_start[i];
+                                               if ( ! (prop in skip_props) ) {
+                                                       header += prop + ',';
+                                               }
+                                       }
+                                       send(header.substring(0,header.length-1) + '\n');
+                               }
+                               first_row = false;
+                       }
+                       rowstring = "";
+
+                       if ( filter_matches(row.value, pfilter) )
+                       {
+                               //send(header.substring(0,-1) + '\n');
+                               // Send the data that matches the filter when present
+                               for (var i=0 ; i < prop_start.length ; i++) {
+                                       prop = prop_start[i];
+                                       if ( ! (prop in skip_props) )
+                                       {
+                                               if ( (prop == 'rpm_versions') && (rpmpattern != null) && ('rpm_versions' in prop_list) ) {
+                                                       if ( typeof(row.value['rpm_versions']) == "object" ) {
+                                                               for (var i; i < row.value['rpm_versions'].length ; i++ ) {
+                                                                       var rpm = row.value['rpm_versions'][i].match(rpmpattern);
+                                                                       if ( rpm ) { break; }
+                                                               }
+                                                               log(rpm);
+                                                               if ( rpm ) { 
+                                                                       rowstring += rpm.join(' ') + ',';
+                                                               } else {
+                                                                       rowstring += ',';
+                                                               }
+                                                       } else {
+                                                               log("not a string");
+                                                               log(typeof(row.value['rpm_versions']));
+                                                               rowstring += ',';
+                                                       }
+                                               } else {
+                                                       if ( typeof(row.value[prop]) == "object" )
+                                                       {
+                                                               rowstring += row.value[prop].join(" ") + ',';
+                                                       } else {
+                                                               rowstring += row.value[prop] + ',';
+                                                       }
+                                               }
+                                       }
+                               }
+                               send(rowstring.substring(0,rowstring.length-1) + '\n');
+                               count += 1;
+                       }
+               } 
+               // tail 
+               return '\n';
+  });
+  provides("3mtl", function() {
+    var key = "";
+    // render the html head using a template
+    var stash = {
+      header : {
+        index : indexPath,
+        blogName : ddoc.blog.title,
+        feedPath : feedPath,
+        commentsFeed : commentsFeed
+      },
+      scripts : {},
+      db : req.path[0],
+      design : req.path[2],
+      feedPath : feedPath,
+      newPostPath : path.show("edit"),
+      assets : path.asset(),
+      nodes : List.withRows(function(row) {
+        var node = row.value;
+        key = row.key;
+        return {
+          name : node.hostname,
+          site : node.site,
+          date : node.date_created,
+          link : path.list('node','node-page', {startkey : [row.id]}),
+          has_slices : false,
+                 slices : [],
+        };
+      }),
+      older : function() {
+        return path.older(key);
+      },
+      "5" : path.limit(5),
+      "10" : path.limit(10),
+      "25" : path.limit(25)
+    };
+    return Mustache.to_html(ddoc.templates.nodelist, stash, ddoc.templates.partials, List.send);
+  });
+
+  // if the client requests an atom feed and not html, 
+  // we run this function to generate the feed.
+  provides("atom", function() {    
+    var path = require("vendor/couchapp/lib/path").init(req);
+    var markdown = require("vendor/couchapp/lib/markdown");
+    var textile = require("vendor/textile/textile");
+
+    // we load the first row to find the most recent change date
+    var row = getRow();
+    
+    // generate the feed header
+    var feedHeader = Atom.header({
+      updated : (row ? new Date(row.value.created_at) : new Date()),
+      title : ddoc.blog.title,
+      feed_id : path.absolute(indexPath),
+      feed_link : path.absolute(feedPath),
+    });
+    
+    // send the header to the client
+    send(feedHeader);
+
+    // loop over all rows
+    if (row) {
+      do {
+        if (row.value.format == "markdown") {
+          var html = markdown.encode(row.value.body);
+        } else if (row.value.format == "textile") {
+          var html = textile.encode(row.value.body);
+        } else {
+          var html = Mustache.escape(row.value.html);
+        }
+        // generate the entry for this row
+        var feedEntry = Atom.entry({
+          entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),
+          title : row.value.title,
+          content : html,
+          updated : new Date(row.value.created_at),
+          author : row.value.author,
+          alternate : path.absolute(path.show('post', row.id))
+        });
+        // send the entry to client
+        send(feedEntry);
+      } while (row = getRow());
+    }
+
+    // close the loop after all rows are rendered
+    return "</feed>";
+  });
+};
diff --git a/web/query/lists/nodelist.js b/web/query/lists/nodelist.js
new file mode 100644 (file)
index 0000000..f506128
--- /dev/null
@@ -0,0 +1,358 @@
+function(head, req) {
+  var ddoc = this;
+  var Mustache = require("lib/mustache");
+  var List = require("vendor/couchapp/lib/list");
+  var path = require("vendor/couchapp/lib/path").init(req);
+  var Atom = require("vendor/couchapp/lib/atom");
+
+  var within = 0;
+       if ( 'within' in req.query ) {
+               within = Number(req.query['within']);
+               log("within: " + within);
+       } else {
+               within = 60 * 60 * 3;   // within three hours;
+       }
+  var ts = (new Date()).getTime()/1000 - within; // convert milliseconds to seconds
+
+  var indexPath = path.list('nodelist','node-status',{descending:true, limit:10});
+  var feedPath = path.list('nodelist','node-status',{descending:true, limit:10, format:"atom"});
+  var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
+
+  var path_parts = req.path;
+  var EQ=1;
+  var NE=2;
+  var LE=3;
+  var GE=4;
+  var MATCH=5;
+  var AND=6;
+  var OR=7;
+  var NOT_MATCH=8;
+
+  function check_field(query, field, def) { 
+       var ret = def;
+       if ( field in query ) {
+               ret = query[field];
+               log(field + ": " + ret);
+       }
+       return ret;
+  }
+
+  function token_get(sf) {
+               var ret = null;
+               var fields = null;
+               var r = null;
+               var f_eq = /==/;
+               var f_ne = /!=/;
+               var f_le = /<=|%3C=|%3c=/;
+               var f_ge = />=|%3E=|%3e=/;
+               var f_match = /=~/;
+               var f_not_match = /!~/;
+
+               if ( sf.match(f_eq) ) {
+                       fields = sf.split("==");
+                       ret = [fields[0], fields[1], EQ];
+               } else if ( sf.match(f_ne) ) {
+                       fields = sf.split("!=");
+                       ret = [fields[0], fields[1], NE] ;
+               } else if ( sf.match(f_le) ) {
+                       fields = sf.split("<=");
+                       ret = [fields[0], Number(fields[1]), LE] ;
+               } else if ( sf.match(f_ge) ) {
+                       fields = sf.split(">=");
+                       ret = [fields[0], Number(fields[1]), GE] ;
+               } else if ( sf.match(f_not_match) ) {
+                       fields = sf.split("!~");
+                       r = fields[1];
+                       ret = [fields[0], r, NOT_MATCH] ;
+
+               } else if ( sf.match(f_match) ) {
+                       log("sf: " + sf);
+                       fields = sf.split("=~");
+
+                       log("fields: " + fields);
+                       //r = RegExp(fields[1]);
+                       r = fields[1];
+                       log("regex: " + r);
+                       ret = [fields[0], r, MATCH] ;
+                       log("ret: " + ret);
+               } else {
+                       log("no match!")
+               }
+               return ret;
+  }
+  function token_conjunction_get(sf) {
+               var f_and = /AND|%%|&&/;
+               var f_or = /OR|\|\|/;
+
+               var l = [];
+               var tcg = {}; 
+               var type=AND;
+               var fields = null;
+
+               log("sfilter: " + sf);
+               if ( sf.match(f_and) ) {
+                       fields = sf.split(f_and);
+                       type = AND;
+
+               } else if ( sf.match(f_or) ) {
+                       fields = sf.split(f_or);
+                       type = OR;
+
+               } else {
+                       type = AND;
+                       fields = [ sf ];
+               }
+
+               for ( var i=0; i < fields.length ; i++)
+               {
+                       var t = token_get(fields[i]);
+                       log("t:" + t);
+                       l.push(t);
+               }
+               log("l:" + l);
+               tcg['list'] = l;
+               tcg['type'] = type;
+               log("tcg:" + JSON.stringify(tcg));
+               return tcg;
+  }
+
+  function token_test(token, row)
+  {
+       var     prop = token[0];
+       var val = token[1];
+       var op = token[2];
+       var result_minor = true;
+
+               if ( op == EQ ) {
+                       result_minor &= val == row[prop];
+               } else if ( op == NE ) {
+                       result_minor &= val != row[prop];
+               } else if ( op == LE && prop in row ) {
+                       result_minor &= Number(row[prop]) <= val;
+                       //result_minor &= parseFloat(row[prop]) <= val;
+               } else if ( op == GE && prop in row ) {
+                       //#result_minor &= Number(row[prop]) >= val;
+                       result_minor &= parseFloat(row[prop]) >= val;
+               } else if ( ( op == MATCH || op == NOT_MATCH ) && prop in row ) {
+                       val = new RegExp(val);
+                       if ( typeof(row[prop]) == "object" ) {
+                               var found = false;
+                               for (var i=0; i < row[prop].length ; i++ ) {
+                                       if ( val.test(row[prop][i]) ) { 
+                                               found = true;
+                                               break; 
+                                       }
+                               }
+                               result_minor &= found;
+                       } else {
+                               result_minor &= val.test(row[prop]);
+                       }
+                       if ( op == NOT_MATCH ) {
+                               // reverse the result if we actually want a non-match
+                           result_minor = !result_minor;
+                       }
+               } else {
+                       result_minor = false;
+               }
+       return result_minor;
+  }
+
+  function parse_filter (sfilter) { 
+
+               log("sfilter: " + sfilter);
+               if ( sfilter == "" ) { return null; }
+               var ret = token_conjunction_get(sfilter);
+               log("tcg2: " + JSON.stringify(ret));
+               return ret;
+  } 
+
+  function filter_matches (row, pfilter) { 
+               var result_minor = true;
+               var result = true;
+               if ( pfilter != null ) {
+
+                       // set defaults based on conjunction type
+                       if ( pfilter['type'] == AND ) {
+                               result = true;
+                       } else if ( pfilter['type'] == OR ) {
+                               result = false;
+                       }
+
+                       for ( var i=0; i < pfilter['list'].length ; i++ )
+                       {
+                               tok = pfilter['list'][i];
+                               result_minor = token_test(tok, row);
+
+                               if (pfilter['type'] == AND ) {
+                                       result &= result_minor;
+                               } else if ( pfilter['type'] == OR ) {
+                                       result |= result_minor;
+                               }
+                       }
+                       //log("filter:" + JSON.stringify(pfilter) + " result: " +  result);
+
+               }
+               return result; 
+       } 
+  // The provides function serves the format the client requests.
+  // The first matching format is sent, so reordering functions changes 
+  // thier priority. In this case HTML is the preferred format, so it comes first.
+  provides("text", function() {
+               var row, first_row = true;
+               var skip_header = false;
+               var skip_props = {'_id' : "", '_rev' : "", '_deleted_conflicts' : '',}; // 'hardware' : '', };
+
+               var more_props = true;
+               var header;
+               var count=0;
+               var rowstring;
+               var pfilter = null;
+               var pattern = null;
+               var prop_start = [ "hostname", 'ts' ];
+               var prop_list = {'hostname' : '', 'ts' : ''};
+               var show = check_field(req.query, 'show', null);
+
+               if ( show != null ) { show = Number(show); }
+
+               log("req: ");
+               log(req);
+               log(head);
+
+               if ( 'filter' in req.query ) {
+                       pfilter = parse_filter(req.query['filter']);
+                       log("pf: " + JSON.stringify(pfilter));
+               }
+
+               if ( 'rpmpattern' in req.query ) {
+                       pattern = req.query['rpmpattern'];
+                       log("found pattern");
+                       log(pattern);
+               }
+
+               if ( 'prop_list' in req.query || 'fields' in req.query ) {
+                       if ( 'prop_list' in req.query ) { 
+                               prop_start = req.query['prop_list'].split(',');
+                       } else {
+                               prop_start = req.query['fields'].split(',');
+                       }
+                       prop_list = {'hostname' : '', 'ts' : ''};
+                       for ( var i=0; i < prop_start.length ; i++) {
+                               prop_list[prop_start[i]] = "";
+                       }
+                       more_props = false;
+               }
+               if ( 'skip_header' in req.query ) { 
+                       skip_header = true;
+               }
+
+               while (row = getRow()) {
+                       if ( row.key[0] < ts ) { continue; }
+                       if ( show != null && count >= show ) { break; }
+
+                       if ( first_row == true )
+                       {
+                               header = "";
+                               // get a list of all properties
+                               for (var prop in row.value ) {
+                                       if ( ! (prop in prop_list) && more_props ) {
+                                               prop_start.push(prop);
+                                               prop_list[prop] = "";
+                                       }
+                               }
+                               if ( ! skip_header ) {
+                                       // create a header for all the data to follow
+                                       for (var i=0 ; i < prop_start.length ; i++) {
+                                               prop = prop_start[i];
+                                               if ( ! (prop in skip_props) ) {
+                                                       header += prop + ',';
+                                               }
+                                       }
+                                       send(header.substring(0,header.length-1) + '\n');
+                               }
+                               first_row = false;
+                       }
+                       rowstring = "";
+
+                       if ( filter_matches(row.value, pfilter) )
+                       {
+                               //send(header.substring(0,-1) + '\n');
+                               // Send the data that matches the filter when present
+                               for (var i=0 ; i < prop_start.length ; i++) {
+                                       prop = prop_start[i];
+                                       if ( ! (prop in skip_props) )
+                                       {
+                                               if ( (prop == 'rpm_versions') && (pattern != null) && ('rpm_versions' in prop_list) ) {
+                                                       if ( typeof(row.value['rpm_versions']) == "object" ) {
+                                                               for (var i; i < row.value['rpm_versions'].length ; i++ ) {
+                                                                       var rpm = row.value['rpm_versions'][i].match(pattern);
+                                                                       if ( rpm ) { break; }
+                                                               }
+                                                               log(rpm);
+                                                               if ( rpm ) { 
+                                                                       rowstring += rpm.join(' ') + ',';
+                                                               } else {
+                                                                       rowstring += ',';
+                                                               }
+                                                       } else {
+                                                               log("not a string");
+                                                               log(typeof(row.value['rpm_versions']));
+                                                               rowstring += ',';
+                                                       }
+                                               //if ( false ) {
+                                               //      log("test");
+                                               } else {
+                                                       if ( typeof(row.value[prop]) == "object" )
+                                                       {
+                                                               rowstring += row.value[prop].join(" ") + ',';
+                                                       } else {
+                                                               rowstring += row.value[prop] + ',';
+                                                       }
+                                               }
+                                       }
+                               }
+                               send(rowstring.substring(0,rowstring.length-1) + '\n');
+                               count += 1;
+                       }
+               } 
+               // tail 
+               return '\n';
+  });
+  provides("3mtl", function() {
+    var key = "";
+    // render the html head using a template
+    var stash = {
+      header : {
+        index : indexPath,
+        blogName : ddoc.blog.title,
+        feedPath : feedPath,
+        commentsFeed : commentsFeed
+      },
+      scripts : {},
+      db : req.path[0],
+      design : req.path[2],
+      feedPath : feedPath,
+      newPostPath : path.show("edit"),
+      assets : path.asset(),
+      nodes : List.withRows(function(row) {
+        var node = row.value;
+        key = row.key;
+        return {
+          name : node.hostname,
+          site : node.site,
+          date : node.date_created,
+          link : path.list('node','node-page', {startkey : [row.id]}),
+          has_slices : false,
+                 slices : [],
+        };
+      }),
+      older : function() {
+        return path.older(key);
+      },
+      "5" : path.limit(5),
+      "10" : path.limit(10),
+      "25" : path.limit(25)
+    };
+    return Mustache.to_html(ddoc.templates.nodelist, stash, ddoc.templates.partials, List.send);
+  });
+
+};
diff --git a/web/query/lists/nodelist.js.backup b/web/query/lists/nodelist.js.backup
new file mode 100644 (file)
index 0000000..8cf74a7
--- /dev/null
@@ -0,0 +1,377 @@
+function(head, req) {
+  var ddoc = this;
+  var Mustache = require("lib/mustache");
+  var List = require("vendor/couchapp/lib/list");
+  var path = require("vendor/couchapp/lib/path").init(req);
+  var Atom = require("vendor/couchapp/lib/atom");
+
+  var within = 0;
+       if ( 'within' in req.query ) {
+               within = Number(req.query['within']);
+               log("within: " + within);
+       } else {
+               within = 60 * 60 * 3;   // within three hours;
+       }
+  var ts = (new Date()).getTime()/1000 - within; // convert milliseconds to seconds
+
+  var indexPath = path.list('nodelist','node-status',{descending:true, limit:10});
+  var feedPath = path.list('nodelist','node-status',{descending:true, limit:10, format:"atom"});
+  var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
+
+
+  var path_parts = req.path;
+  var EQ=1;
+  var NE=2;
+  var LE=3;
+  var GE=4;
+  var MATCH=5;
+  var AND=6;
+  var OR=7;
+
+  function check_field(query, field, def) { 
+       var ret = def;
+       if ( field in query ) {
+               ret = query[field];
+               log(field + ": " + ret);
+       }
+       return ret;
+  }
+
+  function tokenize(sf) {
+               var ret = null;
+               var fields = null;
+               var f_eq = /==/;
+               var f_ne = /!=/;
+               var f_le = /<=|%3C=|%3c=/;
+               var f_ge = />=|%3E=|%3e=/;
+               var f_match = /=~/;
+
+               if ( sf.match(f_eq) ) {
+                       fields = sf.split("==");
+                       ret = [fields[0], fields[1], EQ];
+               } else if ( sf.match(f_ne) ) {
+                       fields = sf.split("!=");
+                       ret = [fields[0], fields[1], NE] ;
+               } else if ( sf.match(f_le) ) {
+                       fields = sf.split("<=");
+                       ret = [fields[0], Number(fields[1]), LE] ;
+               } else if ( sf.match(f_ge) ) {
+                       fields = sf.split(">=");
+                       ret = [fields[0], Number(fields[1]), GE] ;
+               } else if ( sf.match(f_match) ) {
+                       fields = sf.split("=~");
+                       ret = [fields[0], new RegExp(fields[1]), MATCH] ;
+               }
+               return ret;
+  }
+
+
+  function pf(x) { return 0; };
+  function parse_filter (sfilter) { 
+               var f_and_or = /AND|%%|&&|\|\|/;
+               var f_eq = /==/;
+               var f_ne = /!=/;
+               var f_le = /<=|%3C=|%3c=/;
+               var f_ge = />=|%3E=|%3e=/;
+               var f_match = /=~/;
+               var eq = {};
+               var kv_list = [ ];
+               var pfilter = [ ] ;
+
+               log("sfilter: " + sfilter);
+               if ( sfilter.match(f_and_or) )
+               {
+                       sfilter_list = sfilter.split(f_and_or);
+                       log("sfilter_list: " + sfilter_list);
+               } else {
+                       sfilter_list = [ sfilter ] ;
+               }
+
+               for ( var i=0; i < sfilter_list.length ; i++ ) 
+               {
+                       sf = sfilter_list[i];
+                       t = tokenize(sf);
+                       kv_list[i] = t;
+
+                       if ( sf.match(f_eq) ) {
+                               var fields = sf.split("==");
+                               eq[fields[0]] = [fields[1], EQ] ;
+                       } else if ( sf.match(f_ne) ) {
+                               var fields = sf.split("!=");
+                               eq[fields[0]] = [fields[1], NE] ;
+                       } else if ( sf.match(f_le) ) {
+                               var fields = sf.split("<=");
+                               eq[fields[0]] = [Number(fields[1]), LE] ;
+                       } else if ( sf.match(f_ge) ) {
+                               var fields = sf.split(">=");
+                               eq[fields[0]] = [Number(fields[1]), GE] ;
+                       } else if ( sf.match(f_match) ) {
+                               var fields = sf.split("=~");
+                               eq[fields[0]] = [new RegExp(fields[1]), MATCH] ;
+                       }
+               }
+               pfilter = [ eq ];
+               //log("kv_list: " + JSON.stringify(kv_list));
+               //pfilter = [ kv_list, AND ];
+
+               return pfilter;
+       } 
+
+  function filter_matches (row, pfilter) { 
+               var result = true;
+               if ( pfilter != null ) {
+                       for ( var i=0; i < pfilter.length ; i++ )
+                       {
+                               //log("starting filter_matches");
+
+                               for ( var prop in pfilter[i] ) {
+                                       f = pfilter[i][prop];
+                                       //for ( var p in row ) {
+                                       //      log("row[" + p + "] == " + row[p]);
+                                       //}
+
+                                       //log("filter:" + f);
+                                       //log("row[prop]: " + row[prop]);
+                                       //log("typeof(row[prop]): " + typeof(row[prop]));
+                                       if ( f[1] == EQ )
+                                       {
+                                               result &= f[0] == row[prop];
+                                       } else if ( f[1] == NE ) {
+                                               result &= f[0] != row[prop];
+                                       } else if ( f[1] == LE && prop in row ) {
+                                               result &= Number(row[prop]) <= f[0];
+                                       } else if ( f[1] == GE && prop in row ) {
+                                               result &= Number(row[prop]) >= f[0];
+                                       } else if ( f[1] == MATCH && prop in row ) {
+                                               if ( typeof(row[prop]) == "object" ) {
+                                                       var found = false;
+                                                       for (var i; i < row[prop].length ; i++ ) {
+                                                               if ( f[0].test(row[prop][i]) ) { 
+                                                                       found = true;
+                                                                       break; 
+                                                               }
+                                                       }
+                                                       result &= found;
+                                               } else {
+                                                       result &= f[0].test(row[prop]);
+                                               }
+                                       } else {
+                                               result = false;
+                                       }
+                                       //log("result: " + result);
+                               }
+                       }
+                       //log("filter:" + JSON.stringify(pfilter) + " result: " +  result);
+               }
+               return result; 
+       } 
+  // The provides function serves the format the client requests.
+  // The first matching format is sent, so reordering functions changes 
+  // thier priority. In this case HTML is the preferred format, so it comes first.
+  provides("text", function() {
+               var row, first_row = true;
+               var skip_header = false;
+               var skip_props = {'_id' : "", '_rev' : "", '_deleted_conflicts' : '',}; // 'hardware' : '', };
+
+               //send(JSON.stringify(req)+'\n');
+               var more_props = true;
+               var header;
+               var count=0;
+               var rowstring;
+               var pfilter = null;
+               var rpmpattern = null;
+               var prop_start = [ "hostname", 'ts' ];
+               var prop_list = {'hostname' : '', 'ts' : ''};
+
+               var show = check_field(req.query, 'show', null);
+               if ( show != null ) { show = Number(show); }
+
+               log("req: ");
+               log(req);
+               log(head);
+
+               if ( 'filter' in req.query ) {
+                       pfilter = parse_filter(req.query['filter']);
+                       log(pfilter);
+               }
+
+               if ( 'rpmpattern' in req.query ) {
+                       rpmpattern = req.query['rpmpattern'];
+                       log("found rpmpattern");
+                       log(rpmpattern);
+               }
+
+
+               if ( 'prop_list' in req.query || 'fields' in req.query ) {
+                       if ( 'prop_list' in req.query ) { 
+                               prop_start = req.query['prop_list'].split(',');
+                       } else {
+                               prop_start = req.query['fields'].split(',');
+                       }
+                       prop_list = {'hostname' : '', 'ts' : ''};
+                       for ( var i=0; i < prop_start.length ; i++) {
+                               prop_list[prop_start[i]] = "";
+                       }
+                       more_props = false;
+               }
+               if ( 'skip_header' in req.query ) { 
+                       skip_header = true;
+               }
+
+               while (row = getRow()) {
+                       if ( row.key[0] < ts ) { continue; }
+                       if ( show != null && count >= show ) { break; }
+
+                       if ( first_row == true )
+                       {
+                               header = "";
+                               // get a list of all properties
+                               for (var prop in row.value ) {
+                                       if ( ! (prop in prop_list) && more_props ) {
+                                               prop_start.push(prop);
+                                               prop_list[prop] = "";
+                                       }
+                               }
+                               if ( ! skip_header ) {
+                                       // create a header for all the data to follow
+                                       for (var i=0 ; i < prop_start.length ; i++) {
+                                               prop = prop_start[i];
+                                               if ( ! (prop in skip_props) ) {
+                                                       header += prop + ',';
+                                               }
+                                       }
+                                       send(header.substring(0,header.length-1) + '\n');
+                               }
+                               first_row = false;
+                       }
+                       rowstring = "";
+
+                       if ( filter_matches(row.value, pfilter) )
+                       {
+                               //send(header.substring(0,-1) + '\n');
+                               // Send the data that matches the filter when present
+                               for (var i=0 ; i < prop_start.length ; i++) {
+                                       prop = prop_start[i];
+                                       if ( ! (prop in skip_props) )
+                                       {
+                                               if ( (prop == 'rpm_versions') && (rpmpattern != null) && ('rpm_versions' in prop_list) ) {
+                                                       if ( typeof(row.value['rpm_versions']) == "object" ) {
+                                                               for (var i; i < row.value['rpm_versions'].length ; i++ ) {
+                                                                       var rpm = row.value['rpm_versions'][i].match(rpmpattern);
+                                                                       if ( rpm ) { break; }
+                                                               }
+                                                               log(rpm);
+                                                               if ( rpm ) { 
+                                                                       rowstring += rpm.join(' ') + ',';
+                                                               } else {
+                                                                       rowstring += ',';
+                                                               }
+                                                       } else {
+                                                               log("not a string");
+                                                               log(typeof(row.value['rpm_versions']));
+                                                               rowstring += ',';
+                                                       }
+                                               } else {
+                                                       if ( typeof(row.value[prop]) == "object" )
+                                                       {
+                                                               rowstring += row.value[prop].join(" ") + ',';
+                                                       } else {
+                                                               rowstring += row.value[prop] + ',';
+                                                       }
+                                               }
+                                       }
+                               }
+                               send(rowstring.substring(0,rowstring.length-1) + '\n');
+                               count += 1;
+                       }
+               } 
+               // tail 
+               return '\n';
+  });
+  provides("3mtl", function() {
+    var key = "";
+    // render the html head using a template
+    var stash = {
+      header : {
+        index : indexPath,
+        blogName : ddoc.blog.title,
+        feedPath : feedPath,
+        commentsFeed : commentsFeed
+      },
+      scripts : {},
+      db : req.path[0],
+      design : req.path[2],
+      feedPath : feedPath,
+      newPostPath : path.show("edit"),
+      assets : path.asset(),
+      nodes : List.withRows(function(row) {
+        var node = row.value;
+        key = row.key;
+        return {
+          name : node.hostname,
+          site : node.site,
+          date : node.date_created,
+          link : path.list('node','node-page', {startkey : [row.id]}),
+          has_slices : false,
+                 slices : [],
+        };
+      }),
+      older : function() {
+        return path.older(key);
+      },
+      "5" : path.limit(5),
+      "10" : path.limit(10),
+      "25" : path.limit(25)
+    };
+    return Mustache.to_html(ddoc.templates.nodelist, stash, ddoc.templates.partials, List.send);
+  });
+
+  // if the client requests an atom feed and not html, 
+  // we run this function to generate the feed.
+  provides("atom", function() {    
+    var path = require("vendor/couchapp/lib/path").init(req);
+    var markdown = require("vendor/couchapp/lib/markdown");
+    var textile = require("vendor/textile/textile");
+
+    // we load the first row to find the most recent change date
+    var row = getRow();
+    
+    // generate the feed header
+    var feedHeader = Atom.header({
+      updated : (row ? new Date(row.value.created_at) : new Date()),
+      title : ddoc.blog.title,
+      feed_id : path.absolute(indexPath),
+      feed_link : path.absolute(feedPath),
+    });
+    
+    // send the header to the client
+    send(feedHeader);
+
+    // loop over all rows
+    if (row) {
+      do {
+        if (row.value.format == "markdown") {
+          var html = markdown.encode(row.value.body);
+        } else if (row.value.format == "textile") {
+          var html = textile.encode(row.value.body);
+        } else {
+          var html = Mustache.escape(row.value.html);
+        }
+        // generate the entry for this row
+        var feedEntry = Atom.entry({
+          entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),
+          title : row.value.title,
+          content : html,
+          updated : new Date(row.value.created_at),
+          author : row.value.author,
+          alternate : path.absolute(path.show('post', row.id))
+        });
+        // send the entry to client
+        send(feedEntry);
+      } while (row = getRow());
+    }
+
+    // close the loop after all rows are rendered
+    return "</feed>";
+  });
+};
diff --git a/web/query/lists/post.js b/web/query/lists/post.js
new file mode 100644 (file)
index 0000000..6f7426a
--- /dev/null
@@ -0,0 +1,59 @@
+function(head, req) {
+  var Mustache = require("lib/mustache");
+  var ddoc = this;
+  var List = require("vendor/couchapp/lib/list");
+  var path = require("vendor/couchapp/lib/path").init(req);
+  var markdown = require("vendor/couchapp/lib/markdown");
+  var textile = require("vendor/textile/textile");
+
+  var indexPath = path.list('index','recent-posts',{descending:true, limit:10});
+  var feedPath = path.list('index','recent-posts',{descending:true, limit:10, format:"atom"});
+  var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
+  
+  provides("html", function() {
+    // get the first row and make sure it's a post
+    var post = getRow().value;
+    if (post.type != "post") {
+      throw(["error", "not_found", "not a post"]);
+    } else {
+      if (post.format == "markdown") {
+        var html = markdown.encode(post.body);
+      } else if (post.format == "textile") {
+        var html = textile.encode(post.body);
+      } else {
+        var html = Mustache.escape(post.html);
+      }
+
+      var stash = {
+        header : {
+          index : indexPath,
+          blogName : ddoc.blog.title,
+          feedPath : feedPath,
+          commentsFeed : commentsFeed
+        },
+        scripts : {},
+        title : post.title,
+        post_id : post._id,
+        date : post.created_at,
+        html : html,
+        comments : List.withRows(function(row) {
+          var v = row.value;
+          if (v.type != "comment") {
+            return;
+          }
+          // keep getting comments until we get to the next post...
+          return {
+            comment : {
+              name : v.commenter.nickname || v.commenter.name,
+              url : v.commenter.url,
+              avatar : v.commenter.gravatar_url || 'http://www.gravatar.com/avatar/'+v.commenter.gravatar+'.jpg?s=40&d=identicon',
+              html : markdown.encode(Mustache.escape(v.comment)),
+              created_at : v.created_at
+            }
+          };
+        })
+      };
+      return Mustache.to_html(ddoc.templates.post, stash, ddoc.templates.partials, List.send);   
+    }
+  });
+}
\ No newline at end of file
diff --git a/web/query/lists/rpms.js b/web/query/lists/rpms.js
new file mode 100644 (file)
index 0000000..cbb553b
--- /dev/null
@@ -0,0 +1,129 @@
+function(head, req) {
+  var ddoc = this;
+  var Mustache = require("lib/mustache");
+  var List = require("vendor/couchapp/lib/list");
+  var path = require("vendor/couchapp/lib/path").init(req);
+  var Atom = require("vendor/couchapp/lib/atom");
+
+  var within = 0;
+       if ( 'within' in req.query ) {
+               within = Number(req.query['within']);
+               log("within: " + within);
+       } else {
+               within = 60 * 60 * 24;  // within the last day.
+       }
+  var ts = (new Date()).getTime()/1000 - within; // convert milliseconds to seconds
+
+  var indexPath = path.list('rpms','rpm-to-host',{descending:true, limit:10});
+  var feedPath = path.list('rpms','rpm-to-host',{descending:true, limit:10, format:"atom"});
+  var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
+
+  function check_field(query, field, def) { 
+       var ret = def;
+       if ( field in query ) {
+               ret = query[field];
+               log(field + ": " + ret);
+       }
+       return ret;
+  }
+
+  var path_parts = req.path;
+
+  // The first matching format is sent, so reordering functions changes 
+  // thier priority. In this case HTML is the preferred format, so it comes first.
+  provides("text", function() {
+               var row, first_row = true;
+               var more_props = true;
+               var header;
+               var hostname = check_field(req.query, 'hostname', false);
+               var rpm = check_field(req.query, 'rpm', false);
+               var show_hosts = check_field(req.query, 'show_hosts', false);
+               var show_rpms = check_field(req.query, 'show_rpms', false);
+               while (row = getRow()) {
+                       if ( row.key[0] < ts ) { continue; }
+               } 
+
+               return '\n';
+  });
+  provides("html", function() {
+    var key = "";
+    // render the html head using a template
+       //
+    var stash = {
+      header : {
+           title : 'blah blah blah',
+        index : indexPath,
+        blogName : ddoc.blog.title,
+        feedPath : feedPath,
+        commentsFeed : commentsFeed
+      },
+      scripts : {},
+      db : req.path[0],
+      design : req.path[2],
+      feedPath : feedPath,
+      newPostPath : path.show("edit"),
+      assets : path.asset(),
+         rpmversion : check_field(req.query, 'rpmversion', 'List all'),
+
+      nodes : List.withRows(function(row) {
+        var node = row.value;
+        key = row.key;
+        return {
+          name : key[1],
+          val  : row.value,
+          link : path.list('rpms','rpm-to-host', {rpmversion : key[1], group : true, startkey : [key[1]], endkey : [key[1],{}]}),
+        };
+      }),
+    };
+    return Mustache.to_html(ddoc.templates.rpms, stash, ddoc.templates.partials, List.send);
+  });
+
+  // if the client requests an atom feed and not html, 
+  // we run this function to generate the feed.
+  provides("atom", function() {    
+    var path = require("vendor/couchapp/lib/path").init(req);
+    var markdown = require("vendor/couchapp/lib/markdown");
+    var textile = require("vendor/textile/textile");
+
+    // we load the first row to find the most recent change date
+    var row = getRow();
+    
+    // generate the feed header
+    var feedHeader = Atom.header({
+      updated : (row ? new Date(row.value.created_at) : new Date()),
+      title : ddoc.blog.title,
+      feed_id : path.absolute(indexPath),
+      feed_link : path.absolute(feedPath),
+    });
+    
+    // send the header to the client
+    send(feedHeader);
+
+    // loop over all rows
+    if (row) {
+      do {
+        if (row.value.format == "markdown") {
+          var html = markdown.encode(row.value.body);
+        } else if (row.value.format == "textile") {
+          var html = textile.encode(row.value.body);
+        } else {
+          var html = Mustache.escape(row.value.html);
+        }
+        // generate the entry for this row
+        var feedEntry = Atom.entry({
+          entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),
+          title : row.value.title,
+          content : html,
+          updated : new Date(row.value.created_at),
+          author : row.value.author,
+          alternate : path.absolute(path.show('post', row.id))
+        });
+        // send the entry to client
+        send(feedEntry);
+      } while (row = getRow());
+    }
+
+    // close the loop after all rows are rendered
+    return "</feed>";
+  });
+};
diff --git a/web/query/out.hardware b/web/query/out.hardware
new file mode 100644 (file)
index 0000000..42f9b71
--- /dev/null
@@ -0,0 +1,630 @@
+hostname,hardware
+planetlab-04.naist.jp,H/W path Device Class Description|=======================================================| system ProLiant DL320 G3|/0 bus Motherboard|/0/0 memory 64KB BIOS|/0/400 processor Intel(R) Pentium(R) 4 CPU 3.40GHz|/0/400/710 memory 16KB L1 cache|/0/400/720 memory 2MB L2 cache|/0/400/730 memory L3 cache|/0/400/0.1 processor Logical CPU|/0/400/0.2 processor Logical CPU|/0/1000 memory 4GB System Memory|/0/1000/0 memory 1GB DIMM DDR Synchronous 400 MHz (2.5 ns)|/0/1000/1 memory 1GB DIMM DDR Synchronous 400 MHz (2.5 ns)|/0/1000/2 memory 1GB DIMM DDR Synchronous 400 MHz (2.5 ns)|/0/1000/3 memory 1GB DIMM DDR Synchronous 400 MHz (2.5 ns)|/0/100 bridge E7220/E7221 Memory Controller Hub|/0/100/1 bridge E7220/E7221 PCI Express Root Port|/0/100/1/0 bridge 6700PXH PCI Express-to-PCI Bridge A|/0/100/1/0/1 eth0 network NetXtreme BCM5704 Gigabit Ethernet|/0/100/1/0/1.1 eth1 network NetXtreme BCM5704 Gigabit Ethernet|/0/100/1/0.2 bridge 6700PXH PCI Express-to-PCI Bridge B|/0/100/1c bridge 82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 1|/0/100/1d bus 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1|/0/100/1d/1 usb2 bus UHCI Host Controller|/0/100/1d.1 bus 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2|/0/100/1d.1/1 usb3 bus UHCI Host Controller|/0/100/1d.2 bus 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3|/0/100/1d.2/1 usb4 bus UHCI Host Controller|/0/100/1d.3 bus 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4|/0/100/1d.3/1 usb5 bus UHCI Host Controller|/0/100/1d.7 bus 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller|/0/100/1d.7/1 usb1 bus EHCI Host Controller|/0/100/1e bridge 82801 PCI Bridge|/0/100/1e/2 system Integrated Lights Out Controller|/0/100/1e/2.2 system Integrated Lights Out Processor|/0/100/1e/3 display Rage XL|/0/100/1f bridge 82801FB/FR (ICH6/ICH6R) LPC Interface Bridge|/0/100/1f.1 scsi0 storage 82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller|/0/100/1f.1/0.0.0 /dev/cdrom disk DV-28E-N|/0/100/1f.1/0.0.0/0 /dev/cdrom disk |/0/100/1f.2 scsi2 storage 82801FR/FRW (ICH6R/ICH6RW) SATA Controller|/0/100/1f.2/0.0.0 /dev/sda disk 149GB Maxtor 6L160M0|/0/100/1f.2/0.0.0/1 /dev/sda1 volume 149GB Linux LVM Physical Volume partition|
+planetlab1.cs.umass.edu,H/W path Device Class Description|================================================================| system HP Compaq dc7800p Small Form Factor|/0 bus 0AA8h|/0/1 memory 128KB BIOS|/0/5 processor Intel(R) Core(TM)2 Duo CPU E6550 @ 2.33GHz|/0/5/6 memory 64KB L1 cache|/0/5/7 memory 4MB L2 cache|/0/5/8 memory L3 cache|/0/5/1.1 processor Logical CPU|/0/5/1.2 processor Logical CPU|/0/33 memory System Memory|/0/33/0 memory 1GB DIMM Synchronous 667 MHz (1.5 ns)|/0/33/1 memory 1GB DIMM Synchronous 667 MHz (1.5 ns)|/0/33/2 memory 1GB DIMM Synchronous 667 MHz (1.5 ns)|/0/33/3 memory 1GB DIMM Synchronous 667 MHz (1.5 ns)|/0/34 memory Flash Memory|/0/34/0 memory 4MB Chip FLASH Non-volatile|/0/0 memory |/0/2 memory |/0/100 bridge 82Q35 Express DRAM Controller|/0/100/2 display 82Q35 Express Integrated Graphics Controller|/0/100/3 communication 82Q35 Express MEI Controller|/0/100/3.2 storage 82Q35 Express PT IDER Controller|/0/100/3.3 communication 82Q35 Express Serial KT Controller|/0/100/19 eth0 network 82566DM-2 Gigabit Network Connection|/0/100/1a bus 82801I (ICH9 Family) USB UHCI Controller #4|/0/100/1a/1 usb3 bus UHCI Host Controller|/0/100/1a.1 bus 82801I (ICH9 Family) USB UHCI Controller #5|/0/100/1a.1/1 usb4 bus UHCI Host Controller|/0/100/1a.2 bus 82801I (ICH9 Family) USB UHCI Controller #6|/0/100/1a.2/1 usb5 bus UHCI Host Controller|/0/100/1a.7 bus 82801I (ICH9 Family) USB2 EHCI Controller #2|/0/100/1a.7/1 usb1 bus EHCI Host Controller|/0/100/1a.7/1/5 scsi6 generic USB Mass Storage Device|/0/100/1a.7/1/5/0.0.0 /dev/sdb disk 1927MB USB2FlashStorage|/0/100/1a.7/1/5/0.0.0/0 /dev/sdb disk 1927MB|/0/100/1a.7/1/5/0.0.0/0/4 /dev/sdb4 volume 50MB FAT16 partition|/0/100/1b multimedia 82801I (ICH9 Family) HD Audio Controller|/0/100/1c bridge 82801I (ICH9 Family) PCI Express Port 1|/0/100/1c.1 bridge 82801I&n