Documentation and Api Reference

* API Reference and documentation as two seperate sphinx
  document sets
* Information from the Devstack guide and README moved over
  to the new documentation
* Configuration examples
* Examples of building plugins
* Both use the new sphinx-rtd-theme

Change-Id: If347905aa14b77b5943f1a9de97f6e287b98ce95
This commit is contained in:
Amelia Cordwell 2017-08-28 16:59:48 +12:00 committed by Adrian Turjak
parent 1e891daf21
commit 6cbf3fa7f7
23 changed files with 2245 additions and 570 deletions

View File

@ -1,198 +0,0 @@
# Deploying Adjutant in Devstack
This is a guide to setting up Adjutant in a running Devstack environment close to how we have been running it for development purposes.
This guide assumes you are running this in a clean ubuntu 16.04 virtual machine with sudo access.
## Deploy Devstack
Grab the Devstack repo.
```
git clone https://github.com/openstack-dev/devstack.git
```
And then define a basic localrc file with the password set and place that in the devstack folder (adjutant's default conf assumes 'openstack' as the admin password):
```
ADMIN_PASSWORD=openstack
MYSQL_PASSWORD=openstack
DATABASE_PASSWORD=openstack
RABBIT_PASSWORD=openstack
SERVICE_PASSWORD=openstack
```
Run the devstack build:
```
./devstack/stack.sh
```
Provided your VM has enough ram to handle a devstack install this should take a while, but go smoothly. Ideally give your VM 5gb or more of ram, any less can cause the devstack build to fail.
## Deploy Adjutant
Grab the Adjutant repo.
```
git clone https://github.com/catalyst/adjutant.git
```
Then you'll want to setup a virtual environment.
```
cd adjutant
virtualenv venv
source venv/bin/activate
```
Once that is done you can install Adjutant and its requirements:
```
pip install -r requirements.txt
python setup.py develop
```
If you prefer you can install it fully, but using develop instead allows you update the Adjutant code and have the service reflect that without rerunning the install.
## Configure Adjutant
Most of the default conf values should work fine against devstack, but one thing that you will need to change is the uuid for the public network in DEFAULT_ACTION_SETTINGS for the actions NewDefaultNetworkAction and NewProjectDefaultNetworkAction. If you don't set this correctly, then signups or tasks using those actions will not be able to correctly create a default network as they cannot find the correct external public network.
On a fresh devstack there is only one public network so to find the public network uuid you can to run:
```
openstack network show public
```
And then grab the id value and put that into the Adjutant conf.
### Username is email
The example conf for Adjutant is setup with `USERNAME_IS_EMAIL = TRUE` which works on the assumption that usernames are emails. This is easy to change in the conf, but a fairly useful way of avoiding username clashes. If you set this to `False` then usernames will be required as well as emails for most tasks that deal with user creation.
Migrating between the two states hasn't yet been handled entirely, so once you pick a valuve for `USERNAME_IS_EMAIL` stick with it, or clear the database inbetween.
## Running Adjutant
Still in the Adjutant repo directory, you will now need to run the migrations to build a basic database. By default this will use sqlite3, and for testing and development this is ideal and easier.
```
adjutant-api migrate
```
Now the that the migrations have been setup and the database built run the service from the same directory, and it will revert to using the config file at 'conf/conf.yaml':
```
adjutant-api runserver 0.0.0.0:5050
```
Note: The port doesn't matter, but 5050 is a safe bet as it isn't used by any other DevStack services and we can then safely assume you will be using the same url for the rest of the guide.
Now you have Adjutant running, keep this window open as you'll want to keep an eye on the console output.
## Add Adjutant to Keystone Catalogue
In a new SSH termimal connected to your ubuntu VM setup your credentials as environment variables:
```
export OS_USERNAME=admin
export OS_PASSWORD=openstack
export OS_PROJECT_NAME=demo
export OS_USER_DOMAIN_NAME=default
export OS_PROJECT_DOMAIN_NAME=default
export OS_AUTH_URL=http://localhost/identity
export OS_IDENTITY_API_VERSION=3
export OS_REGION_NAME=RegionOne
```
If you used the localrc file as given above, these should work.
Now setup a new service in Keystone for Adjutant and add an endpoint for it:
```
openstack service create registration --name adjutant
openstack endpoint create adjutant public http://0.0.0.0:5050/v1 --region RegionOne
```
## Adjutant specific roles
To allow certain actions, Adjutant requires two special roles to exist. You can create them as such:
```
openstack role create project_admin
openstack role create project_mod
```
Also because Adjutant by default also adds the role, you will want to create 'heat_stack_owner' which isn't by default present in devstack unless you install Heat:
```
openstack role create heat_stack_owner
```
## Testing Adjutant via the CLI
Now that the service is running, and the endpoint setup, you will want to install the client and try talking to the service:
```
sudo pip install python-adjutantclient
```
In this case the client should be safe to install globally with sudo, but you can also install it in the same virtualenv as Adjutant itself, or make a new virtualenv.
Now lets check the status of the service:
```
openstack adjutant status
```
What you should get is:
```
{
"error_notifications": [],
"last_completed_task": null,
"last_created_task": null
}
```
Seeing as we've done nothing to the service yet this is the expected output.
To list the users on your current project (admin users are hidden by default):
```
openstack project user list
```
The above action is only possibly for users with the following roles: 'admin', 'project_admin', 'project_mod'
Now lets try inviting a new user:
```
openstack project user invite bob@example.com project_admin
```
You will then get a note saying your invitation has been sent. You can list your project users again with 'openstack project user list' to see your invite.
Now if you look at the log in the Adjutant terminal you should still have open, you will see a print out of the email that would have been sent to bob@example.com. In the email is a line that looks like this:
```
http://192.168.122.160:8080/token/e86cbfb187d34222ace90845f900893c
```
Normally that would direct the user to a Horizon dashboard page where they can submit their password.
Since we don't have that running, your only option is to submit it via the CLI. This is cumbersome, but doable. From that url in your Adjutant output, grab the values after '.../token/'. That is bob's token. You can submit that via the CLI:
```
openstack admin task token submit <token> <json_data>
openstack admin task token submit e86cbfb187d34222ace90845f900893c '{"password": "123456"}'
```
Now if you get the user list, you will see bob is now active:
```
openstack project user list
```
And also shows up as a user if you do:
```
openstack user list
```
And since you are an admin, you can even take a look at the tasks themselves:
```
openstack admin task list
```
The topmost one should be your invite, and if you then do a show using that id you can see some details about it:
```
openstack admin task show <UUID>
```
## Setting Up Adjutant on Horizon
Adjutant has a Horizon UI plugin, the code and setup instructions for it can be found here:
https://github.com/catalyst/adjutant-ui
If you do set this up, you will want to edit the default Adjutant conf to so that the TOKEN_SUBMISSION_URL is correctly set to point at your Horizon.

362
README.md
View File

@ -6,364 +6,8 @@ Primarily built as user registration service that fits into the OpenStack ecosys
Useful for automating generic admin tasks that users might request but otherwise can't do without the admin role. Also allows automating the signup and creation of new users, but also allows such requests to require approval first if wanted. Due to issuing of uri+tokens for final steps of some actions, allows for a password submit/reset system as well.
### Functionality:
## Documentation:
The main workflow consists of three possible steps which can be executed at different points in time, depending on how the TaskView is defined.
Documentation is stored in doc/, a sphinx build of the documentation can be generated with the command 'tox -e docs'.
The base use case is three stages:
* Recieve Request
* Validate request data against action serializers.
* If valid, setup Task to represent the request, and the Actions specified for that TaskView.
* The service runs the pre_approve function on all actions which should do any self validation to mark the actions themselves as valid or invalid, and populating the nodes in the Task based on that.
* Admin Approval
* An admin looks at the Task and its notes.
* If they decide it is safe to approve, they do so.
* If there are any invalid actions approval will do nothing until the action data is updated and initial validation is rerun.
* The service runs the post_approve function on all actions.
* If any of the actions require a Token to be issued and emailed for additional data such as a user password, then that will occur.
* If no Token is required, the Task will run submit actions, and be marked as complete.
* Token Submit
* User submits the Token data.
* The service runs the submit function on all actions, passing along the Token data, normally a password.
* The action will then complete with the given final data.
* Task is marked as complete.
There are cases and TaskViews that auto-approve, and thus automatically do the middle step right after the first. There are also others which do not need a Token and thus run the submit step as part of the second, or even all three at once. The exact number of 'steps' and the time between them depends on the definition of the TaskView.
Actions themselves can also effectively do anything within the scope of those three stages, and there is even the ability to chain multiple actions together, and pass data along to other actions.
The points that are modular, or will be made more modular in future, are the TaskViews and the actions tied to them. Adding new actions is easy, and attaching them to existing TaskViews is as well. Adding new TaskViews is also almost entirely modular.
Creation and management of Tasks, Tokens, and Notifications is not modular and is the framework around the defined Actions and TaskViews that handles how they are executed. This helps keep the way Actions are executed consistent and simpler to maintain, but does also allow Actions to run almost any logic within those consistent steps.
#### Version Endpoints:
* ../ - GET
* JSON containing details of the currently available versions (just v1 for now)
#### Admin Endpoints:
Endpoints for the management of tasks, tokens, and notifications. Most of these are limited by roles, or are for admin use only.
* ../v1/tasks - GET
* A json containing all tasks.
* Possible parameters are:
* filters (specified below)
* tasks_per_page, defaults to 25
* page, page number to access (starts at 1)
* ../v1/tasks/<uuid> - GET
* Get details for a specific task.
* ../v1/tasks/<uuid> - PUT
* Update a task and retrigger pre_approve.
* ../v1/tasks/<uuid> - POST
* approve a task
* ../v1/token - GET
* A json containing all tokens.
* Can also be filtered.
* ../v1/token - POST
* Reissue tokens for a given task.
* ../v1/token - DELETE
* Delete all expired tokens.
* ../v1/token/<uuid> - GET
* return a json describing the actions and required fields for the token.
* ../v1/token/<uuid> - POST
* submit the token.
* ../v1/notification - GET
* Get a list of all unacknowledged notifications.
* Can also be filtered.
* ../v1/notification - POST
* Acknowledge a list of notifications.
* ../v1/notification/<id> - GET
* Details on a specific notification.
* ../v1/notification/<id> - POST
* Acknowledge a specific notification.
##### Filtering Tasks, Tokens, and Notifications
The task, token, and notification list endpoints can be filtered using a slight variant of the Django ORM filters.
This is done but sending a json with filters via HTTP parameters:
```javascript
{'filters': {'fieldname': { 'operation': 'value'}}
```
Example:
```javascript
{'filters': {'task_id': { 'exact': '842433bb-fa08-4fc1-8c3b-aa9904ceb370'}}
```
This looks a bit messy in the url as that json ends up being url-safe encoded, but doing the filters this way gives us a fairly large amount of flexibility.
Possible field lookup operations:
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#id4
#### OpenStack Style TaskView Endpoints:
For ease of integration with OpenStack, these endpoints are setup to work and partly mimic the way similar ones would work in Keystone. They work and use the TaskViews, with some specical changes for certain required endpoints.
* ../v1/openstack/users - GET
* Returns a list of users on your project, and their roles.
* Also returns a list of pending user invites.
* ../v1/openstack/users - POST
* authenticated endpoint limited by role
* auto-approved
* add/invite a user to your project
* adds an existing user with the selected role, or if non-existent user sends a uri+token to them for setup user before adding the role.
* allows adding of users to own project without needing an admin role
* ../v1/openstack/users/<user_id> - GET
* Get details on the given user, including their roles on your project.
* ../v1/openstack/users/<user_id> - DELETE
* Used to cancel a pending user invite.
* ../v1/openstack/users/<user_id>/roles - GET
* Returns a list of roles for the user on your project.
* ../v1/openstack/users/<user_id>/roles - PUT
* Add roles to a user.
* ../v1/openstack/users/<user_id>/roles - DELETE
* Remove roles from a user.
* ../v1/openstack/roles - GET
* Returns a list of roles you are allowed to edit on your project.
* ../v1/openstack/forgotpassword - POST
* Submit a username/email to have a password reset token emailed to it.
* ../v1/openstack/email-update - POST
* Submit a new email address for your user.
* Will notifiy old email address, and send a uri+token to new email to confirm.
* On confirm will update email (or username if username is email)
* ../v1/openstack/sign-up - POST
* unauthenticated endpoint
* for signup of new users/projects.
* task requires manual approval, sends a uri+token for password setup after the project is created and setup.
* create project
* setup basic networking if needed
* create user with random password
* set user given password on token submit
* ../v1/openstack/quotas/ - GET
* JSON containg the specifications of each quota size, and data about the quota size for all regions in the current project
* An additional parameter regions, containing a comma separated list of regions can be passed as well to limit the regions it will return data about.
* ../v1/openstack/quotas/ - POST
* Change the quota for all regions
* The quota will automatically update if the new quota level is adjacent to the current one and there has not been an update to that region in the past 30 days
* Other options will require admin approval before updating
* POST body should be a JSON dict containing the size ('size') and optionally 'regions', a list of regions to update to
#### (DEPRECATED) Default TaskView Endpoints:
Basic default endpoints for the TaskViews. These are still mostly used for testing, but are not really for production use. We will be getting rid of them eventually, although they do currently serve as a good basis for some of the more complex views.
* ../v1/actions/CreateProject - GET
* return a json describing the actions and required fields for the endpoint.
* ../v1/actions/CreateProject - POST
* unauthenticated endpoint
* for signup of new users/projects.
* task requires manual approval, sends a uri+token for password setup after the project is created and setup.
* create project
* setup basic networking if needed
* create user with random password
* set user given password on token submit
* ../v1/actions/InviteUser - GET
* return a json describing the actions and required fields for the endpoint.
* ../v1/actions/InviteUser - POST
* authenticated endpoint limited by role
* auto-approved
* add/invite a user to your project
* adds an existing user with the selected role, or if non-existent user sends a uri+token to them for setup user before adding the role.
* allows adding of users to own project without needing an admin role
* ../v1/actions/EditUser - GET
* return a json describing the actions and required fields for the endpoint.
* also returns a list of users that can be edited on your project.
* ../v1/actions/EditUser - POST
* authenticated endpoint limited by role
* auto-approved
* add/remove roles from a user on your project
* ../v1/actions/ResetPassword - GET
* return a json describing the actions and required fields for the endpoint.
* ../v1/actions/ResetPassword - POST
* unauthenticated endpoint
* auto-approved
* issue a uri+token to user email to reset password
* ../v1/actions/UpdateEmail - GET
* return a json describing the actions and required fields for the endpoint.
* ../v1/actions/UpdateEmail - POST
* Authenticated but open to any user
* auto-approved
* takes an email address
* issue a uri+token to new email to update to that email
#### More API Documentation:
While in debug mode the service will supply online browsable documentation via Django REST Swagger.
This is viewable at:
* ../docs
### Implementation Details:
#### Project Requirements:
The requirements for the service started as a system capable of taking requests for user sign up, waiting for approval, then on approval doing various setup and creation actions, followed by sending a uri+token to the user to set their password.
Creating a user directly in Keystone before approval also was to be avoided, and storing the password before approval was to be avoided.
Due to the steps involved, and the time between them, data had to be stored somewhere, and some representation of the request had to be stored as well. The ability to tie other actions and pieces of automation to the process also seemed useful and time saving considering the steps setting up a user might involve.
If that was the case, the system should ideally also have been modular enough to allow swapping of actions if circumstances changed, or new pieces of automation needed to be added or removed. Pushing as much logic to the concept of an 'action' seemed the ideal situation.
#### What is an Action?
Actions are a generic database model which knows what 'type' of action it is. On pulling the actions related to a Task from the database we wrap it into the appropriate class type which handles all the logic associated with that action type.
An Action is both a simple database representation of itself, and a more complex in memory class that handles all the logic around it.
Each action class has the functions "pre_approve", "post_approve", and "submit". These relate to stages of the approval process, and any python code can be executed in those functions, some of which should ideally be validation that the data passed makes sense.
Multiple actions can be chained together under one Task and will execute in the defined order. Actions can pass information along via an in memory cache/field on the task object, but that is only safe for the same stage of execution. Actions can also store data back to the database if their logic requires some info passed along to a later step of execution.
See **actions.models** and **actions.v1** for a good idea of Actions.
#### What is a Task?
A task is a top level model representation of the request. It wraps the request metadata, and based on the TaskView, will have actions associated with it.
See **api.models**.
#### What is a Token?
A token is a unique identifier linking to a task, so that anyone submitting the token will submit to the actions related to the task.
See **api.models**.
#### What is an TaskView
TaskViews are classes which extend the base TaskView class and use its imbuilt functions to process actions. They also have actions associated with them and the inbuilt functions from the base class are there to process and validate those against data coming in.
The TaskView will process incoming data and build it into a Task, and the related Action classes.
They are very simple to define as the inbuilt functions handle all the real logic, but defining which functions of those are called changes the view to create a task that either requires approval or auto-approves, with some cases auto-approval coming from the actions themselves if setup to do so.
The base TaskView class has three functions:
* get
* just a basic view function that by default returns list of actions, and their required fields for the action view.
* process_actions
* needs to be called in the TaskView definition
* A function to run the processing and validation of request data for actions.
* Builds and returns the task object, or the validation errors.
* approve
* Takes a task and approves it, running post_approve actions and issuing a token if needed.
* Used only if no admin approval is needed for Tasks create by this TaskView.
See **api.v1.tasks** and look at the TaskView class to get a better idea.
For a more complex variant, look at **api.v1.openstack** to see some more unique TaskViews specific to certain endpoints we needed to mimic OpenStack functionality.
In fact, at base these are ApiViews from Django Rest Framework, with some magic built in functions for task processing. You normally will want to pick a HTTP method for your core task itself, but having the other methods return data related to the task, or even trigger slight variants of a task is doable.
## Development:
### Packaging and Installing:
While this is a Django application, it does not follow the standard Django folder structure because of certain packaging requirements. As such the project does not have a manage.py file and must be installed via setup.py or pip (if an sdist is built) to access the manage.py funcationality.
Rather than a standard Django application, treat this as a more standard python application in this regard.
Once installed, all the normal manage.py functions can be called directly on the 'adjutant-api' commandline function.
### Dev Environment:
Dev is mainly done within a virtualenv setup alongside a devstack deployment.
For more info, see:
```
DEVSTACK_GUIDE.md
```
### Running tests:
We use tox to build a venv and run the tests. The same tests are also run for us in CI via jenkins.
Provided you have tox and its requirements installed running tests is very simple:
```
$ tox
```
To run just action unit tests:
```
$ tox -- adjutant.actions
```
To run a single api test:
```
$ tox -- adjutant.api.v1.tests.test_api_taskview.TaskViewTests.test_duplicate_tasks_new_user
```
### Adding Actions:
Adding new actions is done by creating a new django app in the actions module and defining the action models and their serializers. Action must extend the BaseAction class as defined in the **actions.models.v1.base** module. They also must register themselves to the global store of actions in **action.models**.
The documentation for this is mainly inline.
For examples of actions look in: **action.models.v1.users**, **action.models.v1.project**, and **action.models.v1.resources**
### Adding TaskViews:
TaskViews also need to be registered, and then are made active in the conf. To see how to register a TaskView look at **api.v1.models** and in the default conf under **ACTIVE_TASKVIEWS** too see how they are enabled.
For examples see **api.v1.openstack**.
## Setup/Deployment:
### Custom Email Templates
Custom email templates are placed in:
```
/etc/adjutant/templates/
```
This is so that adding personalised or deployment specific templates is kept outside of the scope of the service itself and managed by the deployer.
## Plugins
As Adjutant is built on top of Django, we've used parts of Django's installed apps system to allow us a plugin mechanism that allows additional actions and views to be brought in via external sources. This allows company specific or deployer specific changes to easily live outside of the core service and simply extend the core service where and when need.
An example of such a plugin is here:
https://github.com/catalyst/adjutant-odoo
## Future Plans:
Most future plans are around adding additional Actions to the service, but there will be some features that will require some refactoring.
While we are presently working with the Keystone V3 API, groups are not being used, but we intend to update the service to also manage and handle user groups. Managing Domains isn't really doable, but having the service be able to accept Domains, and multiple Domain back-ends is being planned.
Additional Actions and TaskViews we wish to add in the near future:
* Update Quota
* Admin is required to do this
* Allows users to request a quota increase and by requiring an admin to simply check, and confirm the request, will make the process faster.
* Makes it effectively a quick 2 step process.
* For eventual heirarchical-multi-tenancy this would ideally send these qouta increase requests to the parent for approval.
* Hierarchical Multi-Tenancy in a single domain environment
* 'project_admin' to be able to create sub-projects off the current scoped project.
* This works as per normal in Keystone but does not require an admin role and enforces a naming convention to ensure unique namespaces per sub-projects and somewhat avoid the project name uniqueness issues per domain.
* This also adds inherited role support to the already existing user invite and user role editing features.
* some VERY basic sub-project quota (number of sub-projects allowed) via metadata stored on a project in Keystone, with quota calculations in Adjutant checking against number of sub-projects created in your WHOLE tree within given shifting window.
* Stand-alone Setup Network Action + TaskView
* For users who missed or forgot the step at Project creation, and want a quick network setup.
* Blank Slate
* Doesn't need admin, and could reuse your own token.
* Clear my entire project of any and all resources.
* Already doable via the APIs, but would be nice to have it in an easy to request action so clients don't need to code it themselves.
* A way to setup and manage MFA credentials for a user
* relies on work in Keystone around MFA, and serves only as a means to allow an initial challenge response that requires an initial passcode before MFA would become active for the user in Keystone.
Features that might require a slight refactor:
* Add optional serializers for token data.
Even less likely, and further far-future additions:
* Split the system into the api, a queue, and workers. That way tasks are processed asynchronously by the workers.
* Will require a bunch of rethinking, but most of the core logic will be reused, with the workers simply waiting for events and executing them on the tasks/actions in much the same way as they are presently.
* Remove concept of predefined action steps entirely, setup Actions to have any possible number of 'steps'.
* Will require moving actions to an iterator style pattern with a "next_action" style function as the driving force.
* Will alter how chaining actions together works, thus may require a lot of work to define a sensible pattern for chaining them together.
An API Reference is stored in api-ref. This is also a sphinx build and can be generated with 'tox -e api-ref'.

View File

@ -16,10 +16,10 @@
Django settings for Adjutant.
For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/
https://docs.djangoproject.com/en/1.11/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
https://docs.djangoproject.com/en/1.11/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)

Binary file not shown.

View File

View File

@ -0,0 +1,478 @@
*************************
Administrative Endpoints
*************************
Endpoints for the management of tasks, tokens, and notifications. Most of
these are limited by roles, or are for admin use only.
Status
=======
.. rest_method:: GET /v1/status
Authentication: Administrator
Normal Response Code: 200
Simple status endpoint.
Returns a list of unacknowledged error notifications,
and both the last created and last completed tasks.
List Tasks
===========
.. rest_method:: GET /v1/tasks
Authentication: Administrator
Normal Response Codes: 200
Error Response Codes: 401, 403
Lists all tasks.
.. rest_parameters:: parameters.yaml
- filters: filters
- page: page
- tasks_per_page: tasks_per_page
Request Example
-----------------
.. code-block:: bash
curl -H "X-Auth-Token: $OS_TOKEN" http://adjutant/v1/tasks
Response Example
------------------
.. code-block:: javascript
{
"tasks": [
{
"action_notes": {
"ResetUserPasswordAction": [
"Existing user with matching email.",
]
},
"actions": [
{
"action_name": "ResetUserPasswordAction",
"data": {
"domain_name": "Default",
"email": "demo@example.com"
},
"valid": true
}
],
"approved": true,
"approved_by": {},
"approved_on": "2017-08-30T21:29:48.484441Z",
"cancelled": false,
"completed": true,
"completed_on": "2017-08-30T21:30:13.269498Z",
"created_on": "2017-08-30T21:29:47.989851Z",
"ip_address": "127.0.0.1",
"keystone_user": {},
"project_id": null,
"task_type": "reset_password",
"uuid": "d5c7901cfecd45ec9a87871035c9f662"
},
{
"action_notes": {
"NewProjectDefaultNetworkAction": [],
"NewProjectWithUserAction": [],
"SetProjectQuotaAction": []
},
"actions": [
{
"action_name": "NewProjectWithUserAction",
"data": {
"domain_id": "default",
"email": "test@example.com",
"parent_id": null,
"project_name": "test_project"
},
"valid": true
},
{
"action_name": "NewProjectDefaultNetworkAction",
"data": {
"region": "RegionOne",
"setup_network": false
},
"valid": true
},
{
"action_name": "SetProjectQuotaAction",
"data": {},
"valid": true
}
],
"approved": false,
"approved_by": {},
"approved_on": None,
"cancelled": false,
"completed": false,
"completed_on": null,
"created_on": "2017-07-26T21:44:21.082248Z",
"ip_address": "127.0.0.1",
"keystone_user": {},
"project_id": null,
"task_type": "signup",
"uuid": "370d952c63ba410c8704abc12cfd97b7"
}
}
Task Details
=============
.. rest_method:: GET /v1/tasks/<task_id>
Authentication: Administrator
Normal Response Codes: 200
Error Response Codes: 401, 403, 404
Gives details for the specific task.
.. rest_parameters:: parameters.yaml
- task_id: task_id
Request Example
----------------
.. code-block:: bash
curl -H "X-Auth-Token: $OS_TOKEN" http://adjutant/v1/tasks/d5c7901cfecd45ec9a87871035c9f662
Response Example
-----------------
.. code-block:: javascript
{
"action_notes": {
"ResetUserPasswordAction": [
"Existing user with matching email.",
]
},
"actions": [
{
"action_name": "ResetUserPasswordAction",
"data": {
"domain_name": "Default",
"email": "demo@example.com"
},
"valid": true
}
],
"approved": true,
"approved_by": {},
"approved_on": "2017-08-30T21:29:48.484441Z",
"cancelled": false,
"completed": true,
"completed_on": null,
"created_on": "2017-08-30T21:29:47.989851Z",
"ip_address": "127.0.0.1",
"keystone_user": {},
"project_id": null,
"task_type": "reset_password",
"uuid": "d5c7901cfecd45ec9a87871035c9f662"
}
Update Task
============
.. rest_method:: PUT /v1/tasks/<task_id>
Authentication: Project Admin or Project Moderator
Normal Response Codes: 200
Error Response Codes: 400, 401, 403, 404
Replace the data in an unapproved action and rerun the preapproval steps
.. rest_parameters:: parameters.yaml
- task_data: task_data
Request Example
----------------
.. code-block:: bash
curl -H "X-Auth-Token: $OS_TOKEN" \
-H 'Content-Type: application/json' \
-X PUT --data '{
"project_name": "a_project",
"email": "example.a@t.com",
"region": "RegionOne",
"setup_network": false
}' http://0.0.0.0:5050/v1/tasks/19dbe418ecc14aeb94053f23eda01c78
Response Example
----------------
.. code-block:: javascript
{
"notes": ["Task successfully updated."]
}
Approve Task
============
.. rest_method:: POST /v1/tasks/<task_id>
Authentication: Administrator
Normal Response Codes: 200
Error Response Codes: 400, 401, 403, 404
Approves a task and runs the actions approval steps.
.. rest_parameters:: parameters.yaml
- task_id: task_id
- approved: approved
Request Example
----------------
.. code-block:: bash
curl -H "X-Auth-Token: $OS_TOKEN" -H 'Content-Type: application/json' \
-d '{"approved": true}' http://0.0.0.0:5050/v1/tasks/19dbe418ecc14aeb94053f23eda01c78
Response Example
-----------------
.. code-block:: javascript
{
"notes": ["Created Token."]
}
In most cases an email will be sent after approval to the user who requested
the task.
Cancel Task
===========
.. rest_method:: DELETE /v1/tasks/<task_id>
Authentication: Administrator, Project Admin or Project Moderator
Normal Response Codes: 200
Error Response Codes: 400, 401, 403, 404
Cancel a task. Tasks can be cancelled at any stage prior to their completion,
an issued token for a cancelled task will be invalidated.
Project Admins and Project Moderators can only cancel tasks associated with
their projects.
.. rest_parameters:: parameters.yaml
- task_id: task_id
List tokens
============
.. rest_method:: GET /v1/tokens
Authentication: Administrator
Normal Response Codes: 200
Error Response Codes: 401, 403
List all active tokens.
.. rest_parameters:: parameters.yaml
- filters: filters
Reissue Tokens
===============
.. rest_method:: POST /v1/tokens
Authentication: Administrator, Project Admin or Project Moderator
Normal Response Codes: 200
Error Response Codes: 400, 401, 403, 404
Reissue a token for the specified task.
.. rest_parameters:: parameters.yaml
- task_id: task_id_body
Delete Expired Tokens
======================
.. rest_method:: DELETE /v1/token
Authentication: Administrator
Normal Response Codes: 200
Error Response Codes: 401, 403
Delete all expired tokens.
Note that if a token has expired it will be deleted when someone attempts to
access it, this will prevent the database from clogging up however will not
have an effect on functionality.
Get Token Details
======================
.. rest_method:: GET /v1/token/<token_id>
Authentication: Unauthenticated
Normal Response Codes: 200
Error Response Codes: 401, 403, 404
Details the actions, task_type and required fields of the token
.. rest_parameters:: parameters.yaml
- token_id: token_id
Request Example
----------------
.. code-block:: bash
curl http://0.0.0.0:5050/v1/tokens/771af33fb28e46aab45e265bd6a6d469
Response Example
-----------------
.. code-block:: javascript
{
"actions": [
"NewProjectWithUserAction",
"NewProjectDefaultNetworkAction",
"SetProjectQuotaAction"
],
"required_fields": [
"password"
],
"task_type": "signup"
}
Submit Token
======================
.. rest_method:: POST /v1/token/<token_id>
Authentication: Unauthenticated
Normal Response Codes: 200
Error Response Codes: 400, 404
Submit a token and it's data to the last stage of an action execution.
A 400 will be return if it does not contain all of the necessary fields.
.. rest_parameters:: parameters.yaml
- token_id: token_id
- token_data: token_data
Request Example
------------------
.. code-block:: bash
curl -H 'Content-Type: application/json' \
-d '{"password": "12345"}' http://0.0.0.0:5050/v1/tokens/771af33fb28e46aab45e265bd6a6d469
Response Example
-----------------
.. code-block:: javascript
{"notes":["Token submitted successfully."]}
In most cases an email will be sent after token submission, detailing what
has changed.
List Notifications
======================
.. rest_method:: GET /v1/notification
Authentication: Administrator
Normal Response Codes: 200
Error Response Codes: 401, 403
List all unacknowledged notifications
.. rest_parameters:: parameters.yaml
- filters: filters
Acknowledge a List of Notifications
===================================
.. rest_method:: POST /v1/notification
Authentication: Administrator
Mark a given list of notifications as acknowledged
.. rest_parameters:: parameters.yaml
- notifications: notifications
Notification Details
=====================
.. rest_method:: GET /v1/notification/<notification_id>
Get details of a specific notification
.. rest_parameters:: parameters.yaml
- notification_id: notification_id
Acknowledge Notification
========================
.. rest_method:: GET /v1/notification/<notification_id>
Acknowledge a specific notification.
.. rest_parameters:: parameters.yaml
- notification_id: notification_id
- acknowledged: acknowledged
Filtering Tasks, Tokens, and Notifications
==========================================
The task, token, and notification list endpoints can be filtered using a
slight variant of the Django ORM filters.
This is done but sending a json with filters via HTTP parameters:
.. code-block:: javascript
{'filters': {'fieldname': { 'operation': 'value'}}
Example:
.. code-block:: javascript
{'filters': {'task_id': { 'exact': '842433bb-fa08-4fc1-8c3b-aa9904ceb370'}}
This looks a bit messy in the url as that json ends up being url-safe encoded,
but doing the filters this way gives us a fairly large amount of flexibility.
Possible field lookup operations:
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#id4

286
api-ref/source/conf.py Normal file
View File

@ -0,0 +1,286 @@
# -*- coding: utf-8 -*-
#
# Adjutant API Reference documentation build configuration file, created by
# sphinx-quickstart on Wed Sep 6 13:55:48 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# import sys
# import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.intersphinx',
'os_api_ref',
'openstackdocstheme'
]
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
repository_name = 'openstack/adjutant'
bug_project = 'adjutant'
bug_tag = ''
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Adjutant API Reference'
copyright = u'2017, Catalyst IT Ltd'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1'
# The full version, including alpha/beta/rc tags.
release = '1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
# html_theme = 'openstackdocs'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {}
# If false, no module index is generated.
# html_domain_indices = True
# If false, no index is generated.
# html_use_index = True
# If true, the index is split into individual pages for each letter.
# html_split_index = False
# If true, links to the reST sources are added to the pages.
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'AdjutantAPIReferencedoc'
# -- Options for LaTeX output ---------------------------------------------
# latex_elements = {
# # The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper',
#
# # The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt',
#
# # Additional stuff for the LaTeX preamble.
# 'preamble': '',
# }
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'AdjutantAPIReference.tex',
u'Adjutant API Reference Documentation',
u'Catalyst IT Ltd', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False
# If true, show page references after internal links.
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
# latex_appendices = []
# If false, no module index is generated.
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'adjutantapireference', u'Adjutant API Reference Documentation',
[u'Catalyst IT Ltd'], 1)
]
# If true, show URL addresses after external links.
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'AdjutantAPIReference', u'Adjutant API Reference Documentation',
u'Catalyst IT Ltd', 'AdjutantAPIReference',
'A simple workflow framework to help automate admin and user tasks in '
'and around OpenStack via a pluggable API exposing tasks made up of '
'easily chainable actions.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
# texinfo_appendices = []
# If false, no module index is generated.
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
# texinfo_no_detailmenu = False
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}

View File

@ -0,0 +1,38 @@
200:
default: |
Request was successful.
task-view: |
Request successful, task submitted.
202:
default: |
Request is accepted, but processing may take some time.
400:
default: |
Bad request
task-view: |
Invalid task data, or missing parameters. The response body will include
the details of the missing or invalid parameters.
401:
default: |
User is unauthenticated, or X-Auth-Token has expired.
403:
default: |
User has the wrong roles for this operation.
404:
default: |
The requested resource could not be found.
405:
default: |
Method is not valid for this endpoint and resource.
409:
default: |
Conflict
task-view: |
Duplicate task.
500:
default: |
Something went wrong with the service which prevents it from
fulfilling the request.
503:
default: |
Adjutant cannot connect to the Keystone Authentication Server.

15
api-ref/source/index.rst Normal file
View File

@ -0,0 +1,15 @@
.. Adjutant API Reference documentation master file, created by
sphinx-quickstart on Wed Sep 6 13:55:48 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Adjutant API Reference documentation!
==================================================
.. toctree::
:maxdepth: 2
v1-api-reference
Adjutant is a workflow framework built with Django and Django-Rest-Framework to
automate basic admin tasks within an OpenStack Cloud.

View File

@ -0,0 +1,152 @@
# variables in header
X-Auth-Token:
description: |
A valid authentication token for a user.
in: header
required: true
type: string
# Path parameters
notification_id:
description: |
The notification UUID, as given on list endpoints and in email correspondence.
in: path
required: true
type: string
task_id:
description: |
The task UUID as given in the task list and email correspondence.
in: path
required: true
type: string
token_id:
description: |
The token UUID, as given on the lists and in email correspondence.
in: path
required: true
type: string
user_id:
description: |
The user id, as seen on the ../v1/openstack/users page. Note that this
is the openstack user id for confirmed users and the task ID for invited
users.
in: path
required: true
type: string
# Query Parameters
filters:
description: |
Django style filters for task, token and notification endpoints.
See section `Filters` for details.
in: query
required: false
type: dictionary
region:
description: |
Region to setup the default network in.
in: query
required: true
type: string
setup_network:
description: |
Whether or not to setup a default network for a new project
in: query
required: true
type: boolean
page:
description: |
Page number to access, starts at and defaults to 1.
in: query
required: false
type: int
project_name:
description: |
Name for the new project.
in: query
required: true
type: string
tasks_per_page:
description: |
Limit on the tasks viewed on each page.
in: query
required: false
type: int
# Body Parameters
acknowledged:
description: |
Confirmation for acknowledging a notification.
in: body
required: true
type: boolean
approved:
description: |
Confirmation to approve a task.
in: body
required: true
type: boolean
email:
description: |
New user email address.
in: body
required: true
type: string
email_password:
description: |
Email address for the user whose password needs resetting
in: body
required: true
type: string
email_signup:
description: |
Email address for the default user and project admin.
in: body
required: true
type: string
notifications:
description: |
List of notification UUIDs to acknowledge
in: body
required: true
type: array
roles:
description: |
List of roles for the user.
in: body
required: true
type: array
task_data:
description: |
A dictionary replacing all the data for a task. See the task details
for what values should be included
in: body
required: true
type: dictionary
task_id_body:
description: |
The task UUID as given in the task list and email correspondence.
in: body
required: true
type: int
token_data:
description: |
A dictionary replacing all the data for a task. Use the token get request
to see what should needs to be included.
in: body
required: true
type: dictionary
username:
description: |
New user username, required only if USERNAME_IS_EMAIL is false.
in: body
required: false
type: string
username_password:
description: |
Username, required only if USERNAME_IS_EMAIL is false.
in: body
required: false
type: string

View File

@ -0,0 +1,297 @@
************************************
OpenStack Style TaskView Endpoints
************************************
A response of 'task created' means that the task requires admin approval and
a response of 'created token' indicates that the task has been auto-approved
and awaits the submission of an emailed token.
List users
========================
.. rest_method:: GET /v1/openstack/users
Authentication: Project Moderator or Admin
List current and pending users in the current project.
Request Example
----------------
.. code-block:: bash
curl -H "X-Auth-Token: $NOS_TOKEN" http://0.0.0.0:5050/v1/openstack/users
Response Example
-----------------
.. code-block:: javascript
{
"users": [
{
"cohort": "Member",
"email": "demo@example.com",
"id": "",
"manageable": false,
"name": "demo",
"roles": [
"project_admin",
"__member__"
],
"status": "Active"
}
]
}
Invite User
============
.. rest_method:: POST /v1/openstack/users
Authentication: Project Moderator or Admin
An auto-approved task that will add a user to the project. If the user already
exists it will add them directly, otherwise it will create a user when the
invitee submits a token sent to them though email
.. rest_parameters:: parameters.yaml
- roles: roles
- email: email
- username: username
Request Example
-----------------
.. code-block:: bash
curl -H "X-Auth-Token: $NOS_TOKEN" http://0.0.0.0:5050/v1/openstack/users \
-H 'Content-Type: application/json' \
-d '{"roles": ["_member_"], "email": "new@example.com"}'
Response Example
-----------------
.. code-block:: javascript
{
"notes": ["created token"]
}
User Details
=============
.. rest_method:: GET /v1/openstack/users/<user_id>
Authentication: Project Moderator or Admin
Get details on the given user including their roles on your project
.. rest_parameters:: parameters.yaml
- user_id: user_id
Cancel User Invite
==================
.. rest_method:: DELETE /v1/openstack/users/<user_id>
Authentication: Project Moderator or Admin
Cancel a pending user invitation. Current users can be removed from your
project by removing all of their roles.
.. rest_parameters:: parameters.yaml
- user_id: user_id
List User Roles
==================
.. rest_method:: GET /v1/openstack/users/<user_id>/roles
Authentication: Project Moderator or Admin
List all roles the user has on the current project
.. rest_parameters:: parameters.yaml
- user_id: user_id
Add User Roles
==================
.. rest_method:: PUT /v1/openstack/users/<user_id>/roles
Authentication: Project Moderator or Admin
Add the specified roles to the user on the current project.
There is additional authentication in the forms of what roles can be edited
by the current user. If the target user has any role not editable by the
current user the user will not be able to edit any of their roles.
Editiable roles can be found at the ``List available roles`` endpoint.
.. rest_parameters:: parameters.yaml
- user_id: user_id
- roles: roles
Request Example
-----------------
.. code-block:: bash
curl -H "X-Auth-Token: $NOS_TOKEN" -H 'Content-Type: application/json' \
-d '{"roles": ["project_mod"]}' -X PUT \
http://0.0.0.0:5050/v1/openstack/users/5123ca764f3d40d79e3589e91f1ccb8f/roles
Response Example
-----------------
.. code-block:: javascript
{
"notes": [
"Task completed successfully."
]
}
Remove User Roles
==================
.. rest_method:: DELETE /v1/openstack/users/<user_id>/roles
Authentication: Project Moderator or Admin
Remove the specified roles from the user on the current project.
A project moderator will not be able to change the roles of a project admin.
.. rest_parameters:: parameters.yaml
- user_id: user_id
- roles: roles
Request Example
-----------------
.. code-block:: bash
curl -H "X-Auth-Token: $NOS_TOKEN" -H 'Content-Type: application/json' \
-d '{"roles": ["project_mod"]}' -X DELETE \
http://0.0.0.0:5050/v1/openstack/users/5123ca764f3d40d79e3589e91f1ccb8f/roles
Response Example
-----------------
.. code-block:: javascript
{
"notes": [
"Task completed successfully."
]
}
List Available Roles
=====================
.. rest_method:: GET /v1/openstack/roles
Authentication: Project Moderator or Admin
List the roles available for the current user to modify.
Request Example
----------------
.. code-block:: bash
curl -H "X-Auth-Token: $NOS_TOKEN" http://0.0.0.0:5050/v1/openstack/roles/
Response Example
-----------------
.. code-block:: javascript
{
"roles": [
{
"domain_id": null,
"id": "b81efc1e23a043d0976dc39b3e2727c3",
"links": {
"self": "http://identity/v3/roles/b81efc1e23a043d0976dc39b3e2727c3"
},
"name": "project_mod"
},
{
"domain_id": null,
"id": "9fe2ff9ee4384b1894a90878d3e92bab",
"links": {
"self": "http://identity/v3/roles/9fe2ff9ee4384b1894a90878d3e92bab"
},
"name": "_member_"
},
]
}
Password Reset
===================
.. rest_method:: POST /v1/openstack/users/password-reset
Authentication: Unauthenticated
Unauthenticated for forgotten password requests. If the email has an associated
user a token will be sent to them to use to reset their password with.
.. rest_parameters:: parameters.yaml
- email: email_password
- username: username_password
Request Example
----------------
.. code-block:: bash
curl -d '{"email": "demo@example.org"}' http://0.0.0.0:5050/v1/openstack/users/password-reset
Response Example
-----------------
.. code-block:: javascript
{
"notes": ["If user with email exists, reset token will be issued."]
}
Update Email Address
=====================
.. rest_method:: POST /v1/openstack/email-update
Authentication: Authenticated
Submit a new email address for the current user. A submission token will be
sent to the new address, and a notification to the old email address. The
account address will not change until the token submission.
.. rest_parameters:: parameters.yaml
- email: email
Sign Up
========
.. rest_method:: POST /v1/openstack/sign-up
Authentication: Unauthenticated
Account creation endpoint.
.. rest_parameters:: parameters.yaml
- email: email
- username: username
- project_name: project_name
- setup_network: setup_network
- region: region
Request Example
----------------
.. code-block:: bash
curl -H 'X-Auth-Token: $OS_TOKEN' -H 'Content-Type: application/json' -d '{
"email": "example@example.com", "project_name": "example_project"}'
-X POST http://0.0.0.0:5050/v1/openstack/sign-up
Response Example
-----------------
.. code-block:: javascript
{
"notes": ["task created"]
}

View File

@ -0,0 +1,93 @@
#######################
Version 1 API reference
#######################
This is the reference for Adjutant when it is using the default configuration.
Different deployments may exclude certain task views or include their own
additional ones.
The core functionality of Adjutant is built around the concept of tasks and
actions.
Actions are both concepts in the database and code that can execute whatever
logic is necessary at each stage.
Tasks can bundle a number of actions and have 3 main steps.
1. A user submits a request to the specified endpoint.
2. An admin approves the request, or it is automatically approved. At this
point the admin can also update invalid data inside the task.
3. If necessary a user will be emailed a token and will submit additional data
(ie passwords or a confirmation) to finish the task.
Depending on the task and the data provided some steps may be skipped.
******************
Authentication
******************
The 'X-Auth-Token' header value should be provided for authentication
with a valid Keystone token.
******************
HTTP Status Codes
******************
.. rest_status_code:: success http-status.yaml
- 200
- 200: task-view
- 202
.. rest_status_code:: error http-status.yaml
- 400
- 401
- 403
- 404
- 405
- 409
- 500
- 503
******************
Service Discovery
******************
Version Discovery Endpoint
===========================
.. rest_method:: GET /
Unauthenticated.
JSON containing details of the currently available versions (just v1 for now)
Normal response code: 200
Version One Details Endpoint
=============================
.. rest_method:: GET /v1
Unauthenticated.
Details V1 version details and the available taskviews and their fields.
See below for further details on the individual taskviews.
Normal response code: 200
.. include:: admin-api.inc
.. include:: taskviews.inc
************************************
Additional API Documentation:
************************************
While in debug mode the service will supply online browsable documentation via
Django REST Swagger.
This is viewable at: ../docs

View File

@ -53,7 +53,7 @@ KEYSTONE:
password: openstack
project_name: admin
# MUST BE V3 API:
auth_url: http://localhost:5000/v3
auth_url: http://localhost/identity/v3
domain_id: default
TOKEN_SUBMISSION_URL: http://192.168.122.160:8080/token/

View File

View File

@ -30,7 +30,7 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
extensions = ['os_api_ref', ]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -66,7 +66,10 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
# html_theme = 'alabaster'
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the

View File

@ -0,0 +1,247 @@
Configuring Adjutant
====================================
.. highlight:: yaml
Adjutant is designed to be highly configurable for various needs. The goal
of Adjutant is to provide a variety of common tasks and actions that can
be easily extended or changed based upon the needs of your OpenStack.
The default Adjutant configuration is found in conf/conf.yaml, and but will
be overriden if a file is placed at ``/etc/adjutant/conf.yaml``.
The first part of the configuration file contains standard Django settings.
.. code-block:: yaml
SECRET_KEY:
ALLOWED_HOSTS:
- "*"
ADDITIONAL_APPS:
- adjutant.api.v1
- adjutant.actions.v1
DATABASES:
default:
ENGINE: django.db.backends.sqlite3
NAME: db.sqlite3
LOGGING:
EMAIL_SETTINGS:
EMAIL_BACKEND: django.core.mail.backends.console.EmailBackend
If you have any plugins, ensure that they are also added to
**ADDITIONAL_APPS**.
The next part of the confirguration file contains a number of settings for all
taskviews.
.. code-block:: yaml
USERNAME_IS_EMAIL: True
KEYSTONE:
username:
password:
project_name:
auth_url: http://localhost:5000/v3
domain_id: default
TOKEN_SUBMISSION_URL: http://192.168.122.160:8080/token/
# time for the token to expire in hours
TOKEN_EXPIRE_TIME: 24
ROLES_MAPPING:
admin:
- project_admin
- project_mod
- _member_
project_admin:
- project_admin
- project_mod
- _member_
project_mod:
- project_mod
- heat_stack_owner
- _member_
**USERNAME_IS_EMAIL** impacts account creation, and email modification actions.
In the case that it is true, any task passing a username and email pair, the
username will be ignored. This also impacts where emails are sent to.
The keystone settings must be for a user with administrative privileges,
and must use the Keystone V3 API endpoint.
If you have Horizon configured with adjutant-api **TOKEN_SUBMISSION_URL**
should point to that.
**ROLES_MAPPING** defines which roles can modify other roles. In the default
configuration a user who has the role project_mod will not be able to
modify any of the roles for a user with the project_admin role.
Standard Task Settings
----------------------
.. code-block:: yaml
ACTIVE_TASKVIEWS:
- UserRoles
- UserDetail
- UserResetPassword
- UserSetPassword
- UserList
- RoleList
- SignUp
- UserUpdateEmail
All in use taskviews, including those that are from plugins must be included
in this list. If a task is removed from this list its endpoint will not be
accessable however users who have started tasks will still be able submit them.
.. code-block:: yaml
DEFAULT_TASK_SETTINGS:
duplicate_policy: null
emails:
initial:
subject: Initial Confirmation
reply: no-reply@example.com
from: bounce+%(task_uuid)s@example.com
template: initial.txt
# html_template: initial.txt
token:
completed:
notifications:
EmailNotification:
standard:
emails:
- example@example.com
reply: no-reply@example.com
from: bounce+%(task_uuid)s@example.com
template: notification.txt
# html_template: completed.txt
error:
The default settings can be overridden for individual tasks in the
TASK_SETTINGS configuration, these are cascading overrides. Two additional
options are available, overriding the default actions or adding in additional
actions. These will run in the order specified.
.. code-block:: yaml
signup:
default_actions:
- NewProjectAction
invite_user:
additional_actions:
- SendAdditionalEmailAction
By default duplicate tasks will be marked as invalid, however the duplicate
policy can be set to 'cancel' to cancel duplicates and start a new class.
Email Settings
~~~~~~~~~~~~~~
The ``initial`` email will be sent after the user makes the request, the
``token`` email will be sent after approval steps are run, and the
``completed`` email will be sent after the token is submitted.
The emails will be sent to the current user, however this can be changed at
the action level with the ``get_email()`` function.
Notification Settings
~~~~~~~~~~~~~~~~~~~~~
The type of notifications can be defined here for both standard notifications
and error notifications::
notifications:
EmailNotification:
standard:
emails:
- example@example.com
reply: no-reply@example.com
template: notification.txt
error:
emails:
- errors@example.com
reply: no-reply@example.com
template: notification.txt
<other notification engine>:
Currently EmailNotification is the only available notification engine however
new engines can be added through plugins and may have different settings.
Action Settings
---------------
Default action settings.
Actions will each have their own specific settings, dependent on what they
are for. The standard settings for a number of default actions are below:
An action can have it's settings overridden in the settings for it's task.
This will only effect when the action is called through that specific task
Overriding action settings for a specific task.
Email Templates
---------------
Additional templates can be placed in ``/etc/adjutant/templates/`` and will be
loaded in automatically. A plain text template and an HTML template can be
specified separately. The context for this will include the task object and
a dictionary containing the action objects.
Additional Emails
------------------
The SendAdditionalEmailAction is designed to be added in at configuration
for relevant tasks. It's templates are also passed a context dictionary with
the task and actions available. By default the template is null and the email
will not send.
The settings for this action should be defined within the action_settings
for it's related task view.
.. code-block:: yaml
additional_actions:
- SendAdditionalEmailAction
action_settings:
SendAdditionalEmailAction:
initial:
subject: OpenStack Email Update Requested
template: email_update_started.txt
email_current_user: True
The additional email action can also send to a subset of people.
The user who made the request can be emailed with ::
email_current_user: true
Or the email can be sent to everyone who has a certain role on the project.
(Multiple roles can also be specified)
.. code-block:: yaml
email_roles:
- project_admin
Or an email can be sent to a specified address in the task cache
(key: ``additional_emails``) ::
email_in_task_cache: true
Or sent to an arbitrary administrative email address(es)::
email_additional_addresses:
- admin@example.org
This can be useful in the case of large project affecting actions.

125
doc/source/design.rst Normal file
View File

@ -0,0 +1,125 @@
####################################
Functionality
####################################
Adjutant is built around tasks and actions.
Actions are a generic database model which knows what 'type' of action it is.
On pulling the actions related to a Task from the database we wrap it into the
appropriate class type which handles all the logic associated with that action
type.
An Action is both a simple database representation of itself, and a more
complex in memory class that handles all the logic around it.
Each action class has the functions "pre_approve", "post_approve", and
"submit". These relate to stages of the approval process, and any python code
can be executed in those functions, some of which should ideally be validation.
Multiple actions can be chained together under one Task and will execute in
the defined order. Actions can pass information along via an in memory
cache/field on the task object, but that is only safe for the same stage of
execution. Actions can also store data back to the database if their logic
requires some info passed along to a later step of execution.
See ``actions.models`` and ``actions.v1`` for a good idea of Actions.
Tasks originate at a TaskView, and start the action processing. They encompass
the user side of interaction.
The main workflow consists of three possible steps which can be executed at
different points in time, depending on how the TaskView and the actions within
it are defined.
The base use case is three stages:
* Recieve Request
* Validate request data against action serializers.
* If valid, setup Task to represent the request, and the Actions specified
for that TaskView.
* The service runs the pre_approve function on all actions which should do
any self validation to mark the actions themselves as valid or invalid,
and populating the nodes in the Task based on that.
* Admin Approval
* An admin looks at the Task and its notes.
* If they decide it is safe to approve, they do so.
* If there are any invalid actions approval will do nothing until the
action data is updated and initial validation is rerun.
* The service runs the post_approve function on all actions.
* If any of the actions require a Token to be issued and emailed for
additional data such as a user password, then that will occur.
* If no Token is required, the Task will run submit actions, and be
marked as complete.
* Token Submit
* User submits the Token data.
* The service runs the submit function on all actions, passing along the
Token data, normally a password.
* The action will then complete with the given final data.
* Task is marked as complete.
There are cases and TaskViews that auto-approve, and thus automatically do the
middle step right after the first. There are also others which do not need a
Token and thus run the submit step as part of the second, or even all three at
once. The exact number of 'steps' and the time between them depends on the
definition of the TaskView.
Actions themselves can also effectively do anything within the scope of those
three stages, and there is even the ability to chain multiple actions together,
and pass data along to other actions.
Details for adding taskviews and actions can be found on the :doc:`plugins`
page.
What is an Action?
====================
Actions are a generic database model which knows what 'type' of action it is.
On pulling the actions related to a Task from the database we wrap it into the
appropriate class type which handles all the logic associated with that action
type.
An Action is both a simple database representation of itself, and a more
complex in memory class that handles all the logic around it.
Each action class has the functions "pre_approve", "post_approve", and
"submit". These relate to stages of the approval process, and any python code
can be executed in those functions.
What is a Task?
================
A task is a top level model representation of the request. It wraps the
request metadata, and based on the TaskView, will have actions associated with
it.
What is a Token?
==================
A token is a unique identifier linking to a task, so that anyone submitting
the token will submit to the actions related to the task.
What is an TaskView?
====================
TaskViews are classes which extend the base TaskView class and use its inbuilt
functions to process actions. They also have actions associated with them and
the inbuilt functions from the base class are there to process and validate
those against data coming in.
The TaskView will process incoming data and build it into a Task,
and the related Action classes.
The base TaskView class has three functions:
* get
* just a basic view function that by default returns list of actions,
and their required fields for the action view.
* process_actions
* needs to be called in the TaskView definition
* A function to run the processing and validation of request data for
actions.
* Builds and returns the task object, or the validation errors.
At their base TaskViews are django-rest ApiViews, with a few magic functions
to wrap the task logic.

View File

@ -0,0 +1,242 @@
###############################
Deploying Adjutant in Devstack
###############################
This is a guide to setting up Adjutant in a running Devstack
environment close to how we have been running it for development purposes.
This guide assumes you are running this in a clean ubuntu 16.04
virtual machine with sudo access.
***************
Deploy Devstack
***************
Grab the Devstack repo::
git clone https://github.com/openstack-dev/devstack.git
And then define a basic localrc file with the password set and place that in
the devstack folder (adjutant's default conf assumes 'openstack' as the admin
password)::
ADMIN_PASSWORD=openstack
MYSQL_PASSWORD=openstack
DATABASE_PASSWORD=openstack
RABBIT_PASSWORD=openstack
SERVICE_PASSWORD=openstack
Run the devstack build::
./devstack/stack.sh
Provided your VM has enough ram to handle a devstack install this should
take a while, but go smoothly. Ideally give your VM 5gb or more of ram, any
less can cause the devstack build to fail.
***************
Deploy Adjutant
***************
Grab the Adjutant repo::
git clone https://github.com/openstack/adjutant.git
Then you'll want to setup a virtual environment::
cd adjutant
virtualenv venv
source venv/bin/activate
Once that is done you can install Adjutant and its requirements::
pip install -r requirements.txt
python setup.py develop
If you prefer you can install it fully, but using develop instead allows you
update the Adjutant code and have the service reflect that without rerunning
the install.
******************
Configure Adjutant
******************
Most of the default conf values should work fine against devstack, but one
thing that you will need to change is the uuid for the public network in
`DEFAULT_ACTION_SETTINGS` for the actions NewDefaultNetworkAction and
NewProjectDefaultNetworkAction. If you don't set this correctly, then signups
or tasks using those actions will not be able to correctly create a default
network as they cannot find the correct external public network.
On a fresh devstack there is only one public network so to find the public
network uuid you can to run::
openstack network show public
And then grab the id value and put that into the Adjutant conf.
Username is email
=================
The example conf for Adjutant is setup with `USERNAME_IS_EMAIL = TRUE` which
works on the assumption that usernames are emails. This is easy to change in
the conf, but a fairly useful way of avoiding username clashes. If you set this
to `False` then usernames will be required as well as emails for most tasks
that deal with user creation.
Migrating between the two states hasn't yet been handled entirely, so once you
pick a value for `USERNAME_IS_EMAIL` stick with it, or clear the database
inbetween.
******************
Running Adjutant
******************
Still in the Adjutant repo directory, you will now need to run the migrations
to build a basic database. By default this will use sqlite3.::
adjutant-api migrate
Now the that the migrations have been setup and the database built run the
service from the same directory, and it will revert to using the config file
at 'conf/conf.yaml'::
adjutant-api runserver 0.0.0.0:5050
.. note::
The port doesn't matter, but 5050 is a safe bet as it isn't used by any
other DevStack services and we can then safely assume you will be using
the same url for the rest of the guide.
Now you have Adjutant running, keep this window open as you'll want to keep
an eye on the console output.
**********************************
Add Adjutant to Keystone Catalogue
**********************************
In a new SSH termimal connected to your ubuntu VM setup your credentials as
environment variables::
export OS_USERNAME=admin
export OS_PASSWORD=openstack
export OS_PROJECT_NAME=demo
export OS_USER_DOMAIN_NAME=default
export OS_PROJECT_DOMAIN_NAME=default
export OS_AUTH_URL=http://localhost/identity
export OS_IDENTITY_API_VERSION=3
export OS_REGION_NAME=RegionOne
If you used the localrc file as given above, these should work.
Now setup a new service in Keystone for Adjutant and add an endpoint for it::
openstack service create registration --name adjutant
openstack endpoint create adjutant public http://0.0.0.0:5050/v1 --region RegionOne
**********************************
Adjutant specific roles
**********************************
To allow certain actions, Adjutant requires two special roles to exist.
You can create them as such::
openstack role create project_admin
openstack role create project_mod
Also because Adjutant by default also adds the role, you will want to create
'heat_stack_owner' which isn't by default present in devstack unless you
install Heat::
openstack role create heat_stack_owner
**********************************
Testing Adjutant via the CLI
**********************************
Now that the service is running, and the endpoint setup, you will want
to install the client and try talking to the service::
sudo pip install python-adjutantclient
In this case the client should be safe to install globally with sudo, but you
can also install it in the same virtualenv as Adjutant itself, or make a new
virtualenv.
Now lets check the status of the service::
openstack adjutant status
What you should get is::
{
"error_notifications": [],
"last_completed_task": null,
"last_created_task": null
}
Seeing as we've done nothing to the service yet this is the expected output.
To list the users on your current project (admin users are hidden by default)::
openstack project user list
The above action is only possibly for users with the following roles:
'admin', 'project_admin', 'project_mod'
Now lets try inviting a new user::
openstack project user invite bob@example.com project_admin
You will then get a note saying your invitation has been sent. You can list
your project users again with 'openstack project user list' to see your invite.
Now if you look at the log in the Adjutant terminal you should still
have open, you will see a print out of the email that would have been sent
to bob@example.com. In the email is a line that looks like this::
http://192.168.122.160:8080/token/e86cbfb187d34222ace90845f900893c
Normally that would direct the user to a Horizon dashboard page where they can
submit their password.
Since we don't have that running, your only option is to submit it via the CLI.
This is cumbersome, but doable. From that url in your Adjutant output, grab the
values after '.../token/'. That is bob's token. You can submit that via the
CLI::
openstack admin task token submit <token> <json_data>
openstack admin task token submit e86cbfb187d34222ace90845f900893c '{"password": "123456"}'
Now if you get the user list, you will see bob is now active::
openstack project user list
And also shows up as a user if you do::
openstack user list
And since you are an admin, you can even take a look at the tasks themselves::
openstack admin task list
The topmost one should be your invite, and if you then do a show using that
id you can see some details about it::
openstack admin task show <UUID>
**********************************
Setting Up Adjutant on Horizon
**********************************
Adjutant has a Horizon UI plugin, the code and setup instructions for it can
be found `here <https://github.com/openstack/adjutant-ui>`_
If you do set this up, you will want to edit the default Adjutant conf to so
that the TOKEN_SUBMISSION_URL is correctly set to point at your Horizon.

View File

@ -3,17 +3,87 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
####################################
Welcome to Adjutant's documentation!
====================================
####################################
.. toctree::
:maxdepth: 2
:caption: Contents:
:maxdepth: 1
design
devstack-guide
configuration
plugins
.. standard task views and actions
A basic workflow framework built using Django and Django-Rest-Framework to
help automate Admin tasks within an OpenStack cluster.
Indices and tables
==================
The goal of Adjutant is to provide a place and standard actions to fill in
functionality missing from Keystone, and allow for the easy addition of
business logic into more complex tasks, and connections with outside systems.
* :ref:`genindex`
* :ref:`search`
Tasks are built around three states of initial submission, admin approval and
token submission. All of the states are not always used in every task, but this
format allows the easy implementation of systems requiring approval and checks
final user data entry.
While this is a Django application, it does not follow the standard Django
folder structure because of certain packaging requirements. As such the project
does not have a manage.py file and must be installed via setup.py or pip.
Once installed, all the normal manage.py functions can be called directly on
the 'adjutant-api' commandline function.
The command ``tox -e venv {your commands}`` can be used and will setup a
virtual environment with all the required dependencies for you.
For example, running the server on port 5050 can be done with::
tox -e venv adjutant-api runserver 0.0.0.0:5050
************************
Client and UI Libraries
************************
Both a commandline/python and a horizon plugin exist for adjutant:
* `python-adjutantclient <https://github.com/openstack/python-adjutantclient>`_
* `adjutant-ui <https://github.com/openstack/adjutant-ui>`_
************************
Tests and Documentation
************************
Tests and documentation are managed by tox, they can be run simply with the
command ``tox``.
To run just action unit tests::
tox adjutant.actions
To run a single api test::
tox adjutant.api.v1.tests.test_api_taskview.TaskViewTests.test_duplicate_tasks_new_user
Tox will run the tests in Python 2.7, Python 3.5 and produce a coverage report.
Api reference can be generated with the command ``tox -e api-ref`` . This will
be placed in the ``api-ref/build`` directory, these docs can be generated with
the command ``tox -e docs``, these will be placed inside the ``doc/build``
directory.
************************
Contributing
************************
Bugs and blueprints for Adjutant, its ui and client are managed `here on
launchpad. <https://launchpad.net/adjutant>`_
Changes should be submitted through the OpenStack gerrit, the guide for
contributing to OpenStack projects is
`here <https://docs.openstack.org/contributor-guide/>`_ .

171
doc/source/plugins.rst Normal file
View File

@ -0,0 +1,171 @@
##############################
Creating Plugins for Adjutant
##############################
As Adjutant is built on top of Django, we've used parts of Django's installed
apps system to allow us a plugin mechanism that allows additional actions and
views to be brought in via external sources. This allows company specific or
deployer specific changes to easily live outside of the core service and simply
extend the core service where and when need.
An example of such a plugin is here:
https://github.com/catalyst/adjutant-odoo
New TaskViews should inherit from adjutant.api.v1.tasks.TaskView
can be registered as such::
from adjutant.api.v1.models import register_taskview_class,
from myplugin import tasks
register_taskview_class(r'^openstack/sign-up/?$', tasks.OpenStackSignUp)
Actions must be derived from adjutant.actions.v1.base.BaseAction and are
registered alongside their serializer::
from adjutant.actions.v1.models import register_action_class
register_action_class(NewClientSignUpAction, NewClientSignUpActionSerializer)
Serializers can inherit from either rest_framework.serializers.Serializer, or
the current serializers in adjutant.actions.v1.serializers.
A task must both be registered with a valid URL and specified in
ACTIVE_TASKVIEWS in the configuration to be accessible.
A new task from a plugin can effectively 'override' a default task by
registering with the same URL, and sharing the task type. However it must have
a different class name and the previous task must be removed from
ACTIVE_TASKVIEWS.
**********************
Building Taskviews
**********************
Examples of taskviews can be found in adjutant.api.v1.openstack
Minimally they can look like this::
class NewCreateProject(TaskView):
task_type = "new_create_project"
default_actions = ["NewProjectWithUserAction", ]
def post(self, request, format=None):
processed, status = self.process_actions(request)
errors = processed.get('errors', None)
if errors:
self.logger.info("(%s) - Validation errors with task." %
timezone.now())
return Response(errors, status=status)
return Response(response_dict, status=status)
Access can be restricted with the decorators mod_or_admin, project_admin and
admin decorators found in adjutant.api.utils. The request handlers are fairly
standard django view handlers and can execute any needed code. Additional
information for the actions should be placed in request.data.
*********************
Building Actions
*********************
Examples of actions can be found in adjutant.actions.v1.
Minimally actions should define their required fields and implement 3
functions::
required = [
'user_id',
'value1',
]
def _pre_approve(self):
self.perform_action('initial')
def _post_approve(self):
self.perform_action('token')
self.action.task.cache['value'] = self.value1
def _submit(self, data):
self.perform_action('completed')
self.add_note("Submit action performed")
Information set in the action task cache is available in email templates under
task.cache.value, and the action data is available in action.ActionName.value.
If a token email is needed to be sent the action should also implement::
def _get_email(self):
return self.keystone_user.email
If an action does not require outside approval this function should be run at
the pre-approval stage::
self.set_auto_approve(True)
If an action requires a token this should be set at the post approval stage::
self.action.need_token = True
self.set_token_fields(["confirm"])
All actions must be paired with a serializer to do basic data structure
checking, but should also check data validity during the action. Serializers
are django-rest-framework serializers, but there are also two base serializers
available in adjutant.actions.v1.serializers, BaseUserNameSerializer and
BaseUserIdSerializer.
All fields required for an action must be placed through the serializer
otherwise they will be inaccessible to the action.
Example::
from adjutant.actions.v1.serializers import BaseUserIdSerializer
from rest_framework import serializers
class NewActionSerializer(BaseUserIdSerializer):
value_1 = serializers.CharField()
******************************
Building Notification Engines
******************************
Notification Engines can also be added through a plugin::
from adjutant.notifcations.models import NotificationEngine
from django.conf import settings
class NewNotificationEngine(NotificationEngine):
def _notify(self, task, notification):
if self.conf.get('do_this_thing'):
# do something with the task and notification
settings.NOTIFICATION_ENGINES.update(
{'NewNotificationEngine': NewNotificationEngine})
They should be then refered to in conf.yaml::
TASK_SETTINGS:
signup:
notifications:
NewNotificationEngine:
standard:
do_this_thing: True
error:
do_this_thing: False
*************************************************
Using the Identity Manager, and Openstack Clients
*************************************************
The Identity Manager is designed to replace access to the Keystone Client. It
can be imported from ``adjutant.actions.user_store.IdentityManager`` .
Functions for access to some of the other Openstack Clients are in
``adjutant.actions.openstack_clients``.

View File

@ -2,3 +2,7 @@ mock==1.2.0
flake8>=3.0.4
coverage>=4.4.1
sphinx!=1.6.1,>=1.5.1
os-api-ref>=1.0.0 # Apache-2.0
sphinx-catalystcloud-theme>=0.0.1 # MIT
sphinx-rtd-theme>=0.2.4
doc8

10
tox.ini
View File

@ -27,7 +27,15 @@ commands =
commands = {posargs}
[testenv:docs]
commands = python setup.py build_sphinx
commands =
doc8 doc/source
python setup.py build_sphinx
[testenv:api-ref]
commands =
doc8 api-ref/source
python setup.py build_sphinx -s api-ref/source/ --build-dir api-ref/build/
[flake8]
ignore = D100,D101,D102,D103,D104,D105,D200,D203,D202,D204,D205,D208,D400,D401