- The big restructuring: alle desktopspezifischen Prototypen unter einem Verzeichnis
authorJosef Spillner <josef.spillner@tu-dresden.de>
Sat, 25 Feb 2012 18:00:23 +0000 (15:00 -0300)
committerJosef Spillner <josef.spillner@tu-dresden.de>
Sat, 25 Feb 2012 18:00:23 +0000 (15:00 -0300)
92 files changed:
allocdemo/README.allocdemo [deleted file]
allocdemo/TODO [deleted file]
allocdemo/allocdemo [deleted file]
allocdemo/data/allocdemo-wrapper [deleted file]
allocdemo/data/code-block.png [deleted file]
allocdemo/delivery-frontends/FortuneClientMobileScreenshot.png [deleted file]
allocdemo/delivery-frontends/FortuneClientScreenshot.jpg [deleted file]
allocdemo/delivery-frontends/FortunePlasmoidScreenshot.png [deleted file]
allocdemo/delivery-frontends/dynvoker.org.png [deleted file]
allocdemo/delivery-frontends/fortuneservice.clientsxml [deleted file]
allocdemo/delivery-frontends/servicefrontenddisplayer [deleted file]
allocdemo/nubi-tree/README.nubi-tree [deleted file]
allocdemo/nubi-tree/credits [deleted file]
allocdemo/nubi-tree/icons/cloudfolder.png [deleted file]
allocdemo/nubi-tree/icons/dialog-error.png [deleted file]
allocdemo/nubi-tree/icons/dialog-warning.png [deleted file]
allocdemo/nubi-tree/icons/drive-removable-media-usb-pendrive.png [deleted file]
allocdemo/nubi-tree/icons/folder-locked.png [deleted file]
allocdemo/nubi-tree/icons/folder-remote.png [deleted file]
allocdemo/nubi-tree/icons/folder-tar.png [deleted file]
allocdemo/nubi-tree/icons/folder-yellow.png [deleted file]
allocdemo/nubi-tree/icons/user-online.png [deleted file]
allocdemo/nubi-tree/icons/weather-many-clouds.png [deleted file]
allocdemo/nubi-tree/kde-integration/README.integration [deleted file]
allocdemo/nubi-tree/kde-integration/kde-cloudstorage.dia [deleted file]
allocdemo/nubi-tree/kde-integration/kde-cloudstorage.png [deleted file]
allocdemo/nubi-tree/kde-integration/nubi-network.desktop [deleted file]
allocdemo/nubi-tree/kde-integration/nubisavelink [deleted file]
allocdemo/nubi-tree/kde-integration/rsync-kde [deleted file]
allocdemo/nubi-tree/kde-integration/syncme [deleted file]
allocdemo/nubi-tree/kde-integration/syncwithcloud_sm.desktop [deleted file]
allocdemo/nubi-tree/nubi-tree [deleted file]
allocdemo/ramdisks.sim [deleted file]
allocdemo/resources/dropbox.png [deleted file]
allocdemo/resources/dropbox.resource [deleted file]
allocdemo/resources/mediencenter.png [deleted file]
allocdemo/resources/mediencenter.resource [deleted file]
allocdemo/resources/sugarsync.png [deleted file]
allocdemo/resources/sugarsync.resource [deleted file]
allocdemo/simulation-modes [deleted file]
allocdemo/single-ram.sim [deleted file]
allocdemo/single-sim.sim [deleted file]
allocdemo/web/alloc-ramdisk.png [deleted file]
allocdemo/web/alloc-sim.png [deleted file]
allocdemo/web/allocdemo-20111016.tar.gz [deleted file]
allocdemo/web/nubitree.png [deleted file]
kde-cloudstorage/allocdemo/README.allocdemo [new file with mode: 0644]
kde-cloudstorage/allocdemo/TODO [new file with mode: 0644]
kde-cloudstorage/allocdemo/allocdemo [new file with mode: 0755]
kde-cloudstorage/allocdemo/data/allocdemo-wrapper [new file with mode: 0755]
kde-cloudstorage/allocdemo/data/code-block.png [new file with mode: 0644]
kde-cloudstorage/allocdemo/ramdisks.sim [new file with mode: 0644]
kde-cloudstorage/allocdemo/resources/dropbox.png [new file with mode: 0644]
kde-cloudstorage/allocdemo/resources/dropbox.resource [new file with mode: 0644]
kde-cloudstorage/allocdemo/resources/mediencenter.png [new file with mode: 0644]
kde-cloudstorage/allocdemo/resources/mediencenter.resource [new file with mode: 0644]
kde-cloudstorage/allocdemo/resources/sugarsync.png [new file with mode: 0644]
kde-cloudstorage/allocdemo/resources/sugarsync.resource [new file with mode: 0644]
kde-cloudstorage/allocdemo/simulation-modes [new file with mode: 0644]
kde-cloudstorage/allocdemo/single-ram.sim [new file with mode: 0644]
kde-cloudstorage/allocdemo/single-sim.sim [new file with mode: 0644]
kde-cloudstorage/allocdemo/web/alloc-ramdisk.png [new file with mode: 0644]
kde-cloudstorage/allocdemo/web/alloc-sim.png [new file with mode: 0644]
kde-cloudstorage/allocdemo/web/allocdemo-20111016.tar.gz [new file with mode: 0644]
kde-cloudstorage/allocdemo/web/nubitree.png [new file with mode: 0644]
kde-cloudstorage/delivery-frontends/FortuneClientMobileScreenshot.png [new file with mode: 0644]
kde-cloudstorage/delivery-frontends/FortuneClientScreenshot.jpg [new file with mode: 0644]
kde-cloudstorage/delivery-frontends/FortunePlasmoidScreenshot.png [new file with mode: 0644]
kde-cloudstorage/delivery-frontends/dynvoker.org.png [new file with mode: 0644]
kde-cloudstorage/delivery-frontends/fortuneservice.clientsxml [new file with mode: 0644]
kde-cloudstorage/delivery-frontends/servicefrontenddisplayer [new file with mode: 0755]
kde-cloudstorage/kde-integration/README.integration [new file with mode: 0644]
kde-cloudstorage/kde-integration/kde-cloudstorage.dia [new file with mode: 0644]
kde-cloudstorage/kde-integration/kde-cloudstorage.png [new file with mode: 0644]
kde-cloudstorage/kde-integration/nubi-network.desktop [new file with mode: 0644]
kde-cloudstorage/kde-integration/nubisavelink [new file with mode: 0755]
kde-cloudstorage/kde-integration/rsync-kde [new file with mode: 0755]
kde-cloudstorage/kde-integration/syncme [new file with mode: 0755]
kde-cloudstorage/kde-integration/syncwithcloud_sm.desktop [new file with mode: 0644]
kde-cloudstorage/nubi-tree/README.nubi-tree [new file with mode: 0644]
kde-cloudstorage/nubi-tree/credits [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/cloudfolder.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/dialog-error.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/dialog-warning.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/drive-removable-media-usb-pendrive.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/folder-locked.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/folder-remote.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/folder-tar.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/folder-yellow.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/user-online.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/icons/weather-many-clouds.png [new file with mode: 0644]
kde-cloudstorage/nubi-tree/nubi-tree [new file with mode: 0755]

diff --git a/allocdemo/README.allocdemo b/allocdemo/README.allocdemo
deleted file mode 100644 (file)
index 4de0ec4..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-Cloud Storage Allocator - Visualisation and Simulation Tool
-===========================================================
-
-This graphical tool demonstrates the effects of a smart cloud storage
-allocator. The idea is that an agent (e.g. a robot or an outdoor work team)
-produce much data which cannot be stored or processed locally. The allocator
-contacts cloud storage providers and allocates capacity by negotiating
-contracts. Depending on their non-functional properties, more providers
-will be contacted or existing allocation limits be raised. Eventually, the
-allocation shall always be optimal concerning the agent's preferences.
-
-Currently, the tool contains a simulator with a somewhat smart event
-generator to suggest non-existent disk usage, and a simulator which uses
-operating system facilities like RAM disks to pretent storage cloud behaviour.
-Later it will be attached to the real allocator to get
-proper realtime information about the usage of allocated cloud storage.
-See 'simulation-modes' for details of how the tool works.
-
-Installation Requirements:
-* Python
-* PyQt4
-
-Preparation of ramdisk integration through /etc/sudoers:
-allocuser ALL = NOPASSWD: /usr/local/bin/allocdemo-wrapper
-
-Invocation:
-./allocdemo [<*.sim>]
-
-Keys:
-* S - toggle view with/without scales
-* B - toggle view with/without aggregating bundle widget
-* P - pause the simulation (also Pause key)
-* +/- - influence the simulated usage
-* Tab - toggle between aggregation policies
-* ESC - quit the application
-
diff --git a/allocdemo/TODO b/allocdemo/TODO
deleted file mode 100644 (file)
index 30261c7..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-- bei Reallokation: Generator durch 1.4 teilen, verhindert Kettenreaktion
-- adaptive oder großzügigere y-Achsenbeschriftung
-- Kapazität bei Unlimited-Provider an tatsächliches Datenaufkommen anpassen
-- (schwierig) Ansichten synchronisierbar gestalten (durch Leerevents bzw. Offset)
-
diff --git a/allocdemo/allocdemo b/allocdemo/allocdemo
deleted file mode 100755 (executable)
index cdbf81c..0000000
+++ /dev/null
@@ -1,639 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from PyQt4 import QtGui, QtCore
-from PyQt4.QtCore import pyqtSlot, Qt
-import random
-import sys
-import glob
-import subprocess
-import os
-import optparse
-import math
-import codecs
-import datetime
-import locale
-
-#os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
-
-def dirsize(mountdir, apparent):
-       if apparent:
-               apparent = "--apparent"
-       else:
-               apparent = ""
-       size = subprocess.Popen("du --bytes -s %s '%s' 2>/dev/null | cut -f 1" % (apparent, mountdir), shell=True, stdout=subprocess.PIPE).communicate()[0].strip()
-       if not size:
-               size = None
-       else:
-               size = int(size)
-       return size
-
-class Contract:
-       def __init__(self):
-               self.providericon = None
-               self.providername = None
-               self.hostname = None
-               self.duration = None
-               self.usage = None
-               self.capacity = None
-               self.policy = None
-
-               self.expirydate = None
-               self.filled = None
-
-               self.mountdir = None
-
-               self.capacities = []
-
-       def calculate_expirydate(self):
-               expdate = datetime.datetime.now() + datetime.timedelta(self.duration - self.usage)
-               self.expirydate = expdate.strftime(locale.nl_langinfo(locale.D_T_FMT))
-
-       def allocate(self, date, capacity):
-               self.capacities.append((date, capacity))
-               self.capacity = capacity
-
-class ContractBundle(Contract):
-       def __init__(self):
-               Contract.__init__(self)
-               self.providername = u"RAOC=Σ"
-               self.hostname = ""
-               self.duration = 1
-               self.usage = 0
-               self.capacity = 1
-
-               self.contracts = []
-
-       def addcontract(self, contract):
-               self.contracts.append(contract)
-
-       def reconfigure(self, generatorpolicy):
-               datelist = [x.duration - x.usage for x in self.contracts]
-               if generatorpolicy == "smart-redundancy":
-                       datelist.sort()
-                       # FIXME: assumes capacity > security preference by falling back to mirroring/RAID1
-                       datelist = datelist[:2]
-               if generatorpolicy == "min-redundancy":
-                       self.duration = max(datelist)
-               elif generatorpolicy == "max-redundancy":
-                       self.duration = min(datelist)
-               elif generatorpolicy == "smart-redundancy":
-                       self.duration = min(datelist)
-               self.calculate_expirydate()
-
-class AggregationGenerator:
-       policies = ["min-redundancy", "max-redundancy", "smart-redundancy"]
-
-       def __init__(self):
-               self.policy = AggregationGenerator.policies[0]
-               self.n = None
-               self.k = None
-               self.reconfigure()
-
-       def value(self, contract):
-               #filled = 0
-               #for subcontract in contract.contracts:
-               #       filled += subcontract.filled
-               if self.policy == "min-redundancy":
-                       filled = reduce(lambda x, y: x + y, [x.filled for x in contract.contracts])
-               elif self.policy == "max-redundancy":
-                       filled = min([x.filled for x in contract.contracts])
-               elif self.policy == "smart-redundancy":
-                       filled = min([x.filled for x in contract.contracts])
-                       # FIXME: smarter calculation
-
-               contract.filled = filled
-               return filled
-
-       def reconfigure(self):
-               if self.policy == "min-redundancy":
-                       self.n = 2
-                       self.k = 1
-               elif self.policy == "max-redundancy":
-                       self.n = 1
-                       self.k = 1
-               elif self.policy == "smart-redundancy":
-                       self.n = 2
-                       self.k = 2
-
-       def param_n(self):
-               return self.n
-
-       def param_k(self):
-               return self.k
-
-       def level(self):
-               if self.k == 1 and self.n == 2:
-                       level = "striping"
-               elif self.k == 1 and self.n == 1:
-                       level = "mirroring"
-               else:
-                       level = "erasure-coding"
-               return level
-
-class SampleGenerator:
-       def __init__(self):
-               self.lastvalue = None
-
-       def value(self, contract):
-               if not self.lastvalue:
-                       self.lastvalue = random.random()
-               self.lastvalue += 0.05 - random.random() / 10
-               if random.random() < 0.03:
-                       self.lastvalue = random.random()
-               if self.lastvalue < 0:
-                       self.lastvalue = 0
-               if self.lastvalue > 1:
-                       self.lastvalue = 1
-               return self.lastvalue * contract.capacity
-
-class RamdiskGenerator:
-       def __init__(self, backing):
-               args = backing.split(":")
-               self.fs = args[0]
-               self.mountdir = args[1]
-               self.mountsize = 2000 * 1024
-
-               self.cleanup()
-
-               os.system("mkdir -p '%s'" % self.mountdir)
-               os.system("sudo allocdemo-wrapper mount '%s' %d" % (self.mountdir, self.mountsize))
-
-       def cleanup(self):
-               # FIXME: proper conditional cleanup handler
-               os.system("sudo allocdemo-wrapper umount '%s'" % self.mountdir)
-
-       def value(self, contract):
-               # FIXME: This is not the right place to change the contract
-               contract.capacity = self.mountsize
-               return dirsize(self.mountdir, False)
-
-       def resize(self, size):
-               self.mountsize = size
-               os.system("sudo allocdemo-wrapper resize '%s' %d" % (self.mountdir, size))
-
-class Alloc(QtCore.QObject):
-       onlinestates = ["online", "impaired", "offline"]
-
-       def __init__(self, contract):
-               QtCore.QObject.__init__(self)
-
-               self.contract = contract
-               self.online = "online"
-               self.points = []
-               self.dependencyallocs = []
-
-               self.generator = None
-               self.timer = None
-
-       def append(self, value):
-               self.points.append(value)
-               self.emit(QtCore.SIGNAL("valuesChanged()"))
-
-       def registerGenerator(self, generator, frequency):
-               self.generator = generator
-
-               self.timer = QtCore.QTimer()
-               self.connect(self.timer, QtCore.SIGNAL("timeout()"), self, QtCore.SLOT("slot_timer()"))
-               self.timer.start(frequency)
-
-               if isinstance(self.contract, ContractBundle):
-                       self.contract.reconfigure(self.generator.policy)
-
-       def registerDependencyAlloc(self, alloc):
-               self.dependencyallocs.append(alloc)
-               self.connect(alloc, QtCore.SIGNAL("allocationChanged()"), self, QtCore.SLOT("slot_allocation()"))
-
-       def reconfigure(self):
-               #self.allocate(0, self.capacity / 2)
-               # FIXME: needs points (in alloc) and subcontract fill status
-               self.slot_allocation()
-
-       @pyqtSlot()
-       def slot_timer(self):
-               newpoint = self.generator.value(self.contract)
-               self.append(newpoint)
-               self.contract.filled = newpoint
-
-               if isinstance(self.generator, SampleGenerator):
-                       if not isinstance(self.contract, ContractBundle):
-                               online = random.randint(0, 8)
-                               if online:
-                                       self.online = "online"
-                               else:
-                                       self.online = "offline"
-                               if newpoint > 0.8 * self.contract.capacity and self.contract.policy == "Upgradeable Maximum":
-                                       self.contract.allocate(len(self.points), self.contract.capacity * 1.4)
-                                       self.emit(QtCore.SIGNAL("allocationChanged()"))
-               elif isinstance(self.generator, RamdiskGenerator):
-                       self.online = "online"
-                       if not isinstance(self.contract, ContractBundle):
-                               if newpoint > 0.8 * self.contract.capacity and self.contract.policy == "Upgradeable Maximum":
-                                       self.generator.resize(self.contract.capacity * 1.4)
-                                       self.contract.allocate(len(self.points), self.generator.mountsize)
-                                       self.emit(QtCore.SIGNAL("allocationChanged()"))
-               elif isinstance(self.generator, AggregationGenerator):
-                       self.online = "online"
-                       all_offline = True
-                       for alloc in self.dependencyallocs:
-                               if alloc.online == "offline":
-                                       self.online = "impaired"
-                               else:
-                                       all_offline = False
-                       if all_offline:
-                               self.online = "offline"
-
-       @pyqtSlot()
-       def slot_allocation(self):
-               if self.generator.policy == "min-redundancy":
-                       aggregatedcapacity = reduce(lambda x, y: x + y, [x.capacity for x in self.contract.contracts])
-               elif self.generator.policy == "max-redundancy":
-                       aggregatedcapacity = min([x.capacity for x in self.contract.contracts])
-               elif self.generator.policy == "smart-redundancy":
-                       aggregatedcapacity = min([x.capacity for x in self.contract.contracts])
-                       # FIXME: smarter calculation
-
-               self.contract.allocate(len(self.points), aggregatedcapacity)
-               self.emit(QtCore.SIGNAL("allocationChanged()"))
-
-class AllocWidget(QtGui.QFrame):
-       def __init__(self, parent=None):
-               QtGui.QFrame.__init__(self, parent)
-
-               self.setMinimumWidth(400)
-               self.setMinimumHeight(150)
-
-               self.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Sunken)
-
-               pal = QtGui.QPalette()
-               pal.setColor(QtGui.QPalette.Window, QtGui.QColor(30, 30, 30))
-               self.setPalette(pal)
-               self.setAutoFillBackground(True)
-
-               self.viewscale = False
-
-               self.linecolor = QtGui.QColor(255, 255, 0)
-               self.areacolor = QtGui.QColor(255, 100, 0)
-
-               self.alloc = None
-
-       def paintEvent(self, paintevent):
-               if not self.alloc:
-                       return
-
-               painter = QtGui.QPainter(self)
-               painter.setPen(QtGui.QColor(255, 255, 255))
-
-               if self.alloc.contract.providericon:
-                       painter.drawPixmap(QtCore.QRect(10, 10, 32, 32), QtGui.QPixmap("resources/" + self.alloc.contract.providericon))
-
-               font = painter.font()
-               font.setPixelSize(32)
-               painter.setFont(font)
-               #painter.drawText(QtCore.QPoint(62, 42), self.alloc.name)
-               painter.drawText(QtCore.QRect(62, 10, self.width() - 72, 42), 0, self.alloc.contract.providername)
-
-               if isinstance(self.alloc.contract, ContractBundle):
-                       fsize = 24 / len(self.alloc.contract.contracts)
-                       font.setPixelSize(fsize)
-                       painter.setFont(font)
-                       for y, contract in enumerate(self.alloc.contract.contracts):
-                               painter.drawText(QtCore.QRect(62 + 140, 13 + y * (fsize + 3), self.width() - 72, fsize + 3), 0, contract.providername)
-
-               font.setPixelSize(12)
-               painter.setFont(font)
-               painter.drawText(QtCore.QRect(62, 47, self.width() - 72, 15), 0, self.alloc.contract.hostname)
-               if self.viewscale:
-                       painter.drawText(QtCore.QRect(62, 70, self.width() - 72, 15), 0, "Contract expiry: %s (%i days)" % (self.alloc.contract.expirydate, self.alloc.contract.duration - self.alloc.contract.usage))
-
-               if isinstance(self.alloc.contract, ContractBundle):
-                       painter.drawText(QtCore.QRect(62, 85, self.width() - 72, 15), 0, "Aggregation policy: %s" % self.alloc.generator.policy)
-                       if self.viewscale:
-                               n = self.alloc.generator.param_n()
-                               k = self.alloc.generator.param_k()
-                               level = self.alloc.generator.level()
-                               painter.drawText(QtCore.QRect(62, 47, self.width() - 72, 15), 0, "RAOC Level: %s (n=%d,k=%d)" % (level, n, k))
-               else:
-                       if self.viewscale:
-                               painter.drawText(QtCore.QRect(62, 85, self.width() - 72, 15), 0, "Allocation policy: %s" % self.alloc.contract.policy)
-
-               if self.alloc.online == "online":
-                       onlinecolor = QtGui.QColor(0, 255, 0)
-               elif self.alloc.online == "impaired":
-                       onlinecolor = QtGui.QColor(255, 255, 0)
-               else:
-                       onlinecolor = QtGui.QColor(255, 0, 0)
-               onlinestatus = self.alloc.online
-
-               painter.drawRect(QtCore.QRect(self.width() - 100, 50, 50, 12))
-               painter.fillRect(QtCore.QRect(self.width() - 99, 51, 49, 11), onlinecolor)
-
-               font.setPixelSize(10)
-               painter.setFont(font)
-               painter.setPen(QtGui.QColor(0, 0, 0))
-               painter.drawText(QtCore.QRect(self.width() - 100, 51, 49, 11), Qt.AlignCenter, onlinestatus)
-               painter.setPen(QtGui.QColor(255, 255, 255))
-
-               if self.viewscale:
-                       contractusage = float(self.alloc.contract.usage) / self.alloc.contract.duration
-                       if contractusage < 0.75:
-                               contractcolor = QtGui.QColor(0, 255, 0)
-                       elif contractusage < 0.90:
-                               contractcolor = QtGui.QColor(255, 255, 0)
-                       else:
-                               contractcolor = QtGui.QColor(255, 0, 0)
-
-                       painter.drawRect(QtCore.QRect(self.width() - 100, 73, 50, 12))
-                       painter.fillRect(QtCore.QRect(self.width() - 99, 74, contractusage * 48, 11), contractcolor)
-
-               diax = 0
-               diay = 100
-               diawidth = self.width() - 1
-               diaheight = self.height() - 1
-
-               if self.viewscale:
-                       diax = 40
-                       diaheight = self.height() - 21
-                       diawidth = self.width() - 11
-
-               maxcapacity = max([y for (x, y) in self.alloc.contract.capacities])
-
-               polygon = QtGui.QPolygonF()
-               polygon.append(QtCore.QPointF(diawidth, diaheight))
-               polygon.append(QtCore.QPointF(diax, diaheight))
-
-               pointlen = len(self.alloc.points) - 1
-               if pointlen == 0:
-                       pointlen = 1
-               for x, point in enumerate(self.alloc.points):
-                       cappoint = float(point) / maxcapacity
-                       polygon.append(QtCore.QPointF(x * (diawidth - diax) / pointlen + diax, (1.0 - cappoint) * (diaheight - diay) + diay))
-
-               painter.setPen(self.linecolor)
-               painter.setBrush(self.areacolor)
-               painter.drawPolygon(polygon)
-
-               fixedmaximum = False
-               upgradeablemaximum = False
-               allocationtrigger = False
-               if self.alloc.contract.policy == "Fixed Maximum":
-                       fixedmaximum = True
-               elif self.alloc.contract.policy == "Upgradeable Maximum":
-                       upgradeablemaximum = True
-                       allocationtrigger = True
-               if isinstance(self.alloc.contract, ContractBundle):
-                       # FIXME: more efficient calculation when adding a dependency contract vs. more storage
-                       # FIXME: consider upgradeable maximum vs. fixed maximum on the basis of generator.{n,k}
-                       if self.alloc.generator.policy == "min-redundancy":
-                               fixedmaximum = True
-                       for contract in self.alloc.contract.contracts:
-                               if self.alloc.generator.policy == "min-redundancy":
-                                       if contract.policy == "Upgradeable Maximum":
-                                               if fixedmaximum:
-                                                       fixedmaximum = False
-                                                       upgradeablemaximum = True
-                                       elif contract.policy == "Unlimited":
-                                                       fixedmaximum = False
-                                                       upgradeablemaximum = False
-                               elif self.alloc.generator.policy == "max-redundancy" or self.alloc.generator.policy == "smart-redundancy":
-                                       if contract.policy == "Fixed Maximum":
-                                               fixedmaximum = True
-                                       elif contract.policy == "Upgradeable Maximum":
-                                               if not fixedmaximum:
-                                                       fixedmaximum = False
-                                                       upgradeablemaximum = True
-
-               if fixedmaximum or upgradeablemaximum:
-                       if fixedmaximum:
-                               painter.setPen(QtGui.QPen(QtGui.QColor(200, 50, 0), 1, Qt.SolidLine))
-                       else:
-                               painter.setPen(QtGui.QPen(QtGui.QColor(200, 50, 0), 1, Qt.DashLine))
-                       alloclen = len(self.alloc.contract.capacities)
-                       for i in range(alloclen):
-                               (date, alloclimit) = self.alloc.contract.capacities[i]
-                               if i < alloclen - 1:
-                                       (dateend, alloclimitend) = self.alloc.contract.capacities[i + 1]
-                               else:
-                                       alloclimitend = alloclimit
-                                       dateend = len(self.alloc.points)
-                               # FIXME: date/variable spacing
-                               allocpoints = len(self.alloc.points)
-                               if allocpoints == 0:
-                                       allocpoints = 1
-                               x = date * (diawidth - diax) / allocpoints + diax
-                               xend = dateend * (diawidth - diax) / allocpoints + diax
-                               y = (1.0 - (alloclimit / maxcapacity)) * (diaheight - diay) + diay
-                               yend = (1.0 - (alloclimitend / maxcapacity)) * (diaheight - diay) + diay
-                               painter.drawLine(QtCore.QPoint(x, y), QtCore.QPoint(xend, y))
-                               painter.drawLine(QtCore.QPoint(xend, y), QtCore.QPoint(xend, yend))
-
-                       if self.viewscale:
-                               font.setPixelSize(10)
-                               painter.drawText(QtCore.QRect(diawidth - 100, 87, 100, 13), Qt.AlignRight, "Capacity")
-
-               if allocationtrigger and self.viewscale:
-                       painter.setPen(QtGui.QPen(QtGui.QColor(100, 80, 0), 1, Qt.DashLine))
-                       alloctrigger = 100 + 0.2 * (diaheight - diay)
-                       painter.drawLine(QtCore.QPoint(diax, alloctrigger), QtCore.QPoint(diawidth, alloctrigger))
-                       if self.viewscale:
-                               painter.drawText(QtCore.QRect(diawidth - 100, alloctrigger - 13, 100, 13), Qt.AlignRight, "Allocation Trigger")
-
-               if isinstance(self.alloc.generator, RamdiskGenerator) and self.viewscale:
-                       font.setPixelSize(8)
-                       painter.setFont(font)
-                       painter.setPen(QtGui.QColor(255, 200, 0))
-                       painter.drawText(QtCore.QRect(62 - 150, 47 + 4, self.width() - 72, 15), Qt.AlignRight, "RAMDISK:%s" % self.alloc.generator.mountdir)
-
-               if self.viewscale:
-                       magnitude = math.log(self.alloc.contract.capacity, 1024)
-                       units = ("Bytes", "kB", "MB", "GB", "TB")
-                       unit = units[int(magnitude)]
-
-                       font.setPixelSize(8)
-                       painter.setFont(font)
-                       painter.setPen(QtGui.QColor(255, 255, 255))
-                       painter.drawLine(QtCore.QPoint(diax, 100), QtCore.QPoint(diax, self.height() - 10))
-                       painter.drawLine(QtCore.QPoint(diax - 10, self.height() - 20), QtCore.QPoint(self.width() - 10, self.height() - 20))
-                       limit = (diaheight - 80) / 20
-                       for y in range(limit + 1):
-                               capsection = "%3.2f" % float(((limit - y) * float(self.alloc.contract.capacity) / limit) / (1024 ** int(magnitude)))
-                               pos = y * (diaheight - diay) / limit + diay
-                               painter.drawLine(QtCore.QPoint(diax - 10, pos), QtCore.QPoint(diax, pos))
-                               if y > 0:
-                                       painter.drawText(QtCore.QRect(0, pos - 10, diax - 1, 15), Qt.AlignRight, str(capsection))
-                       painter.drawText(QtCore.QRect(0, diay - 10, diax - 1, 15), Qt.AlignRight, unit)
-                       stepsize = len(self.alloc.points) / 15 + 1
-                       for x in range(0, len(self.alloc.points), stepsize):
-                               pos = x * (diawidth - diax) / pointlen + diax
-                               painter.drawLine(QtCore.QPoint(pos, self.height() - 20), QtCore.QPoint(pos, self.height() - 10))
-                               if x < len(self.alloc.points) - 1:
-                                       painter.drawText(QtCore.QRect(pos + 2, self.height() - 18, 15, 15), 0, str(x))
-
-               #painter.end()
-
-       def registerAlloc(self, alloc):
-               self.alloc = alloc
-               self.connect(alloc, QtCore.SIGNAL("valuesChanged()"), self, QtCore.SLOT("update()"))
-               self.connect(alloc, QtCore.SIGNAL("allocationChanged()"), self, QtCore.SLOT("update()"))
-
-       def toggleview(self):
-               self.viewscale = not self.viewscale
-               self.update()
-
-class AllocDemo(QtGui.QWidget):
-       def __init__(self, simscript, parent=None):
-               QtGui.QWidget.__init__(self, parent)
-
-               self.resize(520, 500)
-               self.setWindowTitle("Optimal Cloud Allocation [using %s]" % simscript)
-               self.setWindowIcon(QtGui.QIcon("data/code-block.png"))
-
-               self.allocs = []
-               self.generators = []
-               self.blocks = []
-
-               simbacking = {}
-               if simscript:
-                       lines = codecs.open(simscript, "r", "utf-8").readlines()
-                       for line in lines:
-                               args = line.strip().split("=")
-                               if len(args) == 2:
-                                       simbacking[args[0].strip()] = args[1].strip()
-
-               resources = glob.glob("resources/*.resource")
-               for resource in resources:
-                       contract = Contract()
-                       lines = codecs.open(resource, "r", "utf-8").readlines()
-                       for line in lines:
-                               args = line.strip().split("=")
-                               if len(args) != 2:
-                                       continue
-                               # FIXME: no other way of assigning arbitrary attributes?
-                               try:
-                                       exec("contract.%s = %s" % (args[0].strip(), args[1].strip()))
-                               except:
-                                       exec("contract.%s = u\"%s\"" % (args[0].strip(), args[1].strip()))
-
-                       if simscript:
-                               if not simbacking.has_key(contract.providername):
-                                       continue
-
-                       contract.calculate_expirydate()
-
-                       alloc = Alloc(contract)
-                       if simbacking.has_key(contract.providername) and simbacking[contract.providername] != "simulation":
-                               generator = RamdiskGenerator(simbacking[contract.providername])
-                               contract.allocate(0, generator.mountsize)
-                       else:
-                               generator = SampleGenerator()
-                               contract.allocate(0, contract.capacity)
-                       alloc.registerGenerator(generator, contract.updatefrequency)
-                       self.allocs.append(alloc)
-                       self.generators.append(generator)
-
-               bundle = ContractBundle()
-               for alloc in self.allocs:
-                       bundle.addcontract(alloc.contract)
-               bundle.calculate_expirydate()
-
-               alloc = Alloc(bundle)
-               generator = AggregationGenerator()
-               alloc.registerGenerator(generator, 4000)
-               self.allocs.append(alloc)
-               self.generators.append(generator)
-
-               for alloc in self.allocs:
-                       if isinstance(alloc.contract, ContractBundle):
-                               for contract in alloc.contract.contracts:
-                                       for depalloc in self.allocs:
-                                               if depalloc.contract == contract:
-                                                       alloc.registerDependencyAlloc(depalloc)
-                               alloc.slot_allocation()
-
-               self.setuplayout()
-
-       def cleanup(self):
-               for generator in self.generators:
-                       if "cleanup" in dir(generator):
-                               generator.cleanup()
-
-       def setuplayout(self):
-               grid = QtGui.QGridLayout()
-               self.setLayout(grid)
-
-               rowlimit = 3
-               if len(self.allocs) >= 4:
-                       rowlimit = 2
-
-               for i, alloc in enumerate(self.allocs):
-                       block = AllocWidget()
-                       block.registerAlloc(alloc)
-                       grid.addWidget(block, i % rowlimit, i / rowlimit)
-                       self.blocks.append(block)
-
-                       if isinstance(alloc.contract, ContractBundle):
-                               block.linecolor = QtGui.QColor(100, 100, 255)
-                               block.areacolor = QtGui.QColor(0, 0, 200)
-                               block.setVisible(False)
-
-       def keyPressEvent(self, event):
-               if event.key() == Qt.Key_Escape:
-                       #event.accept()
-                       self.cleanup()
-                       self.close()
-               elif event.key() == Qt.Key_S:
-                       for block in self.blocks:
-                               block.toggleview()
-               elif event.key() == Qt.Key_B:
-                       for block in self.blocks:
-                               if isinstance(block.alloc.contract, ContractBundle):
-                                       block.setVisible(not block.isVisible())
-               elif event.key() in (Qt.Key_P, Qt.Key_Pause):
-                       for alloc in self.allocs:
-                               if alloc.timer.isActive():
-                                       alloc.timer.stop()
-                               else:
-                                       alloc.timer.start()
-               elif event.key() in (Qt.Key_Plus, Qt.Key_Minus):
-                       for generator in self.generators:
-                               if isinstance(generator, SampleGenerator):
-                                       if generator.lastvalue:
-                                               if event.key() == Qt.Key_Plus:
-                                                       generator.lastvalue += 0.1
-                                               else:
-                                                       generator.lastvalue -= 0.1
-               elif event.key() == Qt.Key_Tab:
-                       for generator in self.generators:
-                               if isinstance(generator, AggregationGenerator):
-                                       pos = AggregationGenerator.policies.index(generator.policy)
-                                       pos += 1
-                                       if pos == len(AggregationGenerator.policies):
-                                               pos = 0
-                                       generator.policy = AggregationGenerator.policies[pos]
-                                       generator.reconfigure()
-                       for block in self.blocks:
-                               if isinstance(block.alloc.contract, ContractBundle):
-                                       block.update()
-                       for alloc in self.allocs:
-                               if isinstance(alloc.contract, ContractBundle):
-                                       alloc.contract.reconfigure(alloc.generator.policy)
-                                       alloc.reconfigure()
-
-parser = optparse.OptionParser()
-#parser.add_option("-h", "--help", help="Script help")
-
-(options, args) = parser.parse_args()
-
-simscript = None
-
-if len(args) == 1:
-       simscript = args[0]
-elif len(args) > 1:
-       print >>sys.stderr, "Syntax: allocdemo [<*.sim>]"
-       sys.exit(1)
-
-app = QtGui.QApplication(sys.argv)
-allocdemo = AllocDemo(simscript)
-allocdemo.move(500, 200)
-allocdemo.show()
-sys.exit(app.exec_())
diff --git a/allocdemo/data/allocdemo-wrapper b/allocdemo/data/allocdemo-wrapper
deleted file mode 100755 (executable)
index e2dd7f3..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/sh
-
-cmd=$1
-
-if [ -z $cmd ]
-then
-       echo "Syntax: allocdemo-wrapper <cmd> [cmdoptions...]" >&2
-       exit 1
-fi
-
-case $cmd in
-       mount)
-               mountpoint=$2
-               size=$3
-               mount -t tmpfs -o size=$size tmpfs "$mountpoint"
-       ;;
-       umount)
-               mountpoint=$2
-               umount "$mountpoint" 2>/dev/null
-       ;;
-       resize)
-               mountpoint=$2
-               size=$3
-               mount -o remount,size=$size "$mountpoint"
-       ;;
-       *)
-               echo "Error: Invalid command $cmd" >&2
-               echo "Valid commands:"
-               echo "* mount  <mountpoint> <size>"
-               echo "* resize <mountpoint> <size>"
-               echo "* umount <mountpoint>"
-               exit 1
-       ;;
-esac
diff --git a/allocdemo/data/code-block.png b/allocdemo/data/code-block.png
deleted file mode 100644 (file)
index 68a5460..0000000
Binary files a/allocdemo/data/code-block.png and /dev/null differ
diff --git a/allocdemo/delivery-frontends/FortuneClientMobileScreenshot.png b/allocdemo/delivery-frontends/FortuneClientMobileScreenshot.png
deleted file mode 100644 (file)
index 55def6d..0000000
Binary files a/allocdemo/delivery-frontends/FortuneClientMobileScreenshot.png and /dev/null differ
diff --git a/allocdemo/delivery-frontends/FortuneClientScreenshot.jpg b/allocdemo/delivery-frontends/FortuneClientScreenshot.jpg
deleted file mode 100644 (file)
index 106b971..0000000
Binary files a/allocdemo/delivery-frontends/FortuneClientScreenshot.jpg and /dev/null differ
diff --git a/allocdemo/delivery-frontends/FortunePlasmoidScreenshot.png b/allocdemo/delivery-frontends/FortunePlasmoidScreenshot.png
deleted file mode 100644 (file)
index eefbb63..0000000
Binary files a/allocdemo/delivery-frontends/FortunePlasmoidScreenshot.png and /dev/null differ
diff --git a/allocdemo/delivery-frontends/dynvoker.org.png b/allocdemo/delivery-frontends/dynvoker.org.png
deleted file mode 100644 (file)
index 6d50c21..0000000
Binary files a/allocdemo/delivery-frontends/dynvoker.org.png and /dev/null differ
diff --git a/allocdemo/delivery-frontends/fortuneservice.clientsxml b/allocdemo/delivery-frontends/fortuneservice.clientsxml
deleted file mode 100644 (file)
index 809f6bb..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0"?>
-<serviceclients>
-  <client name="fs_rubyqtclient" type="standalone">
-    <description>
-       FortuneService Standalone Client written with Ruby and Qt
-    </description>
-    <image>
-      FortuneClientScreenshot.jpg
-    </image>
-    <version>
-      1.1
-    </version>
-    <url>
-      http://localhost/servicedata/fortuneclient.zip
-    </url>
-    <requirements>
-      <requirement tag="Desktop"/>
-      <requirement tag="Qt" minVersion="4.5"/>
-      <requirement tag="Ruby" minVersion="1.8"/>
-    </requirements>
-  </client>
-  <client name="fs_rubyqtclient_mobile" type="standalone">
-    <description>
-       Mobile FortuneService Standalone Client written with Ruby and Qt
-    </description>
-    <image>
-      FortuneClientMobileScreenshot.png
-    </image>
-    <version>
-      1.1
-    </version>
-    <url>
-      http://localhost/servicedata/fortuneclient.zip
-    </url>
-    <requirements>
-      <requirement tag="Smartphone"/>
-      <requirement tag="Qt" minVersion="4.5"/>
-      <requirement tag="Ruby" minVersion="1.8"/>
-    </requirements>
-  </client>
-  <client name="fs_plasmoidclient" type="plasmoid">
-    <description>
-       FortuneService Client for KDE Plasma written with Python
-    </description>
-    <image>
-      FortunePlasmoidScreenshot.png
-    </image>
-    <version>
-      1.1
-    </version>
-    <url>
-      http://localhost/servicedata/fortunemoid.zip
-    </url>
-    <requirements>
-      <requirement tag="Desktop"/>
-      <requirement tag="Plasma" minVersion="4.3"/>
-      <requirement tag="Python" minVersion="2.5"/>
-    </requirements>
-  </client>
-</serviceclients>
diff --git a/allocdemo/delivery-frontends/servicefrontenddisplayer b/allocdemo/delivery-frontends/servicefrontenddisplayer
deleted file mode 100755 (executable)
index facfcef..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from PyQt4 import QtGui, QtCore
-from PyQt4.QtCore import pyqtSlot, Qt
-import sys
-import xml.dom.minidom
-
-class Client:
-       def __init__(self):
-               self.clienttype = None
-               self.description = None
-               self.image = None
-               self.version = None
-
-class FrontendsWidget(QtGui.QWidget):
-       def __init__(self, parent=None):
-               QtGui.QWidget.__init__(self, parent)
-
-               self.resize(500, 300)
-               self.setWindowTitle("Service frontend selection")
-
-               clients = []
-               clientsxml = xml.dom.minidom.parse("fortuneservice.clientsxml")
-               for clientxml in clientsxml.documentElement.childNodes:
-                       if isinstance(clientxml, xml.dom.minidom.Element) and clientxml.nodeName == "client":
-                               #print clientxml
-                               client = Client()
-                               client.clienttype = clientxml.getAttribute("type")
-                               client.description = clientxml.getElementsByTagName("description")[0].firstChild.nodeValue.strip()
-                               client.image = clientxml.getElementsByTagName("image")[0].firstChild.nodeValue.strip()
-                               client.version = clientxml.getElementsByTagName("version")[0].firstChild.nodeValue.strip()
-                               #print client.clienttype, client.description, client.image, client.version
-                               clients.append(client)
-
-               webclient = Client()
-               webclient.clienttype = "web"
-               webclient.description = "Dynvoker: Dynamically created web service client"
-               webclient.image = "dynvoker.org.png"
-               webclient.version = "beta"
-               clients.append(webclient)
-
-               layout = QtGui.QGridLayout(self)
-               layout.setSpacing(10)
-               for i, client in enumerate(clients):
-                       pixmap = QtGui.QPixmap(client.image).scaledToWidth(128)
-                       pixwidget = QtGui.QLabel()
-                       pixwidget.setPixmap(pixmap)
-                       layout.addWidget(pixwidget, i, 0)
-
-                       clienttype = "Unknown frontend"
-                       if client.clienttype == "plasmoid":
-                               clienttype = "Desktop Applet"
-                       elif client.clienttype == "standalone":
-                               clienttype = "Standalone Client"
-                       elif client.clienttype == "web":
-                               clienttype = "Web Application"
-
-                       title = QtGui.QLabel("<b>" + clienttype + "</b>")
-                       desc = QtGui.QLabel(client.description)
-                       version = QtGui.QLabel("<i>Version: " + client.version + "</i>")
-
-                       sublayout = QtGui.QVBoxLayout()
-                       sublayout.addWidget(title)
-                       sublayout.addWidget(desc)
-                       sublayout.addWidget(version)
-                       sublayout.addStretch()
-                       layout.addLayout(sublayout, i, 1)
-
-       def keyPressEvent(self, event):
-               if event.key() == Qt.Key_Escape:
-                       self.close()
-
-app = QtGui.QApplication(sys.argv)
-few = FrontendsWidget()
-few.move(350, 200)
-few.show()
-sys.exit(app.exec_())
diff --git a/allocdemo/nubi-tree/README.nubi-tree b/allocdemo/nubi-tree/README.nubi-tree
deleted file mode 100644 (file)
index 0494e1b..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-NubiTree - mockup for integrating NubiSave into the desktop
-Josef Spillner <josef.spillner@tu-dresden.de>
-===========================================================
-
-NubiSave (nubisave.org) is an open source optimal cloud storage controller.
-Optimality criteria such as security, performance or cost are requested from the
-user to define both the optimal set of storage providers and optimal allocation
-and scheduling strategies.
-
-NubiTree is supposed to become a convenient PyQt user interface as alternative
-to the functional but rather clumsy Java GUI which ships with NubiSave. Both GUIs
-are supposed to be used interchangeably through the Cloud Storage Configuration
-Schema (CSCS). NubiTree will make cloud storage configuration pleasant especially
-for users of the KDE desktop (kde.org) and Maemo/Meego/Mer/Nemo mobile devices
-(merproject.org).
diff --git a/allocdemo/nubi-tree/credits b/allocdemo/nubi-tree/credits
deleted file mode 100644 (file)
index 9018ca7..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-icons/cloudfolder.png:
- »Oxygen Cloud Folder« by MoonBlossom
- http://kde-look.org/content/show.php/Oxygen+Cloud+Folder?content=140203
-
-Other icons: Oxygen icon set
diff --git a/allocdemo/nubi-tree/icons/cloudfolder.png b/allocdemo/nubi-tree/icons/cloudfolder.png
deleted file mode 100644 (file)
index a9a3cae..0000000
Binary files a/allocdemo/nubi-tree/icons/cloudfolder.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/icons/dialog-error.png b/allocdemo/nubi-tree/icons/dialog-error.png
deleted file mode 100644 (file)
index a9095a2..0000000
Binary files a/allocdemo/nubi-tree/icons/dialog-error.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/icons/dialog-warning.png b/allocdemo/nubi-tree/icons/dialog-warning.png
deleted file mode 100644 (file)
index 0666a39..0000000
Binary files a/allocdemo/nubi-tree/icons/dialog-warning.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/icons/drive-removable-media-usb-pendrive.png b/allocdemo/nubi-tree/icons/drive-removable-media-usb-pendrive.png
deleted file mode 100644 (file)
index 568c078..0000000
Binary files a/allocdemo/nubi-tree/icons/drive-removable-media-usb-pendrive.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/icons/folder-locked.png b/allocdemo/nubi-tree/icons/folder-locked.png
deleted file mode 100644 (file)
index 765359d..0000000
Binary files a/allocdemo/nubi-tree/icons/folder-locked.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/icons/folder-remote.png b/allocdemo/nubi-tree/icons/folder-remote.png
deleted file mode 100644 (file)
index 48bb768..0000000
Binary files a/allocdemo/nubi-tree/icons/folder-remote.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/icons/folder-tar.png b/allocdemo/nubi-tree/icons/folder-tar.png
deleted file mode 100644 (file)
index 0b3c4bd..0000000
Binary files a/allocdemo/nubi-tree/icons/folder-tar.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/icons/folder-yellow.png b/allocdemo/nubi-tree/icons/folder-yellow.png
deleted file mode 100644 (file)
index fc649a9..0000000
Binary files a/allocdemo/nubi-tree/icons/folder-yellow.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/icons/user-online.png b/allocdemo/nubi-tree/icons/user-online.png
deleted file mode 100644 (file)
index 68d6fd1..0000000
Binary files a/allocdemo/nubi-tree/icons/user-online.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/icons/weather-many-clouds.png b/allocdemo/nubi-tree/icons/weather-many-clouds.png
deleted file mode 100644 (file)
index 53e1db6..0000000
Binary files a/allocdemo/nubi-tree/icons/weather-many-clouds.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/kde-integration/README.integration b/allocdemo/nubi-tree/kde-integration/README.integration
deleted file mode 100644 (file)
index 4a664b4..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-nubi-network.desktop:
- - makes NubiSave folder appear in the network places in the file dialogue
- - copy to /usr/share/kde4/apps/remoteview
-
-syncwithcloud_sm.desktop:
- - links a folder or file to the NubiSave folder
- - copy to /usr/share/kde4/services/ServiceMenus
-
-nubisavelink:
- - helper application for "sync with cloud"
- - copy to /usr/(local/)bin
-
-cloudfolder.png (from ../icons):
- - nicer icon for the network places
- - copy to /usr/share/icons/oxygen/64x64/places
-
-syncme:
- - asynchronous delayed incremental copying of files and directories
- - copy to /usr/(local/)bin
- - NOTE: This tool can also be used independently for all sorts of sync tasks!
-
-rsync-kde:
- - wrapper around 'rsync' which adds a graphical progress bar
- - copy to /usr/(local/)bin
diff --git a/allocdemo/nubi-tree/kde-integration/kde-cloudstorage.dia b/allocdemo/nubi-tree/kde-integration/kde-cloudstorage.dia
deleted file mode 100644 (file)
index 9ca69d9..0000000
Binary files a/allocdemo/nubi-tree/kde-integration/kde-cloudstorage.dia and /dev/null differ
diff --git a/allocdemo/nubi-tree/kde-integration/kde-cloudstorage.png b/allocdemo/nubi-tree/kde-integration/kde-cloudstorage.png
deleted file mode 100644 (file)
index f47d620..0000000
Binary files a/allocdemo/nubi-tree/kde-integration/kde-cloudstorage.png and /dev/null differ
diff --git a/allocdemo/nubi-tree/kde-integration/nubi-network.desktop b/allocdemo/nubi-tree/kde-integration/nubi-network.desktop
deleted file mode 100644 (file)
index 07f1a61..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Desktop Entry]
-Icon=cloudfolder
-Name=NubiSave Optimal Cloud Storage
-Open=false
-Type=Link
-URL[$e]=file://${HOME}/nubisave/
diff --git a/allocdemo/nubi-tree/kde-integration/nubisavelink b/allocdemo/nubi-tree/kde-integration/nubisavelink
deleted file mode 100755 (executable)
index adb2dc6..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/sh
-#
-# Links a file or directory to the NubiSave splitter folder
-
-splitterfolder=$HOME/nubisave
-
-which syncme >/dev/null
-
-if [ -z $1 ]
-then
-       echo "Syntax error: nubisavelink [--move] <sourcepath>" >&2
-       exit 1
-fi
-
-move=0
-src=$1
-if [ "$src" = "--move" ]
-then
-       move=1
-       src=$2
-fi
-
-if [ $move -eq 0 ]
-then
-       if [ $? != 0 ]
-       then
-               # FIXME: Symbolic links are not supported, obviously
-               #ln -sf "$src" $splitterfolder
-               cp -r "$src" "$splitterfolder"
-       else
-               syncme add "$src" "$splitterfolder"
-               # FIXME: config reload workaround by forced restart
-               syncme stop
-               syncme start
-       fi
-else
-       basesrc=`basename $src`
-       mv "$src" "$splitterfolder"
-       ln -sf "$splitterfolder/$basesrc" "$src"
-fi
-
diff --git a/allocdemo/nubi-tree/kde-integration/rsync-kde b/allocdemo/nubi-tree/kde-integration/rsync-kde
deleted file mode 100755 (executable)
index 534b1a6..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/sh
-#
-# Displays an rsync progress bar among the KIO job notifications
-# Syntax: rsync-kde <sourcedir> <targetdir>
-
-source=$1
-target=$2
-
-if [ -z $source ] || [ -z $target ]
-then
-       echo "Syntax error: rsync-kde <sourcedir> <targetdir>" >&2
-       exit 1
-fi
-
-rsp=`qdbus --literal org.kde.JobViewServer /JobViewServer requestView "Cloud Sync" "cloudfolder" 7`
-#rsp="[ObjectPath: /JobViewServer/JobView_50]"
-path=`echo $rsp | sed -e 's/\[ObjectPath: \(.*\)\]/\1/'`
-
-#qdbus org.kde.JobViewServer $path setDestUrl "http://foo"
-qdbus org.kde.JobViewServer $path setSuspended True
-qdbus org.kde.JobViewServer $path setInfoMessage "Synchronising $source into the cloud [$target]"
-sleep 1
-qdbus org.kde.JobViewServer $path setSuspended False
-
-allfiles=`find $source | wc -l`
-logfile=~/._rsynckdelog$$
-
-rsync -avz $source $target > $logfile &
-
-stats=0
-while [ $stats -lt 100 ]
-do
-       syncedfiles=`wc -l $logfile | cut -d " " -f 1`
-       stats=$((100*$syncedfiles/$allfiles))
-       qdbus org.kde.JobViewServer $path setPercent $stats
-       sleep 1
-done
-
-rm -f $logfile
-qdbus org.kde.JobViewServer $path terminate ""
-
diff --git a/allocdemo/nubi-tree/kde-integration/syncme b/allocdemo/nubi-tree/kde-integration/syncme
deleted file mode 100755 (executable)
index 977b952..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Tool for maintaining synchronisation links between directories
-
-import sys
-import os
-import signal
-import errno
-import time
-import optparse
-import xml.dom.minidom
-import inotifyx
-
-class Link:
-       def __init__(self, sourcedir, targetdir):
-               self.sourcedir = sourcedir
-               self.targetdir = targetdir
-               self.versioned = None
-
-class LinkCollection:
-       def __init__(self, configdir):
-               self.configdir = configdir
-               self.links = []
-
-       def load(self):
-               config = os.path.join(self.configdir, "syncme-links.xml")
-               try:
-                       f = open(config)
-               except:
-                       return
-               dom = xml.dom.minidom.parseString(" ".join(f.readlines()))
-               f.close()
-
-               if dom.documentElement.localName != "links":
-                       print >>sys.stderr, "Warning: Configuration file '%s' is misformatted." % config
-                       return
-
-               linksxmllist = dom.documentElement.getElementsByTagName("link")
-               for linksxml in linksxmllist:
-                       link = Link(linksxml.getAttribute("sourcedir"), linksxml.getAttribute("targetdir"))
-                       link.versioned = (linksxml.getAttribute("versioned") == "true")
-                       self.links.append(link)
-
-       def save(self):
-               dom = xml.dom.minidom.Document()
-               root = dom.createElement("links")
-               dom.appendChild(root)
-               for link in self.links:
-                       linkxml = dom.createElement("link")
-                       linkxml.setAttribute("sourcedir", link.sourcedir)
-                       linkxml.setAttribute("targetdir", link.targetdir)
-                       linkxml.setAttribute("versioned", ("false", "true")[int(link.versioned)])
-                       root.appendChild(linkxml)
-
-               try:
-                       os.makedirs(self.configdir)
-               except os.error, e:
-                       if e.errno != errno.EEXIST:
-                               raise
-
-               config = os.path.join(self.configdir, "syncme-links.xml")
-               f = open(config, "w")
-               dom.writexml(f, addindent=" ", newl="\n")
-               f.close()
-
-class Syncer():
-       def __init__(self, sourcedir, targetdir, versioned):
-               self.sourcedir = sourcedir
-               self.targetdir = targetdir
-               # FIXME: versioning is ignored for now (as is adaptation etc.)
-
-       def sync(self):
-               print "[syncme-daemon (%s->%s)] start" % (self.sourcedir, self.targetdir)
-               os.system("rsync -az '%s' '%s'" % (self.sourcedir, self.targetdir))
-               print "[syncme-daemon (%s->%s)] finish" % (self.sourcedir, self.targetdir)
-
-class Daemon():
-       def __init__(self):
-               pass
-
-       def launch(self):
-               logfile = os.path.join(links.configdir, "daemon.log")
-               log = open(logfile, "a")
-               sys.stdout = log
-               sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 0)
-               print "** Daemon launch at %s" % time.ctime()
-
-               notifyeventmask = inotifyx.IN_DELETE | inotifyx.IN_CLOSE_WRITE
-               notifier = inotifyx.init()
-               configwatcher = inotifyx.add_watch(notifier, os.path.join(links.configdir, "syncme-links.xml"), notifyeventmask)
-
-               syncers = {}
-               watchers = {}
-               for link in links.links:
-                       syncer = Syncer(link.sourcedir, link.targetdir, link.versioned)
-                       if not syncers.has_key(link.sourcedir):
-                               syncers[link.sourcedir] = []
-                       syncers[link.sourcedir].append(syncer)
-               for sourcedir in syncers.keys():
-                       sourcedirwatcher = inotifyx.add_watch(notifier, sourcedir, notifyeventmask)
-                       watchers[sourcedirwatcher] = sourcedir
-
-               for syncer in sum(syncers.values(), []):
-                       syncer.sync()
-
-               halt_daemon = False
-               while not halt_daemon:
-                       #time.sleep(60)
-                       events = inotifyx.get_events(notifier)
-                       for event in events:
-                               if event.wd == configwatcher:
-                                       print "[notification] Configuration has changed, reload!"
-                                       # FIXME: Perform reloading, merge with existing config to avoid unneeded sync runs!
-                               else:
-                                       sourcedir = watchers[event.wd]
-                                       print "[notification] Sourcedir [%s] has changed, run syncer!" % sourcedir
-                                       for syncer in syncers[sourcedir]:
-                                               syncer.sync()
-
-               inotifyx.rm_watch(notifier, configwatcher)
-               for watcher in watcher.keys():
-                       inotifyx.rm_watch(notifier, watcher)
-               os.close(notifier)
-
-               self.terminate(os.getpid())
-
-       def terminate(self, pid):
-               os.kill(pid, signal.SIGTERM)
-               pidfile = os.path.join(links.configdir, ".pid")
-               os.unlink(pidfile)
-
-       def writepid(self, pid):
-               pidfile = os.path.join(links.configdir, ".pid")
-               f = open(pidfile, "w")
-               print >>f, pid
-               f.close()
-
-       def readpid(self):
-               pidfile = os.path.join(links.configdir, ".pid")
-               try:
-                       f = open(pidfile)
-               except:
-                       return -1
-               pid = f.read().strip()
-               f.close()
-               return int(pid)
-
-links = LinkCollection(os.path.expanduser('~') + "/.syncme")
-links.load()
-
-def daemon_start():
-       d = Daemon()
-       pid = d.readpid()
-       if pid > 0:
-               print >>sys.stderr, "Error: Daemon already running."
-               sys.exit(-1)
-       pid = os.fork()
-       if pid == 0:
-               d = Daemon()
-               d.launch()
-               sys.exit(0)
-       elif pid > 0:
-               d = Daemon()
-               d.writepid(pid)
-               sys.exit(0)
-       else:
-               print >>sys.stderr, "Error: Cannot fork."
-               sys.exit(-1)
-
-def daemon_stop():
-       d = Daemon()
-       pid = d.readpid()
-       if pid < 0:
-               print >>sys.stderr, "Error: Daemon not running."
-               sys.exit(-1)
-       d.terminate(pid)
-
-def daemon_status():
-       d = Daemon()
-       pid = d.readpid()
-       if pid < 0:
-               print "Daemon is not running."
-       elif pid > 0:
-               print "Daemon is running."
-
-               if not os.path.exists(os.path.join("/proc", str(pid))):
-                       print "Warning: The system state appears to be invalid."
-               # FIXME: add more pid comparison logic to detect stale pidfiles, also for start/stop
-
-def links_list():
-       source_label = "Source directory"
-       target_label = "Target directory"
-       option_label = "Options"
-
-       source_maxlen = max([len(link.sourcedir) for link in links.links] + [len(source_label)])
-       target_maxlen = max([len(link.targetdir) for link in links.links] + [len(target_label)])
-
-       print source_label, " " * (source_maxlen - len(source_label)) + "|",
-       print target_label, " " * (target_maxlen - len(target_label)) + "|",
-       print option_label
-       print "-" * source_maxlen,
-       print "+",
-       print "-" * target_maxlen,
-       print "+",
-       print "-" * len(option_label)
-
-       for link in links.links:
-               print link.sourcedir, " " * (source_maxlen - len(link.sourcedir)) + "|",
-               print link.targetdir, " " * (target_maxlen - len(link.targetdir)) + "|",
-               print ("", "versioned")[int(link.versioned)]
-
-def links_add(sourcedir, destdir, versioned):
-       link = Link(sourcedir, destdir)
-       link.versioned = (versioned == True)
-       links.links.append(link)
-       links.save()
-
-parser = optparse.OptionParser()
-parser.add_option("-v", "--versioned", action="store_true", help="Synchronisation should be versioned")
-parser.add_option("-H", "--help-commands", action="store_true", help="List available tool commands")
-(options, args) = parser.parse_args()
-
-if options.help_commands:
-       print "Daemon control commands:"
-       print "* start:  Start the synchronisation routines asynchronously"
-       print "* stop:   Stop the synchronisation routines"
-       print "* status: Show if the daemon is already running"
-       print ""
-       print "Synchronisation links maintenance commands:"
-       print "* list:   Show all links and their status"
-       print "* add:    Add an additional link to be synchronised"
-       print "          (Syntax: add [--versioned] <sourcedir> <destdir>)"
-       sys.exit(0)
-
-if len(args) == 0:
-       print >>sys.stderr, "Error: Needs a command, see --help-commands."
-       sys.exit(1)
-
-cmd = args[0]
-
-if cmd == "start":
-       if len(args) > 1:
-               print >>sys.stderr, "Error: Superfluous arguments."
-               sys.exit(1)
-       daemon_start()
-elif cmd == "stop":
-       if len(args) > 1:
-               print >>sys.stderr, "Error: Superfluous arguments."
-               sys.exit(1)
-       daemon_stop()
-elif cmd == "status":
-       if len(args) > 1:
-               print >>sys.stderr, "Error: Superfluous arguments."
-               sys.exit(1)
-       daemon_status()
-elif cmd == "list":
-       if len(args) > 1:
-               print >>sys.stderr, "Error: Superfluous arguments."
-               sys.exit(1)
-       links_list()
-elif cmd == "add":
-       if len(args) != 3:
-               print >>sys.stderr, "Error: Incorrect number of arguments."
-               sys.exit(1)
-       links_add(args[1], args[2], options.versioned)
-else:
-       print >>sys.stderr, "Error: '%s' is not a valid command, see --help-commands." % cmd
-       sys.exit(1)
-
diff --git a/allocdemo/nubi-tree/kde-integration/syncwithcloud_sm.desktop b/allocdemo/nubi-tree/kde-integration/syncwithcloud_sm.desktop
deleted file mode 100644 (file)
index 67e9e6d..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-[Desktop Entry]
-Actions=SyncWithCloud;MoveIntoCloud;
-Type=Service
-Icon=
-Name=
-X-KDE-ServiceTypes=KonqPopupMenu/Plugin,application/octet-stream,inode/directory
-
-[Desktop Action SyncWithCloud]
-Exec=nubisavelink %U
-Icon=cloudfolder
-Name=Sync with the cloud
-Name[de]=Mit der Cloud synchronisieren
-
-[Desktop Action MoveIntoCloud]
-Exec=nubisavelink --move %U
-Icon=cloudfolder
-Name=Move into the cloud
-Name[de]=In die Cloud verschieben
diff --git a/allocdemo/nubi-tree/nubi-tree b/allocdemo/nubi-tree/nubi-tree
deleted file mode 100755 (executable)
index 7588bf6..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from PyQt4 import QtGui, QtCore
-from PyQt4.QtCore import pyqtSlot, Qt
-import sys
-import optparse
-import os
-import glob
-import ConfigParser
-
-class LeafType:
-       def __init__(self, identifier, name, icon):
-               self.identifier = identifier
-               self.name = name
-               self.icon = icon
-               self.needsbackend = False
-
-class LeafTypeRegistry:
-       def __init__(self):
-               self.leaftypes = []
-               self.leaftypes.append(LeafType("splitter", "NubiSave splitter folder", "icons/cloudfolder.png"))
-               self.leaftypes.append(LeafType("directory", "Local directory", "icons/folder-yellow.png"))
-               self.leaftypes.append(LeafType("removable", "Removable device", "icons/drive-removable-media-usb-pendrive.png"))
-               self.leaftypes.append(LeafType("encfs", "Encryption/EncFS overlay", "icons/folder-locked.png"))
-               self.leaftypes.append(LeafType("s3", "Amazon S3", "icons/weather-many-clouds.png"))
-               self.leaftypes.append(LeafType("zipfs", "Compression/ZIPFS overlay", "icons/folder-tar.png"))
-               self.leaftypes.append(LeafType("sshfs", "Secure Shell/SSHFS", "icons/folder-remote.png"))
-
-       def get(self, identifier):
-               return filter(lambda x: x.identifier == identifier, self.leaftypes)[0]
-
-class NubiTree:
-       STATUS_UNKNOWN = 0
-       STATUS_OK = 1
-       STATUS_WARNING_CAPACITY = 2
-       STATUS_WARNING_REDUNDANCY = 3
-       STATUS_ERROR = 4
-       ltr = LeafTypeRegistry()
-
-       def __init__(self, identifier, directory, backend, status):
-               leaftype = NubiTree.ltr.get(identifier)
-               self.name = leaftype.name
-               self.icon = leaftype.icon
-               self.directory = directory
-               self.backend = backend
-               self.status = status
-
-               self.leaves = []
-
-class NubiTreeWidget(QtGui.QWidget):
-       def __init__(self, parent=None):
-               QtGui.QWidget.__init__(self, parent)
-
-               self.resize(800, 300)
-               self.setWindowTitle("NubiTree mockup")
-
-               model = QtGui.QStandardItemModel()
-               model.setColumnCount(4)
-
-               model.setHeaderData(0, Qt.Horizontal, "Module")
-               model.setHeaderData(1, Qt.Horizontal, "Directory")
-               model.setHeaderData(2, Qt.Horizontal, "Backend")
-               model.setHeaderData(3, Qt.Horizontal, "Status")
-
-               self.qtv = QtGui.QTreeView(self)
-               self.qtv.setModel(model)
-
-               self.connect(self.qtv, QtCore.SIGNAL("pressed(QModelIndex)"), self, QtCore.SLOT("slot_pressed(QModelIndex)"))
-
-               layout = QtGui.QVBoxLayout(self)
-               layout.addWidget(self.qtv)
-
-               self.properties(layout)
-
-       @pyqtSlot(QtCore.QModelIndex)
-       def slot_pressed(self, index):
-               if QtGui.QApplication.mouseButtons() & Qt.RightButton:
-                       action_repair = QtGui.QAction("Repair", self)
-                       action_repair.setEnabled(False)
-                       action_submodule = QtGui.QAction("Add submodule", self)
-                       action_submodule.setEnabled(False)
-                       action_configure = QtGui.QAction("Configure...", self)
-                       action_remove = QtGui.QAction("Remove", self)
-                       menu = QtGui.QMenu()
-                       menu.addAction(action_configure)
-                       menu.addAction(action_remove)
-                       menu.addSeparator()
-                       menu.addAction(action_submodule)
-                       menu.addSeparator()
-                       menu.addAction(action_repair)
-                       menu.exec_(QtGui.QCursor.pos())
-
-       def settree(self, tree):
-               model = self.qtv.model()
-               self.interprettree(model.invisibleRootItem(), tree)
-
-               self.qtv.expandAll()
-               for i in range(model.columnCount()):
-                       self.qtv.resizeColumnToContents(i)
-
-       def interprettree(self, modelitem, tree):
-               statusicon = {}
-               statusicon[NubiTree.STATUS_UNKNOWN] = str()
-               statusicon[NubiTree.STATUS_OK] = "icons/user-online.png"
-               statusicon[NubiTree.STATUS_WARNING_CAPACITY] = "icons/dialog-warning.png"
-               statusicon[NubiTree.STATUS_WARNING_REDUNDANCY] = "icons/dialog-warning.png"
-               statusicon[NubiTree.STATUS_ERROR] = "icons/dialog-error.png"
-
-               statustext = {}
-               statustext[NubiTree.STATUS_UNKNOWN] = str()
-               statustext[NubiTree.STATUS_OK] = str()
-               statustext[NubiTree.STATUS_WARNING_CAPACITY] = "Storage capacity exhausted soon"
-               statustext[NubiTree.STATUS_WARNING_REDUNDANCY] = "Redundancy currently not reached"
-               statustext[NubiTree.STATUS_ERROR] = "Storage resource not reachable"
-
-               leafitem = QtGui.QStandardItem(QtGui.QIcon(tree.icon), tree.name)
-               leafitem.setEditable(False)
-               statusicon = statusicon[tree.status]
-               status = QtGui.QStandardItem(QtGui.QIcon(statusicon), str())
-               status.setToolTip(statustext[tree.status])
-               directory = QtGui.QStandardItem(tree.directory)
-               if tree.backend:
-                       backend = QtGui.QStandardItem(tree.backend)
-               else:
-                       backend = QtGui.QStandardItem()
-                       backend.setEditable(False)
-               modelitem.appendRow((leafitem, directory, backend, status))
-               for leaf in tree.leaves:
-                       self.interprettree(leafitem, leaf)
-
-       def properties(self, layout):
-               label_splitter = QtGui.QLabel("Splitter:")
-               combo_splitter = QtGui.QComboBox()
-               combo_splitter.addItem("Simple Splitter")
-               combo_splitter.addItem("Erasure Coding")
-               combo_splitter.addItem("Full Redundancy")
-
-               label_redundancy = QtGui.QLabel("Redundancy:")
-               slider_redundancy = QtGui.QSlider(Qt.Horizontal)
-               slider_redundancy.setMaximum(100)
-               self.label_redundancy_result = QtGui.QLabel()
-
-               self.connect(slider_redundancy, QtCore.SIGNAL("sliderMoved(int)"), self, QtCore.SLOT("slot_slider(int)"))
-               self.slot_slider(0)
-
-               label_sched = QtGui.QLabel("Scheduler:")
-               combo_sched = QtGui.QComboBox()
-               combo_sched.addItem("Round-Robin")
-               combo_sched.addItem("Fastest First")
-
-               hbox = QtGui.QHBoxLayout()
-               hbox.addWidget(label_splitter)
-               hbox.addWidget(combo_splitter)
-               hbox.addWidget(label_redundancy)
-               hbox.addWidget(slider_redundancy)
-               hbox.addWidget(self.label_redundancy_result)
-               hbox.addWidget(label_sched)
-               hbox.addWidget(combo_sched)
-               hbox.addStretch()
-
-               layout.addLayout(hbox)
-
-       @pyqtSlot(int)
-       def slot_slider(self, value):
-               self.label_redundancy_result.setText(str(value) + "%")
-
-       def keyPressEvent(self, event):
-               if event.key() == Qt.Key_Escape:
-                       self.close()
-
-def loadtree(storagesdir):
-       leaves = {}
-       relations = {}
-
-       modulepaths = glob.glob(storagesdir + "/*")
-       for modulepath in modulepaths:
-               module = os.path.basename(modulepath)
-               config = os.path.join(modulepath, "config/config")
-
-               cp = ConfigParser.ConfigParser()
-               cp.read(config)
-
-               ltr = LeafTypeRegistry()
-               for leaftype in ltr.leaftypes:
-                       if module.startswith(leaftype.identifier):
-                               backend = None
-                               if leaftype.identifier == "directory":
-                                       mountdir = cp.get("mounting", "directory")
-                               elif leaftype.identifier == "encfs":
-                                       backendmodule = cp.get("parameter", "backendservice1")
-                                       relations[module] = backendmodule
-                                       backend = os.path.join(storagesdir, backendmodule, "data")
-                                       #mountdir = os.path.join(storagesdir, "backend", backendmodule)
-                                       mountdir = os.path.join(modulepath, "data")
-                               #print "=> %s [mounted @ %s, backend %s]" % (leaftype.name, mountdir, backend)
-
-                               leaf = NubiTree(leaftype.identifier, mountdir, backend, NubiTree.STATUS_UNKNOWN)
-                               leaves[module] = leaf
-
-       for relation in relations:
-               #print relation, relations[relation]
-               leaves[relation].leaves.append(leaves[relations[relation]])
-               leaves.pop(relations[relation])
-
-       #for leaf in leaves:
-       #       print leaf, leaves[leaf]
-
-       tree = NubiTree("splitter", "/home/demo/nubisavemount", None, NubiTree.STATUS_UNKNOWN)
-       tree.leaves = leaves.values()
-
-       return tree
-
-def simulatetree(storagesdir):
-       tree = NubiTree("splitter", "/home/demo/nubisavemount", None, NubiTree.STATUS_WARNING_REDUNDANCY)
-       localdir2 = NubiTree("directory", "%s/dir2" % storagesdir, None, NubiTree.STATUS_OK)
-       encdir = NubiTree("encfs", "%s/dir1-encrypt" % storagesdir, "/home/demo/.nubisave/dir1",  NubiTree.STATUS_OK)
-       localdir1 = NubiTree("directory", "%s/dir1" % storagesdir, None, NubiTree.STATUS_WARNING_CAPACITY)
-       s3dir = NubiTree("s3", "%s/dir-amazon" % storagesdir, "s3://s3.amazonaws.com", NubiTree.STATUS_ERROR)
-       zipfsdir = NubiTree("zipfs", "%s/dir-ssh-compress" % storagesdir, "/home/demo/.nubisave/dir-ssh", NubiTree.STATUS_OK)
-       sshfsdir = NubiTree("sshfs", "%s/dir-ssh" % storagesdir, "ssh://demo@saturn.local", NubiTree.STATUS_WARNING_CAPACITY)
-       subtree = NubiTree("splitter", "%s/subsplit" % storagesdir, None, NubiTree.STATUS_OK)
-       localdir3 = NubiTree("directory", "%s/dir3" % storagesdir, None, NubiTree.STATUS_OK)
-       localdir4 = NubiTree("removable", "%s/dir-usb" % storagesdir, None, NubiTree.STATUS_OK)
-
-       tree.leaves.append(localdir2)
-       tree.leaves.append(encdir)
-       encdir.leaves.append(localdir1)
-       tree.leaves.append(s3dir)
-       tree.leaves.append(zipfsdir)
-       zipfsdir.leaves.append(sshfsdir)
-       tree.leaves.append(subtree)
-       subtree.leaves.append(localdir3)
-       subtree.leaves.append(localdir4)
-
-       return tree
-
-storagesdir = os.path.expanduser('~') + "/.nubisave/storages"
-
-parser = optparse.OptionParser()
-parser.add_option("-l", "--load", action="store_true", dest="load", help="Load NubiSave configuration from %s" % storagesdir)
-(options, args) = parser.parse_args()
-
-if options.load:
-       tree = loadtree(storagesdir)
-else:
-       tree = simulatetree(storagesdir)
-
-app = QtGui.QApplication(sys.argv)
-ntw = NubiTreeWidget()
-ntw.settree(tree)
-ntw.move(350, 200)
-ntw.show()
-sys.exit(app.exec_())
diff --git a/allocdemo/ramdisks.sim b/allocdemo/ramdisks.sim
deleted file mode 100644 (file)
index 7e18ecf..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-SugarSync = tmpfs:/tmp/.alloc/sugarsync
-Dropbox = tmpfs:/tmp/.alloc/dropbox
-*T*** Mediencenter = tmpfs:/tmp/.alloc/mediencenter
diff --git a/allocdemo/resources/dropbox.png b/allocdemo/resources/dropbox.png
deleted file mode 100644 (file)
index 1ee9850..0000000
Binary files a/allocdemo/resources/dropbox.png and /dev/null differ
diff --git a/allocdemo/resources/dropbox.resource b/allocdemo/resources/dropbox.resource
deleted file mode 100644 (file)
index 5d500ed..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-providericon = dropbox.png
-providername = Dropbox
-hostname = dropbox.com
-duration = 90
-usage = 44
-capacity = 9.5
-policy = Upgradeable Maximum
-
-updatefrequency = 1400
diff --git a/allocdemo/resources/mediencenter.png b/allocdemo/resources/mediencenter.png
deleted file mode 100644 (file)
index 575e3a7..0000000
Binary files a/allocdemo/resources/mediencenter.png and /dev/null differ
diff --git a/allocdemo/resources/mediencenter.resource b/allocdemo/resources/mediencenter.resource
deleted file mode 100644 (file)
index 0b22405..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-providericon = mediencenter.png
-providername = *T*** Mediencenter
-hostname = mediencenter.t-online.de
-duration = 55
-usage = 28
-capacity = 5.0
-policy = Fixed Maximum
-
-updatefrequency = 2200
diff --git a/allocdemo/resources/sugarsync.png b/allocdemo/resources/sugarsync.png
deleted file mode 100644 (file)
index b16432e..0000000
Binary files a/allocdemo/resources/sugarsync.png and /dev/null differ
diff --git a/allocdemo/resources/sugarsync.resource b/allocdemo/resources/sugarsync.resource
deleted file mode 100644 (file)
index 6d12038..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-providericon = sugarsync.png
-providername = SugarSync
-hostname = sugarsync.com
-duration = 19
-usage = 15
-capacity = 2.8
-policy = Unlimited
-
-updatefrequency = 4000
diff --git a/allocdemo/simulation-modes b/allocdemo/simulation-modes
deleted file mode 100644 (file)
index a8540b8..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-Simulation Modes
-================
-
-1. Full Simulation
-------------------
-
-In this mode, the resource and contract description information (resources/*.resource)
-is read in to initialise the simulator. Both the usage information and the capacity
-changes are simulated. There is no input (no file writes) and no output (no connection
-to the cloud) in this mode.
-
-2. Semi-Simulation
-------------------
-
-Connects to a filesystem so that files written to it impact the usage information and hence
-the capacity changes. This mode requires operating system access (possibly with root privileges).
-There are three possibilities:
-* a) Hard disk block device: Multiple possible, with different sizes, resizeable with high effort only.
-* b) RAM-Disk: Multiple possible, but only one size for all, not resizeable.
-* c) RAM filesystem tmpfs: Multiple possible, with different sizes, easily resizeable.
-*    Strange variant with size=0: With size equal to RAM, not resizeable, hidden in 'df' output(!)
-
-3. No Simulation
-----------------
-
-Connects to the cloud so that all usage information and capacity changes are real. Depending
-on the implementation (using FUSE or not), this mode still connects to a local filesystem
-but doesn't require root access anymore. However, it requires cloud account access.
-
-
diff --git a/allocdemo/single-ram.sim b/allocdemo/single-ram.sim
deleted file mode 100644 (file)
index e452b97..0000000
+++ /dev/null
@@ -1 +0,0 @@
-*T*** Mediencenter = tmpfs:/tmp/.alloc/mediencenter
diff --git a/allocdemo/single-sim.sim b/allocdemo/single-sim.sim
deleted file mode 100644 (file)
index cc74609..0000000
+++ /dev/null
@@ -1 +0,0 @@
-*T*** Mediencenter = simulation
diff --git a/allocdemo/web/alloc-ramdisk.png b/allocdemo/web/alloc-ramdisk.png
deleted file mode 100644 (file)
index 5661cfc..0000000
Binary files a/allocdemo/web/alloc-ramdisk.png and /dev/null differ
diff --git a/allocdemo/web/alloc-sim.png b/allocdemo/web/alloc-sim.png
deleted file mode 100644 (file)
index 4671068..0000000
Binary files a/allocdemo/web/alloc-sim.png and /dev/null differ
diff --git a/allocdemo/web/allocdemo-20111016.tar.gz b/allocdemo/web/allocdemo-20111016.tar.gz
deleted file mode 100644 (file)
index f6d7d4e..0000000
Binary files a/allocdemo/web/allocdemo-20111016.tar.gz and /dev/null differ
diff --git a/allocdemo/web/nubitree.png b/allocdemo/web/nubitree.png
deleted file mode 100644 (file)
index bb49554..0000000
Binary files a/allocdemo/web/nubitree.png and /dev/null differ
diff --git a/kde-cloudstorage/allocdemo/README.allocdemo b/kde-cloudstorage/allocdemo/README.allocdemo
new file mode 100644 (file)
index 0000000..4de0ec4
--- /dev/null
@@ -0,0 +1,36 @@
+Cloud Storage Allocator - Visualisation and Simulation Tool
+===========================================================
+
+This graphical tool demonstrates the effects of a smart cloud storage
+allocator. The idea is that an agent (e.g. a robot or an outdoor work team)
+produce much data which cannot be stored or processed locally. The allocator
+contacts cloud storage providers and allocates capacity by negotiating
+contracts. Depending on their non-functional properties, more providers
+will be contacted or existing allocation limits be raised. Eventually, the
+allocation shall always be optimal concerning the agent's preferences.
+
+Currently, the tool contains a simulator with a somewhat smart event
+generator to suggest non-existent disk usage, and a simulator which uses
+operating system facilities like RAM disks to pretent storage cloud behaviour.
+Later it will be attached to the real allocator to get
+proper realtime information about the usage of allocated cloud storage.
+See 'simulation-modes' for details of how the tool works.
+
+Installation Requirements:
+* Python
+* PyQt4
+
+Preparation of ramdisk integration through /etc/sudoers:
+allocuser ALL = NOPASSWD: /usr/local/bin/allocdemo-wrapper
+
+Invocation:
+./allocdemo [<*.sim>]
+
+Keys:
+* S - toggle view with/without scales
+* B - toggle view with/without aggregating bundle widget
+* P - pause the simulation (also Pause key)
+* +/- - influence the simulated usage
+* Tab - toggle between aggregation policies
+* ESC - quit the application
+
diff --git a/kde-cloudstorage/allocdemo/TODO b/kde-cloudstorage/allocdemo/TODO
new file mode 100644 (file)
index 0000000..30261c7
--- /dev/null
@@ -0,0 +1,5 @@
+- bei Reallokation: Generator durch 1.4 teilen, verhindert Kettenreaktion
+- adaptive oder großzügigere y-Achsenbeschriftung
+- Kapazität bei Unlimited-Provider an tatsächliches Datenaufkommen anpassen
+- (schwierig) Ansichten synchronisierbar gestalten (durch Leerevents bzw. Offset)
+
diff --git a/kde-cloudstorage/allocdemo/allocdemo b/kde-cloudstorage/allocdemo/allocdemo
new file mode 100755 (executable)
index 0000000..cdbf81c
--- /dev/null
@@ -0,0 +1,639 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from PyQt4 import QtGui, QtCore
+from PyQt4.QtCore import pyqtSlot, Qt
+import random
+import sys
+import glob
+import subprocess
+import os
+import optparse
+import math
+import codecs
+import datetime
+import locale
+
+#os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
+
+def dirsize(mountdir, apparent):
+       if apparent:
+               apparent = "--apparent"
+       else:
+               apparent = ""
+       size = subprocess.Popen("du --bytes -s %s '%s' 2>/dev/null | cut -f 1" % (apparent, mountdir), shell=True, stdout=subprocess.PIPE).communicate()[0].strip()
+       if not size:
+               size = None
+       else:
+               size = int(size)
+       return size
+
+class Contract:
+       def __init__(self):
+               self.providericon = None
+               self.providername = None
+               self.hostname = None
+               self.duration = None
+               self.usage = None
+               self.capacity = None
+               self.policy = None
+
+               self.expirydate = None
+               self.filled = None
+
+               self.mountdir = None
+
+               self.capacities = []
+
+       def calculate_expirydate(self):
+               expdate = datetime.datetime.now() + datetime.timedelta(self.duration - self.usage)
+               self.expirydate = expdate.strftime(locale.nl_langinfo(locale.D_T_FMT))
+
+       def allocate(self, date, capacity):
+               self.capacities.append((date, capacity))
+               self.capacity = capacity
+
+class ContractBundle(Contract):
+       def __init__(self):
+               Contract.__init__(self)
+               self.providername = u"RAOC=Σ"
+               self.hostname = ""
+               self.duration = 1
+               self.usage = 0
+               self.capacity = 1
+
+               self.contracts = []
+
+       def addcontract(self, contract):
+               self.contracts.append(contract)
+
+       def reconfigure(self, generatorpolicy):
+               datelist = [x.duration - x.usage for x in self.contracts]
+               if generatorpolicy == "smart-redundancy":
+                       datelist.sort()
+                       # FIXME: assumes capacity > security preference by falling back to mirroring/RAID1
+                       datelist = datelist[:2]
+               if generatorpolicy == "min-redundancy":
+                       self.duration = max(datelist)
+               elif generatorpolicy == "max-redundancy":
+                       self.duration = min(datelist)
+               elif generatorpolicy == "smart-redundancy":
+                       self.duration = min(datelist)
+               self.calculate_expirydate()
+
+class AggregationGenerator:
+       policies = ["min-redundancy", "max-redundancy", "smart-redundancy"]
+
+       def __init__(self):
+               self.policy = AggregationGenerator.policies[0]
+               self.n = None
+               self.k = None
+               self.reconfigure()
+
+       def value(self, contract):
+               #filled = 0
+               #for subcontract in contract.contracts:
+               #       filled += subcontract.filled
+               if self.policy == "min-redundancy":
+                       filled = reduce(lambda x, y: x + y, [x.filled for x in contract.contracts])
+               elif self.policy == "max-redundancy":
+                       filled = min([x.filled for x in contract.contracts])
+               elif self.policy == "smart-redundancy":
+                       filled = min([x.filled for x in contract.contracts])
+                       # FIXME: smarter calculation
+
+               contract.filled = filled
+               return filled
+
+       def reconfigure(self):
+               if self.policy == "min-redundancy":
+                       self.n = 2
+                       self.k = 1
+               elif self.policy == "max-redundancy":
+                       self.n = 1
+                       self.k = 1
+               elif self.policy == "smart-redundancy":
+                       self.n = 2
+                       self.k = 2
+
+       def param_n(self):
+               return self.n
+
+       def param_k(self):
+               return self.k
+
+       def level(self):
+               if self.k == 1 and self.n == 2:
+                       level = "striping"
+               elif self.k == 1 and self.n == 1:
+                       level = "mirroring"
+               else:
+                       level = "erasure-coding"
+               return level
+
+class SampleGenerator:
+       def __init__(self):
+               self.lastvalue = None
+
+       def value(self, contract):
+               if not self.lastvalue:
+                       self.lastvalue = random.random()
+               self.lastvalue += 0.05 - random.random() / 10
+               if random.random() < 0.03:
+                       self.lastvalue = random.random()
+               if self.lastvalue < 0:
+                       self.lastvalue = 0
+               if self.lastvalue > 1:
+                       self.lastvalue = 1
+               return self.lastvalue * contract.capacity
+
+class RamdiskGenerator:
+       def __init__(self, backing):
+               args = backing.split(":")
+               self.fs = args[0]
+               self.mountdir = args[1]
+               self.mountsize = 2000 * 1024
+
+               self.cleanup()
+
+               os.system("mkdir -p '%s'" % self.mountdir)
+               os.system("sudo allocdemo-wrapper mount '%s' %d" % (self.mountdir, self.mountsize))
+
+       def cleanup(self):
+               # FIXME: proper conditional cleanup handler
+               os.system("sudo allocdemo-wrapper umount '%s'" % self.mountdir)
+
+       def value(self, contract):
+               # FIXME: This is not the right place to change the contract
+               contract.capacity = self.mountsize
+               return dirsize(self.mountdir, False)
+
+       def resize(self, size):
+               self.mountsize = size
+               os.system("sudo allocdemo-wrapper resize '%s' %d" % (self.mountdir, size))
+
+class Alloc(QtCore.QObject):
+       onlinestates = ["online", "impaired", "offline"]
+
+       def __init__(self, contract):
+               QtCore.QObject.__init__(self)
+
+               self.contract = contract
+               self.online = "online"
+               self.points = []
+               self.dependencyallocs = []
+
+               self.generator = None
+               self.timer = None
+
+       def append(self, value):
+               self.points.append(value)
+               self.emit(QtCore.SIGNAL("valuesChanged()"))
+
+       def registerGenerator(self, generator, frequency):
+               self.generator = generator
+
+               self.timer = QtCore.QTimer()
+               self.connect(self.timer, QtCore.SIGNAL("timeout()"), self, QtCore.SLOT("slot_timer()"))
+               self.timer.start(frequency)
+
+               if isinstance(self.contract, ContractBundle):
+                       self.contract.reconfigure(self.generator.policy)
+
+       def registerDependencyAlloc(self, alloc):
+               self.dependencyallocs.append(alloc)
+               self.connect(alloc, QtCore.SIGNAL("allocationChanged()"), self, QtCore.SLOT("slot_allocation()"))
+
+       def reconfigure(self):
+               #self.allocate(0, self.capacity / 2)
+               # FIXME: needs points (in alloc) and subcontract fill status
+               self.slot_allocation()
+
+       @pyqtSlot()
+       def slot_timer(self):
+               newpoint = self.generator.value(self.contract)
+               self.append(newpoint)
+               self.contract.filled = newpoint
+
+               if isinstance(self.generator, SampleGenerator):
+                       if not isinstance(self.contract, ContractBundle):
+                               online = random.randint(0, 8)
+                               if online:
+                                       self.online = "online"
+                               else:
+                                       self.online = "offline"
+                               if newpoint > 0.8 * self.contract.capacity and self.contract.policy == "Upgradeable Maximum":
+                                       self.contract.allocate(len(self.points), self.contract.capacity * 1.4)
+                                       self.emit(QtCore.SIGNAL("allocationChanged()"))
+               elif isinstance(self.generator, RamdiskGenerator):
+                       self.online = "online"
+                       if not isinstance(self.contract, ContractBundle):
+                               if newpoint > 0.8 * self.contract.capacity and self.contract.policy == "Upgradeable Maximum":
+                                       self.generator.resize(self.contract.capacity * 1.4)
+                                       self.contract.allocate(len(self.points), self.generator.mountsize)
+                                       self.emit(QtCore.SIGNAL("allocationChanged()"))
+               elif isinstance(self.generator, AggregationGenerator):
+                       self.online = "online"
+                       all_offline = True
+                       for alloc in self.dependencyallocs:
+                               if alloc.online == "offline":
+                                       self.online = "impaired"
+                               else:
+                                       all_offline = False
+                       if all_offline:
+                               self.online = "offline"
+
+       @pyqtSlot()
+       def slot_allocation(self):
+               if self.generator.policy == "min-redundancy":
+                       aggregatedcapacity = reduce(lambda x, y: x + y, [x.capacity for x in self.contract.contracts])
+               elif self.generator.policy == "max-redundancy":
+                       aggregatedcapacity = min([x.capacity for x in self.contract.contracts])
+               elif self.generator.policy == "smart-redundancy":
+                       aggregatedcapacity = min([x.capacity for x in self.contract.contracts])
+                       # FIXME: smarter calculation
+
+               self.contract.allocate(len(self.points), aggregatedcapacity)
+               self.emit(QtCore.SIGNAL("allocationChanged()"))
+
+class AllocWidget(QtGui.QFrame):
+       def __init__(self, parent=None):
+               QtGui.QFrame.__init__(self, parent)
+
+               self.setMinimumWidth(400)
+               self.setMinimumHeight(150)
+
+               self.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Sunken)
+
+               pal = QtGui.QPalette()
+               pal.setColor(QtGui.QPalette.Window, QtGui.QColor(30, 30, 30))
+               self.setPalette(pal)
+               self.setAutoFillBackground(True)
+
+               self.viewscale = False
+
+               self.linecolor = QtGui.QColor(255, 255, 0)
+               self.areacolor = QtGui.QColor(255, 100, 0)
+
+               self.alloc = None
+
+       def paintEvent(self, paintevent):
+               if not self.alloc:
+                       return
+
+               painter = QtGui.QPainter(self)
+               painter.setPen(QtGui.QColor(255, 255, 255))
+
+               if self.alloc.contract.providericon:
+                       painter.drawPixmap(QtCore.QRect(10, 10, 32, 32), QtGui.QPixmap("resources/" + self.alloc.contract.providericon))
+
+               font = painter.font()
+               font.setPixelSize(32)
+               painter.setFont(font)
+               #painter.drawText(QtCore.QPoint(62, 42), self.alloc.name)
+               painter.drawText(QtCore.QRect(62, 10, self.width() - 72, 42), 0, self.alloc.contract.providername)
+
+               if isinstance(self.alloc.contract, ContractBundle):
+                       fsize = 24 / len(self.alloc.contract.contracts)
+                       font.setPixelSize(fsize)
+                       painter.setFont(font)
+                       for y, contract in enumerate(self.alloc.contract.contracts):
+                               painter.drawText(QtCore.QRect(62 + 140, 13 + y * (fsize + 3), self.width() - 72, fsize + 3), 0, contract.providername)
+
+               font.setPixelSize(12)
+               painter.setFont(font)
+               painter.drawText(QtCore.QRect(62, 47, self.width() - 72, 15), 0, self.alloc.contract.hostname)
+               if self.viewscale:
+                       painter.drawText(QtCore.QRect(62, 70, self.width() - 72, 15), 0, "Contract expiry: %s (%i days)" % (self.alloc.contract.expirydate, self.alloc.contract.duration - self.alloc.contract.usage))
+
+               if isinstance(self.alloc.contract, ContractBundle):
+                       painter.drawText(QtCore.QRect(62, 85, self.width() - 72, 15), 0, "Aggregation policy: %s" % self.alloc.generator.policy)
+                       if self.viewscale:
+                               n = self.alloc.generator.param_n()
+                               k = self.alloc.generator.param_k()
+                               level = self.alloc.generator.level()
+                               painter.drawText(QtCore.QRect(62, 47, self.width() - 72, 15), 0, "RAOC Level: %s (n=%d,k=%d)" % (level, n, k))
+               else:
+                       if self.viewscale:
+                               painter.drawText(QtCore.QRect(62, 85, self.width() - 72, 15), 0, "Allocation policy: %s" % self.alloc.contract.policy)
+
+               if self.alloc.online == "online":
+                       onlinecolor = QtGui.QColor(0, 255, 0)
+               elif self.alloc.online == "impaired":
+                       onlinecolor = QtGui.QColor(255, 255, 0)
+               else:
+                       onlinecolor = QtGui.QColor(255, 0, 0)
+               onlinestatus = self.alloc.online
+
+               painter.drawRect(QtCore.QRect(self.width() - 100, 50, 50, 12))
+               painter.fillRect(QtCore.QRect(self.width() - 99, 51, 49, 11), onlinecolor)
+
+               font.setPixelSize(10)
+               painter.setFont(font)
+               painter.setPen(QtGui.QColor(0, 0, 0))
+               painter.drawText(QtCore.QRect(self.width() - 100, 51, 49, 11), Qt.AlignCenter, onlinestatus)
+               painter.setPen(QtGui.QColor(255, 255, 255))
+
+               if self.viewscale:
+                       contractusage = float(self.alloc.contract.usage) / self.alloc.contract.duration
+                       if contractusage < 0.75:
+                               contractcolor = QtGui.QColor(0, 255, 0)
+                       elif contractusage < 0.90:
+                               contractcolor = QtGui.QColor(255, 255, 0)
+                       else:
+                               contractcolor = QtGui.QColor(255, 0, 0)
+
+                       painter.drawRect(QtCore.QRect(self.width() - 100, 73, 50, 12))
+                       painter.fillRect(QtCore.QRect(self.width() - 99, 74, contractusage * 48, 11), contractcolor)
+
+               diax = 0
+               diay = 100
+               diawidth = self.width() - 1
+               diaheight = self.height() - 1
+
+               if self.viewscale:
+                       diax = 40
+                       diaheight = self.height() - 21
+                       diawidth = self.width() - 11
+
+               maxcapacity = max([y for (x, y) in self.alloc.contract.capacities])
+
+               polygon = QtGui.QPolygonF()
+               polygon.append(QtCore.QPointF(diawidth, diaheight))
+               polygon.append(QtCore.QPointF(diax, diaheight))
+
+               pointlen = len(self.alloc.points) - 1
+               if pointlen == 0:
+                       pointlen = 1
+               for x, point in enumerate(self.alloc.points):
+                       cappoint = float(point) / maxcapacity
+                       polygon.append(QtCore.QPointF(x * (diawidth - diax) / pointlen + diax, (1.0 - cappoint) * (diaheight - diay) + diay))
+
+               painter.setPen(self.linecolor)
+               painter.setBrush(self.areacolor)
+               painter.drawPolygon(polygon)
+
+               fixedmaximum = False
+               upgradeablemaximum = False
+               allocationtrigger = False
+               if self.alloc.contract.policy == "Fixed Maximum":
+                       fixedmaximum = True
+               elif self.alloc.contract.policy == "Upgradeable Maximum":
+                       upgradeablemaximum = True
+                       allocationtrigger = True
+               if isinstance(self.alloc.contract, ContractBundle):
+                       # FIXME: more efficient calculation when adding a dependency contract vs. more storage
+                       # FIXME: consider upgradeable maximum vs. fixed maximum on the basis of generator.{n,k}
+                       if self.alloc.generator.policy == "min-redundancy":
+                               fixedmaximum = True
+                       for contract in self.alloc.contract.contracts:
+                               if self.alloc.generator.policy == "min-redundancy":
+                                       if contract.policy == "Upgradeable Maximum":
+                                               if fixedmaximum:
+                                                       fixedmaximum = False
+                                                       upgradeablemaximum = True
+                                       elif contract.policy == "Unlimited":
+                                                       fixedmaximum = False
+                                                       upgradeablemaximum = False
+                               elif self.alloc.generator.policy == "max-redundancy" or self.alloc.generator.policy == "smart-redundancy":
+                                       if contract.policy == "Fixed Maximum":
+                                               fixedmaximum = True
+                                       elif contract.policy == "Upgradeable Maximum":
+                                               if not fixedmaximum:
+                                                       fixedmaximum = False
+                                                       upgradeablemaximum = True
+
+               if fixedmaximum or upgradeablemaximum:
+                       if fixedmaximum:
+                               painter.setPen(QtGui.QPen(QtGui.QColor(200, 50, 0), 1, Qt.SolidLine))
+                       else:
+                               painter.setPen(QtGui.QPen(QtGui.QColor(200, 50, 0), 1, Qt.DashLine))
+                       alloclen = len(self.alloc.contract.capacities)
+                       for i in range(alloclen):
+                               (date, alloclimit) = self.alloc.contract.capacities[i]
+                               if i < alloclen - 1:
+                                       (dateend, alloclimitend) = self.alloc.contract.capacities[i + 1]
+                               else:
+                                       alloclimitend = alloclimit
+                                       dateend = len(self.alloc.points)
+                               # FIXME: date/variable spacing
+                               allocpoints = len(self.alloc.points)
+                               if allocpoints == 0:
+                                       allocpoints = 1
+                               x = date * (diawidth - diax) / allocpoints + diax
+                               xend = dateend * (diawidth - diax) / allocpoints + diax
+                               y = (1.0 - (alloclimit / maxcapacity)) * (diaheight - diay) + diay
+                               yend = (1.0 - (alloclimitend / maxcapacity)) * (diaheight - diay) + diay
+                               painter.drawLine(QtCore.QPoint(x, y), QtCore.QPoint(xend, y))
+                               painter.drawLine(QtCore.QPoint(xend, y), QtCore.QPoint(xend, yend))
+
+                       if self.viewscale:
+                               font.setPixelSize(10)
+                               painter.drawText(QtCore.QRect(diawidth - 100, 87, 100, 13), Qt.AlignRight, "Capacity")
+
+               if allocationtrigger and self.viewscale:
+                       painter.setPen(QtGui.QPen(QtGui.QColor(100, 80, 0), 1, Qt.DashLine))
+                       alloctrigger = 100 + 0.2 * (diaheight - diay)
+                       painter.drawLine(QtCore.QPoint(diax, alloctrigger), QtCore.QPoint(diawidth, alloctrigger))
+                       if self.viewscale:
+                               painter.drawText(QtCore.QRect(diawidth - 100, alloctrigger - 13, 100, 13), Qt.AlignRight, "Allocation Trigger")
+
+               if isinstance(self.alloc.generator, RamdiskGenerator) and self.viewscale:
+                       font.setPixelSize(8)
+                       painter.setFont(font)
+                       painter.setPen(QtGui.QColor(255, 200, 0))
+                       painter.drawText(QtCore.QRect(62 - 150, 47 + 4, self.width() - 72, 15), Qt.AlignRight, "RAMDISK:%s" % self.alloc.generator.mountdir)
+
+               if self.viewscale:
+                       magnitude = math.log(self.alloc.contract.capacity, 1024)
+                       units = ("Bytes", "kB", "MB", "GB", "TB")
+                       unit = units[int(magnitude)]
+
+                       font.setPixelSize(8)
+                       painter.setFont(font)
+                       painter.setPen(QtGui.QColor(255, 255, 255))
+                       painter.drawLine(QtCore.QPoint(diax, 100), QtCore.QPoint(diax, self.height() - 10))
+                       painter.drawLine(QtCore.QPoint(diax - 10, self.height() - 20), QtCore.QPoint(self.width() - 10, self.height() - 20))
+                       limit = (diaheight - 80) / 20
+                       for y in range(limit + 1):
+                               capsection = "%3.2f" % float(((limit - y) * float(self.alloc.contract.capacity) / limit) / (1024 ** int(magnitude)))
+                               pos = y * (diaheight - diay) / limit + diay
+                               painter.drawLine(QtCore.QPoint(diax - 10, pos), QtCore.QPoint(diax, pos))
+                               if y > 0:
+                                       painter.drawText(QtCore.QRect(0, pos - 10, diax - 1, 15), Qt.AlignRight, str(capsection))
+                       painter.drawText(QtCore.QRect(0, diay - 10, diax - 1, 15), Qt.AlignRight, unit)
+                       stepsize = len(self.alloc.points) / 15 + 1
+                       for x in range(0, len(self.alloc.points), stepsize):
+                               pos = x * (diawidth - diax) / pointlen + diax
+                               painter.drawLine(QtCore.QPoint(pos, self.height() - 20), QtCore.QPoint(pos, self.height() - 10))
+                               if x < len(self.alloc.points) - 1:
+                                       painter.drawText(QtCore.QRect(pos + 2, self.height() - 18, 15, 15), 0, str(x))
+
+               #painter.end()
+
+       def registerAlloc(self, alloc):
+               self.alloc = alloc
+               self.connect(alloc, QtCore.SIGNAL("valuesChanged()"), self, QtCore.SLOT("update()"))
+               self.connect(alloc, QtCore.SIGNAL("allocationChanged()"), self, QtCore.SLOT("update()"))
+
+       def toggleview(self):
+               self.viewscale = not self.viewscale
+               self.update()
+
+class AllocDemo(QtGui.QWidget):
+       def __init__(self, simscript, parent=None):
+               QtGui.QWidget.__init__(self, parent)
+
+               self.resize(520, 500)
+               self.setWindowTitle("Optimal Cloud Allocation [using %s]" % simscript)
+               self.setWindowIcon(QtGui.QIcon("data/code-block.png"))
+
+               self.allocs = []
+               self.generators = []
+               self.blocks = []
+
+               simbacking = {}
+               if simscript:
+                       lines = codecs.open(simscript, "r", "utf-8").readlines()
+                       for line in lines:
+                               args = line.strip().split("=")
+                               if len(args) == 2:
+                                       simbacking[args[0].strip()] = args[1].strip()
+
+               resources = glob.glob("resources/*.resource")
+               for resource in resources:
+                       contract = Contract()
+                       lines = codecs.open(resource, "r", "utf-8").readlines()
+                       for line in lines:
+                               args = line.strip().split("=")
+                               if len(args) != 2:
+                                       continue
+                               # FIXME: no other way of assigning arbitrary attributes?
+                               try:
+                                       exec("contract.%s = %s" % (args[0].strip(), args[1].strip()))
+                               except:
+                                       exec("contract.%s = u\"%s\"" % (args[0].strip(), args[1].strip()))
+
+                       if simscript:
+                               if not simbacking.has_key(contract.providername):
+                                       continue
+
+                       contract.calculate_expirydate()
+
+                       alloc = Alloc(contract)
+                       if simbacking.has_key(contract.providername) and simbacking[contract.providername] != "simulation":
+                               generator = RamdiskGenerator(simbacking[contract.providername])
+                               contract.allocate(0, generator.mountsize)
+                       else:
+                               generator = SampleGenerator()
+                               contract.allocate(0, contract.capacity)
+                       alloc.registerGenerator(generator, contract.updatefrequency)
+                       self.allocs.append(alloc)
+                       self.generators.append(generator)
+
+               bundle = ContractBundle()
+               for alloc in self.allocs:
+                       bundle.addcontract(alloc.contract)
+               bundle.calculate_expirydate()
+
+               alloc = Alloc(bundle)
+               generator = AggregationGenerator()
+               alloc.registerGenerator(generator, 4000)
+               self.allocs.append(alloc)
+               self.generators.append(generator)
+
+               for alloc in self.allocs:
+                       if isinstance(alloc.contract, ContractBundle):
+                               for contract in alloc.contract.contracts:
+                                       for depalloc in self.allocs:
+                                               if depalloc.contract == contract:
+                                                       alloc.registerDependencyAlloc(depalloc)
+                               alloc.slot_allocation()
+
+               self.setuplayout()
+
+       def cleanup(self):
+               for generator in self.generators:
+                       if "cleanup" in dir(generator):
+                               generator.cleanup()
+
+       def setuplayout(self):
+               grid = QtGui.QGridLayout()
+               self.setLayout(grid)
+
+               rowlimit = 3
+               if len(self.allocs) >= 4:
+                       rowlimit = 2
+
+               for i, alloc in enumerate(self.allocs):
+                       block = AllocWidget()
+                       block.registerAlloc(alloc)
+                       grid.addWidget(block, i % rowlimit, i / rowlimit)
+                       self.blocks.append(block)
+
+                       if isinstance(alloc.contract, ContractBundle):
+                               block.linecolor = QtGui.QColor(100, 100, 255)
+                               block.areacolor = QtGui.QColor(0, 0, 200)
+                               block.setVisible(False)
+
+       def keyPressEvent(self, event):
+               if event.key() == Qt.Key_Escape:
+                       #event.accept()
+                       self.cleanup()
+                       self.close()
+               elif event.key() == Qt.Key_S:
+                       for block in self.blocks:
+                               block.toggleview()
+               elif event.key() == Qt.Key_B:
+                       for block in self.blocks:
+                               if isinstance(block.alloc.contract, ContractBundle):
+                                       block.setVisible(not block.isVisible())
+               elif event.key() in (Qt.Key_P, Qt.Key_Pause):
+                       for alloc in self.allocs:
+                               if alloc.timer.isActive():
+                                       alloc.timer.stop()
+                               else:
+                                       alloc.timer.start()
+               elif event.key() in (Qt.Key_Plus, Qt.Key_Minus):
+                       for generator in self.generators:
+                               if isinstance(generator, SampleGenerator):
+                                       if generator.lastvalue:
+                                               if event.key() == Qt.Key_Plus:
+                                                       generator.lastvalue += 0.1
+                                               else:
+                                                       generator.lastvalue -= 0.1
+               elif event.key() == Qt.Key_Tab:
+                       for generator in self.generators:
+                               if isinstance(generator, AggregationGenerator):
+                                       pos = AggregationGenerator.policies.index(generator.policy)
+                                       pos += 1
+                                       if pos == len(AggregationGenerator.policies):
+                                               pos = 0
+                                       generator.policy = AggregationGenerator.policies[pos]
+                                       generator.reconfigure()
+                       for block in self.blocks:
+                               if isinstance(block.alloc.contract, ContractBundle):
+                                       block.update()
+                       for alloc in self.allocs:
+                               if isinstance(alloc.contract, ContractBundle):
+                                       alloc.contract.reconfigure(alloc.generator.policy)
+                                       alloc.reconfigure()
+
+parser = optparse.OptionParser()
+#parser.add_option("-h", "--help", help="Script help")
+
+(options, args) = parser.parse_args()
+
+simscript = None
+
+if len(args) == 1:
+       simscript = args[0]
+elif len(args) > 1:
+       print >>sys.stderr, "Syntax: allocdemo [<*.sim>]"
+       sys.exit(1)
+
+app = QtGui.QApplication(sys.argv)
+allocdemo = AllocDemo(simscript)
+allocdemo.move(500, 200)
+allocdemo.show()
+sys.exit(app.exec_())
diff --git a/kde-cloudstorage/allocdemo/data/allocdemo-wrapper b/kde-cloudstorage/allocdemo/data/allocdemo-wrapper
new file mode 100755 (executable)
index 0000000..e2dd7f3
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+cmd=$1
+
+if [ -z $cmd ]
+then
+       echo "Syntax: allocdemo-wrapper <cmd> [cmdoptions...]" >&2
+       exit 1
+fi
+
+case $cmd in
+       mount)
+               mountpoint=$2
+               size=$3
+               mount -t tmpfs -o size=$size tmpfs "$mountpoint"
+       ;;
+       umount)
+               mountpoint=$2
+               umount "$mountpoint" 2>/dev/null
+       ;;
+       resize)
+               mountpoint=$2
+               size=$3
+               mount -o remount,size=$size "$mountpoint"
+       ;;
+       *)
+               echo "Error: Invalid command $cmd" >&2
+               echo "Valid commands:"
+               echo "* mount  <mountpoint> <size>"
+               echo "* resize <mountpoint> <size>"
+               echo "* umount <mountpoint>"
+               exit 1
+       ;;
+esac
diff --git a/kde-cloudstorage/allocdemo/data/code-block.png b/kde-cloudstorage/allocdemo/data/code-block.png
new file mode 100644 (file)
index 0000000..68a5460
Binary files /dev/null and b/kde-cloudstorage/allocdemo/data/code-block.png differ
diff --git a/kde-cloudstorage/allocdemo/ramdisks.sim b/kde-cloudstorage/allocdemo/ramdisks.sim
new file mode 100644 (file)
index 0000000..7e18ecf
--- /dev/null
@@ -0,0 +1,3 @@
+SugarSync = tmpfs:/tmp/.alloc/sugarsync
+Dropbox = tmpfs:/tmp/.alloc/dropbox
+*T*** Mediencenter = tmpfs:/tmp/.alloc/mediencenter
diff --git a/kde-cloudstorage/allocdemo/resources/dropbox.png b/kde-cloudstorage/allocdemo/resources/dropbox.png
new file mode 100644 (file)
index 0000000..1ee9850
Binary files /dev/null and b/kde-cloudstorage/allocdemo/resources/dropbox.png differ
diff --git a/kde-cloudstorage/allocdemo/resources/dropbox.resource b/kde-cloudstorage/allocdemo/resources/dropbox.resource
new file mode 100644 (file)
index 0000000..5d500ed
--- /dev/null
@@ -0,0 +1,9 @@
+providericon = dropbox.png
+providername = Dropbox
+hostname = dropbox.com
+duration = 90
+usage = 44
+capacity = 9.5
+policy = Upgradeable Maximum
+
+updatefrequency = 1400
diff --git a/kde-cloudstorage/allocdemo/resources/mediencenter.png b/kde-cloudstorage/allocdemo/resources/mediencenter.png
new file mode 100644 (file)
index 0000000..575e3a7
Binary files /dev/null and b/kde-cloudstorage/allocdemo/resources/mediencenter.png differ
diff --git a/kde-cloudstorage/allocdemo/resources/mediencenter.resource b/kde-cloudstorage/allocdemo/resources/mediencenter.resource
new file mode 100644 (file)
index 0000000..0b22405
--- /dev/null
@@ -0,0 +1,9 @@
+providericon = mediencenter.png
+providername = *T*** Mediencenter
+hostname = mediencenter.t-online.de
+duration = 55
+usage = 28
+capacity = 5.0
+policy = Fixed Maximum
+
+updatefrequency = 2200
diff --git a/kde-cloudstorage/allocdemo/resources/sugarsync.png b/kde-cloudstorage/allocdemo/resources/sugarsync.png
new file mode 100644 (file)
index 0000000..b16432e
Binary files /dev/null and b/kde-cloudstorage/allocdemo/resources/sugarsync.png differ
diff --git a/kde-cloudstorage/allocdemo/resources/sugarsync.resource b/kde-cloudstorage/allocdemo/resources/sugarsync.resource
new file mode 100644 (file)
index 0000000..6d12038
--- /dev/null
@@ -0,0 +1,9 @@
+providericon = sugarsync.png
+providername = SugarSync
+hostname = sugarsync.com
+duration = 19
+usage = 15
+capacity = 2.8
+policy = Unlimited
+
+updatefrequency = 4000
diff --git a/kde-cloudstorage/allocdemo/simulation-modes b/kde-cloudstorage/allocdemo/simulation-modes
new file mode 100644 (file)
index 0000000..a8540b8
--- /dev/null
@@ -0,0 +1,30 @@
+Simulation Modes
+================
+
+1. Full Simulation
+------------------
+
+In this mode, the resource and contract description information (resources/*.resource)
+is read in to initialise the simulator. Both the usage information and the capacity
+changes are simulated. There is no input (no file writes) and no output (no connection
+to the cloud) in this mode.
+
+2. Semi-Simulation
+------------------
+
+Connects to a filesystem so that files written to it impact the usage information and hence
+the capacity changes. This mode requires operating system access (possibly with root privileges).
+There are three possibilities:
+* a) Hard disk block device: Multiple possible, with different sizes, resizeable with high effort only.
+* b) RAM-Disk: Multiple possible, but only one size for all, not resizeable.
+* c) RAM filesystem tmpfs: Multiple possible, with different sizes, easily resizeable.
+*    Strange variant with size=0: With size equal to RAM, not resizeable, hidden in 'df' output(!)
+
+3. No Simulation
+----------------
+
+Connects to the cloud so that all usage information and capacity changes are real. Depending
+on the implementation (using FUSE or not), this mode still connects to a local filesystem
+but doesn't require root access anymore. However, it requires cloud account access.
+
+
diff --git a/kde-cloudstorage/allocdemo/single-ram.sim b/kde-cloudstorage/allocdemo/single-ram.sim
new file mode 100644 (file)
index 0000000..e452b97
--- /dev/null
@@ -0,0 +1 @@
+*T*** Mediencenter = tmpfs:/tmp/.alloc/mediencenter
diff --git a/kde-cloudstorage/allocdemo/single-sim.sim b/kde-cloudstorage/allocdemo/single-sim.sim
new file mode 100644 (file)
index 0000000..cc74609
--- /dev/null
@@ -0,0 +1 @@
+*T*** Mediencenter = simulation
diff --git a/kde-cloudstorage/allocdemo/web/alloc-ramdisk.png b/kde-cloudstorage/allocdemo/web/alloc-ramdisk.png
new file mode 100644 (file)
index 0000000..5661cfc
Binary files /dev/null and b/kde-cloudstorage/allocdemo/web/alloc-ramdisk.png differ
diff --git a/kde-cloudstorage/allocdemo/web/alloc-sim.png b/kde-cloudstorage/allocdemo/web/alloc-sim.png
new file mode 100644 (file)
index 0000000..4671068
Binary files /dev/null and b/kde-cloudstorage/allocdemo/web/alloc-sim.png differ
diff --git a/kde-cloudstorage/allocdemo/web/allocdemo-20111016.tar.gz b/kde-cloudstorage/allocdemo/web/allocdemo-20111016.tar.gz
new file mode 100644 (file)
index 0000000..f6d7d4e
Binary files /dev/null and b/kde-cloudstorage/allocdemo/web/allocdemo-20111016.tar.gz differ
diff --git a/kde-cloudstorage/allocdemo/web/nubitree.png b/kde-cloudstorage/allocdemo/web/nubitree.png
new file mode 100644 (file)
index 0000000..bb49554
Binary files /dev/null and b/kde-cloudstorage/allocdemo/web/nubitree.png differ
diff --git a/kde-cloudstorage/delivery-frontends/FortuneClientMobileScreenshot.png b/kde-cloudstorage/delivery-frontends/FortuneClientMobileScreenshot.png
new file mode 100644 (file)
index 0000000..55def6d
Binary files /dev/null and b/kde-cloudstorage/delivery-frontends/FortuneClientMobileScreenshot.png differ
diff --git a/kde-cloudstorage/delivery-frontends/FortuneClientScreenshot.jpg b/kde-cloudstorage/delivery-frontends/FortuneClientScreenshot.jpg
new file mode 100644 (file)
index 0000000..106b971
Binary files /dev/null and b/kde-cloudstorage/delivery-frontends/FortuneClientScreenshot.jpg differ
diff --git a/kde-cloudstorage/delivery-frontends/FortunePlasmoidScreenshot.png b/kde-cloudstorage/delivery-frontends/FortunePlasmoidScreenshot.png
new file mode 100644 (file)
index 0000000..eefbb63
Binary files /dev/null and b/kde-cloudstorage/delivery-frontends/FortunePlasmoidScreenshot.png differ
diff --git a/kde-cloudstorage/delivery-frontends/dynvoker.org.png b/kde-cloudstorage/delivery-frontends/dynvoker.org.png
new file mode 100644 (file)
index 0000000..6d50c21
Binary files /dev/null and b/kde-cloudstorage/delivery-frontends/dynvoker.org.png differ
diff --git a/kde-cloudstorage/delivery-frontends/fortuneservice.clientsxml b/kde-cloudstorage/delivery-frontends/fortuneservice.clientsxml
new file mode 100644 (file)
index 0000000..809f6bb
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<serviceclients>
+  <client name="fs_rubyqtclient" type="standalone">
+    <description>
+       FortuneService Standalone Client written with Ruby and Qt
+    </description>
+    <image>
+      FortuneClientScreenshot.jpg
+    </image>
+    <version>
+      1.1
+    </version>
+    <url>
+      http://localhost/servicedata/fortuneclient.zip
+    </url>
+    <requirements>
+      <requirement tag="Desktop"/>
+      <requirement tag="Qt" minVersion="4.5"/>
+      <requirement tag="Ruby" minVersion="1.8"/>
+    </requirements>
+  </client>
+  <client name="fs_rubyqtclient_mobile" type="standalone">
+    <description>
+       Mobile FortuneService Standalone Client written with Ruby and Qt
+    </description>
+    <image>
+      FortuneClientMobileScreenshot.png
+    </image>
+    <version>
+      1.1
+    </version>
+    <url>
+      http://localhost/servicedata/fortuneclient.zip
+    </url>
+    <requirements>
+      <requirement tag="Smartphone"/>
+      <requirement tag="Qt" minVersion="4.5"/>
+      <requirement tag="Ruby" minVersion="1.8"/>
+    </requirements>
+  </client>
+  <client name="fs_plasmoidclient" type="plasmoid">
+    <description>
+       FortuneService Client for KDE Plasma written with Python
+    </description>
+    <image>
+      FortunePlasmoidScreenshot.png
+    </image>
+    <version>
+      1.1
+    </version>
+    <url>
+      http://localhost/servicedata/fortunemoid.zip
+    </url>
+    <requirements>
+      <requirement tag="Desktop"/>
+      <requirement tag="Plasma" minVersion="4.3"/>
+      <requirement tag="Python" minVersion="2.5"/>
+    </requirements>
+  </client>
+</serviceclients>
diff --git a/kde-cloudstorage/delivery-frontends/servicefrontenddisplayer b/kde-cloudstorage/delivery-frontends/servicefrontenddisplayer
new file mode 100755 (executable)
index 0000000..facfcef
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from PyQt4 import QtGui, QtCore
+from PyQt4.QtCore import pyqtSlot, Qt
+import sys
+import xml.dom.minidom
+
+class Client:
+       def __init__(self):
+               self.clienttype = None
+               self.description = None
+               self.image = None
+               self.version = None
+
+class FrontendsWidget(QtGui.QWidget):
+       def __init__(self, parent=None):
+               QtGui.QWidget.__init__(self, parent)
+
+               self.resize(500, 300)
+               self.setWindowTitle("Service frontend selection")
+
+               clients = []
+               clientsxml = xml.dom.minidom.parse("fortuneservice.clientsxml")
+               for clientxml in clientsxml.documentElement.childNodes:
+                       if isinstance(clientxml, xml.dom.minidom.Element) and clientxml.nodeName == "client":
+                               #print clientxml
+                               client = Client()
+                               client.clienttype = clientxml.getAttribute("type")
+                               client.description = clientxml.getElementsByTagName("description")[0].firstChild.nodeValue.strip()
+                               client.image = clientxml.getElementsByTagName("image")[0].firstChild.nodeValue.strip()
+                               client.version = clientxml.getElementsByTagName("version")[0].firstChild.nodeValue.strip()
+                               #print client.clienttype, client.description, client.image, client.version
+                               clients.append(client)
+
+               webclient = Client()
+               webclient.clienttype = "web"
+               webclient.description = "Dynvoker: Dynamically created web service client"
+               webclient.image = "dynvoker.org.png"
+               webclient.version = "beta"
+               clients.append(webclient)
+
+               layout = QtGui.QGridLayout(self)
+               layout.setSpacing(10)
+               for i, client in enumerate(clients):
+                       pixmap = QtGui.QPixmap(client.image).scaledToWidth(128)
+                       pixwidget = QtGui.QLabel()
+                       pixwidget.setPixmap(pixmap)
+                       layout.addWidget(pixwidget, i, 0)
+
+                       clienttype = "Unknown frontend"
+                       if client.clienttype == "plasmoid":
+                               clienttype = "Desktop Applet"
+                       elif client.clienttype == "standalone":
+                               clienttype = "Standalone Client"
+                       elif client.clienttype == "web":
+                               clienttype = "Web Application"
+
+                       title = QtGui.QLabel("<b>" + clienttype + "</b>")
+                       desc = QtGui.QLabel(client.description)
+                       version = QtGui.QLabel("<i>Version: " + client.version + "</i>")
+
+                       sublayout = QtGui.QVBoxLayout()
+                       sublayout.addWidget(title)
+                       sublayout.addWidget(desc)
+                       sublayout.addWidget(version)
+                       sublayout.addStretch()
+                       layout.addLayout(sublayout, i, 1)
+
+       def keyPressEvent(self, event):
+               if event.key() == Qt.Key_Escape:
+                       self.close()
+
+app = QtGui.QApplication(sys.argv)
+few = FrontendsWidget()
+few.move(350, 200)
+few.show()
+sys.exit(app.exec_())
diff --git a/kde-cloudstorage/kde-integration/README.integration b/kde-cloudstorage/kde-integration/README.integration
new file mode 100644 (file)
index 0000000..4a664b4
--- /dev/null
@@ -0,0 +1,24 @@
+nubi-network.desktop:
+ - makes NubiSave folder appear in the network places in the file dialogue
+ - copy to /usr/share/kde4/apps/remoteview
+
+syncwithcloud_sm.desktop:
+ - links a folder or file to the NubiSave folder
+ - copy to /usr/share/kde4/services/ServiceMenus
+
+nubisavelink:
+ - helper application for "sync with cloud"
+ - copy to /usr/(local/)bin
+
+cloudfolder.png (from ../icons):
+ - nicer icon for the network places
+ - copy to /usr/share/icons/oxygen/64x64/places
+
+syncme:
+ - asynchronous delayed incremental copying of files and directories
+ - copy to /usr/(local/)bin
+ - NOTE: This tool can also be used independently for all sorts of sync tasks!
+
+rsync-kde:
+ - wrapper around 'rsync' which adds a graphical progress bar
+ - copy to /usr/(local/)bin
diff --git a/kde-cloudstorage/kde-integration/kde-cloudstorage.dia b/kde-cloudstorage/kde-integration/kde-cloudstorage.dia
new file mode 100644 (file)
index 0000000..9ca69d9
Binary files /dev/null and b/kde-cloudstorage/kde-integration/kde-cloudstorage.dia differ
diff --git a/kde-cloudstorage/kde-integration/kde-cloudstorage.png b/kde-cloudstorage/kde-integration/kde-cloudstorage.png
new file mode 100644 (file)
index 0000000..f47d620
Binary files /dev/null and b/kde-cloudstorage/kde-integration/kde-cloudstorage.png differ
diff --git a/kde-cloudstorage/kde-integration/nubi-network.desktop b/kde-cloudstorage/kde-integration/nubi-network.desktop
new file mode 100644 (file)
index 0000000..07f1a61
--- /dev/null
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Icon=cloudfolder
+Name=NubiSave Optimal Cloud Storage
+Open=false
+Type=Link
+URL[$e]=file://${HOME}/nubisave/
diff --git a/kde-cloudstorage/kde-integration/nubisavelink b/kde-cloudstorage/kde-integration/nubisavelink
new file mode 100755 (executable)
index 0000000..adb2dc6
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# Links a file or directory to the NubiSave splitter folder
+
+splitterfolder=$HOME/nubisave
+
+which syncme >/dev/null
+
+if [ -z $1 ]
+then
+       echo "Syntax error: nubisavelink [--move] <sourcepath>" >&2
+       exit 1
+fi
+
+move=0
+src=$1
+if [ "$src" = "--move" ]
+then
+       move=1
+       src=$2
+fi
+
+if [ $move -eq 0 ]
+then
+       if [ $? != 0 ]
+       then
+               # FIXME: Symbolic links are not supported, obviously
+               #ln -sf "$src" $splitterfolder
+               cp -r "$src" "$splitterfolder"
+       else
+               syncme add "$src" "$splitterfolder"
+               # FIXME: config reload workaround by forced restart
+               syncme stop
+               syncme start
+       fi
+else
+       basesrc=`basename $src`
+       mv "$src" "$splitterfolder"
+       ln -sf "$splitterfolder/$basesrc" "$src"
+fi
+
diff --git a/kde-cloudstorage/kde-integration/rsync-kde b/kde-cloudstorage/kde-integration/rsync-kde
new file mode 100755 (executable)
index 0000000..534b1a6
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# Displays an rsync progress bar among the KIO job notifications
+# Syntax: rsync-kde <sourcedir> <targetdir>
+
+source=$1
+target=$2
+
+if [ -z $source ] || [ -z $target ]
+then
+       echo "Syntax error: rsync-kde <sourcedir> <targetdir>" >&2
+       exit 1
+fi
+
+rsp=`qdbus --literal org.kde.JobViewServer /JobViewServer requestView "Cloud Sync" "cloudfolder" 7`
+#rsp="[ObjectPath: /JobViewServer/JobView_50]"
+path=`echo $rsp | sed -e 's/\[ObjectPath: \(.*\)\]/\1/'`
+
+#qdbus org.kde.JobViewServer $path setDestUrl "http://foo"
+qdbus org.kde.JobViewServer $path setSuspended True
+qdbus org.kde.JobViewServer $path setInfoMessage "Synchronising $source into the cloud [$target]"
+sleep 1
+qdbus org.kde.JobViewServer $path setSuspended False
+
+allfiles=`find $source | wc -l`
+logfile=~/._rsynckdelog$$
+
+rsync -avz $source $target > $logfile &
+
+stats=0
+while [ $stats -lt 100 ]
+do
+       syncedfiles=`wc -l $logfile | cut -d " " -f 1`
+       stats=$((100*$syncedfiles/$allfiles))
+       qdbus org.kde.JobViewServer $path setPercent $stats
+       sleep 1
+done
+
+rm -f $logfile
+qdbus org.kde.JobViewServer $path terminate ""
+
diff --git a/kde-cloudstorage/kde-integration/syncme b/kde-cloudstorage/kde-integration/syncme
new file mode 100755 (executable)
index 0000000..977b952
--- /dev/null
@@ -0,0 +1,270 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Tool for maintaining synchronisation links between directories
+
+import sys
+import os
+import signal
+import errno
+import time
+import optparse
+import xml.dom.minidom
+import inotifyx
+
+class Link:
+       def __init__(self, sourcedir, targetdir):
+               self.sourcedir = sourcedir
+               self.targetdir = targetdir
+               self.versioned = None
+
+class LinkCollection:
+       def __init__(self, configdir):
+               self.configdir = configdir
+               self.links = []
+
+       def load(self):
+               config = os.path.join(self.configdir, "syncme-links.xml")
+               try:
+                       f = open(config)
+               except:
+                       return
+               dom = xml.dom.minidom.parseString(" ".join(f.readlines()))
+               f.close()
+
+               if dom.documentElement.localName != "links":
+                       print >>sys.stderr, "Warning: Configuration file '%s' is misformatted." % config
+                       return
+
+               linksxmllist = dom.documentElement.getElementsByTagName("link")
+               for linksxml in linksxmllist:
+                       link = Link(linksxml.getAttribute("sourcedir"), linksxml.getAttribute("targetdir"))
+                       link.versioned = (linksxml.getAttribute("versioned") == "true")
+                       self.links.append(link)
+
+       def save(self):
+               dom = xml.dom.minidom.Document()
+               root = dom.createElement("links")
+               dom.appendChild(root)
+               for link in self.links:
+                       linkxml = dom.createElement("link")
+                       linkxml.setAttribute("sourcedir", link.sourcedir)
+                       linkxml.setAttribute("targetdir", link.targetdir)
+                       linkxml.setAttribute("versioned", ("false", "true")[int(link.versioned)])
+                       root.appendChild(linkxml)
+
+               try:
+                       os.makedirs(self.configdir)
+               except os.error, e:
+                       if e.errno != errno.EEXIST:
+                               raise
+
+               config = os.path.join(self.configdir, "syncme-links.xml")
+               f = open(config, "w")
+               dom.writexml(f, addindent=" ", newl="\n")
+               f.close()
+
+class Syncer():
+       def __init__(self, sourcedir, targetdir, versioned):
+               self.sourcedir = sourcedir
+               self.targetdir = targetdir
+               # FIXME: versioning is ignored for now (as is adaptation etc.)
+
+       def sync(self):
+               print "[syncme-daemon (%s->%s)] start" % (self.sourcedir, self.targetdir)
+               os.system("rsync -az '%s' '%s'" % (self.sourcedir, self.targetdir))
+               print "[syncme-daemon (%s->%s)] finish" % (self.sourcedir, self.targetdir)
+
+class Daemon():
+       def __init__(self):
+               pass
+
+       def launch(self):
+               logfile = os.path.join(links.configdir, "daemon.log")
+               log = open(logfile, "a")
+               sys.stdout = log
+               sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 0)
+               print "** Daemon launch at %s" % time.ctime()
+
+               notifyeventmask = inotifyx.IN_DELETE | inotifyx.IN_CLOSE_WRITE
+               notifier = inotifyx.init()
+               configwatcher = inotifyx.add_watch(notifier, os.path.join(links.configdir, "syncme-links.xml"), notifyeventmask)
+
+               syncers = {}
+               watchers = {}
+               for link in links.links:
+                       syncer = Syncer(link.sourcedir, link.targetdir, link.versioned)
+                       if not syncers.has_key(link.sourcedir):
+                               syncers[link.sourcedir] = []
+                       syncers[link.sourcedir].append(syncer)
+               for sourcedir in syncers.keys():
+                       sourcedirwatcher = inotifyx.add_watch(notifier, sourcedir, notifyeventmask)
+                       watchers[sourcedirwatcher] = sourcedir
+
+               for syncer in sum(syncers.values(), []):
+                       syncer.sync()
+
+               halt_daemon = False
+               while not halt_daemon:
+                       #time.sleep(60)
+                       events = inotifyx.get_events(notifier)
+                       for event in events:
+                               if event.wd == configwatcher:
+                                       print "[notification] Configuration has changed, reload!"
+                                       # FIXME: Perform reloading, merge with existing config to avoid unneeded sync runs!
+                               else:
+                                       sourcedir = watchers[event.wd]
+                                       print "[notification] Sourcedir [%s] has changed, run syncer!" % sourcedir
+                                       for syncer in syncers[sourcedir]:
+                                               syncer.sync()
+
+               inotifyx.rm_watch(notifier, configwatcher)
+               for watcher in watcher.keys():
+                       inotifyx.rm_watch(notifier, watcher)
+               os.close(notifier)
+
+               self.terminate(os.getpid())
+
+       def terminate(self, pid):
+               os.kill(pid, signal.SIGTERM)
+               pidfile = os.path.join(links.configdir, ".pid")
+               os.unlink(pidfile)
+
+       def writepid(self, pid):
+               pidfile = os.path.join(links.configdir, ".pid")
+               f = open(pidfile, "w")
+               print >>f, pid
+               f.close()
+
+       def readpid(self):
+               pidfile = os.path.join(links.configdir, ".pid")
+               try:
+                       f = open(pidfile)
+               except:
+                       return -1
+               pid = f.read().strip()
+               f.close()
+               return int(pid)
+
+links = LinkCollection(os.path.expanduser('~') + "/.syncme")
+links.load()
+
+def daemon_start():
+       d = Daemon()
+       pid = d.readpid()
+       if pid > 0:
+               print >>sys.stderr, "Error: Daemon already running."
+               sys.exit(-1)
+       pid = os.fork()
+       if pid == 0:
+               d = Daemon()
+               d.launch()
+               sys.exit(0)
+       elif pid > 0:
+               d = Daemon()
+               d.writepid(pid)
+               sys.exit(0)
+       else:
+               print >>sys.stderr, "Error: Cannot fork."
+               sys.exit(-1)
+
+def daemon_stop():
+       d = Daemon()
+       pid = d.readpid()
+       if pid < 0:
+               print >>sys.stderr, "Error: Daemon not running."
+               sys.exit(-1)
+       d.terminate(pid)
+
+def daemon_status():
+       d = Daemon()
+       pid = d.readpid()
+       if pid < 0:
+               print "Daemon is not running."
+       elif pid > 0:
+               print "Daemon is running."
+
+               if not os.path.exists(os.path.join("/proc", str(pid))):
+                       print "Warning: The system state appears to be invalid."
+               # FIXME: add more pid comparison logic to detect stale pidfiles, also for start/stop
+
+def links_list():
+       source_label = "Source directory"
+       target_label = "Target directory"
+       option_label = "Options"
+
+       source_maxlen = max([len(link.sourcedir) for link in links.links] + [len(source_label)])
+       target_maxlen = max([len(link.targetdir) for link in links.links] + [len(target_label)])
+
+       print source_label, " " * (source_maxlen - len(source_label)) + "|",
+       print target_label, " " * (target_maxlen - len(target_label)) + "|",
+       print option_label
+       print "-" * source_maxlen,
+       print "+",
+       print "-" * target_maxlen,
+       print "+",
+       print "-" * len(option_label)
+
+       for link in links.links:
+               print link.sourcedir, " " * (source_maxlen - len(link.sourcedir)) + "|",
+               print link.targetdir, " " * (target_maxlen - len(link.targetdir)) + "|",
+               print ("", "versioned")[int(link.versioned)]
+
+def links_add(sourcedir, destdir, versioned):
+       link = Link(sourcedir, destdir)
+       link.versioned = (versioned == True)
+       links.links.append(link)
+       links.save()
+
+parser = optparse.OptionParser()
+parser.add_option("-v", "--versioned", action="store_true", help="Synchronisation should be versioned")
+parser.add_option("-H", "--help-commands", action="store_true", help="List available tool commands")
+(options, args) = parser.parse_args()
+
+if options.help_commands:
+       print "Daemon control commands:"
+       print "* start:  Start the synchronisation routines asynchronously"
+       print "* stop:   Stop the synchronisation routines"
+       print "* status: Show if the daemon is already running"
+       print ""
+       print "Synchronisation links maintenance commands:"
+       print "* list:   Show all links and their status"
+       print "* add:    Add an additional link to be synchronised"
+       print "          (Syntax: add [--versioned] <sourcedir> <destdir>)"
+       sys.exit(0)
+
+if len(args) == 0:
+       print >>sys.stderr, "Error: Needs a command, see --help-commands."
+       sys.exit(1)
+
+cmd = args[0]
+
+if cmd == "start":
+       if len(args) > 1:
+               print >>sys.stderr, "Error: Superfluous arguments."
+               sys.exit(1)
+       daemon_start()
+elif cmd == "stop":
+       if len(args) > 1:
+               print >>sys.stderr, "Error: Superfluous arguments."
+               sys.exit(1)
+       daemon_stop()
+elif cmd == "status":
+       if len(args) > 1:
+               print >>sys.stderr, "Error: Superfluous arguments."
+               sys.exit(1)
+       daemon_status()
+elif cmd == "list":
+       if len(args) > 1:
+               print >>sys.stderr, "Error: Superfluous arguments."
+               sys.exit(1)
+       links_list()
+elif cmd == "add":
+       if len(args) != 3:
+               print >>sys.stderr, "Error: Incorrect number of arguments."
+               sys.exit(1)
+       links_add(args[1], args[2], options.versioned)
+else:
+       print >>sys.stderr, "Error: '%s' is not a valid command, see --help-commands." % cmd
+       sys.exit(1)
+
diff --git a/kde-cloudstorage/kde-integration/syncwithcloud_sm.desktop b/kde-cloudstorage/kde-integration/syncwithcloud_sm.desktop
new file mode 100644 (file)
index 0000000..67e9e6d
--- /dev/null
@@ -0,0 +1,18 @@
+[Desktop Entry]
+Actions=SyncWithCloud;MoveIntoCloud;
+Type=Service
+Icon=
+Name=
+X-KDE-ServiceTypes=KonqPopupMenu/Plugin,application/octet-stream,inode/directory
+
+[Desktop Action SyncWithCloud]
+Exec=nubisavelink %U
+Icon=cloudfolder
+Name=Sync with the cloud
+Name[de]=Mit der Cloud synchronisieren
+
+[Desktop Action MoveIntoCloud]
+Exec=nubisavelink --move %U
+Icon=cloudfolder
+Name=Move into the cloud
+Name[de]=In die Cloud verschieben
diff --git a/kde-cloudstorage/nubi-tree/README.nubi-tree b/kde-cloudstorage/nubi-tree/README.nubi-tree
new file mode 100644 (file)
index 0000000..0494e1b
--- /dev/null
@@ -0,0 +1,15 @@
+NubiTree - mockup for integrating NubiSave into the desktop
+Josef Spillner <josef.spillner@tu-dresden.de>
+===========================================================
+
+NubiSave (nubisave.org) is an open source optimal cloud storage controller.
+Optimality criteria such as security, performance or cost are requested from the
+user to define both the optimal set of storage providers and optimal allocation
+and scheduling strategies.
+
+NubiTree is supposed to become a convenient PyQt user interface as alternative
+to the functional but rather clumsy Java GUI which ships with NubiSave. Both GUIs
+are supposed to be used interchangeably through the Cloud Storage Configuration
+Schema (CSCS). NubiTree will make cloud storage configuration pleasant especially
+for users of the KDE desktop (kde.org) and Maemo/Meego/Mer/Nemo mobile devices
+(merproject.org).
diff --git a/kde-cloudstorage/nubi-tree/credits b/kde-cloudstorage/nubi-tree/credits
new file mode 100644 (file)
index 0000000..9018ca7
--- /dev/null
@@ -0,0 +1,5 @@
+icons/cloudfolder.png:
+ »Oxygen Cloud Folder« by MoonBlossom
+ http://kde-look.org/content/show.php/Oxygen+Cloud+Folder?content=140203
+
+Other icons: Oxygen icon set
diff --git a/kde-cloudstorage/nubi-tree/icons/cloudfolder.png b/kde-cloudstorage/nubi-tree/icons/cloudfolder.png
new file mode 100644 (file)
index 0000000..a9a3cae
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/cloudfolder.png differ
diff --git a/kde-cloudstorage/nubi-tree/icons/dialog-error.png b/kde-cloudstorage/nubi-tree/icons/dialog-error.png
new file mode 100644 (file)
index 0000000..a9095a2
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/dialog-error.png differ
diff --git a/kde-cloudstorage/nubi-tree/icons/dialog-warning.png b/kde-cloudstorage/nubi-tree/icons/dialog-warning.png
new file mode 100644 (file)
index 0000000..0666a39
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/dialog-warning.png differ
diff --git a/kde-cloudstorage/nubi-tree/icons/drive-removable-media-usb-pendrive.png b/kde-cloudstorage/nubi-tree/icons/drive-removable-media-usb-pendrive.png
new file mode 100644 (file)
index 0000000..568c078
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/drive-removable-media-usb-pendrive.png differ
diff --git a/kde-cloudstorage/nubi-tree/icons/folder-locked.png b/kde-cloudstorage/nubi-tree/icons/folder-locked.png
new file mode 100644 (file)
index 0000000..765359d
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/folder-locked.png differ
diff --git a/kde-cloudstorage/nubi-tree/icons/folder-remote.png b/kde-cloudstorage/nubi-tree/icons/folder-remote.png
new file mode 100644 (file)
index 0000000..48bb768
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/folder-remote.png differ
diff --git a/kde-cloudstorage/nubi-tree/icons/folder-tar.png b/kde-cloudstorage/nubi-tree/icons/folder-tar.png
new file mode 100644 (file)
index 0000000..0b3c4bd
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/folder-tar.png differ
diff --git a/kde-cloudstorage/nubi-tree/icons/folder-yellow.png b/kde-cloudstorage/nubi-tree/icons/folder-yellow.png
new file mode 100644 (file)
index 0000000..fc649a9
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/folder-yellow.png differ
diff --git a/kde-cloudstorage/nubi-tree/icons/user-online.png b/kde-cloudstorage/nubi-tree/icons/user-online.png
new file mode 100644 (file)
index 0000000..68d6fd1
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/user-online.png differ
diff --git a/kde-cloudstorage/nubi-tree/icons/weather-many-clouds.png b/kde-cloudstorage/nubi-tree/icons/weather-many-clouds.png
new file mode 100644 (file)
index 0000000..53e1db6
Binary files /dev/null and b/kde-cloudstorage/nubi-tree/icons/weather-many-clouds.png differ
diff --git a/kde-cloudstorage/nubi-tree/nubi-tree b/kde-cloudstorage/nubi-tree/nubi-tree
new file mode 100755 (executable)
index 0000000..7588bf6
--- /dev/null
@@ -0,0 +1,254 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from PyQt4 import QtGui, QtCore
+from PyQt4.QtCore import pyqtSlot, Qt
+import sys
+import optparse
+import os
+import glob
+import ConfigParser
+
+class LeafType:
+       def __init__(self, identifier, name, icon):
+               self.identifier = identifier
+               self.name = name
+               self.icon = icon
+               self.needsbackend = False
+
+class LeafTypeRegistry:
+       def __init__(self):
+               self.leaftypes = []
+               self.leaftypes.append(LeafType("splitter", "NubiSave splitter folder", "icons/cloudfolder.png"))
+               self.leaftypes.append(LeafType("directory", "Local directory", "icons/folder-yellow.png"))
+               self.leaftypes.append(LeafType("removable", "Removable device", "icons/drive-removable-media-usb-pendrive.png"))
+               self.leaftypes.append(LeafType("encfs", "Encryption/EncFS overlay", "icons/folder-locked.png"))
+               self.leaftypes.append(LeafType("s3", "Amazon S3", "icons/weather-many-clouds.png"))
+               self.leaftypes.append(LeafType("zipfs", "Compression/ZIPFS overlay", "icons/folder-tar.png"))
+               self.leaftypes.append(LeafType("sshfs", "Secure Shell/SSHFS", "icons/folder-remote.png"))
+
+       def get(self, identifier):
+               return filter(lambda x: x.identifier == identifier, self.leaftypes)[0]
+
+class NubiTree:
+       STATUS_UNKNOWN = 0
+       STATUS_OK = 1
+       STATUS_WARNING_CAPACITY = 2
+       STATUS_WARNING_REDUNDANCY = 3
+       STATUS_ERROR = 4
+       ltr = LeafTypeRegistry()
+
+       def __init__(self, identifier, directory, backend, status):
+               leaftype = NubiTree.ltr.get(identifier)
+               self.name = leaftype.name
+               self.icon = leaftype.icon
+               self.directory = directory
+               self.backend = backend
+               self.status = status
+
+               self.leaves = []
+
+class NubiTreeWidget(QtGui.QWidget):
+       def __init__(self, parent=None):
+               QtGui.QWidget.__init__(self, parent)
+
+               self.resize(800, 300)
+               self.setWindowTitle("NubiTree mockup")
+
+               model = QtGui.QStandardItemModel()
+               model.setColumnCount(4)
+
+               model.setHeaderData(0, Qt.Horizontal, "Module")
+               model.setHeaderData(1, Qt.Horizontal, "Directory")
+               model.setHeaderData(2, Qt.Horizontal, "Backend")
+               model.setHeaderData(3, Qt.Horizontal, "Status")
+
+               self.qtv = QtGui.QTreeView(self)
+               self.qtv.setModel(model)
+
+               self.connect(self.qtv, QtCore.SIGNAL("pressed(QModelIndex)"), self, QtCore.SLOT("slot_pressed(QModelIndex)"))
+
+               layout = QtGui.QVBoxLayout(self)
+               layout.addWidget(self.qtv)
+
+               self.properties(layout)
+
+       @pyqtSlot(QtCore.QModelIndex)
+       def slot_pressed(self, index):
+               if QtGui.QApplication.mouseButtons() & Qt.RightButton:
+                       action_repair = QtGui.QAction("Repair", self)
+                       action_repair.setEnabled(False)
+                       action_submodule = QtGui.QAction("Add submodule", self)
+                       action_submodule.setEnabled(False)
+                       action_configure = QtGui.QAction("Configure...", self)
+                       action_remove = QtGui.QAction("Remove", self)
+                       menu = QtGui.QMenu()
+                       menu.addAction(action_configure)
+                       menu.addAction(action_remove)
+                       menu.addSeparator()
+                       menu.addAction(action_submodule)
+                       menu.addSeparator()
+                       menu.addAction(action_repair)
+                       menu.exec_(QtGui.QCursor.pos())
+
+       def settree(self, tree):
+               model = self.qtv.model()
+               self.interprettree(model.invisibleRootItem(), tree)
+
+               self.qtv.expandAll()
+               for i in range(model.columnCount()):
+                       self.qtv.resizeColumnToContents(i)
+
+       def interprettree(self, modelitem, tree):
+               statusicon = {}
+               statusicon[NubiTree.STATUS_UNKNOWN] = str()
+               statusicon[NubiTree.STATUS_OK] = "icons/user-online.png"
+               statusicon[NubiTree.STATUS_WARNING_CAPACITY] = "icons/dialog-warning.png"
+               statusicon[NubiTree.STATUS_WARNING_REDUNDANCY] = "icons/dialog-warning.png"
+               statusicon[NubiTree.STATUS_ERROR] = "icons/dialog-error.png"
+
+               statustext = {}
+               statustext[NubiTree.STATUS_UNKNOWN] = str()
+               statustext[NubiTree.STATUS_OK] = str()
+               statustext[NubiTree.STATUS_WARNING_CAPACITY] = "Storage capacity exhausted soon"
+               statustext[NubiTree.STATUS_WARNING_REDUNDANCY] = "Redundancy currently not reached"
+               statustext[NubiTree.STATUS_ERROR] = "Storage resource not reachable"
+
+               leafitem = QtGui.QStandardItem(QtGui.QIcon(tree.icon), tree.name)
+               leafitem.setEditable(False)
+               statusicon = statusicon[tree.status]
+               status = QtGui.QStandardItem(QtGui.QIcon(statusicon), str())
+               status.setToolTip(statustext[tree.status])
+               directory = QtGui.QStandardItem(tree.directory)
+               if tree.backend:
+                       backend = QtGui.QStandardItem(tree.backend)
+               else:
+                       backend = QtGui.QStandardItem()
+                       backend.setEditable(False)
+               modelitem.appendRow((leafitem, directory, backend, status))
+               for leaf in tree.leaves:
+                       self.interprettree(leafitem, leaf)
+
+       def properties(self, layout):
+               label_splitter = QtGui.QLabel("Splitter:")
+               combo_splitter = QtGui.QComboBox()
+               combo_splitter.addItem("Simple Splitter")
+               combo_splitter.addItem("Erasure Coding")
+               combo_splitter.addItem("Full Redundancy")
+
+               label_redundancy = QtGui.QLabel("Redundancy:")
+               slider_redundancy = QtGui.QSlider(Qt.Horizontal)
+               slider_redundancy.setMaximum(100)
+               self.label_redundancy_result = QtGui.QLabel()
+
+               self.connect(slider_redundancy, QtCore.SIGNAL("sliderMoved(int)"), self, QtCore.SLOT("slot_slider(int)"))
+               self.slot_slider(0)
+
+               label_sched = QtGui.QLabel("Scheduler:")
+               combo_sched = QtGui.QComboBox()
+               combo_sched.addItem("Round-Robin")
+               combo_sched.addItem("Fastest First")
+
+               hbox = QtGui.QHBoxLayout()
+               hbox.addWidget(label_splitter)
+               hbox.addWidget(combo_splitter)
+               hbox.addWidget(label_redundancy)
+               hbox.addWidget(slider_redundancy)
+               hbox.addWidget(self.label_redundancy_result)
+               hbox.addWidget(label_sched)
+               hbox.addWidget(combo_sched)
+               hbox.addStretch()
+
+               layout.addLayout(hbox)
+
+       @pyqtSlot(int)
+       def slot_slider(self, value):
+               self.label_redundancy_result.setText(str(value) + "%")
+
+       def keyPressEvent(self, event):
+               if event.key() == Qt.Key_Escape:
+                       self.close()
+
+def loadtree(storagesdir):
+       leaves = {}
+       relations = {}
+
+       modulepaths = glob.glob(storagesdir + "/*")
+       for modulepath in modulepaths:
+               module = os.path.basename(modulepath)
+               config = os.path.join(modulepath, "config/config")
+
+               cp = ConfigParser.ConfigParser()
+               cp.read(config)
+
+               ltr = LeafTypeRegistry()
+               for leaftype in ltr.leaftypes:
+                       if module.startswith(leaftype.identifier):
+                               backend = None
+                               if leaftype.identifier == "directory":
+                                       mountdir = cp.get("mounting", "directory")
+                               elif leaftype.identifier == "encfs":
+                                       backendmodule = cp.get("parameter", "backendservice1")
+                                       relations[module] = backendmodule
+                                       backend = os.path.join(storagesdir, backendmodule, "data")
+                                       #mountdir = os.path.join(storagesdir, "backend", backendmodule)
+                                       mountdir = os.path.join(modulepath, "data")
+                               #print "=> %s [mounted @ %s, backend %s]" % (leaftype.name, mountdir, backend)
+
+                               leaf = NubiTree(leaftype.identifier, mountdir, backend, NubiTree.STATUS_UNKNOWN)
+                               leaves[module] = leaf
+
+       for relation in relations:
+               #print relation, relations[relation]
+               leaves[relation].leaves.append(leaves[relations[relation]])
+               leaves.pop(relations[relation])
+
+       #for leaf in leaves:
+       #       print leaf, leaves[leaf]
+
+       tree = NubiTree("splitter", "/home/demo/nubisavemount", None, NubiTree.STATUS_UNKNOWN)
+       tree.leaves = leaves.values()
+
+       return tree
+
+def simulatetree(storagesdir):
+       tree = NubiTree("splitter", "/home/demo/nubisavemount", None, NubiTree.STATUS_WARNING_REDUNDANCY)
+       localdir2 = NubiTree("directory", "%s/dir2" % storagesdir, None, NubiTree.STATUS_OK)
+       encdir = NubiTree("encfs", "%s/dir1-encrypt" % storagesdir, "/home/demo/.nubisave/dir1",  NubiTree.STATUS_OK)
+       localdir1 = NubiTree("directory", "%s/dir1" % storagesdir, None, NubiTree.STATUS_WARNING_CAPACITY)
+       s3dir = NubiTree("s3", "%s/dir-amazon" % storagesdir, "s3://s3.amazonaws.com", NubiTree.STATUS_ERROR)
+       zipfsdir = NubiTree("zipfs", "%s/dir-ssh-compress" % storagesdir, "/home/demo/.nubisave/dir-ssh", NubiTree.STATUS_OK)
+       sshfsdir = NubiTree("sshfs", "%s/dir-ssh" % storagesdir, "ssh://demo@saturn.local", NubiTree.STATUS_WARNING_CAPACITY)
+       subtree = NubiTree("splitter", "%s/subsplit" % storagesdir, None, NubiTree.STATUS_OK)
+       localdir3 = NubiTree("directory", "%s/dir3" % storagesdir, None, NubiTree.STATUS_OK)
+       localdir4 = NubiTree("removable", "%s/dir-usb" % storagesdir, None, NubiTree.STATUS_OK)
+
+       tree.leaves.append(localdir2)
+       tree.leaves.append(encdir)
+       encdir.leaves.append(localdir1)
+       tree.leaves.append(s3dir)
+       tree.leaves.append(zipfsdir)
+       zipfsdir.leaves.append(sshfsdir)
+       tree.leaves.append(subtree)
+       subtree.leaves.append(localdir3)
+       subtree.leaves.append(localdir4)
+
+       return tree
+
+storagesdir = os.path.expanduser('~') + "/.nubisave/storages"
+
+parser = optparse.OptionParser()
+parser.add_option("-l", "--load", action="store_true", dest="load", help="Load NubiSave configuration from %s" % storagesdir)
+(options, args) = parser.parse_args()
+
+if options.load:
+       tree = loadtree(storagesdir)
+else:
+       tree = simulatetree(storagesdir)
+
+app = QtGui.QApplication(sys.argv)
+ntw = NubiTreeWidget()
+ntw.settree(tree)
+ntw.move(350, 200)
+ntw.show()
+sys.exit(app.exec_())