├── test-requirements.txt
├── requirements.txt
├── .coveragerc
├── .gitignore
├── examples
├── facecat
│ ├── media
│ │ └── cat.png
│ ├── static
│ │ ├── fonts
│ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ └── glyphicons-halflings-regular.woff
│ │ ├── css
│ │ │ └── facecat.css
│ │ └── js
│ │ │ └── bootstrap.min.js
│ ├── README.md
│ ├── views
│ │ ├── all.html
│ │ ├── show.html
│ │ └── index.html
│ └── facecat.py
├── walkthrough
│ ├── README.md
│ └── walkthrough.py
└── README.md
├── .travis.yml
├── CHANGES.md
├── doc
└── source
│ ├── index.rst
│ ├── runabove.rst
│ ├── examples.txt
│ └── conf.py
├── LICENSE
├── runabove
├── tests
│ ├── __init__.py
│ ├── region.py
│ ├── flavor.py
│ ├── image.py
│ ├── account.py
│ ├── client.py
│ ├── ssh_key.py
│ ├── wrapper_api.py
│ ├── instance.py
│ └── storage.py
├── __init__.py
├── exception.py
├── image.py
├── region.py
├── base.py
├── flavor.py
├── client.py
├── account.py
├── ssh_key.py
├── wrapper_api.py
├── instance.py
└── storage.py
├── setup.py
└── README.md
/test-requirements.txt:
--------------------------------------------------------------------------------
1 | mock
2 | httpretty==0.8.3
3 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | python-swiftclient>=2.1.0
2 | requests>=1.1
3 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | source = runabove
3 |
4 | [report]
5 | omit = */tests/*
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
3 | build/
4 | dist/
5 | python_runabove.egg-info/
6 | venv/
7 | .coverage
8 |
--------------------------------------------------------------------------------
/examples/facecat/media/cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NicolasLM/python-runabove/master/examples/facecat/media/cat.png
--------------------------------------------------------------------------------
/examples/facecat/static/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NicolasLM/python-runabove/master/examples/facecat/static/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/examples/facecat/static/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NicolasLM/python-runabove/master/examples/facecat/static/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/examples/facecat/static/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NicolasLM/python-runabove/master/examples/facecat/static/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.7"
4 | install:
5 | - "python setup.py install"
6 | - "pip install -r test-requirements.txt"
7 | - "pip install coverage"
8 | - "pip install coveralls"
9 | script: coverage run setup.py test
10 | after_success:
11 | coveralls
12 |
--------------------------------------------------------------------------------
/examples/walkthrough/README.md:
--------------------------------------------------------------------------------
1 | Walkthrough
2 | ===========
3 |
4 | This is an interactive Python script presenting RunAbove SDK. It shows how to
5 | use:
6 |
7 | * Instances
8 | * Regions
9 | * Flavors
10 | * Images
11 | * SSH keys
12 |
13 | To launch the script install RunAbove SDK and just enter:
14 | ```bash
15 | python walkthrough.py
16 | ```
17 |
--------------------------------------------------------------------------------
/CHANGES.md:
--------------------------------------------------------------------------------
1 | Changelog of Python SDK for RunAbove API
2 | ========================================
3 |
4 | Release 1.1.0 (2014-07-30)
5 | --------------------------
6 |
7 | * Limit the scope of permissions required by an application
8 | * Redirect the user to an URL after signing in
9 |
10 |
11 | Release 1.0.0 (2014-07-09)
12 | --------------------------
13 |
14 | * Initial release
15 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | RunAbove Python SDK examples
2 | ============================
3 |
4 | This folder contains demo applications that uses the Python SDK for the
5 | RunAbove API.
6 |
7 | Facecat
8 | -------
9 |
10 | A small image hosting web application that uses Object Storage as backend for
11 | images.
12 |
13 | Walkthrough
14 | -----------
15 |
16 | A Python script that shows how to list instances, containers, SSH keys and more
17 | as well as creating and deleting instances.
18 |
--------------------------------------------------------------------------------
/examples/facecat/static/css/facecat.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 20px;
3 | padding-bottom: 20px;
4 | }
5 |
6 | .navbar {
7 | margin-bottom: 20px;
8 | }
9 |
10 | .center-block {
11 | float: none;
12 | }
13 |
14 | span.glyphicon-big {
15 | font-size: 1.2em;
16 | }
17 |
18 | .btn-file {
19 | position: relative;
20 | overflow: hidden;
21 | }
22 |
23 | .btn-file input[type=file] {
24 | position: absolute;
25 | top: 0;
26 | right: 0;
27 | min-width: 100%;
28 | min-height: 100%;
29 | font-size: 999px;
30 | text-align: right;
31 | filter: alpha(opacity=0);
32 | opacity: 0;
33 | outline: none;
34 | background: white;
35 | cursor: inherit;
36 | display: block;
37 | }
38 |
39 | input[readonly] {
40 | background-color: white !important;
41 | cursor: text !important;
42 | }
43 |
44 | #submit {
45 | margin-top: 5px;
46 | }
47 |
48 | .padded {
49 | padding: 30px;
50 | }
51 |
--------------------------------------------------------------------------------
/doc/source/index.rst:
--------------------------------------------------------------------------------
1 | .. runabove documentation master file, created by
2 | sphinx-quickstart on Mon Apr 28 14:12:38 2014.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Python bindings to RunAbove API
7 | ====================================
8 |
9 | RunAbove client is official client of RunAbove API.
10 | You can manage your instances, your object storage and your account with this client.
11 |
12 | For more information about our API check:
13 | https://community.runabove.com/knowledge-base/article/how-to-use-runabove-api
14 |
15 |
16 | Documentation
17 | =============
18 | .. automodule:: runabove
19 | .. toctree::
20 | :maxdepth: 2
21 |
22 | runabove
23 |
24 | Examples
25 | ========
26 | .. include:: examples.txt
27 |
28 |
29 | Indices and tables
30 | ==================
31 | * :ref:`genindex`
32 | * :ref:`modindex`
33 | * :ref:`search`
34 |
--------------------------------------------------------------------------------
/doc/source/runabove.rst:
--------------------------------------------------------------------------------
1 | Runabove
2 | ========
3 |
4 | Account
5 | ------------------
6 |
7 | .. automodule:: runabove.account
8 | :members:
9 |
10 | Client
11 | ------------------
12 |
13 | .. automodule:: runabove.client
14 | :members:
15 |
16 | Exception
17 | ------------------
18 |
19 | .. automodule:: runabove.exception
20 | :members:
21 |
22 | Flavor
23 | ------------------
24 |
25 | .. automodule:: runabove.flavor
26 | :members:
27 |
28 | Image
29 | ------------------
30 |
31 | .. automodule:: runabove.image
32 | :members:
33 |
34 | Instance
35 | ------------------
36 |
37 | .. automodule:: runabove.instance
38 | :members:
39 |
40 | Region
41 | ------------------
42 |
43 | .. automodule:: runabove.region
44 | :members:
45 |
46 | Ssh_key
47 | ------------------
48 |
49 | .. automodule:: runabove.ssh_key
50 | :members:
51 |
52 | Storage
53 | ------------------
54 |
55 | .. automodule:: runabove.storage
56 | :members:
57 |
58 | Wrapper_api
59 | --------------------
60 |
61 | .. automodule:: runabove.wrapper_api
62 | :members:
63 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014, OVH
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
21 | Except as contained in this notice, the name of OVH and or its trademarks (and
22 | among others RunAbove) shall not be used in advertising or otherwise to promote
23 | the sale, use or other dealings in this Software without prior written
24 | authorization from OVH.
25 |
--------------------------------------------------------------------------------
/examples/facecat/README.md:
--------------------------------------------------------------------------------
1 | Facecat
2 | =======
3 |
4 | This is an example application of RunAbove Object Storage with the Python SDK.
5 | It uses the bottle web framework and OpenCV to make a funny picture hosting
6 | webapp. Images are stored in RunAbove Object Storage and served to clients
7 | directly from there.
8 |
9 | To allow clients to download objects directly, the container is made public.
10 |
11 | How to install it on Debian/Ubuntu?
12 | --------------------------------
13 |
14 | First make sure that you installed the RunAbove Python SDK on your machine.
15 | Then you can install the requirements used by the example application.
16 |
17 | The application requires OpenCV to apply modifications to the stored images.
18 | OpenCV and its Python bindings are available from repositories in Debian and
19 | Ubuntu.
20 |
21 | ```bash
22 | apt-get install libjpeg-dev python-dev python-opencv
23 | pip install bottle pillow
24 | ```
25 |
26 | If you want to get the application working quickly without installing OpenCV on
27 | your computer you can install it on a Debian instance in RunAbove.
28 |
29 | In the file `facecat.py` you must put your application key, application secret
30 | and consumer key which are the credentials needed to access your RunAbove
31 | account from the API.
32 |
33 | You can launch the web server and access it with your browser:
34 |
35 | ```bash
36 | python facecat.py
37 | ```
38 |
--------------------------------------------------------------------------------
/runabove/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
--------------------------------------------------------------------------------
/runabove/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | """RunAbove bindings."""
29 | import pkg_resources
30 |
31 | from client import Runabove
32 |
33 | __version__ = pkg_resources.get_distribution("python-runabove").version
34 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- encoding: utf-8 -*-
3 | #
4 | # Copyright (c) 2014, OVH
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in
14 | # all copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | # SOFTWARE.
23 | #
24 | # Except as contained in this notice, the name of OVH and or its trademarks
25 | # (and among others RunAbove) shall not be used in advertising or otherwise to
26 | # promote the sale, use or other dealings in this Software without prior
27 | # written authorization from OVH.
28 |
29 | import setuptools
30 |
31 | setuptools.setup(
32 | name='python-runabove',
33 | version='1.1.0',
34 | author='RunAbove',
35 | author_email='dev@runabove.com',
36 | url='https://www.runabove.com/',
37 | description='RunAbove API Client Library',
38 | keywords = "runabove api sdk",
39 | license='MIT',
40 | packages=['runabove'],
41 | test_suite='runabove.tests',
42 | install_requires=[
43 | 'python-swiftclient>=2.1.0',
44 | 'requests>=1.1'
45 | ]
46 | )
47 |
--------------------------------------------------------------------------------
/runabove/exception.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | """This module defines the exceptions used in the SDK."""
29 |
30 |
31 | class APIError(Exception):
32 | """General error of the API."""
33 |
34 | def __init__(self, msg=None):
35 | Exception.__init__(self, msg)
36 |
37 |
38 | class ReadOnlyException(APIError):
39 | """Error raised when a read only data is modified"""
40 | pass
41 |
42 |
43 | class ResourceNotFoundError(APIError):
44 | """Error raised when a requested resource does not exist."""
45 | pass
46 |
47 |
48 | class BadParametersError(APIError):
49 | """Error raised when a request contains bad parameters"""
50 | pass
51 |
52 |
53 | class ResourceAlreadyExistsError(APIError):
54 | """Error raised when trying to create a resource that exists."""
55 | pass
56 |
57 |
58 | class NetworkError(APIError):
59 | """Error raised when there is an error from network layer"""
60 | pass
61 |
--------------------------------------------------------------------------------
/examples/facecat/views/all.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/runabove/image.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | """RunAbove image service library."""
29 |
30 | from base import Resource, BaseManagerWithList
31 |
32 |
33 | class ImageManager(BaseManagerWithList):
34 | """Manage images available in RunAbove."""
35 |
36 | basepath = '/image'
37 |
38 | def get_by_id(self, image_id=None):
39 | """Get one image from a RunAbove account.
40 |
41 | :param image_id: ID of the image to retrieve
42 | """
43 | url = self.basepath + '/' + self._api.encode_for_api(image_id)
44 | image = self._api.get(url)
45 | return self._dict_to_obj(image)
46 |
47 | def _dict_to_obj(self, key):
48 | """Converts a dict to an image object."""
49 | region = self._handler.regions._name_to_obj(key['region'])
50 | return Image(self,
51 | key['id'],
52 | key.get('name'),
53 | region=region)
54 |
55 |
56 | class Image(Resource):
57 | """Represents one image."""
58 |
59 | def __init__(self, manager, id, name, region):
60 | self._manager = manager
61 | self.id = id
62 | self.name = name
63 | self.region = region
64 |
65 |
--------------------------------------------------------------------------------
/examples/facecat/views/show.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Picture - Facecat
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
19 |
20 |
21 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/runabove/region.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | """RunAbove region service library."""
29 |
30 | from base import Resource, BaseManager
31 | from .exception import ResourceNotFoundError
32 |
33 |
34 | class RegionManager(BaseManager):
35 | """Manage regions available in RunAbove."""
36 |
37 | basepath = '/region'
38 |
39 | def list(self):
40 | """Get list of regions available."""
41 |
42 | res = self._api.get(self.basepath)
43 | regions = []
44 | for region_name in res:
45 | regions.append(Region(self, region_name))
46 | return regions
47 |
48 | def _name_to_obj(self, region_name):
49 | """Makes a region object by a name.
50 |
51 | It does not check if the region actually exists.
52 | """
53 | return Region(self, region_name)
54 |
55 | def get_by_name(self, region_name):
56 | """Get a region by its name.
57 |
58 | :param region_name: Name of the region to retrieve
59 | :raises ResourceNotFoundError: Region does not exist
60 | """
61 | regions = self.list()
62 | for region in regions:
63 | if region.name == region_name:
64 | return region
65 | raise ResourceNotFoundError(msg='Region %s does not exist'
66 | % region_name)
67 |
68 |
69 | class Region(Resource):
70 | """Represents one region."""
71 |
72 | def __init__(self, manager, name):
73 | self._manager = manager
74 | self.name = name
75 |
--------------------------------------------------------------------------------
/runabove/base.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | """RunAbove base class definition library."""
29 |
30 |
31 | class BaseManager(object):
32 | """Basic manager type providing common operations.
33 |
34 | Managers interact with a particular type ressource (instances,
35 | images, etc.) and provide CRUD operations for them.
36 | """
37 | def __init__(self, wrapper_api, runabove_handler):
38 | """Build a manager with reference to the API.
39 |
40 | :param wrapper_api: client of RunAbove API
41 | :param runabove_handler: reference to RunAbove user interface
42 | """
43 | self._api = wrapper_api
44 | self._handler = runabove_handler
45 |
46 | class BaseManagerWithList(BaseManager):
47 | """Manager with list and list_by_region methods."""
48 |
49 | def list(self):
50 | """Get a list of objects in an account."""
51 | objs = []
52 | for obj in self._api.get(self.basepath):
53 | objs.append(self._dict_to_obj(obj))
54 | return objs
55 |
56 | def list_by_region(self, region):
57 | """Get a list of objects in a region."""
58 | try:
59 | region_name = region.name
60 | except AttributeError:
61 | region_name = region
62 | content = {'region': region_name}
63 | objs = []
64 | for obj in self._api.get(self.basepath, content):
65 | objs.append(self._dict_to_obj(obj))
66 | return objs
67 |
68 |
69 |
70 | class Resource(object):
71 | """Base class for resource (obj, flavor, etc.)."""
72 |
73 |
--------------------------------------------------------------------------------
/runabove/flavor.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | """RunAbove flavor service library."""
29 |
30 | from base import Resource, BaseManagerWithList
31 | from .exception import ResourceNotFoundError
32 |
33 |
34 | class FlavorManager(BaseManagerWithList):
35 | """Manage flavors available in RunAbove."""
36 |
37 | basepath = '/flavor'
38 |
39 | def _dict_to_obj(self, flavor):
40 | """Converts a dict to a Flavor object."""
41 | region = self._handler.regions._name_to_obj(flavor['region'])
42 | return Flavor(self,
43 | flavor['id'],
44 | flavor.get('disk'),
45 | flavor.get('name'),
46 | flavor.get('ram'),
47 | flavor.get('vcpus'),
48 | region)
49 |
50 | def get_by_id(self, flavor_id):
51 | """Get a flavor by its id.
52 |
53 | :param flavor_id: ID of the flavor to retrieve
54 | :raises ResourceNotFoundError: Flavor does not exist
55 | """
56 | for flavor in self.list():
57 | if flavor.id == flavor_id:
58 | return flavor
59 | raise ResourceNotFoundError(msg='Flavor %s does not exist'
60 | % flavor_id)
61 |
62 |
63 | class Flavor(Resource):
64 | """Represents one flavor."""
65 |
66 | def __init__(self, manager, flavor_id, disk,
67 | name, ram, vcpus, region):
68 | self._manager = manager
69 | self.id = flavor_id
70 | self.disk = disk
71 | self.name = name
72 | self.ram = ram
73 | self.vcpus = vcpus
74 | self.region = region
75 |
--------------------------------------------------------------------------------
/runabove/tests/region.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | import runabove
29 | import unittest
30 | import mock
31 | import json
32 |
33 | class TestRegion(unittest.TestCase):
34 |
35 | answer_list = '["BHS-1", "SBG-1"]'
36 |
37 | @mock.patch('runabove.wrapper_api')
38 | @mock.patch('runabove.client')
39 | def setUp(self, mock_wrapper, mock_client):
40 | self.mock_wrapper = mock_wrapper
41 | self.regions = runabove.region.RegionManager(mock_wrapper, mock_client)
42 |
43 | def test_base_path(self):
44 | self.assertEquals(self.regions.basepath, '/region')
45 |
46 | def test_list(self):
47 | self.mock_wrapper.get.return_value = json.loads(self.answer_list)
48 | region_list = self.regions.list()
49 | self.mock_wrapper.get.assert_called_once_with(self.regions.basepath)
50 | self.assertIsInstance(region_list, list)
51 | self.assertTrue(len(region_list) > 0)
52 | for region in region_list:
53 | self.assertIsInstance(region, runabove.region.Region)
54 |
55 | def test_get_by_name(self):
56 | region_name = 'SBG-1'
57 | self.mock_wrapper.get.return_value = json.loads(self.answer_list)
58 | region = self.regions.get_by_name(region_name)
59 | self.mock_wrapper.get.assert_called_once_with(self.regions.basepath)
60 | self.assertIsInstance(region, runabove.region.Region)
61 | self.assertEquals(region.name, region_name)
62 |
63 | def test_get_by_name_404(self):
64 | with self.assertRaises(runabove.exception.ResourceNotFoundError):
65 | self.regions.get_by_name('RBX-404')
66 |
67 | if __name__ == '__main__':
68 | unittest.main()
69 |
--------------------------------------------------------------------------------
/doc/source/examples.txt:
--------------------------------------------------------------------------------
1 | To communicate with the API, each call made by your application must be signed
2 | and include the consumer key of the user. The signature process is
3 | automatically handled by the SDK. However if the user don't have a valid
4 | consumer key yet you can redirect him to RunAbove authentication page with the
5 | following code::
6 |
7 |
8 | from runabove import Runabove
9 |
10 | application_key = 'your_app_key'
11 | application_secret = 'your_app_secret'
12 |
13 | # Create an instance of Runabove SDK interface
14 | run = Runabove(application_key, application_secret)
15 |
16 | # Request an URL to securely authenticate the user
17 | print "You should login here: %s" % run.get_login_url()
18 | raw_input("When you are logged, press Enter")
19 |
20 | # Show the consumer key
21 | print "Your consumer key is: %s" % run.get_consumer_key()
22 |
23 | How to manage instances?
24 | ------------------------
25 |
26 | Launching an instance is easy. First get the flavor, image and region where you
27 | want your instance to be created and call `Runabove.instances.create()`. To
28 | delete an instance just call the `instance.delete()` method::
29 |
30 | from runabove import Runabove
31 |
32 | application_key = 'your_app_key'
33 | application_secret = 'your_app_secret'
34 | consumer_key = 'your_consumer_key'
35 |
36 | # Create the Runabove SDK interface
37 | run = Runabove(application_key,
38 | application_secret,
39 | consumer_key=consumer_key)
40 |
41 | # Get a region, flavor and image
42 | region = run.regions.list().pop()
43 | flavor = run.flavors.list_by_region(region).pop()
44 | image = run.images.list_by_region(region).pop()
45 |
46 | # Launch a new instance
47 | instance = run.instances.create(region, 'My instance', flavor, image)
48 |
49 | # List instances
50 | print 'Instances:'
51 | for i in run.instances.list():
52 | print ' - %s (%s)' % (i.name, i.image.name)
53 |
54 | # Delete the newly created instance
55 | instance.delete()
56 | print '%s deleted' % instance.name
57 |
58 | How to use storage?
59 | -------------------
60 | ::
61 |
62 | from runabove import Runabove
63 |
64 | application_key = 'your_app_key'
65 | application_secret = 'your_app_secret'
66 | consumer_key = 'your_consumer_key'
67 |
68 | # Create an instance of Runabove SDK interface
69 | run = Runabove(application_key,
70 | application_secret,
71 | consumer_key=consumer_key)
72 |
73 | # Get a region available
74 | region = run.regions.list().pop()
75 |
76 | # Create a new container
77 | container_name = 'storage_test'
78 | container = run.containers.create(region, container_name)
79 | print "Storage container '%s' created" % container.name
80 |
81 | # Create a new object
82 | object_name = 'object.txt'
83 | container.create_object(object_name, 'This is the content')
84 | print "Object '%s' created" % object_name
85 |
86 | # List objects of the container
87 | print "Objects in '%s':" % container.name
88 | for obj in container.list_objects():
89 | print " - %s (%d bytes)" % (obj.name, obj.size)
90 |
91 | # Delete the object
92 | obj.delete()
93 | print "Object '%s' deleted" % obj.name
94 |
95 | # Delete the container
96 | container.delete()
97 | print "Storage container '%s' deleted" % container.name
98 |
--------------------------------------------------------------------------------
/runabove/tests/flavor.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | import unittest
29 | import mock
30 | import json
31 |
32 | import runabove
33 |
34 | class TestFlavor(unittest.TestCase):
35 |
36 | answer_list = '''[
37 | {
38 | "id": "4245b91e-d9cf-4c9d-a109-f6a32da8a5cc",
39 | "disk": 240,
40 | "name": "pci2.d.r1",
41 | "ram": 28672,
42 | "vcpus": 4,
43 | "region": "BHS-1"
44 | },
45 | {
46 | "id": "ab35df0e-4632-48b2-b6a5-c1f1d922bd43",
47 | "disk": 240,
48 | "name": "pci2.d.c1",
49 | "ram": 16384,
50 | "vcpus": 6,
51 | "region": "BHS-1"
52 | }
53 | ]'''
54 |
55 | @mock.patch('runabove.wrapper_api')
56 | @mock.patch('runabove.client')
57 | def setUp(self, mock_wrapper, mock_client):
58 | self.mock_wrapper = mock_wrapper
59 | self.flavors = runabove.flavor.FlavorManager(mock_wrapper, mock_client)
60 |
61 | def test_base_path(self):
62 | self.assertEquals(self.flavors.basepath, '/flavor')
63 |
64 | def test_list(self):
65 | self.mock_wrapper.get.return_value = json.loads(self.answer_list)
66 | flavor_list = self.flavors.list()
67 | self.mock_wrapper.get.assert_called_once_with(self.flavors.basepath)
68 | for flavor in flavor_list:
69 | self.assertIsInstance(flavor, runabove.flavor.Flavor)
70 |
71 | def test_list_by_region(self):
72 | region_name = 'BHS-1'
73 | content = {'region': region_name}
74 | self.mock_wrapper.get.return_value = json.loads(self.answer_list)
75 | flavor_list = self.flavors.list_by_region(region_name)
76 | self.mock_wrapper.get.assert_called_once_with(
77 | self.flavors.basepath,
78 | content
79 | )
80 | for flavor in flavor_list:
81 | self.assertIsInstance(flavor, runabove.flavor.Flavor)
82 |
83 | def test_get_by_id(self):
84 | self.mock_wrapper.get.return_value = json.loads(self.answer_list)
85 | f = self.flavors.get_by_id('ab35df0e-4632-48b2-b6a5-c1f1d922bd43')
86 | self.assertIsInstance(f, runabove.flavor.Flavor)
87 |
88 | def test_get_by_id_404(self):
89 | with self.assertRaises(runabove.exception.ResourceNotFoundError):
90 | self.flavors.get_by_id('40404040-4040-4040-4040-404040404040')
91 |
92 | if __name__ == '__main__':
93 | unittest.main()
94 |
--------------------------------------------------------------------------------
/runabove/client.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | """RunAbove SDK interface for users."""
29 |
30 | from wrapper_api import WrapperApi
31 | from flavor import FlavorManager
32 | from region import RegionManager
33 | from ssh_key import SSHKeyManager
34 | from image import ImageManager
35 | from instance import InstanceManager
36 | from storage import ContainerManager
37 | from account import AccountManager
38 |
39 |
40 | class Runabove(object):
41 | """SDK interface to get cloud services from RunAbove."""
42 |
43 | access_rules = [
44 | {'method': 'GET', 'path': '/*'},
45 | {'method': 'POST', 'path': '/*'},
46 | {'method': 'PUT', 'path': '/*'},
47 | {'method': 'DELETE', 'path': '/*'}
48 | ]
49 |
50 | def __init__(self, application_key, application_secret, consumer_key=None):
51 | """Create the main interface of the SDK.
52 |
53 | :param application_key: key of your RunAbove api's application
54 | :param application_secret: password of your RunAbove api's application
55 | """
56 | self._api = WrapperApi(application_key,
57 | application_secret,
58 | consumer_key)
59 | self.flavors = FlavorManager(self._api, self)
60 | self.regions = RegionManager(self._api, self)
61 | self.ssh_keys = SSHKeyManager(self._api, self)
62 | self.images = ImageManager(self._api, self)
63 | self.instances = InstanceManager(self._api, self)
64 | self.account = AccountManager(self._api, self)
65 | self.containers = ContainerManager(self._api, self)
66 |
67 | def get_login_url(self, access_rules=None, redirect_url=None):
68 | """Get the URL to identify and login a customer.
69 |
70 | RunAbove API uses a remote connection to avoid storing passwords inside
71 | third party program. So the authentication is in two steps:
72 | First the app has to get a login URL and show it to the customer.
73 | Then, the customer must login with his account using this URL and the
74 | consumer key will be validated by the API.
75 |
76 | :param access_rules: List of access required by the application
77 | :param redirect_url: URL where user will be redirected after signin
78 | :raises ApiException: Error send by api
79 | """
80 | if isinstance(access_rules, list):
81 | self.access_rules = access_rules
82 | credentials = self._api.request_credentials(self.access_rules,
83 | redirect_url)
84 | return credentials['validationUrl']
85 |
86 | def get_consumer_key(self):
87 | """Get the current consumer key to communicate with the API."""
88 |
89 | return self._api.consumer_key
90 |
--------------------------------------------------------------------------------
/runabove/account.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | """RunAbove account service library."""
29 |
30 | from base import Resource, BaseManager
31 | from .exception import ResourceNotFoundError
32 |
33 |
34 | class AccountManager(BaseManager):
35 | """Manage the account attached to the user."""
36 |
37 | basepath = '/me'
38 |
39 | def get(self):
40 | """Get information about an account."""
41 | res = self._api.get(self.basepath)
42 | return self._dict_to_obj(res)
43 |
44 | def _load_balance(self):
45 | """Loads information about balance.
46 |
47 | Sums total of all projects, so no usage per project.
48 | """
49 | balance = self._api.get(self.basepath + '/balance')
50 | total_usage = 0
51 | for project in balance['currentUsages']:
52 | total_usage += project['currentTotal']
53 | return (total_usage, balance['creditLeft'])
54 |
55 | def _dict_to_obj(self, key):
56 | """Converts a dict to an Account object."""
57 | return Account(self,
58 | key.get('accountIdentifier'),
59 | key.get('firstname'),
60 | key.get('name'),
61 | key.get('address'),
62 | key.get('city'),
63 | key.get('postalCode'),
64 | key.get('area'),
65 | key.get('country'),
66 | key.get('email'),
67 | key.get('cellNumber'))
68 |
69 |
70 | class Account(Resource):
71 | """Represents one account."""
72 |
73 | def __init__(self, manager, account_id, first_name, last_name, address,
74 | city, postal_code, area, country, email, phone):
75 | self._manager = manager
76 | self.account_id = account_id
77 | self.first_name = first_name
78 | self.last_name = last_name
79 | self.address = address
80 | self.city = city
81 | self.postal_code = postal_code
82 | self.area = area
83 | self.country = country
84 | self.email = email
85 | self.phone = phone
86 | self._current_total = None
87 | self._credit_left = None
88 |
89 | @property
90 | def current_total(self):
91 | """Lazy loading of balance information."""
92 | if not self._current_total:
93 | self._current_total = self._manager._load_balance()[0]
94 | self._credit_left = self._manager._load_balance()[1]
95 | return self._current_total
96 |
97 | @property
98 | def credit_left(self):
99 | """Lazy loading of balance information."""
100 | if not self._credit_left:
101 | self._current_total = self._manager._load_balance()[0]
102 | self._credit_left = self._manager._load_balance()[1]
103 | return self._credit_left
104 |
--------------------------------------------------------------------------------
/runabove/ssh_key.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | """RunAbove SSH key service library."""
29 |
30 | from base import Resource, BaseManagerWithList
31 |
32 |
33 | class SSHKeyManager(BaseManagerWithList):
34 | """Manage the SSH keys attached to an account."""
35 |
36 | basepath = '/ssh'
37 |
38 | def get_by_name(self, region, name):
39 | """Get one SSH key from a RunAbove account.
40 |
41 | :param region: Region where the key is
42 | :param name: Name of the key to retrieve
43 | """
44 | try:
45 | region_name = region.name
46 | except AttributeError:
47 | region_name = region
48 | url = self.basepath + '/' + self._api.encode_for_api(name)
49 | key = self._api.get(url, {'region': region_name})
50 | return self._dict_to_obj(key)
51 |
52 | def _dict_to_obj(self, key):
53 | """Converts a dict to an SSHKey object."""
54 | region = self._handler.regions._name_to_obj(key['region'])
55 | return SSHKey(self,
56 | key['name'],
57 | key.get('fingerPrint'),
58 | key.get('publicKey'),
59 | region)
60 |
61 | def create(self, region, name, public_key):
62 | """Register a new SSH key in a RunAbove account.
63 |
64 | :param region: Region where the key will be added
65 | :param name: Name of the key
66 | :param public_key: Public key value
67 | """
68 | try:
69 | region_name = region.name
70 | except AttributeError:
71 | region_name = region
72 | content = {
73 | 'publicKey': public_key,
74 | 'region': region_name,
75 | 'name': name
76 | }
77 | self._api.post(self.basepath, content)
78 | return self.get_by_name(region_name, name)
79 |
80 | def delete(self, region, key):
81 | """Delete an SSH key from an account.
82 |
83 | :param region: Region where the key is
84 | :param key: SSH key to be deleted
85 | """
86 | try:
87 | region_name = region.name
88 | except AttributeError:
89 | region_name = region
90 | try:
91 | name = key.name
92 | except AttributeError:
93 | name = key
94 | url = self.basepath + '/' + self._api.encode_for_api(name)
95 | return self._api.delete(url, {'region': region_name})
96 |
97 |
98 | class SSHKey(Resource):
99 | """Represents one SSH key."""
100 |
101 | def __init__(self, manager, name, finger_print, public_key, region):
102 | self._manager = manager
103 | self.name = name
104 | self.finger_print = finger_print
105 | self.public_key = public_key
106 | self.region = region
107 |
108 | def delete(self):
109 | """Delete the key from the account."""
110 | self._manager.delete(self.region, self)
111 |
--------------------------------------------------------------------------------
/runabove/tests/image.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | import runabove
29 | import unittest
30 | import mock
31 | import json
32 |
33 |
34 | class TestImage(unittest.TestCase):
35 |
36 | answer_list = '''[
37 | {
38 | "id": "fedora",
39 | "name": "Fedora 20",
40 | "region": "BHS-1"
41 | },
42 | {
43 | "id": "centos",
44 | "name": "CentOS 6",
45 | "region": "BHS-1"
46 | }
47 | ]'''
48 |
49 | answer_one = '''{
50 | "id": "Pfdq813FxcFel78954aFEfcpaW21",
51 | "name": "ra-snapshot",
52 | "status": "active",
53 | "creationDate": "2014-04-15T12:10:05Z",
54 | "minDisk": 240,
55 | "minRam": 0,
56 | "region": "BHS-1"
57 | }'''
58 |
59 | @mock.patch('runabove.wrapper_api')
60 | @mock.patch('runabove.client')
61 | def setUp(self, mock_wrapper, mock_client):
62 | self.mock_wrapper = mock_wrapper
63 | self.mock_client = mock_client
64 | self.mock_client.regions = runabove.region.RegionManager(mock_wrapper,
65 | mock_client)
66 | self.images = runabove.image.ImageManager(mock_wrapper, mock_client)
67 |
68 | def test_base_path(self):
69 | self.assertEquals(self.images.basepath, '/image')
70 |
71 | def test_list(self):
72 | self.mock_wrapper.get.return_value = json.loads(self.answer_list)
73 | image_list = self.images.list()
74 | self.mock_wrapper.get.assert_called_once_with(self.images.basepath)
75 | self.assertIsInstance(image_list, list)
76 | self.assertEquals(len(image_list), 2)
77 | for image in image_list:
78 | self.assertIsInstance(image, runabove.image.Image)
79 |
80 | def test_list_by_region(self):
81 | region_name = 'BHS-1'
82 | self.mock_wrapper.get.return_value = json.loads(self.answer_list)
83 | image_list = self.images.list_by_region(region_name)
84 | self.mock_wrapper.get.assert_called_once_with(
85 | self.images.basepath,
86 | {'region': region_name}
87 | )
88 | self.assertIsInstance(image_list, list)
89 | self.assertEquals(len(image_list), 2)
90 | for image in image_list:
91 | self.assertIsInstance(image, runabove.image.Image)
92 | self.assertEquals(image.region.name, 'BHS-1')
93 |
94 | def test_find_by_image_id(self):
95 | the_id = "Pfdq813FxcFel78954aFEfcpaW21"
96 | self.mock_wrapper.get.return_value = json.loads(self.answer_one)
97 | image = self.images.get_by_id(the_id)
98 | self.mock_wrapper.get.assert_called_once_with(
99 | self.images.basepath + '/' +\
100 | self.images._api.encode_for_api(the_id)
101 | )
102 | self.assertIsInstance(image, runabove.image.Image)
103 | self.assertEquals(image.id, the_id)
104 |
105 | if __name__ == '__main__':
106 | unittest.main()
107 |
--------------------------------------------------------------------------------
/runabove/tests/account.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | #
3 | # Copyright (c) 2014, OVH
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | # SOFTWARE.
22 | #
23 | # Except as contained in this notice, the name of OVH and or its trademarks
24 | # (and among others RunAbove) shall not be used in advertising or otherwise to
25 | # promote the sale, use or other dealings in this Software without prior
26 | # written authorization from OVH.
27 |
28 | import unittest
29 | import mock
30 | import json
31 |
32 | import runabove
33 |
34 | class TestAccount(unittest.TestCase):
35 |
36 | answer_account = '''{
37 | "email": "test@runabove.com",
38 | "accountIdentifier": "test@runabove.com",
39 | "firstname": "Test",
40 | "country": "US",
41 | "city": null,
42 | "area": null,
43 | "cellNumber": "+99.999999999",
44 | "name": "Test",
45 | "address": null,
46 | "postalCode": null
47 | }'''
48 |
49 | answer_balance = '''{
50 | "currentUsages": [
51 | {
52 | "projectId": "randomlongstring",
53 | "currentTotal": 192.39
54 | },
55 | {
56 | "projectId": "randomlongstring2",
57 | "currentTotal": 1
58 | }
59 | ],
60 | "creditLeft": 200
61 | }'''
62 |
63 | @mock.patch('runabove.wrapper_api')
64 | def setUp(self, mock_wrapper):
65 | self.mock_wrapper = mock_wrapper
66 | self.account = runabove.account.AccountManager(mock_wrapper, None)
67 |
68 | def test_base_path(self):
69 | self.assertEquals(self.account.basepath, '/me')
70 |
71 | def test_account_existance(self):
72 | self.mock_wrapper.get.return_value = json.loads(self.answer_account)
73 | account = self.account.get()
74 | self.assertIsInstance(account, runabove.account.Account)
75 |
76 | def test_load_balance(self):
77 | self.mock_wrapper.get.return_value = json.loads(self.answer_balance)
78 | balance = self.account._load_balance()
79 | self.assertIsInstance(balance, tuple)
80 | self.assertTrue(len(balance) == 2)
81 | self.assertEquals(balance[0], 193.39)
82 | self.assertEquals(balance[1], 200)
83 |
84 |
85 | class TestAccountObject(unittest.TestCase):
86 |
87 | @mock.patch('runabove.account.AccountManager')
88 | def setUp(self, mock_accounts):
89 | self.mock_accounts = mock_accounts
90 | self.account = runabove.account.Account(
91 | self.mock_accounts,
92 | 'test@runabove.com',
93 | 'Test',
94 | 'Test',
95 | None,
96 | None,
97 | None,
98 | None,
99 | 'US',
100 | 'test@runabove.com',
101 | '+99.999999999'
102 | )
103 |
104 | def test_current_total(self):
105 | self.mock_accounts._load_balance.return_value = (193.39, 200)
106 | self.assertEquals(self.account.current_total, 193.39)
107 | self.mock_accounts._load_balance.assert_called_once()
108 |
109 | def test_credit_left(self):
110 | self.mock_accounts._load_balance.return_value = (193.39, 200)
111 | self.assertEquals(self.account.credit_left, 200)
112 | self.mock_accounts._load_balance.assert_called_once()
113 |
114 | if __name__ == '__main__':
115 | unittest.main()
116 |
--------------------------------------------------------------------------------
/examples/facecat/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Facecat
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
19 |
20 |
21 |
22 |