Commit 4816e34a by Őry Máté

store: rewrite

parent e98b628f
......@@ -44,7 +44,7 @@ from common.models import HumanReadableObject, create_readable, Encoder
from vm.tasks.agent_tasks import add_keys, del_keys
from dashboard import store_api
from .store_api import Store
logger = getLogger(__name__)
......@@ -213,12 +213,10 @@ def create_profile(sender, user, request, **kwargs):
return False
profile, created = Profile.objects.get_or_create(user=user)
if created:
user_home = "u-%d" % user.pk
if not store_api.userexist(user_home):
store_api.createuser(user_home, profile.smb_password, None,
profile.disk_quota)
try:
Store(user).create_user(profile.smb_password, None, profile.disk_quota)
except:
logger.exception("Can't create user %s", unicode(user))
return created
user_logged_in.connect(create_profile)
......
from django.http import Http404
import json
import logging
import requests
from urlparse import urljoin
from datetime import datetime
from sizefield.utils import filesizeformat
from django.http import Http404
from django.conf import settings
from requests import get, post, codes
from sizefield.utils import filesizeformat
logger = logging.getLogger(__name__)
class Mock(object):
class StoreApiException(Exception):
pass
def get_host():
return settings.STORE_URL
def get_request_arguments():
args = {'verify': settings.STORE_VERIFY_SSL}
if settings.STORE_SSL_AUTH:
args['cert'] = (settings.STORE_CLIENT_CERT, settings.STORE_CLIENT_KEY)
if settings.STORE_BASIC_AUTH:
args['auth'] = (settings.STORE_CLIENT_USER,
settings.STORE_CLIENT_PASSWORD)
return args
def post_request(url, payload, timeout=None):
try:
headers = {'content-type': 'application/json'}
r = requests.post(url, data=payload, headers=headers, timeout=timeout,
**get_request_arguments())
return r
except Exception as e:
logger.error("Error in store POST: %s" % e)
dummy = Mock()
setattr(dummy, "status_code", 200)
setattr(dummy, "content", "[]")
return dummy
def get_request(url, timeout=None):
try:
headers = {'content-type': 'application/json'}
r = requests.get(url, headers=headers, timeout=timeout,
**get_request_arguments())
return r
except Exception as e:
logger.error("Error in store GET: %s" % e)
dummy = Mock()
setattr(dummy, "status_code", 200)
setattr(dummy, "content", "[]")
return dummy
def listfolder(neptun, path):
url = settings.STORE_URL + '/' + neptun
payload = json.dumps({'CMD': 'LIST', 'PATH': path})
r = post_request(url, payload, timeout=5)
if r.status_code == requests.codes.ok:
tupplelist = json.loads(r.content)
return tupplelist
else:
raise Http404
def toplist(neptun):
url = settings.STORE_URL + '/' + neptun
payload = json.dumps({'CMD': 'TOPLIST'})
r = post_request(url, payload, timeout=2)
if r.status_code == requests.codes.ok:
tupplelist = json.loads(r.content)
return tupplelist
else:
raise Http404
def requestdownload(neptun, path):
url = settings.STORE_URL + '/' + neptun
payload = json.dumps({'CMD': 'DOWNLOAD', 'PATH': path})
r = post_request(url, payload)
response = json.loads(r.content)
return response['LINK']
def requestupload(neptun, path):
url = settings.STORE_URL+'/'+neptun
payload = json.dumps({'CMD': 'UPLOAD', 'PATH': path})
r = post_request(url, payload)
response = json.loads(r.content)
print response
if r.status_code == requests.codes.ok:
return response['LINK']
else:
raise Http404
def requestremove(neptun, path):
url = settings.STORE_URL+'/'+neptun
payload = json.dumps({'CMD': 'REMOVE', 'PATH': path})
r = post_request(url, payload)
if r.status_code == requests.codes.ok:
return True
else:
return False
def requestnewfolder(neptun, path):
url = settings.STORE_URL+'/'+neptun
payload = json.dumps({'CMD': 'NEW_FOLDER', 'PATH': path})
r = post_request(url, payload)
if r.status_code == requests.codes.ok:
return True
else:
return False
def requestrename(neptun, old_path, new_name):
url = settings.STORE_URL+'/'+neptun
payload = json.dumps(
{'CMD': 'RENAME', 'NEW_NAME': new_name, 'PATH': old_path})
r = post_request(url, payload)
if r.status_code == requests.codes.ok:
return True
else:
return False
def requestquota(neptun):
url = settings.STORE_URL+'/'+neptun
r = get_request(url)
if r.status_code == requests.codes.ok:
return json.loads(r.content)
else:
return False
def set_quota(neptun, quota):
url = settings.STORE_URL+'/quota/'+neptun
payload = json.dumps({'QUOTA': quota})
r = post_request(url, payload)
if r.status_code == requests.codes.ok:
return True
else:
return False
def userexist(neptun):
url = settings.STORE_URL+'/'+neptun
r = get_request(url, timeout=5)
if r.status_code == requests.codes.ok:
return True
else:
return False
def createuser(neptun, password, key_list, quota):
url = settings.STORE_URL+'/new/'+neptun
payload = json.dumps(
{'SMBPASSWD': password, 'KEYS': key_list, 'QUOTA': quota})
r = post_request(url, payload, timeout=5)
if r.status_code == requests.codes.ok:
return True
else:
return False
def updateauthorizationinfo(neptun, password, key_list):
url = settings.STORE_URL+'/set/'+neptun
payload = json.dumps({'SMBPASSWD': password, 'KEYS': key_list})
r = post_request(url, payload)
if r.status_code == requests.codes.ok:
return True
else:
return False
def process_list(content):
for d in content:
d['human_readable_date'] = datetime.utcfromtimestamp(float(
d['MTIME']))
delta = (datetime.utcnow() - d['human_readable_date']).total_seconds()
d['is_new'] = delta < 5 and delta > 0
d['human_readable_size'] = (
"directory" if d['TYPE'] == "D" else
filesizeformat(float(d['SIZE'])))
if len(d['DIR']) == 1 and d['DIR'][0] == ".":
d['directory'] = "/"
class NotOkException(StoreApiException):
def __init__(self, status, *args, **kwargs):
self.status = status
super(NotOkException, self).__init__(*args, **kwargs)
class Store(object):
def __init__(self, user, default_timeout=0.5):
self.request_args = {'verify': settings.STORE_VERIFY_SSL}
if settings.STORE_SSL_AUTH:
self.request_args['cert'] = (settings.STORE_CLIENT_CERT,
settings.STORE_CLIENT_KEY)
if settings.STORE_BASIC_AUTH:
self.request_args['auth'] = (settings.STORE_CLIENT_USER,
settings.STORE_CLIENT_PASSWORD)
self.username = "u-%d" % user.pk
self.default_timeout = default_timeout
self.store_url = settings.STORE_URL
def _request(self, url, method=get, timeout=None,
raise_status_code=True, **kwargs):
url = urljoin(self.store_url, url)
if timeout is None:
timeout = self.default_timeout
payload = json.dumps(kwargs)
try:
headers = {'content-type': 'application/json'}
response = method(url, data=payload, headers=headers,
timeout=timeout, **self.request_args)
except Exception:
logger.exception("Error in store %s loading %s",
unicode(method), url)
raise
else:
d['directory'] = "/" + d['DIR'] + "/"
d['path'] = d['directory']
d['path'] += d['NAME']
if d['TYPE'] == "D":
d['path'] += "/"
if raise_status_code and response.status_code != codes.ok:
if response.status_code == 404:
raise Http404()
else:
raise NotOkException(response.status_code)
return response
def _request_cmd(self, cmd, **kwargs):
return self._request(self.username, post, CMD=cmd)
def list(self, path, process=True):
r = self._request_cmd("LIST", PATH=path)
result = r.json()
if process:
return self._process_list(result)
else:
return result
return sorted(content, key=lambda k: k['TYPE'])
def toplist(self, process=True):
r = self._request_cmd("TOPLIST")
result = r.json()
if process:
return self._process_list(result)
else:
return result
def request_download(self, path):
r = self._request_cmd("DOWNLOAD", PATH=path)
return r.json()['LINK']
def request_upload(self, path):
r = self._request_cmd("UPLOAD", PATH=path)
return r.json()['LINK']
def remove(self, path):
self._request_cmd("REMOVE", PATH=path)
def new_folder(self, path):
self._request_cmd("NEW_FOLDER", PATH=path)
def rename(self, old_path, new_name):
self._request_cmd("RENAME", PATH=old_path, NEW_NAME=new_name)
def get_quota(self): # no CMD? :o
r = self._request(self.username)
return r.json()
def set_quota(self, quota):
self._request(self.username + "/quota/", post, QUOTA=quota)
def user_exist(self):
try:
self._request(self.username)
return True
except NotOkException:
return False
def create_user(self, password, keys, quota):
self._request("/new/" + self.username, SMBPASSWD=password, KEYS=keys,
QUOTA=quota)
@staticmethod
def _process_list(content):
for d in content:
d['human_readable_date'] = datetime.utcfromtimestamp(float(
d['MTIME']))
delta = (datetime.utcnow() -
d['human_readable_date']).total_seconds()
d['is_new'] = 0 < delta < 5
d['human_readable_size'] = (
"directory" if d['TYPE'] == "D" else
filesizeformat(float(d['SIZE'])))
if len(d['DIR']) == 1 and d['DIR'][0] == ".":
d['directory'] = "/"
else:
d['directory'] = "/" + d['DIR'] + "/"
d['path'] = d['directory']
d['path'] += d['NAME']
if d['TYPE'] == "D":
d['path'] += "/"
return sorted(content, key=lambda k: k['TYPE'])
......@@ -21,6 +21,7 @@ from __future__ import unicode_literals, absolute_import
from collections import OrderedDict
from itertools import chain
from os import getenv
from os.path import join, normpath, dirname
import os
import json
import logging
......@@ -83,7 +84,7 @@ from storage.models import Disk
from firewall.models import Vlan, Host, Rule
from .models import Favourite, Profile, GroupProfile, FutureMember
from dashboard import store_api
from .store_api import Store
logger = logging.getLogger(__name__)
saml_available = hasattr(settings, "SAML_CONFIG")
......@@ -225,14 +226,15 @@ class IndexView(LoginRequiredMixin, TemplateView):
'operator', user).all()[:5]
# toplist
user_home = "u-%d" % user.pk
cache_key = "toplist-%s" % user_home
cache_key = "toplist-%d" % self.request.user.pk
cache = get_cache("default")
toplist = cache.get(cache_key)
if not toplist:
try:
toplist = store_api.process_list(store_api.toplist(user_home))
except Http404:
toplist = Store(self.request.user).toplist()
except Exception:
logger.exception("Unable to get tolist for %s",
unicode(self.request.user))
toplist = []
cache.set(cache_key, toplist, 300)
......@@ -3092,14 +3094,12 @@ class StoreList(LoginRequiredMixin, TemplateView):
directory = self.request.GET.get("directory", "/")
directory = "/" if not len(directory) else directory
user_home = "u-%d" % self.request.user.pk
content = store_api.listfolder(user_home, directory)
context['root'] = store_api.process_list(content)
context['root'] = Store(self.request.user).list(directory)
context['up_url'] = self.create_up_directory(directory)
context['current'] = directory
context['next_url'] = "%s%s?directory=%s" % (
settings.DJANGO_URL[:-1], reverse("dashboard.views.store-list"),
directory)
settings.DJANGO_URL.rstrip("/"),
reverse("dashboard.views.store-list"), directory)
return context
def get(self, *args, **kwargs):
......@@ -3113,28 +3113,31 @@ class StoreList(LoginRequiredMixin, TemplateView):
return super(StoreList, self).get(*args, **kwargs)
def create_up_directory(self, directory):
cut = -2 if directory.endswith("/") else -1
return "/".join(directory.split("/")[:cut]) + "/"
return normpath(join('/', directory, '..'))
@require_GET
@login_required
def store_download(request):
user_home = "u-%d" % request.user.pk
path = request.GET.get("path")
url = store_api.requestdownload(user_home, path)
url = Store(request.user).request_download(path)
return redirect(url)
@require_GET
@login_required
def store_upload(request):
user_home = "u-%d" % request.user.pk
directory = request.GET.get("directory", "/")
action = store_api.requestupload(user_home, directory)
try:
action = Store(request.user).request_upload(directory)
except Exception:
logger.exception("Unable to upload")
messages.error(request, _("Unable to upload file."))
return redirect("/")
next_url = "%s%s?directory=%s" % (
settings.DJANGO_URL[:-1], reverse("dashboard.views.store-list"),
directory)
settings.DJANGO_URL.rstrip("/"),
reverse("dashboard.views.store-list"), directory)
return render(request, "dashboard/store/upload.html",
{'directory': directory, 'action': action,
......@@ -3144,9 +3147,13 @@ def store_upload(request):
@require_GET
@login_required
def store_get_upload_url(request):
user_home = "u-%d" % request.user.pk
current_dir = request.GET.get("current_dir")
url = store_api.requestupload(user_home, current_dir)
try:
url = Store(request.user).request_upload(current_dir)
except Exception:
logger.exception("Unable to upload")
messages.error(request, _("Unable to upload file."))
return redirect("/")
return HttpResponse(
json.dumps({'url': url}), content_type="application/json")
......@@ -3171,30 +3178,31 @@ class StoreRemove(LoginRequiredMixin, TemplateView):
return context
def post(self, *args, **kwargs):
user_home = "u-%d" % self.request.user.pk
path = self.request.POST.get("path")
store_api.requestremove(user_home, path)
try:
Store(self.request.user).remove(path)
except Exception:
logger.exception("Unable to remove %s", path)
messages.error(self.request, _("Unable to remove %s.") % path)
if path.endswith("/"):
return redirect("%s?directory=%s" % (
reverse("dashboard.views.store-list"),
os.path.dirname(os.path.dirname(path)),
))
else:
return redirect("%s?directory=%s" % (
reverse("dashboard.views.store-list"),
os.path.dirname(path),
))
return redirect("%s?directory=%s" % (
reverse("dashboard.views.store-list"),
dirname(dirname(path)),
))
@require_POST
@login_required
def store_new_directory(request):
user_home = "u-%d" % request.user.pk
path = request.POST.get("path")
name = request.POST.get("name")
store_api.requestnewfolder(user_home, path + name)
try:
Store(request.user).new_folder(join(path, name))
except Exception:
logger.exception("Unable to create folder %s in %s for %s",
name, path, unicode(request.user))
messages.error(request, _("Unable to create folder."))
return redirect("%s?directory=%s" % (
reverse("dashboard.views.store-list"), path))
......@@ -3202,12 +3210,12 @@ def store_new_directory(request):
@require_POST
@login_required
def store_refresh_toplist(request):
user_home = "u-%d" % request.user.pk
cache_key = "toplist-%s" % user_home
cache_key = "toplist-%d" % request.user.pk
cache = get_cache("default")
try:
toplist = store_api.process_list(store_api.toplist(user_home))
except Http404:
toplist = Store(request.user).toplist()
except Exception:
logger.exception("Can't get toplist of %s", unicode(request.user))
toplist = []
cache.set(cache_key, toplist, 300)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment