diff --git a/doc/source/index.rst b/doc/source/index.rst index f1ac436..f4e23c5 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -14,7 +14,7 @@ Contents: readme installation - usage + usage/index v1-api-documentation authentication-documentation contributing diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 5d2d299..887ebac 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -2,6 +2,15 @@ Installation ============== +.. note:: + + There has not been a stable release of python-cratonclient to PyPI yet. To + install the current version in development use: + + .. code:: + + pip install git+https://git.openstack.org/openstack/python-cratonclient + At the command line:: $ pip install python-cratonclient diff --git a/doc/source/usage.rst b/doc/source/usage/authentication.rst similarity index 53% rename from doc/source/usage.rst rename to doc/source/usage/authentication.rst index 2708cfa..dbbfeaa 100644 --- a/doc/source/usage.rst +++ b/doc/source/usage/authentication.rst @@ -1,24 +1,8 @@ -======================= - Python API User Guide -======================= +.. _usage-auth: -Once you have installed ``python-cratonclient``, there are a few things you -need to get started using the Python API: - -#. You need to know how to authenticate to the Craton API you wish to talk to - - Some Craton API services will be deployed using Craton's in-built - authentication system while others may use Keystone. - -#. You need your credentials - -#. You need the location of your Craton API service - -Let's cover authentication first: - - -Authenticating to Craton -======================== +========================== + Authenticating to Craton +========================== There are two ways to authenticate to Craton: @@ -71,9 +55,6 @@ Then, we need to do the following: .. code-block:: python - from keystoneauth1.identity.v3 import password as password_auth - from keystoneauth1 import session as ksa_session - from cratonclient import auth from cratonclient.v1 import client @@ -89,46 +70,3 @@ Then, we need to do the following: session=craton_session, url=URL, ) - - -Communicating with Craton -========================= - -Now that you've configured your authentication method, you can interact with -your ``craton`` object like so: - -.. code-block:: python - - for region in craton.regions.list(): - print('Region {} contains:'.format(region.name)) - for host in craton.hosts.list(region_id=region.id): - print(' {}'.format(host.name)) - - -The Craton API has the following resources: - -- Cells - -- Hosts - -- Network Devices - -- Network Interfaces - -- Networks - -- Projects - -- Regions - -- Users - -Of these: - -- Cells - -- Hosts - -- Regions - -Are implemented. diff --git a/doc/source/usage/cells.rst b/doc/source/usage/cells.rst new file mode 100644 index 0000000..d602640 --- /dev/null +++ b/doc/source/usage/cells.rst @@ -0,0 +1,136 @@ +===================== + Using the Cells API +===================== + +Here we will assume that we already have a +:class:`~cratonclient.client.Client` instance configured with the appropriate +authentication method (as demonstrated in :ref:`usage-auth`). + +Listing Cells +------------- + +The Cells API implements pagination. This means that by default, it does not +return all cells known to Craton. To ignore page limits and offsets, we can +allow cratonclient to do handle pagination for us: + +.. code-block:: python + + for cell in craton.cells.list(): + print_cell_info(cell) + +By default :meth:`~cratonclient.v1.cells.CellsManager.list` will handle +pagination for you. If, instead, you want to handle it yourself you will want +to do something akin to: + +.. code-block:: python + + first_page_of_cells = list(craton.cells.list(autopaginate=False)) + marker_id = first_page_of_cells[-1].id + second_page_of_cells = list(craton.cells.list( + autopaginate=False, + marker=marker_id, + )) + marker_id = second_page_of_cells[-1].id + third_page_of_cells = list(craton.cells.list( + autopaginate=False, + marker=marker_id, + )) + # etc. + +A more realistic example, however, might look like this: + +.. code-block:: python + + cells_list = None + marker = None + while cells_list and cells_list is not None: + cells_list = list(craton.cells.list( + marker=marker, + autopaginate=False, + )) + # do something with cells_list + if cells_list: + marker = cells_list[-1].id + +This will have the effect of stopping the while loop when you eventually +receive an empty list from ``craton.cells.list(...)``. + +Creating Cells +-------------- + +Cells live below a Region in Craton. To create a cell, the only required items +are a ``name`` for the cell, a cloud ID, and a region ID. The name must be +unique among cells in the same project. + +.. code-block:: python + + cell = craton.cells.create( + name='my-cell-0', + cloud_id=cloud_id, + region_id=region_id, + note='This is my cell, there are many like it, but this is mine.', + variables={ + 'some-var': 'some-var-value', + }, + ) + +Retrieving a Specific Cell +-------------------------- + +Cells can be retrieved by id. + +.. code-block:: python + + cell = craton.cells.get(1) + +Using a Cell's Variables +------------------------- + +Once we have a cell we can introspect its variables like so: + +.. code-block:: python + + cell = craton.cells.get(cell_id) + cell_vars = cell.variables.get() + +To update them: + +.. code-block:: python + + updated_vars = { + 'var-a': 'new-var-a', + 'var-b': 'new-var-b', + 'updated-var': 'updated value', + } + cell.variables.update(**updated_vars) + +To delete them: + +.. code-block:: python + + cell.variables.delete('var-a', 'var-b', 'updated-var') + +Updating a Cell +--------------- + +We can update a cell's attributes (but not its variables) like so: + +.. code-block:: python + + craton.cells.update( + cell_id, + name='new name', + note='Updated note.', + ) + +Most attributes that you can specify on creation can also be specified for +updating the cell as well. + +Deleting a Cell +--------------- + +We can delete with only its id: + +.. code-block:: python + + craton.cells.delete(cell_id) diff --git a/doc/source/usage/clouds.rst b/doc/source/usage/clouds.rst new file mode 100644 index 0000000..f614b20 --- /dev/null +++ b/doc/source/usage/clouds.rst @@ -0,0 +1,134 @@ +====================== + Using the Clouds API +====================== + +Here we will assume that we already have a +:class:`~cratonclient.client.Client` instance configured with the appropriate +authentication method (as demonstrated in :ref:`usage-auth`). + +Listing Clouds +-------------- + +The Clouds API implements pagination. This means that by default, it does not +return all clouds known to Craton. To ignore page limits and offsets, we can +allow cratonclient to do handle pagination for us: + +.. code-block:: python + + for cloud in craton.clouds.list(): + print_cloud_info(cloud) + +By default :meth:`~cratonclient.v1.clouds.CloudsManager.list` will handle +pagination for you. If, instead, you want to handle it yourself you will want +to do something akin to: + +.. code-block:: python + + first_page_of_clouds = list(craton.clouds.list(autopaginate=False)) + marker_id = first_page_of_clouds[-1].id + second_page_of_clouds = list(craton.clouds.list( + autopaginate=False, + marker=marker_id, + )) + marker_id = second_page_of_clouds[-1].id + third_page_of_clouds = list(craton.clouds.list( + autopaginate=False, + marker=marker_id, + )) + # etc. + +A more realistic example, however, might look like this: + +.. code-block:: python + + clouds_list = None + marker = None + while clouds_list and clouds_list is not None: + clouds_list = list(craton.clouds.list( + marker=marker, + autopaginate=False, + )) + # do something with clouds_list + if clouds_list: + marker = clouds_list[-1].id + +This will have the effect of stopping the while loop when you eventually +receive an empty list from ``craton.clouds.list(...)``. + +Creating Clouds +--------------- + +Clouds are the top-level item in Craton. To create a cloud, the only required +item is a ``name`` for the cloud. This must be unique among clouds in the same +project. + +.. code-block:: python + + cloud = craton.clouds.create( + name='my-cloud-0', + note='This is my cloud, there are many like it, but this is mine.', + variables={ + 'some-var': 'some-var-value', + }, + ) + +Retrieving a Specific Cloud +--------------------------- + +Clouds can be retrieved by id. + +.. code-block:: python + + cloud = craton.clouds.get(1) + +Using a Cloud's Variables +------------------------- + +Once we have a cloud we can introspect its variables like so: + +.. code-block:: python + + cloud = craton.clouds.get(cloud_id) + cloud_vars = cloud.variables.get() + +To update them: + +.. code-block:: python + + updated_vars = { + 'var-a': 'new-var-a', + 'var-b': 'new-var-b', + 'updated-var': 'updated value', + } + cloud.variables.update(**updated_vars) + +To delete them: + +.. code-block:: python + + cloud.variables.delete('var-a', 'var-b', 'updated-var') + +Updating a Cloud +---------------- + +We can update a cloud's attributes (but not its variables) like so: + +.. code-block:: python + + craton.clouds.update( + cloud_id, + name='new name', + note='Updated note.', + ) + +Most attributes that you can specify on creation can also be specified for +updating the cloud as well. + +Deleting a Cloud +---------------- + +We can delete with only its id: + +.. code-block:: python + + craton.clouds.delete(cloud_id) diff --git a/doc/source/usage/devices.rst b/doc/source/usage/devices.rst new file mode 100644 index 0000000..7010aba --- /dev/null +++ b/doc/source/usage/devices.rst @@ -0,0 +1,32 @@ +======================= + Using the Devices API +======================= + +Here we will assume that we already have a +:class:`~cratonclient.client.Client` instance configured with the appropriate +authentication method (as demonstrated in :ref:`usage-auth`). + +.. note:: + + The Devices API is quite unlike other API endpoints presently in craton. + At the moment, it returns both hosts and network devices. It concatenates + the two lists in an indeterminate manner. On one invocation, you may + receive the hosts first and then the network devices, on another you may + receive them in the alternate order. If more items are returned in these + listings, then the number of different orderings will only increase + factorially. + +Listing Devices +--------------- + +The Devices API implements pagination. This means that by default, it does not +return all devices known to Craton. To ignore page limits and offsets, we can +allow cratonclient to do handle pagination for us: + +.. code-block:: python + + for device in craton.devices.list(): + print_device_info(device) + +By default :meth:`~cratonclient.v1.devices.DevicesManager.list` will handle +pagination for you. diff --git a/doc/source/usage/hosts.rst b/doc/source/usage/hosts.rst new file mode 100644 index 0000000..304f79d --- /dev/null +++ b/doc/source/usage/hosts.rst @@ -0,0 +1,148 @@ +===================== + Using the Hosts API +===================== + +Here we will assume that we already have a +:class:`~cratonclient.client.Client` instance configured with the appropriate +authentication method (as demonstrated in :ref:`usage-auth`). + +Listing Hosts +------------- + +The Hosts API implements pagination. This means that by default, it does not +return all hosts known to Craton. To ignore page limits and offsets, we can +allow cratonclient to do handle pagination for us: + +.. code-block:: python + + for host in craton.hosts.list(): + print_host_info(host) + +By default :meth:`~cratonclient.v1.hosts.HostsManager.list` will handle +pagination for you. If, instead, you want to handle it yourself you will want +to do something akin to: + +.. code-block:: python + + first_page_of_hosts = list(craton.hosts.list(autopaginate=False)) + marker_id = first_page_of_hosts[-1].id + second_page_of_hosts = list(craton.hosts.list( + autopaginate=False, + marker=marker_id, + )) + marker_id = second_page_of_hosts[-1].id + third_page_of_hosts = list(craton.hosts.list( + autopaginate=False, + marker=marker_id, + )) + # etc. + +A more realistic example, however, might look like this: + +.. code-block:: python + + hosts_list = None + marker = None + while hosts_list and hosts_list is not None: + hosts_list = list(craton.hosts.list( + marker=marker, + autopaginate=False, + )) + # do something with hosts_list + if hosts_list: + marker = hosts_list[-1].id + +This will have the effect of stopping the while loop when you eventually +receive an empty list from ``craton.hosts.list(...)``. + +Creating Hosts +-------------- + +Hosts live inside either a Region or Cell in Craton. To create a host, one +needs: + +- A unique name + +- A unique IP address + +- A "device type" (this is freeform), e.g., "server", "container", "nova-vm", + etc. + +- A cloud ID + +- A region ID + +.. code-block:: python + + host = craton.hosts.create( + name='my-host-0', + ip_address='127.0.1.0', + device_type='server', + cloud_id=cloud_id, + region_id=region_id, + note='This is my host, there are many like it, but this is mine.', + variables={ + 'some-var': 'some-var-value', + }, + ) + +Retrieving a Specific Host +-------------------------- + +Hosts can be retrieved by id. + +.. code-block:: python + + host = craton.hosts.get(1) + +Using a Host's Variables +------------------------ + +Once we have a host we can introspect its variables like so: + +.. code-block:: python + + host = craton.hosts.get(host_id) + host_vars = host.variables.get() + +To update them: + +.. code-block:: python + + updated_vars = { + 'var-a': 'new-var-a', + 'var-b': 'new-var-b', + 'updated-var': 'updated value', + } + host.variables.update(**updated_vars) + +To delete them: + +.. code-block:: python + + host.variables.delete('var-a', 'var-b', 'updated-var') + +Updating a Host +--------------- + +We can update a host's attributes (but not its variables) like so: + +.. code-block:: python + + craton.hosts.update( + host_id, + name='new name', + note='Updated note.', + ) + +Most attributes that you can specify on creation can also be specified for +updating the host as well. + +Deleting a Host +--------------- + +We can delete with only its id: + +.. code-block:: python + + craton.hosts.delete(host_id) diff --git a/doc/source/usage/index.rst b/doc/source/usage/index.rst new file mode 100644 index 0000000..918e023 --- /dev/null +++ b/doc/source/usage/index.rst @@ -0,0 +1,30 @@ +======================= + Python API User Guide +======================= + +Once you have installed ``python-cratonclient``, there are a few things you +need to get started using the Python API: + +#. You need to know how to authenticate to the Craton API you wish to talk to + + Some Craton API services will be deployed using Craton's in-built + authentication system while others may use Keystone. + +#. You need your credentials + +#. You need the location of your Craton API service + +This chapter of python-cratonclient's documentation is broken down into +chapters: + +.. toctree:: + :maxdepth: 2 + + authentication + supported-apis + clouds + regions + cells + hosts + devices + projects diff --git a/doc/source/usage/projects.rst b/doc/source/usage/projects.rst new file mode 100644 index 0000000..7de0a14 --- /dev/null +++ b/doc/source/usage/projects.rst @@ -0,0 +1,135 @@ +======================== + Using the Projects API +======================== + +Here we will assume that we already have a +:class:`~cratonclient.client.Client` instance configured with the appropriate +authentication method (as demonstrated in :ref:`usage-auth`). + +Listing Projects +---------------- + +The Projects API implements pagination. This means that by default, it does not +return all projects known to Craton. To ignore page limits and offsets, we can +allow cratonclient to do handle pagination for us: + +.. code-block:: python + + for project in craton.projects.list(): + print_project_info(project) + +By default :meth:`~cratonclient.v1.projects.ProjectsManager.list` will handle +pagination for you. If, instead, you want to handle it yourself you will want +to do something akin to: + +.. code-block:: python + + first_page_of_projects = list(craton.projects.list(autopaginate=False)) + marker_id = first_page_of_projects[-1].id + second_page_of_projects = list(craton.projects.list( + autopaginate=False, + marker=marker_id, + )) + marker_id = second_page_of_projects[-1].id + third_page_of_projects = list(craton.projects.list( + autopaginate=False, + marker=marker_id, + )) + # etc. + +A more realistic example, however, might look like this: + +.. code-block:: python + + projects_list = None + marker = None + while projects_list and projects_list is not None: + projects_list = list(craton.projects.list( + marker=marker, + autopaginate=False, + )) + # do something with projects_list + if projects_list: + marker = projects_list[-1].id + +This will have the effect of stopping the while loop when you eventually +receive an empty list from ``craton.projects.list(...)``. + +Creating Projects +----------------- + +Projects are top-level items in Craton. To create a project, one needs: + +- A unique name + +- Permission to create new projects + + +.. code-block:: python + + project = craton.projects.create( + name='my-project-0', + variables={ + 'some-var': 'some-var-value', + }, + ) + +Retrieving a Specific Project +----------------------------- + +Projects can be retrieved by id. + +.. code-block:: python + + project = craton.projects.get(1) + +Using a Project's Variables +--------------------------- + +Once we have a project we can introspect its variables like so: + +.. code-block:: python + + project = craton.projects.get(project_id) + project_vars = project.variables.get() + +To update them: + +.. code-block:: python + + updated_vars = { + 'var-a': 'new-var-a', + 'var-b': 'new-var-b', + 'updated-var': 'updated value', + } + project.variables.update(**updated_vars) + +To delete them: + +.. code-block:: python + + project.variables.delete('var-a', 'var-b', 'updated-var') + +Updating a Project +------------------ + +We can update a project's attributes (but not its variables) like so: + +.. code-block:: python + + craton.projects.update( + project_id, + name='new name', + ) + +Most attributes that you can specify on creation can also be specified for +updating the project as well. + +Deleting a Project +------------------ + +We can delete with only its id: + +.. code-block:: python + + craton.projects.delete(project_id) diff --git a/doc/source/usage/regions.rst b/doc/source/usage/regions.rst new file mode 100644 index 0000000..2f487b8 --- /dev/null +++ b/doc/source/usage/regions.rst @@ -0,0 +1,135 @@ +======================= + Using the Regions API +======================= + +Here we will assume that we already have a +:class:`~cratonclient.client.Client` instance configured with the appropriate +authentication method (as demonstrated in :ref:`usage-auth`). + +Listing Regions +--------------- + +The Regions API implements pagination. This means that by default, it does not +return all regions known to Craton. To ignore page limits and offsets, we can +allow cratonclient to do handle pagination for us: + +.. code-block:: python + + for region in craton.regions.list(): + print_region_info(region) + +By default :meth:`~cratonclient.v1.regions.RegionsManager.list` will handle +pagination for you. If, instead, you want to handle it yourself you will want +to do something akin to: + +.. code-block:: python + + first_page_of_regions = list(craton.regions.list(autopaginate=False)) + marker_id = first_page_of_regions[-1].id + second_page_of_regions = list(craton.regions.list( + autopaginate=False, + marker=marker_id, + )) + marker_id = second_page_of_regions[-1].id + third_page_of_regions = list(craton.regions.list( + autopaginate=False, + marker=marker_id, + )) + # etc. + +A more realistic example, however, might look like this: + +.. code-block:: python + + regions_list = None + marker = None + while regions_list and regions_list is not None: + regions_list = list(craton.regions.list( + marker=marker, + autopaginate=False, + )) + # do something with regions_list + if regions_list: + marker = regions_list[-1].id + +This will have the effect of stopping the while loop when you eventually +receive an empty list from ``craton.regions.list(...)``. + +Creating Regions +---------------- + +Regions are required to be part of a Cloud in Craton. To create a region, the +only required items are a ``name`` for the region and the ID of the cloud it +belongs to. The name must be unique among regions in the same project. + +.. code-block:: python + + region = craton.regions.create( + name='my-region-0', + cloud_id=cloud_id, + note='This is my region, there are many like it, but this is mine.', + variables={ + 'some-var': 'some-var-value', + }, + ) + +Retrieving a Specific Region +---------------------------- + +Regions can be retrieved by id. + +.. code-block:: python + + region = craton.regions.get(1) + +Using a Region's Variables +-------------------------- + +Once we have a region we can introspect its variables like so: + +.. code-block:: python + + region = craton.regions.get(region_id) + region_vars = region.variables.get() + +To update them: + +.. code-block:: python + + updated_vars = { + 'var-a': 'new-var-a', + 'var-b': 'new-var-b', + 'updated-var': 'updated value', + } + region.variables.update(**updated_vars) + +To delete them: + +.. code-block:: python + + region.variables.delete('var-a', 'var-b', 'updated-var') + +Updating a Region +----------------- + +We can update a region's attributes (but not its variables) like so: + +.. code-block:: python + + craton.regions.update( + region_id, + name='new name', + note='Updated note.', + ) + +Most attributes that you can specify on creation can also be specified for +updating the region as well. + +Deleting a Region +----------------- + +We can delete with only its id: + +.. code-block:: python + + craton.regions.delete(region_id) diff --git a/doc/source/usage/supported-apis.rst b/doc/source/usage/supported-apis.rst new file mode 100644 index 0000000..eb19f19 --- /dev/null +++ b/doc/source/usage/supported-apis.rst @@ -0,0 +1,50 @@ +=========================== + Communicating with Craton +=========================== + +Now that you've configured your authentication method, you can interact with +your ``craton`` object like so: + +.. code-block:: python + + for region in craton.regions.list(): + print('Region {} contains:'.format(region.name)) + for host in craton.hosts.list(region_id=region.id): + print(' {}'.format(host.name)) + + +The Craton API has the following resources: + +- Cells + +- Clouds + +- Devices + +- Hosts + +- Network Devices + +- Network Interfaces + +- Networks + +- Projects + +- Regions + +- Users + +Of these: + +- Cells + +- Clouds + +- Hosts + +- Projects + +- Regions + +Are implemented.