You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by di...@apache.org on 2019/10/29 17:27:37 UTC

[airavata-sandbox] branch master updated: Jupyter lab integrations

This is an automated email from the ASF dual-hosted git repository.

dimuthuupe pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-sandbox.git


The following commit(s) were added to refs/heads/master by this push:
     new 29d4fa0  Jupyter lab integrations
29d4fa0 is described below

commit 29d4fa039daaacab6f44822c1ba25f2ccb3451fa
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Tue Oct 29 13:27:21 2019 -0400

    Jupyter lab integrations
---
 .../jhub_airavata_authenticator/__init__.py        |   0
 .../jhub_airavata_authenticator/airavata_auth.py   |   9 +
 .../jhub_airavata_authenticator/setup.py           |  51 ++++
 .../jhub_airavata_authenticator/version.py         |  10 +
 .../jupyter_airavata_data_store/__init__.py        |   0
 .../airavata_file_operations.py                    | 120 ++++++++++
 .../jupyter_airavata_data_store/checkpoint.py      |  44 ++++
 .../jupyter_airavata_data_store/content_manager.py | 262 +++++++++++++++++++++
 .../jupyter_airavata_data_store/ipycompact.py      |  92 ++++++++
 .../jupyter_airavata_data_store/setup.py           |  51 ++++
 .../jupyter_airavata_data_store/version.py         |  10 +
 11 files changed, 649 insertions(+)

diff --git a/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/jhub_airavata_authenticator/__init__.py b/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/jhub_airavata_authenticator/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/jhub_airavata_authenticator/airavata_auth.py b/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/jhub_airavata_authenticator/airavata_auth.py
new file mode 100644
index 0000000..dde3d1a
--- /dev/null
+++ b/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/jhub_airavata_authenticator/airavata_auth.py
@@ -0,0 +1,9 @@
+from tornado import gen
+from jupyterhub.auth import Authenticator
+
+class Airav:ataAuthenticator(Authenticator):
+
+    @gen.coroutine
+    def authenticate(self, handler, data):
+        if data['username'] == 'admin':
+            return data['username']
\ No newline at end of file
diff --git a/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/setup.py b/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/setup.py
new file mode 100644
index 0000000..8aa0e43
--- /dev/null
+++ b/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/setup.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+from __future__ import print_function
+
+import os
+import sys
+
+from setuptools import setup
+
+pjoin = os.path.join
+here = os.path.abspath(os.path.dirname(__file__))
+
+# Get the current package version.
+version_ns = {}
+with open(pjoin(here, 'version.py')) as f:
+    exec(f.read(), {}, version_ns)
+
+setup_args = dict(
+    name                = 'jhub_airavata_authenticator',
+    packages            = ['jhub_airavata_authenticator'],
+    version             = version_ns['__version__'],
+    description         = """Airavata Authenticator: An Authenticator for Jupyterhub that authenticates against Airavata user store.""",
+    long_description    = "",
+    author              = "Dimuthu Wannipurage (https://github.com/DImuthuUpe)",
+    author_email        = "dimuthu.upeksha2@gmail.com",
+    url                 = "https://github.com/DImuthuUpe/jhub_airavata_authenticator",
+    license             = "Apache 2.0",
+    platforms           = "Linux, Mac OS X",
+    keywords            = ['Authenticator', 'Airavata'],
+    classifiers         = [
+        'Intended Audience :: Developers',
+        'Intended Audience :: System Administrators',
+        'Intended Audience :: Science/Research',
+        'License :: Apache 2.0',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 3',
+    ],
+)
+
+# setuptools requirements
+if 'setuptools' in sys.modules:
+    setup_args['install_requires'] = install_requires = []
+    install_requires.append('jupyterhub')
+    install_requires.append('lxml')
+
+def main():
+    setup(**setup_args)
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file
diff --git a/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/version.py b/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/version.py
new file mode 100644
index 0000000..65c9a51
--- /dev/null
+++ b/jupyter-lab-integrations/auth-plugin/jhub_airavata_authenticator/version.py
@@ -0,0 +1,10 @@
+version_info = (
+    0,
+    0,
+    1,
+    #'dev', # comment-out this line for a release
+)
+__version__ = '.'.join(map(str, version_info[:3]))
+
+if len(version_info) > 3:
+    __version__ = '%s-%s' % (__version__, version_info[3])
\ No newline at end of file
diff --git a/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/__init__.py b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/airavata_file_operations.py b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/airavata_file_operations.py
new file mode 100644
index 0000000..e06d4a1
--- /dev/null
+++ b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/airavata_file_operations.py
@@ -0,0 +1,120 @@
+import os
+from pathlib import Path
+from airavata.api import Airavata
+from thrift import Thrift
+from thrift.transport import TSocket, TSSLSocket
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol
+from airavata.model.security.ttypes import AuthzToken
+
+
+class AiravataFileOperations:
+
+    basePath = "/tmp/a"
+    gateway = "default"
+    storageResourceId = "192.168.99.105_3bf6b8d9-67a6-4faf-b201-68550eeeb978"
+    user = "default-admin"
+
+    def __init__(self):
+        try:
+            self.socket = TSSLSocket.TSSLSocket('192.168.99.105', 9930, validate=False)
+
+        except Thrift.TException as tx:
+            print('%s' % (tx.message))
+
+    def is_file(self, path):
+        absPath = self.appendPaths(self.basePath, path)
+        transport = TTransport.TBufferedTransport(self.socket)
+        transport.open()
+        protocol = TBinaryProtocol.TBinaryProtocol(transport)
+        client = Airavata.Client(protocol)
+
+        fileStructure = client.getFileDetailsFromStorage(AuthzToken(""), self.gateway, self.storageResourceId, self.user, absPath)
+
+        transport.close()
+        return fileStructure.isFile and fileStructure.isExist
+
+    def file_exists(self, path):
+        absPath = self.appendPaths(self.basePath, path)
+        print("airavata_file_operations.file_exists " + absPath)
+
+        transport = TTransport.TBufferedTransport(self.socket)
+        transport.open()
+        protocol = TBinaryProtocol.TBinaryProtocol(transport)
+        client = Airavata.Client(protocol)
+
+        fileStructure = client.getFileDetailsFromStorage(AuthzToken(""), self.gateway, self.storageResourceId, self.user, absPath)
+
+        transport.close()
+
+        return fileStructure.isFile and fileStructure.isExist
+
+    def dir_exists(self, path):
+        absPath = self.appendPaths(self.basePath, path)
+        print("airavata_file_operations.dir_exists " + absPath)
+
+        transport = TTransport.TBufferedTransport(self.socket)
+        transport.open()
+        protocol = TBinaryProtocol.TBinaryProtocol(transport)
+        client = Airavata.Client(protocol)
+
+        fileStructure = client.getFileDetailsFromStorage(AuthzToken(""), self.gateway, self.storageResourceId, self.user, absPath)
+
+        transport.close()
+        return (not fileStructure.isFile) and (fileStructure.isExist)
+
+    def ls_dir(self, path):
+        # returns a list of paths of the all the files inside the directory
+        absPath = self.appendPaths(self.basePath, path)
+
+        transport = TTransport.TBufferedTransport(self.socket)
+        transport.open()
+        protocol = TBinaryProtocol.TBinaryProtocol(transport)
+        client = Airavata.Client(protocol)
+
+        fileStructures = client.listDirectoryFromStorage(AuthzToken(""), self.gateway, self.storageResourceId, self.user, absPath)
+        fileList = []
+        for fileStructure in fileStructures:
+            fileList.append(fileStructure.path.replace(self.basePath, "", 1))
+
+        transport.close()
+        return fileList
+
+    def read_file(self, path):
+        absPath = self.appendPaths(self.basePath, path)
+        print("airavata_file_operations.read_file " + absPath)
+        transport = TTransport.TBufferedTransport(self.socket)
+        transport.open()
+        protocol = TBinaryProtocol.TBinaryProtocol(transport)
+        client = Airavata.Client(protocol)
+
+        fileStructure = client.downloadFileFromStorage(AuthzToken(""), self.gateway, self.storageResourceId, self.user, absPath)
+        print(fileStructure)
+        transport.close()
+        return fileStructure.content.decode("utf-8")
+
+    def write_file(self, path, content):
+        absPath = self.appendPaths(self.basePath, path)
+        print("airavata_file_operations.write_file " + absPath)
+
+        transport = TTransport.TBufferedTransport(self.socket)
+        transport.open()
+        protocol = TBinaryProtocol.TBinaryProtocol(transport)
+        client = Airavata.Client(protocol)
+
+        client.uploadFileToStorage(AuthzToken(""), self.gateway, self.storageResourceId, self.user, bytearray(content, "utf-8"), absPath, "text")
+        transport.close()
+
+    def create_dir(self, path):
+        Path(self.basePath + path).mkdir()
+
+    def rename_file(self, oldPath, newPath):
+        os.renames(self.basePath + oldPath, self.basePath + newPath)
+
+    def delete_file(self, path):
+        os.remove(self.basePath + path)
+
+    def appendPaths(self, first, second):
+        if second.startswith("/"):
+            second = second[1:]
+        return first + "/" + second
diff --git a/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/checkpoint.py b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/checkpoint.py
new file mode 100644
index 0000000..95da23f
--- /dev/null
+++ b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/checkpoint.py
@@ -0,0 +1,44 @@
+from jupyter_airavata_data_store.ipycompact import Checkpoints, GenericCheckpointsMixin
+
+class AiravataCheckpoints(GenericCheckpointsMixin, Checkpoints):
+
+    def create_notebook_checkpoint(self, nb, path):
+        """Create a checkpoint of the current state of a notebook
+        Returns a checkpoint_id for the new checkpoint.
+        """
+        self.log.info("Airavata.checkpounts.create_notebook_checkpoint: ('%s')", path)
+
+    def create_file_checkpoint(self, content, format, path):
+        """Create a checkpoint of the current state of a file
+        Returns a checkpoint_id for the new checkpoint.
+        """
+        self.log.info("Airavata.checkpounts.create_file_checkpoint: ('%s')", path)
+
+    def delete_checkpoint(self, checkpoint_id, path):
+        """delete a checkpoint for a file"""
+        self.log.info("Airavata.checkpounts.delete_checkpoint: ('%s') ('%s')", checkpoint_id, path)
+
+    def get_checkpoint_content(self, checkpoint_id, path):
+        """Get the content of a checkpoint."""
+        self.log.info("Airavata.checkpounts.get_checkpoint_content: ('%s') ('%s')", checkpoint_id, path)
+
+    def get_notebook_checkpoint(self, checkpoint_id, path):
+        self.log.info("Airavata.checkpounts.get_notebook_checkpoint: ('%s') ('%s')", checkpoint_id, path)
+
+    def get_file_checkpoint(self, checkpoint_id, path):
+        self.log.info("Airavata.checkpounts.get_file_checkpoint: ('%s') ('%s')", checkpoint_id, path)
+
+    def list_checkpoints(self, path):
+        """Return a list of checkpoints for a given file"""
+        self.log.info("Airavata.checkpounts.list_checkpoints: ('%s')", path)
+
+    def rename_all_checkpoints(self, old_path, new_path):
+        """Rename all checkpoints for old_path to new_path."""
+        self.log.info("Airavata.checkpounts.rename_all_checkpoints: ('%s') ('%s')", old_path, new_path)
+
+    def delete_all_checkpoints(self, path):
+        """Delete all checkpoints for the given path."""
+        self.log.info("Airavata.checkpounts.delete_all_checkpoints: ('%s')", path)
+
+
+
diff --git a/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/content_manager.py b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/content_manager.py
new file mode 100644
index 0000000..d26bdb3
--- /dev/null
+++ b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/content_manager.py
@@ -0,0 +1,262 @@
+import datetime
+import mimetypes
+import json
+
+import os
+from notebook.services.contents.largefilemanager import LargeFileManager
+from jupyter_airavata_data_store.ipycompact import ContentsManager
+from jupyter_airavata_data_store.ipycompact import GenericFileCheckpoints, reads, from_dict
+from tornado.web import HTTPError
+from jupyter_airavata_data_store.airavata_file_operations import AiravataFileOperations
+from jupyter_airavata_data_store.checkpoint import AiravataCheckpoints
+
+DUMMY_CREATED_DATE = datetime.datetime.fromtimestamp(86400)
+NBFORMAT_VERSION = 4
+
+class AiaravataContentManager(ContentsManager):
+
+    fileOperations = AiravataFileOperations()
+
+    def file_exists(self, path =''):
+        # Does a file exist at the given path?
+        self.log.info("AiaravataContentManager.file_exists: ('%s')", path)
+        return self.fileOperations.file_exists(path)
+
+    def dir_exists(self, path):
+        # Does a directory exist at the given path?
+        self.log.info("AiaravataContentManager.dir_exists: path('%s')", path)
+        result = self.fileOperations.dir_exists(path)
+        print(result)
+        return result
+
+    def get(self, path, content=True, type=None, format=None):
+        # Get a file or directory model.
+        self.log.info("AiaravataContentManager.get path('%s') type(%s) format(%s)", path, type, format)
+        path = path.strip('/')
+
+        if type is None:
+            type = self.guess_type(path)
+        try:
+            func = {
+                "directory": self._get_directory,
+                "notebook": self._get_notebook,
+                "file": self._get_file,
+            }[type]
+        except KeyError:
+            raise ValueError("Unknown type passed: '{}'".format(type))
+
+        return func(path=path, content=content, format=format)
+
+    def save(self, model, path):
+        """Save a file or directory model to path.
+        """
+        self.log.info("AiaravataContentManager: save %s: '%s'", model, path)
+        if "type" not in model:
+            self.do_error("No model type provided", 400)
+        if "content" not in model and model["type"] != "directory":
+            self.do_error("No file content provided", 400)
+
+        if model["type"] not in ("file", "directory", "notebook"):
+            self.do_error("Unhandled contents type: %s" % model["type"], 400)
+
+        try:
+            if model["type"] == "notebook":
+                validation_message = self._save_notebook(model, path)
+            elif model["type"] == "file":
+                validation_message = self._save_file(model, path)
+            else:
+                validation_message = self._save_directory(path)
+        except Exception as e:
+            self.log.error("Error while saving file: %s %s", path, e, exc_info=True)
+            self.do_error("Unexpected error while saving file: %s %s" % (path, e), 500)
+
+        model = self.get(path, type=model["type"], content=False)
+        if validation_message is not None:
+            model["message"] = validation_message
+        return model
+
+    def rename_file(self, old_path, new_path):
+        """Rename a file or directory.
+        NOTE: This method is unfortunately named on the base class.  It
+        actually moves a file or a directory.
+        """
+        self.log.info("AiaravataContentManager: Init rename of '%s' to '%s'", old_path, new_path)
+        if self.file_exists(new_path) or self.dir_exists(new_path):
+            self.already_exists(new_path)
+        elif self.file_exists(old_path) or self.dir_exists(old_path):
+            self.log.info("AiaravataContentManager: Actually renaming '%s' to '%s'", old_path,
+                           new_path)
+            self.fileOperations.rename_file(old_path, new_path)
+        else:
+            self.no_such_entity(old_path)
+
+    def delete_file(self, path):
+        """Delete the file or directory at path.
+        """
+        self.log.info("AiaravataContentManager: delete_file '%s'", path)
+        if self.file_exists(path) or self.dir_exists(path):
+            self.fileOperations.delete_file(path)
+        else:
+            self.no_such_entity(path)
+
+    def is_hidden(self, path):
+        """Is path a hidden directory or file?
+        """
+        self.log.info("AiaravataContentManager: is_hidden '%s'", path)
+        return False
+
+    def _checkpoints_class_default(self):
+        return AiravataCheckpoints
+
+    def do_error(self, msg, code=500):
+        raise HTTPError(code, msg)
+
+    def no_such_entity(self, path):
+        self.do_error("No such entity: [{path}]".format(path=path), 404)
+
+    def already_exists(self, path):
+        thing = "File" if self.file_exists(path) else "Directory"
+        self.do_error(u"{thing} already exists: [{path}]".format(thing=thing, path=path), 409)
+
+    def guess_type(self, path, allow_directory=True):
+        """
+        Guess the type of a file.
+        If allow_directory is False, don't consider the possibility that the
+        file is a directory.
+        Parameters
+        ----------
+            obj: s3.Object or string
+        """
+        if path.endswith(".ipynb"):
+            return "notebook"
+        elif allow_directory and self.dir_exists(path):
+            return "directory"
+        else:
+            return "file"
+
+    def _get_directory(self, path, content=True, format=None):
+        self.log.info("AiaravataContentManager.get_directory: path('%s') content(%s) format(%s)", path, content, format)
+        return self._directory_model_from_path(path, content=content)
+
+    def _get_notebook(self, path, content=True, format=None):
+        self.log.info("AiaravataContentManager.get_notebook: path('%s') type(%s) format(%s)", path, content, format)
+        return self._notebook_model_from_path(path, content=content, format=format)
+
+    def _get_file(self, path, content=True, format=None):
+        self.log.info("AiaravataContentManager.get_file: path('%s') type(%s) format(%s)", path, content, format)
+        return self._file_model_from_path(path, content=content, format=format)
+
+    def _directory_model_from_path(self, path, content=False):
+        self.log.info("AiaravataContentManager._directory_model_from_path: path('%s') type(%s)", path, content)
+        model = base_directory_model(path)
+        if content:
+            if not self.dir_exists(path):
+                self.no_such_entity(path)
+            model["format"] = "json"
+            dir_content = self.fileOperations.ls_dir(path)
+            model["content"] = self._convert_file_records(dir_content)
+        return model
+
+    def _notebook_model_from_path(self, path, content=False, format=None):
+        """
+        Build a notebook model from database record.
+        """
+        model = base_model(path)
+        model["type"] = "notebook"
+        if self.fileOperations.is_file(path):
+            model["last_modified"] = model["created"] = DUMMY_CREATED_DATE
+        else:
+            model["last_modified"] = model["created"] = DUMMY_CREATED_DATE
+        if content:
+            if not self.fileOperations.file_exists(path):
+                self.no_such_entity(path)
+            print("step 2")
+            file_content = self.fileOperations.read_file(path)
+            nb_content = reads(file_content, as_version=NBFORMAT_VERSION)
+            self.mark_trusted_cells(nb_content, path)
+            model["format"] = "json"
+            model["content"] = nb_content
+            self.validate_notebook_model(model)
+        return model
+
+    def _file_model_from_path(self, path, content=False, format=None):
+        """
+        Build a file model from database record.
+        """
+        model = base_model(path)
+        model["type"] = "file"
+        if self.fileOperations.file_exists(path):
+            model["last_modified"] = model["created"] = DUMMY_CREATED_DATE
+        else:
+            model["last_modified"] = model["created"] = DUMMY_CREATED_DATE
+        if content:
+            print("step 1")
+            content = self.fileOperations.read_file(path)
+
+            model["format"] = format or "text"
+            model["content"] = content
+            model["mimetype"] = mimetypes.guess_type(path)[0] or "text/plain"
+            if format == "base64":
+                model["format"] = format or "base64"
+                from base64 import b64decode
+                model["content"] = b64decode(content)
+            print("step 3")
+
+        return model
+
+    def _convert_file_records(self, paths):
+        """
+        Applies _notebook_model_from_s3_path or _file_model_from_s3_path to each entry of `paths`,
+        depending on the result of `guess_type`.
+        """
+        ret = []
+        for path in paths:
+            type_ = self.guess_type(path, allow_directory=True)
+            if type_ == "notebook":
+                ret.append(self._notebook_model_from_path(path, False))
+            elif type_ == "file":
+                ret.append(self._file_model_from_path(path, False, None))
+            elif type_ == "directory":
+                ret.append(self._directory_model_from_path(path, False))
+            else:
+                self.do_error("Unknown file type %s for file '%s'" % (type_, path), 500)
+        return ret
+
+    def _save_notebook(self, model, path):
+        nb_contents = from_dict(model['content'])
+        self.check_and_sign(nb_contents, path)
+        file_contents = json.dumps(model["content"])
+        self.fileOperations.write_file(path, file_contents)
+        self.validate_notebook_model(model)
+        return model.get("message")
+
+    def _save_file(self, model, path):
+        file_contents = model["content"]
+        file_format = model.get('format')
+        self.fileOperations.write_file(path, file_contents)
+
+    def _save_directory(self, path):
+        self.fileOperations.create_dir(path)
+
+
+def base_model(path):
+    return {
+        "name": path.rsplit('/', 1)[-1],
+        "path": path,
+        "writable": True,
+        "last_modified": None,
+        "created": None,
+        "content": None,
+        "format": None,
+        "mimetype": None,
+    }
+
+
+def base_directory_model(path):
+    model = base_model(path)
+    model.update(
+        type="directory",
+        last_modified=DUMMY_CREATED_DATE,
+        created=DUMMY_CREATED_DATE,)
+    return model
+
diff --git a/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/ipycompact.py b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/ipycompact.py
new file mode 100644
index 0000000..a146c5c
--- /dev/null
+++ b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/jupyter_airavata_data_store/ipycompact.py
@@ -0,0 +1,92 @@
+"""
+Utilities for managing IPython 3/4 compat.
+Taken from: https://github.com/quantopian/pgcontents/blob/master/pgcontents/utils/ipycompat.py
+"""
+import IPython
+
+SUPPORTED_VERSIONS = {3, 4, 5, 6}
+IPY_MAJOR = IPython.version_info[0]
+if IPY_MAJOR not in SUPPORTED_VERSIONS:
+    raise ImportError("IPython version %d is not supported." % IPY_MAJOR)
+
+IPY3 = (IPY_MAJOR == 3)
+
+if IPY3:
+    from IPython.config import Config
+    from IPython.html.services.contents.manager import ContentsManager
+    from IPython.html.services.contents.checkpoints import (
+        Checkpoints,
+        GenericCheckpointsMixin,)
+    from IPython.html.services.contents.filemanager import FileContentsManager
+    from IPython.html.services.contents.filecheckpoints import (GenericFileCheckpoints)
+    from IPython.html.services.contents.tests.test_manager import (TestContentsManager)
+    from IPython.html.services.contents.tests.test_contents_api import (APITest)
+    from IPython.html.utils import to_os_path
+    from IPython.nbformat import from_dict, reads, writes
+    from IPython.nbformat.v4.nbbase import (
+        new_code_cell,
+        new_markdown_cell,
+        new_notebook,
+        new_raw_cell,)
+    from IPython.nbformat.v4.rwbase import strip_transient
+    from IPython.utils.traitlets import (
+        Any,
+        Bool,
+        Dict,
+        Instance,
+        Integer,
+        HasTraits,
+        Unicode,)
+else:
+    from traitlets.config import Config
+    from notebook.services.contents.checkpoints import (
+        Checkpoints,
+        GenericCheckpointsMixin,)
+    from notebook.services.contents.filemanager import FileContentsManager
+    from notebook.services.contents.filecheckpoints import (GenericFileCheckpoints)
+    from notebook.services.contents.manager import ContentsManager
+    from notebook.services.contents.tests.test_manager import (TestContentsManager)
+    from notebook.services.contents.tests.test_contents_api import (APITest)
+    from notebook.utils import to_os_path
+    from nbformat import from_dict, reads, writes
+    from nbformat.v4.nbbase import (
+        new_code_cell,
+        new_markdown_cell,
+        new_notebook,
+        new_raw_cell,)
+    from nbformat.v4.rwbase import strip_transient
+    from traitlets import (
+        Any,
+        Bool,
+        Dict,
+        Instance,
+        Integer,
+        HasTraits,
+        Unicode,)
+
+__all__ = [
+    'APITest',
+    'Any',
+    'Bool',
+    'Checkpoints',
+    'Config',
+    'ContentsManager',
+    'Dict',
+    'FileContentsManager',
+    'GenericCheckpointsMixin',
+    'GenericFileCheckpoints',
+    'HasTraits',
+    'Instance',
+    'Integer',
+    'TestContentsManager',
+    'Unicode',
+    'from_dict',
+    'new_code_cell',
+    'new_markdown_cell',
+    'new_notebook',
+    'new_raw_cell',
+    'reads',
+    'strip_transient',
+    'to_os_path',
+    'writes',
+]
\ No newline at end of file
diff --git a/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/setup.py b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/setup.py
new file mode 100644
index 0000000..268e24b
--- /dev/null
+++ b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/setup.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+from __future__ import print_function
+
+import os
+import sys
+
+from setuptools import find_packages, setup
+
+pjoin = os.path.join
+here = os.path.abspath(os.path.dirname(__file__))
+
+# Get the current package version.
+version_ns = {}
+with open(pjoin(here, 'version.py')) as f:
+    exec(f.read(), {}, version_ns)
+
+setup_args = dict(
+    name                = 'jupyter_airavata_data_store',
+    packages            = find_packages(include=['jupyter_airavata_data_store', 'jupyter_airavata_data_store.*']),
+    version             = version_ns['__version__'],
+    description         = """Jupyter Airavata Data Store: This overrides default Jupyter filesystem based storage with Airavata storage utilizing Airavata data catalog APIs.""",
+    long_description    = "",
+    author              = "Dimuthu Wannipurage (https://github.com/DImuthuUpe)",
+    author_email        = "dimuthu.upeksha2@gmail.com",
+    url                 = "https://github.com/DImuthuUpe/jupyter_airavata_data_store",
+    license             = "Apache 2.0",
+    platforms           = "Linux, Mac OS X",
+    keywords            = ['Data Store', 'Airavata', 'Jupyter'],
+    classifiers         = [
+        'Intended Audience :: Developers',
+        'Intended Audience :: System Administrators',
+        'Intended Audience :: Science/Research',
+        'License :: Apache 2.0',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 3',
+    ],
+)
+
+# setuptools requirements
+if 'setuptools' in sys.modules:
+    setup_args['install_requires'] = install_requires = []
+    install_requires.append('jupyterhub')
+    install_requires.append('lxml')
+
+def main():
+    setup(**setup_args)
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file
diff --git a/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/version.py b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/version.py
new file mode 100644
index 0000000..65c9a51
--- /dev/null
+++ b/jupyter-lab-integrations/datastore-plugin/jupyter_airavata_data_store/version.py
@@ -0,0 +1,10 @@
+version_info = (
+    0,
+    0,
+    1,
+    #'dev', # comment-out this line for a release
+)
+__version__ = '.'.join(map(str, version_info[:3]))
+
+if len(version_info) > 3:
+    __version__ = '%s-%s' % (__version__, version_info[3])
\ No newline at end of file