1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-27 09:25:10 +03:00

Work in progress on inventory script using API.

This commit is contained in:
Chris Church 2013-06-20 04:20:48 -04:00
parent 65defb75fb
commit 59d1ae7322
17 changed files with 524 additions and 7 deletions

View File

@ -0,0 +1,29 @@
# Django REST Framework
from rest_framework import authentication
from rest_framework import exceptions
# AnsibleWorks
from ansibleworks.main.models import Job
class JobCallbackAuthentication(authentication.BaseAuthentication):
'''
Custom authentication used for views accessed by the inventory and callback
scripts when running a job.
'''
def authenticate(self, request):
auth = authentication.get_authorization_header(request).split()
if len(auth) != 2 or auth[0].lower() != 'token' or '-' not in auth[1]:
return None
job_id, job_key = auth[1].split('-', 1)
try:
job = Job.objects.get(pk=job_id, status='running')
except Job.DoesNotExist:
return None
token = job.callback_auth_token
if auth[1] != token:
raise exceptions.AuthenticationFailed('Invalid job callback token')
return (None, token)
def authenticate_header(self, request):
return 'Token'

View File

@ -2,6 +2,7 @@
# All Rights Reserved.
# Python
import hmac
import json
import os
import shlex
@ -814,6 +815,13 @@ class Job(CommonModel):
except TaskMeta.DoesNotExist:
pass
@property
def callback_auth_token(self):
'''Return temporary auth token used for task callbacks via API.'''
if self.status == 'running':
h = hmac.new(settings.SECRET_KEY, self.created.isoformat())
return '%d-%s' % (self.pk, h.hexdigest())
def get_passwords_needed_to_start(self):
'''Return list of password field names needed to start the job.'''
needed = []

View File

@ -116,3 +116,15 @@ class CustomRbac(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return self.has_permission(request, view, obj)
class JobCallbackPermission(CustomRbac):
def has_permission(self, request, view, obj=None):
# If another authentication method was used other than the one for job
# callbacks, return True to fall through to the next permission class.
if request.user or not request.auth:
return super(JobCallbackPermission, self).has_permission(request, view, obj)
# FIXME: Verify that inventory or job event requested are for the same
# job ID present in the auth token, etc.
return True

View File

@ -86,10 +86,13 @@ class RunJob(Task):
# answer: TBD
env['ACOM_JOB_ID'] = str(job.pk)
env['ACOM_INVENTORY_ID'] = str(job.inventory.pk)
env['INVENTORY_ID'] = str(job.inventory.pk)
env['ANSIBLE_CALLBACK_PLUGINS'] = plugin_dir
env['ACOM_CALLBACK_EVENT_SCRIPT'] = callback_script
if hasattr(settings, 'ANSIBLE_TRANSPORT'):
env['ANSIBLE_TRANSPORT'] = getattr(settings, 'ANSIBLE_TRANSPORT')
env['REST_API_URL'] = settings.INTERNAL_API_URL
env['REST_API_TOKEN'] = job.callback_auth_token or ''
env['ANSIBLE_NOCOLOR'] = '1' # Prevent output of escape sequences.
return env
@ -108,8 +111,11 @@ class RunJob(Task):
# it doesn't make sense to rely on ansible-playbook's default of using
# the current user.
ssh_username = ssh_username or 'root'
inventory_script = self.get_path_to('management', 'commands',
'acom_inventory.py')
if False:
inventory_script = self.get_path_to('management', 'commands',
'acom_inventory.py')
else:
inventory_script = self.get_path_to('..', 'scripts', 'inventory.py')
args = ['ansible-playbook', '-i', inventory_script]
if job.job_type == 'check':
args.append('--check')

View File

@ -6,6 +6,7 @@ from ansibleworks.main.tests.users import UsersTest
from ansibleworks.main.tests.inventory import InventoryTest
from ansibleworks.main.tests.projects import ProjectsTest
from ansibleworks.main.tests.commands import *
from ansibleworks.main.tests.scripts import *
from ansibleworks.main.tests.tasks import RunJobTest
from ansibleworks.main.tests.jobs import *

View File

@ -240,3 +240,8 @@ class BaseTransactionTest(BaseTestMixin, django.test.TransactionTestCase):
Base class for tests requiring transactions (or where the test database
needs to be accessed by subprocesses).
'''
class BaseLiveServerTest(BaseTestMixin, django.test.LiveServerTestCase):
'''
Base class for tests requiring a live test server.
'''

View File

@ -13,7 +13,7 @@ from django.utils.timezone import now
from ansibleworks.main.models import *
from ansibleworks.main.tests.base import BaseTest
__all__ = ['RunCommandAsScriptTest', 'AcomInventoryTest',
__all__ = ['RunCommandAsScriptTest',# 'AcomInventoryTest',
'AcomCallbackEventTest']
class BaseCommandTest(BaseTest):

View File

@ -0,0 +1,271 @@
# Copyright (c) 2013 AnsibleWorks, Inc.
# All Rights Reserved.
# Python
import json
import os
import StringIO
import subprocess
import sys
import tempfile
# Django
from django.conf import settings
from django.utils.timezone import now
# AnsibleWorks
from ansibleworks.main.models import *
from ansibleworks.main.tests.base import BaseLiveServerTest
__all__ = ['InventoryScriptTest']
class BaseScriptTest(BaseLiveServerTest):
'''
Base class for tests that run external scripts to access the API.
'''
def setUp(self):
super(BaseScriptTest, self).setUp()
self._sys_path = [x for x in sys.path]
self._environ = dict(os.environ.items())
self._temp_files = []
def tearDown(self):
super(BaseScriptTest, self).tearDown()
sys.path = self._sys_path
for k,v in self._environ.items():
if os.environ.get(k, None) != v:
os.environ[k] = v
for k,v in os.environ.items():
if k not in self._environ.keys():
del os.environ[k]
for tf in self._temp_files:
if os.path.exists(tf):
os.remove(tf)
def run_script(self, name, *args, **options):
'''
Run an external script and capture its stdout/stderr and return code.
'''
#stdin_fileobj = options.pop('stdin_fileobj', None)
pargs = [name]
for k,v in options.items():
pargs.append('%s%s' % ('-' if len(k) == 1 else '--', k))
if not v is True:
pargs.append(str(v))
for arg in args:
pargs.append(str(arg))
proc = subprocess.Popen(pargs, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()
return proc.returncode, stdout, stderr
class InventoryScriptTest(BaseScriptTest):
'''
Test helper to run management command as standalone script.
'''
def setUp(self):
super(InventoryScriptTest, self).setUp()
self.setup_users()
self.organizations = self.make_organizations(self.super_django_user, 2)
self.projects = self.make_projects(self.normal_django_user, 2)
self.organizations[0].projects.add(self.projects[1])
self.organizations[1].projects.add(self.projects[0])
self.inventories = []
self.hosts = []
self.groups = []
for n, organization in enumerate(self.organizations):
inventory = Inventory.objects.create(name='inventory-%d' % n,
description='description for inventory %d' % n,
organization=organization,
variables=json.dumps({'n': n}) if n else '')
self.inventories.append(inventory)
hosts = []
for x in xrange(10):
if n > 0:
variables = json.dumps({'ho': 'hum-%d' % x})
else:
variables = ''
host = inventory.hosts.create(name='host-%02d-%02d.example.com' % (n, x),
inventory=inventory,
variables=variables)
if x in (3, 7):
host.mark_inactive()
hosts.append(host)
self.hosts.extend(hosts)
groups = []
for x in xrange(5):
if n > 0:
variables = json.dumps({'gee': 'whiz-%d' % x})
else:
variables = ''
group = inventory.groups.create(name='group-%d' % x,
inventory=inventory,
variables=variables)
if x == 2:
group.mark_inactive()
groups.append(group)
group.hosts.add(hosts[x])
group.hosts.add(hosts[x + 5])
if n > 0 and x == 4:
group.parents.add(groups[3])
self.groups.extend(groups)
def run_inventory_script(self, *args, **options):
os.environ.setdefault('REST_API_URL', self.live_server_url)
os.environ.setdefault('REST_API_TOKEN',
self.super_django_user.auth_token.key)
name = os.path.join(os.path.dirname(__file__), '..', '..', 'scripts',
'inventory.py')
return self.run_script(name, *args, **options)
def test_without_inventory_id(self):
rc, stdout, stderr = self.run_inventory_script(list=True)
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
rc, stdout, stderr = self.run_inventory_script(host=self.hosts[0].name)
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
def test_list_with_inventory_id_as_argument(self):
inventory = self.inventories[0]
self.assertTrue(inventory.active)
rc, stdout, stderr = self.run_inventory_script(list=True,
inventory=inventory.pk)
self.assertEqual(rc, 0, stderr)
data = json.loads(stdout)
groups = inventory.groups.filter(active=True)
groupnames = groups.values_list('name', flat=True)
self.assertEqual(set(data.keys()), set(groupnames))
# Groups for this inventory should only have hosts, and no group
# variable data or parent/child relationships.
for k,v in data.items():
self.assertTrue(isinstance(v, (list, tuple)))
group = inventory.groups.get(active=True, name=k)
hosts = group.hosts.filter(active=True)
hostnames = hosts.values_list('name', flat=True)
self.assertEqual(set(v), set(hostnames))
for group in inventory.groups.filter(active=False):
self.assertFalse(group.name in data.keys(),
'deleted group %s should not be in data' % group)
# Command line argument for inventory ID should take precedence over
# environment variable.
inventory_pks = set(map(lambda x: x.pk, self.inventories))
invalid_id = [x for x in xrange(9999) if x not in inventory_pks][0]
os.environ['INVENTORY_ID'] = str(invalid_id)
rc, stdout, stderr = self.run_inventory_script(list=True,
inventory=inventory.pk)
self.assertEqual(rc, 0, stderr)
data = json.loads(stdout)
def test_list_with_inventory_id_in_environment(self):
inventory = self.inventories[1]
self.assertTrue(inventory.active)
os.environ['INVENTORY_ID'] = str(inventory.pk)
rc, stdout, stderr = self.run_inventory_script(list=True)
self.assertEqual(rc, 0, stderr)
data = json.loads(stdout)
groups = inventory.groups.filter(active=True)
groupnames = list(groups.values_list('name', flat=True)) + ['all']
self.assertEqual(set(data.keys()), set(groupnames))
# Groups for this inventory should have hosts, variable data, and one
# parent/child relationship.
for k,v in data.items():
self.assertTrue(isinstance(v, dict))
if k == 'all':
self.assertEqual(v.get('vars', {}), inventory.variables_dict)
continue
group = inventory.groups.get(active=True, name=k)
hosts = group.hosts.filter(active=True)
hostnames = hosts.values_list('name', flat=True)
self.assertEqual(set(v.get('hosts', [])), set(hostnames))
if group.variables:
self.assertEqual(v.get('vars', {}), group.variables_dict)
if k == 'group-3':
children = group.children.filter(active=True)
childnames = children.values_list('name', flat=True)
self.assertEqual(set(v.get('children', [])), set(childnames))
else:
self.assertFalse('children' in v)
def test_valid_host(self):
# Host without variable data.
inventory = self.inventories[0]
self.assertTrue(inventory.active)
host = inventory.hosts.filter(active=True)[2]
os.environ['INVENTORY_ID'] = str(inventory.pk)
rc, stdout, stderr = self.run_inventory_script(host=host.name)
self.assertEqual(rc, 0, stderr)
data = json.loads(stdout)
self.assertEqual(data, {})
# Host with variable data.
inventory = self.inventories[1]
self.assertTrue(inventory.active)
host = inventory.hosts.filter(active=True)[4]
os.environ['INVENTORY_ID'] = str(inventory.pk)
rc, stdout, stderr = self.run_inventory_script(host=host.name)
self.assertEqual(rc, 0, stderr)
data = json.loads(stdout)
self.assertEqual(data, host.variables_dict)
def test_invalid_host(self):
# Valid host, but not part of the specified inventory.
inventory = self.inventories[0]
self.assertTrue(inventory.active)
host = Host.objects.exclude(inventory=inventory)[0]
self.assertTrue(host.active)
os.environ['INVENTORY_ID'] = str(inventory.pk)
rc, stdout, stderr = self.run_inventory_script(host=host.name)
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
# Invalid hostname not in database.
rc, stdout, stderr = self.run_inventory_script(host='blah.example.com')
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
def test_with_invalid_inventory_id(self):
inventory_pks = set(map(lambda x: x.pk, self.inventories))
invalid_id = [x for x in xrange(1, 9999) if x not in inventory_pks][0]
os.environ['INVENTORY_ID'] = str(invalid_id)
rc, stdout, stderr = self.run_inventory_script(list=True)
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
os.environ['INVENTORY_ID'] = 'not_an_int'
rc, stdout, stderr = self.run_inventory_script(list=True)
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
os.environ['INVENTORY_ID'] = str(invalid_id)
rc, stdout, stderr = self.run_inventory_script(host=self.hosts[1].name)
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
os.environ['INVENTORY_ID'] = 'not_an_int'
rc, stdout, stderr = self.run_inventory_script(host=self.hosts[2].name)
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
def test_with_deleted_inventory(self):
inventory = self.inventories[0]
inventory.mark_inactive()
self.assertFalse(inventory.active)
os.environ['INVENTORY_ID'] = str(inventory.pk)
rc, stdout, stderr = self.run_inventory_script(list=True)
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
def test_without_list_or_host_argument(self):
inventory = self.inventories[0]
self.assertTrue(inventory.active)
os.environ['INVENTORY_ID'] = str(inventory.pk)
rc, stdout, stderr = self.run_inventory_script()
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})
def _test_with_both_list_and_host_arguments(self):
inventory = self.inventories[0]
self.assertTrue(inventory.active)
os.environ['INVENTORY_ID'] = str(inventory.pk)
rc, stdout, stderr = self.run_inventory_script(list=True, host='blah')
self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {})

View File

@ -7,7 +7,7 @@ import tempfile
from django.conf import settings
from django.test.utils import override_settings
from ansibleworks.main.models import *
from ansibleworks.main.tests.base import BaseTransactionTest
from ansibleworks.main.tests.base import BaseTransactionTest, BaseLiveServerTest
from ansibleworks.main.tasks import RunJob
TEST_PLAYBOOK = '''- hosts: test-group
@ -90,7 +90,7 @@ TEST_SSH_KEY_DATA_UNLOCK = 'unlockme'
@override_settings(CELERY_ALWAYS_EAGER=True,
CELERY_EAGER_PROPAGATES_EXCEPTIONS=True)
class BaseCeleryTest(BaseTransactionTest):
class BaseCeleryTest(BaseLiveServerTest):#BaseTransactionTest):
'''
Base class for celery task tests.
'''
@ -128,6 +128,7 @@ class RunJobTest(BaseCeleryTest):
self.build_args_callback()
return args
RunJob.build_args = new_build_args
settings.INTERNAL_API_URL = self.live_server_url
def tearDown(self):
super(RunJobTest, self).tearDown()
@ -193,6 +194,7 @@ class RunJobTest(BaseCeleryTest):
expect_traceback=False):
msg = 'job status is %s, expected %s' % (job.status, expected)
msg = '%s\nargs:\n%s' % (msg, job.job_args)
msg = '%s\nenv:\n%s' % (msg, job.job_env)
if job.result_traceback:
msg = '%s\ngot traceback:\n%s' % (msg, job.result_traceback)
if job.result_stdout:

View File

@ -53,6 +53,7 @@ inventory_urls = patterns('ansibleworks.main.views',
url(r'^(?P<pk>[0-9]+)/groups/$', 'inventory_groups_list'),
url(r'^(?P<pk>[0-9]+)/root_groups/$', 'inventory_root_groups_list'),
url(r'^(?P<pk>[0-9]+)/variable_data/$', 'inventory_variable_detail'),
url(r'^(?P<pk>[0-9]+)/script/$', 'inventory_script_view'),
)
host_urls = patterns('ansibleworks.main.views',

View File

@ -26,6 +26,7 @@ from rest_framework.views import APIView
# AnsibleWorks
from ansibleworks.main.access import *
from ansibleworks.main.authentication import JobCallbackAuthentication
from ansibleworks.main.base_views import *
from ansibleworks.main.models import *
from ansibleworks.main.rbac import *
@ -983,6 +984,53 @@ class GroupVariableDetail(BaseVariableDetail):
model = Group
serializer_class = GroupVariableDataSerializer
class InventoryScriptView(generics.RetrieveAPIView):
'''
Return inventory group and host data as needed for an inventory script.
Without query parameters, return groups with hosts, children and vars
(equivalent to the --list parameter to an inventory script).
With ?host=HOSTNAME, return host vars for the given host (equivalent to the
--host HOSTNAME parameter to an inventory script).
'''
model = Inventory
authentication_classes = [JobCallbackAuthentication] + api_settings.DEFAULT_AUTHENTICATION_CLASSES
permission_classes = (JobCallbackPermission,)
filter_backends = ()
def retrieve(self, request, *args, **kwargs):
self.object = self.get_object()
hostname = request.QUERY_PARAMS.get('host', '')
if hostname:
try:
host = self.object.hosts.get(active=True, name=hostname)
data = host.variables_dict
except Host.DoesNotExist:
raise Http404
else:
data = {}
for group in self.object.groups.filter(active=True):
hosts = group.hosts.filter(active=True)
children = group.children.filter(active=True)
group_info = {
'hosts': list(hosts.values_list('name', flat=True)),
'children': list(children.values_list('name', flat=True)),
'vars': group.variables_dict,
}
group_info = dict(filter(lambda x: bool(x[1]),
group_info.items()))
if group_info.keys() in ([], ['hosts']):
data[group.name] = group_info.get('hosts', [])
else:
data[group.name] = group_info
if self.object.variables_dict:
data['all'] = {
'vars': self.object.variables_dict,
}
return Response(data)
class JobTemplateList(BaseList):
model = JobTemplate

120
ansibleworks/scripts/inventory.py Executable file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env python
# Copyright (c) 2013 AnsibleWorks, Inc.
# All Rights Reserved.
# Python
import json
import optparse
import os
import sys
import urllib
import urlparse
# Requests
import requests
class TokenAuth(requests.auth.AuthBase):
def __init__(self, token):
self.token = token
def __call__(self, request):
request.headers['Authorization'] = 'Token %s' % self.token
return request
class InventoryScript(object):
def __init__(self, **options):
self.options = options
def get_data(self):
parts = urlparse.urlsplit(self.base_url)
if parts.username and parts.password:
auth = (parts.username, parts.password)
elif self.auth_token:
auth = TokenAuth(self.auth_token)
else:
auth = None
url = urlparse.urlunsplit([parts.scheme,
'%s:%d' % (parts.hostname, parts.port),
parts.path, parts.query, parts.fragment])
url_path = '/api/v1/inventories/%d/script/' % self.inventory_id
if self.hostname:
url_path += '?%s' % urllib.urlencode({'host': self.hostname})
url = urlparse.urljoin(url, url_path)
response = requests.get(url, auth=auth)
response.raise_for_status()
sys.stdout.write(json.dumps(response.json(), indent=self.indent) + '\n')
def run(self):
try:
self.base_url = self.options.get('base_url', '') or \
os.getenv('REST_API_URL', '')
if not self.base_url:
raise ValueError('No REST API URL specified')
self.auth_token = self.options.get('authtoken', '') or \
os.getenv('REST_API_TOKEN', '')
parts = urlparse.urlsplit(self.base_url)
if not (parts.username and parts.password) and not self.auth_token:
raise ValueError('No REST API token or username/password '
'specified')
try:
# Command line argument takes precedence over environment
# variable.
self.inventory_id = int(self.options.get('inventory_id', 0) or \
os.getenv('INVENTORY_ID', 0))
except ValueError:
raise ValueError('Inventory ID must be an integer')
if not self.inventory_id:
raise ValueError('No inventory ID specified')
self.hostname = self.options.get('hostname', '')
self.list_ = self.options.get('list', False)
self.indent = self.options.get('indent', None)
if self.list_ and self.hostname:
raise RuntimeError('Only --list or --host may be specified')
elif self.list_ or self.hostname:
self.get_data()
else:
raise RuntimeError('Either --list or --host must be specified')
except Exception, e:
# Always return an empty hash on stdout, even when an error occurs.
sys.stdout.write(json.dumps({}))
#print >> file(os.path.join(os.path.dirname(__file__), 'foo.log'), 'a'), repr(e)
#if hasattr(e, 'response'):
# print >> file(os.path.join(os.path.dirname(__file__), 'foo.log'), 'a'), e.response.content
if self.options.get('traceback', False):
raise
sys.stderr.write(str(e) + '\n')
if hasattr(e, 'response'):
sys.stderr.write(e.response.content + '\n')
sys.exit(1)
def main():
parser = optparse.OptionParser()
parser.add_option('-v', '--verbosity', action='store', dest='verbosity',
default='1', type='choice', choices=['0', '1', '2', '3'],
help='Verbosity level; 0=minimal output, 1=normal output'
', 2=verbose output, 3=very verbose output')
parser.add_option('--traceback', action='store_true',
help='Raise on exception on error')
parser.add_option('-u', '--url', dest='base_url', default='',
help='Base URL to access REST API (can also be specified'
' using REST_API_URL environment variable)')
parser.add_option('--authtoken', dest='authtoken', default='',
help='Authentication token used to access REST API (can '
'also be specified using REST_API_TOKEN environment '
'variable)')
parser.add_option('-i', '--inventory', dest='inventory_id', type='int',
default=0, help='Inventory ID (can also be specified '
'using INVENTORY_ID environment variable)')
parser.add_option('--list', action='store_true', dest='list',
default=False, help='Return JSON hash of host groups.')
parser.add_option('--host', dest='hostname', default='',
help='Return JSON hash of host vars.')
parser.add_option('--indent', dest='indent', type='int', default=None,
help='Indentation level for pretty printing output')
options, args = parser.parse_args()
InventoryScript(**vars(options)).run()
if __name__ == '__main__':
main()

View File

@ -133,7 +133,6 @@ INSTALLED_APPS = (
INTERNAL_IPS = ('127.0.0.1',)
REST_FRAMEWORK = {
'FILTER_BACKEND': 'ansibleworks.main.custom_filters.CustomFilterBackend',
'DEFAULT_PAGINATION_SERIALIZER_CLASS': 'ansibleworks.main.pagination.PaginationSerializer',
'PAGINATE_BY': 25,
'PAGINATE_BY_PARAM': 'page_size',
@ -142,6 +141,9 @@ REST_FRAMEWORK = {
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_FILTER_BACKENDS': (
'ansibleworks.main.custom_filters.CustomFilterBackend',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
@ -218,6 +220,9 @@ DEVSERVER_MODULES = (
#'devserver.modules.profile.LineProfilerModule',
)
# Set default ports for live server tests.
os.environ.setdefault('DJANGO_LIVE_TEST_SERVER_ADDRESS', 'localhost:9013-9199')
# Skip migrations when running tests.
SOUTH_TESTS_MIGRATE = False
@ -234,6 +239,11 @@ CELERYD_TASK_SOFT_TIME_LIMIT = 3540
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'
CELERYBEAT_MAX_LOOP_INTERVAL = 60
if 'devserver' in INSTALLED_APPS:
INTERNAL_API_URL = 'http://127.0.0.1:%s' % DEVSERVER_DEFAULT_PORT
else:
INTERNAL_API_URL = 'http://127.0.0.1:8000'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,

View File

@ -21,6 +21,8 @@ ALLOWED_HOSTS = []
# Production should only use minified JS for UI.
USE_MINIFIED_JS = True
INTERNAL_API_URL = 'http://127.0.0.1:80'
# If a local_settings.py file is present here, use it and ignore the global
# settings. Normally, local settings would only be present during development.
try:

View File

@ -11,6 +11,7 @@ djangorestframework>=2.3.0,<2.4.0
Markdown
pexpect
python-dateutil
requests
South>=0.8,<2.0
django-debug-toolbar

Binary file not shown.

View File

@ -53,7 +53,7 @@ def proc_data_files(data_files):
setup(
name='ansibleworks',
version=__version__.split("-")[0],
version=__version__.split("-")[0], # FIXME: Should keep full version here?
author='AnsibleWorks, Inc.',
author_email='support@ansibleworks.com',
description='AnsibleWorks API, UI and Task Engine',
@ -75,6 +75,7 @@ setup(
'pexpect',
'python-dateutil',
'PyYAML',
'requests',
'South>=0.8,<2.0',
],
setup_requires=[],