azurenbmanager.py
143 lines
| 5.4 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r8180 | """A notebook manager that uses Azure blob storage. | ||
Authors: | ||||
* Brian Granger | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2012 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
import datetime | ||||
import azure | ||||
from azure.storage import BlobService | ||||
from tornado import web | ||||
Brian Granger
|
r8194 | from .nbmanager import NotebookManager | ||
Brian Granger
|
r8180 | from IPython.nbformat import current | ||
from IPython.utils.traitlets import Unicode, Instance | ||||
#----------------------------------------------------------------------------- | ||||
# Classes | ||||
#----------------------------------------------------------------------------- | ||||
Brian Granger
|
r8194 | class AzureNotebookManager(NotebookManager): | ||
Brian Granger
|
r8180 | |||
account_name = Unicode('', config=True, help='Azure storage account name.') | ||||
account_key = Unicode('', config=True, help='Azure storage account key.') | ||||
container = Unicode('', config=True, help='Container name for notebooks.') | ||||
blob_service_host_base = Unicode('.blob.core.windows.net', config=True, | ||||
help='The basename for the blob service URL. If running on the preview site this ' | ||||
'will be .blob.core.azure-preview.com.') | ||||
def _blob_service_host_base_changed(self, new): | ||||
self._update_service_host_base(new) | ||||
blob_service = Instance('azure.storage.BlobService') | ||||
def _blob_service_default(self): | ||||
return BlobService(account_name=self.account_name, account_key=self.account_key) | ||||
def __init__(self, **kwargs): | ||||
Brian Granger
|
r8181 | super(AzureNotebookManager, self).__init__(**kwargs) | ||
Brian Granger
|
r8180 | self._update_service_host_base(self.blob_service_host_base) | ||
self._create_container() | ||||
def _update_service_host_base(self, shb): | ||||
azure.BLOB_SERVICE_HOST_BASE = shb | ||||
def _create_container(self): | ||||
self.blob_service.create_container(self.container) | ||||
def load_notebook_names(self): | ||||
"""On startup load the notebook ids and names from Azure. | ||||
The blob names are the notebook ids and the notebook names are stored | ||||
as blob metadata. | ||||
""" | ||||
self.mapping = {} | ||||
blobs = self.blob_service.list_blobs(self.container) | ||||
ids = [blob.name for blob in blobs] | ||||
for id in ids: | ||||
md = self.blob_service.get_blob_metadata(self.container, id) | ||||
name = md['x-ms-meta-nbname'] | ||||
self.mapping[id] = name | ||||
def list_notebooks(self): | ||||
"""List all notebooks in the container. | ||||
This version uses `self.mapping` as the authoritative notebook list. | ||||
""" | ||||
data = [dict(notebook_id=id,name=name) for id, name in self.mapping.items()] | ||||
data = sorted(data, key=lambda item: item['name']) | ||||
return data | ||||
def read_notebook_object(self, notebook_id): | ||||
"""Get the object representation of a notebook by notebook_id.""" | ||||
if not self.notebook_exists(notebook_id): | ||||
raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id) | ||||
try: | ||||
s = self.blob_service.get_blob(self.container, notebook_id) | ||||
except: | ||||
raise web.HTTPError(500, u'Notebook cannot be read.') | ||||
try: | ||||
# v1 and v2 and json in the .ipynb files. | ||||
nb = current.reads(s, u'json') | ||||
except: | ||||
raise web.HTTPError(500, u'Unreadable JSON notebook.') | ||||
# Todo: The last modified should actually be saved in the notebook document. | ||||
# We are just using the current datetime until that is implemented. | ||||
last_modified = datetime.datetime.utcnow() | ||||
return last_modified, nb | ||||
def write_notebook_object(self, nb, notebook_id=None): | ||||
"""Save an existing notebook object by notebook_id.""" | ||||
try: | ||||
new_name = nb.metadata.name | ||||
except AttributeError: | ||||
raise web.HTTPError(400, u'Missing notebook name') | ||||
if notebook_id is None: | ||||
notebook_id = self.new_notebook_id(new_name) | ||||
if notebook_id not in self.mapping: | ||||
raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id) | ||||
try: | ||||
data = current.writes(nb, u'json') | ||||
except Exception as e: | ||||
raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e) | ||||
metadata = {'nbname': new_name} | ||||
try: | ||||
self.blob_service.put_blob(self.container, notebook_id, data, 'BlockBlob', x_ms_meta_name_values=metadata) | ||||
except Exception as e: | ||||
raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e) | ||||
self.mapping[notebook_id] = new_name | ||||
return notebook_id | ||||
def delete_notebook(self, notebook_id): | ||||
"""Delete notebook by notebook_id.""" | ||||
if not self.notebook_exists(notebook_id): | ||||
raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id) | ||||
try: | ||||
self.blob_service.delete_blob(self.container, notebook_id) | ||||
except Exception as e: | ||||
raise web.HTTPError(400, u'Unexpected error while deleting notebook: %s' % e) | ||||
else: | ||||
self.delete_notebook_id(notebook_id) | ||||
Brian Granger
|
r8181 | |||
def log_info(self): | ||||
self.log.info("Serving notebooks from Azure storage: %s, %s", self.account_name, self.container) | ||||