From db295c36ad8856b3ae23379341c692defdd6338d Mon Sep 17 00:00:00 2001 From: Marc Aymerich Date: Thu, 28 Jan 2016 11:18:28 +0000 Subject: [PATCH] Added owncloud support --- orchestra/contrib/domains/backends.py | 2 +- orchestra/contrib/domains/models.py | 4 +- orchestra/contrib/saas/backends/owncloud.py | 103 ++++++++++++ orchestra/contrib/saas/services/owncloud.py | 13 ++ orchestra/contrib/saas/settings.py | 19 +++ .../static/orchestra/icons/apps/ownCloud.png | Bin 0 -> 2166 bytes .../static/orchestra/icons/apps/ownCloud.svg | 151 ++++++++++++++++++ 7 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 orchestra/contrib/saas/backends/owncloud.py create mode 100644 orchestra/contrib/saas/services/owncloud.py create mode 100644 orchestra/static/orchestra/icons/apps/ownCloud.png create mode 100644 orchestra/static/orchestra/icons/apps/ownCloud.svg diff --git a/orchestra/contrib/domains/backends.py b/orchestra/contrib/domains/backends.py index 7932dd78..ffa18a4c 100644 --- a/orchestra/contrib/domains/backends.py +++ b/orchestra/contrib/domains/backends.py @@ -25,7 +25,7 @@ class Bind9MasterDomainBackend(ServiceController): ('domains.Record', 'domain__origin'), ('domains.Domain', 'origin'), ) - ignore_fields = ['serial'] + ignore_fields = ('serial',) doc_settings = (settings, ('DOMAINS_MASTERS_PATH',) ) diff --git a/orchestra/contrib/domains/models.py b/orchestra/contrib/domains/models.py index 92c8c615..1cd893a5 100644 --- a/orchestra/contrib/domains/models.py +++ b/orchestra/contrib/domains/models.py @@ -102,7 +102,7 @@ class Domain(models.Model): for domain in self.subdomains.exclude(pk=self.pk): # queryset.update() is not used because we want to trigger backend to delete ex-topdomains domain.top = self - domain.save(update_fields=['top']) + domain.save(update_fields=('top',)) def get_description(self): if self.is_top: @@ -151,7 +151,7 @@ class Domain(models.Model): serial = str(self.serial)[:8] + '%.2d' % num serial = int(serial) self.serial = serial - self.save(update_fields=['serial']) + self.save(update_fields=('serial',)) def get_records(self): types = {} diff --git a/orchestra/contrib/saas/backends/owncloud.py b/orchestra/contrib/saas/backends/owncloud.py new file mode 100644 index 00000000..943421fd --- /dev/null +++ b/orchestra/contrib/saas/backends/owncloud.py @@ -0,0 +1,103 @@ +import re +import sys +import textwrap +import xml.etree.ElementTree as ET +from urllib.parse import urlparse + +import requests +from django.utils.translation import ugettext_lazy as _ + +from orchestra.contrib.orchestration import ServiceController + +from . import ApacheTrafficByHost +from .. import settings + + +class OwnCloudBackend(ServiceController): + """ + Creates a wordpress site on a WordPress MultiSite installation. + + You should point it to the database server + """ + verbose_name = _("ownCloud SaaS") + model = 'saas.SaaS' + default_route_match = "saas.service == 'owncloud'" + doc_settings = (settings, + ('SAAS_OWNCLOUD_API_URL',) + ) + + def validate_response(self, response): + if response.status_code != requests.codes.ok: + request = response.request + context = (request.method, response.url, request.body, response.status_code) + raise RuntimeError("%s %s '%s' HTTP %s" % context) + root = ET.fromstring(response.text) + statuscode = root.find("./meta/statuscode").text + if statuscode != '100': + message = root.find("./meta/status").text + request = response.request + context = (request.method, response.url, request.body, statuscode, message) + raise RuntimeError("%s %s '%s' ERROR %s, %s" % context) + + def api_call(self, action, url_path, *args, **kwargs): + BASE_URL = settings.SAAS_OWNCLOUD_API_URL.rstrip('/') + url = '/'.join((BASE_URL, url_path)) + response = action(url, *args, **kwargs) + self.validate_response(response) + return response + + def api_get(self, url_path, *args, **kwargs): + return self.api_call(requests.get, url_path, *args, **kwargs) + + def api_post(self, url_path, *args, **kwargs): + return self.api_call(requests.post, url_path, *args, **kwargs) + + def api_put(self, url_path, *args, **kwargs): + return self.api_call(requests.put, url_path, *args, **kwargs) + + def api_delete(self, url_path, *args, **kwargs): + return self.api_call(requests.delete, url_path, *args, **kwargs) + + def create(self, saas): + data = { + 'userid': saas.name, + 'password': saas.password + } + self.api_post('users', data) + + def update(self, saas): + data = { + 'password': saas.password, + } + self.api_put('users/%s' % saas.name, data) + + def update_or_create(self, saas, server): + try: + self.api_get('users/%s' % saas.name) + except RuntimeError: + if getattr(saas, 'password'): + self.create(saas) + else: + raise + else: + if getattr(saas, 'password'): + self.update(saas) + + def remove(self, saas, server): + self.api_delete('users/%s' % saas.name) + + def save(self, saas): + self.append(self.update_or_create, saas) + + def delete(self, saas): + self.append(self.remove, saas) + + +class OwncloudTraffic(ApacheTrafficByHost): + __doc__ = ApacheTrafficByHost.__doc__ + verbose_name = _("ownCloud SaaS Traffic") + default_route_match = "saas.service == 'owncloud'" + doc_settings = (settings, + ('SAAS_TRAFFIC_IGNORE_HOSTS', 'SAAS_OWNCLOUD_LOG_PATH') + ) + log_path = settings.SAAS_OWNCLOUD_LOG_PATH diff --git a/orchestra/contrib/saas/services/owncloud.py b/orchestra/contrib/saas/services/owncloud.py new file mode 100644 index 00000000..2f850f07 --- /dev/null +++ b/orchestra/contrib/saas/services/owncloud.py @@ -0,0 +1,13 @@ +from django import forms +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from .. import settings +from .options import SoftwareService + + +class OwnCloudService(SoftwareService): + name = 'owncloud' + verbose_name = "ownCloud" + icon = 'orchestra/icons/apps/ownCloud.png' + site_domain = settings.SAAS_OWNCLOUD_DOMAIN diff --git a/orchestra/contrib/saas/settings.py b/orchestra/contrib/saas/settings.py index 2a5460d1..7552d26e 100644 --- a/orchestra/contrib/saas/settings.py +++ b/orchestra/contrib/saas/settings.py @@ -17,6 +17,7 @@ SAAS_ENABLED_SERVICES = Setting('SAAS_ENABLED_SERVICES', 'orchestra.contrib.saas.services.wordpress.WordPressService', 'orchestra.contrib.saas.services.dokuwiki.DokuWikiService', 'orchestra.contrib.saas.services.drupal.DrupalService', + 'orchestra.contrib.saas.services.owncloud.OwnCloudService', 'orchestra.contrib.saas.services.seafile.SeaFileService', ), # lazy loading @@ -201,6 +202,24 @@ SAAS_SEAFILE_DEFAULT_QUOTA = Setting('SAAS_SEAFILE_DEFAULT_QUOTA', ) +# ownCloud + +SAAS_OWNCLOUD_DOMAIN = Setting('SAAS_OWNCLOUD_DOMAIN', + 'owncloud.{}'.format(ORCHESTRA_BASE_DOMAIN), + help_text="Uses ORCHESTRA_BASE_DOMAIN by default.", +) + +SAAS_OWNCLOUD_API_URL = Setting('SAAS_OWNCLOUD_API_URL', + 'https://admin:secret@owncloud.{}/ocs/v1.php/cloud/'.format(ORCHESTRA_BASE_DOMAIN), +) + +SAAS_OWNCLOUD_LOG_PATH = Setting('SAAS_OWNCLOUD_LOG_PATH', + '', + help_text=_('Filesystem path for the webserver access logs.
' + 'LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Host}i\"" host'), +) + + # BSCW SAAS_BSCW_DOMAIN = Setting('SAAS_BSCW_DOMAIN', diff --git a/orchestra/static/orchestra/icons/apps/ownCloud.png b/orchestra/static/orchestra/icons/apps/ownCloud.png new file mode 100644 index 0000000000000000000000000000000000000000..10f53271819dd2461f884d3f11d839233619ff5d GIT binary patch literal 2166 zcmV-+2#NQJP)S3)u}Z4gEQVu3Q%kHttRE(F62-INr#0FAOr&bmF|*$79IWqMx$}s!wTj8l#TzC z7XSJXfdMk?+LUZvxnURkm%KaeHUoknaO&&N6_F9c#sW}umv|#OdWZ$O>bkT!fBHyj zvDyFt_ex5k(N%MQ$-C2`0u008n^R|{VS|IWFbs23AmC*KxDCMGCFW1iQ2~)B&YTxE zk_P~xwPaDUqKOd`P$9+Q$JmJXH4V>{e*Fm4j*qpx6L>JFShXlA>E?TRr z<~2SO1c9NU!BDB&?v8#wLO7gG7)@pXfKsgmLx4DUF`GDdF`Fg`0t@HQN?!cl+*xAU ze-Z%WZz6!AsN6RukFSx?BFU(cVIWBo27?iS{iV&~1biOmzdaql3m*&~j|+WuEm$lT ze1zVpGn$}M>rhlu))db$4E7&B=~;BYY~=QJA0L!S>AAm&0E@#R4ILEZdE!h4cJDui zhK9#q`{>@yAV~rZ4Gf~jL?CHVJPsT^g(Kgdfw|t&tcH)!3$ZcdFmLWO{Cx2$3X4md zd{=VvSeLKoPMQ`sAxG*QjI{12fA~xm~NF^fF)EI)zX3Il9kE>^~ zSXbmSdNqK$w(2~;g+^2;RC_H}o4l-C!5J7N_kUx~Vh^Xwn2I3Isqz5yC-b(5V%QHUNp%C&yg(n{LTCy~$RpKYy1;}#;SRGMv9ujggF6~E+_ z*qIB17JaxzTA@(2N}eJ~aJk$rlLvsD8@FJ!+MAIF0H@1^tZO%^DRci4lykG-NKH-M zJ8cS3X>==2p1F`1|L4C;8uz-VfZx==-63>P5K11DcbR-63`QfoJOyrjCUZSLSoW!( zzSgi6fXH182_QtR(J$SRc8LFca%+VFU}X4U+%72UI`b#UzgvP4!$Mp2+iZ4xxMH(h zq0)Tm&QDe-R2x27vqRF@+n-0@ESx{5nN2PeW5(3U@D%V79v0F?+D|cT=pfuHe$Z~5Mx&=I z6sm$p70N7)UZ04^e}EuOW{a@3q*Fg-VC3+@7&asrBuQX-QUaDQO#s`&13G;zCPuy7 zZ2?0<2I77P&QgBieO^-Xrs31)Et&Y$u^+coDK%LDNEX|JoQ7r!@qCCTBi)vfJw z08nXkSdf&0@nc8f;IZ$U`L5^XWAhl7H`k{0OLM8X>pLt#GR!6xQhi32*7{NUv4*cV)V<%%P(wZ zV;BZjhn+`CC88~f32&FlBy_6<^fk5c_VR3Dce-3yy*Um0Qr9&2`gnJkyu6Tf}a6fXm?^bWl(W zABXM1NNE2XXO5?p#m0k_2|HUtTSu{Z~9Y3k6iEG$&5~H*-j4_6?tc!uw#cD1;9U!L-+6^szBx@7NuV z;7?ZX=y&~QK1C1&qN84NZc6!Ed4Nn31;C_K>GnM+e-xLInIp`-Rmi(nQVJGDAv`Pu zqa%i!r^Jpo^pnv?#UlR|0HD%nH$1FRCTvbUKo=I5qLHM$p9E85$6MZ?H?uTACRw1U z(xjfhls)v))f>KkzCuUByqS91&u^PdCffP@Fh>B0Q)~6F+HCd-AP5JK%gLhsea{29 z3cyF9)NFD(T@wfb4BLZsNh+aNqWQ0&%;j>8R;qQe4ySV*NfHhZ7Aw1-RD1zIE`aCn zUxrmysXu6FXo#}e9U>2kvVlOcuh9FnQ0Vm)fW{UTRc$at^Lacam(9+^lbem6x|!=W syzrO1eFAzLeFAzLeFAzLFT8;N1CTiw4wZU8CIA2c07*qoM6N<$g3lTNVgLXD literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/apps/ownCloud.svg b/orchestra/static/orchestra/icons/apps/ownCloud.svg new file mode 100644 index 00000000..b64fe143 --- /dev/null +++ b/orchestra/static/orchestra/icons/apps/ownCloud.svg @@ -0,0 +1,151 @@ + + + + + + image/svg+xml + + Druplicon + + + + + + Druplicon + + + + + + + + + +