Commit d4893698 authored by SIMONIN Matthieu's avatar SIMONIN Matthieu

Initial commit

Support for basic listing for basic resources.
parents
.pytest_cache
################
# Ansible #
################
*.retry
################
# VIM #
################
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# Session
Session.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
################
# Python #
################
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# This file is a template, and might need editing before it works on your project.
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: python:3.6
python3.5:
image: python:3.5
stage: test
tags: [qlf-ci.inria.fr]
script:
- pip install tox
- tox -e py35 pep8
python3.6:
image: python:3.6
stage: test
tags: [qlf-ci.inria.fr]
script:
- pip install tox
- tox -e py36 pep8
#### Entering th release zone
pages:
stage: deploy
tags: [qlf-ci.inria.fr]
only:
- tags
script:
- pip install tox
- tox -e docs
- mkdir -p public/
- cp -r docs/_build/html/* public/
artifacts:
paths:
- public
pypi:
stage: deploy
tags: [qlf-ci.inria.fr]
only:
- tags
script:
- python setup.py bdist_wheel
- pip install twine
# credentials are set in the env by gitlab
- twine upload dist/* --verbose
* 0.0.1
- TODO
This diff is collapsed.
# http://stackoverflow.com/a/24727824
# https://www.youtube.com/watch?v=o-UbWsO9rZk
include LICENSE.txt
Python Grid5000
================
``python-grid5000`` is a Python package providing access to the Grid5000 API.
.. warning::
The code is currently being developed heavily.
Thanks to
=========
The code is borrowed from `python-gitlab
<https://github.com/python-gitlab/python-gitlab>`_ with small adaptation to
conform with Grid5000 API model.
Installation
============
Requirements
------------
python-gitlab depends on:
* `python-requests <http://docs.python-requests.org/en/latest/>`_
Install with pip
----------------
.. code-block:: console
pip install python-grid5000
Examples
========
* Initial (not curated) example
.. code-block:: python
import time
from grid5000 import Grid5000
gk = Grid5000(login="your_login", password="your_password")
root = gk.root.get()
print(root)
sites = gk.sites.list()
site = sites[0]
print(site)
jobs = sites[0].jobs.list()
print(jobs)
job = jobs[0]
print(job)
job = sites[0].jobs.create({
"name":"pyg5k",
"command": "sleep 3600",
})
while job.state != "running":
job.refresh()
print("waiting the job to start")
time.sleep(5)
print(job)
print("assigned nodes=%s" % job.assigned_nodes)
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -msphinx
SPHINXPROJ = python-grid5000
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
# -*- coding: utf-8 -*-
#
# python-grid5000 documentation build configuration file, created by
# sphinx-quickstart on Thu Sep 21 21:45:39 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sphinx_rtd_theme
import sys
sys.path.insert(0, os.path.abspath('..'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
#extensions = []
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'python-grid5000'
copyright = u'2019, Matthieu Simonin'
author = u'Matthieu Simonin'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
import pkg_resources
version = pkg_resources.require(project)[0].version
# The full version, including alpha/beta/rc tags.
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
#html_theme = 'alabaster'
html_theme = "sphinx_rtd_theme"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
'**': [
'about.html',
'navigation.html',
'relations.html', # needs 'show_related': True theme option to display
'searchbox.html',
'donate.html',
]
}
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'python-grid5000doc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'python-grid5000.tex', u'python-grid5000 Documentation',
u'Matthieu Simonin', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'python-grid5000', u'python-grid5000 Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'python-grid5000', u'python-grid5000 Documentation',
author, 'python-grid5000', 'One line description of project.',
'Miscellaneous'),
]
rst_epilog = """
.. |python-grid5000| replace:: python-grid5000
"""
# Document also the __init__
autoclass_content = "both"
.. include:: ../README.rst
This diff is collapsed.
import importlib
class RESTManager(object):
"""Base class for CRUD operations on objects.
Derivated class must define ``_path`` and ``_obj_cls``.
``_path``: Base URL path on which requests will be sent (e.g. '/projects')
``_obj_cls``: The class of objects that will be created
"""
_path = None
_obj_cls = None
def __init__(self, gk, parent=None):
"""REST manager constructor.
Args:
gl (Grid5000): :class:`~gitlab.Grid5000` connection to use to make
requests.
parent: REST object to which the manager is attached.
"""
self.grid5000 = gk
self._parent = parent # for nested managers
self._computed_path = self._compute_path()
@property
def parent_attrs(self):
return self._parent_attrs
def _compute_path(self, path=None):
self._parent_attrs = {}
if path is None:
path = self._path
if self._parent is None or not hasattr(self, '_from_parent_attrs'):
return path
data = {self_attr: getattr(self._parent, parent_attr, None)
for self_attr, parent_attr in self._from_parent_attrs.items()}
self._parent_attrs = data
return path % data
@property
def path(self):
return self._computed_path
class RESTObject(object):
"""Represents an object built from server data.
It holds the attributes know from the server, and the updated attributes in
another. This allows smart updates, if the object allows it.
You can redefine ``_id_attr`` in child classes to specify which attribute
must be used as uniq ID. ``None`` means that the object can be updated
without ID in the url.
"""
_id_attr = 'uid'
def __init__(self, manager, attrs):
self.__dict__.update({
'manager': manager,
'_attrs': attrs,
'_updated_attrs': {},
'_module': importlib.import_module(self.__module__)
})
self.__dict__['_parent_attrs'] = self.manager.parent_attrs
self._create_managers()
def __getstate__(self):
state = self.__dict__.copy()
module = state.pop('_module')
state['_module_name'] = module.__name__
return state
def __setstate__(self, state):
module_name = state.pop('_module_name')
self.__dict__.update(state)
self._module = importlib.import_module(module_name)
def __getattr__(self, name):
try:
return self.__dict__['_updated_attrs'][name]
except KeyError:
try:
value = self.__dict__['_attrs'][name]
# If the value is a list, we copy it in the _updated_attrs dict
# because we are not able to detect changes made on the object
# (append, insert, pop, ...). Without forcing the attr
# creation __setattr__ is never called, the list never ends up
# in the _updated_attrs dict, and the update() and save()
# method never push the new data to the server.
# See https://github.com/python-gitlab/python-gitlab/issues/306
#
# note: _parent_attrs will only store simple values (int) so we
# don't make this check in the next except block.
if isinstance(value, list):
self.__dict__['_updated_attrs'][name] = value[:]
return self.__dict__['_updated_attrs'][name]
return value
except KeyError:
try:
return self.__dict__['_parent_attrs'][name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
self.__dict__['_updated_attrs'][name] = value
def __str__(self):
data = self._attrs.copy()
data.update(self._updated_attrs)
return '%s => %s' % (type(self), data)
def __repr__(self):
if self._id_attr:
return '<%s %s:%s>' % (self.__class__.__name__,
self._id_attr,
self.get_id())
else:
return '<%s>' % self.__class__.__name__
def _create_managers(self):
managers = getattr(self, '_managers', None)
if managers is None:
return
for attr, cls_name in self._managers:
cls = getattr(self._module, cls_name)
manager = cls(self.manager.grid5000, parent=self)
self.__dict__[attr] = manager
def _update_attrs(self, new_attrs):
self.__dict__['_updated_attrs'] = {}
self.__dict__['_attrs'].update(new_attrs)
def get_id(self):
"""Returns the id of the resource."""
if self._id_attr is None:
return None
return getattr(self, self._id_attr)
@property
def attributes(self):
d = self.__dict__['_updated_attrs'].copy()
d.update(self.__dict__['_attrs'])
d.update(self.__dict__['_parent_attrs'])
return d
import functools
class Grid5000Error(Exception):
def __init__(self, error_message="", response_code=None,
response_body=None):
Exception.__init__(self, error_message)
# Http status code
self.response_code = response_code
# Full http response
self.response_body = response_body
# Parsed error message from gitlab
try:
# if we receive str/bytes we try to convert to unicode/str to have
# consistent message types (see #616)
self.error_message = error_message.decode()
except Exception:
self.error_message = error_message
def __str__(self):
if self.response_code is not None:
return "{0}: {1}".format(self.response_code, self.error_message)
else:
return "{0}".format(self.error_message)
class Grid5000AuthenticationError(Grid5000Error):
pass
class RedirectError(Grid5000Error):
pass
class Grid5000ParsingError(Grid5000Error):
pass
class Grid5000ConnectionError(Grid5000Error):
pass
class Grid5000OperationError(Grid5000Error):
pass
class Grid5000HttpError(Grid5000Error):
pass
class Grid5000ListError(Grid5000OperationError):
pass
class Grid5000GetError(Grid5000OperationError):
pass
class Grid5000CreateError(Grid5000OperationError):
pass
class Grid5000UpdateError(Grid5000OperationError):
pass
class Grid5000DeleteError(Grid5000OperationError):
pass
class Grid5000SetError(Grid5000OperationError):
pass
class Grid5000ProtectError(Grid5000OperationError):
pass
class Grid5000TransferProjectError(Grid5000OperationError):
pass
class Grid5000ProjectDeployKeyError(Grid5000OperationError):
pass
class Grid5000CancelError(Grid5000OperationError):
pass
class Grid5000PipelineCancelError(Grid5000CancelError):
pass
class Grid5000RetryError(Grid5000OperationError):
pass
class Grid5000BuildCancelError(Grid5000CancelError):
pass
class Grid5000BuildRetryError(Grid5000RetryError):
pass
class Grid5000BuildPlayError(Grid5000RetryError):
pass
class Grid5000BuildEraseError(Grid5000RetryError):
pass
class Grid5000JobCancelError(Grid5000CancelError):
pass
class Grid5000JobRetryError(Grid5000RetryError):
pass
class Grid5000JobPlayError(Grid5000RetryError):
pass
class Grid5000JobEraseError(Grid5000RetryError):
pass
class Grid5000PipelineRetryError(Grid5000RetryError):
pass
class Grid5000BlockError(Grid5000OperationError):
pass
class Grid5000UnblockError(Grid5000OperationError):
pass
class Grid5000SubscribeError(Grid5000OperationError):
pass
class Grid5000UnsubscribeError(Grid5000OperationError):