123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- # Copyright (C) 2014 Ion Torrent Systems, Inc. All Rights Reserved
- from __future__ import absolute_import
- import json
- import os
- import os.path
- import boto
- import boto.s3.key
- import celery.states
- import requests
- from celery.result import AsyncResult
- from celery.task import task
- from celery.utils.log import get_task_logger
- from django import forms
- from django.conf import settings
- from django.http import Http404, HttpResponse
- from django.shortcuts import get_object_or_404, redirect, render
- from django.views.decorators.http import require_POST
- from ion.utils import makeCSA
- from iondb.rundb import models
- from iondb.rundb.api import SupportUploadResource
- from iondb.utils import makePDF
- logger = get_task_logger(__name__)
- def list_export_uploads(request, tag):
- uploads = models.FileMonitor.objects.filter(tags__contains="upload").order_by(
- "-created"
- )
- if tag:
- uploads = uploads.filter(tags_contains=tag)
- ctx = {"uploads": uploads}
- return render(request, "rundb/data/data_export.html", ctx)
- def md5_stats_file(path):
- with open(path, "rb", 8192) as fp:
- digest_hex, diges_64, size = boto.s3.key.compute_md5(fp)
- return digest_hex, diges_64, size
- @task
- def export_upload_file(monitor_id):
- monitor = models.FileMonitor.objects.get(pk=monitor_id)
- full = monitor.full_path()
- monitor.status = "Checking"
- monitor.save()
- if not os.path.exists(full):
- logger.error("OS Error in file uploader")
- monitor.status = "Error: file does not exist"
- monitor.save()
- return
- digest_hex, diges_64, size = md5_stats_file(full)
- monitor.size = size
- monitor.md5sum = digest_hex
- monitor.url = "{0}:{1}".format(monitor.name, monitor.md5sum)
- monitor.status = "Connecting"
- monitor.save()
- try:
- con = boto.connect_s3(settings.AWS_ACCESS_KEY, settings.AWS_SECRET_KEY)
- bucket = con.get_bucket(settings.AWS_BUCKET_NAME)
- key = bucket.get_key(monitor.url)
- if key is not None:
- monitor.status = "Complete"
- monitor.save()
- return
- key = bucket.new_key(monitor.url)
- except Exception as err:
- logger.exception("Connecting error")
- monitor.status = "Error: {0}".format(err)
- monitor.save()
- return
- monitor.status = "Uploading"
- monitor.save()
- # Rewrite this into a class or a callable object
- # last_time = time.time()
- def get_progress(current, total):
- # now = time.time()
- # if now - last_time >= 0.5:
- monitor.progress = current
- monitor.save()
- # last_time = now
- try:
- key.set_contents_from_filename(
- full, cb=get_progress, num_cb=1000, md5=(digest_hex, diges_64)
- )
- except Exception as err:
- logger.exception("Uploading error")
- monitor.status = "Error: Uploading {0}".format(err)[:60]
- monitor.save()
- return
- monitor.progress = monitor.size
- monitor.status = "Complete"
- monitor.save()
- def export_upload_report(request):
- try:
- report_pk = int(request.POST.get("report"))
- except ValueError:
- raise Http404("'{0}' is not a valid report ID".format(report_pk))
- path = request.POST.get("file_path")
- report = get_object_or_404(models.Results, pk=report_pk)
- root = report.get_report_dir()
- full_path = os.path.join(root, path)
- if not os.path.exists(full_path):
- raise Http404(
- "'{0}' does not exist as a file in report {1}".format(path, report_pk)
- )
- tag = "report/{0}/".format(report_pk)
- monitor = models.FileMonitor(
- local_dir=os.path.dirname(full_path),
- name=os.path.basename(full_path),
- tags="upload," + tag,
- status="Queued",
- )
- monitor.save()
- result = export_upload_file.delay(monitor.id)
- monitor.celery_task_id = result.task_id
- monitor.save()
- return redirect("list_export_uploads", tag="")
- class SupportUploadForm(forms.Form):
- contact_email = forms.EmailField(required=True)
- description = forms.CharField(required=True)
- result = forms.IntegerField(required=True)
- @task
- def filemonitor_errback(task_id, monitor_pk):
- try:
- monitor = models.FileMonitor.objects.get(pk=monitor_pk)
- except Exception:
- logger.exception("Monitor error callback failed for pk={0}".format(monitor_pk))
- return
- monitor.status = "Error"
- monitor.save()
- @task
- def generate_csa(result_pk, monitor_pk=None):
- result = models.Results.objects.get(pk=result_pk)
- report_dir = result.get_report_dir()
- raw_data_dir = result.experiment.expDir
- try:
- monitor = models.FileMonitor.objects.get(pk=monitor_pk)
- except models.FileMonitor.DoesNotExist:
- monitor = models.FileMonitor()
- monitor.tags = "generate_csa"
- csa_file_name = "csa_{0:04d}.zip".format(int(result_pk))
- monitor.status = "Generating"
- monitor.local_dir = report_dir
- monitor.name = csa_file_name
- monitor.save()
- # Generate report PDF file.
- # This will create a file named report.pdf in results directory
- makePDF.write_report_pdf(result_pk)
- csa_path = makeCSA.makeCSA(report_dir, raw_data_dir, monitor.name)
- digest_hex, digest_64, size = md5_stats_file(csa_path)
- monitor.md5sum = digest_hex
- monitor.size = size
- monitor.status = "Generated"
- monitor.save()
- def get_ts_info():
- path = "/etc/torrentserver/tsconf.conf"
- d = dict()
- if os.path.exists(path):
- for l in open(path):
- row = map(str.strip, l.split(":", 1))
- if len(row) == 2:
- d[row[0]] = row[1]
- return d
- def make_auth_header(account):
- return {"Authorization": "Bearer {0}".format(account.access_token)}
- @task
- def is_authenticated(account_pk):
- account = models.RemoteAccount.objects.get(pk=account_pk)
- if account.has_access():
- url = settings.SUPPORT_AUTH_URL
- auth_header = make_auth_header(account)
- info = get_ts_info()
- params = {
- "version": info.get("version", "Version Missing"),
- "serial_number": info.get("serialnumber", "Serial Missing"),
- }
- try:
- response = requests.get(
- url, params=params, headers=auth_header, verify=False
- )
- except requests.exceptions.RequestException as err:
- logger.error("Request Exception: {0}".format(err))
- else:
- if response.ok:
- return True
- else:
- logger.error("Authentication failure at {0}: {1}".format(url, response))
- return False
- @task
- def check_authentication(support_upload_pk):
- upload = models.SupportUpload.objects.get(pk=support_upload_pk)
- upload.local_status = "Authenticating"
- upload.save()
- if is_authenticated(upload.account_id):
- upload.local_status = "Authenticated"
- upload.save()
- return True
- else:
- upload.local_status = "Access Denied"
- upload.save()
- return False
- @task
- def upload_to_support(support_upload_pk):
- upload = models.SupportUpload.objects.select_related("file", "account").get(
- pk=support_upload_pk
- )
- upload.local_status = "Uploading"
- upload.save()
- url = settings.SUPPORT_UPLOAD_URL
- auth_header = make_auth_header(upload.account)
- info = get_ts_info()
- form_data = {
- "contact_email": upload.contact_email,
- "description": upload.description,
- "version": info.get("version", "Version Missing"),
- "serial_number": info.get("serialnumber", "Serial Missing"),
- }
- path = upload.file.full_path()
- files = {"file": open(path, "rb")}
- try:
- response = requests.post(
- url, data=form_data, files=files, headers=auth_header, verify=False
- )
- except Exception as err:
- upload.local_status = "Error"
- upload.local_message = str(err)
- else:
- if response.ok:
- try:
- tick = response.json()
- except ValueError:
- tick = {}
- upload.local_status = "Complete"
- upload.ticket_id = tick.get("ticket_id", "None")
- upload.ticket_status = tick.get("ticket_status", "Remote Error")
- upload.ticket_message = tick.get(
- "ticket_message",
- "There was an error in the support server. Your Torrent Server is working fine, and you should contact your support representative.",
- )
- else:
- upload.local_status = "Error"
- upload.local_message = response.reason
- finally:
- upload.save()
- @task(max_retries=120)
- def check_and_upload(support_upload_pk, auth_task, gen_task):
- auth = AsyncResult(auth_task)
- gen_csa = AsyncResult(gen_task)
- if not (auth.ready() and gen_csa.ready()):
- check_and_upload.retry(countdown=1)
- elif auth.status == "SUCCESS" and gen_csa.status == "SUCCESS":
- if auth.result:
- upload_to_support(support_upload_pk)
- else:
- return
- @require_POST
- def report_support_upload(request):
- form = SupportUploadForm(request.POST)
- # check for existing support upload
- data = {"created": False}
- account = models.RemoteAccount.objects.get(remote_resource="support.iontorrent")
- if not account.has_access():
- data["error"] = "invalid_auth"
- if not form.is_valid():
- data["error"] = "invalid_form"
- data["form_errors"] = form.errors
- if "error" not in data:
- result_pk = form.cleaned_data["result"]
- support_upload = (
- models.SupportUpload.objects.filter(result=result_pk)
- .order_by("-id")
- .first()
- )
- if not support_upload:
- data["created"] = True
- support_upload = models.SupportUpload(
- account=account,
- result_id=result_pk,
- user=request.user,
- local_status="Preparing",
- contact_email=form.cleaned_data["contact_email"],
- description=form.cleaned_data["description"],
- )
- support_upload.save()
- async_result = AsyncResult(support_upload.celery_task_id)
- if (
- not support_upload.celery_task_id
- or async_result.status in celery.states.READY_STATES
- ):
- if not support_upload.file:
- monitor = models.FileMonitor()
- monitor.save()
- support_upload.file = monitor
- support_upload.save()
- support_upload.file.status = ("Queued",)
- support_upload.file.tags = "support_upload,generate_csa"
- support_upload.file.save()
- generate = generate_csa.s(result_pk, support_upload.file.pk)
- errback = filemonitor_errback.s(support_upload.file.pk)
- gen_result = generate.apply_async(link_error=errback)
- support_upload.file.celery_task_id = gen_result.task_id
- auth_result = check_authentication.delay(support_upload.pk)
- upload_result = check_and_upload.apply_async(
- (support_upload.pk, auth_result.task_id, gen_result.task_id),
- countdown=1,
- )
- support_upload.celery_task_id = upload_result.task_id
- support_upload.save()
- resource = SupportUploadResource()
- uri = resource.get_resource_uri(support_upload)
- data["resource_uri"] = uri
- # make CSA
- # check auth
- # start upload task
- # respond with support upload object for JS polling
- return HttpResponse(
- json.dumps(data, indent=4, sort_keys=True), mimetype="application/json"
- )
|