diff --git a/.gitignore b/.gitignore index e1f6c47..463eb71 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.pyc +.tox/* +build/* +html/* python_reddwarfclient.egg* diff --git a/README.rst b/README.rst index 9feeab5..929143b 100644 --- a/README.rst +++ b/README.rst @@ -5,15 +5,17 @@ This is a client for the Reddwarf API. There's a Python API (the ``reddwarfclient`` module), and a command-line script (``reddwarf``). Each implements 100% (or less ;) ) of the Reddwarf API. -.. contents:: Contents: - :local: - Command-line API ---------------- -TODO: Add docs +To use the command line API, first log in using your user name, api key, +tenant, and appropriate auth url. -Python API ----------- +.. code-block:: bash + + $ reddwarf-cli --username=jsmith --apikey=abcdefg --tenant=12345 --auth_url=http://reddwarf_auth:35357/v2.0/tokens auth login + +At this point you will be authenticated and given a token, which is stored +at ~/.apitoken. From there you can make other calls to the CLI. TODO: Add docs diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..0ad4db2 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +import sys, os + +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage'] + +templates_path = ['_templates'] + +source_suffix = '.rst' + +master_doc = 'index' + +project = u'python-reddwarfclient' +copyright = u'2012, OpenStack' + +version = '1.0' +release = '1.0' +exclude_trees = [] + +pygments_style = 'sphinx' + +html_theme = 'default' +html_static_path = ['_static'] +htmlhelp_basename = 'python-reddwarfclientdoc' +latex_documents = [ + ('index', 'python-reddwarfclient.tex', u'python-reddwarfclient Documentation', + u'OpenStack', 'manual'), +] + diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..0ad4db2 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +import sys, os + +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage'] + +templates_path = ['_templates'] + +source_suffix = '.rst' + +master_doc = 'index' + +project = u'python-reddwarfclient' +copyright = u'2012, OpenStack' + +version = '1.0' +release = '1.0' +exclude_trees = [] + +pygments_style = 'sphinx' + +html_theme = 'default' +html_static_path = ['_static'] +htmlhelp_basename = 'python-reddwarfclientdoc' +latex_documents = [ + ('index', 'python-reddwarfclient.tex', u'python-reddwarfclient Documentation', + u'OpenStack', 'manual'), +] + diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..454f8ab --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,31 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + +.. include:: ../../README.rst + :start-line: 0 + :end-line: 22 + + +.. contents:: Contents + :local: + +.. include:: ./usage.rst + +.. include:: ./pydocs.rst + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/source/pydocs.rst b/docs/source/pydocs.rst new file mode 100644 index 0000000..eddda86 --- /dev/null +++ b/docs/source/pydocs.rst @@ -0,0 +1,16 @@ +PyDocs +================= + +reddwarfclient +-------------- + + +.. automodule:: reddwarfclient + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: reddwarfclient.client.Dbaas + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/usage.rst b/docs/source/usage.rst new file mode 100644 index 0000000..caaa12a --- /dev/null +++ b/docs/source/usage.rst @@ -0,0 +1,209 @@ +Using the Client Programmatically +================================= + + +.. testsetup:: + + # Creates some vars we don't show in the docs. + AUTH_URL="http://localhost:8779/v1.0/auth" + + from reddwarfclient import Dbaas + from reddwarfclient import auth + class FakeAuth(auth.Authenticator): + + def authenticate(self): + class FakeCatalog(object): + def __init__(self, auth): + self.auth = auth + + def get_public_url(self): + return "%s/%s" % ('http://localhost:8779/v1.0', + self.auth.tenant) + + def get_token(self): + return self.auth.tenant + + return FakeCatalog(self) + + from reddwarfclient import Dbaas + OLD_INIT = Dbaas.__init__ + def new_init(*args, **kwargs): + kwargs['auth_strategy'] = FakeAuth + OLD_INIT(*args, **kwargs) + + # Monkey patch init so it'll work with fake auth. + Dbaas.__init__ = new_init + + + client = Dbaas("jsmith", "abcdef", tenant="12345", + auth_url=AUTH_URL) + client.authenticate() + + # Delete all instances. + instances = [1] + while len(instances) > 0: + instances = client.instances.list() + for instance in instances: + try: + instance.delete() + except: + pass + + flavor_id = "1" + for i in range(30): + name = "Instance #%d" % i + client.instances.create(name, flavor_id, None) + + + +Authentication +-------------- + +Authenticating is necessary to use every feature of the client (except to +discover available versions). + +To create the client, create an instance of the Dbaas (Database as a Service) +class. The auth url, auth user, key, and tenant ID must be specified in the +call to the constructor. + +.. testcode:: + + from reddwarfclient import Dbaas + global AUTH_URL + + client = Dbaas("jsmith", "abcdef", tenant="12345", + auth_url=AUTH_URL) + client.authenticate() + +The default authentication strategy assumes a Keystone complaint auth system. +For Rackspace auth, use the keyword argument "auth_strategy='rax'". + + +Versions +-------- + +You can discover the available versions by querying the versions property as +follows: + + +.. testcode:: + + versions = client.versions.index("http://localhost:8779") + + +The "index" method returns a list of Version objects which have the ID as well +as a list of links, each with a URL to use to reach that particular version. + +.. testcode:: + + for version in versions: + print(version.id) + for link in version.links: + if link['rel'] == 'self': + print(" %s" % link['href']) + +.. testoutput:: + + v1.0 + http://localhost:8779/v1.0 + + +Instances +--------- + +The following example creates a 512 MB instance with a 1 GB volume: + +.. testcode:: + + client.authenticate() + flavor_id = "1" + volume = {'size':1} + databases = [{"name": "my_db", + "character_set": "latin2", # This and the next field are + "collate": "latin2_general_ci"}] # optional. + users = [{"name": "jsmith", "password": "12345", + "databases": [{"name": "my_db"}] + }] + instance = client.instances.create("My Instance", flavor_id, volume, + databases, users) + +To retrieve the instance, use the "get" method of "instances": + +.. testcode:: + + updated_instance = client.instances.get(instance.id) + print(updated_instance.name) + print(" Status=%s Flavor=%s" % + (updated_instance.status, updated_instance.flavor['id'])) + +.. testoutput:: + + My Instance + Status=BUILD Flavor=1 + +You can delete an instance by calling "delete" on the instance object itself, +or by using the delete method on "instances." + +.. testcode:: + + # Wait for the instance to be ready before we delete it. + import time + from reddwarfclient.exceptions import NotFound + + while instance.status == "BUILD": + instance.get() + time.sleep(1) + print("Ready in an %s state." % instance.status) + instance.delete() + # Delete and wait for the instance to go away. + while True: + try: + instance = client.instances.get(instance.id) + assert instance.status == "SHUTDOWN" + except NotFound: + break + +.. testoutput:: + + Ready in an ACTIVE state. + + +Listing instances and Pagination +-------------------------------- + +To list all instances, use the list method of "instances": + +.. testcode:: + + instances = client.instances.list() + + +Lists paginate after twenty items, meaning you'll only get twenty items back +even if there are more. To see the next set of items, send a marker. The marker +is a key value (in the case of instances, the ID) which is the non-inclusive +starting point for all returned items. + +The lists returned by the client always include a "next" property. This +can be used as the "marker" argument to get the next section of the list +back from the server. If no more items are available, then the next property +is None. + +.. testcode:: + + # There are currently 30 instances. + + instances = client.instances.list() + print(len(instances)) + print(instances.next is None) + + instances2 = client.instances.list(marker=instances.next) + print(len(instances2)) + print(instances2.next is None) + +.. testoutput:: + + 20 + False + 10 + True + diff --git a/setup.py b/setup.py index 1b0fff1..47752c8 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ setuptools.setup( version="2012.3", author="Rackspace", description="Rich client bindings for Reddwarf REST API.", - long_description=read_file("README.rst"), + long_description="""Rich client bindings for Reddwarf REST API.""", license="Apache License, Version 2.0", url="https://github.com/openstack/python-reddwarfclient", packages=["reddwarfclient"], diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..fa600d3 --- /dev/null +++ b/tox.ini @@ -0,0 +1,13 @@ +# Python Reddwarf Client + +[tox] +envlist = py26, docs + +[testenv:docs] +deps = + coverage + httplib2 + sphinx +commands = + sphinx-build -b doctest {toxinidir}/docs/source {envtmpdir}/html + sphinx-build -b html {toxinidir}/docs/source {envtmpdir}/html