Installing Open-Xchange 7.8 on UCS when the master is on 4.3

OX is on the verge of releasing Version 7.10. That version will not be supported on (or packaged for) Debian 8 Jessie, but only on Debian 9 Stretch (and of course RedHat and SuSE). On the other hand, 7.8.x will not be supported on (or packaged for) Stretch.

The latest UCS version is 4.3 at the moment. This is based on Debian Stretch.

Therefore the current OX is not supported on UCS 4.3, but according to Open Xchange an  Univention, OX 7.8.4 on UCS 4.2 is supported in an environment with 4.3 servers.

So, when your UCS master is running 4.3, you can theoretically run OX on a 4.2 server. This works fine when you installed OX while the master is still on 4.2 or earlier.

In my case, my master was on 4.3. In order to install OX, I set up a server with UC 4.2 and joined it to the master. This already broke on installation but worked in a later step flawlessly.

Next, I started to install OX:

[root@ucsox:~] # univention-app install oxseforucs  ... Installing some packages of oxseforucs on ucsmaster.mydomain Password for root@ucsmaster.mydomain: Going to install OX App Suite (7.8.4-ucs10) (must_have_fitting_ucs_version) The application requires UCS version 4.2. Unable to install oxseforucs. Aborting... Installing master packages for oxseforucs on ucsmaster.mydomain failed!

Oopsie. But consistent: univention-app list doesn’t show oxseforucs on the master, therefore there are no package sources and no packages. Fsck.

So let’s do this manually:

[root@ucsmaster:~] # ucr set repository/online/component/oxseforucs_20171205095418/description="OX App Suite" \
  repository/online/component/oxseforucs_20171205095418/localmirror=false \
  repository/online/component/oxseforucs_20171205095418/server="" \
  repository/online/component/oxseforucs_20171205095418/unmaintained='disabled' \ 
  repository/online/component/oxseforucs_20171205095418/version='current' \
[root@ucsmaster:~] # apt-get update
[root@ucsmaster:~] # apt-get install univention-ox-common univention-ox-dependencies-master \
  univention-ox-directory-integration univention-ox-framework
Now you can install OX on the 4.2 server.
[root@ucsox:~] # univention-app install oxseforucs --do-not-install-master-packages-remotely



Howto: Using check_mk/WATO via ssh and jumphost

I thought I’d pen this down right here as it took me a bit to really figure this out.

Problem: I have some Hosts I would like to monitor but I cannot access them directly (VPN also isn’t an option in this case), so I would like to monitor them using SSH, some directly, some behind a jumphost.


Usually the check_mk uses xinetd listening on Port 6556 only limited by allow_from in xinetd config and maybe iptables. This is fine in a closed, trusted environment but not really over public networks.

We could now either use VPN or tunnel the port through ssh port forwarding, but I found it more convenient just using ssh as a datasource program.

Preparing the nodes

We surely won’t use passwords for this, but rather a key with very limited capabilities.

So, first go to the monitoring site (make sure to do this as the monitoring user) and create a key pair:

OMD[site]:$ ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/omd/sites/site/.ssh/id_ed25519): /omd/sites/site/.ssh/id_check_mk
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /omd/sites/site/.ssh/id_check_mk.
Your public key has been saved in /omd/sites/site/.ssh/
The key fingerprint is:
The key's randomart image is:
+--[ED25519 256]--+
|. |
|o . . . . |
|.o . . . . . |
|oo. . . . .o |
|.o. . S. . +E |
| = . . o + ++ |
|= o o + .o +o+.|
| .+. = . . =o.+..|
| .+*o . o+Bo. |

Don’t use a passphrase. Now append the public key to the authorized_keys file on the monitored nodes. I am using ansible for this:

- name: add mod ssh-key
 user: root
 state: present
 key: "{{ lookup('file', '/root/.ssh/') }}"
 key_options: 'command="/usr/bin/check_mk_agent"'

This results in:

command="/usr/bin/check_mk_agent" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAXXXXXXT9PMJYIN4Mjdc9gsSAAAAAAxAZMhblN4+Mn18wh

Now you can ssh to that node from the monitoring host using the key, you should then get the output of the check_mk_agent.


Case 1: Directly accessible node

In WATO, go to Host & Service Parameters => Datasource Programs => Individual program call instead of agent access.

We give a simple name and the command line using the <IP> macro.

ssh -tt -o StrictHostKeyChecking=no root@

Some hosts have issues spawning a tty, so we omit that with -tt. I also had some issues with the host keys which afford disabling StrictHostKeyChecking.

I am using this rule for all monitored hosts, therefor I don’t need any rules set up. If you only want to apply these rules to single hosts, just add their names below, or better create a rule as shown in the jumphost example below.

Case 2: Nodes behind jumphost

We use the same approach with a jumphost, just adding that host in between.

We extend our ssh line by -J jump@jumphostMake sure that you don’t use the root account on the jump host!

If you are using an older or different version of SSH which doesn’t support the -J switch, you need to do it using the old style -W version.

As I only want specific nodes to be monitored using the jumphost, I created another choice in the networking host tags, so all Hosts tagged with “No ping possible” are monitored using the jumphost.

Now another issue arises: Ping fails, which means that all services are being monitored properly while the host has the status “down”. So we need to change that, too with another rule, also tagged with the same tag above.

Go to Host & Service Parameters => Monitoring Configuration => Host Check Command and add a new rule.

Switch the command from PING to Use the status of the Check_MK Agent and chose the correct host tag.




Bridged Xen on Debian Wheezy on a Hetzner Server

Xen (not XeServer, btw!) seems to have taken a bak-seat recently, RedHat/CentOS/Fedora concentrating on KVM and Debian silently neglecting it.

This is reflected in documentation, there is a lot of outdated stuff around, especially about bridged setups. Same occurs to packages, at least in Debian Wheezy (NB: I also tried on testing, same results with fairly newer packages).

My aim was a virtual host which is directly connected to the internet without any external firewall running different virtual machines which ARE thoroughly firewalled. In order to archive this, I am running the quite decent Sophos UTM (formerly Astaro) as a VM, this is the only virtual machine with direct access to the external network interface. It’s other interface just like all other VMs are connected to an internal bridge without any link to the rest of the world. This is why routing isn’t an option.

This article focusses on the Xen server and the bridging setup, maybe I will write another one later about Sophos UTM etc.



I am running this setup on some servers at Hetzner, though this should be working at most other hosters (some tend to drop the switch connection when they sense a pseudo ARP-spoofing, take care!), I am in no way affiliated to Hetzner.

My setup needs a secondary IP address (the main IP address is used for management of the host, I am assuming the following setup:

External Host-IP:

Secondary IP, used on UTM: At least at Hetzner, this IP address needs to have it’s own MAC address assigned, this can be done in their Robot tool.

Setting up the host

I want to have my host running directly on the (Software-)RAID, I personally don’t really like running the OS on LVM. But I also want to have my VMs live in an LVM realm in order to easily take snapshots, clone etc.

This means that Hetzner’s default setup isn’t very helpful. But they have an answer file based installation using the rescue system. Therefore: boot into the rescue system and run install image.

Note: Preserve the temporary password for the rescue system, you will need it for the freshly installed system!

This lets you define your custom install file and then installs everything within a few minutes. I chose Debian Wheezy Minimal. The only two settings I changed were the hostname (I am using dome in this example) and the partition setup:


I chose 50GB for my root filesystem and 12GB swap.

After saving the file and starting the installation, I had to wait for about five minutes and was presented with a brand new Debian system.


After the first boot I changed the root password and added my own SSH key.

Note: This document doesn’t cover hardening your server, which you really should do!

First thing to do is updating all package sources:

root@dom2 ~ # apt-get update

I tend to install emacs23-nox as soon as possible, YMMV.

It is quite handy to add your domain, if you are using one, to /etc/resolv.conf and to /etc/hosts.

Next is changing the network setup, so edit /etc/network/interfaces :

# Loopback device:
auto lo
iface lo inet loopback

# device: eth0 => transfigured to a bridge!
auto  virbr0
iface virbr0 inet static
  bridge_ports eth0
  bridge_stp off
  bridge_fd 1
  bridge_hello 2
  bridge_maxage 12
  allow-hotplug virbr0

auto virbr1
iface virbr1 inet static
  bridge_ports none
  bridge_fd 1
  bridge_stp off
  bridge_hello 1
  down ifconfig virbr1 down
  allow-hotplug virbr1

So we transformed our (only) network card eth0 into a bridge called virbr0 and added a secondary bridge, virbr1.

Set up Xen

First install the xen system (4.1 on Wheezy) and the xen-tools which are quite helpful setting up VMs.

root@dom2 ~ # apt-get install xen-system-amd64
root@dom2 ~ # apt-get install xen-tools

This will install xen and all the necessary tools.

In order to boot into a xen enabled hypervisor, we need to adapt GRUB:

root@dom2 ~ # dpkg-divert --divert /etc/grub.d/08_linux_xen --rename /etc/grub.d/20_linux_xen

Before we reboot we also adapt the boot command line in /etc/default/grub :

GRUB_CMDLINE_LINUX_DEFAULT="nomodeset dom0_max_vcpus=1 dom0_vcpus_pin dom0_mem=2048M nopat cgroup_enable=memory swapaccount=1"

This basically limits resources on Dom0.

Now update grub and reboot:

root@dom2 ~ # update-grub
Generating grub.cfg ...
Found linux image: /boot/vmlinuz-3.2.0-4-amd64
Found initrd image: /boot/initrd.img-3.2.0-4-amd64
Found linux image: /boot/vmlinuz-3.2.0-4-amd64
Found initrd image: /boot/initrd.img-3.2.0-4-amd64
root@dom2 ~ # reboot

After reboot we can check if Xen is up and running.

root@dom2 ~ # xm list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0  2047     1     r-----      7.3

Looks fine.

Now we need to set up the network, which is quite straight forward:

Edit the file /etc/xen/xend-config.sxp  and comment out everything about networking, routing and vif except this line:

(vif-script vif-bridge)

You  may also fine tune your Xen setup by changing the following lines:

# enable-dom0-ballooning below) and for xm mem-set when applied to dom0.
(dom0-min-mem 196)

# Whether to enable auto-ballooning of dom0 to allow domUs to be created.
# If enable-dom0-ballooning = no, dom0 will never balloon out.
(enable-dom0-ballooning no)

# xen kernel to reserve the memory for 32-bit paravirtual domains, default
# is "0" (0GB).
#(total_available_memory 0)

# In SMP system, dom0 will use dom0-cpus # of CPUS
# If dom0-cpus = 0, dom0 will take all cpus available
(dom0-cpus 1)

The first thing we changed tells Xen to run a script called vif-bridge  located in /etc/xen/scripts/  as soon as a virtual machine is being created. The script basically checks if the bridge exists and connects the VMs virtual network card to the bridge.

Now we need to adapt this file to our naming convention, so let’s replace the occurrences of xenbr  to virbr  in the file /etc/xen/scripts/vif-bridge :

  # This lets old config files work without modification
  if [ ! -e "/sys/class/net/$bridge" ] && [ -z "${bridge##virbr*}" ]
     if [ -e "/sys/class/net/eth${bridge#virbr1}/bridge" ]

Now restart xend (for some reason the service is called xen  on Debian.

root@dom2 ~ # service xen restart
[ ok ] Restarting Xen daemons: xend xend xenconsoled.

Getting the first VM up and running

Using xen-create-image  from the xen-tools makes it a piece of cake installing our first VM:

root@dom2 ~ # xen-create-image --hostname=test --ip= --netmask --gateway= --bridge=virbr1 --lvm=vg0 --mirror= --memory 512m --swap 1000M --dist=wheezy


  You appear to have a missing vif-script, or network-script, in the
 Xen configuration file /etc/xen/xend-config.sxp.

  Please fix this and restart Xend, or your guests will not be able
 to use any networking!

General Information
Hostname       :  test
Distribution   :  wheezy
Mirror         :
Partitions     :  swap            1000M (swap)
                  /               4Gb   (ext3)
Image type     :  full
Memory size    :  512m
Kernel path    :  /boot/vmlinuz-3.2.0-4-amd64
Initrd path    :  /boot/initrd.img-3.2.0-4-amd64

Networking Information
IP Address 1   : [MAC: 00:16:3E:88:89:EC]
Netmask        :
Gateway        :

Creating swap on /dev/vg0/test-swap

Creating ext3 filesystem on /dev/vg0/test-disk
Installation method: debootstrap

Running hooks

No role scripts were specified.  Skipping

Creating Xen configuration file

No role scripts were specified.  Skipping
Setting up root password
Generating a password for the new guest.
All done
Logfile produced at:

Installation Summary
Hostname        :  test
Distribution    :  wheezy
IP-Address(es)  :
RSA Fingerprint :  76:ab:f1:50:4e:71:49:7e:06:13:87:5c:8a:1d:62:82
Root Password   :  XXXXXXXXXX

You can safely ignore the warning about vif-bridge.

Now there’s a little bug in the xen-tools:

root@dom2 ~ # xm create /etc/xen/test.cfg
Using config file "/etc/xen/test.cfg".
Error: invalid literal for int() with base 10: '512m'

So edit /etc/xen/test.cfg and remove the m from 512m:

memory      = '512m'

memory      = '512'

Now let’s run it:

root@dom2 ~ # xm create /etc/xen/test.cfg
Using config file "/etc/xen/test.cfg".
Started domain test (id=6)

We can now connect a console to the vm and see why’s going on (you can also create it with the -c parameter above …).

root@dom2 ~ # xm console test

Debian GNU/Linux 7 test hvc0

test login: root
Linux test 3.2.0-4-amd64 #1 SMP Debian 3.2.60-1+deb7u3 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

Hint: CTRL + 5 gets you to of the console again.


HowTo: Hetzner Backup-Server Automount

Das Problem: Hetzner bietet bei einem Root-Server zwar 100GB Backup-Space an, auf den kann man aber nur per FTP, SFTP oder CIFS zugreifen, es ist z.B. kein direktes rsync möglich.

Ursprünglich hatte ich den backup space einfach ständig gemounted, allerdings gab es immer wieder Probleme mit hängenden Handles, vermutlich gehen die Backupserver in einen Energiesparmodus etc. Also habe ich auf AutoFS umgeschwenkt, und das funktioniert hier prima (Was ich bei grösseren Umgebungen so nicht behaupten kann, btw).

Also hier kurz eine Anleitung, wie man den Backupspace sehr einfach per CIFS und Autofs nutzen kann.

Achtung: Sämtlicher Datenverkehr findet umverschlüsselt zwischen dem Root-Server und dem Backupserver statt. Wollte ich nur loswerden.


Im Robot (Hetzner Config-Tool) muss ein Backup-Space angelegt sein, dafür gibt es dann einen Servernamen, einen Usernamen und ein Passwort.

Hier gelten folgende Daten:

Username: u12345
Passwort: secret

Ausserdem braucht ihr das Paket autofs , ist eigentlich bei jeder Distro im Repo vorhanden.

Ich mounte meinen Backupserver unter /mnt/backup-server/.


Zuerst legen wir eine neue Mapping-Datei an, wir nennen sie auto.backup und legen sie in /etc. Sie hat folgenden Inhalt:

backup-server -fstype=cifs,iocharset=utf8,rw,credentials=/etc/backup-credentials.txt,uid=0,gid=0,file_mode=0660,dir_mode=0770 ://

Der Reihe nach bedeutet diese Zeile:

  • Mounte das Share unter backup-server/ relativ zum übergeordneten Mountpunkt (siehe unten)
  • Mount-typ ist CIFS, also SMB
  • Charset ist UTF8 (wichtig, falls Ihr Umlaute etc. verwendet)
  • rw, also lesen und schreiben
  • Zugangsdaten sind in der Datei /etc/backup-credentials.txt  abgelegt. Achtung: auch hier umverschlüsselt.
  • Neue Dateien werden mit 0660, neue Ordner mit 0770 angelegt.
  • Am Schluss steht der Pfad auf dem Server, der gemounted werden soll. ://  sieht seltsam aus, passt aber.

Die /etc/backup-credentials.txt sieht so aus:


Zuletzt fügen wir dann noch in der /etc/auto.master  folgende Zeile ein:

/mnt	/etc/auto.backup --ghost

–ghost  teilt autofs mit, dass es beim umounten den Mountpunkt nicht löschen soll.

Nun noch den Automounter mit service autofs start  starten.

Sobald man nun auf den Ordner /mnt/backup-server  zugreift, wird er gemounted. Feine Sache.



HowTo: VPN between FritzBox and Sophos UTM (was: Astaro UTM) with DynDNS

After searching ans trying quite a lot, I finally managed to connect my Sophos UTM 9 (which is very nice and free for hime users!) to my FritzBox. Especially the fact that I don’t have a fixed IP address at home makes this a bit tricky.


The UTM is running as a virtual machine on my root server. It has got one public IP (RED, which is bridged (bridge0) to the network card of the host, and one private IP (GREEN) on a secondary bridge (bridge1) in my Xen environment.



Configuration FritzBox:

My first try was simply using the Add new VPN connections form in the FritzBox. This won’t work, because as soon as you need to rely on a dynamic hostname (as opposed to a fixed IP) the FritzBox forces aggressive mode for IKE, which will fail with the following message:

unsupported exchange type ISAKMP_XCHG_AGGR in message

So I had to create a config file manually, actually I adapted it from a few occurrences in the web:

vpncfg {
connections {
enabled = yes;
conn_type = conntype_lan;
name = "Astaro";
always_renew = yes;
reject_not_encrypted = no;
dont_filter_netbios = yes;
localip =;
local_virtualip =;
remoteip =;
remote_virtualip =;
localid {
	fqdn = my.dyndnsname;
remoteid {
	ipaddr =;
mode = phase1_mode_idp;
phase1ss = "alt/all/all";
keytype = connkeytype_pre_shared;
cert_do_server_auth = no;
use_nat_t = yes;
use_xauth = no;
use_cfgmode = no;
phase2localid {
ipnet {
ipaddr =;
mask =;
phase2ss = "esp-all-all/ah-none/comp-all/pfs";
//phase2ss = "esp-3des-sha/ah-no/comp-no/pfs";
accesslist = "permit ip any";
ike_forward_rules = "udp", 
// EOF

UTM settings

First, create a policy in UTM:


Now create a gateway which points to the FritzBox:


Make sure that the VPN ID type is set to FQDN and that it matches your dynamic hostname. (usually the same as the gateway). Create a remote network, in this case, I called it Fidicinstrasse (this is where the Fritzbox lives).

Finally, create a connection:


That’s all, wait a few seconds and the connection should come up automatically.






HowTo: Zwei Geräte an einen Waschmaschinenabfluss anschliessen

Nachdem ich nun eine ganze Weile rumgesucht habe, und inzwischen erfolgreich war, dachte ich, ich stelle das mal hier hin, vielleicht nutzt es mal jemandem.

Aus Platzgründen sind bei uns im Bad sowohl die Waschmaschine als auch der Geschirrspüler (letzterer auf ersterem). Seltsam, ist aber so. Dummerweise gibt es allerdings nur einen einzigen Wasser- und Abwasseranschluss, eingelassen in die Badewannenrückwand.

Der Wasseranschluss ist kein Problem, einfach ein T-Stück, und in meinem Fall noch ein 90°-Winkel wegen der Wand, und schon passen zwei Schläuche, jeweils mit AquaStop an den Hahn.

Tricky wird es mit dem Abfluss. Die wenigsten Geräte verfügen anscheinend über ein Rücklaufventil, und herauszufinden, welche das sind, ist nicht ganz einfach. Und wer kauft schon Wasch- und Spülmaschine aufgrund eines Solchen.

Ein Anruf bei Bosch (von denen sowohl unsere neue Waschmaschine als auch – wenn auch älter – der Geschirrspüler ist) hilft nur bedingt weiter. “Nein, an einem Wandanschluss können Sie nicht beide Geräte verwenden, das geht überhaupt nicht, auf gar keinen Fall, nein. Das geht höchstens an einem Unterbausiphon mit Trennwand.”. Na gut. Dann halt ohne Hilfe von Bosch, und ohne Trennwand.

Den ersten Versuch wagte ich mit einem einfachen Y-Stück aus dem Baumarkt. Mein Plan war, die beiden Ablaufschläuche in Schleifen oberhalb des Abflusses (der sich in ca. 50cm Höhe befindet) zu verlegen, so dass das Wasser nicht in die jeweilige andere Maschine laufen kann. Gesagt getan. Angeschlossen, Geschirrspüler laufen lassen, prima, kein Wasser in der Waschmaschine. Waschmaschine laufen lassen. Prima, kein Wasser im Geschirrspüler.

Allerdings: beim nächsten Durchlauf stand im Geschirrspüler das dreckige Wasser und das Geschirr war mit einer Schicht aus Fett und Spülmittel überzogen (soviel zu den Tensiden…).

Geschirrspüler aufgemacht, Problem gefunden: Da ist zwar ein Rückschlagventil drin, allerdings wird, wenn die Waschmaschine abpumpt, ganz wie in einer Strahlpumpe , der Geschirrspüler leergesaugt und das Ventil leicht in das Ablaufrohr gezogen. Woraufhin es verklemmt und mechanisch (Nach entfernen von Blende und Seitendeckel) wieder gelöst werden muss.

Temporär also erst mal das Y-Stück entfernt, Ablaufschlauch des Geschirrspülers in die Wanne gehängt. Läuft, ist aber eklig, und die Vorstellung, dass der Schlauch mal neben der Wanne hängt, ist auch nicht toll…

Also weiter gesucht. Und gefunden. Von der Firma Dallmer gibt es einen Waschgerätedoppelanschluss. Feine Sache. Mit Trennwand!

Kreditkarte mit guten 25 Euro belastet (neben dem Anschluss habe ich noch eine Verlängerung gebraucht, damit der nicht dem Zulauf in den Weg kommt).

Drei Tage später geliefert. Eingebaut. Läuft.

Und so sieht’s aus:


HowTo: Create a simple Tag Cloud for existing HTML content using JS with jQuery

Everybody loves tag clouds. Hopefully, otherwise I spent an hour for nothing creating one for

There are a lot of plugins for jQuery, or external libs etc. for creating tab clouds, most times with fancy AJAX calls, JSON processing and a neat database backend. We only need plain jQuery and for some sexyness the jquery.timer plugin. Please notice that this plugin needs jQuery.plugin to work.

I just wanted to have something which uses my existing HTML list while just extending that with a few tags. My website consists of one single HTML page optically diverted by jQuery tabs and accordions, therefore my main content isn’t visible when the tag cloud is available. So I will just pick some invisible content and display it in an empty space in my page. Another approach could be to just hide everything NOT sporting the chosen tag.

I created a small fiddle at as an example.

So, this is what I got:

<div id="content">
<h2>Here goes the main content</h2>
<dd>Big, striped cat</dd>
<dd>Big hairy king of the road</dd>
<dd>Not just a browser</dd>
<dd>Quite ugly meateating big bird</dd>
<dd>Sign of love or peace or something</dd>
<dd>Rather this than a snail!</dd>

I would like to display each group of <dt></dt><dd></dd> when their tags are highlighted. Therefore we need some Tags. I decided to use the name attribute, as it is deprecated though still usable in HTML5 without breaking anything. You could of course also use e.g. class or anything self defined.

Tags are separated by blanks as we really only want single worded tags in this case. You can easily change the separator in order to use tags including spaces, though you would need to display the tags with a border and recreate the selector…

<div id="content">
<h2>Here goes the main content</h2>
<dt name="Loud Roaring Carnivore Cat Stripes">Tiger</dt>
<dd>Big, striped cat</dd>
<dt name="Loud Roaring Carnivore Maned Cat">Lion</dt>
<dd>Big hairy king of the road</dd>
<dt name="Cat Brown">Lynx</dt>
<dd>Not just a browser</dd>
<dt name="Carnivore Bird Neck Brown">Vulture</dt>
<dd>Quite ugly meateating big bird</dd>
<dt name="Cute White Bird">Dove</dt>
<dd>Sign of love or peace or something</dd>
<dt name="Bird Small Brown">Sparrow</dt>
<dd>Rather this than a snail!</dd>

We also need some space for the tags and the highlighted content to be displayed.

<div id="main">
    <div id="cloud"></div>
    <div id="cloudcontent"></div>

Cloud will contain the tag cloud while cloudcontent will be used to display the chosen content.

So, let’s create some dynamics.

We will create a function consisting of three parts. The variable tags will hold an associative array { “tag” => number_of_occurrences }  with all tags.

function tagcloud() {
   var tags = {};

The first part gathers all tags and fills our associative array with each tag and the number of occurrences.

$( "dt" ).each(function( index ) {
		if ( $( this ).attr ( "name" ) ) {
			$.each( $( this ).attr( "name" ).split (" " ), function( index, value ) {
				if ( !( value in tags) ) {
					tags[ value ] = 1;
				else {
					tags[ value ]++;

Here we iterate over all <dt>s, check if they have a name attribute and if so get it’s value and split it into an array.

We now iterate over this array. If the value (i.e. the tag) already exists in our tags list, it increments the number of occurrences of this tag, otherwise it will create a new item with the name of the tag and a number of one.

This is our tags array:


The next part appends all tags to the cloud div

$.each( tags, function ( name, count ) {
	if ( count > 8 ) count = 8;
	$("#cloud").append("<span class='cloudelement size-" + count + "' id='" + name + "'>" + name + "</span> ");		

I created different CSS classes with different shades of grey and font sizes. These are applied to a span surrounding each tag, making the higher number be larger and darker. In order to keep it nice, I limited it to 8 levels.

Now we need to create some dynamics when hovering the tags.

	$('.cloudelement').mouseover( function() {
		$("#cloudcontent").append( "<dl> ");
	    $( "dt[name~='" + $( this ).html() + "']" ).each( function ( index ) {
		   $("#cloudcontent").append( "<dt>" + $( this ).parent().prev().html() + ", " +
		   							$( this ).html() + "</dt>" );
		   $( "#cloudcontent" ).append( "<dd>" + $( this ).next().html() + "</dd>" );
		$("#cloudcontent").append( "</dl>");
		$('#cloudcontent').timer( 'start' );

As we applied the class cloudelement to all tags, we can simply bind the mouseover() event to them.

We first clear the cloud content div and then append a definition list, then we select all <dt>s where the tag exists in the name attribute and iterate over these. We simply append new <dt></dt><dd></dd>s to our new dl consisting of the <dt>s we have found, the <h3> above it and the following <dd>.

Finally we start a timer which will remove the shown items after five seconds.

The timer will be initialized like this:

		delay: 5000,
		repeat: false,
		callback: function( index ) {
			$( "#cloudcontent" ).fadeOut( "slow", function() {
				$('#cloudcontent').timer( 'stop' );	            

So, when time ran out, the callback function will be executed which fades out the complete cloudcontent div, clears it when fading finished and shows it again (empty now) to be prepared to display the next content.

Put the timer initialization together with a call to the tagcloud()  function in the classical $(function() .





HowTo install Ubooquity on QNAP

Ubooquity is a very nice little server which scans your eBooks and Comics and displays them in a tablet friendly way.

It is Java-based and runs fine on your Desktop, but if you’re running a (QNAP-)NAS which already stores all your books, why not have them served nicely.




I did this on A QNAP TS-421 (ARM-CPU) Running OS version 4.x, though this should work the same way (except the Java installation on x86 see link below) on any other QNAP NAS.

I am assuming that you already have IPKG installed and are able to log in via ssh and already have some experience with the (Linux-)shell.

Install coreutils, procps and Java

The start script for the daemon requires the nohup and pgrep command which unfortunately aren’t shipped with the basic installation.

So simply do a

ipkg install coreutils
ipkg install procps

Install Java

Follow the instructions on in order to install Java. In brief:

Install Ubooquity

Download the jar from and put it on your QNAP NAS. I created a Folder Ubooquity in Public/ where everything from Ubooquity lives, so it is in /share/Public/Ubooquity/ now.

Do a test run on the shell:

java -jar Ubooquity.jar -webadmin

Now you should be able to connect to the admin server on http://<qnapaddress>:2202/admin

Set a password for administration and play with the Web ui.

Install as a service

As soon as you close the shell from above, Ubooquity quits itself. Not very cool. So we need to install it as a daemon, a service starting on system start and then running all time.

Ubooquity provides a nice startup script called at Get it and put it next to Ubooquity.jar. As pgrep on QNAP doesn’t support the -c (count) option, we need to change one line:

Replace all occurrences of the line

count=`pgrep -fc Ubooquity.jar`


count=`pgrep -f Ubooquity.jar | wc -l`

QNAP provides a quite easy way to register an application as a service. Simply edit the file /etc/config/qpkg.conf and add the following block.

Name = Ubooquity
Version = 1.4.0
Enable = TRUE
QPKG_File = none.qpkg
Date = 2014-04-28
Shell = /share/Public/Ubooquity/
Install_Path = /share/Public/Ubooquity
Web_Port = 2202
WebUI = /
Author = Ubooquity

You may have to adapt the paths to your installation.

Now you can start Ubooquity in the App Center just like any other app.