mirror of
https://github.com/ansible/awx.git
synced 2026-01-10 15:32:07 -03:30
Start of API guide + some whitespace removal.
This commit is contained in:
parent
175f3c23da
commit
44372ed191
15
TODO.md
15
TODO.md
@ -6,7 +6,7 @@ TODO items for ansible commander
|
||||
NEXT STEPS
|
||||
|
||||
* 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
|
||||
* Michael -- enhance callback plugin to provide runner and playbook context
|
||||
|
||||
@ -14,23 +14,18 @@ LATER
|
||||
-----
|
||||
|
||||
* UI layer
|
||||
* CLI client
|
||||
* CLI client (and libs)
|
||||
* clean up initial migrations
|
||||
* init scripts, Apache proxying, etc
|
||||
* does inventory script need any caching
|
||||
* 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 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?
|
||||
* 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
|
||||
|
||||
|
||||
|
||||
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):
|
||||
''' have any illegal changes been made (for a PUT request)? '''
|
||||
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
|
||||
changed = cls.fields_changed(check_fields, obj, request.DATA)
|
||||
if len(changed.keys()) > 0:
|
||||
if len(changed.keys()) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -49,7 +49,7 @@ class EditHelper(object):
|
||||
''' return the fields that would be changed by a prospective PUT operation '''
|
||||
changed = {}
|
||||
for f in fields:
|
||||
left = getattr(obj, f, None)
|
||||
left = getattr(obj, f, None)
|
||||
if left is None:
|
||||
raise Exception("internal error, %s is not a member of %s" % (f, obj))
|
||||
right = data.get(f, None)
|
||||
@ -74,11 +74,11 @@ class UserHelper(object):
|
||||
|
||||
@classmethod
|
||||
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()
|
||||
return matching_teams or cls.can_user_administrate(user, obj)
|
||||
|
||||
@classmethod
|
||||
|
||||
@classmethod
|
||||
def can_user_delete(cls, user, obj):
|
||||
if user.is_superuser:
|
||||
return True
|
||||
@ -87,8 +87,8 @@ class UserHelper(object):
|
||||
|
||||
|
||||
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:
|
||||
@ -98,23 +98,23 @@ class CommonModel(models.Model):
|
||||
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!
|
||||
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)
|
||||
active = models.BooleanField(default=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.name)
|
||||
|
||||
@classmethod
|
||||
@classmethod
|
||||
def can_user_administrate(cls, user, obj):
|
||||
# FIXME: do we want a seperate method to override put? This is kind of general purpose
|
||||
raise exceptions.NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
@classmethod
|
||||
def can_user_delete(cls, user, obj):
|
||||
raise exceptions.NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
@classmethod
|
||||
def can_user_read(cls, user, obj):
|
||||
raise exceptions.NotImplementedError()
|
||||
|
||||
@ -130,16 +130,16 @@ class CommonModel(models.Model):
|
||||
return False
|
||||
rc = cls.can_user_administrate(user, obj)
|
||||
return rc
|
||||
|
||||
|
||||
@classmethod
|
||||
def can_user_unattach(cls, user, obj, sub_obj, relationship):
|
||||
return cls.can_user_administrate(user, obj)
|
||||
|
||||
|
||||
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:
|
||||
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
|
||||
return True
|
||||
|
||||
|
||||
|
||||
class AuditTrail(models.Model):
|
||||
'''
|
||||
changing any object records the change
|
||||
'''
|
||||
|
||||
changing any object records the change
|
||||
'''
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
|
||||
|
||||
resource_type = models.CharField(max_length=64)
|
||||
modified_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, blank=True)
|
||||
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)
|
||||
|
||||
class Organization(CommonModel):
|
||||
'''
|
||||
organizations are the basic unit of multi-tenancy divisions
|
||||
'''
|
||||
|
||||
'''
|
||||
organizations are the basic unit of multi-tenancy divisions
|
||||
'''
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
|
||||
@ -196,11 +196,11 @@ class Organization(CommonModel):
|
||||
import lib.urls
|
||||
return reverse(lib.urls.views_OrganizationsDetail, args=(self.pk,))
|
||||
|
||||
@classmethod
|
||||
@classmethod
|
||||
def can_user_delete(cls, user, obj):
|
||||
return user in obj.admins.all()
|
||||
|
||||
@classmethod
|
||||
@classmethod
|
||||
def can_user_administrate(cls, user, obj):
|
||||
# FIXME: super user checks should be higher up so we don't have to repeat them
|
||||
if user.is_superuser:
|
||||
@ -210,11 +210,11 @@ class Organization(CommonModel):
|
||||
rc = user in obj.admins.all()
|
||||
return rc
|
||||
|
||||
@classmethod
|
||||
@classmethod
|
||||
def can_user_read(cls, user, obj):
|
||||
return cls.can_user_administrate(user,obj) or user in obj.users.all()
|
||||
|
||||
@classmethod
|
||||
@classmethod
|
||||
def can_user_delete(cls, user, obj):
|
||||
return cls.can_user_administrate(user, obj)
|
||||
|
||||
@ -222,15 +222,15 @@ class Organization(CommonModel):
|
||||
return self.name
|
||||
|
||||
class Inventory(CommonModel):
|
||||
'''
|
||||
'''
|
||||
an inventory source contains lists and hosts.
|
||||
'''
|
||||
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
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):
|
||||
if self.organization:
|
||||
@ -242,7 +242,7 @@ class Host(CommonModel):
|
||||
'''
|
||||
A managed node
|
||||
'''
|
||||
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
|
||||
@ -255,7 +255,7 @@ class Group(CommonModel):
|
||||
'''
|
||||
A group of managed nodes. May belong to multiple groups
|
||||
'''
|
||||
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
|
||||
@ -272,8 +272,8 @@ class Group(CommonModel):
|
||||
class VariableData(CommonModel):
|
||||
'''
|
||||
A set of host or group variables
|
||||
'''
|
||||
|
||||
'''
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
verbose_name_plural = _('variable data')
|
||||
@ -291,7 +291,7 @@ class Credential(CommonModel):
|
||||
Usually this is a SSH key location, and possibly an unlock password.
|
||||
If used with sudo, a sudo password should be set if required.
|
||||
'''
|
||||
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
|
||||
@ -304,25 +304,25 @@ class Credential(CommonModel):
|
||||
ssh_key_unlock = 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)
|
||||
|
||||
|
||||
|
||||
class Team(CommonModel):
|
||||
'''
|
||||
A team is a group of users that work on common projects.
|
||||
'''
|
||||
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
|
||||
|
||||
projects = models.ManyToManyField('Project', blank=True, related_name='teams')
|
||||
users = models.ManyToManyField('auth.User', blank=True, related_name='teams')
|
||||
organizations = models.ManyToManyField('Organization', related_name='teams')
|
||||
|
||||
class Project(CommonModel):
|
||||
'''
|
||||
'''
|
||||
A project represents a playbook git repo that can access a set of inventories
|
||||
'''
|
||||
|
||||
'''
|
||||
|
||||
inventories = models.ManyToManyField('Inventory', blank=True, related_name='projects')
|
||||
local_repository = models.CharField(max_length=1024)
|
||||
scm_type = models.CharField(max_length=64)
|
||||
@ -332,7 +332,7 @@ class Project(CommonModel):
|
||||
import lib.urls
|
||||
return reverse(lib.urls.views_ProjectsDetail, args=(self.pk,))
|
||||
|
||||
@classmethod
|
||||
@classmethod
|
||||
def can_user_administrate(cls, user, obj):
|
||||
if user.is_superuser:
|
||||
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.
|
||||
'''
|
||||
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
|
||||
@ -369,10 +369,10 @@ class Permission(CommonModel):
|
||||
# TODO: other job types (later)
|
||||
|
||||
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:
|
||||
app_label = 'main'
|
||||
|
||||
@ -387,18 +387,18 @@ class LaunchJob(CommonModel):
|
||||
return run_launch_job.delay(self.pk)
|
||||
|
||||
# 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
|
||||
# ssh-agent bash
|
||||
# ssh-add ... < key entry
|
||||
#
|
||||
# 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:
|
||||
# run, check -- enough for now, more initially
|
||||
# if check, add "--check" to parameters
|
||||
@ -408,7 +408,7 @@ class LaunchJob(CommonModel):
|
||||
# self.context.runner
|
||||
# and the callback will read the environment for ACOM_CELERY_JOB_ID or similar
|
||||
# and log tons into the database
|
||||
|
||||
|
||||
# we'll also log stdout/stderr somewhere for debugging
|
||||
|
||||
# the ansible commander setup instructions will include installing the database logging callback
|
||||
@ -423,7 +423,7 @@ class LaunchJob(CommonModel):
|
||||
# TODO: Events
|
||||
|
||||
class LaunchJobStatus(CommonModel):
|
||||
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
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')
|
||||
status = models.IntegerField()
|
||||
result_data = models.TextField()
|
||||
|
||||
|
||||
|
||||
# TODO: reporting (MPD)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user