mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 14:57:39 -02:30
Start of API guide + some whitespace removal.
This commit is contained in:
15
TODO.md
15
TODO.md
@@ -6,7 +6,7 @@ TODO items for ansible commander
|
|||||||
NEXT STEPS
|
NEXT STEPS
|
||||||
|
|
||||||
* Michael -- flesh out REST model & tests for everything, API guide
|
* Michael -- flesh out REST model & tests for everything, API guide
|
||||||
* Chris ---- document celery devs stuff.
|
* Chris ---- document celery devs stuff.
|
||||||
* Chris ---- inventory script is done, integrate with celery system, make celery REST triggerable, callback plugin
|
* Chris ---- inventory script is done, integrate with celery system, make celery REST triggerable, callback plugin
|
||||||
* Michael -- enhance callback plugin to provide runner and playbook context
|
* Michael -- enhance callback plugin to provide runner and playbook context
|
||||||
|
|
||||||
@@ -14,23 +14,18 @@ LATER
|
|||||||
-----
|
-----
|
||||||
|
|
||||||
* UI layer
|
* UI layer
|
||||||
* CLI client
|
* CLI client (and libs)
|
||||||
* clean up initial migrations
|
* clean up initial migrations
|
||||||
* init scripts, Apache proxying, etc
|
* init scripts, Apache proxying, etc
|
||||||
* does inventory script need any caching
|
* does inventory script need any caching
|
||||||
* credentials subsystem -- let app hold on to keys for user
|
* credentials subsystem -- let app hold on to keys for user
|
||||||
|
|
||||||
TWEAKS
|
TWEAKS/ASSORTED
|
||||||
------
|
---------------
|
||||||
|
|
||||||
* add a synthetic bit to the organization to indicate if the current user is an administator of it
|
* add a synthetic bit to the organization to indicate if the current user is an administator of it
|
||||||
* add related resources to the user object to they can quickly find organizations they are an admin of, organizations, teams, projects, etc
|
|
||||||
|
|
||||||
ASSORTED
|
|
||||||
-------
|
|
||||||
|
|
||||||
* uniqueness checks for playbook paths?
|
* uniqueness checks for playbook paths?
|
||||||
* allow multiple playbook execution types per project, different tag choices (Projects/Runnables?)
|
* allow multiple playbook execution types per project, different --tag choices, different --limit choices (maybe just free form in the job for now?)
|
||||||
* permissions infrastructure about who can kick off what kind of jobs
|
* permissions infrastructure about who can kick off what kind of jobs
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
310
docs/api_guide.md
Normal file
310
docs/api_guide.md
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
API Guide
|
||||||
|
=========
|
||||||
|
|
||||||
|
This document describes how to do nearly everything in Ansible commander via the REST API.
|
||||||
|
|
||||||
|
To begin, you should have set up Ansible Commander (see SETUP.md) and created at least one Django super user.
|
||||||
|
To do this from the development project, run "make adduser".
|
||||||
|
|
||||||
|
For the purposes of this guide, we will assume the superuser username and password is admin/admin.
|
||||||
|
|
||||||
|
Conventions
|
||||||
|
===========
|
||||||
|
|
||||||
|
* Ansible Commander uses a standard REST API, rooted at /api/v1/ on the server.
|
||||||
|
* All data is JSON. You may have to specify the content/type on POST or PUT requests accordingly.
|
||||||
|
* All URIs should end in "/" or you will get a 301 redirect.
|
||||||
|
|
||||||
|
Time to get started.
|
||||||
|
|
||||||
|
Aside: Django Admin
|
||||||
|
===================
|
||||||
|
|
||||||
|
When working with REST objects it is often helpful to use the Django admin interface to pre-create objects,
|
||||||
|
and then retrieve collections of objects with GET request to look at their structure. This will help
|
||||||
|
users understand how to create new objects as well as how to edit them.
|
||||||
|
|
||||||
|
Aside: Read Only Fields
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Certain fields in the REST API are marked read only. These usually include the URL of a resource, the ID,
|
||||||
|
and occasionally some internal fields. For instance, the 'created_by' attribute of each object indicates
|
||||||
|
which user created the resource, and can not be edited.
|
||||||
|
|
||||||
|
Working With Users
|
||||||
|
==================
|
||||||
|
|
||||||
|
After a fresh install, there should be no organizations and only the superuser.
|
||||||
|
|
||||||
|
Since we're explaining our first resource, we'll be a bit more verbose with the user
|
||||||
|
information than with some other REST resources.
|
||||||
|
|
||||||
|
Listing Users
|
||||||
|
-------------
|
||||||
|
|
||||||
|
We will pay special attention to this first access request, as any list view looks basically
|
||||||
|
the same.
|
||||||
|
|
||||||
|
curl http://admin:admin@127.0.0.1:8013/api/v1/users/
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1, "next": null, "previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"url": "/api/v1/users/1/",
|
||||||
|
"id": 1, "username": "admin",
|
||||||
|
"first_name": "",
|
||||||
|
"last_name": "",
|
||||||
|
"email": "root@localhost.localdomain",
|
||||||
|
"is_active": true,
|
||||||
|
"is_superuser": true,
|
||||||
|
"related": {
|
||||||
|
"admin_of_organizations": "/api/v1/users/1/admin_of_organizations/",
|
||||||
|
"organizations": "/api/v1/users/1/organizations/",
|
||||||
|
"teams": "/api/v1/users/1/teams/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Notice that the requests are paginated, so you may have to access additional URLs if 'previous' and 'next'
|
||||||
|
are set to access the full collection.
|
||||||
|
|
||||||
|
You will also see 'related' URLs on each object, which tell you from any point in the API what resources
|
||||||
|
you can get to from a particular resource.
|
||||||
|
|
||||||
|
Adding Users
|
||||||
|
------------
|
||||||
|
|
||||||
|
To add a user, just POST to the users collection:
|
||||||
|
|
||||||
|
curl -X POST -d @input http://admin:admin@127.0.0.1:8013/api/v1/users/
|
||||||
|
|
||||||
|
{
|
||||||
|
"username" : "foo", "password: "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
And optionally specify other fields. Super users or organization admins can create users. We'll learn more about
|
||||||
|
organizations shortly!
|
||||||
|
|
||||||
|
Getting a User
|
||||||
|
--------------
|
||||||
|
|
||||||
|
As you would expect, a user, superuser, or organization admin can retrieve the record of a user directly via:
|
||||||
|
|
||||||
|
curl http://admin:admin@127.0.0.1/api/v1/users/1/
|
||||||
|
|
||||||
|
And recieves something very much like this:
|
||||||
|
|
||||||
|
{
|
||||||
|
"url": "/api/v1/users/1/",
|
||||||
|
"id": 1, "username": "admin",
|
||||||
|
"first_name": "",
|
||||||
|
"last_name": "",
|
||||||
|
"email": "root@localhost.localdomain",
|
||||||
|
"is_active": true,
|
||||||
|
"is_superuser": true,
|
||||||
|
"related": {
|
||||||
|
"admin_of_organizations": "/api/v1/users/1/admin_of_organizations/",
|
||||||
|
"organizations": "/api/v1/users/1/organizations/",
|
||||||
|
"teams": "/api/v1/users/1/teams/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Deleting Users
|
||||||
|
--------------
|
||||||
|
|
||||||
|
In ansible commander, nothing is truly deleted, objects are only set inactive and renamed. This allows recovery
|
||||||
|
of those objects using the Django administrative interface or the database command line.
|
||||||
|
|
||||||
|
To delete an object, just DELETE to a resource, like so:
|
||||||
|
|
||||||
|
curl -X DELETE http://admin:admin@127.0.0.1:8013/api/v1/users/2/
|
||||||
|
|
||||||
|
Only a user's organization admins, or a superuser, can mark a user as deleted.
|
||||||
|
|
||||||
|
Updating Users
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A user may update only his password, and an organization admin or superuser can update a lot more about the
|
||||||
|
user. To update a user, just PUT to the resource, like so:
|
||||||
|
|
||||||
|
curl -X PUT -d @input http://admin:admin@127.0.0.1:8013/api/v1/users/2/
|
||||||
|
|
||||||
|
{
|
||||||
|
'password': 'new_password'
|
||||||
|
}
|
||||||
|
|
||||||
|
Organizations
|
||||||
|
=============
|
||||||
|
|
||||||
|
Organizations are the basic unit of multi-tenancy and access in Ansible Commander.
|
||||||
|
|
||||||
|
At the basic level, when a user is created, he should then be added to one or more organziations.
|
||||||
|
|
||||||
|
These may represent concepts like "Engineering" or "Finance".
|
||||||
|
|
||||||
|
Under each organization, there will also be multiple teams, like "Engineering QA Team", or "Engineering Release Team".
|
||||||
|
|
||||||
|
Organizations also have admin users (which can be assigned by the super user or other organization admins) whom also
|
||||||
|
have most of the powers of a superuser to users inside that organization.
|
||||||
|
|
||||||
|
Listing Organizations
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
GET /api/v1/organizations/
|
||||||
|
|
||||||
|
Editing Organizations
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
PUT /api/v1/organizations/1/
|
||||||
|
|
||||||
|
Adding users to an organization
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
POST the data you recieved from GET /api/v1/users/X/ to /api/v1/organizations/Y/organizations/
|
||||||
|
|
||||||
|
Similarly, you can list users of an organization if you are an org admin or superuser with:
|
||||||
|
|
||||||
|
GET /api/v1/organizations/X/users/
|
||||||
|
|
||||||
|
Adding admins to an organization
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
POST the data you recieved from GET /api/v1/users/X/ to /api/v1/organizations/Y/admins/
|
||||||
|
|
||||||
|
Similarly, you can list admins of an organization if you are an org admin or superuser, with:
|
||||||
|
|
||||||
|
GET /api/v1/organizations/X/admins/
|
||||||
|
|
||||||
|
Note that adding a user as an admin does not automatically add them as a regular organization user. If the user
|
||||||
|
is to be a member of any project teams, add them both to the admin list and the users list for a given organization.
|
||||||
|
|
||||||
|
Adding projects to an organization
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
An organization may also have projects, which would be a concept like "ACME Corp Application" or "Application Foo".
|
||||||
|
|
||||||
|
Projects are covered in a later section, but are added to organizations, once created, by posting the record
|
||||||
|
recieved from /api/v1/projects/X/ to /api/v1/organizations/Y/.
|
||||||
|
|
||||||
|
Removing users, admins, and projects from organizations
|
||||||
|
-------------------------------------------------------
|
||||||
|
|
||||||
|
Deleting from sub-collections in Ansible Commander is not handled by a DELETE verb, but by a modified POST.
|
||||||
|
|
||||||
|
To remove a user ID X from an organization Y, simply POST the following to the collection /api/v1/organizations/Y/users/:
|
||||||
|
|
||||||
|
{ 'id' : X, 'disassociate' : 1 }
|
||||||
|
|
||||||
|
A similar post can also be made to /api/v1/organizations/Y/admins/ to remove an admin user or
|
||||||
|
/api/v1/organizations/Y/projects/.
|
||||||
|
|
||||||
|
The same removal facility is available elsewhere in Ansible commander for most related resources.
|
||||||
|
|
||||||
|
Projects
|
||||||
|
========
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
Teams
|
||||||
|
=====
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
Inventories
|
||||||
|
===========
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
Adding Inventories
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Adding Hosts
|
||||||
|
------------
|
||||||
|
|
||||||
|
Adding Groups
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Editing Hosts or Groups
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Adding Host Variables
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Adding Group Variables
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Editing Host or Group Variables
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Removing Host or Group Variables
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Adding Sub Groups
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Removing Sub Groups
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Access Via Inventory Script
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Credentials
|
||||||
|
===========
|
||||||
|
|
||||||
|
Creating credentials
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Adding credentials to users, teams, or projects
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Seeing what credentials a user has access to
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
Launch Jobs
|
||||||
|
===========
|
||||||
|
|
||||||
|
Submitting A Launch Job
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Getting Job Results
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Browsing Log Data
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
|
||||||
|
Tags
|
||||||
|
====
|
||||||
|
|
||||||
|
Adding Tags
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Finding Tagged Resources
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Editing/Removing Tags
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Permissions
|
||||||
|
===========
|
||||||
|
|
||||||
|
Permissions can be granted to users or teams and indicate they are allowed to use
|
||||||
|
certain combinations of credentials, inventories to deploy to certain projects.
|
||||||
|
|
||||||
|
For instance, a permission may define that the QA Team can use certain credentials to push
|
||||||
|
to a Test Inventory, while another permission may indicate the Release Engineering team can
|
||||||
|
push a different project with different credentials to a Production inventory source.
|
||||||
|
|
||||||
|
Audit Trails
|
||||||
|
============
|
||||||
|
|
||||||
|
Viewing object audit trails
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -37,10 +37,10 @@ class EditHelper(object):
|
|||||||
def illegal_changes(cls, request, obj, model_class):
|
def illegal_changes(cls, request, obj, model_class):
|
||||||
''' have any illegal changes been made (for a PUT request)? '''
|
''' have any illegal changes been made (for a PUT request)? '''
|
||||||
can_admin = model_class.can_user_administrate(request.user, obj)
|
can_admin = model_class.can_user_administrate(request.user, obj)
|
||||||
if (not can_admin) or (can_admin == 'partial'):
|
if (not can_admin) or (can_admin == 'partial'):
|
||||||
check_fields = model_class.admin_only_edit_fields
|
check_fields = model_class.admin_only_edit_fields
|
||||||
changed = cls.fields_changed(check_fields, obj, request.DATA)
|
changed = cls.fields_changed(check_fields, obj, request.DATA)
|
||||||
if len(changed.keys()) > 0:
|
if len(changed.keys()) > 0:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ class EditHelper(object):
|
|||||||
''' return the fields that would be changed by a prospective PUT operation '''
|
''' return the fields that would be changed by a prospective PUT operation '''
|
||||||
changed = {}
|
changed = {}
|
||||||
for f in fields:
|
for f in fields:
|
||||||
left = getattr(obj, f, None)
|
left = getattr(obj, f, None)
|
||||||
if left is None:
|
if left is None:
|
||||||
raise Exception("internal error, %s is not a member of %s" % (f, obj))
|
raise Exception("internal error, %s is not a member of %s" % (f, obj))
|
||||||
right = data.get(f, None)
|
right = data.get(f, None)
|
||||||
@@ -74,11 +74,11 @@ class UserHelper(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_read(cls, user, obj):
|
def can_user_read(cls, user, obj):
|
||||||
''' a user can be read if they are on the same team or can be administrated '''
|
''' a user can be read if they are on the same team or can be administrated '''
|
||||||
matching_teams = user.teams.filter(users__in = [ user ]).count()
|
matching_teams = user.teams.filter(users__in = [ user ]).count()
|
||||||
return matching_teams or cls.can_user_administrate(user, obj)
|
return matching_teams or cls.can_user_administrate(user, obj)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_delete(cls, user, obj):
|
def can_user_delete(cls, user, obj):
|
||||||
if user.is_superuser:
|
if user.is_superuser:
|
||||||
return True
|
return True
|
||||||
@@ -87,8 +87,8 @@ class UserHelper(object):
|
|||||||
|
|
||||||
|
|
||||||
class CommonModel(models.Model):
|
class CommonModel(models.Model):
|
||||||
'''
|
'''
|
||||||
common model for all object types that have these standard fields
|
common model for all object types that have these standard fields
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -98,23 +98,23 @@ class CommonModel(models.Model):
|
|||||||
description = models.TextField(blank=True, default='')
|
description = models.TextField(blank=True, default='')
|
||||||
created_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, related_name='%s(class)s_created') # not blank=False on purpose for admin!
|
created_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, related_name='%s(class)s_created') # not blank=False on purpose for admin!
|
||||||
creation_date = models.DateField(auto_now_add=True)
|
creation_date = models.DateField(auto_now_add=True)
|
||||||
tags = models.ManyToManyField('Tag', related_name='%(class)s_by_tag', blank=True)
|
tags = models.ManyToManyField('Tag', related_name='%(class)s_by_tag', blank=True)
|
||||||
audit_trail = models.ManyToManyField('AuditTrail', related_name='%(class)s_by_audit_trail', blank=True)
|
audit_trail = models.ManyToManyField('AuditTrail', related_name='%(class)s_by_audit_trail', blank=True)
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return unicode(self.name)
|
return unicode(self.name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_administrate(cls, user, obj):
|
def can_user_administrate(cls, user, obj):
|
||||||
# FIXME: do we want a seperate method to override put? This is kind of general purpose
|
# FIXME: do we want a seperate method to override put? This is kind of general purpose
|
||||||
raise exceptions.NotImplementedError()
|
raise exceptions.NotImplementedError()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_delete(cls, user, obj):
|
def can_user_delete(cls, user, obj):
|
||||||
raise exceptions.NotImplementedError()
|
raise exceptions.NotImplementedError()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_read(cls, user, obj):
|
def can_user_read(cls, user, obj):
|
||||||
raise exceptions.NotImplementedError()
|
raise exceptions.NotImplementedError()
|
||||||
|
|
||||||
@@ -130,16 +130,16 @@ class CommonModel(models.Model):
|
|||||||
return False
|
return False
|
||||||
rc = cls.can_user_administrate(user, obj)
|
rc = cls.can_user_administrate(user, obj)
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_unattach(cls, user, obj, sub_obj, relationship):
|
def can_user_unattach(cls, user, obj, sub_obj, relationship):
|
||||||
return cls.can_user_administrate(user, obj)
|
return cls.can_user_administrate(user, obj)
|
||||||
|
|
||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
'''
|
|
||||||
any type of object can be given a search tag
|
|
||||||
'''
|
'''
|
||||||
|
any type of object can be given a search tag
|
||||||
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
@@ -162,15 +162,15 @@ class Tag(models.Model):
|
|||||||
# anybody can read tags, we won't show much detail other than the names
|
# anybody can read tags, we won't show much detail other than the names
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class AuditTrail(models.Model):
|
class AuditTrail(models.Model):
|
||||||
'''
|
|
||||||
changing any object records the change
|
|
||||||
'''
|
'''
|
||||||
|
changing any object records the change
|
||||||
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
resource_type = models.CharField(max_length=64)
|
resource_type = models.CharField(max_length=64)
|
||||||
modified_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, blank=True)
|
modified_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, blank=True)
|
||||||
delta = models.TextField() # FIXME: switch to JSONField
|
delta = models.TextField() # FIXME: switch to JSONField
|
||||||
@@ -181,10 +181,10 @@ class AuditTrail(models.Model):
|
|||||||
tag = models.ForeignKey('Tag', on_delete=SET_NULL, null=True, blank=True)
|
tag = models.ForeignKey('Tag', on_delete=SET_NULL, null=True, blank=True)
|
||||||
|
|
||||||
class Organization(CommonModel):
|
class Organization(CommonModel):
|
||||||
'''
|
'''
|
||||||
organizations are the basic unit of multi-tenancy divisions
|
organizations are the basic unit of multi-tenancy divisions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
@@ -196,11 +196,11 @@ class Organization(CommonModel):
|
|||||||
import lib.urls
|
import lib.urls
|
||||||
return reverse(lib.urls.views_OrganizationsDetail, args=(self.pk,))
|
return reverse(lib.urls.views_OrganizationsDetail, args=(self.pk,))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_delete(cls, user, obj):
|
def can_user_delete(cls, user, obj):
|
||||||
return user in obj.admins.all()
|
return user in obj.admins.all()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_administrate(cls, user, obj):
|
def can_user_administrate(cls, user, obj):
|
||||||
# FIXME: super user checks should be higher up so we don't have to repeat them
|
# FIXME: super user checks should be higher up so we don't have to repeat them
|
||||||
if user.is_superuser:
|
if user.is_superuser:
|
||||||
@@ -210,11 +210,11 @@ class Organization(CommonModel):
|
|||||||
rc = user in obj.admins.all()
|
rc = user in obj.admins.all()
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_read(cls, user, obj):
|
def can_user_read(cls, user, obj):
|
||||||
return cls.can_user_administrate(user,obj) or user in obj.users.all()
|
return cls.can_user_administrate(user,obj) or user in obj.users.all()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_delete(cls, user, obj):
|
def can_user_delete(cls, user, obj):
|
||||||
return cls.can_user_administrate(user, obj)
|
return cls.can_user_administrate(user, obj)
|
||||||
|
|
||||||
@@ -222,15 +222,15 @@ class Organization(CommonModel):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class Inventory(CommonModel):
|
class Inventory(CommonModel):
|
||||||
'''
|
'''
|
||||||
an inventory source contains lists and hosts.
|
an inventory source contains lists and hosts.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
verbose_name_plural = _('inventories')
|
verbose_name_plural = _('inventories')
|
||||||
|
|
||||||
organization = models.ForeignKey(Organization, null=True, on_delete=SET_NULL, related_name='inventories')
|
organization = models.ForeignKey(Organization, null=True, on_delete=SET_NULL, related_name='inventories')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
if self.organization:
|
if self.organization:
|
||||||
@@ -242,7 +242,7 @@ class Host(CommonModel):
|
|||||||
'''
|
'''
|
||||||
A managed node
|
A managed node
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
@@ -255,7 +255,7 @@ class Group(CommonModel):
|
|||||||
'''
|
'''
|
||||||
A group of managed nodes. May belong to multiple groups
|
A group of managed nodes. May belong to multiple groups
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
@@ -272,8 +272,8 @@ class Group(CommonModel):
|
|||||||
class VariableData(CommonModel):
|
class VariableData(CommonModel):
|
||||||
'''
|
'''
|
||||||
A set of host or group variables
|
A set of host or group variables
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
verbose_name_plural = _('variable data')
|
verbose_name_plural = _('variable data')
|
||||||
@@ -291,7 +291,7 @@ class Credential(CommonModel):
|
|||||||
Usually this is a SSH key location, and possibly an unlock password.
|
Usually this is a SSH key location, and possibly an unlock password.
|
||||||
If used with sudo, a sudo password should be set if required.
|
If used with sudo, a sudo password should be set if required.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
@@ -304,25 +304,25 @@ class Credential(CommonModel):
|
|||||||
ssh_key_unlock = models.CharField(blank=True, default='', max_length=1024)
|
ssh_key_unlock = models.CharField(blank=True, default='', max_length=1024)
|
||||||
ssh_password = models.CharField(blank=True, default='', max_length=1024)
|
ssh_password = models.CharField(blank=True, default='', max_length=1024)
|
||||||
sudo_password = models.CharField(blank=True, default='', max_length=1024)
|
sudo_password = models.CharField(blank=True, default='', max_length=1024)
|
||||||
|
|
||||||
|
|
||||||
class Team(CommonModel):
|
class Team(CommonModel):
|
||||||
'''
|
'''
|
||||||
A team is a group of users that work on common projects.
|
A team is a group of users that work on common projects.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
projects = models.ManyToManyField('Project', blank=True, related_name='teams')
|
projects = models.ManyToManyField('Project', blank=True, related_name='teams')
|
||||||
users = models.ManyToManyField('auth.User', blank=True, related_name='teams')
|
users = models.ManyToManyField('auth.User', blank=True, related_name='teams')
|
||||||
organizations = models.ManyToManyField('Organization', related_name='teams')
|
organizations = models.ManyToManyField('Organization', related_name='teams')
|
||||||
|
|
||||||
class Project(CommonModel):
|
class Project(CommonModel):
|
||||||
'''
|
'''
|
||||||
A project represents a playbook git repo that can access a set of inventories
|
A project represents a playbook git repo that can access a set of inventories
|
||||||
'''
|
'''
|
||||||
|
|
||||||
inventories = models.ManyToManyField('Inventory', blank=True, related_name='projects')
|
inventories = models.ManyToManyField('Inventory', blank=True, related_name='projects')
|
||||||
local_repository = models.CharField(max_length=1024)
|
local_repository = models.CharField(max_length=1024)
|
||||||
scm_type = models.CharField(max_length=64)
|
scm_type = models.CharField(max_length=64)
|
||||||
@@ -332,7 +332,7 @@ class Project(CommonModel):
|
|||||||
import lib.urls
|
import lib.urls
|
||||||
return reverse(lib.urls.views_ProjectsDetail, args=(self.pk,))
|
return reverse(lib.urls.views_ProjectsDetail, args=(self.pk,))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_administrate(cls, user, obj):
|
def can_user_administrate(cls, user, obj):
|
||||||
if user.is_superuser:
|
if user.is_superuser:
|
||||||
return True
|
return True
|
||||||
@@ -357,7 +357,7 @@ class Permission(CommonModel):
|
|||||||
'''
|
'''
|
||||||
A permission allows a user, project, or team to be able to use an inventory source.
|
A permission allows a user, project, or team to be able to use an inventory source.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
@@ -369,10 +369,10 @@ class Permission(CommonModel):
|
|||||||
# TODO: other job types (later)
|
# TODO: other job types (later)
|
||||||
|
|
||||||
class LaunchJob(CommonModel):
|
class LaunchJob(CommonModel):
|
||||||
'''
|
|
||||||
a launch job is a request to apply a project to an inventory source with a given credential
|
|
||||||
'''
|
'''
|
||||||
|
a launch job is a request to apply a project to an inventory source with a given credential
|
||||||
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
@@ -387,18 +387,18 @@ class LaunchJob(CommonModel):
|
|||||||
return run_launch_job.delay(self.pk)
|
return run_launch_job.delay(self.pk)
|
||||||
|
|
||||||
# project has one default playbook but really should have a list of playbooks and flags ...
|
# project has one default playbook but really should have a list of playbooks and flags ...
|
||||||
|
|
||||||
|
|
||||||
# ENOUGH_TO_RUN_DJANGO=foo ACOM_INVENTORY_ID=<pk> ansible-playbook <path to project selected playbook.yml> -i ansible-commander-inventory.py
|
# ENOUGH_TO_RUN_DJANGO=foo ACOM_INVENTORY_ID=<pk> ansible-playbook <path to project selected playbook.yml> -i ansible-commander-inventory.py
|
||||||
# ^-- this is a hard coded path
|
# ^-- this is a hard coded path
|
||||||
# ssh-agent bash
|
# ssh-agent bash
|
||||||
# ssh-add ... < key entry
|
# ssh-add ... < key entry
|
||||||
#
|
#
|
||||||
# inventory script I can write, and will use ACOM_INVENTORY_ID
|
# inventory script I can write, and will use ACOM_INVENTORY_ID
|
||||||
#
|
|
||||||
#
|
#
|
||||||
# playbook in source control is already on the disk
|
#
|
||||||
|
# playbook in source control is already on the disk
|
||||||
|
|
||||||
# job_type:
|
# job_type:
|
||||||
# run, check -- enough for now, more initially
|
# run, check -- enough for now, more initially
|
||||||
# if check, add "--check" to parameters
|
# if check, add "--check" to parameters
|
||||||
@@ -408,7 +408,7 @@ class LaunchJob(CommonModel):
|
|||||||
# self.context.runner
|
# self.context.runner
|
||||||
# and the callback will read the environment for ACOM_CELERY_JOB_ID or similar
|
# and the callback will read the environment for ACOM_CELERY_JOB_ID or similar
|
||||||
# and log tons into the database
|
# and log tons into the database
|
||||||
|
|
||||||
# we'll also log stdout/stderr somewhere for debugging
|
# we'll also log stdout/stderr somewhere for debugging
|
||||||
|
|
||||||
# the ansible commander setup instructions will include installing the database logging callback
|
# the ansible commander setup instructions will include installing the database logging callback
|
||||||
@@ -423,7 +423,7 @@ class LaunchJob(CommonModel):
|
|||||||
# TODO: Events
|
# TODO: Events
|
||||||
|
|
||||||
class LaunchJobStatus(CommonModel):
|
class LaunchJobStatus(CommonModel):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
verbose_name_plural = _('launch job statuses')
|
verbose_name_plural = _('launch job statuses')
|
||||||
@@ -431,7 +431,7 @@ class LaunchJobStatus(CommonModel):
|
|||||||
launch_job = models.ForeignKey('LaunchJob', null=True, on_delete=SET_NULL, related_name='launch_job_statuses')
|
launch_job = models.ForeignKey('LaunchJob', null=True, on_delete=SET_NULL, related_name='launch_job_statuses')
|
||||||
status = models.IntegerField()
|
status = models.IntegerField()
|
||||||
result_data = models.TextField()
|
result_data = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
# TODO: reporting (MPD)
|
# TODO: reporting (MPD)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user